11#define highbyte(val) (uint8_t)((val) >> 8)
12#define lowbyte(val) (uint8_t)((val) &0xff)
17static const char *
const TAG =
"ld2410";
19LD2410Component::LD2410Component() {}
21void LD2410Component::dump_config() {
22 ESP_LOGCONFIG(TAG,
"LD2410:");
23#ifdef USE_BINARY_SENSOR
24 LOG_BINARY_SENSOR(
" ",
"TargetBinarySensor", this->target_binary_sensor_);
25 LOG_BINARY_SENSOR(
" ",
"MovingTargetBinarySensor", this->moving_target_binary_sensor_);
26 LOG_BINARY_SENSOR(
" ",
"StillTargetBinarySensor", this->still_target_binary_sensor_);
27 LOG_BINARY_SENSOR(
" ",
"OutPinPresenceStatusBinarySensor", this->out_pin_presence_status_binary_sensor_);
30 LOG_SWITCH(
" ",
"EngineeringModeSwitch", this->engineering_mode_switch_);
31 LOG_SWITCH(
" ",
"BluetoothSwitch", this->bluetooth_switch_);
34 LOG_BUTTON(
" ",
"ResetButton", this->reset_button_);
35 LOG_BUTTON(
" ",
"RestartButton", this->restart_button_);
36 LOG_BUTTON(
" ",
"QueryButton", this->query_button_);
39 LOG_SENSOR(
" ",
"LightSensor", this->light_sensor_);
40 LOG_SENSOR(
" ",
"MovingTargetDistanceSensor", this->moving_target_distance_sensor_);
41 LOG_SENSOR(
" ",
"StillTargetDistanceSensor", this->still_target_distance_sensor_);
42 LOG_SENSOR(
" ",
"MovingTargetEnergySensor", this->moving_target_energy_sensor_);
43 LOG_SENSOR(
" ",
"StillTargetEnergySensor", this->still_target_energy_sensor_);
44 LOG_SENSOR(
" ",
"DetectionDistanceSensor", this->detection_distance_sensor_);
46 LOG_SENSOR(
" ",
"NthGateStillSesnsor", s);
49 LOG_SENSOR(
" ",
"NthGateMoveSesnsor", s);
53 LOG_TEXT_SENSOR(
" ",
"VersionTextSensor", this->version_text_sensor_);
54 LOG_TEXT_SENSOR(
" ",
"MacTextSensor", this->mac_text_sensor_);
57 LOG_SELECT(
" ",
"LightFunctionSelect", this->light_function_select_);
58 LOG_SELECT(
" ",
"OutPinLevelSelect", this->out_pin_level_select_);
59 LOG_SELECT(
" ",
"DistanceResolutionSelect", this->distance_resolution_select_);
60 LOG_SELECT(
" ",
"BaudRateSelect", this->baud_rate_select_);
63 LOG_NUMBER(
" ",
"LightThresholdNumber", this->light_threshold_number_);
64 LOG_NUMBER(
" ",
"MaxStillDistanceGateNumber", this->max_still_distance_gate_number_);
65 LOG_NUMBER(
" ",
"MaxMoveDistanceGateNumber", this->max_move_distance_gate_number_);
66 LOG_NUMBER(
" ",
"TimeoutNumber", this->timeout_number_);
68 LOG_NUMBER(
" ",
"Still Thresholds Number", n);
71 LOG_NUMBER(
" ",
"Move Thresholds Number", n);
74 this->read_all_info();
78 " Firmware Version : %s",
79 this->
throttle_,
const_cast<char *
>(this->
mac_.c_str()),
const_cast<char *
>(this->version_.c_str()));
82void LD2410Component::setup() {
83 ESP_LOGCONFIG(TAG,
"Running setup");
84 this->read_all_info();
87void LD2410Component::read_all_info() {
97 if (this->baud_rate_select_ !=
nullptr && this->baud_rate_select_->state != baud_rate) {
98 this->baud_rate_select_->publish_state(baud_rate);
103void LD2410Component::restart_and_read_all_info() {
106 this->
set_timeout(1000, [
this]() { this->read_all_info(); });
109void LD2410Component::loop() {
110 const int max_line_length = 80;
111 static uint8_t buffer[max_line_length];
119 ESP_LOGV(TAG,
"Sending COMMAND %02X", command);
124 if (command_value !=
nullptr)
125 len += command_value_len;
134 if (command_value !=
nullptr) {
135 for (
int i = 0; i < command_value_len; i++) {
148 if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1)
156 int32_t current_millis =
millis();
166 bool engineering_mode = buffer[
DATA_TYPES] == 0x01;
168 if (this->engineering_mode_switch_ !=
nullptr &&
170 this->engineering_mode_switch_->publish_state(engineering_mode);
173#ifdef USE_BINARY_SENSOR
182 if (this->target_binary_sensor_ !=
nullptr) {
183 this->target_binary_sensor_->publish_state(target_state != 0x00);
185 if (this->moving_target_binary_sensor_ !=
nullptr) {
186 this->moving_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 0));
188 if (this->still_target_binary_sensor_ !=
nullptr) {
189 this->still_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 1));
200 if (this->moving_target_distance_sensor_ !=
nullptr) {
202 if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
203 this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
205 if (this->moving_target_energy_sensor_ !=
nullptr) {
207 if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
208 this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
210 if (this->still_target_distance_sensor_ !=
nullptr) {
212 if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
213 this->still_target_distance_sensor_->publish_state(new_still_target_distance);
215 if (this->still_target_energy_sensor_ !=
nullptr) {
217 if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
218 this->still_target_energy_sensor_->publish_state(new_still_target_energy);
220 if (this->detection_distance_sensor_ !=
nullptr) {
222 if (this->detection_distance_sensor_->get_state() != new_detect_distance)
223 this->detection_distance_sensor_->publish_state(new_detect_distance);
225 if (engineering_mode) {
231 for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) {
240 for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) {
249 if (this->light_sensor_ !=
nullptr) {
251 if (this->light_sensor_->get_state() != new_light_sensor)
252 this->light_sensor_->publish_state(new_light_sensor);
255 for (
auto *s : this->gate_move_sensors_) {
256 if (s !=
nullptr && !std::isnan(s->get_state())) {
257 s->publish_state(NAN);
260 for (
auto *s : this->gate_still_sensors_) {
261 if (s !=
nullptr && !std::isnan(s->get_state())) {
262 s->publish_state(NAN);
265 if (this->light_sensor_ !=
nullptr && !std::isnan(this->light_sensor_->get_state())) {
266 this->light_sensor_->publish_state(NAN);
270#ifdef USE_BINARY_SENSOR
271 if (engineering_mode) {
272 if (this->out_pin_presence_status_binary_sensor_ !=
nullptr) {
273 this->out_pin_presence_status_binary_sensor_->publish_state(buffer[
OUT_PIN_SENSOR] == 0x01);
276 if (this->out_pin_presence_status_binary_sensor_ !=
nullptr) {
277 this->out_pin_presence_status_binary_sensor_->publish_state(
false);
286 std::string::size_type version_size = 256;
289 version.resize(version_size + 1);
290 version_size = std::snprintf(&version[0], version.size(),
VERSION_FMT, buffer[13], buffer[12], buffer[17],
291 buffer[16], buffer[15], buffer[14]);
292 }
while (version_size + 1 > version.size());
293 version.resize(version_size);
297const char MAC_FMT[] =
"%02X:%02X:%02X:%02X:%02X:%02X";
300const std::string
NO_MAC(
"08:05:04:03:02:01");
303 std::string::size_type mac_size = 256;
306 mac.resize(mac_size + 1);
307 mac_size = std::snprintf(&mac[0], mac.size(),
MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
309 }
while (mac_size + 1 > mac.size());
310 mac.resize(mac_size);
319 float normalized_value = value * 1.0;
320 if (n !=
nullptr && (!n->
has_state() || n->
state != normalized_value)) {
321 n->
state = normalized_value;
322 return [n, normalized_value]() { n->
publish_state(normalized_value); };
329 ESP_LOGV(TAG,
"Handling ACK DATA for COMMAND %02X", buffer[
COMMAND]);
331 ESP_LOGE(TAG,
"Error with last command : incorrect length");
334 if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {
335 ESP_LOGE(TAG,
"Error with last command : incorrect Header");
339 ESP_LOGE(TAG,
"Error with last command : status != 0x01");
343 ESP_LOGE(TAG,
"Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]);
348 case lowbyte(CMD_ENABLE_CONF):
349 ESP_LOGV(TAG,
"Handled Enable conf command");
351 case lowbyte(CMD_DISABLE_CONF):
352 ESP_LOGV(TAG,
"Handled Disabled conf command");
354 case lowbyte(CMD_SET_BAUD_RATE):
355 ESP_LOGV(TAG,
"Handled baud rate change command");
357 if (this->baud_rate_select_ !=
nullptr) {
358 ESP_LOGE(TAG,
"Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str());
362 case lowbyte(CMD_VERSION):
364 ESP_LOGV(TAG,
"FW Version is: %s",
const_cast<char *
>(this->
version_.c_str()));
365#ifdef USE_TEXT_SENSOR
366 if (this->version_text_sensor_ !=
nullptr) {
367 this->version_text_sensor_->publish_state(this->
version_);
371 case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): {
372 std::string distance_resolution =
373 DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->
two_byte_to_int_(buffer[10], buffer[11]));
374 ESP_LOGV(TAG,
"Distance resolution is: %s",
const_cast<char *
>(distance_resolution.c_str()));
376 if (this->distance_resolution_select_ !=
nullptr &&
377 this->distance_resolution_select_->state != distance_resolution) {
378 this->distance_resolution_select_->publish_state(distance_resolution);
382 case lowbyte(CMD_QUERY_LIGHT_CONTROL): {
386 ESP_LOGV(TAG,
"Light function is: %s",
const_cast<char *
>(this->
light_function_.c_str()));
388 ESP_LOGV(TAG,
"Out pin level is: %s",
const_cast<char *
>(this->
out_pin_level_.c_str()));
390 if (this->light_function_select_ !=
nullptr && this->light_function_select_->state != this->light_function_) {
393 if (this->out_pin_level_select_ !=
nullptr && this->out_pin_level_select_->state != this->out_pin_level_) {
398 if (this->light_threshold_number_ !=
nullptr &&
399 (!this->light_threshold_number_->has_state() ||
400 this->light_threshold_number_->state != this->light_threshold_)) {
405 case lowbyte(CMD_MAC):
410 ESP_LOGV(TAG,
"MAC Address is: %s",
const_cast<char *
>(this->
mac_.c_str()));
411#ifdef USE_TEXT_SENSOR
412 if (this->mac_text_sensor_ !=
nullptr) {
413 this->mac_text_sensor_->publish_state(this->
mac_);
417 if (this->bluetooth_switch_ !=
nullptr) {
422 case lowbyte(CMD_GATE_SENS):
423 ESP_LOGV(TAG,
"Handled sensitivity command");
425 case lowbyte(CMD_BLUETOOTH):
426 ESP_LOGV(TAG,
"Handled bluetooth command");
428 case lowbyte(CMD_SET_DISTANCE_RESOLUTION):
429 ESP_LOGV(TAG,
"Handled set distance resolution command");
431 case lowbyte(CMD_SET_LIGHT_CONTROL):
432 ESP_LOGV(TAG,
"Handled set light control command");
434 case lowbyte(CMD_BT_PASSWORD):
435 ESP_LOGV(TAG,
"Handled set bluetooth password command");
437 case lowbyte(CMD_QUERY):
439 if (buffer[10] != 0xAA)
446 std::vector<std::function<void(
void)>> updates;
447 updates.push_back(
set_number_value(this->max_move_distance_gate_number_, buffer[12]));
448 updates.push_back(
set_number_value(this->max_still_distance_gate_number_, buffer[13]));
452 for (std::vector<number::Number *>::size_type i = 0; i != this->gate_move_threshold_numbers_.size(); i++) {
453 updates.push_back(
set_number_value(this->gate_move_threshold_numbers_[i], buffer[14 + i]));
458 for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) {
459 updates.push_back(
set_number_value(this->gate_still_threshold_numbers_[i], buffer[23 + i]));
465 for (
auto &update : updates) {
482 buffer[pos++] = readch;
488 if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
489 ESP_LOGV(TAG,
"Will handle Periodic Data");
492 }
else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
493 buffer[pos - 1] == 0x01) {
494 ESP_LOGV(TAG,
"Will handle ACK Data");
498 ESP_LOGV(TAG,
"ACK Data incomplete");
506 uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
507 uint8_t cmd_value[2] = {0x01, 0x00};
511void LD2410Component::set_bluetooth(
bool enable) {
513 uint8_t enable_cmd_value[2] = {0x01, 0x00};
514 uint8_t disable_cmd_value[2] = {0x00, 0x00};
515 this->
send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2);
516 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
519void LD2410Component::set_distance_resolution(
const std::string &
state) {
521 uint8_t cmd_value[2] = {DISTANCE_RESOLUTION_ENUM_TO_INT.at(
state), 0x00};
522 this->
send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, 2);
523 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
526void LD2410Component::set_baud_rate(
const std::string &
state) {
528 uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(
state), 0x00};
533void LD2410Component::set_bluetooth_password(
const std::string &password) {
534 if (password.length() != 6) {
535 ESP_LOGE(TAG,
"set_bluetooth_password(): invalid password length, must be exactly 6 chars '%s'", password.c_str());
539 uint8_t cmd_value[6];
540 std::copy(password.begin(), password.end(), std::begin(cmd_value));
545void LD2410Component::set_engineering_mode(
bool enable) {
548 uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG;
553void LD2410Component::factory_reset() {
556 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
564 uint8_t cmd_value[2] = {0x01, 0x00};
572void LD2410Component::set_max_distances_timeout() {
573 if (!this->max_move_distance_gate_number_->has_state() || !this->max_still_distance_gate_number_->has_state() ||
574 !this->timeout_number_->has_state()) {
577 int max_moving_distance_gate_range =
static_cast<int>(this->max_move_distance_gate_number_->state);
578 int max_still_distance_gate_range =
static_cast<int>(this->max_still_distance_gate_number_->state);
579 int timeout =
static_cast<int>(this->timeout_number_->state);
580 uint8_t value[18] = {0x00,
582 lowbyte(max_moving_distance_gate_range),
583 highbyte(max_moving_distance_gate_range),
588 lowbyte(max_still_distance_gate_range),
589 highbyte(max_still_distance_gate_range),
602 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
606void LD2410Component::set_gate_threshold(uint8_t gate) {
607 number::Number *motionsens = this->gate_move_threshold_numbers_[gate];
608 number::Number *stillsens = this->gate_still_threshold_numbers_[gate];
610 if (!motionsens->has_state() || !stillsens->has_state()) {
613 int motion =
static_cast<int>(motionsens->state);
614 int still =
static_cast<int>(stillsens->state);
626 uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00,
627 0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00,
628 0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00};
635void LD2410Component::set_gate_still_threshold_number(
int gate, number::Number *n) {
636 this->gate_still_threshold_numbers_[gate] = n;
639void LD2410Component::set_gate_move_threshold_number(
int gate, number::Number *n) {
640 this->gate_move_threshold_numbers_[gate] = n;
644void LD2410Component::set_light_out_control() {
646 if (this->light_threshold_number_ !=
nullptr && this->light_threshold_number_->has_state()) {
651 if (this->light_function_select_ !=
nullptr && this->light_function_select_->has_state()) {
654 if (this->out_pin_level_select_ !=
nullptr && this->out_pin_level_select_->has_state()) {
658 if (this->
light_function_.empty() || this->out_pin_level_.empty() || this->light_threshold_ < 0) {
662 uint8_t light_function = LIGHT_FUNCTION_ENUM_TO_INT.at(this->
light_function_);
664 uint8_t out_pin_level = OUT_PIN_LEVEL_ENUM_TO_INT.at(this->
out_pin_level_);
665 uint8_t value[4] = {light_function, light_threshold, out_pin_level, 0x00};
669 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
674void LD2410Component::set_gate_move_sensor(
int gate, sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; }
675void LD2410Component::set_gate_still_sensor(
int gate, sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; }
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
int two_byte_to_int_(char firstbyte, char secondbyte)
void get_distance_resolution_()
void set_config_mode_(bool enable)
void handle_periodic_data_(uint8_t *buffer, int len)
std::vector< number::Number * > gate_still_threshold_numbers_
void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len)
std::vector< sensor::Sensor * > gate_move_sensors_
std::string out_pin_level_
int32_t last_periodic_millis_
void readline_(int readch, uint8_t *buffer, int len)
int32_t last_engineering_mode_change_millis_
bool handle_ack_data_(uint8_t *buffer, int len)
std::vector< sensor::Sensor * > gate_still_sensors_
void get_light_control_()
std::string light_function_
std::vector< number::Number * > gate_move_threshold_numbers_
Base-class for all numbers.
void publish_state(float state)
Base-class for all sensors.
void publish_state(float state)
Publish a new state to the front-end.
uint32_t get_baud_rate() const
void write_byte(uint8_t data)
void write_array(const uint8_t *data, size_t len)
std::string format_version(uint8_t *buffer)
std::function< void(void)> set_number_value(number::Number *n, float value)
const std::string NO_MAC("08:05:04:03:02:01")
const std::string UNKNOWN_MAC("unknown")
std::string format_mac(uint8_t *buffer)
Providing packet encoding functions for exchanging data with a remote host.
void IRAM_ATTR HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()