LD2410Async
Asynchronous Arduino ESP32 library for the LD2410 mmWave radar sensor
Loading...
Searching...
No Matches
LD2410Async.h
Go to the documentation of this file.
1#pragma once
2
3
4/**
5 * @brief Use the following defines to set the debug level for the library.
6 *
7 * @details
8 * LD2410ASYNC_DEBUG_LEVEL controls debug messages for commands and command responses/ACKs from the sensor.
9 * LD2410ASYNC_DEBUG_DATA_LEVEL controls debug messages for received detection data.
10 *
11 * For both defines, 0 turns off all debug messages, 1 enables the messages, and 2 enables additional output of the received data.
12 *
13 * @note
14 * Don't enable debug messages in production. They will clutter the compiled build with numerous print commands, increasing the build size
15 * and negatively impacting performance.
16 */
17#define LD2410ASYNC_DEBUG_LEVEL 0
18#define LD2410ASYNC_DEBUG_DATA_LEVEL 0
19
20
21#include "Arduino.h"
22#include "Ticker.h"
23#include "LD2410Debug.h"
24#include "LD2410Types.h"
25#include "LD2410Defs.h"
26
27
28
29/**
30 * @brief Asynchronous driver class for the LD2410 human presence radar sensor.
31 *
32 * @details This class provides a non-blocking interface for communicating with the LD2410 sensor,
33 * allowing for efficient data retrieval and configuration without halting the main program flow.
34 * It supports multiple instances and features automatic handling of communication timeouts and errors.
35 * It supports all native commands of the LD2410 and has several additional high level commands for more consistent access to the sensor.
36 *
37 */
39public:
40
41 /**
42 * @brief Result of an asynchronous command execution.
43 *
44 * Every async command reports back its outcome via the callback.
45 *
46 */
47 enum class AsyncCommandResult : byte {
48 SUCCESS, ///< Command completed successfully and ACK was received.
49 FAILED, ///< Command failed (sensor responded with negative ACK).
50 TIMEOUT, ///< No ACK received within the expected time window.
51 CANCELED ///< Command was canceled by the user before completion.
52 };
53
54
55
56
57 /**
58 * @brief Callback signature for asynchronous command completion.
59 *
60 * @param sender Pointer to the LD2410Async instance that triggered the callback.
61 * @param result Outcome of the async operation (SUCCESS, FAILED, TIMEOUT, CANCELED).
62 *
63 */
64 typedef void (*AsyncCommandCallback)(LD2410Async* sender, AsyncCommandResult result);
65
66 /**
67 * @brief Generic callback signature used for simple notifications.
68 *
69 * @param sender Pointer to the LD2410Async instance that triggered the callback.
70 *
71 *
72 */
73 typedef void (*GenericCallback)(LD2410Async* sender);
74
75 /**
76 * @brief Callback type for receiving detection data events.
77 *
78 * This callback is invoked whenever new detection data is processed.
79 * It provides direct access to the LD2410Async instance, along with
80 * a quick flag for presence detection so that applications which only
81 * care about presence can avoid parsing the full DetectionData struct.
82 *
83 * @param sender Pointer to the LD2410Async instance that triggered the callback.
84 * @param presenceDetected True if the radar currently detects presence (moving or stationary), false otherwise.
85 *
86 */
87 typedef void(*DetectionDataCallback)(LD2410Async* sender, bool presenceDetected);
88
89
90
91
92
93public:
94
95
96
97 /**
98 * @brief Latest detection results from the radar.
99 *
100 * @details Updated automatically whenever new data frames are received.
101 * Use onDetectionDataReceived() to be notified
102 * whenever this struct changes.
103 * Use getDetectionData() or getDetectionDataRef() to access the current values, rather than accessing the struct directly.
104 *
105 *
106 */
108
109 /**
110 * @brief Current configuration parameters of the radar.
111 *
112 * @details Filled when configuration query commands are issued
113 * (e.g. requestAllConfigSettingsAsync() or requestGateParametersAsync(), etc.).
114 * Use onConfigDataReceived() to be notified when data in this struct changes.
115 * Use getConfigData() or getConfigDataRef() to access the current values, rather than accessing the struct directly.
116 *
117 * The structure contains only uninitialized data if config data is not queried explicitly.
118 *
119 */
121
122 /**
123 * @brief Static data of the radar
124 *
125 * @details Filled when config mode is enabled (protocol version and buffer size)
126 * and when issuing query commands for the static data (requestAllStaticDataAsync(), requestFirmwareAsync(), requestBluetoothMacAddressAsync()).
127 */
129
130
131
132 /**
133 * @brief True if the sensor is currently in config mode.
134 *
135 * Config mode must be enabled using enableConfigModeAsync() before sending configuration commands.
136 * After sending config commands, always disable the config mode using disableConfigModeAsync(),
137 * otherwise the radar will not send any detection data.
138 *
139 */
140 bool configModeEnabled = false;
141
142
143 /**
144 * @brief True if the sensor is currently in engineering mode.
145 *
146 * In engineering mode, the radar sends detailed per-gate
147 * signal data in addition to basic detection data.
148 *
149 * Use enableEngineeringModeAsync() and disableEngineeringModeAsync() to control the engineering mode.
150 *
151 */
153
154
155
156
157 /**
158 * @brief Current status of the auto-configuration routine.
159 *
160 * Updated by requestAutoConfigStatusAsync().
161 *
162 */
164
165
166
167
168 /**********************************************************************************
169 * Constructor
170 ***********************************************************************************/
171
172 /**
173 * @brief Constructs a new LD2410Async instance bound to a given serial stream.
174 *
175 * The sensor communicates over a UART interface. Pass the corresponding
176 * Stream object (e.g. HardwareSerial, SoftwareSerial, or another compatible
177 * implementation) that is connected to the LD2410 sensor.
178 *
179 * Example:
180 * @code{.cpp}
181 * HardwareSerial radarSerial(2);
182 * LD2410Async radar(radarSerial);
183 * @endcode
184 *
185 * @param serial Reference to a Stream object used to exchange data with the sensor.
186 *
187 */
188 LD2410Async(Stream& serial);
189
190 /**********************************************************************************
191 * begin, end
192 ***********************************************************************************/
193 /**
194 * @brief Starts the background task that continuously reads data from the sensor.
195 *
196 * This method creates a FreeRTOS task which parses all incoming frames
197 * and dispatches registered callbacks. Without calling begin(), the
198 * sensor cannot deliver detection results asynchronously.
199 *
200 * @returns true if the task was successfully started, false if already running.
201 *
202 */
203 bool begin();
204
205 /**
206 * @brief Stops the background task started by begin().
207 *
208 * After calling end(), no more data will be processed until begin() is called again.
209 * This is useful to temporarily suspend radar processing without rebooting.
210 *
211 * @returns true if the task was stopped, false if it was not active.
212 *
213 */
214 bool end();
215
216
217
218 /**********************************************************************************
219 * Inactivity handling
220 ***********************************************************************************/
221
222
223
224 /**
225 * @brief Enables or disables automatic inactivity handling of the sensor.
226 *
227 * When inactivity handling is enabled, the library continuously monitors the time
228 * since the last activity of the sensor (received data or ACK). If no activity is detected
229 * for a longer period, the library will attempt to return the sensor to normal detection operation.
230 *
231 * It will attempt the following steps (each separated by the timeout period for async commands):
232 * 1. Cancel pending async commands, in case the user's code is still waiting for a callback.
233 * 2. If the cancel did not help, it will try to disable config mode, even if the library thinks config mode is disabled.
234 * 3. As a last step, it will try to reboot the sensor.
235 *
236 * If all those recovery steps don't work, the library will try again to recover the sensor after the inactivity timeout period has elapsed.
237 *
238 * @param enable Pass true to enable inactivity handling, false to disable it.
239 *
240 */
241 void setInactivityHandling(bool enable);
242
243 /**
244 * @brief Convenience method: enables inactivity handling.
245 *
246 * Equivalent to calling setInactivityHandling(true).
247 *
248 * Check setInactivityHandling() for details.
249 *
250 */
252
253 /**
254 * @brief Convenience method: disables inactivity handling.
255 *
256 * Equivalent to calling setInactivityHandling(false).
257 *
258 * Check setInactivityHandling() for details.
259 */
261
262 /**
263 * @brief Returns whether inactivity handling is currently enabled.
264 *
265 * @returns true if inactivity handling is enabled, false otherwise.
266 *
267 */
268 bool isInactivityHandlingEnabled() const { return inactivityHandlingEnabled; };
269
270 /**
271 * @brief Sets the timeout period for inactivity handling.
272 *
273 * If no data or command ACK is received within this period,
274 * the library will attempt to recover the sensor as described
275 * in setInactivityHandling().
276 *
277 * Default is 60000 ms (1 minute).
278 *
279 * @note
280 * Make sure to set a value that is larger than the timeout for async commands (see getAsyncCommandTimeoutMs() and setAsyncCommandTimeoutMs()).
281 * Otherwise inactivity handling could kick in while commands are still pending (usually waiting for an ACK), which will result in the cancellation of the pending async command.
282 * To ensure this does not happen, inactivity handling will not use the configured timeout if it is shorter than the command timeout and will instead use the command timeout plus 1000 ms.
283 *
284 * @param timeoutMs Timeout in milliseconds (minimum 10000 ms recommended). 0 will disable inactivity checking and handling.
285 *
286 */
287 void setInactivityTimeoutMs(unsigned long timeoutMs = 60000) { inactivityHandlingTimeoutMs = timeoutMs; };
288
289 /**
290 * @brief Returns the current inactivity handling timeout period.
291 *
292 * @returns Timeout in milliseconds.
293 *
294 */
295 unsigned long getInactivityTimeoutMs() const { return inactivityHandlingTimeoutMs; };
296
297
298
299 /**********************************************************************************
300 * Callback registration methods
301 ***********************************************************************************/
302
303 /**
304 * @brief Registers a callback for new detection data.
305 *
306 * The callback is invoked whenever a valid detection data frame is received
307 * from the radar. This happens for **every frame**, not only when the
308 * `presenceDetected` flag changes.
309 *
310 * The library updates its internal
311 * @ref LD2410Types::DetectionData "DetectionData" structure with each frame
312 * before the callback is executed. This provides both a quick boolean
313 * `presenceDetected` for simple use cases and full access to detailed
314 * per-gate information if needed.
315 *
316 * @param callback Function pointer with signature
317 * void methodName(LD2410Async* sender, bool presenceDetected).
318 *
319 * @note
320 * Within the callback you can use
321 * @ref LD2410Async::getDetectionData "getDetectionData()" or
322 * @ref LD2410Async::getDetectionDataRef "getDetectionDataRef()" to access
323 * the full @ref LD2410Types::DetectionData "DetectionData" struct, including
324 * distances, energies, and gate values.
325 *
326 * ## Examples
327 *
328 * ### Example: Registering the detection data callback
329 * @code{.cpp}
330 * void myDetectionCallback(LD2410Async* sender, bool presence) {
331 * if (presence) {
332 * Serial.println("Presence detected!");
333 * } else {
334 * Serial.println("No presence.");
335 * }
336 * }
337 *
338 * // Somewhere in setup():
339 * radar.onDetectionDataReceived(myDetectionCallback);
340 * @endcode
341 *
342 * ### Example: Access detection data (cloned copy)
343 * @code{.cpp}
344 * LD2410Types::DetectionData data = radar.getDetectionData(); // clone
345 * Serial.print("Moving distance: ");
346 * Serial.println(data.movingTargetDistance);
347 * @endcode
348 *
349 * ### Example: Access detection data (by reference, no copy)
350 * @code{.cpp}
351 * const LD2410Types::DetectionData& data = radar.getDetectionDataRef(); // no copy
352 * Serial.print("Stationary energy: ");
353 * Serial.println(data.stationaryTargetEnergy);
354 * @endcode
355 */
357
358 /**
359 * @brief Registers a callback for configuration data updates.
360 *
361 * The callback is invoked whenever new configuration information
362 * has been received from the sensor (e.g. after calling
363 * @ref LD2410Async::requestAllConfigSettingsAsync "requestAllConfigSettingsAsync()").
364 *
365 * @note
366 * Configuration data is **not sent automatically** by the sensor and
367 * is **not updated automatically** when it changes internally. It must
368 * be explicitly requested by the application. Use
369 * @ref LD2410Async::requestAllConfigSettingsAsync() whenever you need
370 * to refresh the local configuration data structure.
371 *
372 * ## Examples
373 *
374 * ### Example: Registering a config data received callback
375 * @code{.cpp}
376 * void onConfigDataReceived(LD2410Async* sender) {
377 * Serial.println("Config data received from sensor.");
378 * }
379 *
380 * // Somewhere in setup():
381 * radar.onConfigDataReceived(onConfigDataReceived);
382 * @endcode
383 *
384 * ### Example: Access configuration data (cloned copy)
385 * @code{.cpp}
386 * LD2410Types::ConfigData cfg = radar.getConfigData(); // clone
387 * Serial.print("No one timeout: ");
388 * Serial.println(cfg.noOneTimeout);
389 * @endcode
390 *
391 * ### Example: Access configuration data (by reference, no copy)
392 * @code{.cpp}
393 * const LD2410Types::ConfigData& cfg = radar.getConfigDataRef(); // no copy
394 * Serial.print("Resolution: ");
395 * Serial.println(static_cast<int>(cfg.distanceResolution));
396 * @endcode
397 *
398 * @param callback Function pointer with signature
399 * void methodName(LD2410Async* sender).
400 */
402
403 /**
404 * @brief Registers a callback that is invoked whenever the sensor's configuration
405 * has been changed successfully.
406 *
407 * This event is triggered after a configuration command (e.g.
408 * @ref configureAllConfigSettingsAsync "configureAllConfigSettingsAsync()" or
409 * @ref configureDistanceResolutionAsync "configureDistanceResolutionAsync()")
410 * has been executed by the sensor.
411 *
412 * @note
413 * - This callback only signals that the sensor acknowledged and applied a change.
414 * - The local @ref LD2410Types::ConfigData "ConfigData" structure is **not**
415 * automatically updated when this event occurs. To refresh the struct, explicitly
416 * request it using @ref requestAllConfigSettingsAsync "requestAllConfigSettingsAsync()".
417 *
418 * @param callback Function pointer with the signature:
419 * `void myCallback(LD2410Async* sender)`
420 *
421 * Example:
422 * @code
423 * void myConfigChangedCallback(LD2410Async* sender) {
424 * Serial.println("Sensor configuration updated.");
425 * // Optionally request fresh config data:
426 * sender->requestAllConfigSettingsAsync(onConfigReceived);
427 * }
428 *
429 * // In setup():
430 * radar.onConfigChanged(myConfigChangedCallback);
431 * @endcode
432 */
433 void onConfigChanged(GenericCallback callback);
434
435 /**********************************************************************************
436 * Detection and config data access commands
437 ***********************************************************************************/
438 // It is recommended to use the data access commands instead of accessing the detectionData and the configData structs in the class directly.
439
440 /**
441 * @brief Returns a clone of the latest detection data from the radar.
442 *
443 * The returned struct contains the most recently received frame,
444 * including target state, distances, signal strengths, and
445 * (if enabled) engineering mode per-gate data.
446 *
447 * Equivalent to directly accessing the public member detectionData,
448 * but provided for encapsulation and future-proofing.
449 *
450 * @note This function will not query the sensor for data. It just returns
451 * the data that has already been received from the sensor.
452 *
453 * ## Example: Access values from a clone
454 * @code{.cpp}
455 * DetectionData data = radar.getDetectionData(); // makes a copy
456 * if (data.targetState == TargetState::MOVING_TARGET) {
457 * Serial.print("Moving target at distance: ");
458 * Serial.println(data.movingTargetDistance);
459 * }
460 * @endcode
461 *
462 * ## Do:
463 * - Use when you want a snapshot of the latest detection data.
464 * - Modify the returned struct freely without affecting the internal state.
465 *
466 * ## Don't:
467 * - Expect this to fetch new data from the sensor (it only returns what was already received).
468 *
469 * @returns A copy of the current DetectionData.
470 *
471 */
473
474
475 /**
476 * @brief Access the current detection data without making a copy.
477 *
478 * This returns a const reference to the internal struct. It is efficient,
479 * but the data must not be modified directly. Use this if you only want
480 * to read values.
481 *
482 * @note Since this returns a reference to the internal data, the values
483 * may change as new frames arrive. Do not store the reference for
484 * long-term use.
485 * @note This function will not query the sensor for data. It just returns
486 * the data that has already been received from the sensor.
487 *
488 * ## Example: Efficient read access without cloning
489 * @code{.cpp}
490 * const DetectionData& data = radar.getDetectionDataRef(); // no copy
491 * Serial.print("Stationary signal: ");
492 * Serial.println(data.stationaryTargetSignal);
493 * @endcode
494 *
495 * ## Do:
496 * - Use when you only need to read values quickly and efficiently.
497 * - Use when printing or inspecting live data without keeping it.
498 *
499 * ## Don't:
500 * - Try to modify the returned struct (it's const).
501 * - Store the reference long-term (it may be updated at any time).
502 *
503 * @returns Const reference to the current DetectionData.
504 *
505 */
507
508
509 /**
510 * @brief Returns a clone of the current configuration data of the radar.
511 *
512 * The returned struct contains the most recently requested
513 * or received configuration values, such as sensitivities,
514 * resolution, timeouts, and auxiliary settings.
515 *
516 * Equivalent to directly accessing the public member configData,
517 * but provided for encapsulation and future-proofing.
518 *
519 * @note This function will not query the sensor for data. It just returns
520 * the data that has already been received from the sensor.
521 *
522 * ## Example: Clone, modify, and write back
523 * @code{.cpp}
524 * // Clone current config
525 * ConfigData cfg = radar.getConfigData();
526 *
527 * // Modify locally
528 * cfg.noOneTimeout = 60; // change timeout
529 * cfg.distanceGateMotionSensitivity[3] = 80; // adjust sensitivity
530 *
531 * // Send modified config back to sensor
532 * radar.configureAllConfigSettingsAsync(cfg, [](LD2410Async* sender,
533 * AsyncCommandResult result,
534 * byte) {
535 * if (result == AsyncCommandResult::SUCCESS) {
536 * Serial.println("Config updated successfully!");
537 * }
538 * });
539 * @endcode
540 *
541 * ## Do:
542 * - Use when you want a clone of the current config to adjust and send back.
543 * - Safely modify the struct without risking internal state corruption.
544 *
545 * ## Don't:
546 * - Assume this always reflects the live sensor config (it's only as fresh as the last received config data).
547 *
548 * @returns A copy of the current ConfigData.
549 *
550 */
552
553
554 /**
555 * @brief Access the current config data without making a copy.
556 *
557 * This returns a const reference to the internal struct. It is efficient,
558 * but the data must not be modified directly. Use this if you only want
559 * to read values.
560 *
561 * @note Since this returns a reference to the internal data, the values
562 * may change when new configuration is received. Do not store the
563 * reference for long-term use.
564 * @note This function will not query the sensor for data. It just returns
565 * the data that has already been received from the sensor.
566 *
567 * ## Example: Efficient read access without cloning
568 * @code{.cpp}
569 * const ConfigData& cfg = radar.getConfigDataRef(); // no copy
570 * Serial.print("Resolution: ");
571 * Serial.println(static_cast<int>(cfg.distanceResolution));
572 * @endcode
573 *
574 * ## Do:
575 * - Use when you only want to inspect configuration quickly.
576 * - Use for efficient read-only access.
577 *
578 * ## Don't:
579 * - Try to modify the returned struct (it's const).
580 * - Keep the reference and assume it will remain valid forever.
581 *
582 * @returns Const reference to the current ConfigData.
583 *
584 */
586
587
588 /**
589 * Resets the config data to the initial values
590 */
591 void resetConfigData();
592
593 /**********************************************************************************
594 * Special async commands
595 ***********************************************************************************/
596 /**
597 * @brief Checks if an asynchronous command is currently pending.
598 *
599 * @returns true if there is an active command awaiting an ACK,
600 * false if the library is idle.
601 *
602 */
603 bool asyncIsBusy();
604
605 /**
606 * @brief Cancels any pending asynchronous command or sequence.
607 *
608 * @note
609 * Since this command can lead to incomplete operations (in particular with high level commands),
610 * it is not advised to use this command unless really, really necessary. Better wait for the callback
611 * of the current operation.
612 * If canceled, the callback of the running command or command sequence is invoked
613 * with result type CANCELED. After canceling, the sensor may remain in config mode,
614 * consider disabling config mode or rebooting to resume normal detection operation. *
615 */
616 void asyncCancel();
617
618 /**
619 * @brief Sets the timeout for async command callbacks.
620 *
621 * @note
622 * Make sure the timeout is long enough to allow for the execution of long running commands.
623 * In particular, enabling config mode can take up to 4 seconds; therefore, using a value below 4000 is not recommended.
624 *
625 * @param timeoutMs Timeout in milliseconds (default 4000 ms).
626 *
627 */
628 void setAsyncCommandTimeoutMs(unsigned long timeoutMs) { asyncCommandTimeoutMs = timeoutMs; }
629
630 /**
631 * @brief Returns the current async command timeout.
632 *
633 * @return Timeout in milliseconds.
634 *
635 */
636 unsigned long getAsyncCommandTimeoutMs() const { return asyncCommandTimeoutMs; }
637
638
639 /**********************************************************************************
640 * Commands
641 ***********************************************************************************/
642
643 /*---------------------------------------------------------------------------------
644 - Config mode commands
645 ---------------------------------------------------------------------------------*/
646 /**
647 * @brief Enables config mode on the radar.
648 *
649 * Config mode must be enabled before sending any commands (apart from this command) to the sensor.
650 * This command itself is asynchronous - the callback fires once the
651 * sensor acknowledges the mode switch.
652 *
653 * @note If asyncIsBusy() is true, this command will not be sent.
654 * @note Normal detection data is suspended while config mode is active.
655 *
656 * @param callback Callback with signature
657 * void(LD2410Async* sender, AsyncCommandResult result).
658 *
659 *
660 * @returns true if the command was accepted, false if blocked (typically because another async command is pending).
661 *
662 */
664 return enableConfigModeAsync(false, callback);
665 }
666
667 /**
668 * @brief Enables config mode on the radar.
669 *
670 * Config mode must be enabled before sending any commands (apart from this command) to the sensor.
671 * This command itself is asynchronous - the callback fires once the
672 * sensor acknowledges the mode switch.
673 *
674 * @note If asyncIsBusy() is true, this command will not be sent.
675 * @note Normal detection data is suspended while config mode is active.
676 *
677 * @param callback Callback with signature
678 * void(LD2410Async* sender, AsyncCommandResult result).
679 * @param force Forces the command to send the config mode enable command, even if the current status of the library indicates that config mode is already active. The callback of the method will be called anyway, either after the sent command has sent an ACK or after a minimal delay (if the command is not sent).
680 *
681 * @returns true if the command was accepted, false if blocked (typically because another async command is pending).
682 *
683 */
684 bool enableConfigModeAsync(bool force, AsyncCommandCallback callback);
685
686 /**
687 * @brief Disables config mode on the radar.
688 *
689 * This must be called after finishing sending commands to the sensor, to return
690 * the sensor to normal detection operation.
691 *
692 * @note If an async command is already pending (asyncIsBusy() == true),
693 * this command will not be sent.
694 *
695 * @param callback Callback with signature
696 * void(LD2410Async* sender, AsyncCommandResult result).
697 *
698 *
699 * @returns true if the command was accepted, false otherwise (typically because another async command is pending).
700 *
701 */
703 return disableConfigModeAsync(false, callback);
704 }
705
706 /**
707 * @brief Disables config mode on the radar.
708 *
709 * This should be called after finishing sending commands to the sensor, to return
710 * the sensor to normal detection operation.
711 *
712 * @note If an async command is already pending (asyncIsBusy() == true), this command will not be sent, but will still trigger the callback.
713 * @note If config mode is already disabled and sending the command is forced using the force parameter, the command will timeout, since the sensor will not send an ACK for the command.
714 *
715 * @param callback Callback with signature
716 * void(LD2410Async* sender, AsyncCommandResult result).
717 *
718 * @param force Forces sending the command to disable config mode, even if local data indicates that config mode is not enabled. If config mode is not enabled, this command will time out when force is true. The callback of the method will be called anyway, either after the sent command has sent an ACK or after a minimal delay (if the command is not sent).
719 *
720 * @returns true if the command was accepted, false otherwise (typically because another async command is pending).
721 *
722 */
723 bool disableConfigModeAsync(bool force, AsyncCommandCallback callback);
724
725
726 /**
727 * @brief Detects if config mode is enabled
728 *
729 * @returns true if config mode is enabled, false if config mode is disabled.
730 *
731 */
732 bool isConfigModeEnabled() const {
733 return configModeEnabled;
734 };
735
736
737 /*---------------------------------------------------------------------------------
738 - Engineering mode commands
739 ---------------------------------------------------------------------------------*/
740 /**
741 * @brief Enables engineering mode.
742 *
743 * In this mode, the sensor sends detailed per-gate signal values
744 * in addition to basic detection results.
745 *
746 * @note Engineering mode is temporary and lost after power cycle.
747 * @note Requires config mode. Will be enabled automatically if not active.
748 *
749 * @param callback Callback fired when ACK is received or on failure.
750 *
751 *
752 * @returns true if the command was sent, false otherwise.
753 *
754 */
756
757 /**
758 * @brief Disables engineering mode.
759 *
760 * Returns sensor reporting to basic detection results only.
761 *
762 * @note Requires config mode. Will be enabled automatically if not active.
763 *
764 * @param callback Callback fired when ACK is received or on failure.
765 *
766 *
767 * @returns true if the command was sent, false otherwise.
768 *
769 */
771
772 /**
773 * @brief Detects if engineering mode is enabled
774 *
775 * @returns true if engineering mode is enabled, false if engineering mode is disabled
776 *
777 */
780 };
781
782 /*---------------------------------------------------------------------------------
783 - Native sensor commands
784 ---------------------------------------------------------------------------------*/
785 /**
786 * @brief Requests the current gate parameters from the sensor.
787 *
788 * Retrieves sensitivities, max gates, and timeout settings,
789 * which will be written into configData.
790 *
791 * @note Requires config mode. The method will manage mode switching if needed.
792 * @note If an async command is already pending, the request is rejected.
793 *
794 * @param callback Callback fired when data is received or on failure.
795 *
796 *
797 * @returns true if the command was sent, false otherwise.
798 *
799 */
801
802
803
804 /**
805 * @brief Configures the maximum detection gates and "no-one" timeout on the sensor.
806 *
807 * This command updates:
808 * - Maximum motion detection distance gate (2-8).
809 * - Maximum stationary detection distance gate (2-8).
810 * - Timeout duration (0-65535 seconds) until "no presence" is declared.
811 *
812 * @note Requires config mode to be enabled. The method will internally
813 * enable/disable config mode if necessary.
814 * @note If another async command is pending, this call fails.
815 *
816 * @param maxMovingGate Furthest gate used for motion detection (2-8).
817 * @param maxStationaryGate Furthest gate used for stationary detection (2-8).
818 * @param noOneTimeout Timeout in seconds until "no one" is reported (0-65535).
819 * @param callback Callback fired when ACK is received or on failure/timeout.
820 *
821 *
822 * @returns true if the command was sent, false otherwise (busy state or invalid values).
823 *
824 */
825 bool configureMaxGateAndNoOneTimeoutAsync(byte maxMovingGate, byte maxStationaryGate, unsigned short noOneTimeout, AsyncCommandCallback callback);
826
827
828 /**
829 * @brief Configures sensitivity thresholds for all gates at once.
830 *
831 * A sequence of commands will be sent, one for each gate.
832 * Threshold values are automatically clamped to 0-100.
833 *
834 * @note Requires config mode. Will be managed automatically.
835 * @note If another async command is pending, this call fails.
836 *
837 * @param movingThresholds Array of 9 sensitivity values for moving targets (0-100).
838 * @param stationaryThresholds Array of 9 sensitivity values for stationary targets (0-100).
839 * @param callback Callback fired when all updates are acknowledged or on failure.
840 *
841 *
842 * @returns true if the sequence was started, false otherwise.
843 *
844 */
845
846 bool configureDistanceGateSensitivityAsync(const byte movingThresholds[9], const byte stationaryThresholds[9], AsyncCommandCallback callback);
847
848
849 /**
850 * @brief Configures sensitivity thresholds for a single gate.
851 *
852 * Updates both moving and stationary thresholds for the given gate index.
853 * If the gate index is greater than 8, all gates are updated instead.
854 *
855 * @note Requires config mode. Will be managed automatically.
856 * @note If another async command is pending, this call fails.
857 *
858 * @param gate Index of the gate (0-8). Values >8 apply to all gates.
859 * @param movingThreshold Sensitivity for moving targets (0-100).
860 * @param stationaryThreshold Sensitivity for stationary targets (0-100).
861 * @param callback Callback fired when ACK is received or on failure.
862 *
863 *
864 * @returns true if the command was sent, false otherwise.
865 *
866 *
867 */
868 bool configureDistanceGateSensitivityAsync(byte gate, byte movingThreshold, byte stationaryThreshold, AsyncCommandCallback callback);
869
870 /**
871 * @brief Requests the firmware version of the sensor.
872 *
873 * Populates the firmware string when the ACK response arrives.
874 *
875 * @note Requires config mode. Will be managed automatically.
876 *
877 * @param callback Callback fired when firmware info is received.
878 *
879 *
880 * @returns true if the command was sent, false otherwise.
881 *
882 */
884
885 /**
886 * @brief Configures the UART baud rate of the sensor.
887 *
888 * The new baud rate becomes active only after reboot.
889 * The ESP32's Serial interface must also be reconfigured
890 * to the new baud rate after reboot.
891 *
892 * @note Valid values are 1-8. Values outside the range are rejected, and the method will fail.
893 * @note Requires config mode. Will be managed automatically.
894 * @note If another async command is pending, this call fails.
895 * @note After execution, call rebootAsync() to activate changes.
896 *
897 * @param baudRateSetting Numeric setting: 1=9600, 2=19200, 3=38400, 4=57600, 5=115200, 6=230400, 7=25600 (factory default), 8=460800.
898 * @param callback Callback fired when ACK is received or on failure.
899 *
900 *
901 * @returns true if the command was sent, false otherwise.
902 *
903 */
904 bool configureBaudRateAsync(byte baudRateSetting, AsyncCommandCallback callback);
905
906 /**
907 * @brief Configures the baud rate of the sensor's serial port.
908 *
909 * The new baud rate becomes active only after the sensor reboots.
910 * If you change the baud rate, also adjust the baud rate of the ESP32 serial interface connected to the sensor.
911 *
912 * @note If another async command is pending, this call fails.
913 * @note After execution, call rebootAsync() to activate changes.
914 *
915 * @param baudrate A valid baud rate from the Baudrate enum.
916 * @param callback Callback method with the signature void methodName(LD2410Async* sender, AsyncCommandResult result). The callback is invoked after the ACK for the command has been received (success=true), after the command times out (success=false), or after the command has been canceled (success=false).
917 *
918 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
919 *
920 */
922
923
924 /**
925 * @brief Restores factory settings of the sensor.
926 *
927 * Restored settings only become active after a reboot.
928 *
929 * @note Requires config mode. Will be managed automatically.
930 * @note After execution, call rebootAsync() to activate changes.
931 *
932 * @param callback Callback fired when ACK is received or on failure.
933 *
934 *
935 * @returns true if the command was sent, false otherwise.
936 *
937 */
939
940 /**
941 * @brief Reboots the sensor.
942 *
943 * After reboot, the sensor stops responding for a few seconds.
944 * The callback if this method will be triggered as soon as normal operation of the sensor has been detected.
945 * Config and engineering mode are reset once the sensor is back to normal operation.
946 *
947 *
948 * @param callback Callback fired when ACK is received or on failure.
949 *
950 *
951 * @returns true if the command was sent, false otherwise.
952 *
953 */
955 return rebootAsync(false, callback);
956 }
957
958 /**
959 * @brief Reboots the sensor.
960 *
961 * After reboot, the sensor stops responding for a few seconds.
962 * Depending on the value of the dontWaitForNormalOperationAfterReboot parameter, the callback will be triggered once the sensor has sent an ACK for the reboot command (when false) or when normal operation of the sensor resumes.
963 * Config and engineering mode are reset once normal operation of the sensor resumes.
964 *
965 * @note If dontWaitForNormalOperationAfterReboot is true, the sensor reboots only after the ACK has been sent. Wait a short period before sending commands to ensure the reboot is complete and the sensor is responsive.
966 *
967 * @param dontWaitForNormalOperationAfterReboot Controls whether the callback will be triggered once the sensor has sent an ACK for the reboot command (when false) or when normal operation of the sensor resumes (when true).
968 * @param callback Callback fired when ACK is received or on failure.
969 *
970 *
971 * @returns true if the command was sent, false otherwise.
972 *
973 */
974 bool rebootAsync(bool dontWaitForNormalOperationAfterReboot, AsyncCommandCallback callback);
975
976
977 /**
978 * @brief Enables Bluetooth
979 *
980 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
981 *
982 *
983 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
984 *
985 */
987
988 /**
989 * @brief Disables Bluetooth
990 *
991 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
992 *
993 *
994 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
995 *
996 */
998
999 /**
1000 * @brief Requests the Bluetooth MAC address
1001 *
1002 * @note The callback fires when the MAC address has been received from the sensor (is sent with the ACK).
1003 *
1004 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
1005 *
1006 *
1007 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
1008 *
1009 */
1011
1012 /**
1013 * @brief Sets the password for Bluetooth access to the sensor.
1014 *
1015 * @param password New Bluetooth password. Max 6. chars.
1016 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
1017 *
1018 *
1019 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
1020 *
1021 */
1022 bool configureBluetoothPasswordAsync(const char* password, AsyncCommandCallback callback);
1023
1024 /**
1025 * @brief Sets the password for Bluetooth access to the sensor.
1026 *
1027 * @param password New Bluetooth password. Max 6. chars.
1028 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
1029 *
1030 *
1031 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
1032 *
1033 */
1034 bool configureBluetoothPasswordAsync(const String& password, AsyncCommandCallback callback);
1035
1036 /**
1037 * @brief Resets the password for Bluetooth access to the default value (HiLink)
1038 * @param callback Callback method with void methodName(LD2410Async* sender, AsyncCommandResult result) signature. Will be called after the ACK for the command has been received (success=true) or after the command timeout (success=false) or after the command has been canceled (success=false).
1039 *
1040 *
1041 * @returns true if the command has been sent, false if the command can't be sent (typically because another async command is pending).
1042 *
1043 */
1045
1046 /**
1047 * @brief Configures the distance resolution of the radar.
1048 *
1049 * The distance resolution defines the size of each distance gate
1050 * and the maximum detection range:
1051 * - RESOLUTION_75CM - longer range, coarser detail.
1052 * - RESOLUTION_20CM - shorter range, finer detail.
1053 *
1054 * @note Requires config mode. Will be managed automatically.
1055 * @note Requires a reboot to activate value changes. Call rebootAsync() after setting.
1056 * @note Fails if another async command is pending.
1057 *
1058 * @param distanceResolution Value from the DistanceResolution enum.
1059 * Must not be NOT_SET.
1060 * @param callback Function pointer with signature:
1061 * void(LD2410Async* sender, AsyncCommandResult result).
1062 * Fired when the ACK is received or on failure/timeout.
1063 *
1064 *
1065 * @returns true if the command was sent, false if invalid parameters
1066 * or the library is busy.
1067 *
1068 */
1070
1071 /**
1072 * @brief Configures the distance resolution explicitly to 75 cm per gate.
1073 *
1074 * Equivalent to configureDistanceResolutionAsync(DistanceResolution::RESOLUTION_75CM).
1075 *
1076 * @note Requires config mode. Will be managed automatically.
1077 * @note Requires a reboot to activate value changes. Call rebootAsync() after setting.
1078 * @note Fails if another async command is pending.
1079 *
1080 * @param callback Function pointer with signature:
1081 * void(LD2410Async* sender, AsyncCommandResult result).
1082 *
1083 *
1084 * @returns true if the command was sent, false otherwise.
1085 *
1086 */
1088
1089 /**
1090 * @brief Configures the distance resolution explicitly to 20 cm per gate.
1091 *
1092 * Equivalent to configureDistanceResolutionAsync(DistanceResolution::RESOLUTION_20CM).
1093 *
1094 * @note Requires config mode. Will be managed automatically.
1095 * @note Requires a reboot to activate value changes. Call rebootAsync() after setting.
1096 * @note Fails if another async command is pending.
1097 *
1098 * @param callback Function pointer with signature:
1099 * void(LD2410Async* sender, AsyncCommandResult result).
1100 *
1101 *
1102 * @returns true if the command was sent, false otherwise.
1103 *
1104 */
1106
1107 /**
1108 * @brief Requests the current distance resolution setting from the sensor.
1109 *
1110 * The result is written into configData.distanceResolution.
1111 *
1112 * @note Requires config mode. Will be managed automatically.
1113 *
1114 * @param callback Function pointer with signature:
1115 * void(LD2410Async* sender, AsyncCommandResult result).
1116 *
1117 *
1118 * @returns true if the command was sent, false otherwise.
1119 *
1120 */
1122
1123 /**
1124 * @brief Configures the auxiliary control parameters (light and output pin).
1125 *
1126 * This configures how the OUT pin behaves depending on light levels
1127 * and presence detection. Typical use cases include controlling
1128 * an external lamp or relay.
1129 *
1130 * @note Requires config mode. Will be managed automatically.
1131 * @note Both enums must be set to valid values (not NOT_SET).
1132 * @note Fails if another async command is pending.
1133 *
1134 * @param lightControl Light control behavior (see LightControl enum).
1135 * @param lightThreshold Threshold (0-255) used for light-based switching.
1136 * @param outputControl Output pin logic configuration (see OutputControl enum).
1137 * @param callback Function pointer with signature:
1138 * void(LD2410Async* sender, AsyncCommandResult result).
1139 * Fired when ACK is received or on failure/timeout.
1140 *
1141 *
1142 * @returns true if the command was sent, false otherwise.
1143 *
1144 */
1145 bool configureAuxControlSettingsAsync(LD2410Types::LightControl light_control, byte light_threshold, LD2410Types::OutputControl output_control, AsyncCommandCallback callback);
1146
1147 /**
1148 * @brief Requests the current auxiliary control settings.
1149 *
1150 * Fills configData.lightControl, configData.lightThreshold,
1151 * and configData.outputControl.
1152 *
1153 * @note Requires config mode. Will be managed automatically.
1154 *
1155 * @param callback Function pointer with signature:
1156 * void(LD2410Async* sender, AsyncCommandResult result).
1157 * Fired when ACK is received or on failure/timeout.
1158 *
1159 *
1160 * @returns true if the command was sent, false otherwise.
1161 *
1162 */
1164
1165
1166 /**
1167 * @brief Starts the automatic configuration (auto-config) routine on the sensor.
1168 *
1169 * Auto-config lets the radar adjust its internal thresholds and
1170 * sensitivities for the current environment. This can take several
1171 * seconds to complete and results in updated sensitivity values.
1172 *
1173 * The progress and result can be checked with requestAutoConfigStatusAsync().
1174 *
1175 * @note Requires config mode. This method will manage entering and
1176 * exiting config mode automatically.
1177 * @note Auto-config temporarily suspends normal detection reporting.
1178 *
1179 * ## Example: Run auto-config
1180 * @code{.cpp}
1181 * radar.beginAutoConfigAsync([](LD2410Async* sender,
1182 * AsyncCommandResult result,
1183 * byte) {
1184 * if (result == AsyncCommandResult::SUCCESS) {
1185 * Serial.println("Auto-config started.");
1186 * } else {
1187 * Serial.println("Failed to start auto-config.");
1188 * }
1189 * });
1190 * @endcode
1191 *
1192 * ## Do:
1193 * - Use in new environments to optimize detection performance.
1194 * - Query status afterwards with requestAutoConfigStatusAsync().
1195 *
1196 * ## Don't:
1197 * - Expect instant results - the sensor needs time to complete the process.
1198 *
1199 * @param callback Callback with signature:
1200 * void(LD2410Async* sender, AsyncCommandResult result).
1201 * Fired when the command is acknowledged or on failure/timeout.
1202 *
1203 *
1204 * @returns true if the command was sent, false otherwise.
1205 *
1206 */
1208
1209
1210 /**
1211 * @brief Requests the current status of the auto-config routine.
1212 *
1213 * The status is written into the member variable autoConfigStatus:
1214 * - NOT_IN_PROGRESS - no auto-config running.
1215 * - IN_PROGRESS - auto-config is currently running.
1216 * - COMPLETED - auto-config finished (success or failure).
1217 *
1218 * @note Requires config mode. This method will manage mode switching automatically.
1219 * @note If another async command is already pending, this call fails.
1220 *
1221 * ## Example: Check auto-config status
1222 * @code{.cpp}
1223 * radar.requestAutoConfigStatusAsync([](LD2410Async* sender,
1224 * AsyncCommandResult result,
1225 * byte) {
1226 * if (result == AsyncCommandResult::SUCCESS) {
1227 * switch (sender->autoConfigStatus) {
1228 * case AutoConfigStatus::NOT_IN_PROGRESS:
1229 * Serial.println("Auto-config not running.");
1230 * break;
1231 * case AutoConfigStatus::IN_PROGRESS:
1232 * Serial.println("Auto-config in progress...");
1233 * break;
1234 * case AutoConfigStatus::COMPLETED:
1235 * Serial.println("Auto-config completed.");
1236 * break;
1237 * default:
1238 * Serial.println("Unknown auto-config status.");
1239 * }
1240 * } else {
1241 * Serial.println("Failed to request auto-config status.");
1242 * }
1243 * });
1244 * @endcode
1245 *
1246 * ## Do:
1247 * - Use this after beginAutoConfigAsync() to track progress.
1248 * - Use autoConfigStatus for decision-making in your logic.
1249 *
1250 * ## Don't:
1251 * - Assume COMPLETED means success - thresholds should still be verified.
1252 *
1253 * @param callback Callback with signature:
1254 * void(LD2410Async* sender, AsyncCommandResult result).
1255 * Fired when the sensor replies or on failure/timeout.
1256 *
1257 *
1258 * @returns true if the command was sent, false otherwise.
1259 *
1260 */
1262
1263
1264
1265 /*---------------------------------------------------------------------------------
1266 - High level commands
1267 ---------------------------------------------------------------------------------*/
1268 // High level commands typically encapsulate several native commands.
1269 // They provide a more consistent access to the sensors configuration,
1270 // e.g. for reading all config data 3 native commands are necessary,
1271 // to update the same data up to 12 native commands may be required.
1272 // The high-level commands encapsulate both situations into a single command
1273
1274 /**
1275 * @brief Requests all configuration settings from the sensor.
1276 *
1277 * This triggers a sequence of queries that retrieves and updates:
1278 * - Gate parameters (sensitivities, max gates, timeout).
1279 * - Distance resolution setting.
1280 * - Auxiliary light/output control settings.
1281 *
1282 * The results are stored in configData, and the
1283 * onConfigDataReceived() is invoked after completion.
1284 *
1285 * @note This is a high-level method that involves multiple commands.
1286 * @note Requires config mode. This method will manage mode switching automatically.
1287 * @note If another async command is already pending, the request fails.
1288 *
1289 * ## Example: Refresh config data
1290 * @code{.cpp}
1291 * radar.requestAllConfigSettingsAsync([](LD2410Async* sender,
1292 * AsyncCommandResult result,
1293 * byte) {
1294 * if (result == AsyncCommandResult::SUCCESS) {
1295 * Serial.println("All config data refreshed:");
1296 * sender->getConfigDataRef().print();
1297 * }
1298 * });
1299 * @endcode
1300 *
1301 * ## Do:
1302 * - Use this after connecting to ensure configData is fully populated.
1303 * - Call before modifying config if you're unsure of current values.
1304 *
1305 * ## Don't:
1306 * - Expect it to succeed if another async command is still running.
1307 *
1308 * @param callback Callback with signature:
1309 * void(LD2410Async* sender, AsyncCommandResult result).
1310 * Fired when all config data has been received or on failure.
1311 *
1312 *
1313 * @returns true if the command was sent, false otherwise.
1314 *
1315 */
1317
1318
1319 /**
1320 * @brief Requests all static information from the sensor.
1321 *
1322 * This includes:
1323 * - Firmware version string.
1324 * - Bluetooth MAC address (numeric and string form).
1325 *
1326 * The values are written into the public members `firmware`, `mac`,
1327 * and `macString`.
1328 *
1329 * @note This is a high-level method that involves multiple commands.
1330 * @note Requires config mode. Managed automatically by this method.
1331 * @note If another async command is already pending, the request fails.
1332 *
1333 * ## Example: Retrieve firmware and MAC
1334 * @code{.cpp}
1335 * radar.requestAllStaticDataAsync([](LD2410Async* sender,
1336 * AsyncCommandResult result,
1337 * byte) {
1338 * if (result == AsyncCommandResult::SUCCESS) {
1339 * Serial.print("Firmware: ");
1340 * Serial.println(sender->firmware);
1341 *
1342 * Serial.print("MAC: ");
1343 * Serial.println(sender->macString);
1344 * }
1345 * });
1346 * @endcode
1347 *
1348 * ## Do:
1349 * - Use after initialization to log firmware version and MAC.
1350 * - Useful for debugging or inventory identification.
1351 *
1352 * ## Don't:
1353 * - Expect frequently changing data - this is static information.
1354 *
1355 * @param callback Callback with signature:
1356 * void(LD2410Async* sender, AsyncCommandResult result).
1357 * Fired when static data is received or on failure.
1358 *
1359 *
1360 * @returns true if the command was sent, false otherwise.
1361 *
1362 */
1364
1365
1366
1367 /**
1368 * @brief Applies a full ConfigData struct to the LD2410.
1369 *
1370 * If writeAllConfigData is true, the method will first fetch the current config,
1371 * compare it with the provide Config data and then create a command sequence that
1372 * will only update the changes config values.
1373 * If writeAllConfigData is false, the method will write all values in the provided
1374 * ConfigData to the sensor, regardless of whether they differ from the current config.
1375 *
1376 * @note This is a high-level method that involves multiple commands (up to 18).
1377 * @note Requires config mode. This method will manage entering and
1378 * exiting config mode automatically (if config mode is not already active).
1379 * @note If another async command is already pending, the command fails.
1380 * @note Any members of ConfigData that are left at invalid values
1381 * (e.g. enums set to NOT_SET) will cause the sequence to fail.
1382 *
1383 * ## Example: Clone, modify, and apply config
1384 * @code{.cpp}
1385 * ConfigData cfg = radar.getConfigData(); // clone current config
1386 * cfg.noOneTimeout = 120; // change timeout
1387 * cfg.distanceGateMotionSensitivity[2] = 75;
1388 *
1389 * radar.configureAllConfigSettingsAsync(cfg, [](LD2410Async* sender,
1390 * AsyncCommandResult result,
1391 * byte) {
1392 * if (result == AsyncCommandResult::SUCCESS) {
1393 * Serial.println("All config applied successfully!");
1394 * }
1395 * });
1396 * @endcode
1397 *
1398 * ## Do:
1399 * - Always start from a clone of getConfigData() to avoid setting unwanted values for settings that do not need to change.
1400 * - If the method's callback does not report SUCCESS, any portion of the config might have been written to the sensor or the sensor might have remained in config mode.
1401 * Make sure to take appropriate measures to return the sensor to normal operation mode if required (reboot usually does the trick) and check the config values, if they are what you need.
1402 *
1403 * ## Don't:
1404 * - Don't write all config data (writeAllConfigData=true) if not really necessary.
1405 * This generates unnecessary wear on the sensor's memory.
1406 * - Pass uninitialized or partially filled ConfigData (may fail or result in unwanted settings)
1407
1408 *
1409 * @param configToWrite The configuration data to be applied.
1410 * @param writeAllConfigData If true, all fields in configToWrite are applied. If false, changed values are written.
1411 * @param callback Function with signature:
1412 * void(LD2410Async* sender, AsyncCommandResult result),
1413 * executed when the sequence finishes (success/fail/timeout/cancel).
1414 *
1415 *
1416 * @returns true if the command sequence has been started, false otherwise.
1417 *
1418 */
1419 bool configureAllConfigSettingsAsync(const LD2410Types::ConfigData& configToWrite, bool writeAllConfigData, AsyncCommandCallback callback);
1420
1421
1422
1423private:
1424 // ============================================================================
1425 // Low-level serial interface
1426 // ============================================================================
1427
1428 /// Pointer to the Serial/Stream object connected to the LD2410 sensor
1429 Stream* sensor;
1430
1431
1432 // ============================================================================
1433 // Frame parsing state machine
1434 // ============================================================================
1435
1436 /// States used when parsing incoming frames from the sensor
1437 enum ReadFrameState {
1438 WAITING_FOR_HEADER, ///< Waiting for frame header sequence
1439 ACK_HEADER, ///< Parsing header of an ACK frame
1440 DATA_HEADER, ///< Parsing header of a DATA frame
1441 READ_ACK_SIZE, ///< Reading payload size field of an ACK frame
1442 READ_DATA_SIZE, ///< Reading payload size field of a DATA frame
1443 READ_ACK_PAYLOAD, ///< Reading payload content of an ACK frame
1444 READ_DATA_PAYLOAD ///< Reading payload content of a DATA frame
1445 };
1446
1447 /// Result type when trying to read a frame
1448 enum FrameReadResponse {
1449 FAIL = 0, ///< Frame was invalid or incomplete
1450 ACK, ///< A valid ACK frame was received
1451 DATA ///< A valid DATA frame was received
1452 };
1453
1454 int readFrameHeaderIndex = 0; ///< Current index while matching the frame header
1455 int payloadSize = 0; ///< Expected payload size of current frame
1456 ReadFrameState readFrameState = ReadFrameState::WAITING_FOR_HEADER; ///< Current frame parsing state
1457
1458 /// Extract payload size from the current byte and update state machine
1459 bool readFramePayloadSize(byte b, ReadFrameState nextReadFrameState);
1460
1461 /// Read payload bytes until full ACK/DATA frame is assembled
1462 FrameReadResponse readFramePayload(byte b, const byte* tailPattern, LD2410Async::FrameReadResponse successResponseType);
1463
1464 /// State machine entry: read incoming frame and return read response
1465 FrameReadResponse readFrame();
1466
1467
1468 // ============================================================================
1469 // Receive buffer
1470 // ============================================================================
1471
1472 /// Raw buffer for storing incoming bytes
1473 byte receiveBuffer[LD2410Defs::LD2410_Buffer_Size];
1474
1475 /// Current index into receiveBuffer
1476 byte receiveBufferIndex = 0;
1477
1478
1479 // ============================================================================
1480 // Asynchronous command sequence handling
1481 // ============================================================================
1482
1483 /// Maximum number of commands in one async sequence
1484 static constexpr size_t MAX_COMMAND_SEQUENCE_LENGTH = 15;
1485
1486 /// Buffer holding queued commands for sequence execution
1487 byte commandSequenceBuffer[MAX_COMMAND_SEQUENCE_LENGTH][LD2410Defs::LD2410_Buffer_Size];
1488
1489 /// Number of commands currently queued in the sequence buffer
1490 byte commandSequenceBufferCount = 0;
1491
1492 /// Timestamp when the current async sequence started
1493 unsigned long executeCommandSequenceStartMs = 0;
1494
1495 /// Callback for current async sequence
1496 AsyncCommandCallback executeCommandSequenceCallback = nullptr;
1497
1498 AsyncCommandResult executeCommandSequenceResultToReport = AsyncCommandResult::SUCCESS;
1499
1500 /// True if an async sequence is currently pending.
1501 bool executeCommandSequencePending = false;
1502
1503 /// Ticker used for small delay before firing the commandsequence callback when the command sequence is empty.
1504 /// This ensures that the callback is always called asynchronously and never directly from the calling context.
1505 Ticker executeCommandSequenceOnceTicker;
1506
1507 /// Index of currently active command in the sequence buffer
1508 int executeCommandSequenceIndex = 0;
1509
1510 /// Stores config mode state before sequence started (to restore later)
1511 bool executeCommandSequenceInitialConfigModeState = false;
1512
1513 /// Finalize an async sequence and invoke its callback
1514 void executeCommandSequenceAsyncExecuteCallback(LD2410Async::AsyncCommandResult result);
1515
1516 /// Final step of an async sequence: restore config mode if needed and call callback
1517 void executeCommandSequenceAsyncFinalize(LD2410Async::AsyncCommandResult resultToReport);
1518
1519 /// Internal callbacks for sequence steps
1520 static void executeCommandSequenceAsyncDisableConfigModeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1521 static void executeCommandSequenceAsyncCommandCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1522
1523 /// Start executing an async sequence
1524 bool executeCommandSequenceAsync(AsyncCommandCallback callback);
1525
1526 /// Add one command to the sequence buffer
1527 bool addCommandToSequence(const byte* command);
1528
1529 /// Reset sequence buffer to empty
1530 bool resetCommandSequence();
1531
1532
1533 // ============================================================================
1534 // Inactivity handling
1535 // ============================================================================
1536
1537 /// Update last-activity timestamp ("I am alive" signal)
1538 void heartbeat();
1539
1540 bool inactivityHandlingEnabled = true; ///< Whether automatic inactivity handling is active
1541 unsigned long inactivityHandlingTimeoutMs = 60000; ///< Timeout until recovery action is triggered (ms)
1542 unsigned long lastSensorActivityTimestamp = 0; ///< Timestamp of the last sensor activity, i.e., when data from the sensor was last received
1543
1544 byte inactivityHandlingStep = 0;
1545 unsigned long lastInactivityHandlingTimestamp = 0;
1546
1547 bool handleInactivityExitConfigModeDone = false; ///< Flag to avoid repeating config-mode exit
1548
1549 /// Main inactivity handler: exit config mode or reboot if stuck
1550 void handleInactivity();
1551
1552 /// Callback for reboot triggered by inactivity handler
1553 static void handleInactivityRebootCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1554
1555 /// Callback for disabling config mode during inactivity recovery
1556 static void handleInactivityDisableConfigmodeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1557
1558
1559 // ============================================================================
1560 // Reboot handling
1561 // ============================================================================
1562
1563 /// Step 1: Enter config mode before reboot
1564 static void rebootAsyncEnableConfigModeCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1565
1566 /// Step 2: Issue reboot command
1567 static void rebootAsyncRebootCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1568
1569 void rebootAsyncFinialize(LD2410Async::AsyncCommandResult result);
1570
1571 unsigned long rebootAsyncWaitTimeoutMs = 5000;
1572 bool rebootAsyncDontWaitForNormalOperationAfterReboot = false;
1573 bool rebootAsyncPending = false;
1574
1575
1576 // ============================================================================
1577 // ACK/DATA processing
1578 // ============================================================================
1579
1580 /// Process a received ACK frame
1581 bool processAck();
1582
1583 /// Process a received DATA frame
1584 bool processData();
1585
1586
1587 // ============================================================================
1588 // Callbacks
1589 // ============================================================================
1590
1591 GenericCallback configUpdateReceivedReceivedCallback = nullptr; ///< Callback for new config data received
1592 void executeConfigUpdateReceivedCallback(); ///< Execute config-update callback
1593
1594 GenericCallback configChangedCallback = nullptr; ///< Callback for successful config change
1595 void executeConfigChangedCallback(); ///< Execute config-changed callback
1596
1597 DetectionDataCallback detectionDataCallback = nullptr; ///< Callback for new detection data
1598
1599 // ============================================================================
1600 // Command sending
1601 // ============================================================================
1602
1603 /// Send raw command bytes to the sensor
1604 void sendCommand(const byte* command);
1605
1606
1607 // ============================================================================
1608 // FreeRTOS task management
1609 // ============================================================================
1610
1611 TaskHandle_t taskHandle = NULL; ///< Handle to FreeRTOS background task
1612 bool taskStop = false; ///< Stop flag for task loop
1613 void taskLoop(); ///< Background task loop for reading data
1614
1615
1616 // ============================================================================
1617 // Async command handling
1618 // ============================================================================
1619
1620 ///< Timeout for async commands in ms (default 4000).
1621 unsigned long asyncCommandTimeoutMs = 4000;
1622
1623 /// Send a generic async command
1624 bool sendCommandAsync(const byte* command, byte retries, AsyncCommandCallback callback);
1625
1626 // If the number of retries is not defined, there will be no retries
1627 bool sendCommandAsync(const byte* command, AsyncCommandCallback callback) {
1628 return sendCommandAsync(command, 0, callback);
1629 };
1630
1631
1632 /// Invoke async command callback with result
1633 void sendCommandAsyncExecuteCallback(byte commandCode, LD2410Async::AsyncCommandResult result);
1634
1635 /// Stores the data that is needed for the execution of the callback later on
1636 void sendCommandAsyncStoreDataForCallback(const byte* command, byte retries, AsyncCommandCallback callback);
1637
1638 /// Handle async command timeout
1639 void sendCommandAsyncHandleTimeout();
1640
1641 /// Send async command that modifies configuration
1642 bool sendConfigCommandAsync(const byte* command, AsyncCommandCallback callback);
1643
1644 AsyncCommandCallback sendCommandAsyncCallback = nullptr; ///< Current async command callback
1645 unsigned long sendCommandAsyncStartMs = 0; ///< Timestamp when async command started
1646 byte sendCommandAsyncCommandCode = 0; ///< Last command code issued
1647 bool sendCommandAsyncCommandPending = false; ///< True if an async command is currently pending.
1648 byte sendCommandAsyncCommandBuffer[LD2410Defs::LD2410_Buffer_Size];
1649 byte sendCommandAsyncRetriesLeft = 0;
1650 // ============================================================================
1651 // Data processing
1652 // ============================================================================
1653
1654 /// Main dispatcher: process incoming bytes into frames, update state, trigger callbacks
1655 void processReceivedData();
1656
1657 // ============================================================================
1658 // Config mode
1659 // ============================================================================
1660 bool enableConfigModeInternalAsync(bool force, AsyncCommandCallback callback);
1661 bool enableConfigModeInternalAsync(AsyncCommandCallback callback) {
1662 return enableConfigModeInternalAsync(false, callback);
1663 }
1664
1665 bool disableConfigModeInternalAsync(bool force, AsyncCommandCallback callback);
1666
1667 bool disableConfigModeInternalAsync(AsyncCommandCallback callback) {
1668 return disableConfigModeInternalAsync(false, callback);
1669 };
1670
1671 Ticker configModeOnceTicker; // Used for a short delay when config mode just has to execute the callback
1672
1673 // ============================================================================
1674 // Config writing
1675 // ============================================================================
1676 LD2410Types::ConfigData configureAllConfigSettingsAsyncConfigDataToWrite;
1677 bool configureAllConfigSettingsAsyncWriteFullConfig = false;
1678 AsyncCommandCallback configureAllConfigSettingsAsyncConfigCallback = nullptr;
1679 bool configureAllConfigSettingsAsyncConfigActive = false;
1680 bool configureAllConfigSettingsAsyncConfigInitialConfigMode = false;
1681 AsyncCommandResult configureAllConfigSettingsAsyncResultToReport = LD2410Async::AsyncCommandResult::SUCCESS;
1682
1683 void configureAllConfigSettingsAsyncExecuteCallback(LD2410Async::AsyncCommandResult result);
1684 static void configureAllConfigSettingsAsyncConfigModeDisabledCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1685 void configureAllConfigSettingsAsyncFinalize(LD2410Async::AsyncCommandResult resultToReport);
1686 static void configureAllConfigSettingsAsyncWriteConfigCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1687 bool configureAllConfigSettingsAsyncWriteConfig();
1688 static void configureAllConfigSettingsAsyncRequestAllConfigDataCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1689 bool configureAllConfigSettingsAsyncRequestAllConfigData();
1690 static void configureAllConfigSettingsAsyncConfigModeEnabledCallback(LD2410Async* sender, LD2410Async::AsyncCommandResult result);
1691
1692 bool configureAllConfigSettingsAsyncBuildSaveChangesCommandSequence();
1693
1694};
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.
void enableInactivityHandling()
Convenience method: enables inactivity handling.
bool enableConfigModeAsync(AsyncCommandCallback callback)
Enables config mode on the radar.
void setInactivityTimeoutMs(unsigned long timeoutMs=60000)
Sets the timeout period for inactivity handling.
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.
void setAsyncCommandTimeoutMs(unsigned long timeoutMs)
Sets the timeout for async command callbacks.
bool configureDistanceResolution75cmAsync(AsyncCommandCallback callback)
Configures the distance resolution explicitly to 75 cm per gate.
LD2410Types::ConfigData configData
Current configuration parameters of the radar.
void disableInactivityHandling()
Convenience method: disables inactivity handling.
bool requestDistanceResolutionAsync(AsyncCommandCallback callback)
Requests the current distance resolution setting from the sensor.
bool asyncIsBusy()
Checks if an asynchronous command is currently pending.
unsigned long getInactivityTimeoutMs() const
Returns the current inactivity handling timeout period.
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...
unsigned long getAsyncCommandTimeoutMs() const
Returns the current async command timeout.
bool requestFirmwareAsync(AsyncCommandCallback callback)
Requests the firmware version of the sensor.
bool requestBluetoothMacAddressAsync(AsyncCommandCallback callback)
Requests the Bluetooth MAC address.
void(*) GenericCallback(LD2410Async *sender)
Generic callback signature used for simple notifications.
Definition LD2410Async.h:73
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.
void(*) DetectionDataCallback(LD2410Async *sender, bool presenceDetected)
Callback type for receiving detection data events.
Definition LD2410Async.h:87
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.
bool isEngineeringModeEnabled() const
Detects if engineering mode is enabled.
void onDetectionDataReceived(DetectionDataCallback callback)
Registers a callback for new detection data.
bool enableEngineeringModeAsync(AsyncCommandCallback callback)
Enables engineering mode.
bool isInactivityHandlingEnabled() const
Returns whether inactivity handling is currently enabled.
const LD2410Types::DetectionData & getDetectionDataRef() const
Access the current detection data without making a copy.
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.
const LD2410Types::ConfigData & getConfigDataRef() const
Access the current config data without making a copy.
bool enableBluetoothAsync(AsyncCommandCallback callback)
Enables Bluetooth.
constexpr size_t LD2410_Buffer_Size
Definition LD2410Defs.h:11
AutoConfigStatus
State of the automatic threshold configuration routine.
@ NOT_SET
Status not yet retrieved.
OutputControl
Logic level behavior of the auxiliary output pin.
DistanceResolution
Distance resolution per gate for detection.
LightControl
Light-dependent control status of the auxiliary output.
Definition LD2410Types.h:93
Holds the most recent detection data reported by the radar.