14static constexpr uint8_t SHORT_DATA_FRAME_HEADER = 0x6E;
15static constexpr uint8_t SHORT_DATA_FRAME_FOOTER[] PROGMEM = {0x62};
16static constexpr uint8_t SHORT_DATA_FRAME_SIZE = 5;
17static constexpr uint8_t STD_DATA_FRAME_HEADER[] PROGMEM = {0xF4, 0xF3, 0xF2, 0xF1};
18static constexpr uint8_t STD_DATA_FRAME_FOOTER[] PROGMEM = {0xF8, 0xF7, 0xF6, 0xF5};
19static constexpr uint8_t CMD_FRAME_HEADER[]
PROGMEM = {0xFD, 0xFC, 0xFB, 0xFA};
20static constexpr uint8_t CMD_FRAME_FOOTER[]
PROGMEM = {0x04, 0x03, 0x02, 0x01};
21static constexpr uint8_t LONG_HEADER_SIZE = 4;
22static constexpr uint8_t LENGTH_FIELD_SIZE = 2;
23static constexpr uint8_t LONG_PAYLOAD_POS = LONG_HEADER_SIZE + LENGTH_FIELD_SIZE;
25static constexpr uint16_t CMD_CONFIRMATION = 0x0100;
26static constexpr uint16_t CONFIG_MODE_START_CMD = 0x00FF;
27static constexpr uint16_t CONFIG_MODE_START_VALUE = 0x0001;
28static constexpr uint16_t CONFIG_MODE_END_CMD = 0x00FE;
29static constexpr uint16_t VERSION_READ_CMD = 0x0000;
30static constexpr uint16_t PARAMETERS_WRITE_CMD = 0x0070;
31static constexpr uint16_t PARAMETERS_READ_CMD = 0x0071;
32static constexpr uint16_t OUTPUT_MODE_SWITCH_CMD = 0x007A;
34static constexpr uint8_t OUTPUT_MODE_MINIMAL_SIZE = 6;
36static constexpr uint8_t PARAMETER_KEY_SIZE = 2;
37static constexpr uint8_t PARAMETER_VALUE_SIZE = 4;
38static constexpr uint8_t PARAMETERS_WRITE_SIZE =
PARAMETER_COUNT * (PARAMETER_KEY_SIZE + PARAMETER_VALUE_SIZE);
39static constexpr uint8_t CMD_FRAME_MAX_SIZE = LONG_PAYLOAD_POS + 2 + PARAMETERS_WRITE_SIZE +
sizeof(CMD_FRAME_FOOTER);
41#ifdef USE_LD2410S_PARAMETERS
42static constexpr uint16_t PARAMETER_KEYS[]
PROGMEM = {0x0005, 0x000A, 0x0006, 0x0002, 0x000C, 0x000B};
43static_assert(
sizeof(PARAMETER_KEYS) /
sizeof(PARAMETER_KEYS[0]) ==
PARAMETER_COUNT,
"one key per parameter");
46static constexpr uint8_t VERSION_TYPE_SIZE = 4;
47static constexpr uint8_t VERSION_SIZE = VERSION_TYPE_SIZE + 3 * 2;
51static constexpr uint16_t INIT_SEQUENCE[]
PROGMEM = {
52 CONFIG_MODE_START_CMD,
53#ifdef USE_LD2410S_VERSION
56#ifdef USE_LD2410S_PARAMETERS
59 OUTPUT_MODE_SWITCH_CMD, CONFIG_MODE_END_CMD,
61#ifdef USE_LD2410S_PARAMETERS
62static constexpr uint16_t WRITE_SEQUENCE[]
PROGMEM = {CONFIG_MODE_START_CMD, PARAMETERS_WRITE_CMD, CONFIG_MODE_END_CMD};
64static constexpr uint8_t INIT_SEQUENCE_LENGTH =
sizeof(INIT_SEQUENCE) /
sizeof(INIT_SEQUENCE[0]);
65#ifdef USE_LD2410S_PARAMETERS
66static constexpr uint8_t WRITE_SEQUENCE_LENGTH =
sizeof(WRITE_SEQUENCE) /
sizeof(WRITE_SEQUENCE[0]);
68static constexpr uint8_t STEP_MAX_TIMEOUTS = 2;
69static constexpr uint32_t ACK_TIMEOUT_MS = 300;
70static constexpr uint32_t COMMAND_GAP_MS = 300;
71static constexpr uint32_t REINIT_PAUSE_MS = 15000;
72static constexpr uint32_t LINK_TIMEOUT_MS = 5000;
74static constexpr uint8_t DATA_TYPE_STANDARD = 0x01;
76void LD2410SComponent::dump_config() {
77 ESP_LOGCONFIG(TAG,
"LD2410S:");
78#ifdef USE_BINARY_SENSOR
79 LOG_BINARY_SENSOR(
" ",
"Has Target", this->target_binary_sensor_);
82 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"Distance", this->distance_sensor_);
84#ifdef USE_LD2410S_VERSION
85 LOG_TEXT_SENSOR(
" ",
"Version", this->version_text_sensor_);
87#if defined(USE_NUMBER) && defined(USE_LD2410S_PARAMETERS)
88 for (
auto *number : this->
numbers_) {
89 LOG_NUMBER(
" ",
"Setting", number);
92#if defined(USE_SELECT) && defined(USE_LD2410S_PARAMETERS)
93 LOG_SELECT(
" ",
"Response Speed", this->response_speed_select_);
102 const size_t len = std::min(avail,
sizeof(chunk));
104 ESP_LOGV(TAG,
"UART read of %u bytes failed",
static_cast<unsigned>(
len));
107 for (
size_t i = 0; i <
len; i++) {
116#ifdef USE_LD2410S_PARAMETERS
126 ESP_LOGW(TAG,
"No data for %" PRIu32
" s (%u frames dropped), starting over", LINK_TIMEOUT_MS / 1000,
133#ifdef USE_LD2410S_PARAMETERS
134bool LD2410SComponent::set_parameter(
Parameter parameter, uint8_t value) {
137 ESP_LOGW(TAG,
"Parameters not read yet, change ignored");
141 ESP_LOGW(TAG,
"The module reported a parameter out of range, changes are disabled");
144 this->
parameters_[
static_cast<uint8_t
>(parameter)] = value;
153#ifdef USE_LD2410S_PARAMETERS
156 ESP_LOGW(TAG,
"Dropping the pending parameter change, the module's values are read back");
182 ESP_LOGW(TAG,
"No acknowledgement for command %04X, starting over in %" PRIu32
" s", command,
183 REINIT_PAUSE_MS / 1000);
189 ESP_LOGD(TAG,
"No acknowledgement for command %04X, resending", command);
197 uint8_t frame[CMD_FRAME_MAX_SIZE];
199 uint8_t
pos = LONG_PAYLOAD_POS;
200 frame[
pos++] = command & 0xFF;
201 frame[
pos++] = command >> 8;
203 case OUTPUT_MODE_SWITCH_CMD:
204 memset(&frame[
pos], 0, OUTPUT_MODE_MINIMAL_SIZE);
205 pos += OUTPUT_MODE_MINIMAL_SIZE;
207 case CONFIG_MODE_START_CMD:
208 frame[
pos++] = CONFIG_MODE_START_VALUE & 0xFF;
209 frame[
pos++] = CONFIG_MODE_START_VALUE >> 8;
211#ifdef USE_LD2410S_PARAMETERS
212 case PARAMETERS_READ_CMD:
213 case PARAMETERS_WRITE_CMD:
216 frame[
pos++] = key & 0xFF;
217 frame[
pos++] = key >> 8;
218 if (command == PARAMETERS_WRITE_CMD) {
220 for (uint8_t shift = 0; shift < 32; shift += 8) {
221 frame[
pos++] = (value >> shift) & 0xFF;
230 frame[LONG_HEADER_SIZE] =
pos - LONG_PAYLOAD_POS;
231 frame[LONG_HEADER_SIZE + 1] = 0;
233 pos +=
sizeof(CMD_FRAME_FOOTER);
235#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
242 if (this->
rx_len_ >= RX_BUFFER_SIZE) {
248 if (this->
rx_len_ == 1 &&
byte == SHORT_DATA_FRAME_HEADER) {
254 uint8_t std_header[LONG_HEADER_SIZE];
255 uint8_t cmd_header[LONG_HEADER_SIZE];
256 progmem_memcpy(std_header, STD_DATA_FRAME_HEADER, LONG_HEADER_SIZE);
258 const bool std_prefix = memcmp(this->
rx_buffer_, std_header, this->
rx_len_) == 0;
259 const bool cmd_prefix = memcmp(this->
rx_buffer_, cmd_header, this->
rx_len_) == 0;
260 if (std_prefix || cmd_prefix) {
261 if (this->
rx_len_ == LONG_HEADER_SIZE) {
267 const bool retry = this->
rx_len_ > 1;
276 if (this->
rx_len_ < LONG_PAYLOAD_POS) {
280 const uint16_t payload_len =
282 if (payload_len > RX_BUFFER_SIZE - LONG_PAYLOAD_POS -
sizeof(CMD_FRAME_FOOTER)) {
283 ESP_LOGV(TAG,
"Dropping frame with a %u byte payload, larger than the receive buffer", payload_len);
288 this->
expected_len_ = LONG_PAYLOAD_POS + payload_len +
sizeof(CMD_FRAME_FOOTER);
300 uint8_t rest[RX_BUFFER_SIZE];
301 const uint8_t rest_len = this->
rx_len_ - 1;
305 for (uint8_t i = 0; i < rest_len; i++) {
312 const uint8_t *footer_src;
313 uint8_t footer_size =
sizeof(CMD_FRAME_FOOTER);
314 uint8_t payload_pos = LONG_PAYLOAD_POS;
317 footer_src = SHORT_DATA_FRAME_FOOTER;
318 footer_size =
sizeof(SHORT_DATA_FRAME_FOOTER);
322 footer_src = STD_DATA_FRAME_FOOTER;
325 footer_src = CMD_FRAME_FOOTER;
328 uint8_t footer[
sizeof(CMD_FRAME_FOOTER)];
330 if (memcmp(&this->
rx_buffer_[this->
rx_len_ - footer_size], footer, footer_size) != 0) {
331#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
333 ESP_LOGV(TAG,
"Footer does not match header: %s",
339 const uint8_t *payload = &this->
rx_buffer_[payload_pos];
340 const uint16_t payload_len = this->
rx_len_ - payload_pos - footer_size;
361 const uint8_t *data = payload;
363 if (len < 4 || payload[0] != DATA_TYPE_STANDARD) {
365 ESP_LOGV(TAG,
"Ignoring data frame type %02X", payload[0]);
371 const uint8_t
state = data[0];
376 ESP_LOGV(TAG,
"Dropping frame with impossible state %u",
state);
385 ESP_LOGV(TAG,
"Acknowledgement too short (%u bytes)",
len);
388 const uint16_t command_word =
encode_uint16(payload[1], payload[0]);
391 ESP_LOGV(TAG,
"Unexpected acknowledgement %04X", command_word);
395 if (command_word != (command | CMD_CONFIRMATION)) {
396 ESP_LOGV(TAG,
"Unexpected acknowledgement %04X", command_word);
399#ifdef USE_LD2410S_PARAMETERS
400 if (
ack != 0 && command == PARAMETERS_WRITE_CMD) {
403 ESP_LOGW(TAG,
"Module rejected the parameters, ack %04X",
ack);
411 ESP_LOGW(TAG,
"Module rejected command %04X, ack %04X", command,
ack);
415#ifdef USE_LD2410S_PARAMETERS
419#ifdef USE_LD2410S_VERSION
430 const bool was_init = this->
sequence_ == INIT_SEQUENCE;
433 ESP_LOGD(TAG,
"Setup done");
440#ifdef USE_LD2410S_PARAMETERS
444 ESP_LOGW(TAG,
"Parameter answer too short (%u bytes)",
len);
450 const uint8_t *v = &values[i * PARAMETER_VALUE_SIZE];
451 if ((v[1] | v[2] | v[3]) != 0) {
452 ESP_LOGW(TAG,
"Parameter %u out of range: %" PRIu32
", changes stay disabled", i,
457 for (
auto *number : this->
numbers_) {
458 if (number !=
nullptr) {
459 number->publish_state(NAN);
467 this->
parameters_[i] = values[i * PARAMETER_VALUE_SIZE];
471 ESP_LOGD(TAG,
"Parameters: gates %u to %u, no delay %u s, frequencies %u/%u dHz, response speed %u",
478#ifdef USE_LD2410S_VERSION
481 if (
len < VERSION_SIZE) {
482 ESP_LOGW(TAG,
"Version answer too short (%u bytes)",
len);
485 const uint8_t *v = &values[VERSION_TYPE_SIZE];
489 ESP_LOGD(TAG,
"Firmware %s", version);
490 if (this->version_text_sensor_ !=
nullptr) {
491 this->version_text_sensor_->publish_state(version);
498#ifdef USE_BINARY_SENSOR
499 if (this->target_binary_sensor_ !=
nullptr) {
500 this->target_binary_sensor_->publish_state(target);
505 SAFE_PUBLISH_SENSOR(this->distance_sensor_, target ? distance : 0);
509#ifdef USE_LD2410S_PARAMETERS
519 if (this->response_speed_select_ !=
nullptr) {
521 for (uint8_t i = 0; i < RESPONSE_SPEED_COUNT; i++) {
523 this->response_speed_select_->publish_state(i);
527 ESP_LOGV(TAG,
"Unknown response speed %u",
speed);