11 #define highbyte(val) (uint8_t)((val) >> 8) 12 #define lowbyte(val) (uint8_t)((val) &0xff) 17 static const char *
const TAG =
"ld2410";
19 LD2410Component::LD2410Component() {}
21 void 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);
52 #ifdef USE_TEXT_SENSOR 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();
75 ESP_LOGCONFIG(TAG,
" Throttle_ : %ums", this->
throttle_);
76 ESP_LOGCONFIG(TAG,
" MAC Address : %s", const_cast<char *>(this->
mac_.c_str()));
77 ESP_LOGCONFIG(TAG,
" Firmware Version : %s", const_cast<char *>(this->
version_.c_str()));
80 void LD2410Component::setup() {
81 ESP_LOGCONFIG(TAG,
"Setting up LD2410...");
82 this->read_all_info();
83 ESP_LOGCONFIG(TAG,
"Mac Address : %s", const_cast<char *>(this->
mac_.c_str()));
84 ESP_LOGCONFIG(TAG,
"Firmware Version : %s", const_cast<char *>(this->
version_.c_str()));
85 ESP_LOGCONFIG(TAG,
"LD2410 setup complete.");
88 void LD2410Component::read_all_info() {
98 if (this->baud_rate_select_ !=
nullptr && this->baud_rate_select_->state != baud_rate) {
99 this->baud_rate_select_->publish_state(baud_rate);
104 void LD2410Component::restart_and_read_all_info() {
107 this->
set_timeout(1000, [
this]() { this->read_all_info(); });
110 void LD2410Component::loop() {
111 const int max_line_length = 80;
112 static uint8_t buffer[max_line_length];
120 ESP_LOGV(TAG,
"Sending COMMAND %02X", command);
125 if (command_value !=
nullptr)
126 len += command_value_len;
135 if (command_value !=
nullptr) {
136 for (
int i = 0; i < command_value_len; i++) {
149 if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1)
151 if (buffer[7] !=
HEAD || buffer[len - 6] !=
END || buffer[len - 5] !=
CHECK)
157 int32_t current_millis =
millis();
158 if (current_millis - last_periodic_millis_ < this->
throttle_)
167 bool engineering_mode = buffer[
DATA_TYPES] == 0x01;
169 if (this->engineering_mode_switch_ !=
nullptr &&
171 this->engineering_mode_switch_->publish_state(engineering_mode);
174 #ifdef USE_BINARY_SENSOR 183 if (this->target_binary_sensor_ !=
nullptr) {
184 this->target_binary_sensor_->publish_state(target_state != 0x00);
186 if (this->moving_target_binary_sensor_ !=
nullptr) {
187 this->moving_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 0));
189 if (this->still_target_binary_sensor_ !=
nullptr) {
190 this->still_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 1));
201 if (this->moving_target_distance_sensor_ !=
nullptr) {
203 if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
204 this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
206 if (this->moving_target_energy_sensor_ !=
nullptr) {
208 if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
209 this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
211 if (this->still_target_distance_sensor_ !=
nullptr) {
213 if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
214 this->still_target_distance_sensor_->publish_state(new_still_target_distance);
216 if (this->still_target_energy_sensor_ !=
nullptr) {
218 if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
219 this->still_target_energy_sensor_->publish_state(new_still_target_energy);
221 if (this->detection_distance_sensor_ !=
nullptr) {
223 if (this->detection_distance_sensor_->get_state() != new_detect_distance)
224 this->detection_distance_sensor_->publish_state(new_detect_distance);
226 if (engineering_mode) {
232 for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) {
241 for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) {
250 if (this->light_sensor_ !=
nullptr) {
252 if (this->light_sensor_->get_state() != new_light_sensor)
253 this->light_sensor_->publish_state(new_light_sensor);
256 for (
auto *s : this->gate_move_sensors_) {
257 if (s !=
nullptr && !std::isnan(s->get_state())) {
258 s->publish_state(NAN);
261 for (
auto *s : this->gate_still_sensors_) {
262 if (s !=
nullptr && !std::isnan(s->get_state())) {
263 s->publish_state(NAN);
266 if (this->light_sensor_ !=
nullptr && !std::isnan(this->light_sensor_->get_state())) {
267 this->light_sensor_->publish_state(NAN);
271 #ifdef USE_BINARY_SENSOR 272 if (engineering_mode) {
273 if (this->out_pin_presence_status_binary_sensor_ !=
nullptr) {
274 this->out_pin_presence_status_binary_sensor_->publish_state(buffer[
OUT_PIN_SENSOR] == 0x01);
277 if (this->out_pin_presence_status_binary_sensor_ !=
nullptr) {
278 this->out_pin_presence_status_binary_sensor_->publish_state(
false);
287 std::string::size_type version_size = 256;
290 version.resize(version_size + 1);
291 version_size = std::snprintf(&version[0], version.size(),
VERSION_FMT, buffer[13], buffer[12], buffer[17],
292 buffer[16], buffer[15], buffer[14]);
293 }
while (version_size + 1 > version.size());
294 version.resize(version_size);
298 const char MAC_FMT[] =
"%02X:%02X:%02X:%02X:%02X:%02X";
301 const std::string
NO_MAC(
"08:05:04:03:02:01");
304 std::string::size_type mac_size = 256;
307 mac.resize(mac_size + 1);
308 mac_size = std::snprintf(&mac[0], mac.size(),
MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
310 }
while (mac_size + 1 > mac.size());
311 mac.resize(mac_size);
320 float normalized_value = value * 1.0;
321 if (n !=
nullptr && (!n->
has_state() || n->
state != normalized_value)) {
322 n->
state = normalized_value;
323 return [n, normalized_value]() { n->
publish_state(normalized_value); };
330 ESP_LOGV(TAG,
"Handling ACK DATA for COMMAND %02X", buffer[
COMMAND]);
332 ESP_LOGE(TAG,
"Error with last command : incorrect length");
335 if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {
336 ESP_LOGE(TAG,
"Error with last command : incorrect Header");
340 ESP_LOGE(TAG,
"Error with last command : status != 0x01");
344 ESP_LOGE(TAG,
"Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]);
348 switch (buffer[COMMAND]) {
349 case lowbyte(CMD_ENABLE_CONF):
350 ESP_LOGV(TAG,
"Handled Enable conf command");
352 case lowbyte(CMD_DISABLE_CONF):
353 ESP_LOGV(TAG,
"Handled Disabled conf command");
355 case lowbyte(CMD_SET_BAUD_RATE):
356 ESP_LOGV(TAG,
"Handled baud rate change command");
358 if (this->baud_rate_select_ !=
nullptr) {
359 ESP_LOGE(TAG,
"Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str());
363 case lowbyte(CMD_VERSION):
365 ESP_LOGV(TAG,
"FW Version is: %s", const_cast<char *>(this->
version_.c_str()));
366 #ifdef USE_TEXT_SENSOR 367 if (this->version_text_sensor_ !=
nullptr) {
368 this->version_text_sensor_->publish_state(this->
version_);
372 case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): {
373 std::string distance_resolution =
374 DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->
two_byte_to_int_(buffer[10], buffer[11]));
375 ESP_LOGV(TAG,
"Distance resolution is: %s", const_cast<char *>(distance_resolution.c_str()));
377 if (this->distance_resolution_select_ !=
nullptr &&
378 this->distance_resolution_select_->state != distance_resolution) {
379 this->distance_resolution_select_->publish_state(distance_resolution);
383 case lowbyte(CMD_QUERY_LIGHT_CONTROL): {
387 ESP_LOGV(TAG,
"Light function is: %s", const_cast<char *>(this->
light_function_.c_str()));
389 ESP_LOGV(TAG,
"Out pin level is: %s", const_cast<char *>(this->
out_pin_level_.c_str()));
391 if (this->light_function_select_ !=
nullptr && this->light_function_select_->state != this->light_function_) {
394 if (this->out_pin_level_select_ !=
nullptr && this->out_pin_level_select_->state != this->out_pin_level_) {
399 if (this->light_threshold_number_ !=
nullptr &&
400 (!this->light_threshold_number_->has_state() ||
406 case lowbyte(CMD_MAC):
411 ESP_LOGV(TAG,
"MAC Address is: %s", const_cast<char *>(this->
mac_.c_str()));
412 #ifdef USE_TEXT_SENSOR 413 if (this->mac_text_sensor_ !=
nullptr) {
414 this->mac_text_sensor_->publish_state(this->
mac_);
418 if (this->bluetooth_switch_ !=
nullptr) {
423 case lowbyte(CMD_GATE_SENS):
424 ESP_LOGV(TAG,
"Handled sensitivity command");
426 case lowbyte(CMD_BLUETOOTH):
427 ESP_LOGV(TAG,
"Handled bluetooth command");
429 case lowbyte(CMD_SET_DISTANCE_RESOLUTION):
430 ESP_LOGV(TAG,
"Handled set distance resolution command");
432 case lowbyte(CMD_SET_LIGHT_CONTROL):
433 ESP_LOGV(TAG,
"Handled set light control command");
435 case lowbyte(CMD_BT_PASSWORD):
436 ESP_LOGV(TAG,
"Handled set bluetooth password command");
438 case lowbyte(CMD_QUERY):
440 if (buffer[10] != 0xAA)
447 std::vector<std::function<void(void)>> updates;
448 updates.push_back(
set_number_value(this->max_move_distance_gate_number_, buffer[12]));
449 updates.push_back(
set_number_value(this->max_still_distance_gate_number_, buffer[13]));
453 for (std::vector<number::Number *>::size_type i = 0; i != this->gate_move_threshold_numbers_.size(); i++) {
454 updates.push_back(
set_number_value(this->gate_move_threshold_numbers_[i], buffer[14 + i]));
459 for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) {
460 updates.push_back(
set_number_value(this->gate_still_threshold_numbers_[i], buffer[23 + i]));
466 for (
auto &update : updates) {
483 buffer[pos++] = readch;
489 if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
490 ESP_LOGV(TAG,
"Will handle Periodic Data");
493 }
else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
494 buffer[pos - 1] == 0x01) {
495 ESP_LOGV(TAG,
"Will handle ACK Data");
499 ESP_LOGV(TAG,
"ACK Data incomplete");
507 uint8_t
cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
508 uint8_t cmd_value[2] = {0x01, 0x00};
512 void LD2410Component::set_bluetooth(
bool enable) {
514 uint8_t enable_cmd_value[2] = {0x01, 0x00};
515 uint8_t disable_cmd_value[2] = {0x00, 0x00};
516 this->
send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2);
517 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
520 void LD2410Component::set_distance_resolution(
const std::string &
state) {
522 uint8_t cmd_value[2] = {DISTANCE_RESOLUTION_ENUM_TO_INT.at(state), 0x00};
523 this->
send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, 2);
524 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
527 void LD2410Component::set_baud_rate(
const std::string &state) {
529 uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(state), 0x00};
534 void LD2410Component::set_bluetooth_password(
const std::string &password) {
535 if (password.length() != 6) {
536 ESP_LOGE(TAG,
"set_bluetooth_password(): invalid password length, must be exactly 6 chars '%s'", password.c_str());
540 uint8_t cmd_value[6];
541 std::copy(password.begin(), password.end(), std::begin(cmd_value));
546 void LD2410Component::set_engineering_mode(
bool enable) {
549 uint8_t
cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG;
554 void LD2410Component::factory_reset() {
557 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
565 uint8_t cmd_value[2] = {0x01, 0x00};
573 void LD2410Component::set_max_distances_timeout() {
574 if (!this->max_move_distance_gate_number_->has_state() || !this->max_still_distance_gate_number_->has_state() ||
575 !this->timeout_number_->has_state()) {
578 int max_moving_distance_gate_range =
static_cast<int>(this->max_move_distance_gate_number_->state);
579 int max_still_distance_gate_range =
static_cast<int>(this->max_still_distance_gate_number_->state);
580 int timeout =
static_cast<int>(this->timeout_number_->state);
581 uint8_t value[18] = {0x00,
583 lowbyte(max_moving_distance_gate_range),
584 highbyte(max_moving_distance_gate_range),
589 lowbyte(max_still_distance_gate_range),
590 highbyte(max_still_distance_gate_range),
603 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
607 void LD2410Component::set_gate_threshold(uint8_t gate) {
608 number::Number *motionsens = this->gate_move_threshold_numbers_[gate];
609 number::Number *stillsens = this->gate_still_threshold_numbers_[gate];
614 int motion =
static_cast<int>(motionsens->
state);
615 int still =
static_cast<int>(stillsens->
state);
627 uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00,
628 0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00,
629 0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00};
636 void LD2410Component::set_gate_still_threshold_number(
int gate,
number::Number *n) {
637 this->gate_still_threshold_numbers_[gate] = n;
640 void LD2410Component::set_gate_move_threshold_number(
int gate,
number::Number *n) {
641 this->gate_move_threshold_numbers_[gate] = n;
645 void LD2410Component::set_light_out_control() {
647 if (this->light_threshold_number_ !=
nullptr && this->light_threshold_number_->has_state()) {
652 if (this->light_function_select_ !=
nullptr && this->light_function_select_->has_state()) {
655 if (this->out_pin_level_select_ !=
nullptr && this->out_pin_level_select_->has_state()) {
663 uint8_t light_function = LIGHT_FUNCTION_ENUM_TO_INT.at(this->
light_function_);
665 uint8_t out_pin_level = OUT_PIN_LEVEL_ENUM_TO_INT.at(this->
out_pin_level_);
666 uint8_t value[4] = {light_function, light_threshold, out_pin_level, 0x00};
670 this->
set_timeout(200, [
this]() { this->restart_and_read_all_info(); });
675 void LD2410Component::set_gate_move_sensor(
int gate,
sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; }
676 void LD2410Component::set_gate_still_sensor(
int gate,
sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; }
bool has_state() const
Return whether this number has gotten a full state yet.
void set_config_mode_(bool enable)
void readline_(int readch, uint8_t *buffer, int len)
uint32_t get_baud_rate() const
void write_array(const uint8_t *data, size_t len)
const char * to_string(SHTCXType type)
void write_byte(uint8_t data)
std::string light_function_
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void get_distance_resolution_()
std::vector< number::Number * > gate_move_threshold_numbers_
void publish_state(float state)
std::string format_mac(uint8_t *buffer)
std::function< void(void)> set_number_value(number::Number *n, float value)
uint32_t IRAM_ATTR HOT millis()
void get_light_control_()
int32_t last_engineering_mode_change_millis_
Base-class for all numbers.
std::vector< number::Number * > gate_still_threshold_numbers_
void publish_state(float state)
Publish a new state to the front-end.
bool handle_ack_data_(uint8_t *buffer, int len)
std::vector< sensor::Sensor * > gate_move_sensors_
const std::string NO_MAC("08:05:04:03:02:01")
Implementation of SPI Controller mode.
std::vector< sensor::Sensor * > gate_still_sensors_
void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len)
int two_byte_to_int_(char firstbyte, char secondbyte)
void handle_periodic_data_(uint8_t *buffer, int len)
Base-class for all sensors.
std::string out_pin_level_
const std::string UNKNOWN_MAC("unknown")
std::string format_version(uint8_t *buffer)
void IRAM_ATTR HOT delay(uint32_t ms)
int32_t last_periodic_millis_