[ld2410] Reduce RAM usage, general clean-up (#9346)

This commit is contained in:
Keith Burzinski 2025-07-06 09:16:30 -05:00 committed by GitHub
parent e061b6dc55
commit e5a699a004
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 357 additions and 306 deletions

View File

@ -14,8 +14,8 @@ from esphome.const import (
from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns
FactoryResetButton = ld2410_ns.class_("FactoryResetButton", button.Button)
QueryButton = ld2410_ns.class_("QueryButton", button.Button) QueryButton = ld2410_ns.class_("QueryButton", button.Button)
ResetButton = ld2410_ns.class_("ResetButton", button.Button)
RestartButton = ld2410_ns.class_("RestartButton", button.Button) RestartButton = ld2410_ns.class_("RestartButton", button.Button)
CONF_QUERY_PARAMS = "query_params" CONF_QUERY_PARAMS = "query_params"
@ -23,7 +23,7 @@ CONF_QUERY_PARAMS = "query_params"
CONFIG_SCHEMA = { CONFIG_SCHEMA = {
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
cv.Optional(CONF_FACTORY_RESET): button.button_schema( cv.Optional(CONF_FACTORY_RESET): button.button_schema(
ResetButton, FactoryResetButton,
device_class=DEVICE_CLASS_RESTART, device_class=DEVICE_CLASS_RESTART,
entity_category=ENTITY_CATEGORY_CONFIG, entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_RESTART_ALERT, icon=ICON_RESTART_ALERT,
@ -47,7 +47,7 @@ async def to_code(config):
if factory_reset_config := config.get(CONF_FACTORY_RESET): if factory_reset_config := config.get(CONF_FACTORY_RESET):
b = await button.new_button(factory_reset_config) b = await button.new_button(factory_reset_config)
await cg.register_parented(b, config[CONF_LD2410_ID]) await cg.register_parented(b, config[CONF_LD2410_ID])
cg.add(ld2410_component.set_reset_button(b)) cg.add(ld2410_component.set_factory_reset_button(b))
if restart_config := config.get(CONF_RESTART): if restart_config := config.get(CONF_RESTART):
b = await button.new_button(restart_config) b = await button.new_button(restart_config)
await cg.register_parented(b, config[CONF_LD2410_ID]) await cg.register_parented(b, config[CONF_LD2410_ID])

View File

@ -0,0 +1,9 @@
#include "factory_reset_button.h"
namespace esphome {
namespace ld2410 {
void FactoryResetButton::press_action() { this->parent_->factory_reset(); }
} // namespace ld2410
} // namespace esphome

View File

@ -6,9 +6,9 @@
namespace esphome { namespace esphome {
namespace ld2410 { namespace ld2410 {
class ResetButton : public button::Button, public Parented<LD2410Component> { class FactoryResetButton : public button::Button, public Parented<LD2410Component> {
public: public:
ResetButton() = default; FactoryResetButton() = default;
protected: protected:
void press_action() override; void press_action() override;

View File

@ -1,9 +0,0 @@
#include "reset_button.h"
namespace esphome {
namespace ld2410 {
void ResetButton::press_action() { this->parent_->factory_reset(); }
} // namespace ld2410
} // namespace esphome

View File

@ -18,11 +18,10 @@ namespace esphome {
namespace ld2410 { namespace ld2410 {
static const char *const TAG = "ld2410"; static const char *const TAG = "ld2410";
static const char *const NO_MAC = "08:05:04:03:02:01";
static const char *const UNKNOWN_MAC = "unknown"; static const char *const UNKNOWN_MAC = "unknown";
static const char *const VERSION_FMT = "%u.%02X.%02X%02X%02X%02X"; static const char *const VERSION_FMT = "%u.%02X.%02X%02X%02X%02X";
enum BaudRateStructure : uint8_t { enum BaudRate : uint8_t {
BAUD_RATE_9600 = 1, BAUD_RATE_9600 = 1,
BAUD_RATE_19200 = 2, BAUD_RATE_19200 = 2,
BAUD_RATE_38400 = 3, BAUD_RATE_38400 = 3,
@ -33,23 +32,23 @@ enum BaudRateStructure : uint8_t {
BAUD_RATE_460800 = 8, BAUD_RATE_460800 = 8,
}; };
enum DistanceResolutionStructure : uint8_t { enum DistanceResolution : uint8_t {
DISTANCE_RESOLUTION_0_2 = 0x01, DISTANCE_RESOLUTION_0_2 = 0x01,
DISTANCE_RESOLUTION_0_75 = 0x00, DISTANCE_RESOLUTION_0_75 = 0x00,
}; };
enum LightFunctionStructure : uint8_t { enum LightFunction : uint8_t {
LIGHT_FUNCTION_OFF = 0x00, LIGHT_FUNCTION_OFF = 0x00,
LIGHT_FUNCTION_BELOW = 0x01, LIGHT_FUNCTION_BELOW = 0x01,
LIGHT_FUNCTION_ABOVE = 0x02, LIGHT_FUNCTION_ABOVE = 0x02,
}; };
enum OutPinLevelStructure : uint8_t { enum OutPinLevel : uint8_t {
OUT_PIN_LEVEL_LOW = 0x00, OUT_PIN_LEVEL_LOW = 0x00,
OUT_PIN_LEVEL_HIGH = 0x01, OUT_PIN_LEVEL_HIGH = 0x01,
}; };
enum PeriodicDataStructure : uint8_t { enum PeriodicData : uint8_t {
DATA_TYPES = 6, DATA_TYPES = 6,
TARGET_STATES = 8, TARGET_STATES = 8,
MOVING_TARGET_LOW = 9, MOVING_TARGET_LOW = 9,
@ -67,12 +66,12 @@ enum PeriodicDataStructure : uint8_t {
}; };
enum PeriodicDataValue : uint8_t { enum PeriodicDataValue : uint8_t {
HEAD = 0xAA, HEADER = 0xAA,
END = 0x55, FOOTER = 0x55,
CHECK = 0x00, CHECK = 0x00,
}; };
enum AckDataStructure : uint8_t { enum AckData : uint8_t {
COMMAND = 6, COMMAND = 6,
COMMAND_STATUS = 7, COMMAND_STATUS = 7,
}; };
@ -80,11 +79,11 @@ enum AckDataStructure : uint8_t {
// Memory-efficient lookup tables // Memory-efficient lookup tables
struct StringToUint8 { struct StringToUint8 {
const char *str; const char *str;
uint8_t value; const uint8_t value;
}; };
struct Uint8ToString { struct Uint8ToString {
uint8_t value; const uint8_t value;
const char *str; const char *str;
}; };
@ -144,96 +143,119 @@ template<size_t N> const char *find_str(const Uint8ToString (&arr)[N], uint8_t v
} }
// Commands // Commands
static const uint8_t CMD_ENABLE_CONF = 0xFF; static constexpr uint8_t CMD_ENABLE_CONF = 0xFF;
static const uint8_t CMD_DISABLE_CONF = 0xFE; static constexpr uint8_t CMD_DISABLE_CONF = 0xFE;
static const uint8_t CMD_ENABLE_ENG = 0x62; static constexpr uint8_t CMD_ENABLE_ENG = 0x62;
static const uint8_t CMD_DISABLE_ENG = 0x63; static constexpr uint8_t CMD_DISABLE_ENG = 0x63;
static const uint8_t CMD_MAXDIST_DURATION = 0x60; static constexpr uint8_t CMD_MAXDIST_DURATION = 0x60;
static const uint8_t CMD_QUERY = 0x61; static constexpr uint8_t CMD_QUERY = 0x61;
static const uint8_t CMD_GATE_SENS = 0x64; static constexpr uint8_t CMD_GATE_SENS = 0x64;
static const uint8_t CMD_VERSION = 0xA0; static constexpr uint8_t CMD_QUERY_VERSION = 0xA0;
static const uint8_t CMD_QUERY_DISTANCE_RESOLUTION = 0xAB; static constexpr uint8_t CMD_QUERY_DISTANCE_RESOLUTION = 0xAB;
static const uint8_t CMD_SET_DISTANCE_RESOLUTION = 0xAA; static constexpr uint8_t CMD_SET_DISTANCE_RESOLUTION = 0xAA;
static const uint8_t CMD_QUERY_LIGHT_CONTROL = 0xAE; static constexpr uint8_t CMD_QUERY_LIGHT_CONTROL = 0xAE;
static const uint8_t CMD_SET_LIGHT_CONTROL = 0xAD; static constexpr uint8_t CMD_SET_LIGHT_CONTROL = 0xAD;
static const uint8_t CMD_SET_BAUD_RATE = 0xA1; static constexpr uint8_t CMD_SET_BAUD_RATE = 0xA1;
static const uint8_t CMD_BT_PASSWORD = 0xA9; static constexpr uint8_t CMD_BT_PASSWORD = 0xA9;
static const uint8_t CMD_MAC = 0xA5; static constexpr uint8_t CMD_QUERY_MAC_ADDRESS = 0xA5;
static const uint8_t CMD_RESET = 0xA2; static constexpr uint8_t CMD_RESET = 0xA2;
static const uint8_t CMD_RESTART = 0xA3; static constexpr uint8_t CMD_RESTART = 0xA3;
static const uint8_t CMD_BLUETOOTH = 0xA4; static constexpr uint8_t CMD_BLUETOOTH = 0xA4;
// Commands values // Commands values
static const uint8_t CMD_MAX_MOVE_VALUE = 0x00; static constexpr uint8_t CMD_MAX_MOVE_VALUE = 0x00;
static const uint8_t CMD_MAX_STILL_VALUE = 0x01; static constexpr uint8_t CMD_MAX_STILL_VALUE = 0x01;
static const uint8_t CMD_DURATION_VALUE = 0x02; static constexpr uint8_t CMD_DURATION_VALUE = 0x02;
// Header & Footer size
static constexpr uint8_t HEADER_FOOTER_SIZE = 4;
// Command Header & Footer // Command Header & Footer
static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA}; static constexpr uint8_t CMD_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xFD, 0xFC, 0xFB, 0xFA};
static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01}; static constexpr uint8_t CMD_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0x04, 0x03, 0x02, 0x01};
// Data Header & Footer // Data Header & Footer
static const uint8_t DATA_FRAME_HEADER[4] = {0xF4, 0xF3, 0xF2, 0xF1}; static constexpr uint8_t DATA_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xF4, 0xF3, 0xF2, 0xF1};
static const uint8_t DATA_FRAME_END[4] = {0xF8, 0xF7, 0xF6, 0xF5}; static constexpr uint8_t DATA_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0xF8, 0xF7, 0xF6, 0xF5};
// 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 int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
static bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
for (uint8_t i = 0; i < HEADER_FOOTER_SIZE; i++) {
if (header_footer[i] != buffer[i]) {
return false; // Mismatch in header/footer
}
}
return true; // Valid header/footer
}
void LD2410Component::dump_config() { void LD2410Component::dump_config() {
ESP_LOGCONFIG(TAG, "LD2410:"); std::string mac_str =
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC;
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5],
this->version_[4], this->version_[3], this->version_[2]);
ESP_LOGCONFIG(TAG,
"LD2410:\n"
" Firmware version: %s\n"
" MAC address: %s\n"
" Throttle: %u ms",
version.c_str(), mac_str.c_str(), this->throttle_);
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_); ESP_LOGCONFIG(TAG, "Binary Sensors:");
LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); LOG_BINARY_SENSOR(" ", "Target", this->target_binary_sensor_);
LOG_BINARY_SENSOR(" ", "StillTargetBinarySensor", this->still_target_binary_sensor_); LOG_BINARY_SENSOR(" ", "MovingTarget", this->moving_target_binary_sensor_);
LOG_BINARY_SENSOR(" ", "OutPinPresenceStatusBinarySensor", this->out_pin_presence_status_binary_sensor_); LOG_BINARY_SENSOR(" ", "StillTarget", this->still_target_binary_sensor_);
#endif LOG_BINARY_SENSOR(" ", "OutPinPresenceStatus", this->out_pin_presence_status_binary_sensor_);
#ifdef USE_SWITCH
LOG_SWITCH(" ", "EngineeringModeSwitch", this->engineering_mode_switch_);
LOG_SWITCH(" ", "BluetoothSwitch", this->bluetooth_switch_);
#endif
#ifdef USE_BUTTON
LOG_BUTTON(" ", "ResetButton", this->reset_button_);
LOG_BUTTON(" ", "RestartButton", this->restart_button_);
LOG_BUTTON(" ", "QueryButton", this->query_button_);
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
LOG_SENSOR(" ", "LightSensor", this->light_sensor_); ESP_LOGCONFIG(TAG, "Sensors:");
LOG_SENSOR(" ", "MovingTargetDistanceSensor", this->moving_target_distance_sensor_); LOG_SENSOR(" ", "Light", this->light_sensor_);
LOG_SENSOR(" ", "StillTargetDistanceSensor", this->still_target_distance_sensor_); LOG_SENSOR(" ", "DetectionDistance", this->detection_distance_sensor_);
LOG_SENSOR(" ", "MovingTargetEnergySensor", this->moving_target_energy_sensor_); LOG_SENSOR(" ", "MovingTargetDistance", this->moving_target_distance_sensor_);
LOG_SENSOR(" ", "StillTargetEnergySensor", this->still_target_energy_sensor_); LOG_SENSOR(" ", "MovingTargetEnergy", this->moving_target_energy_sensor_);
LOG_SENSOR(" ", "DetectionDistanceSensor", this->detection_distance_sensor_); LOG_SENSOR(" ", "StillTargetDistance", this->still_target_distance_sensor_);
for (sensor::Sensor *s : this->gate_still_sensors_) { LOG_SENSOR(" ", "StillTargetEnergy", this->still_target_energy_sensor_);
LOG_SENSOR(" ", "NthGateStillSesnsor", s);
}
for (sensor::Sensor *s : this->gate_move_sensors_) { for (sensor::Sensor *s : this->gate_move_sensors_) {
LOG_SENSOR(" ", "NthGateMoveSesnsor", s); LOG_SENSOR(" ", "GateMove", s);
}
for (sensor::Sensor *s : this->gate_still_sensors_) {
LOG_SENSOR(" ", "GateStill", s);
} }
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "VersionTextSensor", this->version_text_sensor_); ESP_LOGCONFIG(TAG, "Text Sensors:");
LOG_TEXT_SENSOR(" ", "MacTextSensor", this->mac_text_sensor_); LOG_TEXT_SENSOR(" ", "Mac", this->mac_text_sensor_);
#endif LOG_TEXT_SENSOR(" ", "Version", this->version_text_sensor_);
#ifdef USE_SELECT
LOG_SELECT(" ", "LightFunctionSelect", this->light_function_select_);
LOG_SELECT(" ", "OutPinLevelSelect", this->out_pin_level_select_);
LOG_SELECT(" ", "DistanceResolutionSelect", this->distance_resolution_select_);
LOG_SELECT(" ", "BaudRateSelect", this->baud_rate_select_);
#endif #endif
#ifdef USE_NUMBER #ifdef USE_NUMBER
LOG_NUMBER(" ", "LightThresholdNumber", this->light_threshold_number_); ESP_LOGCONFIG(TAG, "Numbers:");
LOG_NUMBER(" ", "MaxStillDistanceGateNumber", this->max_still_distance_gate_number_); LOG_NUMBER(" ", "LightThreshold", this->light_threshold_number_);
LOG_NUMBER(" ", "MaxMoveDistanceGateNumber", this->max_move_distance_gate_number_); LOG_NUMBER(" ", "MaxMoveDistanceGate", this->max_move_distance_gate_number_);
LOG_NUMBER(" ", "TimeoutNumber", this->timeout_number_); LOG_NUMBER(" ", "MaxStillDistanceGate", this->max_still_distance_gate_number_);
for (number::Number *n : this->gate_still_threshold_numbers_) { LOG_NUMBER(" ", "Timeout", this->timeout_number_);
LOG_NUMBER(" ", "Still Thresholds Number", n);
}
for (number::Number *n : this->gate_move_threshold_numbers_) { for (number::Number *n : this->gate_move_threshold_numbers_) {
LOG_NUMBER(" ", "Move Thresholds Number", n); LOG_NUMBER(" ", "MoveThreshold", n);
}
for (number::Number *n : this->gate_still_threshold_numbers_) {
LOG_NUMBER(" ", "StillThreshold", n);
} }
#endif #endif
this->read_all_info(); #ifdef USE_SELECT
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG, "Selects:");
" Throttle: %ums\n" LOG_SELECT(" ", "BaudRate", this->baud_rate_select_);
" MAC address: %s\n" LOG_SELECT(" ", "DistanceResolution", this->distance_resolution_select_);
" Firmware version: %s", LOG_SELECT(" ", "LightFunction", this->light_function_select_);
this->throttle_, this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_.c_str(), this->version_.c_str()); LOG_SELECT(" ", "OutPinLevel", this->out_pin_level_select_);
#endif
#ifdef USE_SWITCH
ESP_LOGCONFIG(TAG, "Switches:");
LOG_SWITCH(" ", "Bluetooth", this->bluetooth_switch_);
LOG_SWITCH(" ", "EngineeringMode", this->engineering_mode_switch_);
#endif
#ifdef USE_BUTTON
ESP_LOGCONFIG(TAG, "Buttons:");
LOG_BUTTON(" ", "FactoryReset", this->factory_reset_button_);
LOG_BUTTON(" ", "Query", this->query_button_);
LOG_BUTTON(" ", "Restart", this->restart_button_);
#endif
} }
void LD2410Component::setup() { void LD2410Component::setup() {
@ -246,12 +268,12 @@ void LD2410Component::read_all_info() {
this->get_version_(); this->get_version_();
this->get_mac_(); this->get_mac_();
this->get_distance_resolution_(); this->get_distance_resolution_();
this->get_light_control_(); this->query_light_control_();
this->query_parameters_(); this->query_parameters_();
this->set_config_mode_(false); this->set_config_mode_(false);
#ifdef USE_SELECT #ifdef USE_SELECT
const auto baud_rate = std::to_string(this->parent_->get_baud_rate()); const auto baud_rate = std::to_string(this->parent_->get_baud_rate());
if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) { if (this->baud_rate_select_ != nullptr) {
this->baud_rate_select_->publish_state(baud_rate); this->baud_rate_select_->publish_state(baud_rate);
} }
#endif #endif
@ -264,66 +286,59 @@ void LD2410Component::restart_and_read_all_info() {
} }
void LD2410Component::loop() { void LD2410Component::loop() {
const int max_line_length = 80; while (this->available()) {
static uint8_t buffer[max_line_length]; this->readline_(this->read());
while (available()) {
this->readline_(read(), buffer, max_line_length);
} }
} }
void LD2410Component::send_command_(uint8_t command, const uint8_t *command_value, int command_value_len) { void LD2410Component::send_command_(uint8_t command, const uint8_t *command_value, uint8_t command_value_len) {
ESP_LOGV(TAG, "Sending COMMAND %02X", command); ESP_LOGV(TAG, "Sending COMMAND %02X", command);
// frame start bytes // frame header bytes
this->write_array(CMD_FRAME_HEADER, 4); this->write_array(CMD_FRAME_HEADER, sizeof(CMD_FRAME_HEADER));
// length bytes // length bytes
int len = 2; uint8_t len = 2;
if (command_value != nullptr) if (command_value != nullptr) {
len += command_value_len; len += command_value_len;
this->write_byte(lowbyte(len)); }
this->write_byte(highbyte(len)); uint8_t len_cmd[] = {lowbyte(len), highbyte(len), command, 0x00};
this->write_array(len_cmd, sizeof(len_cmd));
// command
this->write_byte(lowbyte(command));
this->write_byte(highbyte(command));
// command value bytes // command value bytes
if (command_value != nullptr) { if (command_value != nullptr) {
for (int i = 0; i < command_value_len; i++) { for (uint8_t i = 0; i < command_value_len; i++) {
this->write_byte(command_value[i]); this->write_byte(command_value[i]);
} }
} }
// frame end bytes // frame footer bytes
this->write_array(CMD_FRAME_END, 4); this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER));
// FIXME to remove // FIXME to remove
delay(50); // NOLINT delay(50); // NOLINT
} }
void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { void LD2410Component::handle_periodic_data_() {
if (len < 12) // Reduce data update rate to reduce home assistant database growth
return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes // Check this first to prevent unnecessary processing done in later checks/parsing
if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1) // check 4 frame start bytes if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
return; return;
if (buffer[7] != HEAD || buffer[len - 6] != END || buffer[len - 5] != CHECK) // Check constant values }
return; // data head=0xAA, data end=0x55, crc=0x00 // 4 frame header bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame footer bytes
// data header=0xAA, data footer=0x55, crc=0x00
/* if (this->buffer_pos_ < 12 || !ld2410::validate_header_footer(DATA_FRAME_HEADER, this->buffer_data_) ||
Reduce data update rate to prevent home assistant database size grow fast this->buffer_data_[7] != HEADER || this->buffer_data_[this->buffer_pos_ - 6] != FOOTER ||
*/ this->buffer_data_[this->buffer_pos_ - 5] != CHECK) {
int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - last_periodic_millis_ < this->throttle_)
return; return;
last_periodic_millis_ = current_millis; }
// Save the timestamp after validating the frame so, if invalid, we'll take the next frame immediately
this->last_periodic_millis_ = App.get_loop_component_start_time();
/* /*
Data Type: 7th Data Type: 7th
0x01: Engineering mode 0x01: Engineering mode
0x02: Normal mode 0x02: Normal mode
*/ */
bool engineering_mode = buffer[DATA_TYPES] == 0x01; bool engineering_mode = this->buffer_data_[DATA_TYPES] == 0x01;
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->engineering_mode_switch_ != nullptr && if (this->engineering_mode_switch_ != nullptr) {
current_millis - last_engineering_mode_change_millis_ > this->throttle_) {
this->engineering_mode_switch_->publish_state(engineering_mode); this->engineering_mode_switch_->publish_state(engineering_mode);
} }
#endif #endif
@ -335,7 +350,7 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
0x02 = Still targets 0x02 = Still targets
0x03 = Moving+Still targets 0x03 = Moving+Still targets
*/ */
char target_state = buffer[TARGET_STATES]; char target_state = this->buffer_data_[TARGET_STATES];
if (this->target_binary_sensor_ != nullptr) { if (this->target_binary_sensor_ != nullptr) {
this->target_binary_sensor_->publish_state(target_state != 0x00); this->target_binary_sensor_->publish_state(target_state != 0x00);
} }
@ -355,27 +370,30 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
*/ */
#ifdef USE_SENSOR #ifdef USE_SENSOR
if (this->moving_target_distance_sensor_ != nullptr) { if (this->moving_target_distance_sensor_ != nullptr) {
int new_moving_target_distance = ld2410::two_byte_to_int(buffer[MOVING_TARGET_LOW], buffer[MOVING_TARGET_HIGH]); int new_moving_target_distance =
ld2410::two_byte_to_int(this->buffer_data_[MOVING_TARGET_LOW], this->buffer_data_[MOVING_TARGET_HIGH]);
if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance) if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
this->moving_target_distance_sensor_->publish_state(new_moving_target_distance); this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
} }
if (this->moving_target_energy_sensor_ != nullptr) { if (this->moving_target_energy_sensor_ != nullptr) {
int new_moving_target_energy = buffer[MOVING_ENERGY]; int new_moving_target_energy = this->buffer_data_[MOVING_ENERGY];
if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy) if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
this->moving_target_energy_sensor_->publish_state(new_moving_target_energy); this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
} }
if (this->still_target_distance_sensor_ != nullptr) { if (this->still_target_distance_sensor_ != nullptr) {
int new_still_target_distance = ld2410::two_byte_to_int(buffer[STILL_TARGET_LOW], buffer[STILL_TARGET_HIGH]); int new_still_target_distance =
ld2410::two_byte_to_int(this->buffer_data_[STILL_TARGET_LOW], this->buffer_data_[STILL_TARGET_HIGH]);
if (this->still_target_distance_sensor_->get_state() != new_still_target_distance) if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
this->still_target_distance_sensor_->publish_state(new_still_target_distance); this->still_target_distance_sensor_->publish_state(new_still_target_distance);
} }
if (this->still_target_energy_sensor_ != nullptr) { if (this->still_target_energy_sensor_ != nullptr) {
int new_still_target_energy = buffer[STILL_ENERGY]; int new_still_target_energy = this->buffer_data_[STILL_ENERGY];
if (this->still_target_energy_sensor_->get_state() != new_still_target_energy) if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
this->still_target_energy_sensor_->publish_state(new_still_target_energy); this->still_target_energy_sensor_->publish_state(new_still_target_energy);
} }
if (this->detection_distance_sensor_ != nullptr) { if (this->detection_distance_sensor_ != nullptr) {
int new_detect_distance = ld2410::two_byte_to_int(buffer[DETECT_DISTANCE_LOW], buffer[DETECT_DISTANCE_HIGH]); int new_detect_distance =
ld2410::two_byte_to_int(this->buffer_data_[DETECT_DISTANCE_LOW], this->buffer_data_[DETECT_DISTANCE_HIGH]);
if (this->detection_distance_sensor_->get_state() != new_detect_distance) if (this->detection_distance_sensor_->get_state() != new_detect_distance)
this->detection_distance_sensor_->publish_state(new_detect_distance); this->detection_distance_sensor_->publish_state(new_detect_distance);
} }
@ -388,7 +406,7 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) { for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) {
sensor::Sensor *s = this->gate_move_sensors_[i]; sensor::Sensor *s = this->gate_move_sensors_[i];
if (s != nullptr) { if (s != nullptr) {
s->publish_state(buffer[MOVING_SENSOR_START + i]); s->publish_state(this->buffer_data_[MOVING_SENSOR_START + i]);
} }
} }
/* /*
@ -397,16 +415,17 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) { for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) {
sensor::Sensor *s = this->gate_still_sensors_[i]; sensor::Sensor *s = this->gate_still_sensors_[i];
if (s != nullptr) { if (s != nullptr) {
s->publish_state(buffer[STILL_SENSOR_START + i]); s->publish_state(this->buffer_data_[STILL_SENSOR_START + i]);
} }
} }
/* /*
Light sensor: 38th bytes Light sensor: 38th bytes
*/ */
if (this->light_sensor_ != nullptr) { if (this->light_sensor_ != nullptr) {
int new_light_sensor = buffer[LIGHT_SENSOR]; int new_light_sensor = this->buffer_data_[LIGHT_SENSOR];
if (this->light_sensor_->get_state() != new_light_sensor) if (this->light_sensor_->get_state() != new_light_sensor) {
this->light_sensor_->publish_state(new_light_sensor); this->light_sensor_->publish_state(new_light_sensor);
}
} }
} else { } else {
for (auto *s : this->gate_move_sensors_) { for (auto *s : this->gate_move_sensors_) {
@ -427,7 +446,7 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
if (engineering_mode) { if (engineering_mode) {
if (this->out_pin_presence_status_binary_sensor_ != nullptr) { if (this->out_pin_presence_status_binary_sensor_ != nullptr) {
this->out_pin_presence_status_binary_sensor_->publish_state(buffer[OUT_PIN_SENSOR] == 0x01); this->out_pin_presence_status_binary_sensor_->publish_state(this->buffer_data_[OUT_PIN_SENSOR] == 0x01);
} }
} else { } else {
if (this->out_pin_presence_status_binary_sensor_ != nullptr) { if (this->out_pin_presence_status_binary_sensor_ != nullptr) {
@ -439,127 +458,149 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
#ifdef USE_NUMBER #ifdef USE_NUMBER
std::function<void(void)> set_number_value(number::Number *n, float value) { std::function<void(void)> set_number_value(number::Number *n, float value) {
float normalized_value = value * 1.0; if (n != nullptr && (!n->has_state() || n->state != value)) {
if (n != nullptr && (!n->has_state() || n->state != normalized_value)) { n->state = value;
n->state = normalized_value; return [n, value]() { n->publish_state(value); };
return [n, normalized_value]() { n->publish_state(normalized_value); };
} }
return []() {}; return []() {};
} }
#endif #endif
bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { bool LD2410Component::handle_ack_data_() {
ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", this->buffer_data_[COMMAND]);
if (len < 10) { if (this->buffer_pos_ < 10) {
ESP_LOGE(TAG, "Invalid length"); ESP_LOGE(TAG, "Invalid length");
return true; return true;
} }
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes if (!ld2410::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) {
ESP_LOGE(TAG, "Invalid header"); ESP_LOGW(TAG, "Invalid header: %s", format_hex_pretty(this->buffer_data_, HEADER_FOOTER_SIZE).c_str());
return true; return true;
} }
if (buffer[COMMAND_STATUS] != 0x01) { if (this->buffer_data_[COMMAND_STATUS] != 0x01) {
ESP_LOGE(TAG, "Invalid status"); ESP_LOGE(TAG, "Invalid status");
return true; return true;
} }
if (ld2410::two_byte_to_int(buffer[8], buffer[9]) != 0x00) { if (ld2410::two_byte_to_int(this->buffer_data_[8], this->buffer_data_[9]) != 0x00) {
ESP_LOGE(TAG, "Invalid command: %u, %u", buffer[8], buffer[9]); ESP_LOGW(TAG, "Invalid command: %02X, %02X", this->buffer_data_[8], this->buffer_data_[9]);
return true; return true;
} }
switch (buffer[COMMAND]) { switch (this->buffer_data_[COMMAND]) {
case lowbyte(CMD_ENABLE_CONF): case CMD_ENABLE_CONF:
ESP_LOGV(TAG, "Enable conf"); ESP_LOGV(TAG, "Enable conf");
break; break;
case lowbyte(CMD_DISABLE_CONF):
case CMD_DISABLE_CONF:
ESP_LOGV(TAG, "Disabled conf"); ESP_LOGV(TAG, "Disabled conf");
break; break;
case lowbyte(CMD_SET_BAUD_RATE):
case CMD_SET_BAUD_RATE:
ESP_LOGV(TAG, "Baud rate change"); ESP_LOGV(TAG, "Baud rate change");
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->baud_rate_select_ != nullptr) { if (this->baud_rate_select_ != nullptr) {
ESP_LOGE(TAG, "Configure baud rate to %s and reinstall", this->baud_rate_select_->state.c_str()); ESP_LOGE(TAG, "Change baud rate to %s and reinstall", this->baud_rate_select_->state.c_str());
} }
#endif #endif
break; break;
case lowbyte(CMD_VERSION):
this->version_ = str_sprintf(VERSION_FMT, buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], buffer[14]); case CMD_QUERY_VERSION: {
ESP_LOGV(TAG, "Firmware version: %s", this->version_.c_str()); std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_));
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5],
this->version_[4], this->version_[3], this->version_[2]);
ESP_LOGV(TAG, "Firmware version: %s", version.c_str());
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->version_text_sensor_ != nullptr) { if (this->version_text_sensor_ != nullptr) {
this->version_text_sensor_->publish_state(this->version_); this->version_text_sensor_->publish_state(version);
} }
#endif #endif
break; break;
case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): { }
std::string distance_resolution =
find_str(DISTANCE_RESOLUTIONS_BY_UINT, ld2410::two_byte_to_int(buffer[10], buffer[11])); case CMD_QUERY_DISTANCE_RESOLUTION: {
ESP_LOGV(TAG, "Distance resolution: %s", distance_resolution.c_str()); const auto *distance_resolution = find_str(DISTANCE_RESOLUTIONS_BY_UINT, this->buffer_data_[10]);
ESP_LOGV(TAG, "Distance resolution: %s", distance_resolution);
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->distance_resolution_select_ != nullptr && if (this->distance_resolution_select_ != nullptr) {
this->distance_resolution_select_->state != distance_resolution) {
this->distance_resolution_select_->publish_state(distance_resolution); this->distance_resolution_select_->publish_state(distance_resolution);
} }
#endif #endif
} break; break;
case lowbyte(CMD_QUERY_LIGHT_CONTROL): { }
this->light_function_ = find_str(LIGHT_FUNCTIONS_BY_UINT, buffer[10]);
this->light_threshold_ = buffer[11] * 1.0; case CMD_QUERY_LIGHT_CONTROL: {
this->out_pin_level_ = find_str(OUT_PIN_LEVELS_BY_UINT, buffer[12]); this->light_function_ = this->buffer_data_[10];
ESP_LOGV(TAG, "Light function: %s", const_cast<char *>(this->light_function_.c_str())); this->light_threshold_ = this->buffer_data_[11];
ESP_LOGV(TAG, "Light threshold: %f", this->light_threshold_); this->out_pin_level_ = this->buffer_data_[12];
ESP_LOGV(TAG, "Out pin level: %s", const_cast<char *>(this->out_pin_level_.c_str())); const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_);
const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_);
ESP_LOGV(TAG,
"Light function is: %s\n"
"Light threshold is: %u\n"
"Out pin level: %s",
light_function_str, this->light_threshold_, out_pin_level_str);
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) { if (this->light_function_select_ != nullptr) {
this->light_function_select_->publish_state(this->light_function_); this->light_function_select_->publish_state(light_function_str);
} }
if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->state != this->out_pin_level_) { if (this->out_pin_level_select_ != nullptr) {
this->out_pin_level_select_->publish_state(this->out_pin_level_); this->out_pin_level_select_->publish_state(out_pin_level_str);
} }
#endif #endif
#ifdef USE_NUMBER #ifdef USE_NUMBER
if (this->light_threshold_number_ != nullptr && if (this->light_threshold_number_ != nullptr) {
(!this->light_threshold_number_->has_state() || this->light_threshold_number_->publish_state(static_cast<float>(this->light_threshold_));
this->light_threshold_number_->state != this->light_threshold_)) {
this->light_threshold_number_->publish_state(this->light_threshold_);
} }
#endif #endif
} break; break;
case lowbyte(CMD_MAC): }
if (len < 20) { case CMD_QUERY_MAC_ADDRESS: {
if (this->buffer_pos_ < 20) {
return false; return false;
} }
this->mac_ = format_mac_address_pretty(&buffer[10]);
ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); this->bluetooth_on_ = std::memcmp(&this->buffer_data_[10], NO_MAC, sizeof(NO_MAC)) != 0;
if (this->bluetooth_on_) {
std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_));
}
std::string mac_str =
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC;
ESP_LOGV(TAG, "MAC address: %s", mac_str.c_str());
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->mac_text_sensor_ != nullptr) { if (this->mac_text_sensor_ != nullptr) {
this->mac_text_sensor_->publish_state(this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_); this->mac_text_sensor_->publish_state(mac_str);
} }
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->bluetooth_switch_ != nullptr) { if (this->bluetooth_switch_ != nullptr) {
this->bluetooth_switch_->publish_state(this->mac_ != NO_MAC); this->bluetooth_switch_->publish_state(this->bluetooth_on_);
} }
#endif #endif
break; break;
case lowbyte(CMD_GATE_SENS): }
case CMD_GATE_SENS:
ESP_LOGV(TAG, "Sensitivity"); ESP_LOGV(TAG, "Sensitivity");
break; break;
case lowbyte(CMD_BLUETOOTH):
case CMD_BLUETOOTH:
ESP_LOGV(TAG, "Bluetooth"); ESP_LOGV(TAG, "Bluetooth");
break; break;
case lowbyte(CMD_SET_DISTANCE_RESOLUTION):
case CMD_SET_DISTANCE_RESOLUTION:
ESP_LOGV(TAG, "Set distance resolution"); ESP_LOGV(TAG, "Set distance resolution");
break; break;
case lowbyte(CMD_SET_LIGHT_CONTROL):
case CMD_SET_LIGHT_CONTROL:
ESP_LOGV(TAG, "Set light control"); ESP_LOGV(TAG, "Set light control");
break; break;
case lowbyte(CMD_BT_PASSWORD):
case CMD_BT_PASSWORD:
ESP_LOGV(TAG, "Set bluetooth password"); ESP_LOGV(TAG, "Set bluetooth password");
break; break;
case lowbyte(CMD_QUERY): // Query parameters response
{ case CMD_QUERY: { // Query parameters response
if (buffer[10] != 0xAA) if (this->buffer_data_[10] != 0xAA)
return true; // value head=0xAA return true; // value head=0xAA
#ifdef USE_NUMBER #ifdef USE_NUMBER
/* /*
@ -567,29 +608,31 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
Still distance range: 14th byte Still distance range: 14th byte
*/ */
std::vector<std::function<void(void)>> updates; std::vector<std::function<void(void)>> updates;
updates.push_back(set_number_value(this->max_move_distance_gate_number_, buffer[12])); updates.push_back(set_number_value(this->max_move_distance_gate_number_, this->buffer_data_[12]));
updates.push_back(set_number_value(this->max_still_distance_gate_number_, buffer[13])); updates.push_back(set_number_value(this->max_still_distance_gate_number_, this->buffer_data_[13]));
/* /*
Moving Sensitivities: 15~23th bytes Moving Sensitivities: 15~23th bytes
*/ */
for (std::vector<number::Number *>::size_type i = 0; i != this->gate_move_threshold_numbers_.size(); i++) { for (std::vector<number::Number *>::size_type i = 0; i != this->gate_move_threshold_numbers_.size(); i++) {
updates.push_back(set_number_value(this->gate_move_threshold_numbers_[i], buffer[14 + i])); updates.push_back(set_number_value(this->gate_move_threshold_numbers_[i], this->buffer_data_[14 + i]));
} }
/* /*
Still Sensitivities: 24~32th bytes Still Sensitivities: 24~32th bytes
*/ */
for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) { for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) {
updates.push_back(set_number_value(this->gate_still_threshold_numbers_[i], buffer[23 + i])); updates.push_back(set_number_value(this->gate_still_threshold_numbers_[i], this->buffer_data_[23 + i]));
} }
/* /*
None Duration: 33~34th bytes None Duration: 33~34th bytes
*/ */
updates.push_back(set_number_value(this->timeout_number_, ld2410::two_byte_to_int(buffer[32], buffer[33]))); updates.push_back(set_number_value(this->timeout_number_,
ld2410::two_byte_to_int(this->buffer_data_[32], this->buffer_data_[33])));
for (auto &update : updates) { for (auto &update : updates) {
update(); update();
} }
#endif #endif
} break; break;
}
default: default:
break; break;
} }
@ -597,59 +640,66 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
return true; return true;
} }
void LD2410Component::readline_(int readch, uint8_t *buffer, int len) { void LD2410Component::readline_(int readch) {
static int pos = 0; if (readch < 0) {
return; // No data available
}
if (readch >= 0) { if (this->buffer_pos_ < MAX_LINE_LENGTH - 1) {
if (pos < len - 1) { this->buffer_data_[this->buffer_pos_++] = readch;
buffer[pos++] = readch; this->buffer_data_[this->buffer_pos_] = 0;
buffer[pos] = 0; } else {
// We should never get here, but just in case...
ESP_LOGW(TAG, "Max command length exceeded; ignoring");
this->buffer_pos_ = 0;
}
if (this->buffer_pos_ < 4) {
return; // Not enough data to process yet
}
if (this->buffer_data_[this->buffer_pos_ - 4] == DATA_FRAME_FOOTER[0] &&
this->buffer_data_[this->buffer_pos_ - 3] == DATA_FRAME_FOOTER[1] &&
this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[2] &&
this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[3]) {
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
this->handle_periodic_data_();
this->buffer_pos_ = 0; // Reset position index for next message
} else if (this->buffer_data_[this->buffer_pos_ - 4] == CMD_FRAME_FOOTER[0] &&
this->buffer_data_[this->buffer_pos_ - 3] == CMD_FRAME_FOOTER[1] &&
this->buffer_data_[this->buffer_pos_ - 2] == CMD_FRAME_FOOTER[2] &&
this->buffer_data_[this->buffer_pos_ - 1] == CMD_FRAME_FOOTER[3]) {
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
if (this->handle_ack_data_()) {
this->buffer_pos_ = 0; // Reset position index for next message
} else { } else {
pos = 0; ESP_LOGV(TAG, "Ack Data incomplete");
}
if (pos >= 4) {
if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
ESP_LOGV(TAG, "Will handle Periodic Data");
this->handle_periodic_data_(buffer, pos);
pos = 0; // Reset position index ready for next time
} else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
buffer[pos - 1] == 0x01) {
ESP_LOGV(TAG, "Will handle ACK Data");
if (this->handle_ack_data_(buffer, pos)) {
pos = 0; // Reset position index ready for next time
} else {
ESP_LOGV(TAG, "ACK Data incomplete");
}
}
} }
} }
} }
void LD2410Component::set_config_mode_(bool enable) { void LD2410Component::set_config_mode_(bool enable) {
uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF; const uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
uint8_t cmd_value[2] = {0x01, 0x00}; const uint8_t cmd_value[2] = {0x01, 0x00};
this->send_command_(cmd, enable ? cmd_value : nullptr, 2); this->send_command_(cmd, enable ? cmd_value : nullptr, sizeof(cmd_value));
} }
void LD2410Component::set_bluetooth(bool enable) { void LD2410Component::set_bluetooth(bool enable) {
this->set_config_mode_(true); this->set_config_mode_(true);
uint8_t enable_cmd_value[2] = {0x01, 0x00}; const uint8_t cmd_value[2] = {enable ? (uint8_t) 0x01 : (uint8_t) 0x00, 0x00};
uint8_t disable_cmd_value[2] = {0x00, 0x00}; this->send_command_(CMD_BLUETOOTH, cmd_value, sizeof(cmd_value));
this->send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2);
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
} }
void LD2410Component::set_distance_resolution(const std::string &state) { void LD2410Component::set_distance_resolution(const std::string &state) {
this->set_config_mode_(true); this->set_config_mode_(true);
uint8_t cmd_value[2] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00}; const uint8_t cmd_value[2] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00};
this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, 2); this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value));
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
} }
void LD2410Component::set_baud_rate(const std::string &state) { void LD2410Component::set_baud_rate(const std::string &state) {
this->set_config_mode_(true); this->set_config_mode_(true);
uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00}; const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00};
this->send_command_(CMD_SET_BAUD_RATE, cmd_value, 2); this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value));
this->set_timeout(200, [this]() { this->restart_(); }); this->set_timeout(200, [this]() { this->restart_(); });
} }
@ -661,14 +711,13 @@ void LD2410Component::set_bluetooth_password(const std::string &password) {
this->set_config_mode_(true); this->set_config_mode_(true);
uint8_t cmd_value[6]; uint8_t cmd_value[6];
std::copy(password.begin(), password.end(), std::begin(cmd_value)); std::copy(password.begin(), password.end(), std::begin(cmd_value));
this->send_command_(CMD_BT_PASSWORD, cmd_value, 6); this->send_command_(CMD_BT_PASSWORD, cmd_value, sizeof(cmd_value));
this->set_config_mode_(false); this->set_config_mode_(false);
} }
void LD2410Component::set_engineering_mode(bool enable) { void LD2410Component::set_engineering_mode(bool enable) {
const uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG;
this->set_config_mode_(true); this->set_config_mode_(true);
last_engineering_mode_change_millis_ = App.get_loop_component_start_time();
uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG;
this->send_command_(cmd, nullptr, 0); this->send_command_(cmd, nullptr, 0);
this->set_config_mode_(false); this->set_config_mode_(false);
} }
@ -682,14 +731,17 @@ void LD2410Component::factory_reset() {
void LD2410Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); } void LD2410Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); }
void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); } void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); }
void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
void LD2410Component::get_version_() { this->send_command_(CMD_QUERY_VERSION, nullptr, 0); }
void LD2410Component::get_mac_() { void LD2410Component::get_mac_() {
uint8_t cmd_value[2] = {0x01, 0x00}; const uint8_t cmd_value[2] = {0x01, 0x00};
this->send_command_(CMD_MAC, cmd_value, 2); this->send_command_(CMD_QUERY_MAC_ADDRESS, cmd_value, sizeof(cmd_value));
} }
void LD2410Component::get_distance_resolution_() { this->send_command_(CMD_QUERY_DISTANCE_RESOLUTION, nullptr, 0); } void LD2410Component::get_distance_resolution_() { this->send_command_(CMD_QUERY_DISTANCE_RESOLUTION, nullptr, 0); }
void LD2410Component::get_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); } void LD2410Component::query_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); }
#ifdef USE_NUMBER #ifdef USE_NUMBER
void LD2410Component::set_max_distances_timeout() { void LD2410Component::set_max_distances_timeout() {
@ -719,7 +771,7 @@ void LD2410Component::set_max_distances_timeout() {
0x00, 0x00,
0x00}; 0x00};
this->set_config_mode_(true); this->set_config_mode_(true);
this->send_command_(CMD_MAXDIST_DURATION, value, 18); this->send_command_(CMD_MAXDIST_DURATION, value, sizeof(value));
delay(50); // NOLINT delay(50); // NOLINT
this->query_parameters_(); this->query_parameters_();
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
@ -749,17 +801,17 @@ void LD2410Component::set_gate_threshold(uint8_t gate) {
uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00, uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00,
0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00, 0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00,
0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00}; 0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00};
this->send_command_(CMD_GATE_SENS, value, 18); this->send_command_(CMD_GATE_SENS, value, sizeof(value));
delay(50); // NOLINT delay(50); // NOLINT
this->query_parameters_(); this->query_parameters_();
this->set_config_mode_(false); this->set_config_mode_(false);
} }
void LD2410Component::set_gate_still_threshold_number(int gate, number::Number *n) { void LD2410Component::set_gate_still_threshold_number(uint8_t gate, number::Number *n) {
this->gate_still_threshold_numbers_[gate] = n; this->gate_still_threshold_numbers_[gate] = n;
} }
void LD2410Component::set_gate_move_threshold_number(int gate, number::Number *n) { void LD2410Component::set_gate_move_threshold_number(uint8_t gate, number::Number *n) {
this->gate_move_threshold_numbers_[gate] = n; this->gate_move_threshold_numbers_[gate] = n;
} }
#endif #endif
@ -767,35 +819,29 @@ void LD2410Component::set_gate_move_threshold_number(int gate, number::Number *n
void LD2410Component::set_light_out_control() { void LD2410Component::set_light_out_control() {
#ifdef USE_NUMBER #ifdef USE_NUMBER
if (this->light_threshold_number_ != nullptr && this->light_threshold_number_->has_state()) { if (this->light_threshold_number_ != nullptr && this->light_threshold_number_->has_state()) {
this->light_threshold_ = this->light_threshold_number_->state; this->light_threshold_ = static_cast<uint8_t>(this->light_threshold_number_->state);
} }
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
this->light_function_ = this->light_function_select_->state; this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->state);
} }
if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) { if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) {
this->out_pin_level_ = this->out_pin_level_select_->state; this->out_pin_level_ = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->state);
} }
#endif #endif
if (this->light_function_.empty() || this->out_pin_level_.empty() || this->light_threshold_ < 0) {
return;
}
this->set_config_mode_(true); this->set_config_mode_(true);
uint8_t light_function = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_); uint8_t value[4] = {this->light_function_, this->light_threshold_, this->out_pin_level_, 0x00};
uint8_t light_threshold = static_cast<uint8_t>(this->light_threshold_); this->send_command_(CMD_SET_LIGHT_CONTROL, value, sizeof(value));
uint8_t out_pin_level = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_);
uint8_t value[4] = {light_function, light_threshold, out_pin_level, 0x00};
this->send_command_(CMD_SET_LIGHT_CONTROL, value, 4);
delay(50); // NOLINT delay(50); // NOLINT
this->get_light_control_(); this->query_light_control_();
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
this->set_config_mode_(false); this->set_config_mode_(false);
} }
#ifdef USE_SENSOR #ifdef USE_SENSOR
void LD2410Component::set_gate_move_sensor(int gate, sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; } void LD2410Component::set_gate_move_sensor(uint8_t gate, sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; }
void LD2410Component::set_gate_still_sensor(int gate, sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; } void LD2410Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; }
#endif #endif
} // namespace ld2410 } // namespace ld2410

View File

@ -29,45 +29,48 @@
namespace esphome { namespace esphome {
namespace ld2410 { namespace ld2410 {
static const uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer
static const uint8_t TOTAL_GATES = 9; // Total number of gates supported by the LD2410
class LD2410Component : public Component, public uart::UARTDevice { class LD2410Component : public Component, public uart::UARTDevice {
#ifdef USE_SENSOR
SUB_SENSOR(moving_target_distance)
SUB_SENSOR(still_target_distance)
SUB_SENSOR(moving_target_energy)
SUB_SENSOR(still_target_energy)
SUB_SENSOR(light)
SUB_SENSOR(detection_distance)
#endif
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(target) SUB_BINARY_SENSOR(out_pin_presence_status)
SUB_BINARY_SENSOR(moving_target) SUB_BINARY_SENSOR(moving_target)
SUB_BINARY_SENSOR(still_target) SUB_BINARY_SENSOR(still_target)
SUB_BINARY_SENSOR(out_pin_presence_status) SUB_BINARY_SENSOR(target)
#endif
#ifdef USE_SENSOR
SUB_SENSOR(light)
SUB_SENSOR(detection_distance)
SUB_SENSOR(moving_target_distance)
SUB_SENSOR(moving_target_energy)
SUB_SENSOR(still_target_distance)
SUB_SENSOR(still_target_energy)
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
SUB_TEXT_SENSOR(version) SUB_TEXT_SENSOR(version)
SUB_TEXT_SENSOR(mac) SUB_TEXT_SENSOR(mac)
#endif #endif
#ifdef USE_NUMBER
SUB_NUMBER(light_threshold)
SUB_NUMBER(max_move_distance_gate)
SUB_NUMBER(max_still_distance_gate)
SUB_NUMBER(timeout)
#endif
#ifdef USE_SELECT #ifdef USE_SELECT
SUB_SELECT(distance_resolution)
SUB_SELECT(baud_rate) SUB_SELECT(baud_rate)
SUB_SELECT(distance_resolution)
SUB_SELECT(light_function) SUB_SELECT(light_function)
SUB_SELECT(out_pin_level) SUB_SELECT(out_pin_level)
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
SUB_SWITCH(engineering_mode)
SUB_SWITCH(bluetooth) SUB_SWITCH(bluetooth)
SUB_SWITCH(engineering_mode)
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
SUB_BUTTON(reset) SUB_BUTTON(factory_reset)
SUB_BUTTON(restart)
SUB_BUTTON(query) SUB_BUTTON(query)
#endif SUB_BUTTON(restart)
#ifdef USE_NUMBER
SUB_NUMBER(max_still_distance_gate)
SUB_NUMBER(max_move_distance_gate)
SUB_NUMBER(timeout)
SUB_NUMBER(light_threshold)
#endif #endif
public: public:
@ -76,14 +79,14 @@ class LD2410Component : public Component, public uart::UARTDevice {
void loop() override; void loop() override;
void set_light_out_control(); void set_light_out_control();
#ifdef USE_NUMBER #ifdef USE_NUMBER
void set_gate_still_threshold_number(int gate, number::Number *n); void set_gate_still_threshold_number(uint8_t gate, number::Number *n);
void set_gate_move_threshold_number(int gate, number::Number *n); void set_gate_move_threshold_number(uint8_t gate, number::Number *n);
void set_max_distances_timeout(); void set_max_distances_timeout();
void set_gate_threshold(uint8_t gate); void set_gate_threshold(uint8_t gate);
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
void set_gate_move_sensor(int gate, sensor::Sensor *s); void set_gate_move_sensor(uint8_t gate, sensor::Sensor *s);
void set_gate_still_sensor(int gate, sensor::Sensor *s); void set_gate_still_sensor(uint8_t gate, sensor::Sensor *s);
#endif #endif
void set_throttle(uint16_t value) { this->throttle_ = value; }; void set_throttle(uint16_t value) { this->throttle_ = value; };
void set_bluetooth_password(const std::string &password); void set_bluetooth_password(const std::string &password);
@ -96,33 +99,35 @@ class LD2410Component : public Component, public uart::UARTDevice {
void factory_reset(); void factory_reset();
protected: protected:
void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len); void send_command_(uint8_t command_str, const uint8_t *command_value, uint8_t command_value_len);
void set_config_mode_(bool enable); void set_config_mode_(bool enable);
void handle_periodic_data_(uint8_t *buffer, int len); void handle_periodic_data_();
bool handle_ack_data_(uint8_t *buffer, int len); bool handle_ack_data_();
void readline_(int readch, uint8_t *buffer, int len); void readline_(int readch);
void query_parameters_(); void query_parameters_();
void get_version_(); void get_version_();
void get_mac_(); void get_mac_();
void get_distance_resolution_(); void get_distance_resolution_();
void get_light_control_(); void query_light_control_();
void restart_(); void restart_();
int32_t last_periodic_millis_ = 0; uint32_t last_periodic_millis_ = 0;
int32_t last_engineering_mode_change_millis_ = 0; uint16_t throttle_ = 0;
uint16_t throttle_; uint8_t light_function_ = 0;
float light_threshold_ = -1; uint8_t light_threshold_ = 0;
std::string version_; uint8_t out_pin_level_ = 0;
std::string mac_; uint8_t buffer_pos_ = 0; // where to resume processing/populating buffer
std::string out_pin_level_; uint8_t buffer_data_[MAX_LINE_LENGTH];
std::string light_function_; uint8_t mac_address_[6] = {0, 0, 0, 0, 0, 0};
uint8_t version_[6] = {0, 0, 0, 0, 0, 0};
bool bluetooth_on_{false};
#ifdef USE_NUMBER #ifdef USE_NUMBER
std::vector<number::Number *> gate_still_threshold_numbers_ = std::vector<number::Number *>(9); std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(TOTAL_GATES);
std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(9); std::vector<number::Number *> gate_still_threshold_numbers_ = std::vector<number::Number *>(TOTAL_GATES);
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
std::vector<sensor::Sensor *> gate_still_sensors_ = std::vector<sensor::Sensor *>(9); std::vector<sensor::Sensor *> gate_move_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES);
std::vector<sensor::Sensor *> gate_move_sensors_ = std::vector<sensor::Sensor *>(9); std::vector<sensor::Sensor *> gate_still_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES);
#endif #endif
}; };