mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[ld2450] Clean-up for consistency, reduce CPU usage when idle (#9363)
This commit is contained in:
parent
4e25b6da7b
commit
db877e688a
@ -13,13 +13,13 @@ from esphome.const import (
|
|||||||
|
|
||||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||||
|
|
||||||
ResetButton = ld2450_ns.class_("ResetButton", button.Button)
|
FactoryResetButton = ld2450_ns.class_("FactoryResetButton", button.Button)
|
||||||
RestartButton = ld2450_ns.class_("RestartButton", button.Button)
|
RestartButton = ld2450_ns.class_("RestartButton", button.Button)
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
CONFIG_SCHEMA = {
|
||||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||||
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,
|
||||||
@ -38,7 +38,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_LD2450_ID])
|
await cg.register_parented(b, config[CONF_LD2450_ID])
|
||||||
cg.add(ld2450_component.set_reset_button(b))
|
cg.add(ld2450_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_LD2450_ID])
|
await cg.register_parented(b, config[CONF_LD2450_ID])
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
#include "factory_reset_button.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ld2450 {
|
||||||
|
|
||||||
|
void FactoryResetButton::press_action() { this->parent_->factory_reset(); }
|
||||||
|
|
||||||
|
} // namespace ld2450
|
||||||
|
} // namespace esphome
|
@ -6,9 +6,9 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ld2450 {
|
namespace ld2450 {
|
||||||
|
|
||||||
class ResetButton : public button::Button, public Parented<LD2450Component> {
|
class FactoryResetButton : public button::Button, public Parented<LD2450Component> {
|
||||||
public:
|
public:
|
||||||
ResetButton() = default;
|
FactoryResetButton() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void press_action() override;
|
void press_action() override;
|
@ -1,9 +0,0 @@
|
|||||||
#include "reset_button.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace ld2450 {
|
|
||||||
|
|
||||||
void ResetButton::press_action() { this->parent_->factory_reset(); }
|
|
||||||
|
|
||||||
} // namespace ld2450
|
|
||||||
} // namespace esphome
|
|
@ -18,11 +18,10 @@ namespace esphome {
|
|||||||
namespace ld2450 {
|
namespace ld2450 {
|
||||||
|
|
||||||
static const char *const TAG = "ld2450";
|
static const char *const TAG = "ld2450";
|
||||||
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,14 +32,13 @@ enum BaudRateStructure : uint8_t {
|
|||||||
BAUD_RATE_460800 = 8
|
BAUD_RATE_460800 = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
// Zone type struct
|
enum ZoneType : uint8_t {
|
||||||
enum ZoneTypeStructure : uint8_t {
|
|
||||||
ZONE_DISABLED = 0,
|
ZONE_DISABLED = 0,
|
||||||
ZONE_DETECTION = 1,
|
ZONE_DETECTION = 1,
|
||||||
ZONE_FILTER = 2,
|
ZONE_FILTER = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum PeriodicDataStructure : uint8_t {
|
enum PeriodicData : uint8_t {
|
||||||
TARGET_X = 4,
|
TARGET_X = 4,
|
||||||
TARGET_Y = 6,
|
TARGET_Y = 6,
|
||||||
TARGET_SPEED = 8,
|
TARGET_SPEED = 8,
|
||||||
@ -48,12 +46,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,
|
||||||
};
|
};
|
||||||
@ -61,11 +59,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,6 +73,13 @@ constexpr StringToUint8 BAUD_RATES_BY_STR[] = {
|
|||||||
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800},
|
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr Uint8ToString DIRECTION_BY_UINT[] = {
|
||||||
|
{DIRECTION_APPROACHING, "Approaching"},
|
||||||
|
{DIRECTION_MOVING_AWAY, "Moving away"},
|
||||||
|
{DIRECTION_STATIONARY, "Stationary"},
|
||||||
|
{DIRECTION_NA, "NA"},
|
||||||
|
};
|
||||||
|
|
||||||
constexpr Uint8ToString ZONE_TYPE_BY_UINT[] = {
|
constexpr Uint8ToString ZONE_TYPE_BY_UINT[] = {
|
||||||
{ZONE_DISABLED, "Disabled"},
|
{ZONE_DISABLED, "Disabled"},
|
||||||
{ZONE_DETECTION, "Detection"},
|
{ZONE_DETECTION, "Detection"},
|
||||||
@ -104,28 +109,35 @@ template<size_t N> const char *find_str(const Uint8ToString (&arr)[N], uint8_t v
|
|||||||
return ""; // Not found
|
return ""; // Not found
|
||||||
}
|
}
|
||||||
|
|
||||||
// LD2450 serial command header & footer
|
|
||||||
static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
|
|
||||||
static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01};
|
|
||||||
// LD2450 UART Serial Commands
|
// LD2450 UART Serial 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_VERSION = 0xA0;
|
static constexpr uint8_t CMD_QUERY_VERSION = 0xA0;
|
||||||
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;
|
||||||
static const uint8_t CMD_SINGLE_TARGET_MODE = 0x80;
|
static constexpr uint8_t CMD_SINGLE_TARGET_MODE = 0x80;
|
||||||
static const uint8_t CMD_MULTI_TARGET_MODE = 0x90;
|
static constexpr uint8_t CMD_MULTI_TARGET_MODE = 0x90;
|
||||||
static const uint8_t CMD_QUERY_TARGET_MODE = 0x91;
|
static constexpr uint8_t CMD_QUERY_TARGET_MODE = 0x91;
|
||||||
static const uint8_t CMD_SET_BAUD_RATE = 0xA1;
|
static constexpr uint8_t CMD_SET_BAUD_RATE = 0xA1;
|
||||||
static const uint8_t CMD_QUERY_ZONE = 0xC1;
|
static constexpr uint8_t CMD_QUERY_ZONE = 0xC1;
|
||||||
static const uint8_t CMD_SET_ZONE = 0xC2;
|
static constexpr uint8_t CMD_SET_ZONE = 0xC2;
|
||||||
|
// Header & Footer size
|
||||||
|
static constexpr uint8_t HEADER_FOOTER_SIZE = 4;
|
||||||
|
// Command Header & Footer
|
||||||
|
static constexpr uint8_t CMD_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xFD, 0xFC, 0xFB, 0xFA};
|
||||||
|
static constexpr uint8_t CMD_FRAME_FOOTER[HEADER_FOOTER_SIZE] = {0x04, 0x03, 0x02, 0x01};
|
||||||
|
// Data Header & Footer
|
||||||
|
static constexpr uint8_t DATA_FRAME_HEADER[HEADER_FOOTER_SIZE] = {0xAA, 0xFF, 0x03, 0x00};
|
||||||
|
static constexpr uint8_t DATA_FRAME_FOOTER[2] = {0x55, 0xCC};
|
||||||
|
// MAC address the module uses when Bluetooth is disabled
|
||||||
|
static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01};
|
||||||
|
|
||||||
static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
|
static inline uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
|
||||||
|
|
||||||
static inline void convert_int_values_to_hex(const int *values, uint8_t *bytes) {
|
static inline void convert_int_values_to_hex(const int *values, uint8_t *bytes) {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
uint16_t val = values[i] & 0xFFFF;
|
uint16_t val = values[i] & 0xFFFF;
|
||||||
bytes[i * 2] = val & 0xFF; // Store low byte first (little-endian)
|
bytes[i * 2] = val & 0xFF; // Store low byte first (little-endian)
|
||||||
bytes[i * 2 + 1] = (val >> 8) & 0xFF; // Store high byte second
|
bytes[i * 2 + 1] = (val >> 8) & 0xFF; // Store high byte second
|
||||||
@ -166,18 +178,13 @@ static inline float calculate_angle(float base, float hypotenuse) {
|
|||||||
return angle_degrees;
|
return angle_degrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string get_direction(int16_t speed) {
|
static bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
|
||||||
static const char *const APPROACHING = "Approaching";
|
for (uint8_t i = 0; i < HEADER_FOOTER_SIZE; i++) {
|
||||||
static const char *const MOVING_AWAY = "Moving away";
|
if (header_footer[i] != buffer[i]) {
|
||||||
static const char *const STATIONARY = "Stationary";
|
return false; // Mismatch in header/footer
|
||||||
|
}
|
||||||
if (speed > 0) {
|
|
||||||
return MOVING_AWAY;
|
|
||||||
}
|
}
|
||||||
if (speed < 0) {
|
return true; // Valid header/footer
|
||||||
return APPROACHING;
|
|
||||||
}
|
|
||||||
return STATIONARY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LD2450Component::setup() {
|
void LD2450Component::setup() {
|
||||||
@ -192,84 +199,93 @@ void LD2450Component::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LD2450Component::dump_config() {
|
void LD2450Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "LD2450:");
|
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,
|
||||||
|
"LD2450:\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(" ", "MovingTarget", this->moving_target_binary_sensor_);
|
||||||
LOG_BINARY_SENSOR(" ", "StillTargetBinarySensor", this->still_target_binary_sensor_);
|
LOG_BINARY_SENSOR(" ", "StillTarget", this->still_target_binary_sensor_);
|
||||||
#endif
|
LOG_BINARY_SENSOR(" ", "Target", this->target_binary_sensor_);
|
||||||
#ifdef USE_SWITCH
|
|
||||||
LOG_SWITCH(" ", "BluetoothSwitch", this->bluetooth_switch_);
|
|
||||||
LOG_SWITCH(" ", "MultiTargetSwitch", this->multi_target_switch_);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BUTTON
|
|
||||||
LOG_BUTTON(" ", "ResetButton", this->reset_button_);
|
|
||||||
LOG_BUTTON(" ", "RestartButton", this->restart_button_);
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
LOG_SENSOR(" ", "TargetCountSensor", this->target_count_sensor_);
|
ESP_LOGCONFIG(TAG, "Sensors:");
|
||||||
LOG_SENSOR(" ", "StillTargetCountSensor", this->still_target_count_sensor_);
|
LOG_SENSOR(" ", "MovingTargetCount", this->moving_target_count_sensor_);
|
||||||
LOG_SENSOR(" ", "MovingTargetCountSensor", this->moving_target_count_sensor_);
|
LOG_SENSOR(" ", "StillTargetCount", this->still_target_count_sensor_);
|
||||||
|
LOG_SENSOR(" ", "TargetCount", this->target_count_sensor_);
|
||||||
for (sensor::Sensor *s : this->move_x_sensors_) {
|
for (sensor::Sensor *s : this->move_x_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthTargetXSensor", s);
|
LOG_SENSOR(" ", "TargetX", s);
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->move_y_sensors_) {
|
for (sensor::Sensor *s : this->move_y_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthTargetYSensor", s);
|
LOG_SENSOR(" ", "TargetY", s);
|
||||||
}
|
|
||||||
for (sensor::Sensor *s : this->move_speed_sensors_) {
|
|
||||||
LOG_SENSOR(" ", "NthTargetSpeedSensor", s);
|
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->move_angle_sensors_) {
|
for (sensor::Sensor *s : this->move_angle_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthTargetAngleSensor", s);
|
LOG_SENSOR(" ", "TargetAngle", s);
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->move_distance_sensors_) {
|
for (sensor::Sensor *s : this->move_distance_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthTargetDistanceSensor", s);
|
LOG_SENSOR(" ", "TargetDistance", s);
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->move_resolution_sensors_) {
|
for (sensor::Sensor *s : this->move_resolution_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthTargetResolutionSensor", s);
|
LOG_SENSOR(" ", "TargetResolution", s);
|
||||||
|
}
|
||||||
|
for (sensor::Sensor *s : this->move_speed_sensors_) {
|
||||||
|
LOG_SENSOR(" ", "TargetSpeed", s);
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->zone_target_count_sensors_) {
|
for (sensor::Sensor *s : this->zone_target_count_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthZoneTargetCountSensor", s);
|
LOG_SENSOR(" ", "ZoneTargetCount", s);
|
||||||
}
|
|
||||||
for (sensor::Sensor *s : this->zone_still_target_count_sensors_) {
|
|
||||||
LOG_SENSOR(" ", "NthZoneStillTargetCountSensor", s);
|
|
||||||
}
|
}
|
||||||
for (sensor::Sensor *s : this->zone_moving_target_count_sensors_) {
|
for (sensor::Sensor *s : this->zone_moving_target_count_sensors_) {
|
||||||
LOG_SENSOR(" ", "NthZoneMovingTargetCountSensor", s);
|
LOG_SENSOR(" ", "ZoneMovingTargetCount", s);
|
||||||
|
}
|
||||||
|
for (sensor::Sensor *s : this->zone_still_target_count_sensors_) {
|
||||||
|
LOG_SENSOR(" ", "ZoneStillTargetCount", 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(" ", "Version", this->version_text_sensor_);
|
||||||
|
LOG_TEXT_SENSOR(" ", "Mac", this->mac_text_sensor_);
|
||||||
for (text_sensor::TextSensor *s : this->direction_text_sensors_) {
|
for (text_sensor::TextSensor *s : this->direction_text_sensors_) {
|
||||||
LOG_TEXT_SENSOR(" ", "NthDirectionTextSensor", s);
|
LOG_TEXT_SENSOR(" ", "Direction", s);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
|
ESP_LOGCONFIG(TAG, "Numbers:");
|
||||||
|
LOG_NUMBER(" ", "PresenceTimeout", this->presence_timeout_number_);
|
||||||
for (auto n : this->zone_numbers_) {
|
for (auto n : this->zone_numbers_) {
|
||||||
LOG_NUMBER(" ", "ZoneX1Number", n.x1);
|
LOG_NUMBER(" ", "ZoneX1", n.x1);
|
||||||
LOG_NUMBER(" ", "ZoneY1Number", n.y1);
|
LOG_NUMBER(" ", "ZoneY1", n.y1);
|
||||||
LOG_NUMBER(" ", "ZoneX2Number", n.x2);
|
LOG_NUMBER(" ", "ZoneX2", n.x2);
|
||||||
LOG_NUMBER(" ", "ZoneY2Number", n.y2);
|
LOG_NUMBER(" ", "ZoneY2", n.y2);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
LOG_SELECT(" ", "BaudRateSelect", this->baud_rate_select_);
|
ESP_LOGCONFIG(TAG, "Selects:");
|
||||||
LOG_SELECT(" ", "ZoneTypeSelect", this->zone_type_select_);
|
LOG_SELECT(" ", "BaudRate", this->baud_rate_select_);
|
||||||
|
LOG_SELECT(" ", "ZoneType", this->zone_type_select_);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_SWITCH
|
||||||
LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_);
|
ESP_LOGCONFIG(TAG, "Switches:");
|
||||||
|
LOG_SWITCH(" ", "Bluetooth", this->bluetooth_switch_);
|
||||||
|
LOG_SWITCH(" ", "MultiTarget", this->multi_target_switch_);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BUTTON
|
||||||
|
ESP_LOGCONFIG(TAG, "Buttons:");
|
||||||
|
LOG_BUTTON(" ", "FactoryReset", this->factory_reset_button_);
|
||||||
|
LOG_BUTTON(" ", "Restart", this->restart_button_);
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGCONFIG(TAG,
|
|
||||||
" Throttle: %ums\n"
|
|
||||||
" MAC Address: %s\n"
|
|
||||||
" Firmware version: %s",
|
|
||||||
this->throttle_, this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_.c_str(), this->version_.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LD2450Component::loop() {
|
void LD2450Component::loop() {
|
||||||
while (this->available()) {
|
while (this->available()) {
|
||||||
this->readline_(read(), this->buffer_data_, MAX_LINE_LENGTH);
|
this->readline_(this->read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +320,7 @@ void LD2450Component::set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_
|
|||||||
this->zone_type_ = zone_type;
|
this->zone_type_ = zone_type;
|
||||||
int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1,
|
int zone_parameters[12] = {zone1_x1, zone1_y1, zone1_x2, zone1_y2, zone2_x1, zone2_y1,
|
||||||
zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2};
|
zone2_x2, zone2_y2, zone3_x1, zone3_y1, zone3_x2, zone3_y2};
|
||||||
for (int i = 0; i < MAX_ZONES; i++) {
|
for (uint8_t i = 0; i < MAX_ZONES; i++) {
|
||||||
this->zone_config_[i].x1 = zone_parameters[i * 4];
|
this->zone_config_[i].x1 = zone_parameters[i * 4];
|
||||||
this->zone_config_[i].y1 = zone_parameters[i * 4 + 1];
|
this->zone_config_[i].y1 = zone_parameters[i * 4 + 1];
|
||||||
this->zone_config_[i].x2 = zone_parameters[i * 4 + 2];
|
this->zone_config_[i].x2 = zone_parameters[i * 4 + 2];
|
||||||
@ -318,15 +334,15 @@ void LD2450Component::send_set_zone_command_() {
|
|||||||
uint8_t cmd_value[26] = {};
|
uint8_t cmd_value[26] = {};
|
||||||
uint8_t zone_type_bytes[2] = {static_cast<uint8_t>(this->zone_type_), 0x00};
|
uint8_t zone_type_bytes[2] = {static_cast<uint8_t>(this->zone_type_), 0x00};
|
||||||
uint8_t area_config[24] = {};
|
uint8_t area_config[24] = {};
|
||||||
for (int i = 0; i < MAX_ZONES; i++) {
|
for (uint8_t i = 0; i < MAX_ZONES; i++) {
|
||||||
int values[4] = {this->zone_config_[i].x1, this->zone_config_[i].y1, this->zone_config_[i].x2,
|
int values[4] = {this->zone_config_[i].x1, this->zone_config_[i].y1, this->zone_config_[i].x2,
|
||||||
this->zone_config_[i].y2};
|
this->zone_config_[i].y2};
|
||||||
ld2450::convert_int_values_to_hex(values, area_config + (i * 8));
|
ld2450::convert_int_values_to_hex(values, area_config + (i * 8));
|
||||||
}
|
}
|
||||||
std::memcpy(cmd_value, zone_type_bytes, 2);
|
std::memcpy(cmd_value, zone_type_bytes, sizeof(zone_type_bytes));
|
||||||
std::memcpy(cmd_value + 2, area_config, 24);
|
std::memcpy(cmd_value + 2, area_config, sizeof(area_config));
|
||||||
this->set_config_mode_(true);
|
this->set_config_mode_(true);
|
||||||
this->send_command_(CMD_SET_ZONE, cmd_value, 26);
|
this->send_command_(CMD_SET_ZONE, cmd_value, sizeof(cmd_value));
|
||||||
this->set_config_mode_(false);
|
this->set_config_mode_(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,14 +358,14 @@ bool LD2450Component::get_timeout_status_(uint32_t check_millis) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract, store and publish zone details LD2450 buffer
|
// Extract, store and publish zone details LD2450 buffer
|
||||||
void LD2450Component::process_zone_(uint8_t *buffer) {
|
void LD2450Component::process_zone_() {
|
||||||
uint8_t index, start;
|
uint8_t index, start;
|
||||||
for (index = 0; index < MAX_ZONES; index++) {
|
for (index = 0; index < MAX_ZONES; index++) {
|
||||||
start = 12 + index * 8;
|
start = 12 + index * 8;
|
||||||
this->zone_config_[index].x1 = ld2450::hex_to_signed_int(buffer, start);
|
this->zone_config_[index].x1 = ld2450::hex_to_signed_int(this->buffer_data_, start);
|
||||||
this->zone_config_[index].y1 = ld2450::hex_to_signed_int(buffer, start + 2);
|
this->zone_config_[index].y1 = ld2450::hex_to_signed_int(this->buffer_data_, start + 2);
|
||||||
this->zone_config_[index].x2 = ld2450::hex_to_signed_int(buffer, start + 4);
|
this->zone_config_[index].x2 = ld2450::hex_to_signed_int(this->buffer_data_, start + 4);
|
||||||
this->zone_config_[index].y2 = ld2450::hex_to_signed_int(buffer, start + 6);
|
this->zone_config_[index].y2 = ld2450::hex_to_signed_int(this->buffer_data_, start + 6);
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
// only one null check as all coordinates are required for a single zone
|
// only one null check as all coordinates are required for a single zone
|
||||||
if (this->zone_numbers_[index].x1 != nullptr) {
|
if (this->zone_numbers_[index].x1 != nullptr) {
|
||||||
@ -395,27 +411,25 @@ void LD2450Component::restart_and_read_all_info() {
|
|||||||
|
|
||||||
// Send command with values to LD2450
|
// Send command with values to LD2450
|
||||||
void LD2450Component::send_command_(uint8_t command, const uint8_t *command_value, uint8_t command_value_len) {
|
void LD2450Component::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 header
|
// 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));
|
uint8_t len_cmd[] = {lowbyte(len), highbyte(len), command, 0x00};
|
||||||
this->write_byte(highbyte(len));
|
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// footer
|
// 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
|
||||||
}
|
}
|
||||||
@ -423,26 +437,23 @@ void LD2450Component::send_command_(uint8_t command, const uint8_t *command_valu
|
|||||||
// LD2450 Radar data message:
|
// LD2450 Radar data message:
|
||||||
// [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC]
|
// [AA FF 03 00] [0E 03 B1 86 10 00 40 01] [00 00 00 00 00 00 00 00] [00 00 00 00 00 00 00 00] [55 CC]
|
||||||
// Header Target 1 Target 2 Target 3 End
|
// Header Target 1 Target 2 Target 3 End
|
||||||
void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
void LD2450Component::handle_periodic_data_() {
|
||||||
// Early throttle check - moved before any processing to save CPU cycles
|
// Early throttle check - moved before any processing to save CPU cycles
|
||||||
if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
|
if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
|
||||||
ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
|
if (this->buffer_pos_ < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
|
||||||
ESP_LOGE(TAG, "Invalid message length");
|
ESP_LOGE(TAG, "Invalid length");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) { // header
|
if (!ld2450::validate_header_footer(DATA_FRAME_HEADER, this->buffer_data_) ||
|
||||||
ESP_LOGE(TAG, "Invalid message header");
|
this->buffer_data_[this->buffer_pos_ - 2] != DATA_FRAME_FOOTER[0] ||
|
||||||
|
this->buffer_data_[this->buffer_pos_ - 1] != DATA_FRAME_FOOTER[1]) {
|
||||||
|
ESP_LOGE(TAG, "Invalid header/footer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) { // footer
|
// Save the timestamp after validating the frame so, if invalid, we'll take the next frame immediately
|
||||||
ESP_LOGE(TAG, "Invalid message footer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->last_periodic_millis_ = App.get_loop_component_start_time();
|
this->last_periodic_millis_ = App.get_loop_component_start_time();
|
||||||
|
|
||||||
int16_t target_count = 0;
|
int16_t target_count = 0;
|
||||||
@ -450,13 +461,13 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
int16_t moving_target_count = 0;
|
int16_t moving_target_count = 0;
|
||||||
int16_t start = 0;
|
int16_t start = 0;
|
||||||
int16_t val = 0;
|
int16_t val = 0;
|
||||||
uint8_t index = 0;
|
|
||||||
int16_t tx = 0;
|
int16_t tx = 0;
|
||||||
int16_t ty = 0;
|
int16_t ty = 0;
|
||||||
int16_t td = 0;
|
int16_t td = 0;
|
||||||
int16_t ts = 0;
|
int16_t ts = 0;
|
||||||
int16_t angle = 0;
|
int16_t angle = 0;
|
||||||
std::string direction{};
|
uint8_t index = 0;
|
||||||
|
Direction direction{DIRECTION_UNDEFINED};
|
||||||
bool is_moving = false;
|
bool is_moving = false;
|
||||||
|
|
||||||
#if defined(USE_BINARY_SENSOR) || defined(USE_SENSOR) || defined(USE_TEXT_SENSOR)
|
#if defined(USE_BINARY_SENSOR) || defined(USE_SENSOR) || defined(USE_TEXT_SENSOR)
|
||||||
@ -468,7 +479,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
is_moving = false;
|
is_moving = false;
|
||||||
sensor::Sensor *sx = this->move_x_sensors_[index];
|
sensor::Sensor *sx = this->move_x_sensors_[index];
|
||||||
if (sx != nullptr) {
|
if (sx != nullptr) {
|
||||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
|
||||||
tx = val;
|
tx = val;
|
||||||
if (this->cached_target_data_[index].x != val) {
|
if (this->cached_target_data_[index].x != val) {
|
||||||
sx->publish_state(val);
|
sx->publish_state(val);
|
||||||
@ -479,7 +490,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
start = TARGET_Y + index * 8;
|
start = TARGET_Y + index * 8;
|
||||||
sensor::Sensor *sy = this->move_y_sensors_[index];
|
sensor::Sensor *sy = this->move_y_sensors_[index];
|
||||||
if (sy != nullptr) {
|
if (sy != nullptr) {
|
||||||
val = ld2450::decode_coordinate(buffer[start], buffer[start + 1]);
|
val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
|
||||||
ty = val;
|
ty = val;
|
||||||
if (this->cached_target_data_[index].y != val) {
|
if (this->cached_target_data_[index].y != val) {
|
||||||
sy->publish_state(val);
|
sy->publish_state(val);
|
||||||
@ -490,7 +501,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
start = TARGET_RESOLUTION + index * 8;
|
start = TARGET_RESOLUTION + index * 8;
|
||||||
sensor::Sensor *sr = this->move_resolution_sensors_[index];
|
sensor::Sensor *sr = this->move_resolution_sensors_[index];
|
||||||
if (sr != nullptr) {
|
if (sr != nullptr) {
|
||||||
val = (buffer[start + 1] << 8) | buffer[start];
|
val = (this->buffer_data_[start + 1] << 8) | this->buffer_data_[start];
|
||||||
if (this->cached_target_data_[index].resolution != val) {
|
if (this->cached_target_data_[index].resolution != val) {
|
||||||
sr->publish_state(val);
|
sr->publish_state(val);
|
||||||
this->cached_target_data_[index].resolution = val;
|
this->cached_target_data_[index].resolution = val;
|
||||||
@ -499,7 +510,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
#endif
|
#endif
|
||||||
// SPEED
|
// SPEED
|
||||||
start = TARGET_SPEED + index * 8;
|
start = TARGET_SPEED + index * 8;
|
||||||
val = ld2450::decode_speed(buffer[start], buffer[start + 1]);
|
val = ld2450::decode_speed(this->buffer_data_[start], this->buffer_data_[start + 1]);
|
||||||
ts = val;
|
ts = val;
|
||||||
if (val) {
|
if (val) {
|
||||||
is_moving = true;
|
is_moving = true;
|
||||||
@ -532,7 +543,7 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ANGLE
|
// ANGLE
|
||||||
angle = calculate_angle(static_cast<float>(ty), static_cast<float>(td));
|
angle = ld2450::calculate_angle(static_cast<float>(ty), static_cast<float>(td));
|
||||||
if (tx > 0) {
|
if (tx > 0) {
|
||||||
angle = angle * -1;
|
angle = angle * -1;
|
||||||
}
|
}
|
||||||
@ -547,14 +558,19 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
// DIRECTION
|
// DIRECTION
|
||||||
direction = get_direction(ts);
|
|
||||||
if (td == 0) {
|
if (td == 0) {
|
||||||
direction = "NA";
|
direction = DIRECTION_NA;
|
||||||
|
} else if (ts > 0) {
|
||||||
|
direction = DIRECTION_MOVING_AWAY;
|
||||||
|
} else if (ts < 0) {
|
||||||
|
direction = DIRECTION_APPROACHING;
|
||||||
|
} else {
|
||||||
|
direction = DIRECTION_STATIONARY;
|
||||||
}
|
}
|
||||||
text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
|
text_sensor::TextSensor *tsd = this->direction_text_sensors_[index];
|
||||||
if (tsd != nullptr) {
|
if (tsd != nullptr) {
|
||||||
if (this->cached_target_data_[index].direction != direction) {
|
if (this->cached_target_data_[index].direction != direction) {
|
||||||
tsd->publish_state(direction);
|
tsd->publish_state(find_str(ld2450::DIRECTION_BY_UINT, direction));
|
||||||
this->cached_target_data_[index].direction = direction;
|
this->cached_target_data_[index].direction = direction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,117 +694,139 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
|
bool LD2450Component::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 ack length");
|
ESP_LOGE(TAG, "Invalid length");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // frame header
|
if (!ld2450::validate_header_footer(CMD_FRAME_HEADER, this->buffer_data_)) {
|
||||||
ESP_LOGE(TAG, "Invalid ack header (command %02X)", buffer[COMMAND]);
|
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 ack status");
|
ESP_LOGE(TAG, "Invalid status");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (buffer[8] || buffer[9]) {
|
if (this->buffer_data_[8] || this->buffer_data_[9]) {
|
||||||
ESP_LOGE(TAG, "Last buffer was %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 command");
|
ESP_LOGV(TAG, "Enable conf");
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_DISABLE_CONF):
|
|
||||||
ESP_LOGV(TAG, "Disable conf command");
|
case CMD_DISABLE_CONF:
|
||||||
|
ESP_LOGV(TAG, "Disabled conf");
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_SET_BAUD_RATE):
|
|
||||||
ESP_LOGV(TAG, "Baud rate change command");
|
case CMD_SET_BAUD_RATE:
|
||||||
|
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_LOGV(TAG, "Change baud rate to %s", 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_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_BLUETOOTH):
|
}
|
||||||
ESP_LOGV(TAG, "Bluetooth command");
|
|
||||||
|
case CMD_BLUETOOTH:
|
||||||
|
ESP_LOGV(TAG, "Bluetooth");
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_SINGLE_TARGET_MODE):
|
|
||||||
ESP_LOGV(TAG, "Single target conf command");
|
case CMD_SINGLE_TARGET_MODE:
|
||||||
|
ESP_LOGV(TAG, "Single target conf");
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
if (this->multi_target_switch_ != nullptr) {
|
if (this->multi_target_switch_ != nullptr) {
|
||||||
this->multi_target_switch_->publish_state(false);
|
this->multi_target_switch_->publish_state(false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_MULTI_TARGET_MODE):
|
|
||||||
ESP_LOGV(TAG, "Multi target conf command");
|
case CMD_MULTI_TARGET_MODE:
|
||||||
|
ESP_LOGV(TAG, "Multi target conf");
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
if (this->multi_target_switch_ != nullptr) {
|
if (this->multi_target_switch_ != nullptr) {
|
||||||
this->multi_target_switch_->publish_state(true);
|
this->multi_target_switch_->publish_state(true);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_QUERY_TARGET_MODE):
|
|
||||||
ESP_LOGV(TAG, "Query target tracking mode command");
|
case CMD_QUERY_TARGET_MODE:
|
||||||
|
ESP_LOGV(TAG, "Query target tracking mode");
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
if (this->multi_target_switch_ != nullptr) {
|
if (this->multi_target_switch_ != nullptr) {
|
||||||
this->multi_target_switch_->publish_state(buffer[10] == 0x02);
|
this->multi_target_switch_->publish_state(this->buffer_data_[10] == 0x02);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_QUERY_ZONE):
|
|
||||||
ESP_LOGV(TAG, "Query zone conf command");
|
case CMD_QUERY_ZONE:
|
||||||
this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16);
|
ESP_LOGV(TAG, "Query zone conf");
|
||||||
|
this->zone_type_ = std::stoi(std::to_string(this->buffer_data_[10]), nullptr, 16);
|
||||||
this->publish_zone_type();
|
this->publish_zone_type();
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
if (this->zone_type_select_ != nullptr) {
|
if (this->zone_type_select_ != nullptr) {
|
||||||
ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str());
|
ESP_LOGV(TAG, "Change zone type to: %s", this->zone_type_select_->state.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (buffer[10] == 0x00) {
|
if (this->buffer_data_[10] == 0x00) {
|
||||||
ESP_LOGV(TAG, "Zone: Disabled");
|
ESP_LOGV(TAG, "Zone: Disabled");
|
||||||
}
|
}
|
||||||
if (buffer[10] == 0x01) {
|
if (this->buffer_data_[10] == 0x01) {
|
||||||
ESP_LOGV(TAG, "Zone: Area detection");
|
ESP_LOGV(TAG, "Zone: Area detection");
|
||||||
}
|
}
|
||||||
if (buffer[10] == 0x02) {
|
if (this->buffer_data_[10] == 0x02) {
|
||||||
ESP_LOGV(TAG, "Zone: Area filter");
|
ESP_LOGV(TAG, "Zone: Area filter");
|
||||||
}
|
}
|
||||||
this->process_zone_(buffer);
|
this->process_zone_();
|
||||||
break;
|
break;
|
||||||
case lowbyte(CMD_SET_ZONE):
|
|
||||||
ESP_LOGV(TAG, "Set zone conf command");
|
case CMD_SET_ZONE:
|
||||||
|
ESP_LOGV(TAG, "Set zone conf");
|
||||||
this->query_zone_info();
|
this->query_zone_info();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -796,55 +834,57 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read LD2450 buffer data
|
// Read LD2450 buffer data
|
||||||
void LD2450Component::readline_(int readch, uint8_t *buffer, uint8_t len) {
|
void LD2450Component::readline_(int readch) {
|
||||||
if (readch < 0) {
|
if (readch < 0) {
|
||||||
return;
|
return; // No data available
|
||||||
}
|
}
|
||||||
if (this->buffer_pos_ < len - 1) {
|
|
||||||
buffer[this->buffer_pos_++] = readch;
|
if (this->buffer_pos_ < MAX_LINE_LENGTH - 1) {
|
||||||
buffer[this->buffer_pos_] = 0;
|
this->buffer_data_[this->buffer_pos_++] = readch;
|
||||||
|
this->buffer_data_[this->buffer_pos_] = 0;
|
||||||
} else {
|
} else {
|
||||||
|
// We should never get here, but just in case...
|
||||||
|
ESP_LOGW(TAG, "Max command length exceeded; ignoring");
|
||||||
this->buffer_pos_ = 0;
|
this->buffer_pos_ = 0;
|
||||||
}
|
}
|
||||||
if (this->buffer_pos_ < 4) {
|
if (this->buffer_pos_ < 4) {
|
||||||
return;
|
return; // Not enough data to process yet
|
||||||
}
|
}
|
||||||
if (buffer[this->buffer_pos_ - 2] == 0x55 && buffer[this->buffer_pos_ - 1] == 0xCC) {
|
if (this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[0] &&
|
||||||
ESP_LOGV(TAG, "Handle periodic radar data");
|
this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[1]) {
|
||||||
this->handle_periodic_data_(buffer, this->buffer_pos_);
|
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 frame
|
this->buffer_pos_ = 0; // Reset position index for next frame
|
||||||
} else if (buffer[this->buffer_pos_ - 4] == 0x04 && buffer[this->buffer_pos_ - 3] == 0x03 &&
|
} else if (ld2450::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
||||||
buffer[this->buffer_pos_ - 2] == 0x02 && buffer[this->buffer_pos_ - 1] == 0x01) {
|
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
||||||
ESP_LOGV(TAG, "Handle command ack data");
|
if (this->handle_ack_data_()) {
|
||||||
if (this->handle_ack_data_(buffer, this->buffer_pos_)) {
|
this->buffer_pos_ = 0; // Reset position index for next message
|
||||||
this->buffer_pos_ = 0; // Reset position index for next frame
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Command ack data invalid");
|
ESP_LOGV(TAG, "Ack Data incomplete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Config Mode - Pre-requisite sending commands
|
// Set Config Mode - Pre-requisite sending commands
|
||||||
void LD2450Component::set_config_mode_(bool enable) {
|
void LD2450Component::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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Bluetooth Enable/Disable
|
// Set Bluetooth Enable/Disable
|
||||||
void LD2450Component::set_bluetooth(bool enable) {
|
void LD2450Component::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(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Baud rate
|
// Set Baud rate
|
||||||
void LD2450Component::set_baud_rate(const std::string &state) {
|
void LD2450Component::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_(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -885,12 +925,12 @@ void LD2450Component::factory_reset() {
|
|||||||
void LD2450Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); }
|
void LD2450Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); }
|
||||||
|
|
||||||
// Get LD2450 firmware version
|
// Get LD2450 firmware version
|
||||||
void LD2450Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
|
void LD2450Component::get_version_() { this->send_command_(CMD_QUERY_VERSION, nullptr, 0); }
|
||||||
|
|
||||||
// Get LD2450 mac address
|
// Get LD2450 mac address
|
||||||
void LD2450Component::get_mac_() {
|
void LD2450Component::get_mac_() {
|
||||||
uint8_t cmd_value[2] = {0x01, 0x00};
|
uint8_t cmd_value[2] = {0x01, 0x00};
|
||||||
this->send_command_(CMD_MAC, cmd_value, 2);
|
this->send_command_(CMD_QUERY_MAC_ADDRESS, cmd_value, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query for target tracking mode
|
// Query for target tracking mode
|
||||||
|
@ -38,10 +38,18 @@ namespace ld2450 {
|
|||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec.
|
static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec.
|
||||||
static const uint8_t MAX_LINE_LENGTH = 60; // Max characters for serial buffer
|
static const uint8_t MAX_LINE_LENGTH = 41; // Max characters for serial buffer
|
||||||
static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
|
static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
|
||||||
static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
|
static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
|
||||||
|
|
||||||
|
enum Direction : uint8_t {
|
||||||
|
DIRECTION_APPROACHING = 0,
|
||||||
|
DIRECTION_MOVING_AWAY = 1,
|
||||||
|
DIRECTION_STATIONARY = 2,
|
||||||
|
DIRECTION_NA = 3,
|
||||||
|
DIRECTION_UNDEFINED = 4,
|
||||||
|
};
|
||||||
|
|
||||||
// Target coordinate struct
|
// Target coordinate struct
|
||||||
struct Target {
|
struct Target {
|
||||||
int16_t x;
|
int16_t x;
|
||||||
@ -67,19 +75,22 @@ struct ZoneOfNumbers {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
class LD2450Component : public Component, public uart::UARTDevice {
|
class LD2450Component : public Component, public uart::UARTDevice {
|
||||||
#ifdef USE_SENSOR
|
|
||||||
SUB_SENSOR(target_count)
|
|
||||||
SUB_SENSOR(still_target_count)
|
|
||||||
SUB_SENSOR(moving_target_count)
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
SUB_BINARY_SENSOR(target)
|
|
||||||
SUB_BINARY_SENSOR(moving_target)
|
SUB_BINARY_SENSOR(moving_target)
|
||||||
SUB_BINARY_SENSOR(still_target)
|
SUB_BINARY_SENSOR(still_target)
|
||||||
|
SUB_BINARY_SENSOR(target)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SENSOR
|
||||||
|
SUB_SENSOR(moving_target_count)
|
||||||
|
SUB_SENSOR(still_target_count)
|
||||||
|
SUB_SENSOR(target_count)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
SUB_TEXT_SENSOR(version)
|
|
||||||
SUB_TEXT_SENSOR(mac)
|
SUB_TEXT_SENSOR(mac)
|
||||||
|
SUB_TEXT_SENSOR(version)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
SUB_NUMBER(presence_timeout)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
SUB_SELECT(baud_rate)
|
SUB_SELECT(baud_rate)
|
||||||
@ -90,12 +101,9 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
|||||||
SUB_SWITCH(multi_target)
|
SUB_SWITCH(multi_target)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
SUB_BUTTON(reset)
|
SUB_BUTTON(factory_reset)
|
||||||
SUB_BUTTON(restart)
|
SUB_BUTTON(restart)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
|
||||||
SUB_NUMBER(presence_timeout)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@ -138,10 +146,10 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
|||||||
protected:
|
protected:
|
||||||
void send_command_(uint8_t command_str, const uint8_t *command_value, uint8_t 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, uint8_t len);
|
void handle_periodic_data_();
|
||||||
bool handle_ack_data_(uint8_t *buffer, uint8_t len);
|
bool handle_ack_data_();
|
||||||
void process_zone_(uint8_t *buffer);
|
void process_zone_();
|
||||||
void readline_(int readch, uint8_t *buffer, uint8_t len);
|
void readline_(int readch);
|
||||||
void get_version_();
|
void get_version_();
|
||||||
void get_mac_();
|
void get_mac_();
|
||||||
void query_target_tracking_mode_();
|
void query_target_tracking_mode_();
|
||||||
@ -159,13 +167,14 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
|||||||
uint32_t moving_presence_millis_ = 0;
|
uint32_t moving_presence_millis_ = 0;
|
||||||
uint16_t throttle_ = 0;
|
uint16_t throttle_ = 0;
|
||||||
uint16_t timeout_ = 5;
|
uint16_t timeout_ = 5;
|
||||||
uint8_t buffer_pos_ = 0; // where to resume processing/populating buffer
|
|
||||||
uint8_t buffer_data_[MAX_LINE_LENGTH];
|
uint8_t buffer_data_[MAX_LINE_LENGTH];
|
||||||
|
uint8_t mac_address_[6] = {0, 0, 0, 0, 0, 0};
|
||||||
|
uint8_t version_[6] = {0, 0, 0, 0, 0, 0};
|
||||||
|
uint8_t buffer_pos_ = 0; // where to resume processing/populating buffer
|
||||||
uint8_t zone_type_ = 0;
|
uint8_t zone_type_ = 0;
|
||||||
|
bool bluetooth_on_{false};
|
||||||
Target target_info_[MAX_TARGETS];
|
Target target_info_[MAX_TARGETS];
|
||||||
Zone zone_config_[MAX_ZONES];
|
Zone zone_config_[MAX_ZONES];
|
||||||
std::string version_{};
|
|
||||||
std::string mac_{};
|
|
||||||
|
|
||||||
// Change detection - cache previous values to avoid redundant publishes
|
// Change detection - cache previous values to avoid redundant publishes
|
||||||
// All values are initialized to sentinel values that are outside the valid sensor ranges
|
// All values are initialized to sentinel values that are outside the valid sensor ranges
|
||||||
@ -176,8 +185,8 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
|||||||
int16_t speed = std::numeric_limits<int16_t>::min(); // -32768, outside practical sensor range
|
int16_t speed = std::numeric_limits<int16_t>::min(); // -32768, outside practical sensor range
|
||||||
uint16_t resolution = std::numeric_limits<uint16_t>::max(); // 65535, unlikely resolution value
|
uint16_t resolution = std::numeric_limits<uint16_t>::max(); // 65535, unlikely resolution value
|
||||||
uint16_t distance = std::numeric_limits<uint16_t>::max(); // 65535, outside range of 0 to ~8990
|
uint16_t distance = std::numeric_limits<uint16_t>::max(); // 65535, outside range of 0 to ~8990
|
||||||
|
Direction direction = DIRECTION_UNDEFINED; // Undefined, will differ from any real direction
|
||||||
float angle = NAN; // NAN, safe sentinel for floats
|
float angle = NAN; // NAN, safe sentinel for floats
|
||||||
std::string direction = ""; // Empty string, will differ from any real direction
|
|
||||||
} cached_target_data_[MAX_TARGETS];
|
} cached_target_data_[MAX_TARGETS];
|
||||||
|
|
||||||
struct CachedZoneData {
|
struct CachedZoneData {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user