13static constexpr uint8_t HEADER_FOOTER_SIZE = 4;
15static constexpr uint8_t CMD_FRAME_HEADER[HEADER_FOOTER_SIZE] PROGMEM = {0xFD, 0xFC, 0xFB, 0xFA};
16static constexpr uint8_t CMD_FRAME_FOOTER[HEADER_FOOTER_SIZE] PROGMEM = {0x04, 0x03, 0x02, 0x01};
17static constexpr uint8_t DATA_FRAME_HEADER[HEADER_FOOTER_SIZE] PROGMEM = {0xF4, 0xF3, 0xF2, 0xF1};
18static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE]
PROGMEM = {0xF8, 0xF7, 0xF6, 0xF5};
20static constexpr uint8_t CMD_REPORT_DATA = 0x04;
21static constexpr uint8_t CMD_QUERY_INSTALLATION_MODE = 0x0A;
22static constexpr uint8_t ACK_FRAME_LENGTH = 12;
23static constexpr uint8_t MAX_QUERY_ATTEMPTS = 3;
25static constexpr uint8_t QUERY_INSTALLATION_MODE_FRAME[]
PROGMEM = {
26 0xFD, 0xFC, 0xFB, 0xFA, CMD_QUERY_INSTALLATION_MODE, 0x0C, 0x00, 0x01, 0x04, 0x03, 0x02, 0x01,
29static bool matches(
const uint8_t *bytes,
const uint8_t *pattern) {
30 uint8_t copy[HEADER_FOOTER_SIZE];
32 return std::memcmp(bytes, copy,
sizeof(copy)) == 0;
36static constexpr uint8_t FRAME_PREFIX_SIZE = 7;
37static uint16_t frame_length(
const uint8_t *frame) {
return encode_uint16(frame[6], frame[5]); }
39static constexpr float RADIANS_TO_DEGREES = 180.0f / std::numbers::pi_v<float>;
44 uint8_t frame[
sizeof(QUERY_INSTALLATION_MODE_FRAME)];
45 progmem_memcpy(frame, QUERY_INSTALLATION_MODE_FRAME,
sizeof(frame));
49void LD2460Component::dump_config() {
52 " Installation mode: %s",
55 : LOG_STR_LITERAL(
"side"));
57 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"TargetCount", this->target_count_sensor_);
59 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"TargetX", t.x);
60 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"TargetY", t.y);
61 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"TargetDistance", t.distance);
62 LOG_SENSOR_WITH_DEDUP_SAFE(
" ",
"TargetAngle", t.angle);
67void LD2460Component::loop() {
69 uint8_t buf[MAX_LINE_LENGTH];
71 size_t to_read = std::min(avail,
sizeof(buf));
77 for (
size_t i = 0; i < to_read; i++) {
85 if (!this->
align_to_header_(DATA_FRAME_HEADER) || data[4] != CMD_REPORT_DATA) {
86 ESP_LOGW(TAG,
"Invalid periodic data header");
90 if (packet_len < 11 || (packet_len - 11) % 4 != 0) {
91 ESP_LOGW(TAG,
"Periodic packet length %u is not a whole number of targets", packet_len);
102 ESP_LOGW(TAG,
"No installation mode reply after %u queries; assuming side mount", MAX_QUERY_ATTEMPTS);
107 uint8_t num_targets = std::min<uint8_t>((packet_len - 11) / 4, MAX_TARGETS);
108 for (uint8_t i = 0; i < MAX_TARGETS; i++) {
110 if (i >= num_targets) {
111 t.last_x = INT16_MIN;
112 t.last_y = INT16_MIN;
113 SAFE_PUBLISH_SENSOR_UNKNOWN(t.x);
114 SAFE_PUBLISH_SENSOR_UNKNOWN(t.y);
115 SAFE_PUBLISH_SENSOR_UNKNOWN(t.distance);
116 SAFE_PUBLISH_SENSOR_UNKNOWN(t.angle);
119 const uint8_t *p = &data[7 + i * 4];
120 auto raw_x =
static_cast<int16_t
>(
encode_uint16(p[1], p[0]));
121 auto raw_y =
static_cast<int16_t
>(
encode_uint16(p[3], p[2]));
122 if (raw_x == t.last_x && raw_y == t.last_y) {
129 float x = raw_x * 0.1f;
130 float y = raw_y * 0.1f;
131 SAFE_PUBLISH_SENSOR(t.x,
x);
132 SAFE_PUBLISH_SENSOR(t.y,
y);
133 if (t.distance.has_sensor()) {
134 SAFE_PUBLISH_SENSOR(t.distance, std::sqrt(
x *
x +
y *
y));
136 if (t.angle.has_sensor()) {
139 angle = std::atan2(
y,
x) * RADIANS_TO_DEGREES;
144 angle = std::atan2(
x,
y) * RADIANS_TO_DEGREES;
146 SAFE_PUBLISH_SENSOR(t.angle, angle);
149 SAFE_PUBLISH_SENSOR(this->target_count_sensor_, num_targets);
157 ESP_LOGW(TAG,
"Max frame length exceeded; ignoring");
166 bool is_data = readch ==
progmem_read_byte(&DATA_FRAME_FOOTER[3]) && matches(tail, DATA_FRAME_FOOTER);
167 if (!is_data && (readch !=
progmem_read_byte(&CMD_FRAME_FOOTER[3]) || !matches(tail, CMD_FRAME_FOOTER))) {
171#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
188 for (uint8_t i = 0; i + FRAME_PREFIX_SIZE <= this->
buffer_pos_; i++) {
190 if (matches(frame, header) && frame_length(frame) == this->
buffer_pos_ - i) {
204 ESP_LOGW(TAG,
"Dropping malformed command frame");
207 if (data[4] != CMD_QUERY_INSTALLATION_MODE) {
208 ESP_LOGD(TAG,
"Ignoring reply to command 0x%02X", data[4]);
211 const uint8_t
mode = data[7];
214 ESP_LOGW(TAG,
"Unexpected installation mode %u",
mode);
219 ESP_LOGD(TAG,
"Installation mode: %s",
221 : LOG_STR_LITERAL(
"side"));
225 t.last_x = INT16_MIN;
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).