LD2410Async
Asynchronous Arduino ESP32 library for the LD2410 mmWave radar sensor
Loading...
Searching...
No Matches
LD2410Async.cpp
Go to the documentation of this file.
1#include "Arduino.h"
2#include "Ticker.h"
3#include "LD2410Async.h"
4#include "LD2410Defs.h"
5#include "LD2410Types.h"
7
8
9
10/******************************************************************************************
11* Utility methods
12******************************************************************************************/
13
14/**
15* @brief Checks whether the last 4 bytes of a buffer match the value supplied in pattern
16*/
17bool bufferEndsWith(const byte* buffer, int iMax, const byte* pattern)
18{
19 for (int j = 3; j >= 0; j--)
20 {
21 --iMax;
22 if (buffer[iMax] != pattern[j]) {
23 return false;
24 }
25 }
26 return true;
27}
28
29String byte2hex(byte b, bool addZero = true)
30{
31 String bStr(b, HEX);
32 bStr.toUpperCase();
33 if (addZero && (bStr.length() == 1))
34 return "0" + bStr;
35 return bStr;
36}
37
38
39
40
41/******************************************************************************************
42* Read frame methods
43******************************************************************************************/
44bool LD2410Async::readFramePayloadSize(byte b, ReadFrameState nextReadFrameState) {
45 receiveBuffer[receiveBufferIndex++] = b;
46 if (receiveBufferIndex >= 2) {
47 payloadSize = receiveBuffer[0] | (receiveBuffer[1] << 8);
48 if (payloadSize <= 0 || payloadSize > sizeof(receiveBuffer) - 4) {
49 // Invalid payload size, wait for header.
50 DEBUG_PRINT("Invalid payload size read: ");
51 DEBUG_PRINTLN(payloadSize);
52
53 readFrameState = ReadFrameState::WAITING_FOR_HEADER;
54 }
55 else {
56 receiveBufferIndex = 0;
57 readFrameState = nextReadFrameState;
58 }
59 return true;
60 }
61 return false;
62}
63
64LD2410Async::FrameReadResponse LD2410Async::readFramePayload(byte b, const byte* tailPattern, LD2410Async::FrameReadResponse successResponseType) {
65 receiveBuffer[receiveBufferIndex++] = b;
66
67 // Add 4 for the tail bytes
68 if (receiveBufferIndex >= payloadSize + 4) {
69
70 readFrameState = ReadFrameState::WAITING_FOR_HEADER;
71
72 if (bufferEndsWith(receiveBuffer, receiveBufferIndex, tailPattern)) {
73 return successResponseType;
74 }
75 else {
76 DEBUG_PRINTLN(" Invalid frame end: ");
77 DEBUG_PRINTBUF(receiveBuffer, receiveBufferIndex);
78
79 }
80
81 }
82 return FAIL;
83};
84
85LD2410Async::FrameReadResponse LD2410Async::readFrame()
86{
87 while (sensor->available()) {
88 byte b = sensor->read();
89
90 switch (readFrameState) {
91 case WAITING_FOR_HEADER:
92 if (b == LD2410Defs::headData[0]) {
93 readFrameHeaderIndex = 1;
94 readFrameState = DATA_HEADER;
95
96 }
97 else if (b == LD2410Defs::headConfig[0]) {
98 readFrameHeaderIndex = 1;
99 readFrameState = ACK_HEADER;
100
101 }
102 break;
103
104 case DATA_HEADER:
105 if (b == LD2410Defs::headData[readFrameHeaderIndex]) {
106 readFrameHeaderIndex++;
107
108 if (readFrameHeaderIndex == sizeof(LD2410Defs::headData)) {
109 receiveBufferIndex = 0;
110 readFrameState = READ_DATA_SIZE;
111 }
112 }
113 else if (b == LD2410Defs::headData[0]) {
114 // fallback: this byte might be the start of a new header
115
116 readFrameHeaderIndex = 1;
117
118 // stay in DATA_HEADER
119 }
120 else if (b == LD2410Defs::headConfig[0]) {
121 // possible start of config header
122 readFrameHeaderIndex = 1;
123 readFrameState = ACK_HEADER;
124
125 }
126 else {
127 // not a header at all
128 readFrameState = WAITING_FOR_HEADER;
129 readFrameHeaderIndex = 0;
130
131 }
132 break;
133
134 case ACK_HEADER:
135 if (b == LD2410Defs::headConfig[readFrameHeaderIndex]) {
136 readFrameHeaderIndex++;
137
138 if (readFrameHeaderIndex == sizeof(LD2410Defs::headConfig)) {
139 receiveBufferIndex = 0;
140 readFrameState = READ_ACK_SIZE;
141 }
142 }
143 else if (b == LD2410Defs::headConfig[0]) {
144 readFrameHeaderIndex = 1;
145 // stay in ACK_HEADER
146
147 }
148 else if (b == LD2410Defs::headData[0]) {
149 // maybe start of data header
150 readFrameHeaderIndex = 1;
151 readFrameState = DATA_HEADER;
152
153 }
154 else {
155 readFrameState = WAITING_FOR_HEADER;
156 readFrameHeaderIndex = 0;
157
158
159 }
160 break;
161
162 case READ_ACK_SIZE:
163 readFramePayloadSize(b, READ_ACK_PAYLOAD);
164 break;
165
166 case READ_DATA_SIZE:
167 readFramePayloadSize(b, READ_DATA_PAYLOAD);
168 break;
169
170 case READ_ACK_PAYLOAD:
171 return readFramePayload(b, LD2410Defs::tailConfig, ACK);
172
173 case READ_DATA_PAYLOAD:
174 return readFramePayload(b, LD2410Defs::tailData, DATA);
175
176 default:
177 readFrameState = WAITING_FOR_HEADER;
178 readFrameHeaderIndex = 0;
179 break;
180 }
181 }
182
183 return FAIL; // not enough bytes yet
184}
185
186
187/******************************************************************************************
188* Generic Callbacks
189******************************************************************************************/
190void LD2410Async::onDetectionDataReceived(DetectionDataCallback callback) {
191 detectionDataCallback = callback;
192}
193
194void LD2410Async::onConfigDataReceived(GenericCallback callback) {
195 configUpdateReceivedReceivedCallback = callback;
196}
197
198void LD2410Async::onConfigChanged(GenericCallback callback) {
199 configChangedCallback = callback;
200}
201
202
203
204void LD2410Async::executeConfigUpdateReceivedCallback() {
205
206 if (configUpdateReceivedReceivedCallback) {
207 configUpdateReceivedReceivedCallback(this);
208 }
209
210}
211
212void LD2410Async::executeConfigChangedCallback() {
213
214 if (configChangedCallback) {
215 configChangedCallback(this);
216 }
217}
218/******************************************************************************************
219* Process received data
220******************************************************************************************/
221bool LD2410Async::processAck()
222{
224 DEBUG_PRINTBUF(receiveBuffer, receiveBufferIndex)
225
226 heartbeat();
227
228 byte command = receiveBuffer[0];
229 byte success = receiveBuffer[1];
230
231 if (!success) {
233 DEBUG_PRINT("FAIL for command: ");
234 DEBUG_PRINTLN(byte2hex(command));
235 sendCommandAsyncExecuteCallback(command, LD2410Async::AsyncCommandResult::FAILED);
236 return false;
237 };
238
239
240 switch (command)
241 {
242 case LD2410Defs::configEnableCommand: // entered config mode
243 configModeEnabled = true;
244 staticData.protocolVersion = receiveBuffer[4] | (receiveBuffer[5] << 8);
245 staticData.bufferSize = receiveBuffer[6] | (receiveBuffer[7] << 8);
247 DEBUG_PRINTLN("ACK for config mode enable received");
248 break;
249 case LD2410Defs::configDisableCommand: // exited config mode
250 configModeEnabled = false;
252 DEBUG_PRINTLN("ACK for config mode disable received");
253 break;
255 DEBUG_PRINTLN("ACK for maxGateCommand received");
256 executeConfigChangedCallback();
257 break;
261 DEBUG_PRINTLN("ACK for engineeringModeEnableComand received");
262 break;
266 DEBUG_PRINTLN("ACK for engineeringModeDisableComand received");
267 break;
270 DEBUG_PRINTLN("ACK for setBaudRateCommand received");
271 break;
274 DEBUG_PRINTLN("ACK for restoreFactorySettingsAsyncCommand received");
275 executeConfigChangedCallback();
276 break;
279 configModeEnabled = false;
281 DEBUG_PRINTLN("ACK for rebootCommand received");
282 break;
285 DEBUG_PRINTLN("ACK for bluetoothSettingsCommand received");
286 break;
288 // This command is only relevant for a Bluetooth connection, but the sensor might send an ACK for it at some point.
290 DEBUG_PRINTLN("ACK for getBluetoothPermissionsCommand received");
291 break;
294 DEBUG_PRINTLN("ACK for setBluetoothPasswordCommand received");
295 break;
298 DEBUG_PRINTLN("ACK for setDistanceResolutionCommand received");
299 executeConfigChangedCallback();
300 break;
302 configData.distanceResolution = LD2410Types::toDistanceResolution(receiveBuffer[4]);
304 DEBUG_PRINTLN("ACK for requestDistanceResolutionCommand received");
305 executeConfigUpdateReceivedCallback();
306 break;
309 DEBUG_PRINTLN("ACK for setAuxControlSettingsCommand received");
310 executeConfigChangedCallback();
311 break;
313 for (int i = 0; i < 6; i++) {
314 staticData.bluetoothMac[i] = receiveBuffer[i + 4];
315 }
316
317 // Format MAC as "AA:BB:CC:DD:EE:FF"
319 "%02X:%02X:%02X:%02X:%02X:%02X",
323 DEBUG_PRINTLN("ACK for requestBluetoothMacAddressAsyncCommand received");
324 break;
327 "%X.%02X.%02X%02X%02X%02X",
328 receiveBuffer[7], // major (no leading zero)
329 receiveBuffer[6], // minor (keep 2 digits)
330 receiveBuffer[11],
331 receiveBuffer[10],
332 receiveBuffer[9],
333 receiveBuffer[8]);
335 DEBUG_PRINTLN("ACK for requestFirmwareAsyncCommand received");
336 break;
337
339 configData.lightControl = LD2410Types::toLightControl(receiveBuffer[4]);
340 configData.lightThreshold = receiveBuffer[5];
341 configData.outputControl = LD2410Types::toOutputControl(receiveBuffer[6]);
343 DEBUG_PRINTLN("ACK for requestAuxControlSettingsCommand received");
344 executeConfigUpdateReceivedCallback();
345 break;
348 DEBUG_PRINTLN("ACK for beginAutoConfigCommand received");
349 break;
351 autoConfigStatus = LD2410Types::toAutoConfigStatus(receiveBuffer[4]);
353 DEBUG_PRINTLN("ACK for requestAutoConfigStatusCommand received");
354 break;
355 case LD2410Defs::requestParamsCommand: // Query parameters
356 configData.numberOfGates = receiveBuffer[5];
357 configData.maxMotionDistanceGate = receiveBuffer[6];
358 configData.maxStationaryDistanceGate = receiveBuffer[7];
359 // Need to check if we should really do this or whether using a fixed number would be better.
360 for (byte i = 0; i <= configData.numberOfGates; i++)
361 configData.distanceGateMotionSensitivity[i] = receiveBuffer[8 + i];
362 for (byte i = 0; i <= configData.numberOfGates; i++)
363 configData.distanceGateStationarySensitivity[i] = receiveBuffer[17 + i];
364 configData.noOneTimeout = receiveBuffer[26] | (receiveBuffer[27] << 8);
366 DEBUG_PRINTLN("ACK for requestGateParametersAsync received");
367 executeConfigUpdateReceivedCallback();
368 break;
371 DEBUG_PRINTLN("ACK for distanceGateSensitivityConfigCommand received");
372 executeConfigChangedCallback();
373 break;
374 default:
376 DEBUG_PRINT("ACK for unknown command received. Command code: ");
377 DEBUG_PRINTLN(byte2hex(command));
378 break;
379 };
380
381 if (command != 0) {
382 sendCommandAsyncExecuteCallback(command, LD2410Async::AsyncCommandResult::SUCCESS);
383 }
384
385
386 return (true);
387}
388
389bool LD2410Async::processData()
390{
391 DEBUG_PRINTBUF_DATA(receiveBuffer, receiveBufferIndex)
392
393 heartbeat();
394
395
396 if (((receiveBuffer[0] == 1) || (receiveBuffer[0] == 2)) && (receiveBuffer[1] == 0xAA))
397 {
398 configModeEnabled = false;
399
400 detectionData.timestamp = millis();
401 // Basic data
402 detectionData.targetState = LD2410Types::toTargetState(receiveBuffer[2] & 7);
403 switch (detectionData.targetState) {
408 break;
409
414 break;
415
420 break;
421 default:
425 break;
426 }
427 detectionData.movingTargetDistance = receiveBuffer[3] | (receiveBuffer[4] << 8);
428 detectionData.movingTargetSignal = receiveBuffer[5];
429 detectionData.stationaryTargetDistance = receiveBuffer[6] | (receiveBuffer[7] << 8);
430 detectionData.stationaryTargetSignal = receiveBuffer[8];
431 detectionData.detectedDistance = receiveBuffer[9] | (receiveBuffer[10] << 8);
432 detectionData.engineeringMode = receiveBuffer[0] == 1;
433
435
437 { // Engineering mode data
438 detectionData.movingTargetGateSignalCount = receiveBuffer[11];
440
441
442 int index = 13;
443 for (byte i = 0; i <= detectionData.movingTargetGateSignalCount; i++)
444 detectionData.movingTargetGateSignals[i] = receiveBuffer[index++];
445 for (byte i = 0; i <= detectionData.stationaryTargetGateSignalCount; i++)
446 detectionData.stationaryTargetGateSignals[i] = receiveBuffer[index++];
447
450 if (index < payloadSize) {
451 detectionData.lightLevel = receiveBuffer[index++];
452 if (index < payloadSize) {
453 detectionData.outPinStatus = receiveBuffer[index++];
454 }
455 }
456 }
457 else
458 { // Clear engineering mode data
463 }
464
465 if (rebootAsyncPending) {
466 rebootAsyncFinialize(LD2410Async::AsyncCommandResult::SUCCESS);
467 }
468
469 if (detectionDataCallback != nullptr) {
470 detectionDataCallback(this, detectionData.presenceDetected);
471 };
472 DEBUG_PRINTLN_DATA("DATA received");
473 return true;
474 }
475 DEBUG_PRINTLN_DATA("DATA invalid");
476 return false;
477}
478
479
480
481
482
483
484/******************************************************************************************
485* Send command
486******************************************************************************************/
487void LD2410Async::sendCommand(const byte* command) {
488 byte size = command[0] + 2;
489
490
491
492 sensor->write(LD2410Defs::headConfig, 4);
493 sensor->write(command, size);
494 sensor->write(LD2410Defs::tailConfig, 4);
495
496}
497
498/**********************************************************************************
499* Send async command methods
500***********************************************************************************/
501void LD2410Async::sendCommandAsyncExecuteCallback(byte commandCode, LD2410Async::AsyncCommandResult result) {
502 if (sendCommandAsyncCommandPending && sendCommandAsyncCommandCode == commandCode) {
503
505 DEBUG_PRINT("Async command duration ms: ");
506 DEBUG_PRINTLN(millis() - sendCommandAsyncStartMs);
507
508 // Just to be sure that no other task changes the callback data or registers a callback before the callback has been executed
509 vTaskSuspendAll();
510 AsyncCommandCallback cb = sendCommandAsyncCallback;
511 sendCommandAsyncCallback = nullptr;
512 sendCommandAsyncStartMs = 0;
513 sendCommandAsyncCommandCode = 0;
514 sendCommandAsyncCommandPending = false;
515 xTaskResumeAll();
516
517 if (cb != nullptr) {
518 cb(this, result);
519 }
520 }
521}
522
523
524
525
527 // Will also trigger the callback on command sequences
528 sendCommandAsyncExecuteCallback(sendCommandAsyncCommandCode, LD2410Async::AsyncCommandResult::CANCELED);
529
530 // Just in case reboot is still waiting
531 // since there might not be an active command or command sequence while we wait for normal operation.
532 rebootAsyncFinialize(LD2410Async::AsyncCommandResult::CANCELED);
533}
534
535void LD2410Async::sendCommandAsyncHandleTimeout() {
536
537 if (sendCommandAsyncCommandPending && sendCommandAsyncStartMs != 0) {
538 unsigned long elapsedTime = millis() - sendCommandAsyncStartMs;
539 if (elapsedTime > asyncCommandTimeoutMs) {
541 DEBUG_PRINT("Command timeout detected. Elapsed time: ");
542 DEBUG_PRINT(elapsedTime);
543 DEBUG_PRINTLN("ms.");
544
545 if (sendCommandAsyncRetriesLeft > 0) {
546 DEBUG_PRINT("Retries left: ");
547 DEBUG_PRINTLN(sendCommandAsyncRetriesLeft);
548 sendCommandAsyncRetriesLeft--;
549
550 sendCommandAsyncStartMs = millis();
551 sendCommand(sendCommandAsyncCommandBuffer);
552 }
553 else {
554
555 DEBUG_PRINTLN("Execute callback with TIMEOUT result.");
556 sendCommandAsyncExecuteCallback(sendCommandAsyncCommandCode, LD2410Async::AsyncCommandResult::TIMEOUT);
557 }
558 }
559 }
560}
561
562
563
564void LD2410Async::sendCommandAsyncStoreDataForCallback(const byte* command, byte retries, AsyncCommandCallback callback) {
565 sendCommandAsyncCommandPending = true;
566 sendCommandAsyncCallback = callback;
567 sendCommandAsyncCommandCode = command[2];
568 sendCommandAsyncRetriesLeft = retries;
569 if (retries > 0) { // No need to copy the data if we are not going to retry anyway
570 memcpy(sendCommandAsyncCommandBuffer, command, command[0] + 2);
571 }
572}
573
574bool LD2410Async::sendCommandAsync(const byte* command, byte retries, AsyncCommandCallback callback)
575{
576 vTaskSuspendAll();
577
578 // Don't check with asyncIsBusy() since this would also check if a sequence or setConfigDataAsync is active
579 if (!sendCommandAsyncCommandPending) {
580
581
582 // Register data for callback
583 sendCommandAsyncStoreDataForCallback(command, retries, callback);
584 xTaskResumeAll();
585
586 sendCommandAsyncStartMs = millis();
587 sendCommand(command);
589 DEBUG_PRINT("Async command ");
590 DEBUG_PRINT(byte2hex(sendCommandAsyncCommandCode));
591 DEBUG_PRINTLN(" sent");
592
593 return true;
594 }
595 else {
596 xTaskResumeAll();
598 DEBUG_PRINT("Error! Async command is pending. Did not send async command ");
599 DEBUG_PRINTLN(byte2hex(command[2]));
600
601 return false;
602 }
603
604
605}
606/**********************************************************************************
607* Async command busy
608***********************************************************************************/
609
611 return sendCommandAsyncCommandPending || executeCommandSequencePending || configureAllConfigSettingsAsyncConfigActive || rebootAsyncPending;
612}
613
614/**********************************************************************************
615* Send async config command methods
616***********************************************************************************/
617
618
619bool LD2410Async::sendConfigCommandAsync(const byte* command, AsyncCommandCallback callback) {
620 // Don't check with asyncIsBusy() since this would also check if a sequence or setConfigDataAsync is active
621 if (sendCommandAsyncCommandPending || executeCommandSequencePending) return false;
622
623 // Reset sequence buffer
624 if (!resetCommandSequence()) return false;;
625
626 // Add the single command
627 if (!addCommandToSequence(command)) return false;
628
629 // Execute as a sequence (with just one command)
630 return executeCommandSequenceAsync(callback);
631}
632
633/**********************************************************************************
634* Async command sequence methods
635***********************************************************************************/
636void LD2410Async::executeCommandSequenceAsyncExecuteCallback(LD2410Async::AsyncCommandResult result) {
637 if (executeCommandSequencePending) {
638
640 DEBUG_PRINT("Command sequence duration ms: ");
641 DEBUG_PRINTLN(millis() - executeCommandSequenceStartMs);
642
643 vTaskSuspendAll();
644 AsyncCommandCallback cb = executeCommandSequenceCallback;
645 executeCommandSequenceCallback = nullptr;
646 executeCommandSequencePending = false;
647 xTaskResumeAll();
648
649 if (cb != nullptr) {
650 cb(this, result);
651 }
652 }
653}
654
655
656// Callback after disabling config mode at the end of sequence
657void LD2410Async::executeCommandSequenceAsyncDisableConfigModeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
658
659 LD2410Async::AsyncCommandResult sequenceResult = sender->executeCommandSequenceResultToReport;
660
663 DEBUG_PRINTLN("Warning: Disabling config mode after command sequence failed. Result: ");
664 DEBUG_PRINTLN((byte)result);
665 sequenceResult = result; // report disable failure if it happened
666 }
667 sender->executeCommandSequenceAsyncExecuteCallback(sequenceResult);
668}
669
670void LD2410Async::executeCommandSequenceAsyncFinalize(LD2410Async::AsyncCommandResult result) {
671 if (!executeCommandSequenceInitialConfigModeState) {
672 executeCommandSequenceResultToReport = result;
673 if (!disableConfigModeInternalAsync(executeCommandSequenceAsyncDisableConfigModeCallback)) {
674 executeCommandSequenceAsyncExecuteCallback(LD2410Async::AsyncCommandResult::FAILED);
675 }
676 }
677 else {
678 executeCommandSequenceAsyncExecuteCallback(result);
679 }
680}
681
682
683// Called when a single command in the sequence completes
684void LD2410Async::executeCommandSequenceAsyncCommandCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
686 // Abort sequence if a command fails
688 DEBUG_PRINT("Error: Command sequence aborted due to command failure. Result: ");
689 DEBUG_PRINTLN((byte)result);
690 sender->executeCommandSequenceAsyncFinalize(result);
691
692 return;
693 };
694
695 // Move to next command
696 sender->executeCommandSequenceIndex++;
697
698 if (sender->executeCommandSequenceIndex < sender->commandSequenceBufferCount) {
699 // Send next command
700 sender->sendCommandAsync(sender->commandSequenceBuffer[sender->executeCommandSequenceIndex], executeCommandSequenceAsyncCommandCallback);
701 }
702 else {
703 // Sequence finished successfully
704
705 sender->executeCommandSequenceAsyncFinalize(LD2410Async::AsyncCommandResult::SUCCESS);
706 }
707}
708
709
710
711bool LD2410Async::executeCommandSequenceAsync(AsyncCommandCallback callback) {
712 if (sendCommandAsyncCommandPending || executeCommandSequencePending) return false;
713 if (commandSequenceBufferCount == 0) return true; // nothing to send
714
716 DEBUG_PRINT("Starting command sequence execution. Number of commands: ");
717 DEBUG_PRINTLN(commandSequenceBufferCount);
718
719 executeCommandSequencePending = true;
720 executeCommandSequenceCallback = callback;
721 executeCommandSequenceInitialConfigModeState = configModeEnabled;
722 executeCommandSequenceStartMs = millis();
723
724 if (commandSequenceBufferCount == 0) {
725 // Wait 1 ms to ensure that the callback is not executed before the caller of this method has finished its work
726 executeCommandSequenceOnceTicker.once_ms(1, [this]() {
727 this->executeCommandSequenceAsyncExecuteCallback(LD2410Async::AsyncCommandResult::SUCCESS);
728 });
729 return true;
730
731 }
732
733 if (!configModeEnabled) {
734 // Need to enable config mode first
735 // So set the sequence index to -1 to ensure the first command in the sequence (at index 0) is executed when the callback fires.
736 executeCommandSequenceIndex = -1;
737 return sendCommandAsync(LD2410Defs::configEnableCommandData, 1, executeCommandSequenceAsyncCommandCallback); // Retry once because this command sometimes fails or may not send an ACK.
738 }
739 else {
740 // Already in config mode, start directly.
741 // We start the first command in the sequence directly and set the sequence index 0, so the second command (if any) is executed when the callback fires.
742 executeCommandSequenceIndex = 0;
743 return sendCommandAsync(commandSequenceBuffer[executeCommandSequenceIndex], executeCommandSequenceAsyncCommandCallback);
744 }
745
746}
747
748bool LD2410Async::addCommandToSequence(const byte* command) {
749 if (sendCommandAsyncCommandPending || executeCommandSequencePending) return false;
750
751 if (commandSequenceBufferCount >= MAX_COMMAND_SEQUENCE_LENGTH) {
753 DEBUG_PRINTLN("Error: Command sequence buffer full.");
754 return false;
755 };
756 // First byte of the command is the payload length
757 uint8_t len = command[0];
758 uint8_t totalLen = len + 2; // payload + 2 length bytes
759
760 if (totalLen > LD2410Defs::LD2410_Buffer_Size) {
762 DEBUG_PRINTLN("Error: Command too long for command sequence buffer.");
763 return false; // safety
764 }
765 memcpy(commandSequenceBuffer[commandSequenceBufferCount],
766 command,
767 totalLen);
768
769 commandSequenceBufferCount++;
771 DEBUG_PRINT("Command added to sequence. Count: ");
772 DEBUG_PRINTLN(commandSequenceBufferCount);
773
774 return true;
775}
776
777
778bool LD2410Async::resetCommandSequence() {
779 if (sendCommandAsyncCommandPending || executeCommandSequencePending) return false;
780
781 commandSequenceBufferCount = 0;
783 DEBUG_PRINTLN("Command sequence reset done.");
784
785 return true;
786}
787
788/**********************************************************************************
789* Config mode commands
790***********************************************************************************/
791// Config mode commands have an internal version which does not check for busy and can therefore be used within other command implementations.
792
793
794
795
796bool LD2410Async::enableConfigModeInternalAsync(bool force, AsyncCommandCallback callback) {
798 DEBUG_PRINTLN("Enable Config Mode");
799
800 if (!configModeEnabled || force) {
801 // Need to send the enable command
802 return sendCommandAsync(LD2410Defs::configEnableCommandData, 1, callback); // We retry once if necessary because this command sometimes fails or might not send an ACK.
803 }
804 else {
805 // Already in config mode -> just trigger the callback after a tiny delay
806 sendCommandAsyncStoreDataForCallback(LD2410Defs::configEnableCommandData, 0, callback);
807 configModeOnceTicker.once_ms(1, [this]() {
809 });
810 return true;
811 }
812}
813
814bool LD2410Async::enableConfigModeAsync(bool force, AsyncCommandCallback callback) {
815 if (asyncIsBusy()) return false;
816 return enableConfigModeInternalAsync(force, callback);
817}
818
819bool LD2410Async::disableConfigModeInternalAsync(bool force, AsyncCommandCallback callback) {
821 DEBUG_PRINTLN("Disable Config Mode");
822
823 if (configModeEnabled || force) {
824 return sendCommandAsync(LD2410Defs::configDisableCommandData, callback);
825 }
826 else {
827 // If config mode doesn't have to be disabled, the callback gets triggered after a minimal delay
828 // to ensure the disableConfigMode method can complete before the callback fires.
829 sendCommandAsyncStoreDataForCallback(LD2410Defs::configDisableCommandData, 0, callback);
830 configModeOnceTicker.once_ms(1, [this]() {
832 });
833 return true;
834 }
835}
836
837bool LD2410Async::disableConfigModeAsync(bool force, AsyncCommandCallback callback) {
838 if (asyncIsBusy()) return false;
839 return disableConfigModeInternalAsync(force, callback);
840}
841
842/**********************************************************************************
843* Native LD2410 commands to control, configure and query the sensor
844***********************************************************************************/
845// All these commands need to be executed in config mode.
846// The code takes care of that: enabling or disabling config mode if necessary,
847// while keeping config mode active if it was already active before the command.
848bool LD2410Async::configureMaxGateAndNoOneTimeoutAsync(byte maxMovingGate, byte maxStationaryGate,
849 unsigned short noOneTimeout,
850 AsyncCommandCallback callback)
851{
853 DEBUG_PRINT(noOneTimeout);
854 DEBUG_PRINTLN("Set Max Gate");
855 if (asyncIsBusy()) return false;
856
857 byte cmd[sizeof(LD2410Defs::maxGateCommandData)];
858 if (LD2410CommandBuilder::buildMaxGateCommand(cmd, maxMovingGate, maxStationaryGate, noOneTimeout)) {
859 return sendConfigCommandAsync(cmd, callback);
860 }
861 return false;
862}
863
864
865bool LD2410Async::requestGateParametersAsync(AsyncCommandCallback callback) {
867 DEBUG_PRINTLN("Request Gate Parameters");
868 if (asyncIsBusy()) return false;
869 return sendConfigCommandAsync(LD2410Defs::requestParamsCommandData, callback);
870}
871
872bool LD2410Async::enableEngineeringModeAsync(AsyncCommandCallback callback) {
874 DEBUG_PRINTLN("Enable EngineeringMode");
875 if (asyncIsBusy()) return false;
876 return sendConfigCommandAsync(LD2410Defs::engineeringModeEnableCommandData, callback);
877}
878
879bool LD2410Async::disableEngineeringModeAsync(AsyncCommandCallback callback) {
881 DEBUG_PRINTLN("Disable EngineeringMode");
882 if (asyncIsBusy()) return false;
883 return sendConfigCommandAsync(LD2410Defs::engineeringModeDisableCommandData, callback);
884}
885
886bool LD2410Async::configureDistanceGateSensitivityAsync(const byte movingThresholds[9],
887 const byte stationaryThresholds[9],
888 AsyncCommandCallback callback)
889{
891 DEBUG_PRINTLN("Set Distance Gate Sensitivities (all gates)");
892
893 if (asyncIsBusy()) return false;
894
895 if (!resetCommandSequence()) return false;
896
897 for (byte gate = 0; gate < 9; gate++) {
899 if (!LD2410CommandBuilder::buildGateSensitivityCommand(cmd, gate, movingThresholds[gate], stationaryThresholds[gate])) return false;
900 if (!addCommandToSequence(cmd)) return false;
901 }
902
903 return executeCommandSequenceAsync(callback);
904}
905
906
907
908bool LD2410Async::configureDistanceGateSensitivityAsync(byte gate, byte movingThreshold,
909 byte stationaryThreshold,
910 AsyncCommandCallback callback)
911{
913 DEBUG_PRINTLN("Set Distance Gate Sensitivity");
914
915 if (asyncIsBusy()) return false;
916
918 if (!LD2410CommandBuilder::buildGateSensitivityCommand(cmd, gate, movingThreshold, stationaryThreshold)) return false;
919
920 return sendConfigCommandAsync(cmd, callback);
921}
922
923
924
925bool LD2410Async::requestFirmwareAsync(AsyncCommandCallback callback) {
927 DEBUG_PRINTLN("Request Firmware");
928
929 if (asyncIsBusy()) return false;
930
931 return sendConfigCommandAsync(LD2410Defs::requestFirmwareCommandData, callback);
932}
933
934
935bool LD2410Async::configureBaudRateAsync(byte baudRateSetting,
936 AsyncCommandCallback callback)
937{
939 DEBUG_PRINTLN("Set Baud Rate");
940
941 if (asyncIsBusy()) return false;
942
943 if ((baudRateSetting < 1) || (baudRateSetting > 8))
944 return false;
945
946 byte cmd[sizeof(LD2410Defs::setBaudRateCommandData)];
947 if (!LD2410CommandBuilder::buildBaudRateCommand(cmd, baudRateSetting)) return false;
948
949 return sendConfigCommandAsync(cmd, callback);
950}
951
952
953
954bool LD2410Async::configureBaudRateAsync(LD2410Types::Baudrate baudRate, AsyncCommandCallback callback) {
955
956 if (asyncIsBusy()) return false;
957
958 return configureBaudRateAsync((byte)baudRate, callback);
959}
960
961
962bool LD2410Async::restoreFactorySettingsAsync(AsyncCommandCallback callback) {
964 DEBUG_PRINTLN("Restore Factory Settings");
965
966 if (asyncIsBusy()) return false;
967 return sendConfigCommandAsync(LD2410Defs::restoreFactorSettingsCommandData, callback);
968}
969
970
971
972bool LD2410Async::enableBluetoothAsync(AsyncCommandCallback callback) {
974 DEBUG_PRINTLN("Enable Bluetooth");
975 if (asyncIsBusy()) return false;
976
977 return sendConfigCommandAsync(LD2410Defs::bluetoothSettingsOnCommandData, callback);
978}
979
980bool LD2410Async::disableBluetoothAsync(AsyncCommandCallback callback) {
982 DEBUG_PRINTLN("Disable Bluetooth");
983 if (asyncIsBusy()) return false;
984 return sendConfigCommandAsync(LD2410Defs::bluetoothSettingsOnCommandData, callback);
985}
986
987
988bool LD2410Async::requestBluetoothMacAddressAsync(AsyncCommandCallback callback) {
990 DEBUG_PRINTLN("Request Mac Address");
991 if (asyncIsBusy()) return false;
992 return sendConfigCommandAsync(LD2410Defs::requestMacAddressCommandData, callback);
993}
994
996 AsyncCommandCallback callback)
997{
999 DEBUG_PRINTLN("Set Bluetooth Password");
1000 if (asyncIsBusy()) return false;
1001
1003 if (!LD2410CommandBuilder::buildBluetoothPasswordCommand(cmd, password)) return false;
1004
1005 return sendConfigCommandAsync(cmd, callback);
1006}
1007
1008
1009
1010bool LD2410Async::configureBluetoothPasswordAsync(const String& password, AsyncCommandCallback callback) {
1011
1012 return configureBluetoothPasswordAsync(password.c_str(), callback);
1013}
1014
1015
1016bool LD2410Async::configureDefaultBluetoothPasswordAsync(AsyncCommandCallback callback) {
1018 DEBUG_PRINTLN("Reset Bluetooth Password");
1019 if (asyncIsBusy()) return false;
1020 return sendConfigCommandAsync(LD2410Defs::setBluetoothPasswordCommandData, callback);
1021}
1022
1023bool LD2410Async::configureDistanceResolution75cmAsync(AsyncCommandCallback callback) {
1025 DEBUG_PRINTLN("Set Distance Resolution 75cm");
1026 if (asyncIsBusy()) return false;
1027 return sendConfigCommandAsync(LD2410Defs::setDistanceResolution75cmCommandData, callback);
1028};
1029
1031 AsyncCommandCallback callback)
1032{
1034 DEBUG_PRINTLN("Set Distance Resolution");
1035 if (asyncIsBusy()) return false;
1036
1037 byte cmd[6];
1038 if (!LD2410CommandBuilder::buildDistanceResolutionCommand(cmd, distanceResolution)) return false;
1039
1040 return sendConfigCommandAsync(cmd, callback);
1041}
1042
1043bool LD2410Async::configuresDistanceResolution20cmAsync(AsyncCommandCallback callback) {
1045 DEBUG_PRINTLN("Set Distance Resolution 20cm");
1046 if (asyncIsBusy()) return false;
1047 return sendConfigCommandAsync(LD2410Defs::setDistanceResolution20cmCommandData, callback);
1048};
1049
1050bool LD2410Async::requestDistanceResolutionAsync(AsyncCommandCallback callback) {
1052 DEBUG_PRINTLN("Request Distance Resolution cm");
1053 if (asyncIsBusy()) return false;
1054 return sendConfigCommandAsync(LD2410Defs::requestDistanceResolutionCommandData, callback);
1055}
1056
1058 LD2410Types::OutputControl outputControl,
1059 AsyncCommandCallback callback)
1060{
1062 DEBUG_PRINTLN("Set Aux Control Settings");
1063 if (asyncIsBusy()) return false;
1064
1066 if (!LD2410CommandBuilder::buildAuxControlCommand(cmd, lightControl, lightThreshold, outputControl)) return false;
1067
1068 return sendConfigCommandAsync(cmd, callback);
1069}
1070
1071
1072
1073bool LD2410Async::requestAuxControlSettingsAsync(AsyncCommandCallback callback) {
1075 DEBUG_PRINTLN("Request Aux Control Settings");
1076
1077 if (asyncIsBusy()) return false;
1078
1079 return sendConfigCommandAsync(LD2410Defs::requestAuxControlSettingsCommandData, callback);
1080}
1081
1082
1083bool LD2410Async::beginAutoConfigAsync(AsyncCommandCallback callback) {
1085 DEBUG_PRINTLN("Begin auto config");
1086
1087 if (asyncIsBusy()) return false;
1088
1089 return sendConfigCommandAsync(LD2410Defs::beginAutoConfigCommandData, callback);
1090};
1091
1092bool LD2410Async::requestAutoConfigStatusAsync(AsyncCommandCallback callback) {
1094 DEBUG_PRINTLN("Reqtest auto config status");
1095
1096 if (asyncIsBusy()) return false;
1097
1098 return sendConfigCommandAsync(LD2410Defs::requestAutoConfigStatusCommandData, callback);
1099}
1100/**********************************************************************************
1101* High level commands that combine several native commands
1102***********************************************************************************/
1103// It is recommend to always use these commands if feasible,
1104// since they streamline the inconsistencies in the native requesr and config commands
1105// and reduce the total number of commands that have to be called.
1106
1107
1108bool LD2410Async::requestAllStaticDataAsync(AsyncCommandCallback callback) {
1110 DEBUG_PRINTLN("Request all static data");
1111
1112 if (asyncIsBusy()) return false;
1113
1114
1115 if (!resetCommandSequence()) return false;
1116
1117 if (!addCommandToSequence(LD2410Defs::requestFirmwareCommandData)) return false;
1118 if (!addCommandToSequence(LD2410Defs::requestMacAddressCommandData)) return false;
1119
1120 return executeCommandSequenceAsync(callback);
1121}
1122
1123bool LD2410Async::requestAllConfigSettingsAsync(AsyncCommandCallback callback) {
1125 DEBUG_PRINTLN("Request all config data");
1126
1127 if (asyncIsBusy()) return false;
1128
1129
1130
1131 if (!resetCommandSequence()) return false;
1132 if (!addCommandToSequence(LD2410Defs::requestDistanceResolutionCommandData)) return false;
1133 if (!addCommandToSequence(LD2410Defs::requestParamsCommandData)) return false;
1134 if (!addCommandToSequence(LD2410Defs::requestAuxControlSettingsCommandData)) return false;
1135
1137 return executeCommandSequenceAsync(callback);
1138
1139}
1140// ----------------------------------------------------------------------------------
1141// - Set config data async methods
1142// ----------------------------------------------------------------------------------
1143// The command to set all config values on the sensor is the most complex command internally.
1144// It uses a first command sequences to get the current sensor config, then checks what
1145// actually needs to be changed and then creates a second command sequence to do the needed changes.
1146
1147void LD2410Async::configureAllConfigSettingsAsyncExecuteCallback(LD2410Async::AsyncCommandResult result) {
1148 AsyncCommandCallback cb = configureAllConfigSettingsAsyncConfigCallback;
1149 configureAllConfigSettingsAsyncConfigCallback = nullptr;
1150 configureAllConfigSettingsAsyncConfigActive = false;
1151
1153 DEBUG_PRINT("configureAllConfigSettingsAsync complete. Result: ");
1154 DEBUG_PRINTLN((byte)result);
1155
1156 if (cb) {
1157 cb(this, result);
1158 }
1159
1160}
1161
1162void LD2410Async::configureAllConfigSettingsAsyncConfigModeDisabledCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1165 DEBUG_PRINTLN("Warning: Disabling config mode after configureAllConfigSettingsAsync failed. Result: ");
1166 DEBUG_PRINTLN((byte)result);
1167 sender->configureAllConfigSettingsAsyncExecuteCallback(result);
1168 }
1169 else {
1170 // Config mode disabled successfully
1172 DEBUG_PRINTLN("Config mode disabled, call the callback");
1173
1174 sender->configureAllConfigSettingsAsyncExecuteCallback(sender->configureAllConfigSettingsAsyncResultToReport);
1175 }
1176}
1177
1178void LD2410Async::configureAllConfigSettingsAsyncFinalize(LD2410Async::AsyncCommandResult resultToReport) {
1179
1180 if (!configureAllConfigSettingsAsyncConfigInitialConfigMode) {
1182 DEBUG_PRINTLN("Config mode was not enabled initially, disable it.");
1183 configureAllConfigSettingsAsyncResultToReport = resultToReport;
1184
1185 if (!disableConfigModeInternalAsync(configureAllConfigSettingsAsyncConfigModeDisabledCallback)) {
1187 DEBUG_PRINTLN("Error: Disabling config mode after configureAllConfigSettingsAsync failed.");
1188 configureAllConfigSettingsAsyncExecuteCallback(LD2410Async::AsyncCommandResult::FAILED);
1189 }
1190 }
1191 else {
1193 DEBUG_PRINTLN("Config mode was enabled initially, no need to disable it, just execute the callback.");
1194
1195 configureAllConfigSettingsAsyncExecuteCallback(resultToReport);
1196 }
1197}
1198
1199
1200void LD2410Async::configureAllConfigSettingsAsyncWriteConfigCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1201 if (AsyncCommandResult::SUCCESS != result) {
1203 DEBUG_PRINTLN("Error: Writing config data to sensor failed.");
1204 };
1205 // Pass result to finalize method, which will also disable config mode if needed
1206 sender->configureAllConfigSettingsAsyncFinalize(result);
1207}
1208
1209
1210bool LD2410Async::configureAllConfigSettingsAsyncBuildSaveChangesCommandSequence() {
1211 // Get a clone of the current config, so it does not get changed while we are working
1213
1214 if (!resetCommandSequence()) {
1216 DEBUG_PRINTLN("Error: Could not reset command sequence.");
1217 return false;
1218 };
1219 // 1. Max gate + no one timeout
1220 if (configureAllConfigSettingsAsyncWriteFullConfig
1221 || currentConfig.maxMotionDistanceGate != configureAllConfigSettingsAsyncConfigDataToWrite.maxMotionDistanceGate
1222 || currentConfig.maxStationaryDistanceGate != configureAllConfigSettingsAsyncConfigDataToWrite.maxStationaryDistanceGate
1223 || currentConfig.noOneTimeout != configureAllConfigSettingsAsyncConfigDataToWrite.noOneTimeout) {
1224 byte cmd[sizeof(LD2410Defs::maxGateCommandData)];
1226 configureAllConfigSettingsAsyncConfigDataToWrite.maxMotionDistanceGate,
1227 configureAllConfigSettingsAsyncConfigDataToWrite.maxStationaryDistanceGate,
1228 configureAllConfigSettingsAsyncConfigDataToWrite.noOneTimeout)) {
1230 DEBUG_PRINTLN("Error: Building max gate command failed.");
1231 return false;
1232 };
1233 if (!addCommandToSequence(cmd)) {
1235 DEBUG_PRINTLN("Error: Adding max gate command to sequence failed.");
1236 return false;
1237 };
1239 DEBUG_PRINTLN("Max gate command added to sequence.");
1240 }
1241 // 2. Gate sensitivities (sequence of commands)
1242 for (byte gate = 0; gate < 9; gate++) {
1243 if (configureAllConfigSettingsAsyncWriteFullConfig
1244 || currentConfig.distanceGateMotionSensitivity[gate] != configureAllConfigSettingsAsyncConfigDataToWrite.distanceGateMotionSensitivity[gate]
1245 || currentConfig.distanceGateStationarySensitivity[gate] != configureAllConfigSettingsAsyncConfigDataToWrite.distanceGateStationarySensitivity[gate]) {
1248 configureAllConfigSettingsAsyncConfigDataToWrite.distanceGateMotionSensitivity[gate],
1249 configureAllConfigSettingsAsyncConfigDataToWrite.distanceGateStationarySensitivity[gate])) {
1251 DEBUG_PRINT("Error: Error building gate sensitivity command for gate ");
1252 DEBUG_PRINTLN(gate);
1253
1254 return false;
1255 };
1256 if (!addCommandToSequence(cmd)) {
1258 DEBUG_PRINT("Error: Adding gate sensitivity command for gate ");
1259 DEBUG_PRINT(gate);
1260 DEBUG_PRINTLN(" to sequence failed.");
1261
1262 return false;
1263 }
1265 DEBUG_PRINT("Gate sensitivity command for gate ");
1266 DEBUG_PRINT(gate);
1267 DEBUG_PRINTLN(" added to sequence.");
1268 }
1269 }
1270
1271 //3. Distance resolution
1272 if (configureAllConfigSettingsAsyncWriteFullConfig
1273 || currentConfig.distanceResolution != configureAllConfigSettingsAsyncConfigDataToWrite.distanceResolution) {
1274 byte cmd[6]; // resolution commands are 6 bytes long
1275 if (!LD2410CommandBuilder::buildDistanceResolutionCommand(cmd, configureAllConfigSettingsAsyncConfigDataToWrite.distanceResolution)) {
1277 DEBUG_PRINTLN("Error: Building distance resolution command failed.");
1278 return false;
1279 };
1280 if (!addCommandToSequence(cmd)) {
1282 DEBUG_PRINTLN("Error: Adding distance resolution command to sequence failed.");
1283 return false;
1284 };
1286 DEBUG_PRINTLN("Distance resolution command added to sequence.");
1287 };
1288
1289 //4. Aux control settings
1290 if (configureAllConfigSettingsAsyncWriteFullConfig
1291 || currentConfig.lightControl != configureAllConfigSettingsAsyncConfigDataToWrite.lightControl
1292 || currentConfig.lightThreshold != configureAllConfigSettingsAsyncConfigDataToWrite.lightThreshold
1293 || currentConfig.outputControl != configureAllConfigSettingsAsyncConfigDataToWrite.outputControl) {
1296 configureAllConfigSettingsAsyncConfigDataToWrite.lightControl,
1297 configureAllConfigSettingsAsyncConfigDataToWrite.lightThreshold,
1298 configureAllConfigSettingsAsyncConfigDataToWrite.outputControl)) {
1300 DEBUG_PRINTLN("Error: Building aux control command failed.");
1301 return false;
1302 };
1303 if (!addCommandToSequence(cmd)) {
1305 DEBUG_PRINTLN("Error: Adding aux control command to sequence failed.");
1306 return false;
1307 };
1309 DEBUG_PRINTLN("Aux control command added to sequence.");
1310 };
1311 return true;
1312};
1313
1314bool LD2410Async::configureAllConfigSettingsAsyncWriteConfig() {
1315
1316 if (!configureAllConfigSettingsAsyncBuildSaveChangesCommandSequence()) {
1317 // Could not build command sequence
1319 DEBUG_PRINTLN("Could not build the command sequence to save the config data");
1320 return false;
1321 }
1322
1323 if (commandSequenceBufferCount == 0) {
1325 DEBUG_PRINTLN("No config changes detected, no need to write anything");
1326 }
1327
1328 if (!executeCommandSequenceAsync(configureAllConfigSettingsAsyncWriteConfigCallback)) {
1330 DEBUG_PRINTLN("Error: Starting command sequence to write config data failed.");
1331
1332 return false;
1333 }
1334 return true;
1335
1336};
1337
1338void LD2410Async::configureAllConfigSettingsAsyncRequestAllConfigDataCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1341 DEBUG_PRINTLN("Error: Requesting current config data failed. Result: ");
1342 DEBUG_PRINTLN((byte)result);
1343 sender->configureAllConfigSettingsAsyncFinalize(result);
1344 return;
1345 }
1346 // Received current config data, now write the changed values
1348 DEBUG_PRINTLN("Current config data received.");
1349
1350 if (!sender->configureAllConfigSettingsAsyncWriteConfig()) {
1352 DEBUG_PRINTLN("Error: Starting to save config data changes failed.");
1353 sender->configureAllConfigSettingsAsyncFinalize(AsyncCommandResult::FAILED);
1354 }
1355}
1356
1357bool LD2410Async::configureAllConfigSettingsAsyncRequestAllConfigData() {
1358 if (resetCommandSequence()
1360 && addCommandToSequence(LD2410Defs::requestParamsCommandData)
1361 && addCommandToSequence(LD2410Defs::requestAuxControlSettingsCommandData))
1362 {
1363 if (executeCommandSequenceAsync(configureAllConfigSettingsAsyncRequestAllConfigDataCallback)) {
1365 DEBUG_PRINTLN("Requesting current config data");
1366 return true;
1367 }
1368 else {
1370 DEBUG_PRINTLN("Error: Starting command sequence to request current config data failed.");
1371 }
1372 }
1373 return false;
1374}
1375
1376void LD2410Async::configureAllConfigSettingsAsyncConfigModeEnabledCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1377 if (result != AsyncCommandResult::SUCCESS) {
1378
1380 DEBUG_PRINTLN("Error: Enabling config mode failed. Result: ");
1381 DEBUG_PRINTLN((byte)result);
1382 sender->configureAllConfigSettingsAsyncFinalize(result);
1383 return;
1384 };
1385
1386 // Received ACK for enable config mode command
1388 DEBUG_PRINTLN("Config mode enabled.");
1389
1390 bool ret = false;
1391 if (sender->configureAllConfigSettingsAsyncWriteFullConfig) {
1392 // If we save all changes anyway, no need to request current config data first
1394 DEBUG_PRINTLN("Saving all data is forced, save directly");
1395 ret = sender->configureAllConfigSettingsAsyncWriteConfig();
1396
1397 }
1398 else {
1400 DEBUG_PRINTLN("Requesting current config data");
1401 ret = sender->configureAllConfigSettingsAsyncRequestAllConfigData();
1402 }
1403 if (!ret) {
1405 DEBUG_PRINTLN("Error: Starting config data write or request of current config data failed.");
1406 sender->configureAllConfigSettingsAsyncFinalize(AsyncCommandResult::FAILED);
1407 }
1408
1409
1410}
1411
1412bool LD2410Async::configureAllConfigSettingsAsync(const LD2410Types::ConfigData& configToWrite, bool writeAllConfigData, AsyncCommandCallback callback)
1413{
1414
1416 DEBUG_PRINTLN("Writing config data to the LD2410");
1417
1418 if (asyncIsBusy()) return false;
1419
1420
1421 if (!configToWrite.isValid()) {
1423 DEBUG_PRINTLN("configToWrite is invalid.");
1424 return false;
1425 }
1426
1427 configureAllConfigSettingsAsyncConfigActive = true;
1428 configureAllConfigSettingsAsyncConfigDataToWrite = configToWrite;
1429 configureAllConfigSettingsAsyncWriteFullConfig = writeAllConfigData;
1430 configureAllConfigSettingsAsyncConfigCallback = callback;
1431 configureAllConfigSettingsAsyncConfigInitialConfigMode = isConfigModeEnabled();
1432
1433 if (!configureAllConfigSettingsAsyncConfigInitialConfigMode) {
1435 DEBUG_PRINTLN("Enable the config mode");
1436 return enableConfigModeInternalAsync(configureAllConfigSettingsAsyncConfigModeEnabledCallback);
1437 }
1438 else {
1439 if (configureAllConfigSettingsAsyncWriteFullConfig) {
1440 // If we save all changes anyway, no need to request current config data first
1442 DEBUG_PRINTLN("Saving all data is forced and config mode is enabled -> save directly");
1443 return configureAllConfigSettingsAsyncWriteConfig();
1444 }
1445 else {
1447 DEBUG_PRINTLN("Config mode is already enabled, just request all config data");
1448 return configureAllConfigSettingsAsyncRequestAllConfigData();
1449 }
1450 }
1451
1452}
1453
1454
1455/*--------------------------------------------------------------------
1456- Reboot command
1457---------------------------------------------------------------------*/
1458// The reboot command is special, since it needs to be sent in config mode,
1459// but doesn't have to disable config mode, since the sensor goes into normal
1460// detection mode after the reboot anyway.
1461
1462void LD2410Async::rebootAsyncFinialize(LD2410Async::AsyncCommandResult result) {
1463 if (rebootAsyncPending) {
1464 rebootAsyncPending = false;
1465 executeCommandSequenceOnceTicker.detach();
1467 DEBUG_PRINTLN("Reboot completed");
1468 executeCommandSequenceAsyncExecuteCallback(result);
1469 }
1470}
1471
1472void LD2410Async::rebootAsyncRebootCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1474
1475 // Not necessary, since the first data frame we receive after the reboot will set these variables back at the right point in time.
1476 // If this does not happen, we are in trouble or likely stuck in config mode anyway.
1477 // sender->configModeEnabled = false;
1478 // sender->engineeringModeEnabled = false;
1479
1481 DEBUG_PRINTLN("Reboot initiated");
1482 if (sender->rebootAsyncDontWaitForNormalOperationAfterReboot || sender->rebootAsyncWaitTimeoutMs == 0) {
1484 DEBUG_PRINTLN("Triggering the reboot callback directly since the parameters of the method want us not to wait until normal operation resumes.");
1485 sender->rebootAsyncFinialize(result);
1486 }
1487 else {
1488 if (sender->rebootAsyncPending) {
1490 DEBUG_PRINT("Will be waiting ");
1491 DEBUG_PRINT(sender->rebootAsyncWaitTimeoutMs);
1492 DEBUG_PRINTLN(" ms for normal operation to resume.");
1493 sender->executeCommandSequenceOnceTicker.once_ms(sender->rebootAsyncWaitTimeoutMs, [sender]() {
1494 DEBUG_PRINT_MILLIS;
1495 DEBUG_PRINTLN("Timeout period while waiting for normal operation to resume has elapsed.");
1496 sender->rebootAsyncFinialize(LD2410Async::AsyncCommandResult::TIMEOUT);
1497 });
1498 }
1499 else {
1501 DEBUG_PRINTLN("It seems that reboot has already been finalized (maybe the callback from processData has already been triggered)");
1502 }
1503 }
1504 }
1505 else {
1507 DEBUG_PRINT("Error! Could not initiate reboot. Result: ");
1508 DEBUG_PRINTLN((int)result);
1509 sender->rebootAsyncFinialize(result);
1510 }
1511
1512}
1513
1514void LD2410Async::rebootAsyncEnableConfigModeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1516 // Received ACK for enable config mode command
1518 DEBUG_PRINTLN("Config mode enabled before reboot");
1519 sender->sendCommandAsync(LD2410Defs::rebootCommandData, rebootAsyncRebootCallback);
1520 }
1521 else {
1522 // Config mode command timeout or canceled
1523 // Just execute the callback
1525 DEBUG_PRINTLN("Error! Could not enabled config mode before reboot");
1526 sender->rebootAsyncFinialize(result);
1527 }
1528}
1529
1530
1531bool LD2410Async::rebootAsync(bool dontWaitForNormalOperationAfterReboot, AsyncCommandCallback callback) {
1532
1533
1535 DEBUG_PRINTLN("Reboot");
1536
1537 if (asyncIsBusy()) return false;
1538
1539 //"Missusing" the variables for the command sequence
1540 executeCommandSequencePending = true;
1541 executeCommandSequenceCallback = callback;
1542 executeCommandSequenceInitialConfigModeState = configModeEnabled;
1543 executeCommandSequenceStartMs = millis();
1544 rebootAsyncDontWaitForNormalOperationAfterReboot = dontWaitForNormalOperationAfterReboot;
1545
1546 // Force config mode (just to be sure)
1547 rebootAsyncPending = enableConfigModeInternalAsync(true, rebootAsyncEnableConfigModeCallback);
1548
1549 return rebootAsyncPending;
1550
1551}
1552
1553/**********************************************************************************
1554* Data access
1555***********************************************************************************/
1559
1563
1564/**********************************************************************************
1565* Rest config data
1566***********************************************************************************/
1567// Resets the values of configdata to their initial value
1571
1572
1573/**********************************************************************************
1574* Inactivity handling
1575***********************************************************************************/
1576void LD2410Async::handleInactivityRebootCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1577 sender->configModeEnabled = false;
1578 sender->engineeringModeEnabled = false;
1579
1580#if (LD2410ASYNC_DEBUG_LEVEL > 0)
1581 if (result == AsyncCommandResult::SUCCESS) {
1583 DEBUG_PRINTLN("LD2410 reboot due to inactivity initiated");
1584 }
1585 else {
1587 DEBUG_PRINT("Error!! Could not initiate LD2410 reboot. Result: ");
1588 DEBUG_PRINTLN((int)result);
1589 }
1590
1591#endif
1592
1593}
1594
1595void LD2410Async::handleInactivityDisableConfigmodeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result) {
1596
1597
1598#if (LD2410ASYNC_DEBUG_LEVEL > 0)
1599 if (result == AsyncCommandResult::SUCCESS) {
1601 DEBUG_PRINTLN("Config mode disabled due to inactivity");
1602 }
1603 else {
1605 DEBUG_PRINT("Error!! Disabling config mode after inactivity failed. Result: ");
1606 DEBUG_PRINTLN((int)result);
1607 }
1608
1609#endif
1610
1611}
1612
1613
1614
1615void LD2410Async::handleInactivity() {
1616
1617 if (inactivityHandlingEnabled && inactivityHandlingTimeoutMs > 0) {
1618 unsigned long timeoutToUse = inactivityHandlingTimeoutMs;
1619 if (timeoutToUse < asyncCommandTimeoutMs + 1000) {
1620 timeoutToUse = asyncCommandTimeoutMs + 1000;
1621 }
1622 unsigned long currentTime = millis();
1623 unsigned long inactiveDurationMs = currentTime - lastSensorActivityTimestamp;
1624 if (lastSensorActivityTimestamp != 0 && inactiveDurationMs > timeoutToUse) {
1625 if (inactivityHandlingStep == 0 || currentTime - lastInactivityHandlingTimestamp > asyncCommandTimeoutMs + 1000) {
1626 lastInactivityHandlingTimestamp = currentTime;
1627
1628 switch (inactivityHandlingStep++) {
1629 case 0:
1631 DEBUG_PRINTLN("Inactivity handling cancels pending async operations");
1632 asyncCancel();
1633 break;
1634 case 1:
1636 DEBUG_PRINTLN("Inactivity handling tries to force disable config mode");
1637 // We don't care about the result or a callback, if the command has the desired effect, fine; otherwise we will try to reboot the sensor anyway
1638 disableConfigModeInternalAsync(true, nullptr);
1639 break;
1640 case 3:
1642 DEBUG_PRINTLN("Inactivity handling reboots the sensor");
1643 // We don't care about the result, if the command has the desired effect, fine; otherwise we will try to reboot the sensor anyway
1644 rebootAsync(true, nullptr);
1645
1646 break;
1647 default:
1649 DEBUG_PRINTLN("Inactivity handling could not revert sensor to normal operation. Reset the inactivity timeout and try again after the configured inactivity period.");
1650 // Inactivity handling has tried everything it can do, so reset the inactivity handling steps and call heartbeat. This will ensure inactivity handling will get called again after the inactivity handling period
1651 inactivityHandlingStep = 0;
1652 heartbeat();
1653 break;
1654 }
1655 }
1656 }
1657 else {
1658 inactivityHandlingStep = 0;
1659 }
1660 }
1661}
1662
1663void LD2410Async::heartbeat() {
1664 lastSensorActivityTimestamp = millis();
1665}
1666
1668 inactivityHandlingEnabled = enable;
1669}
1670
1671/**********************************************************************************
1672* Process received data
1673***********************************************************************************/
1674void LD2410Async::processReceivedData()
1675{
1676 FrameReadResponse type = readFrame();
1677
1678 switch (type) {
1679 case ACK:
1680 processAck();
1681 break;
1682 case DATA:
1683 processData();
1684 break;
1685 default:
1686 break;
1687 }
1688
1689
1690}
1691
1692/**********************************************************************************
1693* Task methods
1694***********************************************************************************/
1695
1696void LD2410Async::taskLoop() {
1698 DEBUG_PRINTLN("Task loop starts");
1699
1700 while (!taskStop) {
1701 processReceivedData();
1702 sendCommandAsyncHandleTimeout();
1703 handleInactivity();
1704 vTaskDelay(10 / portTICK_PERIOD_MS);
1705
1706 }
1707
1709 DEBUG_PRINT(taskStop);
1710 DEBUG_PRINTLN("Task loop exits");
1711
1712 // Signal that the task is done
1713 taskHandle = NULL;
1714 vTaskDelete(NULL); // self-delete
1715}
1716
1717
1718
1719/**********************************************************************************
1720* Begin, end
1721***********************************************************************************/
1722
1724 if (taskHandle == NULL) {
1726 DEBUG_PRINTLN("Starting data processing task");
1727 taskStop = false;
1728
1729 BaseType_t result = xTaskCreate(
1730 [](void* param) {
1731 if (param) {
1732 static_cast<LD2410Async*>(param)->taskLoop();
1733 }
1734 vTaskDelete(NULL);
1735 },
1736 "LD2410Task",
1737 4096,
1738 this,
1739 1,
1740 &taskHandle
1741 );
1742
1743 if (result == pdPASS) {
1744 return true;
1745 }
1746 else {
1748 DEBUG_PRINTLN("Task creation failed");
1749 taskHandle = NULL;
1750 return false;
1751 }
1752 }
1754 DEBUG_PRINTLN("Data processing task already active");
1755 return false;
1756}
1757
1759 if (taskHandle != NULL) {
1761 DEBUG_PRINTLN("Stopping data processing task");
1762 taskStop = true;
1763
1764 // Wait up to 200ms for graceful exit
1765 for (int i = 0; i < 20; i++) {
1766 if (taskHandle == NULL) {
1768 DEBUG_PRINTLN("Task exited gracefully");
1769 return true;
1770 }
1771 vTaskDelay(1 / portTICK_PERIOD_MS);
1772 }
1773
1774 // If still not NULL, force delete
1776 DEBUG_PRINTLN("Forcing task stop");
1777 vTaskDelete(taskHandle);
1778 taskHandle = NULL;
1779 return true;
1780 }
1781
1782 DEBUG_PRINTLN("Data processing task is not active");
1783 return false;
1784}
1785
1786
1787
1788
1789/**********************************************************************************
1790* Constructor
1791***********************************************************************************/
1793{
1794 sensor = &serial;
1795}
String byte2hex(byte b, bool addZero=true)
bool bufferEndsWith(const byte *buffer, int iMax, const byte *pattern)
Checks whether the last 4 bytes of a buffer match the value supplied in pattern.
#define DEBUG_PRINTBUF_DATA(...)
Definition LD2410Debug.h:70
#define DEBUG_PRINT_MILLIS
Definition LD2410Debug.h:50
#define DEBUG_PRINTLN_DATA(...)
Definition LD2410Debug.h:69
#define DEBUG_PRINT(...)
Definition LD2410Debug.h:51
#define DEBUG_PRINTBUF(...)
Definition LD2410Debug.h:53
#define DEBUG_PRINTLN(...)
Definition LD2410Debug.h:52
Asynchronous driver class for the LD2410 human presence radar sensor.
Definition LD2410Async.h:38
bool configureAuxControlSettingsAsync(LD2410Types::LightControl light_control, byte light_threshold, LD2410Types::OutputControl output_control, AsyncCommandCallback callback)
Configures the auxiliary control parameters (light and output pin).
bool requestGateParametersAsync(AsyncCommandCallback callback)
Requests the current gate parameters from the sensor.
void asyncCancel()
Cancels any pending asynchronous command or sequence.
bool configureDistanceResolutionAsync(LD2410Types::DistanceResolution distanceResolution, AsyncCommandCallback callback)
Configures the distance resolution of the radar.
bool rebootAsync(AsyncCommandCallback callback)
Reboots the sensor.
LD2410Types::StaticData staticData
Static data of the radar.
bool begin()
Starts the background task that continuously reads data from the sensor.
AsyncCommandResult
Result of an asynchronous command execution.
Definition LD2410Async.h:47
@ TIMEOUT
No ACK received within the expected time window.
@ FAILED
Command failed (sensor responded with negative ACK).
@ SUCCESS
Command completed successfully and ACK was received.
@ CANCELED
Command was canceled by the user before completion.
bool configureMaxGateAndNoOneTimeoutAsync(byte maxMovingGate, byte maxStationaryGate, unsigned short noOneTimeout, AsyncCommandCallback callback)
Configures the maximum detection gates and "no-one" timeout on the sensor.
bool requestAllStaticDataAsync(AsyncCommandCallback callback)
Requests all static information from the sensor.
bool restoreFactorySettingsAsync(AsyncCommandCallback callback)
Restores factory settings of the sensor.
void resetConfigData()
bool disableConfigModeAsync(AsyncCommandCallback callback)
Disables config mode on the radar.
bool isConfigModeEnabled() const
Detects if config mode is enabled.
void(*) AsyncCommandCallback(LD2410Async *sender, AsyncCommandResult result)
Callback signature for asynchronous command completion.
Definition LD2410Async.h:64
bool configureAllConfigSettingsAsync(const LD2410Types::ConfigData &configToWrite, bool writeAllConfigData, AsyncCommandCallback callback)
Applies a full ConfigData struct to the LD2410.
bool enableConfigModeAsync(AsyncCommandCallback callback)
Enables config mode on the radar.
bool disableBluetoothAsync(AsyncCommandCallback callback)
Disables Bluetooth.
void setInactivityHandling(bool enable)
Enables or disables automatic inactivity handling of the sensor.
void onConfigDataReceived(GenericCallback callback)
Registers a callback for configuration data updates.
LD2410Types::ConfigData getConfigData() const
Returns a clone of the current configuration data of the radar.
LD2410Types::DetectionData getDetectionData() const
Returns a clone of the latest detection data from the radar.
bool configureDistanceResolution75cmAsync(AsyncCommandCallback callback)
Configures the distance resolution explicitly to 75 cm per gate.
LD2410Types::ConfigData configData
Current configuration parameters of the radar.
bool requestDistanceResolutionAsync(AsyncCommandCallback callback)
Requests the current distance resolution setting from the sensor.
bool asyncIsBusy()
Checks if an asynchronous command is currently pending.
bool configModeEnabled
True if the sensor is currently in config mode.
bool configureBluetoothPasswordAsync(const char *password, AsyncCommandCallback callback)
Sets the password for Bluetooth access to the sensor.
void onConfigChanged(GenericCallback callback)
Registers a callback that is invoked whenever the sensor's configuration has been changed successfull...
bool requestFirmwareAsync(AsyncCommandCallback callback)
Requests the firmware version of the sensor.
bool requestBluetoothMacAddressAsync(AsyncCommandCallback callback)
Requests the Bluetooth MAC address.
LD2410Types::DetectionData detectionData
Latest detection results from the radar.
LD2410Types::AutoConfigStatus autoConfigStatus
Current status of the auto-configuration routine.
bool requestAllConfigSettingsAsync(AsyncCommandCallback callback)
Requests all configuration settings from the sensor.
bool engineeringModeEnabled
True if the sensor is currently in engineering mode.
bool configureDefaultBluetoothPasswordAsync(AsyncCommandCallback callback)
Resets the password for Bluetooth access to the default value (HiLink)
bool configureDistanceGateSensitivityAsync(const byte movingThresholds[9], const byte stationaryThresholds[9], AsyncCommandCallback callback)
Configures sensitivity thresholds for all gates at once.
LD2410Async(Stream &serial)
Constructs a new LD2410Async instance bound to a given serial stream.
bool beginAutoConfigAsync(AsyncCommandCallback callback)
Starts the automatic configuration (auto-config) routine on the sensor.
bool requestAuxControlSettingsAsync(AsyncCommandCallback callback)
Requests the current auxiliary control settings.
void onDetectionDataReceived(DetectionDataCallback callback)
Registers a callback for new detection data.
bool enableEngineeringModeAsync(AsyncCommandCallback callback)
Enables engineering mode.
bool disableEngineeringModeAsync(AsyncCommandCallback callback)
Disables engineering mode.
bool configureBaudRateAsync(byte baudRateSetting, AsyncCommandCallback callback)
Configures the UART baud rate of the sensor.
bool requestAutoConfigStatusAsync(AsyncCommandCallback callback)
Requests the current status of the auto-config routine.
bool end()
Stops the background task started by begin().
bool configuresDistanceResolution20cmAsync(AsyncCommandCallback callback)
Configures the distance resolution explicitly to 20 cm per gate.
bool enableBluetoothAsync(AsyncCommandCallback callback)
Enables Bluetooth.
bool buildMaxGateCommand(byte *out, byte maxMotionGate, byte maxStationaryGate, unsigned short noOneTimeout)
bool buildDistanceResolutionCommand(byte *out, LD2410Types::DistanceResolution resolution)
bool buildAuxControlCommand(byte *out, LD2410Types::LightControl lightControl, byte lightThreshold, LD2410Types::OutputControl outputControl)
bool buildGateSensitivityCommand(byte *out, byte gate, byte movingThreshold, byte stationaryThreshold)
bool buildBluetoothPasswordCommand(byte *out, const char *password)
bool buildBaudRateCommand(byte *out, byte baudRateSetting)
constexpr byte requestAutoConfigStatusCommandData[4]
Definition LD2410Defs.h:74
constexpr byte setAuxControlSettingCommandData[8]
Definition LD2410Defs.h:68
constexpr byte configDisableCommand
Definition LD2410Defs.h:21
constexpr byte requestDistanceResolutionCommandData[4]
Definition LD2410Defs.h:31
constexpr byte setDistanceResolutionCommand
Definition LD2410Defs.h:33
constexpr byte configEnableCommandData[6]
Definition LD2410Defs.h:19
constexpr byte engineeringModeDisableComand
Definition LD2410Defs.h:61
constexpr byte requestMacAddressCommandData[6]
Definition LD2410Defs.h:25
constexpr byte setAuxControlSettingsCommand
Definition LD2410Defs.h:67
constexpr byte distanceGateSensitivityConfigCommand
Definition LD2410Defs.h:76
constexpr byte bluetoothSettingsCommand
Definition LD2410Defs.h:46
constexpr byte restoreFactorSettingsCommandData[4]
Definition LD2410Defs.h:41
constexpr byte requestAuxControlSettingsCommandData[4]
Definition LD2410Defs.h:65
constexpr byte distanceGateSensitivityConfigCommandData[0x16]
Definition LD2410Defs.h:77
constexpr byte setBluetoothPasswordCommandData[10]
Definition LD2410Defs.h:53
constexpr byte setDistanceResolution20cmCommandData[6]
Definition LD2410Defs.h:35
constexpr byte maxGateCommandData[0x16]
Definition LD2410Defs.h:83
constexpr byte setBaudRateCommand
Definition LD2410Defs.h:37
constexpr byte tailConfig[4]
Definition LD2410Defs.h:16
constexpr byte headConfig[4]
Definition LD2410Defs.h:15
constexpr byte setBluetoothPasswordCommand
Definition LD2410Defs.h:52
constexpr byte requestAutoConfigStatusCommand
Definition LD2410Defs.h:73
constexpr byte engineeringModeDisableCommandData[4]
Definition LD2410Defs.h:62
constexpr byte rebootCommandData[4]
Definition LD2410Defs.h:44
constexpr byte requestAuxControlSettingsCommand
Definition LD2410Defs.h:64
constexpr byte setBaudRateCommandData[6]
Definition LD2410Defs.h:38
constexpr byte restoreFactorySettingsAsyncCommand
Definition LD2410Defs.h:40
constexpr byte bluetoothSettingsOnCommandData[6]
Definition LD2410Defs.h:47
constexpr byte requestParamsCommand
Definition LD2410Defs.h:55
constexpr byte requestMacAddressCommand
Definition LD2410Defs.h:24
constexpr byte headData[4]
Definition LD2410Defs.h:13
constexpr byte setDistanceResolution75cmCommandData[6]
Definition LD2410Defs.h:34
constexpr byte rebootCommand
Definition LD2410Defs.h:43
constexpr byte requestFirmwareCommand
Definition LD2410Defs.h:27
constexpr byte beginAutoConfigCommandData[6]
Definition LD2410Defs.h:71
constexpr size_t LD2410_Buffer_Size
Definition LD2410Defs.h:11
constexpr byte maxGateCommand
Definition LD2410Defs.h:82
constexpr byte beginAutoConfigCommand
Definition LD2410Defs.h:70
constexpr byte configDisableCommandData[4]
Definition LD2410Defs.h:22
constexpr byte getBluetoothPermissionsCommand
Definition LD2410Defs.h:50
constexpr byte configEnableCommand
Definition LD2410Defs.h:18
constexpr byte tailData[4]
Definition LD2410Defs.h:14
constexpr byte requestFirmwareCommandData[4]
Definition LD2410Defs.h:28
constexpr byte engineeringModeEnableCommandData[4]
Definition LD2410Defs.h:59
constexpr byte engineeringModeEnableComand
Definition LD2410Defs.h:58
constexpr byte requestDistanceResolutionCommand
Definition LD2410Defs.h:30
constexpr byte requestParamsCommandData[4]
Definition LD2410Defs.h:56
OutputControl
Logic level behavior of the auxiliary output pin.
DistanceResolution
Distance resolution per gate for detection.
@ MOVING_AND_STATIONARY_TARGET
Both moving and stationary targets detected.
@ STATIONARY_TARGET
A stationary target has been detected.
@ MOVING_TARGET
A moving target has been detected.
LightControl
Light-dependent control status of the auxiliary output.
Definition LD2410Types.h:93
byte distanceGateStationarySensitivity[9]
Stationary sensitivity values per gate (0-100).
byte distanceGateMotionSensitivity[9]
Motion sensitivity values per gate (0-100).
byte maxMotionDistanceGate
Furthest gate used for motion detection.
OutputControl outputControl
Logic configuration of the OUT pin.
byte lightThreshold
Threshold for auxiliary light control (0-255).
bool isValid() const
Validates the configuration data for correctness.
LightControl lightControl
Light-dependent auxiliary control mode.
byte maxStationaryDistanceGate
Furthest gate used for stationary detection.
byte numberOfGates
Number of distance gates (2-8). This member is read-only; changing its value will not influence the r...
DistanceResolution distanceResolution
Current distance resolution. A reboot is required to activate changes after configureAllConfigSetting...
unsigned short noOneTimeout
Timeout (seconds) until "no presence" is declared.
Holds the most recent detection data reported by the radar.
byte stationaryTargetGateSignals[9]
Per-gate signal strengths for stationary targets.
bool engineeringMode
True if engineering mode data was received.
bool stationaryPresenceDetected
True if a stationary target is detected.
byte stationaryTargetSignal
Signal strength (0-100) of the stationary target.
byte lightLevel
Reported ambient light level (0-255).
unsigned int detectedDistance
General detection distance (cm).
byte movingTargetGateSignalCount
Number of gates with moving target signals.
TargetState targetState
Current detection state.
byte movingTargetGateSignals[9]
Per-gate signal strengths for moving targets.
byte stationaryTargetGateSignalCount
Number of gates with stationary target signals.
bool presenceDetected
True if any target is detected.
byte movingTargetSignal
Signal strength (0-100) of the moving target.
bool outPinStatus
Current status of the OUT pin (true = high, false = low).
unsigned long timestamp
Timestamp (ms since boot) when this data was received.
bool movingPresenceDetected
True if a moving target is detected.
unsigned int movingTargetDistance
Distance (cm) to the nearest moving target.
unsigned int stationaryTargetDistance
Distance (cm) to the nearest stationary target.
char bluetoothMacText[18]
MAC address as a human-readable string (e.g. "AA:BB:CC:DD:EE:FF").
uint16_t protocolVersion
Protocol version reported by the radar.
char firmwareText[16]
Firmware version string of the radar.
uint16_t bufferSize
Buffer size reported by the radar protocol.