ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
ld2420.cpp
Go to the documentation of this file.
1#include "ld2420.h"
4
5/*
6Configure commands - little endian
7
8No command can exceed 64 bytes, otherwise they would need be to be split up into multiple sends.
9
10All send command frames will have:
11 Header = FD FC FB FA, Bytes 0 - 3, uint32_t 0xFAFBFCFD
12 Length, bytes 4 - 5, uint16_t 0x0002, must be at least 2 for the command byte if no addon data.
13 Command bytes 6 - 7, uint16_t
14 Footer = 04 03 02 01 - uint32_t 0x01020304, Always last 4 Bytes.
15Receive
16 Error bytes 8-9 uint16_t, 0 = success, all other positive values = error
17
18Enable config mode:
19Send:
20 UART Tx: FD FC FB FA 04 00 FF 00 02 00 04 03 02 01
21 Command = FF 00 - uint16_t 0x00FF
22 Protocol version = 02 00, can be 1 or 2 - uint16_t 0x0002
23Reply:
24 UART Rx: FD FC FB FA 06 00 FF 01 00 00 02 00 04 03 02 01
25
26Disable config mode:
27Send:
28 UART Tx: FD FC FB FA 02 00 FE 00 04 03 02 01
29 Command = FE 00 - uint16_t 0x00FE
30Receive:
31 UART Rx: FD FC FB FA 04 00 FE 01 00 00 04 03 02 01
32
33Configure system parameters:
34
35UART Tx: FD FC FB FA 08 00 12 00 00 00 64 00 00 00 04 03 02 01 Set system parms
36Command = 12 00 - uint16_t 0x0012, Param
37There are three documented parameters for modes:
38 00 64 = Basic status mode
39 This mode outputs text as presence "ON" or "OFF" and "Range XXXX"
40 where XXXX is a decimal value for distance in cm
41 00 04 = Energy output mode
42 This mode outputs detailed signal energy values for each gate and the target distance.
43 The data format consist of the following.
44 Header HH, Length LL, Presence PP, Distance DD, 16 Gate Energies EE, Footer FF
45 HH HH HH HH LL LL PP DD DD EE EE .. 16x .. FF FF FF FF
46 F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5
47 00 00 = debug output mode
48 This mode outputs detailed values consisting of 20 Dopplers, 16 Ranges for a total 20 * 16 * 4 bytes
49 The data format consist of the following.
50 Header HH, Doppler DD, Range RR, Footer FF
51 HH HH HH HH DD DD DD DD .. 20x .. RR RR RR RR .. 16x .. FF FF FF FF
52 AA BF 10 14 00 00 00 00 .. .. .. .. 00 00 00 00 .. .. .. .. FD FC FB FA
53
54Configure gate sensitivity parameters:
55UART Tx: FD FC FB FA 0E 00 07 00 10 00 60 EA 00 00 20 00 60 EA 00 00 04 03 02 01
56Command = 12 00 - uint16_t 0x0007
57Gate 0 high thresh = 10 00 uint16_t 0x0010, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
58Gate 0 low thresh = 20 00 uint16_t 0x0020, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
59*/
60
61namespace esphome::ld2420 {
62
63ESPHOME_LOG_TAG(TAG, "ld2420");
64
65// Local const's
66static constexpr uint16_t REFRESH_RATE_MS = 1000;
67
68// Command sets
69static constexpr uint16_t CMD_DISABLE_CONF = 0x00FE;
70static constexpr uint16_t CMD_ENABLE_CONF = 0x00FF;
71static constexpr uint16_t CMD_PARM_HIGH_TRESH = 0x0012;
72static constexpr uint16_t CMD_PARM_LOW_TRESH = 0x0021;
73static constexpr uint16_t CMD_PROTOCOL_VER = 0x0002;
74static constexpr uint16_t CMD_READ_ABD_PARAM = 0x0008;
75static constexpr uint16_t CMD_READ_REG_ADDR = 0x0020;
76static constexpr uint16_t CMD_READ_REGISTER = 0x0002;
77static constexpr uint16_t CMD_READ_SERIAL_NUM = 0x0011;
78static constexpr uint16_t CMD_READ_SYS_PARAM = 0x0013;
79static constexpr uint16_t CMD_READ_VERSION = 0x0000;
80static constexpr uint16_t CMD_RESTART = 0x0068;
81static constexpr uint16_t CMD_SYSTEM_MODE = 0x0000;
82static constexpr uint16_t CMD_SYSTEM_MODE_GR = 0x0003;
83static constexpr uint16_t CMD_SYSTEM_MODE_MTT = 0x0001;
84static constexpr uint16_t CMD_SYSTEM_MODE_SIMPLE = 0x0064;
85static constexpr uint16_t CMD_SYSTEM_MODE_DEBUG = 0x0000;
86static constexpr uint16_t CMD_SYSTEM_MODE_ENERGY = 0x0004;
87static constexpr uint16_t CMD_SYSTEM_MODE_VS = 0x0002;
88static constexpr uint16_t CMD_WRITE_ABD_PARAM = 0x0007;
89static constexpr uint16_t CMD_WRITE_REGISTER = 0x0001;
90static constexpr uint16_t CMD_WRITE_SYS_PARAM = 0x0012;
91
92static constexpr uint8_t CMD_ABD_DATA_REPLY_SIZE = 0x04;
93static constexpr uint8_t CMD_ABD_DATA_REPLY_START = 0x0A;
94static constexpr uint8_t CMD_MAX_BYTES = 0x64;
95static constexpr uint8_t CMD_REG_DATA_REPLY_SIZE = 0x02;
96
97static constexpr uint8_t LD2420_ERROR_NONE = 0x00;
98static constexpr uint8_t LD2420_ERROR_TIMEOUT = 0x02;
99static constexpr uint8_t LD2420_ERROR_UNKNOWN = 0x01;
100
101// Register address values
102static constexpr uint16_t CMD_MIN_GATE_REG = 0x0000;
103static constexpr uint16_t CMD_MAX_GATE_REG = 0x0001;
104static constexpr uint16_t CMD_TIMEOUT_REG = 0x0004;
105static constexpr uint16_t CMD_GATE_MOVE_THRESH[TOTAL_GATES] = {0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015,
106 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B,
107 0x001C, 0x001D, 0x001E, 0x001F};
108static constexpr uint16_t CMD_GATE_STILL_THRESH[TOTAL_GATES] = {0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
109 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B,
110 0x002C, 0x002D, 0x002E, 0x002F};
111static constexpr uint32_t FACTORY_MOVE_THRESH[TOTAL_GATES] = {60000, 30000, 400, 250, 250, 250, 250, 250,
112 250, 250, 250, 250, 250, 250, 250, 250};
113static constexpr uint32_t FACTORY_STILL_THRESH[TOTAL_GATES] = {40000, 20000, 200, 200, 200, 200, 200, 150,
114 150, 100, 100, 100, 100, 100, 100, 100};
115static constexpr uint16_t FACTORY_TIMEOUT = 120;
116static constexpr uint16_t FACTORY_MIN_GATE = 1;
117static constexpr uint16_t FACTORY_MAX_GATE = 12;
118
119// COMMAND_BYTE Header & Footer
120static constexpr uint32_t CMD_FRAME_FOOTER = 0x01020304;
121static constexpr uint32_t CMD_FRAME_HEADER = 0xFAFBFCFD;
122static constexpr uint32_t DEBUG_FRAME_FOOTER = 0xFAFBFCFD;
123static constexpr uint32_t DEBUG_FRAME_HEADER = 0x1410BFAA;
124static constexpr uint32_t ENERGY_FRAME_FOOTER = 0xF5F6F7F8;
125static constexpr uint32_t ENERGY_FRAME_HEADER = 0xF1F2F3F4;
126static constexpr int CALIBRATE_VERSION_MIN = 154;
127static constexpr uint8_t CMD_FRAME_COMMAND = 6;
128static constexpr uint8_t CMD_FRAME_DATA_LENGTH = 4;
129static constexpr uint8_t CMD_FRAME_STATUS = 7;
130static constexpr uint8_t CMD_ERROR_WORD = 8;
131static constexpr uint8_t ENERGY_SENSOR_START = 9;
132static constexpr uint8_t CALIBRATE_REPORT_INTERVAL = 4;
133static const char *const OP_NORMAL_MODE_STRING = "Normal";
134static const char *const OP_SIMPLE_MODE_STRING = "Simple";
135
136// Memory-efficient lookup tables
137struct StringToUint8 {
138 const char *str;
139 const uint8_t value;
140};
141
142static constexpr StringToUint8 OP_MODE_BY_STR[] = {
143 {"Normal", OP_NORMAL_MODE},
144 {"Calibrate", OP_CALIBRATE_MODE},
145 {"Simple", OP_SIMPLE_MODE},
146};
147
148static constexpr const char *ERR_MESSAGE[] = {
149 "None",
150 "Unknown",
151 "Timeout",
152};
153
154// Helper function for lookups
155template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) {
156 for (const auto &entry : arr) {
157 if (str == entry.str) {
158 return entry.value;
159 }
160 }
161 return 0xFF; // Not found
162}
163
164static uint8_t calc_checksum(const void *data, size_t size) {
165 return xor_bytes(static_cast<const uint8_t *>(data), size);
166}
167
168static int32_t get_firmware_int(const char *version_string) {
169 // Convert "v1.5.4" -> 154 by skipping 'v' and '.', accumulating digits
170 const char *p = (*version_string == 'v') ? version_string + 1 : version_string;
171 int32_t result = 0;
172 for (; *p != '\0'; p++) {
173 if (*p == '.')
174 continue;
175 if (*p < '0' || *p > '9')
176 return 0;
177 result = result * 10 + (*p - '0');
178 }
179 return result;
180}
181
183 ESP_LOGCONFIG(TAG,
184 "LD2420:\n"
185 " Firmware version: %7s",
186 this->firmware_ver_);
187#ifdef USE_NUMBER
188 ESP_LOGCONFIG(TAG, "Number:");
189 LOG_NUMBER(" ", "Gate Timeout:", this->gate_timeout_number_);
190 LOG_NUMBER(" ", "Gate Max Distance:", this->max_gate_distance_number_);
191 LOG_NUMBER(" ", "Gate Min Distance:", this->min_gate_distance_number_);
192 LOG_NUMBER(" ", "Gate Select:", this->gate_select_number_);
193 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
194 LOG_NUMBER(" ", "Gate Move Threshold:", this->gate_move_threshold_numbers_[gate]);
195 LOG_NUMBER(" ", "Gate Still Threshold::", this->gate_still_threshold_numbers_[gate]);
196 }
197#endif
198#ifdef USE_BUTTON
199 LOG_BUTTON(" ", "Apply Config:", this->apply_config_button_);
200 LOG_BUTTON(" ", "Revert Edits:", this->revert_config_button_);
201 LOG_BUTTON(" ", "Factory Reset:", this->factory_reset_button_);
202 LOG_BUTTON(" ", "Restart Module:", this->restart_module_button_);
203#endif
204#ifdef USE_SELECT
205 ESP_LOGCONFIG(TAG, "Select:");
206 LOG_SELECT(" ", "Operating Mode", this->operating_selector_);
207#endif
208 if (ld2420::get_firmware_int(this->firmware_ver_) < CALIBRATE_VERSION_MIN) {
209 ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->firmware_ver_);
210 }
211}
212
214 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
215 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
216 this->mark_failed();
217 return;
218 }
220#ifdef USE_NUMBER
222#endif
223 this->get_firmware_version_();
224 const char *pfw = this->firmware_ver_;
225 std::string fw_str(pfw);
226
227 for (auto &listener : this->listeners_) {
228 listener->on_fw_version(fw_str);
229 }
230
231 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
233 this->get_gate_threshold_(gate);
234 }
235
236 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
237 if (ld2420::get_firmware_int(this->firmware_ver_) < CALIBRATE_VERSION_MIN) {
238 this->set_operating_mode(OP_SIMPLE_MODE_STRING);
239#ifdef USE_SELECT
240 if (this->operating_selector_ != nullptr) {
241 this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
242 }
243#endif
244 this->set_mode_(CMD_SYSTEM_MODE_SIMPLE);
245 ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->firmware_ver_);
246 } else {
247 this->set_mode_(CMD_SYSTEM_MODE_ENERGY);
248#ifdef USE_SELECT
249 if (this->operating_selector_ != nullptr) {
250 this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING);
251 }
252#endif
253 }
254#ifdef USE_NUMBER
256#endif
257 this->set_system_mode(this->system_mode_);
258 this->set_config_mode(false);
259}
260
262 const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config));
263 if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) {
264 ESP_LOGD(TAG, "No configuration change detected");
265 return;
266 }
267 ESP_LOGD(TAG, "Reconfiguring");
268 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
269 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
270 this->mark_failed();
271 return;
272 }
273 this->set_min_max_distances_timeout(this->new_config.max_gate, this->new_config.min_gate, this->new_config.timeout);
274 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
276 this->set_gate_threshold(gate);
277 }
278 memcpy(&current_config, &new_config, sizeof(new_config));
279#ifdef USE_NUMBER
281#endif
282 this->set_system_mode(this->system_mode_);
283 this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm
284 this->set_operating_mode(OP_NORMAL_MODE_STRING);
285}
286
288 ESP_LOGD(TAG, "Setting factory defaults");
289 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
290 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
291 this->mark_failed();
292 return;
293 }
294 this->set_min_max_distances_timeout(FACTORY_MAX_GATE, FACTORY_MIN_GATE, FACTORY_TIMEOUT);
295#ifdef USE_NUMBER
296 this->gate_timeout_number_->state = FACTORY_TIMEOUT;
297 this->min_gate_distance_number_->state = FACTORY_MIN_GATE;
298 this->max_gate_distance_number_->state = FACTORY_MAX_GATE;
299#endif
300 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
301 this->new_config.move_thresh[gate] = FACTORY_MOVE_THRESH[gate];
302 this->new_config.still_thresh[gate] = FACTORY_STILL_THRESH[gate];
304 this->set_gate_threshold(gate);
305 }
306 memcpy(&this->current_config, &this->new_config, sizeof(this->new_config));
307 this->set_system_mode(this->system_mode_);
308 this->set_config_mode(false);
309#ifdef USE_NUMBER
312#endif
313}
314
316 ESP_LOGD(TAG, "Restarting");
317 this->send_module_restart();
318 this->set_timeout(250, [this]() {
319 this->set_config_mode(true);
320 this->set_system_mode(this->system_mode_);
321 this->set_config_mode(false);
322 });
323}
324
326 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
327#ifdef USE_NUMBER
329#endif
330 ESP_LOGD(TAG, "Reverted config number edits");
331}
332
334 // If there is a active send command do not process it here, the send command call will handle it.
335 if (this->cmd_active_) {
336 return;
337 }
338 this->read_batch_(this->buffer_data_);
339}
340
341void LD2420Component::update_radar_data(uint16_t const *gate_energy, uint8_t sample_number) {
342 for (uint8_t gate = 0; gate < TOTAL_GATES; ++gate) {
343 this->radar_data[gate][sample_number] = gate_energy[gate];
344 }
346}
347
349 // Calculate average and peak values for each gate
350 const float move_factor = gate_move_sensitivity_factor + 1;
351 const float still_factor = (gate_still_sensitivity_factor / 2) + 1;
352 for (uint8_t gate = 0; gate < TOTAL_GATES; ++gate) {
353 uint32_t sum = 0;
354 uint16_t peak = 0;
355
356 for (uint8_t sample_number = 0; sample_number < CALIBRATE_SAMPLES; ++sample_number) {
357 // Calculate average
358 sum += this->radar_data[gate][sample_number];
359
360 // Calculate max value
361 if (this->radar_data[gate][sample_number] > peak) {
362 peak = this->radar_data[gate][sample_number];
363 }
364 }
365
366 // Store average and peak values
367 this->gate_avg[gate] = sum / CALIBRATE_SAMPLES;
368 if (this->gate_peak[gate] < peak) {
369 this->gate_peak[gate] = peak;
370 }
371
372 uint32_t calculated_value =
373 (static_cast<uint32_t>(this->gate_peak[gate]) + (move_factor * static_cast<uint32_t>(this->gate_peak[gate])));
374 this->new_config.move_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
375 calculated_value =
376 (static_cast<uint32_t>(this->gate_peak[gate]) + (still_factor * static_cast<uint32_t>(this->gate_peak[gate])));
377 this->new_config.still_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
378 }
379}
380
382 for (uint8_t gate = 0; gate < TOTAL_GATES; ++gate) {
383 // Output results
384 ESP_LOGI(TAG, "Gate: %2d Avg: %5d Peak: %5d", gate, this->gate_avg[gate], this->gate_peak[gate]);
385 }
386 ESP_LOGI(TAG, "Total samples: %d", this->total_sample_number_counter);
387}
388
390 // If unsupported firmware ignore mode select
391 if (ld2420::get_firmware_int(firmware_ver_) >= CALIBRATE_VERSION_MIN) {
392 this->current_operating_mode = find_uint8(OP_MODE_BY_STR, state);
393 // Entering Auto Calibrate we need to clear the previous data collection
394#ifdef USE_SELECT
395 if (this->operating_selector_ != nullptr) {
397 }
398#endif
400 this->set_calibration_(true);
401 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
402 this->gate_avg[gate] = 0;
403 this->gate_peak[gate] = 0;
404 for (uint8_t i = 0; i < CALIBRATE_SAMPLES; i++) {
405 this->radar_data[gate][i] = 0;
406 }
408 }
409 } else {
410 // Set the current data back so we don't have new data that can be applied in error.
411 if (this->get_calibration_()) {
412 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
413 }
414 this->set_calibration_(false);
415 }
416 } else {
418#ifdef USE_SELECT
419 if (this->operating_selector_ != nullptr) {
420 this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
421 }
422#endif
423 }
424}
425
426void LD2420Component::readline_(int rx_data, uint8_t *buffer, int len) {
427 if (rx_data < 0) {
428 return; // No data available
429 }
430 if (this->buffer_pos_ < len - 1) {
431 buffer[this->buffer_pos_++] = rx_data;
432 buffer[this->buffer_pos_] = 0;
433 } else {
434 // We should never get here, but just in case...
435 ESP_LOGW(TAG, "Max command length exceeded; ignoring");
436 this->buffer_pos_ = 0;
437 }
438 if (this->buffer_pos_ < 4) {
439 return; // Not enough data to process yet
440 }
441 if (memcmp(&buffer[this->buffer_pos_ - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) {
442 this->cmd_active_ = false; // Set command state to inactive after response
443 this->handle_ack_data_(buffer, this->buffer_pos_);
444 this->buffer_pos_ = 0;
445 } else if ((buffer[this->buffer_pos_ - 2] == 0x0D && buffer[this->buffer_pos_ - 1] == 0x0A) &&
446 (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) {
447 this->handle_simple_mode_(buffer, this->buffer_pos_);
448 this->buffer_pos_ = 0;
449 } else if ((memcmp(&buffer[this->buffer_pos_ - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) &&
450 (this->get_mode_() == CMD_SYSTEM_MODE_ENERGY)) {
451 this->handle_energy_mode_(buffer, this->buffer_pos_);
452 this->buffer_pos_ = 0;
453 }
454}
455
457 uint8_t index = 6; // Start at presence byte position
458 uint16_t range;
459 const uint8_t elements = sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]);
460 if (len < static_cast<int>(index + 1 + sizeof(range) + elements * sizeof(this->gate_energy_[0]))) {
461 ESP_LOGW(TAG, "Energy frame too short: %d bytes", len);
462 return;
463 }
464 this->set_presence_(buffer[index]);
465 index++;
466 memcpy(&range, &buffer[index], sizeof(range));
467 index += sizeof(range);
468 this->set_distance_(range);
469 for (uint8_t i = 0; i < elements; i++) { // NOLINT
470 memcpy(&this->gate_energy_[i], &buffer[index], sizeof(this->gate_energy_[0]));
471 index += sizeof(this->gate_energy_[0]);
472 }
473
476 this->sample_number_counter++;
477 if (this->sample_number_counter >= CALIBRATE_SAMPLES) {
478 this->sample_number_counter = 0;
479 }
480 }
481
482 // Resonable refresh rate for home assistant database size health
483 const int32_t current_millis = App.get_loop_component_start_time();
484 if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) {
485 return;
486 }
487 this->last_periodic_millis = current_millis;
488 for (auto &listener : this->listeners_) {
489 listener->on_distance(this->get_distance_());
490 listener->on_presence(this->get_presence_());
491 listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]));
492 }
493
496 if (current_millis - this->report_periodic_millis > REFRESH_RATE_MS * CALIBRATE_REPORT_INTERVAL) {
497 this->report_periodic_millis = current_millis;
498 this->report_gate_data();
499 }
500 }
501}
502
503void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
504 const uint8_t bufsize = 16;
505 uint8_t index{0};
506 uint8_t pos{0};
507 char *endptr{nullptr};
508 char outbuf[bufsize]{0};
509 while (true) {
510 if (pos >= 2 && inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') {
511 this->set_presence_(false);
512 } else if (pos >= 1 && inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') {
513 this->set_presence_(true);
514 }
515 if (inbuf[pos] >= '0' && inbuf[pos] <= '9') {
516 if (index < bufsize - 1) {
517 outbuf[index++] = inbuf[pos];
518 }
519 }
520 if (pos < len - 1) {
521 pos++;
522 } else {
523 break;
524 }
525 }
526 outbuf[index] = '\0';
527 if (index > 1) {
528 this->set_distance_(strtol(outbuf, &endptr, 10));
529 }
530
531 if (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE) {
532 // Resonable refresh rate for home assistant database size health
533 const int32_t current_millis = App.get_loop_component_start_time();
534 if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) {
535 return;
536 }
537 this->last_normal_periodic_millis = current_millis;
538 for (auto &listener : this->listeners_)
539 listener->on_distance(this->get_distance_());
540 for (auto &listener : this->listeners_)
541 listener->on_presence(this->get_presence_());
542 }
543}
544
545void LD2420Component::read_batch_(std::span<uint8_t, MAX_LINE_LENGTH> buffer) {
546 // Read all available bytes in batches to reduce UART call overhead.
547 size_t avail = this->available();
548 uint8_t buf[MAX_LINE_LENGTH];
549 while (avail > 0) {
550 size_t to_read = std::min(avail, sizeof(buf));
551 if (!this->read_array(buf, to_read)) {
552 break;
553 }
554 avail -= to_read;
555
556 for (size_t i = 0; i < to_read; i++) {
557 this->readline_(buf[i], buffer.data(), buffer.size());
558 }
559 }
560}
561
563 this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
564 this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];
565 uint16_t data_pos = 0;
566 if (this->cmd_reply_.length > CMD_MAX_BYTES) {
567 ESP_LOGW(TAG, "Reply frame too long");
568 return;
569 } else if (this->cmd_reply_.length < 2) {
570 ESP_LOGW(TAG, "Command frame too short");
571 return;
572 }
573 memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error));
574 const char *result = this->cmd_reply_.error ? "failure" : "success";
575 if (this->cmd_reply_.error > 0) {
576 return;
577 };
578 this->cmd_reply_.ack = true;
579 switch ((uint16_t) this->cmd_reply_.command) {
580 case (CMD_ENABLE_CONF):
581 ESP_LOGV(TAG, "Set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result);
582 break;
583 case (CMD_DISABLE_CONF):
584 ESP_LOGV(TAG, "Set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result);
585 break;
586 case (CMD_READ_REGISTER): {
587 ESP_LOGV(TAG, "Read register: CMD = %2X %s", CMD_READ_REGISTER, result);
588 // TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file
589 data_pos = 0x0A;
590 uint16_t reg_count = std::min<uint16_t>((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_REG_DATA_REPLY_SIZE,
591 sizeof(this->cmd_reply_.data) / sizeof(this->cmd_reply_.data[0]));
592 for (uint16_t i = 0; i < reg_count; i++) {
593 memcpy(&this->cmd_reply_.data[i], &buffer[data_pos + i * CMD_REG_DATA_REPLY_SIZE], CMD_REG_DATA_REPLY_SIZE);
594 }
595 break;
596 }
597 case (CMD_WRITE_REGISTER):
598 ESP_LOGV(TAG, "Write register: CMD = %2X %s", CMD_WRITE_REGISTER, result);
599 break;
600 case (CMD_WRITE_ABD_PARAM):
601 ESP_LOGV(TAG, "Write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result);
602 break;
603 case (CMD_READ_ABD_PARAM): {
604 ESP_LOGV(TAG, "Read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result);
605 data_pos = CMD_ABD_DATA_REPLY_START;
606 uint16_t abd_count = std::min<uint16_t>((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE,
607 sizeof(this->cmd_reply_.data) / sizeof(this->cmd_reply_.data[0]));
608 for (uint16_t i = 0; i < abd_count; i++) {
609 memcpy(&this->cmd_reply_.data[i], &buffer[data_pos + i * CMD_ABD_DATA_REPLY_SIZE],
610 sizeof(this->cmd_reply_.data[i]));
611 }
612 break;
613 }
614 case (CMD_WRITE_SYS_PARAM):
615 ESP_LOGV(TAG, "Set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result);
616 break;
617 case (CMD_READ_VERSION): {
618 uint8_t ver_len = std::min<uint8_t>(buffer[10], sizeof(this->firmware_ver_) - 1);
619 memcpy(this->firmware_ver_, &buffer[12], ver_len);
620 this->firmware_ver_[ver_len] = '\0';
621 ESP_LOGV(TAG, "Firmware version: %s %s", this->firmware_ver_, result);
622 break;
623 }
624 default:
625 break;
626 }
627}
628
630 uint32_t start_millis = millis();
631 uint8_t error = 0;
632 uint8_t ack_buffer[MAX_LINE_LENGTH];
633 uint8_t cmd_buffer[MAX_LINE_LENGTH];
634 this->cmd_reply_.ack = false;
635 if (frame.command != CMD_RESTART) {
636 this->cmd_active_ = true;
637 } // Restart does not reply, thus no ack state required
638 uint8_t retry = 3;
639 while (retry) {
640 frame.length = 0;
641 uint16_t frame_data_bytes = frame.data_length + 2; // Always add two bytes for the cmd size
642
643 memcpy(&cmd_buffer[frame.length], &frame.header, sizeof(frame.header));
644 frame.length += sizeof(frame.header);
645
646 memcpy(&cmd_buffer[frame.length], &frame_data_bytes, sizeof(frame.data_length));
647 frame.length += sizeof(frame.data_length);
648
649 memcpy(&cmd_buffer[frame.length], &frame.command, sizeof(frame.command));
650 frame.length += sizeof(frame.command);
651
652 for (uint16_t index = 0; index < frame.data_length; index++) {
653 memcpy(&cmd_buffer[frame.length], &frame.data[index], sizeof(frame.data[index]));
654 frame.length += sizeof(frame.data[index]);
655 }
656
657 memcpy(cmd_buffer + frame.length, &frame.footer, sizeof(frame.footer));
658 frame.length += sizeof(frame.footer);
659 this->write_array(cmd_buffer, frame.length);
660
661 error = 0;
662 if (frame.command == CMD_RESTART) {
663 return 0; // restart does not reply exit now
664 }
665
666 while (!this->cmd_reply_.ack) {
667 while (this->available()) {
668 this->readline_(this->read(), ack_buffer, sizeof(ack_buffer));
669 }
671 // Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT.
672 if ((millis() - start_millis) > 1000) {
673 start_millis = millis();
674 error = LD2420_ERROR_TIMEOUT;
675 retry--;
676 break;
677 }
678 }
679 if (this->cmd_reply_.ack) {
680 retry = 0;
681 }
682 if (this->cmd_reply_.error > 0) {
683 this->handle_cmd_error(this->cmd_reply_.error);
684 }
685 }
686 this->cmd_active_ = false; // Resend and give-up are done; let loop() read the UART again
687 return error;
688}
689
691 CmdFrameT cmd_frame;
692 cmd_frame.data_length = 0;
693 cmd_frame.header = CMD_FRAME_HEADER;
694 cmd_frame.command = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
695 if (enable) {
696 memcpy(&cmd_frame.data[0], &CMD_PROTOCOL_VER, sizeof(CMD_PROTOCOL_VER));
697 cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER);
698 }
699 cmd_frame.footer = CMD_FRAME_FOOTER;
700 ESP_LOGV(TAG, "Sending set config %s command: %2X", enable ? LOG_STR_LITERAL("enable") : LOG_STR_LITERAL("disable"),
701 cmd_frame.command);
702 return this->send_cmd_from_array(cmd_frame);
703}
704
705// Sends a restart and set system running mode to normal
707
709 CmdFrameT cmd_frame;
710 cmd_frame.data_length = 0;
711 cmd_frame.header = CMD_FRAME_HEADER;
712 cmd_frame.command = CMD_RESTART;
713 cmd_frame.footer = CMD_FRAME_FOOTER;
714 ESP_LOGV(TAG, "Sending restart command: %2X", cmd_frame.command);
715 this->send_cmd_from_array(cmd_frame);
716}
717
719 CmdFrameT cmd_frame;
720 cmd_frame.data_length = 0;
721 cmd_frame.header = CMD_FRAME_HEADER;
722 cmd_frame.command = CMD_READ_REGISTER;
723 cmd_frame.data[1] = reg;
724 cmd_frame.data_length += 2;
725 cmd_frame.footer = CMD_FRAME_FOOTER;
726 ESP_LOGV(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command);
727 this->send_cmd_from_array(cmd_frame);
728}
729
730void LD2420Component::set_reg_value(uint16_t reg, uint16_t value) {
731 CmdFrameT cmd_frame;
732 cmd_frame.data_length = 0;
733 cmd_frame.header = CMD_FRAME_HEADER;
734 cmd_frame.command = CMD_WRITE_REGISTER;
735 memcpy(&cmd_frame.data[cmd_frame.data_length], &reg, CMD_REG_DATA_REPLY_SIZE);
736 cmd_frame.data_length += 2;
737 memcpy(&cmd_frame.data[cmd_frame.data_length], &value, CMD_REG_DATA_REPLY_SIZE);
738 cmd_frame.data_length += 2;
739 cmd_frame.footer = CMD_FRAME_FOOTER;
740 ESP_LOGV(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value);
741 this->send_cmd_from_array(cmd_frame);
742}
743
745 if (error < std::size(ERR_MESSAGE)) {
746 ESP_LOGE(TAG, "Command failed: %s", ERR_MESSAGE[error]);
747 } else {
748 // The error word comes from the device reply frame; unknown codes must not index ERR_MESSAGE
749 ESP_LOGE(TAG, "Command failed: error 0x%04X", error);
750 }
751}
752
754 uint8_t error;
755 CmdFrameT cmd_frame;
756 cmd_frame.data_length = 0;
757 cmd_frame.header = CMD_FRAME_HEADER;
758 cmd_frame.command = CMD_READ_ABD_PARAM;
759 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_MOVE_THRESH[gate], sizeof(CMD_GATE_MOVE_THRESH[gate]));
760 cmd_frame.data_length += 2;
761 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate]));
762 cmd_frame.data_length += 2;
763 cmd_frame.footer = CMD_FRAME_FOOTER;
764 ESP_LOGV(TAG, "Sending read gate %d high/low threshold command: %2X", gate, cmd_frame.command);
765 error = this->send_cmd_from_array(cmd_frame);
766 if (error == 0) {
767 this->current_config.move_thresh[gate] = cmd_reply_.data[0];
769 }
770 return error;
771}
772
774 uint8_t error;
775 CmdFrameT cmd_frame;
776 cmd_frame.data_length = 0;
777 cmd_frame.header = CMD_FRAME_HEADER;
778 cmd_frame.command = CMD_READ_ABD_PARAM;
779 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
780 sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
781 cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
782 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
783 sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
784 cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
785 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
786 sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
787 cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
788 cmd_frame.footer = CMD_FRAME_FOOTER;
789 ESP_LOGV(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command);
790 error = this->send_cmd_from_array(cmd_frame);
791 if (error == 0) {
792 this->current_config.min_gate = (uint16_t) cmd_reply_.data[0];
793 this->current_config.max_gate = (uint16_t) cmd_reply_.data[1];
794 this->current_config.timeout = (uint16_t) cmd_reply_.data[2];
795 }
796 return error;
797}
798
800 CmdFrameT cmd_frame;
801 uint16_t unknown_parm = 0x0000;
802 cmd_frame.data_length = 0;
803 cmd_frame.header = CMD_FRAME_HEADER;
804 cmd_frame.command = CMD_WRITE_SYS_PARAM;
805 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_SYSTEM_MODE, sizeof(CMD_SYSTEM_MODE));
806 cmd_frame.data_length += sizeof(CMD_SYSTEM_MODE);
807 memcpy(&cmd_frame.data[cmd_frame.data_length], &mode, sizeof(mode));
808 cmd_frame.data_length += sizeof(mode);
809 memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm));
810 cmd_frame.data_length += sizeof(unknown_parm);
811 cmd_frame.footer = CMD_FRAME_FOOTER;
812 ESP_LOGV(TAG, "Sending write system mode command: %2X", cmd_frame.command);
813 if (this->send_cmd_from_array(cmd_frame) == 0) {
814 this->set_mode_(mode);
815 }
816}
817
819 CmdFrameT cmd_frame;
820 cmd_frame.data_length = 0;
821 cmd_frame.header = CMD_FRAME_HEADER;
822 cmd_frame.command = CMD_READ_VERSION;
823 cmd_frame.footer = CMD_FRAME_FOOTER;
824
825 ESP_LOGV(TAG, "Sending read firmware version command: %2X", cmd_frame.command);
826 this->send_cmd_from_array(cmd_frame);
827}
828
829void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, // NOLINT
830 uint32_t timeout) {
831 // Header H, Length L, Register R, Value V, Footer F
832 // |Min Gate |Max Gate |Timeout |
833 // HH HH HH HH LL LL CC CC RR RR VV VV VV VV RR RR VV VV VV VV RR RR VV VV VV VV FF FF FF FF
834 // FD FC FB FA 14 00 07 00 00 00 01 00 00 00 01 00 09 00 00 00 04 00 0A 00 00 00 04 03 02 01 e.g.
835
836 CmdFrameT cmd_frame;
837 cmd_frame.data_length = 0;
838 cmd_frame.header = CMD_FRAME_HEADER;
839 cmd_frame.command = CMD_WRITE_ABD_PARAM;
840 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
841 sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
842 cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
843 memcpy(&cmd_frame.data[cmd_frame.data_length], &min_gate_distance, sizeof(min_gate_distance));
844 cmd_frame.data_length += sizeof(min_gate_distance);
845 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
846 sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
847 cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
848 memcpy(&cmd_frame.data[cmd_frame.data_length], &max_gate_distance, sizeof(max_gate_distance));
849 cmd_frame.data_length += sizeof(max_gate_distance);
850 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
851 sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
852 cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
853 memcpy(&cmd_frame.data[cmd_frame.data_length], &timeout, sizeof(timeout));
854 ;
855 cmd_frame.data_length += sizeof(timeout);
856 cmd_frame.footer = CMD_FRAME_FOOTER;
857
858 ESP_LOGV(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command);
859 this->send_cmd_from_array(cmd_frame);
860}
861
863 // Header H, Length L, Command C, Register R, Value V, Footer F
864 // HH HH HH HH LL LL CC CC RR RR VV VV VV VV RR RR VV VV VV VV FF FF FF FF
865 // FD FC FB FA 14 00 07 00 10 00 00 FF 00 00 00 01 00 0F 00 00 04 03 02 01
866
867 uint16_t move_threshold_gate = CMD_GATE_MOVE_THRESH[gate];
868 uint16_t still_threshold_gate = CMD_GATE_STILL_THRESH[gate];
869 CmdFrameT cmd_frame;
870 cmd_frame.data_length = 0;
871 cmd_frame.header = CMD_FRAME_HEADER;
872 cmd_frame.command = CMD_WRITE_ABD_PARAM;
873 memcpy(&cmd_frame.data[cmd_frame.data_length], &move_threshold_gate, sizeof(move_threshold_gate));
874 cmd_frame.data_length += sizeof(move_threshold_gate);
875 memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.move_thresh[gate],
876 sizeof(this->new_config.move_thresh[gate]));
877 cmd_frame.data_length += sizeof(this->new_config.move_thresh[gate]);
878 memcpy(&cmd_frame.data[cmd_frame.data_length], &still_threshold_gate, sizeof(still_threshold_gate));
879 cmd_frame.data_length += sizeof(still_threshold_gate);
880 memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.still_thresh[gate],
881 sizeof(this->new_config.still_thresh[gate]));
882 cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]);
883 cmd_frame.footer = CMD_FRAME_FOOTER;
884 ESP_LOGV(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command);
885 this->send_cmd_from_array(cmd_frame);
886}
887
888#ifdef USE_NUMBER
890 if (this->gate_timeout_number_ != nullptr) {
891 this->gate_timeout_number_->publish_state(static_cast<uint16_t>(this->current_config.timeout));
892 }
893 if (this->gate_select_number_ != nullptr) {
895 }
896 if (this->min_gate_distance_number_ != nullptr) {
897 this->min_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.min_gate));
898 }
899 if (this->max_gate_distance_number_ != nullptr) {
900 this->max_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.max_gate));
901 }
902 if (this->gate_move_sensitivity_factor_number_ != nullptr) {
904 }
905 if (this->gate_still_sensitivity_factor_number_ != nullptr) {
907 }
908 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
909 if (this->gate_still_threshold_numbers_[gate] != nullptr) {
910 this->gate_still_threshold_numbers_[gate]->publish_state(
911 static_cast<uint16_t>(this->current_config.still_thresh[gate]));
912 }
913 if (this->gate_move_threshold_numbers_[gate] != nullptr) {
914 this->gate_move_threshold_numbers_[gate]->publish_state(
915 static_cast<uint16_t>(this->current_config.move_thresh[gate]));
916 }
917 }
918}
919
921 for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
922 const bool move = this->gate_move_threshold_numbers_[gate] == n;
923 if (!move && this->gate_still_threshold_numbers_[gate] != n)
924 continue;
925 const uint8_t target = this->is_gate_select() ? this->get_gate_select_value() : gate;
926 (move ? this->new_config.move_thresh : this->new_config.still_thresh)[target] = value;
927 return;
928 }
929}
930
936
937#endif
938
939} // namespace esphome::ld2420
BedjetMode mode
BedJet operating mode.
uint8_t checksum
Definition bl0906.h:3
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
void readline_(int rx_data, uint8_t *buffer, int len)
Definition ld2420.cpp:426
void set_system_mode(uint16_t mode)
Definition ld2420.cpp:799
void handle_ack_data_(uint8_t *buffer, int len)
Definition ld2420.cpp:562
std::vector< number::Number * > gate_still_threshold_numbers_
Definition ld2420.h:183
number::Number * gate_select_number_
Definition ld2420.h:178
uint8_t set_config_mode(bool enable)
Definition ld2420.cpp:690
button::Button * factory_reset_button_
Definition ld2420.h:145
void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number)
Definition ld2420.cpp:341
void set_distance_(uint16_t distance)
Definition ld2420.h:167
void set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, uint32_t timeout)
Definition ld2420.cpp:829
void set_gate_threshold(uint8_t gate)
Definition ld2420.cpp:862
void handle_energy_mode_(uint8_t *buffer, int len)
Definition ld2420.cpp:456
button::Button * revert_config_button_
Definition ld2420.h:143
void set_calibration_(bool state)
Definition ld2420.h:173
number::Number * min_gate_distance_number_
Definition ld2420.h:179
button::Button * apply_config_button_
Definition ld2420.h:142
void set_gate_threshold_value(number::Number *n, float value)
Definition ld2420.cpp:920
uint16_t gate_peak[TOTAL_GATES]
Definition ld2420.h:131
std::vector< LD2420Listener * > listeners_
Definition ld2420.h:197
std::vector< number::Number * > gate_move_threshold_numbers_
Definition ld2420.h:184
void set_mode_(uint16_t mode)
Definition ld2420.h:163
uint16_t gate_avg[TOTAL_GATES]
Definition ld2420.h:130
void read_batch_(std::span< uint8_t, MAX_LINE_LENGTH > buffer)
Definition ld2420.cpp:545
uint8_t buffer_data_[MAX_LINE_LENGTH]
Definition ld2420.h:191
int get_gate_threshold_(uint8_t gate)
Definition ld2420.cpp:753
number::Number * gate_still_sensitivity_factor_number_
Definition ld2420.h:182
int send_cmd_from_array(CmdFrameT cmd_frame)
Definition ld2420.cpp:629
void get_reg_value_(uint16_t reg)
Definition ld2420.cpp:718
uint16_t gate_energy_[TOTAL_GATES]
Definition ld2420.h:189
void handle_simple_mode_(const uint8_t *inbuf, int len)
Definition ld2420.cpp:503
number::Number * max_gate_distance_number_
Definition ld2420.h:180
select::Select * operating_selector_
Definition ld2420.h:139
button::Button * restart_module_button_
Definition ld2420.h:144
number::Number * gate_timeout_number_
Definition ld2420.h:177
void set_operating_mode(const char *state)
Definition ld2420.cpp:389
uint16_t radar_data[TOTAL_GATES][CALIBRATE_SAMPLES]
Definition ld2420.h:129
void handle_cmd_error(uint16_t error)
Definition ld2420.cpp:744
void set_presence_(bool presence)
Definition ld2420.h:165
number::Number * gate_move_sensitivity_factor_number_
Definition ld2420.h:181
void set_reg_value(uint16_t reg, uint16_t value)
Definition ld2420.cpp:730
Base-class for all numbers.
Definition number.h:29
void publish_state(float state)
Definition number.cpp:20
void publish_state(const std::string &state)
Definition select.h:35
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:43
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
bool state
Definition fan.h:2
uint32_t buffer
Definition gif.cpp:50
Range range
Definition msa3xx.h:0
ESPHOME_LOG_TAG(TAG, "ld2420.binary_sensor")
@ OP_CALIBRATE_MODE
Definition ld2420.h:31
uint8_t find_uint8(const StringToUint8(&arr)[N], const std::string &str)
Definition ld2420.cpp:155
constexpr uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init=0)
XOR the bytes of data with size len, starting from init.
Definition helpers.h:904
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
size_t size_t pos
Definition helpers.h:1255
void delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:889
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t