From cfb6c4e9d4e5f7bc56680731ac9b4646d8744a95 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Feb 2026 08:48:18 -1000 Subject: [PATCH] [ld2410/ld2412] Fix two_byte_to_int using signed char causing incorrect values The two_byte_to_int() function used `char` parameters instead of `uint8_t`, causing sign extension for byte values >= 0x80. This produced incorrect sensor values for distances where value % 256 >= 128 (e.g. 128cm reported as -128, 200cm reported as -56). Changed return type to uint16_t to match protocol spec (unsigned distances), renamed to two_byte_to_uint16, and moved to shared ld24xx header to eliminate duplication. --- esphome/components/ld2410/ld2410.cpp | 10 ++++------ esphome/components/ld2412/ld2412.cpp | 14 ++++++-------- esphome/components/ld24xx/ld24xx.h | 3 +++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index dd1d53857d..e3b948e4c3 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -173,8 +173,6 @@ static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0x // MAC address the module uses when Bluetooth is disabled static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01}; -static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } - static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) { return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0; } @@ -363,15 +361,15 @@ void LD2410Component::handle_periodic_data_() { #ifdef USE_SENSOR SAFE_PUBLISH_SENSOR( this->moving_target_distance_sensor_, - ld2410::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) + ld24xx::two_byte_to_uint16(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY]) SAFE_PUBLISH_SENSOR( this->still_target_distance_sensor_, - ld2410::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])); + ld24xx::two_byte_to_uint16(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])); SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY]); SAFE_PUBLISH_SENSOR( this->detection_distance_sensor_, - ld2410::two_byte_to_int(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH])); + ld24xx::two_byte_to_uint16(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH])); if (engineering_mode) { /* @@ -579,7 +577,7 @@ bool LD2410Component::handle_ack_data_() { None Duration: 33~34th bytes */ updates.push_back(set_number_value(this->timeout_number_, - ld2410::two_byte_to_int(this->buffer_data_[32], this->buffer_data_[33]))); + ld24xx::two_byte_to_uint16(this->buffer_data_[32], this->buffer_data_[33]))); for (auto &update : updates) { update(); } diff --git a/esphome/components/ld2412/ld2412.cpp b/esphome/components/ld2412/ld2412.cpp index 484d5bd281..f5bf42829e 100644 --- a/esphome/components/ld2412/ld2412.cpp +++ b/esphome/components/ld2412/ld2412.cpp @@ -192,8 +192,6 @@ static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0x // MAC address the module uses when Bluetooth is disabled static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01}; -static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } - static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) { return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0; } @@ -400,20 +398,20 @@ void LD2412Component::handle_periodic_data_() { #ifdef USE_SENSOR SAFE_PUBLISH_SENSOR( this->moving_target_distance_sensor_, - ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) + ld24xx::two_byte_to_uint16(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH])) SAFE_PUBLISH_SENSOR(this->moving_target_energy_sensor_, this->buffer_data_[MOVING_ENERGY]) SAFE_PUBLISH_SENSOR( this->still_target_distance_sensor_, - ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])) + ld24xx::two_byte_to_uint16(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH])) SAFE_PUBLISH_SENSOR(this->still_target_energy_sensor_, this->buffer_data_[STILL_ENERGY]) if (this->detection_distance_sensor_ != nullptr) { int new_detect_distance = 0; if (target_state != 0x00 && (target_state & MOVE_BITMASK)) { new_detect_distance = - ld2412::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]); + ld24xx::two_byte_to_uint16(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]); } else if (target_state != 0x00) { new_detect_distance = - ld2412::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]); + ld24xx::two_byte_to_uint16(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]); } this->detection_distance_sensor_->publish_state_if_not_dup(new_detect_distance); } @@ -638,8 +636,8 @@ bool LD2412Component::handle_ack_data_() { None Duration: 11~12th bytes */ updates.push_back(set_number_value(this->timeout_number_, - ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13]))); - ESP_LOGV(TAG, "timeout_number_: %u", ld2412::two_byte_to_int(this->buffer_data_[12], this->buffer_data_[13])); + ld24xx::two_byte_to_uint16(this->buffer_data_[12], this->buffer_data_[13]))); + ESP_LOGV(TAG, "timeout_number_: %u", ld24xx::two_byte_to_uint16(this->buffer_data_[12], this->buffer_data_[13])); /* Output pin configuration: 13th bytes */ diff --git a/esphome/components/ld24xx/ld24xx.h b/esphome/components/ld24xx/ld24xx.h index fd55167974..70862b6895 100644 --- a/esphome/components/ld24xx/ld24xx.h +++ b/esphome/components/ld24xx/ld24xx.h @@ -39,6 +39,9 @@ namespace esphome::ld24xx { +/// Parse a little-endian 16-bit unsigned value from two bytes. +static inline uint16_t two_byte_to_uint16(uint8_t low, uint8_t high) { return ((uint16_t) high << 8) | low; } + // Helper to find index of value in constexpr array template optional find_index(const uint32_t (&arr)[N], uint32_t value) { for (size_t i = 0; i < N; i++) {