ESPHome 2025.5.0
Loading...
Searching...
No Matches
ld2420.cpp
Go to the documentation of this file.
1#include "ld2420.h"
3
4/*
5Configure commands - little endian
6
7No command can exceed 64 bytes, otherwise they would need be to be split up into multiple sends.
8
9All send command frames will have:
10 Header = FD FC FB FA, Bytes 0 - 3, uint32_t 0xFAFBFCFD
11 Length, bytes 4 - 5, uint16_t 0x0002, must be at least 2 for the command byte if no addon data.
12 Command bytes 6 - 7, uint16_t
13 Footer = 04 03 02 01 - uint32_t 0x01020304, Always last 4 Bytes.
14Receive
15 Error bytes 8-9 uint16_t, 0 = success, all other positive values = error
16
17Enable config mode:
18Send:
19 UART Tx: FD FC FB FA 04 00 FF 00 02 00 04 03 02 01
20 Command = FF 00 - uint16_t 0x00FF
21 Protocol version = 02 00, can be 1 or 2 - uint16_t 0x0002
22Reply:
23 UART Rx: FD FC FB FA 06 00 FF 01 00 00 02 00 04 03 02 01
24
25Disable config mode:
26Send:
27 UART Tx: FD FC FB FA 02 00 FE 00 04 03 02 01
28 Command = FE 00 - uint16_t 0x00FE
29Receive:
30 UART Rx: FD FC FB FA 04 00 FE 01 00 00 04 03 02 01
31
32Configure system parameters:
33
34UART Tx: FD FC FB FA 08 00 12 00 00 00 64 00 00 00 04 03 02 01 Set system parms
35Command = 12 00 - uint16_t 0x0012, Param
36There are three documented parameters for modes:
37 00 64 = Basic status mode
38 This mode outputs text as presence "ON" or "OFF" and "Range XXXX"
39 where XXXX is a decimal value for distance in cm
40 00 04 = Energy output mode
41 This mode outputs detailed signal energy values for each gate and the target distance.
42 The data format consist of the following.
43 Header HH, Length LL, Persence PP, Distance DD, 16 Gate Energies EE, Footer FF
44 HH HH HH HH LL LL PP DD DD EE EE .. 16x .. FF FF FF FF
45 F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5
46 00 00 = debug output mode
47 This mode outputs detailed values consisting of 20 Dopplers, 16 Ranges for a total 20 * 16 * 4 bytes
48 The data format consist of the following.
49 Header HH, Doppler DD, Range RR, Footer FF
50 HH HH HH HH DD DD DD DD .. 20x .. RR RR RR RR .. 16x .. FF FF FF FF
51 AA BF 10 14 00 00 00 00 .. .. .. .. 00 00 00 00 .. .. .. .. FD FC FB FA
52
53Configure gate sensitivity parameters:
54UART 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
55Command = 12 00 - uint16_t 0x0007
56Gate 0 high thresh = 10 00 uint16_t 0x0010, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
57Gate 0 low thresh = 20 00 uint16_t 0x0020, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
58*/
59
60namespace esphome {
61namespace ld2420 {
62
63static const char *const TAG = "ld2420";
64
66
68 ESP_LOGCONFIG(TAG, "LD2420:");
69 ESP_LOGCONFIG(TAG, " Firmware Version : %7s", this->ld2420_firmware_ver_);
70 ESP_LOGCONFIG(TAG, "LD2420 Number:");
71#ifdef USE_NUMBER
72 LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_);
73 LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_);
74 LOG_NUMBER(TAG, " Gate Min Distance:", this->min_gate_distance_number_);
75 LOG_NUMBER(TAG, " Gate Select:", this->gate_select_number_);
76 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
77 LOG_NUMBER(TAG, " Gate Move Threshold:", this->gate_move_threshold_numbers_[gate]);
78 LOG_NUMBER(TAG, " Gate Still Threshold::", this->gate_still_threshold_numbers_[gate]);
79 }
80#endif
81#ifdef USE_BUTTON
82 LOG_BUTTON(TAG, " Apply Config:", this->apply_config_button_);
83 LOG_BUTTON(TAG, " Revert Edits:", this->revert_config_button_);
84 LOG_BUTTON(TAG, " Factory Reset:", this->factory_reset_button_);
85 LOG_BUTTON(TAG, " Restart Module:", this->restart_module_button_);
86#endif
87 ESP_LOGCONFIG(TAG, "LD2420 Select:");
88 LOG_SELECT(TAG, " Operating Mode", this->operating_selector_);
89 if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
90 ESP_LOGW(TAG, "LD2420 Firmware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_);
91 }
92}
93
94uint8_t LD2420Component::calc_checksum(void *data, size_t size) {
95 uint8_t checksum = 0;
96 uint8_t *data_bytes = (uint8_t *) data;
97 for (size_t i = 0; i < size; i++) {
98 checksum ^= data_bytes[i]; // XOR operation
99 }
100 return checksum;
101}
102
103int LD2420Component::get_firmware_int_(const char *version_string) {
104 std::string version_str = version_string;
105 if (version_str[0] == 'v') {
106 version_str = version_str.substr(1);
107 }
108 version_str.erase(remove(version_str.begin(), version_str.end(), '.'), version_str.end());
109 int version_integer = stoi(version_str);
110 return version_integer;
111}
112
114 ESP_LOGCONFIG(TAG, "Setting up LD2420...");
115 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
116 ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
117 this->mark_failed();
118 return;
119 }
121#ifdef USE_NUMBER
123#endif
124 this->get_firmware_version_();
125 const char *pfw = this->ld2420_firmware_ver_;
126 std::string fw_str(pfw);
127
128 for (auto &listener : listeners_) {
129 listener->on_fw_version(fw_str);
130 }
131
132 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
134 this->get_gate_threshold_(gate);
135 }
136
137 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
138 if (get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
139 this->set_operating_mode(OP_SIMPLE_MODE_STRING);
140 this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
141 this->set_mode_(CMD_SYSTEM_MODE_SIMPLE);
142 ESP_LOGW(TAG, "LD2420 Frimware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_);
143 } else {
144 this->set_mode_(CMD_SYSTEM_MODE_ENERGY);
145 this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING);
146 }
147#ifdef USE_NUMBER
149#endif
150 this->set_system_mode(this->system_mode_);
151 this->set_config_mode(false);
152 ESP_LOGCONFIG(TAG, "LD2420 setup complete.");
153}
154
156 const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config));
157 if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) {
158 ESP_LOGCONFIG(TAG, "No configuration change detected");
159 return;
160 }
161 ESP_LOGCONFIG(TAG, "Reconfiguring LD2420...");
162 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
163 ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
164 this->mark_failed();
165 return;
166 }
167 this->set_min_max_distances_timeout(this->new_config.max_gate, this->new_config.min_gate, this->new_config.timeout);
168 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
170 this->set_gate_threshold(gate);
171 }
172 memcpy(&current_config, &new_config, sizeof(new_config));
173#ifdef USE_NUMBER
175#endif
176 this->set_system_mode(this->system_mode_);
177 this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm
178 this->set_operating_mode(OP_NORMAL_MODE_STRING);
179 ESP_LOGCONFIG(TAG, "LD2420 reconfig complete.");
180}
181
183 ESP_LOGCONFIG(TAG, "Setting factory defaults...");
184 if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
185 ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
186 this->mark_failed();
187 return;
188 }
189 this->set_min_max_distances_timeout(FACTORY_MAX_GATE, FACTORY_MIN_GATE, FACTORY_TIMEOUT);
190#ifdef USE_NUMBER
191 this->gate_timeout_number_->state = FACTORY_TIMEOUT;
192 this->min_gate_distance_number_->state = FACTORY_MIN_GATE;
193 this->max_gate_distance_number_->state = FACTORY_MAX_GATE;
194#endif
195 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
196 this->new_config.move_thresh[gate] = FACTORY_MOVE_THRESH[gate];
197 this->new_config.still_thresh[gate] = FACTORY_STILL_THRESH[gate];
199 this->set_gate_threshold(gate);
200 }
201 memcpy(&this->current_config, &this->new_config, sizeof(this->new_config));
202 this->set_system_mode(this->system_mode_);
203 this->set_config_mode(false);
204#ifdef USE_NUMBER
207#endif
208 ESP_LOGCONFIG(TAG, "LD2420 factory reset complete.");
209}
210
212 ESP_LOGCONFIG(TAG, "Restarting LD2420 module...");
213 this->send_module_restart();
214 this->set_timeout(250, [this]() {
215 this->set_config_mode(true);
217 this->set_config_mode(false);
218 });
219 ESP_LOGCONFIG(TAG, "LD2420 Restarted.");
220}
221
223 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
224#ifdef USE_NUMBER
226#endif
227 ESP_LOGCONFIG(TAG, "Reverted config number edits.");
228}
229
231 // If there is a active send command do not process it here, the send command call will handle it.
232 if (!get_cmd_active_()) {
233 if (!available())
234 return;
235 static uint8_t buffer[2048];
236 static uint8_t rx_data;
237 while (available()) {
238 rx_data = read();
239 this->readline_(rx_data, buffer, sizeof(buffer));
240 }
241 }
242}
243
244void LD2420Component::update_radar_data(uint16_t const *gate_energy, uint8_t sample_number) {
245 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
246 this->radar_data[gate][sample_number] = gate_energy[gate];
247 }
249}
250
252 // Calculate average and peak values for each gate
253 const float move_factor = gate_move_sensitivity_factor + 1;
254 const float still_factor = (gate_still_sensitivity_factor / 2) + 1;
255 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
256 uint32_t sum = 0;
257 uint16_t peak = 0;
258
259 for (uint8_t sample_number = 0; sample_number < CALIBRATE_SAMPLES; ++sample_number) {
260 // Calculate average
261 sum += this->radar_data[gate][sample_number];
262
263 // Calculate max value
264 if (this->radar_data[gate][sample_number] > peak) {
265 peak = this->radar_data[gate][sample_number];
266 }
267 }
268
269 // Store average and peak values
270 this->gate_avg[gate] = sum / CALIBRATE_SAMPLES;
271 if (this->gate_peak[gate] < peak)
272 this->gate_peak[gate] = peak;
273
274 uint32_t calculated_value =
275 (static_cast<uint32_t>(this->gate_peak[gate]) + (move_factor * static_cast<uint32_t>(this->gate_peak[gate])));
276 this->new_config.move_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
277 calculated_value =
278 (static_cast<uint32_t>(this->gate_peak[gate]) + (still_factor * static_cast<uint32_t>(this->gate_peak[gate])));
279 this->new_config.still_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
280 }
281}
282
284 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
285 // Output results
286 ESP_LOGI(TAG, "Gate: %2d Avg: %5d Peak: %5d", gate, this->gate_avg[gate], this->gate_peak[gate]);
287 }
288 ESP_LOGI(TAG, "Total samples: %d", this->total_sample_number_counter);
289}
290
292 // If unsupported firmware ignore mode select
293 if (get_firmware_int_(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) {
294 this->current_operating_mode = OP_MODE_TO_UINT.at(state);
295 // Entering Auto Calibrate we need to clear the privoiuos data collection
298 this->set_calibration_(true);
299 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
300 this->gate_avg[gate] = 0;
301 this->gate_peak[gate] = 0;
302 for (uint8_t i = 0; i < CALIBRATE_SAMPLES; i++) {
303 this->radar_data[gate][i] = 0;
304 }
306 }
307 } else {
308 // Set the current data back so we don't have new data that can be applied in error.
309 if (this->get_calibration_())
310 memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
311 this->set_calibration_(false);
312 }
313 } else {
315 this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
316 }
317}
318
319void LD2420Component::readline_(int rx_data, uint8_t *buffer, int len) {
320 static int pos = 0;
321
322 if (rx_data >= 0) {
323 if (pos < len - 1) {
324 buffer[pos++] = rx_data;
325 buffer[pos] = 0;
326 } else {
327 pos = 0;
328 }
329 if (pos >= 4) {
330 if (memcmp(&buffer[pos - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) {
331 this->set_cmd_active_(false); // Set command state to inactive after responce.
332 this->handle_ack_data_(buffer, pos);
333 pos = 0;
334 } else if ((buffer[pos - 2] == 0x0D && buffer[pos - 1] == 0x0A) && (get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) {
335 this->handle_simple_mode_(buffer, pos);
336 pos = 0;
337 } else if ((memcmp(&buffer[pos - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) &&
338 (get_mode_() == CMD_SYSTEM_MODE_ENERGY)) {
339 this->handle_energy_mode_(buffer, pos);
340 pos = 0;
341 }
342 }
343 }
344}
345
346void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) {
347 uint8_t index = 6; // Start at presence byte position
348 uint16_t range;
349 const uint8_t elements = sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]);
350 this->set_presence_(buffer[index]);
351 index++;
352 memcpy(&range, &buffer[index], sizeof(range));
353 index += sizeof(range);
354 this->set_distance_(range);
355 for (uint8_t i = 0; i < elements; i++) { // NOLINT
356 memcpy(&this->gate_energy_[i], &buffer[index], sizeof(this->gate_energy_[0]));
357 index += sizeof(this->gate_energy_[0]);
358 }
359
362 this->sample_number_counter > CALIBRATE_SAMPLES ? this->sample_number_counter = 0 : this->sample_number_counter++;
363 }
364
365 // Resonable refresh rate for home assistant database size health
366 const int32_t current_millis = millis();
367 if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS)
368 return;
369 this->last_periodic_millis = current_millis;
370 for (auto &listener : this->listeners_) {
371 listener->on_distance(get_distance_());
372 listener->on_presence(get_presence_());
373 listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]));
374 }
375
378 if (current_millis - this->report_periodic_millis > REFRESH_RATE_MS * CALIBRATE_REPORT_INTERVAL) {
379 this->report_periodic_millis = current_millis;
380 this->report_gate_data();
381 }
382 }
383}
384
385void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
386 const uint8_t bufsize = 16;
387 uint8_t index{0};
388 uint8_t pos{0};
389 char *endptr{nullptr};
390 char outbuf[bufsize]{0};
391 while (true) {
392 if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') {
393 set_presence_(false);
394 } else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') {
395 set_presence_(true);
396 }
397 if (inbuf[pos] >= '0' && inbuf[pos] <= '9') {
398 if (index < bufsize - 1) {
399 outbuf[index++] = inbuf[pos];
400 pos++;
401 }
402 } else {
403 if (pos < len - 1) {
404 pos++;
405 } else {
406 break;
407 }
408 }
409 }
410 outbuf[index] = '\0';
411 if (index > 1)
412 set_distance_(strtol(outbuf, &endptr, 10));
413
414 if (get_mode_() == CMD_SYSTEM_MODE_SIMPLE) {
415 // Resonable refresh rate for home assistant database size health
416 const int32_t current_millis = millis();
417 if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS)
418 return;
419 this->last_normal_periodic_millis = current_millis;
420 for (auto &listener : this->listeners_)
421 listener->on_distance(get_distance_());
422 for (auto &listener : this->listeners_)
423 listener->on_presence(get_presence_());
424 }
425}
426
427void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
428 this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
429 this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];
430 uint8_t reg_element = 0;
431 uint8_t data_element = 0;
432 uint16_t data_pos = 0;
433 if (this->cmd_reply_.length > CMD_MAX_BYTES) {
434 ESP_LOGW(TAG, "LD2420 reply - received command reply frame is corrupt, length exceeds %d bytes.", CMD_MAX_BYTES);
435 return;
436 } else if (this->cmd_reply_.length < 2) {
437 ESP_LOGW(TAG, "LD2420 reply - received command frame is corrupt, length is less than 2 bytes.");
438 return;
439 }
440 memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error));
441 const char *result = this->cmd_reply_.error ? "failure" : "success";
442 if (this->cmd_reply_.error > 0) {
443 return;
444 };
445 this->cmd_reply_.ack = true;
446 switch ((uint16_t) this->cmd_reply_.command) {
447 case (CMD_ENABLE_CONF):
448 ESP_LOGD(TAG, "LD2420 reply - set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result);
449 break;
450 case (CMD_DISABLE_CONF):
451 ESP_LOGD(TAG, "LD2420 reply - set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result);
452 break;
453 case (CMD_READ_REGISTER):
454 ESP_LOGD(TAG, "LD2420 reply - read register: CMD = %2X %s", CMD_READ_REGISTER, result);
455 // TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file
456 data_pos = 0x0A;
457 for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE * // NOLINT
458 ((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_REG_DATA_REPLY_SIZE));
459 index += CMD_REG_DATA_REPLY_SIZE) {
460 memcpy(&this->cmd_reply_.data[reg_element], &buffer[data_pos + index], sizeof(CMD_REG_DATA_REPLY_SIZE));
461 byteswap(this->cmd_reply_.data[reg_element]);
462 reg_element++;
463 }
464 break;
465 case (CMD_WRITE_REGISTER):
466 ESP_LOGD(TAG, "LD2420 reply - write register: CMD = %2X %s", CMD_WRITE_REGISTER, result);
467 break;
468 case (CMD_WRITE_ABD_PARAM):
469 ESP_LOGD(TAG, "LD2420 reply - write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result);
470 break;
471 case (CMD_READ_ABD_PARAM):
472 ESP_LOGD(TAG, "LD2420 reply - read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result);
473 data_pos = CMD_ABD_DATA_REPLY_START;
474 for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE * // NOLINT
475 ((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE));
476 index += CMD_ABD_DATA_REPLY_SIZE) {
477 memcpy(&this->cmd_reply_.data[data_element], &buffer[data_pos + index],
478 sizeof(this->cmd_reply_.data[data_element]));
479 byteswap(this->cmd_reply_.data[data_element]);
480 data_element++;
481 }
482 break;
483 case (CMD_WRITE_SYS_PARAM):
484 ESP_LOGD(TAG, "LD2420 reply - set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result);
485 break;
486 case (CMD_READ_VERSION):
487 memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]);
488 ESP_LOGD(TAG, "LD2420 reply - module firmware version: %7s %s", this->ld2420_firmware_ver_, result);
489 break;
490 default:
491 break;
492 }
493}
494
496 uint32_t start_millis = millis();
497 uint8_t error = 0;
498 uint8_t ack_buffer[64];
499 uint8_t cmd_buffer[64];
500 this->cmd_reply_.ack = false;
501 if (frame.command != CMD_RESTART)
502 this->set_cmd_active_(true); // Restart does not reply, thus no ack state required.
503 uint8_t retry = 3;
504 while (retry) {
505 frame.length = 0;
506 uint16_t frame_data_bytes = frame.data_length + 2; // Always add two bytes for the cmd size
507
508 memcpy(&cmd_buffer[frame.length], &frame.header, sizeof(frame.header));
509 frame.length += sizeof(frame.header);
510
511 memcpy(&cmd_buffer[frame.length], &frame_data_bytes, sizeof(frame.data_length));
512 frame.length += sizeof(frame.data_length);
513
514 memcpy(&cmd_buffer[frame.length], &frame.command, sizeof(frame.command));
515 frame.length += sizeof(frame.command);
516
517 for (uint16_t index = 0; index < frame.data_length; index++) {
518 memcpy(&cmd_buffer[frame.length], &frame.data[index], sizeof(frame.data[index]));
519 frame.length += sizeof(frame.data[index]);
520 }
521
522 memcpy(cmd_buffer + frame.length, &frame.footer, sizeof(frame.footer));
523 frame.length += sizeof(frame.footer);
524 for (uint16_t index = 0; index < frame.length; index++) {
525 this->write_byte(cmd_buffer[index]);
526 }
527
528 error = 0;
529 if (frame.command == CMD_RESTART) {
530 return 0; // restart does not reply exit now
531 }
532
533 while (!this->cmd_reply_.ack) {
534 while (available()) {
535 this->readline_(read(), ack_buffer, sizeof(ack_buffer));
536 }
538 // Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT.
539 if ((millis() - start_millis) > 1000) {
540 start_millis = millis();
541 error = LD2420_ERROR_TIMEOUT;
542 retry--;
543 break;
544 }
545 }
546 if (this->cmd_reply_.ack)
547 retry = 0;
548 if (this->cmd_reply_.error > 0)
549 handle_cmd_error(error);
550 }
551 return error;
552}
553
555 CmdFrameT cmd_frame;
556 cmd_frame.data_length = 0;
557 cmd_frame.header = CMD_FRAME_HEADER;
558 cmd_frame.command = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
559 if (enable) {
560 memcpy(&cmd_frame.data[0], &CMD_PROTOCOL_VER, sizeof(CMD_PROTOCOL_VER));
561 cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER);
562 }
563 cmd_frame.footer = CMD_FRAME_FOOTER;
564 ESP_LOGD(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command);
565 return this->send_cmd_from_array(cmd_frame);
566}
567
568// Sends a restart and set system running mode to normal
570
572 CmdFrameT cmd_frame;
573 cmd_frame.data_length = 0;
574 cmd_frame.header = CMD_FRAME_HEADER;
575 cmd_frame.command = CMD_RESTART;
576 cmd_frame.footer = CMD_FRAME_FOOTER;
577 ESP_LOGD(TAG, "Sending restart command: %2X", cmd_frame.command);
578 this->send_cmd_from_array(cmd_frame);
579}
580
582 CmdFrameT cmd_frame;
583 cmd_frame.data_length = 0;
584 cmd_frame.header = CMD_FRAME_HEADER;
585 cmd_frame.command = CMD_READ_REGISTER;
586 cmd_frame.data[1] = reg;
587 cmd_frame.data_length += 2;
588 cmd_frame.footer = CMD_FRAME_FOOTER;
589 ESP_LOGD(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command);
590 this->send_cmd_from_array(cmd_frame);
591}
592
593void LD2420Component::set_reg_value(uint16_t reg, uint16_t value) {
594 CmdFrameT cmd_frame;
595 cmd_frame.data_length = 0;
596 cmd_frame.header = CMD_FRAME_HEADER;
597 cmd_frame.command = CMD_WRITE_REGISTER;
598 memcpy(&cmd_frame.data[cmd_frame.data_length], &reg, sizeof(CMD_REG_DATA_REPLY_SIZE));
599 cmd_frame.data_length += 2;
600 memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE));
601 cmd_frame.data_length += 2;
602 cmd_frame.footer = CMD_FRAME_FOOTER;
603 ESP_LOGD(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value);
604 this->send_cmd_from_array(cmd_frame);
605}
606
607void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGI(TAG, "Command failed: %s", ERR_MESSAGE[error]); }
608
610 uint8_t error;
611 CmdFrameT cmd_frame;
612 cmd_frame.data_length = 0;
613 cmd_frame.header = CMD_FRAME_HEADER;
614 cmd_frame.command = CMD_READ_ABD_PARAM;
615 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_MOVE_THRESH[gate], sizeof(CMD_GATE_MOVE_THRESH[gate]));
616 cmd_frame.data_length += 2;
617 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate]));
618 cmd_frame.data_length += 2;
619 cmd_frame.footer = CMD_FRAME_FOOTER;
620 ESP_LOGD(TAG, "Sending read gate %d high/low theshold command: %2X", gate, cmd_frame.command);
621 error = this->send_cmd_from_array(cmd_frame);
622 if (error == 0) {
623 this->current_config.move_thresh[gate] = cmd_reply_.data[0];
625 }
626 return error;
627}
628
630 uint8_t error;
631 CmdFrameT cmd_frame;
632 cmd_frame.data_length = 0;
633 cmd_frame.header = CMD_FRAME_HEADER;
634 cmd_frame.command = CMD_READ_ABD_PARAM;
635 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
636 sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
637 cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
638 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
639 sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
640 cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
641 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
642 sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
643 cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
644 cmd_frame.footer = CMD_FRAME_FOOTER;
645 ESP_LOGD(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command);
646 error = this->send_cmd_from_array(cmd_frame);
647 if (error == 0) {
648 this->current_config.min_gate = (uint16_t) cmd_reply_.data[0];
649 this->current_config.max_gate = (uint16_t) cmd_reply_.data[1];
650 this->current_config.timeout = (uint16_t) cmd_reply_.data[2];
651 }
652 return error;
653}
654
656 CmdFrameT cmd_frame;
657 uint16_t unknown_parm = 0x0000;
658 cmd_frame.data_length = 0;
659 cmd_frame.header = CMD_FRAME_HEADER;
660 cmd_frame.command = CMD_WRITE_SYS_PARAM;
661 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_SYSTEM_MODE, sizeof(CMD_SYSTEM_MODE));
662 cmd_frame.data_length += sizeof(CMD_SYSTEM_MODE);
663 memcpy(&cmd_frame.data[cmd_frame.data_length], &mode, sizeof(mode));
664 cmd_frame.data_length += sizeof(mode);
665 memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm));
666 cmd_frame.data_length += sizeof(unknown_parm);
667 cmd_frame.footer = CMD_FRAME_FOOTER;
668 ESP_LOGD(TAG, "Sending write system mode command: %2X", cmd_frame.command);
669 if (this->send_cmd_from_array(cmd_frame) == 0)
671}
672
674 CmdFrameT cmd_frame;
675 cmd_frame.data_length = 0;
676 cmd_frame.header = CMD_FRAME_HEADER;
677 cmd_frame.command = CMD_READ_VERSION;
678 cmd_frame.footer = CMD_FRAME_FOOTER;
679
680 ESP_LOGD(TAG, "Sending read firmware version command: %2X", cmd_frame.command);
681 this->send_cmd_from_array(cmd_frame);
682}
683
684void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, // NOLINT
685 uint32_t timeout) {
686 // Header H, Length L, Register R, Value V, Footer F
687 // |Min Gate |Max Gate |Timeout |
688 // 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
689 // 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.
690
691 CmdFrameT cmd_frame;
692 cmd_frame.data_length = 0;
693 cmd_frame.header = CMD_FRAME_HEADER;
694 cmd_frame.command = CMD_WRITE_ABD_PARAM;
695 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
696 sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
697 cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
698 memcpy(&cmd_frame.data[cmd_frame.data_length], &min_gate_distance, sizeof(min_gate_distance));
699 cmd_frame.data_length += sizeof(min_gate_distance);
700 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
701 sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
702 cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
703 memcpy(&cmd_frame.data[cmd_frame.data_length], &max_gate_distance, sizeof(max_gate_distance));
704 cmd_frame.data_length += sizeof(max_gate_distance);
705 memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
706 sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
707 cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
708 memcpy(&cmd_frame.data[cmd_frame.data_length], &timeout, sizeof(timeout));
709 ;
710 cmd_frame.data_length += sizeof(timeout);
711 cmd_frame.footer = CMD_FRAME_FOOTER;
712
713 ESP_LOGD(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command);
714 this->send_cmd_from_array(cmd_frame);
715}
716
718 // Header H, Length L, Command C, Register R, Value V, Footer F
719 // HH HH HH HH LL LL CC CC RR RR VV VV VV VV RR RR VV VV VV VV FF FF FF FF
720 // FD FC FB FA 14 00 07 00 10 00 00 FF 00 00 00 01 00 0F 00 00 04 03 02 01
721
722 uint16_t move_threshold_gate = CMD_GATE_MOVE_THRESH[gate];
723 uint16_t still_threshold_gate = CMD_GATE_STILL_THRESH[gate];
724 CmdFrameT cmd_frame;
725 cmd_frame.data_length = 0;
726 cmd_frame.header = CMD_FRAME_HEADER;
727 cmd_frame.command = CMD_WRITE_ABD_PARAM;
728 memcpy(&cmd_frame.data[cmd_frame.data_length], &move_threshold_gate, sizeof(move_threshold_gate));
729 cmd_frame.data_length += sizeof(move_threshold_gate);
730 memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.move_thresh[gate],
731 sizeof(this->new_config.move_thresh[gate]));
732 cmd_frame.data_length += sizeof(this->new_config.move_thresh[gate]);
733 memcpy(&cmd_frame.data[cmd_frame.data_length], &still_threshold_gate, sizeof(still_threshold_gate));
734 cmd_frame.data_length += sizeof(still_threshold_gate);
735 memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.still_thresh[gate],
736 sizeof(this->new_config.still_thresh[gate]));
737 cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]);
738 cmd_frame.footer = CMD_FRAME_FOOTER;
739 ESP_LOGD(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command);
740 this->send_cmd_from_array(cmd_frame);
741}
742
743#ifdef USE_NUMBER
745 if (this->gate_timeout_number_ != nullptr)
746 this->gate_timeout_number_->publish_state(static_cast<uint16_t>(this->current_config.timeout));
747 if (this->gate_select_number_ != nullptr)
749 if (this->min_gate_distance_number_ != nullptr)
750 this->min_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.min_gate));
751 if (this->max_gate_distance_number_ != nullptr)
752 this->max_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.max_gate));
753 if (this->gate_move_sensitivity_factor_number_ != nullptr)
755 if (this->gate_still_sensitivity_factor_number_ != nullptr)
757 for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
758 if (this->gate_still_threshold_numbers_[gate] != nullptr) {
759 this->gate_still_threshold_numbers_[gate]->publish_state(
760 static_cast<uint16_t>(this->current_config.still_thresh[gate]));
761 }
762 if (this->gate_move_threshold_numbers_[gate] != nullptr) {
763 this->gate_move_threshold_numbers_[gate]->publish_state(
764 static_cast<uint16_t>(this->current_config.move_thresh[gate]));
765 }
766 }
767}
768
774
775#endif
776
777} // namespace ld2420
778} // namespace esphome
BedjetMode mode
BedJet operating mode.
uint8_t checksum
Definition bl0906.h:3
virtual void mark_failed()
Mark this component as failed.
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.cpp:72
int get_firmware_int_(const char *version_string)
Definition ld2420.cpp:103
void readline_(int rx_data, uint8_t *buffer, int len)
Definition ld2420.cpp:319
void set_system_mode(uint16_t mode)
Definition ld2420.cpp:655
void handle_ack_data_(uint8_t *buffer, int len)
Definition ld2420.cpp:427
std::vector< number::Number * > gate_still_threshold_numbers_
Definition ld2420.h:252
number::Number * gate_select_number_
Definition ld2420.h:247
uint8_t set_config_mode(bool enable)
Definition ld2420.cpp:554
button::Button * factory_reset_button_
Definition ld2420.h:206
void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number)
Definition ld2420.cpp:244
float get_setup_priority() const override
Definition ld2420.cpp:65
void set_distance_(uint16_t distance)
Definition ld2420.h:235
void set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, uint32_t timeout)
Definition ld2420.cpp:684
uint8_t calc_checksum(void *data, size_t size)
Definition ld2420.cpp:94
void set_gate_threshold(uint8_t gate)
Definition ld2420.cpp:717
void handle_energy_mode_(uint8_t *buffer, int len)
Definition ld2420.cpp:346
uint16_t gate_avg[LD2420_TOTAL_GATES]
Definition ld2420.h:193
button::Button * revert_config_button_
Definition ld2420.h:204
void set_calibration_(bool state)
Definition ld2420.h:242
number::Number * min_gate_distance_number_
Definition ld2420.h:248
button::Button * apply_config_button_
Definition ld2420.h:203
uint16_t radar_data[LD2420_TOTAL_GATES][CALIBRATE_SAMPLES]
Definition ld2420.h:192
std::vector< LD2420Listener * > listeners_
Definition ld2420.h:267
std::vector< number::Number * > gate_move_threshold_numbers_
Definition ld2420.h:253
void set_mode_(uint16_t mode)
Definition ld2420.h:231
uint16_t gate_energy_[LD2420_TOTAL_GATES]
Definition ld2420.h:256
int get_gate_threshold_(uint8_t gate)
Definition ld2420.cpp:609
number::Number * gate_still_sensitivity_factor_number_
Definition ld2420.h:251
void handle_cmd_error(uint8_t error)
Definition ld2420.cpp:607
int send_cmd_from_array(CmdFrameT cmd_frame)
Definition ld2420.cpp:495
void get_reg_value_(uint16_t reg)
Definition ld2420.cpp:581
uint16_t gate_peak[LD2420_TOTAL_GATES]
Definition ld2420.h:194
void handle_simple_mode_(const uint8_t *inbuf, int len)
Definition ld2420.cpp:385
number::Number * max_gate_distance_number_
Definition ld2420.h:249
select::Select * operating_selector_
Definition ld2420.h:200
button::Button * restart_module_button_
Definition ld2420.h:205
void set_operating_mode(const std::string &state)
Definition ld2420.cpp:291
number::Number * gate_timeout_number_
Definition ld2420.h:246
void set_presence_(bool presence)
Definition ld2420.h:233
number::Number * gate_move_sensitivity_factor_number_
Definition ld2420.h:250
void set_reg_value(uint16_t reg, uint16_t value)
Definition ld2420.cpp:593
void set_cmd_active_(bool active)
Definition ld2420.h:237
void publish_state(float state)
Definition number.cpp:9
void publish_state(const std::string &state)
Definition select.cpp:9
void write_byte(uint8_t data)
Definition uart.h:19
bool state
Definition fan.h:0
Range range
Definition msa3xx.h:0
@ OP_CALIBRATE_MODE
Definition ld2420.h:97
const float BUS
For communication buses like i2c/spi.
Definition component.cpp:16
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:773
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
uint32_t still_thresh[LD2420_TOTAL_GATES]
Definition ld2420.h:167
uint32_t move_thresh[LD2420_TOTAL_GATES]
Definition ld2420.h:166
void byteswap()