mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01:00
Add HLK-LD2420 mmWave Radar module component (#4847)
Co-authored-by: descipher <120155735+GelidusResearch@users.noreply.github.com>
This commit is contained in:
parent
d74a8abf9a
commit
22cdb8dfc3
23 changed files with 1865 additions and 0 deletions
|
@ -152,6 +152,7 @@ esphome/components/key_provider/* @ssieb
|
|||
esphome/components/kuntze/* @ssieb
|
||||
esphome/components/lcd_menu/* @numo68
|
||||
esphome/components/ld2410/* @regevbr @sebcaps
|
||||
esphome/components/ld2420/* @descipher
|
||||
esphome/components/ledc/* @OttoWinter
|
||||
esphome/components/libretiny/* @kuba2k2
|
||||
esphome/components/libretiny_pwm/* @kuba2k2
|
||||
|
|
39
esphome/components/ld2420/__init__.py
Normal file
39
esphome/components/ld2420/__init__.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import uart
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@descipher"]
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
ld2420_ns = cg.esphome_ns.namespace("ld2420")
|
||||
LD2420Component = ld2420_ns.class_("LD2420Component", cg.Component, uart.UARTDevice)
|
||||
|
||||
CONF_LD2420_ID = "ld2420_id"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LD2420Component),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"ld2420_uart",
|
||||
require_tx=True,
|
||||
require_rx=True,
|
||||
parity="NONE",
|
||||
stop_bits=1,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
33
esphome/components/ld2420/binary_sensor/__init__.py
Normal file
33
esphome/components/ld2420/binary_sensor/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ID, DEVICE_CLASS_OCCUPANCY
|
||||
from .. import ld2420_ns, LD2420Component, CONF_LD2420_ID
|
||||
|
||||
LD2420BinarySensor = ld2420_ns.class_(
|
||||
"LD2420BinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONF_HAS_TARGET = "has_target"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LD2420BinarySensor),
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
|
||||
device_class=DEVICE_CLASS_OCCUPANCY
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
if CONF_HAS_TARGET in config:
|
||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET])
|
||||
cg.add(var.set_presence_sensor(sens))
|
||||
ld2420 = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
cg.add(ld2420.register_listener(var))
|
|
@ -0,0 +1,16 @@
|
|||
#include "ld2420_binary_sensor.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
static const char *const TAG = "LD2420.binary_sensor";
|
||||
|
||||
void LD2420BinarySensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LD2420 BinarySensor:");
|
||||
LOG_BINARY_SENSOR(" ", "Presence", this->presence_bsensor_);
|
||||
}
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "../ld2420.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420BinarySensor : public LD2420Listener, public Component, binary_sensor::BinarySensor {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_presence_sensor(binary_sensor::BinarySensor *bsensor) { this->presence_bsensor_ = bsensor; };
|
||||
void on_presence(bool presence) override {
|
||||
if (this->presence_bsensor_ != nullptr) {
|
||||
if (this->presence_bsensor_->state != presence)
|
||||
this->presence_bsensor_->publish_state(presence);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
binary_sensor::BinarySensor *presence_bsensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
69
esphome/components/ld2420/button/__init__.py
Normal file
69
esphome/components/ld2420/button/__init__.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
ICON_DATABASE,
|
||||
)
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
|
||||
LD2420ApplyConfigButton = ld2420_ns.class_("LD2420ApplyConfigButton", button.Button)
|
||||
LD2420RevertConfigButton = ld2420_ns.class_("LD2420RevertConfigButton", button.Button)
|
||||
LD2420RestartModuleButton = ld2420_ns.class_("LD2420RestartModuleButton", button.Button)
|
||||
LD2420FactoryResetButton = ld2420_ns.class_("LD2420FactoryResetButton", button.Button)
|
||||
|
||||
CONF_APPLY_CONFIG = "apply_config"
|
||||
CONF_REVERT_CONFIG = "revert_config"
|
||||
CONF_RESTART_MODULE = "restart_module"
|
||||
CONF_FACTORY_RESET = "factory_reset"
|
||||
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Required(CONF_APPLY_CONFIG): button.button_schema(
|
||||
LD2420ApplyConfigButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_REVERT_CONFIG): button.button_schema(
|
||||
LD2420RevertConfigButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART,
|
||||
),
|
||||
cv.Optional(CONF_RESTART_MODULE): button.button_schema(
|
||||
LD2420RestartModuleButton,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_DATABASE,
|
||||
),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
LD2420FactoryResetButton,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_DATABASE,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
ld2420_component = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
if apply_config := config.get(CONF_APPLY_CONFIG):
|
||||
b = await button.new_button(apply_config)
|
||||
await cg.register_parented(b, config[CONF_LD2420_ID])
|
||||
cg.add(ld2420_component.set_apply_config_button(b))
|
||||
if revert_config := config.get(CONF_REVERT_CONFIG):
|
||||
b = await button.new_button(revert_config)
|
||||
await cg.register_parented(b, config[CONF_LD2420_ID])
|
||||
cg.add(ld2420_component.set_revert_config_button(b))
|
||||
if restart_config := config.get(CONF_RESTART_MODULE):
|
||||
b = await button.new_button(restart_config)
|
||||
await cg.register_parented(b, config[CONF_LD2420_ID])
|
||||
cg.add(ld2420_component.set_restart_module_button(b))
|
||||
if factory_reset := config.get(CONF_FACTORY_RESET):
|
||||
b = await button.new_button(factory_reset)
|
||||
await cg.register_parented(b, config[CONF_LD2420_ID])
|
||||
cg.add(ld2420_component.set_factory_reset_button(b))
|
16
esphome/components/ld2420/button/reconfig_buttons.cpp
Normal file
16
esphome/components/ld2420/button/reconfig_buttons.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "reconfig_buttons.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
static const char *const TAG = "LD2420.button";
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
void LD2420ApplyConfigButton::press_action() { this->parent_->apply_config_action(); }
|
||||
void LD2420RevertConfigButton::press_action() { this->parent_->revert_config_action(); }
|
||||
void LD2420RestartModuleButton::press_action() { this->parent_->restart_module_action(); }
|
||||
void LD2420FactoryResetButton::press_action() { this->parent_->factory_reset_action(); }
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
42
esphome/components/ld2420/button/reconfig_buttons.h
Normal file
42
esphome/components/ld2420/button/reconfig_buttons.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../ld2420.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420ApplyConfigButton : public button::Button, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420ApplyConfigButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
class LD2420RevertConfigButton : public button::Button, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420RevertConfigButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
class LD2420RestartModuleButton : public button::Button, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420RestartModuleButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
class LD2420FactoryResetButton : public button::Button, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420FactoryResetButton() = default;
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
775
esphome/components/ld2420/ld2420.cpp
Normal file
775
esphome/components/ld2420/ld2420.cpp
Normal file
|
@ -0,0 +1,775 @@
|
|||
#include "ld2420.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
/*
|
||||
Configure commands - little endian
|
||||
|
||||
No command can exceed 64 bytes, otherwise they would need be to be split up into multiple sends.
|
||||
|
||||
All send command frames will have:
|
||||
Header = FD FC FB FA, Bytes 0 - 3, uint32_t 0xFAFBFCFD
|
||||
Length, bytes 4 - 5, uint16_t 0x0002, must be at least 2 for the command byte if no addon data.
|
||||
Command bytes 6 - 7, uint16_t
|
||||
Footer = 04 03 02 01 - uint32_t 0x01020304, Always last 4 Bytes.
|
||||
Receive
|
||||
Error bytes 8-9 uint16_t, 0 = success, all other positive values = error
|
||||
|
||||
Enable config mode:
|
||||
Send:
|
||||
UART Tx: FD FC FB FA 04 00 FF 00 02 00 04 03 02 01
|
||||
Command = FF 00 - uint16_t 0x00FF
|
||||
Protocol version = 02 00, can be 1 or 2 - uint16_t 0x0002
|
||||
Reply:
|
||||
UART Rx: FD FC FB FA 06 00 FF 01 00 00 02 00 04 03 02 01
|
||||
|
||||
Disable config mode:
|
||||
Send:
|
||||
UART Tx: FD FC FB FA 02 00 FE 00 04 03 02 01
|
||||
Command = FE 00 - uint16_t 0x00FE
|
||||
Receive:
|
||||
UART Rx: FD FC FB FA 04 00 FE 01 00 00 04 03 02 01
|
||||
|
||||
Configure system parameters:
|
||||
|
||||
UART Tx: FD FC FB FA 08 00 12 00 00 00 64 00 00 00 04 03 02 01 Set system parms
|
||||
Command = 12 00 - uint16_t 0x0012, Param
|
||||
There are three documented parameters for modes:
|
||||
00 64 = Basic status mode
|
||||
This mode outputs text as presence "ON" or "OFF" and "Range XXXX"
|
||||
where XXXX is a decimal value for distance in cm
|
||||
00 04 = Energy output mode
|
||||
This mode outputs detailed signal energy values for each gate and the target distance.
|
||||
The data format consist of the following.
|
||||
Header HH, Length LL, Persence PP, Distance DD, Range Gate GG, 16 Gate Energies EE, Footer FF
|
||||
HH HH HH HH LL LL PP DD DD GG GG EE EE .. 16x .. FF FF FF FF
|
||||
F4 F3 F2 F1 00 23 00 00 00 00 01 00 00 .. .. .. .. F8 F7 F6 F5
|
||||
00 00 = debug output mode
|
||||
This mode outputs detailed values consisting of 20 Dopplers, 16 Ranges for a total 20 * 16 * 4 bytes
|
||||
The data format consist of the following.
|
||||
Header HH, Doppler DD, Range RR, Footer FF
|
||||
HH HH HH HH DD DD DD DD .. 20x .. RR RR RR RR .. 16x .. FF FF FF FF
|
||||
AA BF 10 14 00 00 00 00 .. .. .. .. 00 00 00 00 .. .. .. .. FD FC FB FA
|
||||
|
||||
Configure gate sensitivity parameters:
|
||||
UART Tx: FD FC FB FA 0E 00 07 00 10 00 60 EA 00 00 20 00 60 EA 00 00 04 03 02 01
|
||||
Command = 12 00 - uint16_t 0x0007
|
||||
Gate 0 high thresh = 10 00 uint16_t 0x0010, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
|
||||
Gate 0 low thresh = 20 00 uint16_t 0x0020, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
|
||||
*/
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
static const char *const TAG = "ld2420";
|
||||
|
||||
float LD2420Component::get_setup_priority() const { return setup_priority::BUS; }
|
||||
|
||||
void LD2420Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LD2420:");
|
||||
ESP_LOGCONFIG(TAG, " Firmware Version : %7s", this->ld2420_firmware_ver_);
|
||||
ESP_LOGCONFIG(TAG, "LD2420 Number:");
|
||||
LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_);
|
||||
LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_);
|
||||
LOG_NUMBER(TAG, " Gate Min Distance:", this->min_gate_distance_number_);
|
||||
LOG_NUMBER(TAG, " Gate Select:", this->gate_select_number_);
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
LOG_NUMBER(TAG, " Gate Move Threshold:", this->gate_move_threshold_numbers_[gate]);
|
||||
LOG_NUMBER(TAG, " Gate Still Threshold::", this->gate_still_threshold_numbers_[gate]);
|
||||
}
|
||||
LOG_BUTTON(TAG, " Apply Config:", this->apply_config_button_);
|
||||
LOG_BUTTON(TAG, " Revert Edits:", this->revert_config_button_);
|
||||
LOG_BUTTON(TAG, " Factory Reset:", this->factory_reset_button_);
|
||||
LOG_BUTTON(TAG, " Restart Module:", this->restart_module_button_);
|
||||
ESP_LOGCONFIG(TAG, "LD2420 Select:");
|
||||
LOG_SELECT(TAG, " Operating Mode", this->operating_selector_);
|
||||
if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
|
||||
ESP_LOGW(TAG, "LD2420 Firmware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t LD2420Component::calc_checksum(void *data, size_t size) {
|
||||
uint8_t checksum = 0;
|
||||
uint8_t *data_bytes = (uint8_t *) data;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
checksum ^= data_bytes[i]; // XOR operation
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
int LD2420Component::get_firmware_int_(const char *version_string) {
|
||||
std::string version_str = version_string;
|
||||
if (version_str[0] == 'v') {
|
||||
version_str = version_str.substr(1);
|
||||
}
|
||||
version_str.erase(remove(version_str.begin(), version_str.end(), '.'), version_str.end());
|
||||
int version_integer = stoi(version_str);
|
||||
return version_integer;
|
||||
}
|
||||
|
||||
void LD2420Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up LD2420...");
|
||||
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->get_min_max_distances_timeout_();
|
||||
#ifdef USE_NUMBER
|
||||
this->init_gate_config_numbers();
|
||||
#endif
|
||||
this->get_firmware_version_();
|
||||
const char *pfw = this->ld2420_firmware_ver_;
|
||||
std::string fw_str(pfw);
|
||||
|
||||
for (auto &listener : listeners_) {
|
||||
listener->on_fw_version(fw_str);
|
||||
}
|
||||
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
delay_microseconds_safe(125);
|
||||
this->get_gate_threshold_(gate);
|
||||
}
|
||||
|
||||
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
|
||||
if (get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
|
||||
this->set_operating_mode(OP_SIMPLE_MODE_STRING);
|
||||
this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
|
||||
this->set_mode_(CMD_SYSTEM_MODE_SIMPLE);
|
||||
ESP_LOGW(TAG, "LD2420 Frimware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_);
|
||||
} else {
|
||||
this->set_mode_(CMD_SYSTEM_MODE_ENERGY);
|
||||
this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING);
|
||||
}
|
||||
#ifdef USE_NUMBER
|
||||
this->init_gate_config_numbers();
|
||||
#endif
|
||||
this->set_system_mode(this->system_mode_);
|
||||
this->set_config_mode(false);
|
||||
ESP_LOGCONFIG(TAG, "LD2420 setup complete.");
|
||||
}
|
||||
|
||||
void LD2420Component::apply_config_action() {
|
||||
const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config));
|
||||
if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) {
|
||||
ESP_LOGCONFIG(TAG, "No configuration change detected");
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, "Reconfiguring LD2420...");
|
||||
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->set_min_max_distances_timeout(this->new_config.max_gate, this->new_config.min_gate, this->new_config.timeout);
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
delay_microseconds_safe(125);
|
||||
this->set_gate_threshold(gate);
|
||||
}
|
||||
memcpy(¤t_config, &new_config, sizeof(new_config));
|
||||
#ifdef USE_NUMBER
|
||||
this->init_gate_config_numbers();
|
||||
#endif
|
||||
this->set_system_mode(this->system_mode_);
|
||||
this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm
|
||||
this->set_operating_mode(OP_NORMAL_MODE_STRING);
|
||||
ESP_LOGCONFIG(TAG, "LD2420 reconfig complete.");
|
||||
}
|
||||
|
||||
void LD2420Component::factory_reset_action() {
|
||||
ESP_LOGCONFIG(TAG, "Setiing factory defaults...");
|
||||
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->set_min_max_distances_timeout(FACTORY_MAX_GATE, FACTORY_MIN_GATE, FACTORY_TIMEOUT);
|
||||
this->gate_timeout_number_->state = FACTORY_TIMEOUT;
|
||||
this->min_gate_distance_number_->state = FACTORY_MIN_GATE;
|
||||
this->max_gate_distance_number_->state = FACTORY_MAX_GATE;
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
this->new_config.move_thresh[gate] = FACTORY_MOVE_THRESH[gate];
|
||||
this->new_config.still_thresh[gate] = FACTORY_STILL_THRESH[gate];
|
||||
delay_microseconds_safe(125);
|
||||
this->set_gate_threshold(gate);
|
||||
}
|
||||
memcpy(&this->current_config, &this->new_config, sizeof(this->new_config));
|
||||
this->set_system_mode(this->system_mode_);
|
||||
this->set_config_mode(false);
|
||||
#ifdef USE_NUMBER
|
||||
this->init_gate_config_numbers();
|
||||
this->refresh_gate_config_numbers();
|
||||
#endif
|
||||
ESP_LOGCONFIG(TAG, "LD2420 factory reset complete.");
|
||||
}
|
||||
|
||||
void LD2420Component::restart_module_action() {
|
||||
ESP_LOGCONFIG(TAG, "Restarting LD2420 module...");
|
||||
this->send_module_restart();
|
||||
delay_microseconds_safe(45000);
|
||||
this->set_config_mode(true);
|
||||
this->set_system_mode(system_mode_);
|
||||
this->set_config_mode(false);
|
||||
ESP_LOGCONFIG(TAG, "LD2420 Restarted.");
|
||||
}
|
||||
|
||||
void LD2420Component::revert_config_action() {
|
||||
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
|
||||
#ifdef USE_NUMBER
|
||||
this->init_gate_config_numbers();
|
||||
#endif
|
||||
ESP_LOGCONFIG(TAG, "Reverted config number edits.");
|
||||
}
|
||||
|
||||
void LD2420Component::loop() {
|
||||
// If there is a active send command do not process it here, the send command call will handle it.
|
||||
if (!get_cmd_active_()) {
|
||||
if (!available())
|
||||
return;
|
||||
static uint8_t buffer[2048];
|
||||
static uint8_t rx_data;
|
||||
while (available()) {
|
||||
rx_data = read();
|
||||
this->readline_(rx_data, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::update_radar_data(uint16_t const *gate_energy, uint8_t sample_number) {
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
|
||||
this->radar_data[gate][sample_number] = gate_energy[gate];
|
||||
}
|
||||
this->total_sample_number_counter++;
|
||||
}
|
||||
|
||||
void LD2420Component::auto_calibrate_sensitivity() {
|
||||
// Calculate average and peak values for each gate
|
||||
const float move_factor = gate_move_sensitivity_factor + 1;
|
||||
const float still_factor = (gate_still_sensitivity_factor / 2) + 1;
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
|
||||
uint32_t sum = 0;
|
||||
uint16_t peak = 0;
|
||||
|
||||
for (uint8_t sample_number = 0; sample_number < CALIBRATE_SAMPLES; ++sample_number) {
|
||||
// Calculate average
|
||||
sum += this->radar_data[gate][sample_number];
|
||||
|
||||
// Calculate max value
|
||||
if (this->radar_data[gate][sample_number] > peak) {
|
||||
peak = this->radar_data[gate][sample_number];
|
||||
}
|
||||
}
|
||||
|
||||
// Store average and peak values
|
||||
this->gate_avg[gate] = sum / CALIBRATE_SAMPLES;
|
||||
if (this->gate_peak[gate] < peak)
|
||||
this->gate_peak[gate] = peak;
|
||||
|
||||
uint32_t calculated_value =
|
||||
(static_cast<uint32_t>(this->gate_peak[gate]) + (move_factor * static_cast<uint32_t>(this->gate_peak[gate])));
|
||||
this->new_config.move_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
|
||||
calculated_value =
|
||||
(static_cast<uint32_t>(this->gate_peak[gate]) + (still_factor * static_cast<uint32_t>(this->gate_peak[gate])));
|
||||
this->new_config.still_thresh[gate] = static_cast<uint16_t>(calculated_value <= 65535 ? calculated_value : 65535);
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::report_gate_data() {
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; ++gate) {
|
||||
// Output results
|
||||
ESP_LOGI(TAG, "Gate: %2d Avg: %5d Peak: %5d", gate, this->gate_avg[gate], this->gate_peak[gate]);
|
||||
}
|
||||
ESP_LOGI(TAG, "Total samples: %d", this->total_sample_number_counter);
|
||||
}
|
||||
|
||||
void LD2420Component::set_operating_mode(const std::string &state) {
|
||||
// If unsupported firmware ignore mode select
|
||||
if (get_firmware_int_(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) {
|
||||
this->current_operating_mode = OP_MODE_TO_UINT.at(state);
|
||||
// Entering Auto Calibrate we need to clear the privoiuos data collection
|
||||
this->operating_selector_->publish_state(state);
|
||||
if (current_operating_mode == OP_CALIBRATE_MODE) {
|
||||
this->set_calibration_(true);
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
this->gate_avg[gate] = 0;
|
||||
this->gate_peak[gate] = 0;
|
||||
for (uint8_t i = 0; i < CALIBRATE_SAMPLES; i++) {
|
||||
this->radar_data[gate][i] = 0;
|
||||
}
|
||||
this->total_sample_number_counter = 0;
|
||||
}
|
||||
} else {
|
||||
// Set the current data back so we don't have new data that can be applied in error.
|
||||
if (this->get_calibration_())
|
||||
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
|
||||
this->set_calibration_(false);
|
||||
}
|
||||
} else {
|
||||
this->current_operating_mode = OP_SIMPLE_MODE;
|
||||
this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::readline_(int rx_data, uint8_t *buffer, int len) {
|
||||
static int pos = 0;
|
||||
|
||||
if (rx_data >= 0) {
|
||||
if (pos < len - 1) {
|
||||
buffer[pos++] = rx_data;
|
||||
buffer[pos] = 0;
|
||||
} else {
|
||||
pos = 0;
|
||||
}
|
||||
if (pos >= 4) {
|
||||
if (memcmp(&buffer[pos - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) {
|
||||
this->set_cmd_active_(false); // Set command state to inactive after responce.
|
||||
this->handle_ack_data_(buffer, pos);
|
||||
pos = 0;
|
||||
} else if ((buffer[pos - 2] == 0x0D && buffer[pos - 1] == 0x0A) && (get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) {
|
||||
this->handle_simple_mode_(buffer, pos);
|
||||
pos = 0;
|
||||
} else if ((memcmp(&buffer[pos - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) &&
|
||||
(get_mode_() == CMD_SYSTEM_MODE_ENERGY)) {
|
||||
this->handle_energy_mode_(buffer, pos);
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) {
|
||||
uint8_t index = 6; // Start at presence byte position
|
||||
uint16_t range;
|
||||
const uint8_t elements = sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]);
|
||||
this->set_presence_(buffer[index]);
|
||||
index++;
|
||||
memcpy(&range, &buffer[index], sizeof(range));
|
||||
index += sizeof(range);
|
||||
this->set_distance_(range);
|
||||
for (uint8_t i = 0; i < elements; i++) { // NOLINT
|
||||
memcpy(&this->gate_energy_[i], &buffer[index], sizeof(this->gate_energy_[0]));
|
||||
index += sizeof(this->gate_energy_[0]);
|
||||
}
|
||||
|
||||
if (this->current_operating_mode == OP_CALIBRATE_MODE) {
|
||||
this->update_radar_data(gate_energy_, sample_number_counter);
|
||||
this->sample_number_counter > CALIBRATE_SAMPLES ? this->sample_number_counter = 0 : this->sample_number_counter++;
|
||||
}
|
||||
|
||||
// Resonable refresh rate for home assistant database size health
|
||||
const int32_t current_millis = millis();
|
||||
if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS)
|
||||
return;
|
||||
this->last_periodic_millis = current_millis;
|
||||
for (auto &listener : this->listeners_) {
|
||||
listener->on_distance(get_distance_());
|
||||
listener->on_presence(get_presence_());
|
||||
listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]));
|
||||
}
|
||||
|
||||
if (this->current_operating_mode == OP_CALIBRATE_MODE) {
|
||||
this->auto_calibrate_sensitivity();
|
||||
if (current_millis - this->report_periodic_millis > REFRESH_RATE_MS * CALIBRATE_REPORT_INTERVAL) {
|
||||
this->report_periodic_millis = current_millis;
|
||||
this->report_gate_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
|
||||
const uint8_t bufsize = 16;
|
||||
uint8_t index{0};
|
||||
uint8_t pos{0};
|
||||
char *endptr{nullptr};
|
||||
char outbuf[bufsize]{0};
|
||||
while (true) {
|
||||
if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') {
|
||||
set_presence_(false);
|
||||
} else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') {
|
||||
set_presence_(true);
|
||||
}
|
||||
if (inbuf[pos] >= '0' && inbuf[pos] <= '9') {
|
||||
if (index < bufsize - 1) {
|
||||
outbuf[index++] = inbuf[pos];
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
if (pos < len - 1) {
|
||||
pos++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
outbuf[index] = '\0';
|
||||
if (index > 1)
|
||||
set_distance_(strtol(outbuf, &endptr, 10));
|
||||
|
||||
if (get_mode_() == CMD_SYSTEM_MODE_SIMPLE) {
|
||||
// Resonable refresh rate for home assistant database size health
|
||||
const int32_t current_millis = millis();
|
||||
if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS)
|
||||
return;
|
||||
this->last_normal_periodic_millis = current_millis;
|
||||
for (auto &listener : this->listeners_)
|
||||
listener->on_distance(get_distance_());
|
||||
for (auto &listener : this->listeners_)
|
||||
listener->on_presence(get_presence_());
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
|
||||
this->cmd_reply_.command = buffer[CMD_FRAME_COMMAND];
|
||||
this->cmd_reply_.length = buffer[CMD_FRAME_DATA_LENGTH];
|
||||
uint8_t reg_element = 0;
|
||||
uint8_t data_element = 0;
|
||||
uint16_t data_pos = 0;
|
||||
if (this->cmd_reply_.length > CMD_MAX_BYTES) {
|
||||
ESP_LOGW(TAG, "LD2420 reply - received command reply frame is corrupt, length exceeds %d bytes.", CMD_MAX_BYTES);
|
||||
return;
|
||||
} else if (this->cmd_reply_.length < 2) {
|
||||
ESP_LOGW(TAG, "LD2420 reply - received command frame is corrupt, length is less than 2 bytes.");
|
||||
return;
|
||||
}
|
||||
memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error));
|
||||
const char *result = this->cmd_reply_.error ? "failure" : "success";
|
||||
if (this->cmd_reply_.error > 0) {
|
||||
return;
|
||||
};
|
||||
this->cmd_reply_.ack = true;
|
||||
switch ((uint16_t) this->cmd_reply_.command) {
|
||||
case (CMD_ENABLE_CONF):
|
||||
ESP_LOGD(TAG, "LD2420 reply - set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result);
|
||||
break;
|
||||
case (CMD_DISABLE_CONF):
|
||||
ESP_LOGD(TAG, "LD2420 reply - set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result);
|
||||
break;
|
||||
case (CMD_READ_REGISTER):
|
||||
ESP_LOGD(TAG, "LD2420 reply - read register: CMD = %2X %s", CMD_READ_REGISTER, result);
|
||||
// TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file
|
||||
data_pos = 0x0A;
|
||||
for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE * // NOLINT
|
||||
((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_REG_DATA_REPLY_SIZE));
|
||||
index += CMD_REG_DATA_REPLY_SIZE) {
|
||||
memcpy(&this->cmd_reply_.data[reg_element], &buffer[data_pos + index], sizeof(CMD_REG_DATA_REPLY_SIZE));
|
||||
byteswap(this->cmd_reply_.data[reg_element]);
|
||||
reg_element++;
|
||||
}
|
||||
break;
|
||||
case (CMD_WRITE_REGISTER):
|
||||
ESP_LOGD(TAG, "LD2420 reply - write register: CMD = %2X %s", CMD_WRITE_REGISTER, result);
|
||||
break;
|
||||
case (CMD_WRITE_ABD_PARAM):
|
||||
ESP_LOGD(TAG, "LD2420 reply - write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result);
|
||||
break;
|
||||
case (CMD_READ_ABD_PARAM):
|
||||
ESP_LOGD(TAG, "LD2420 reply - read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result);
|
||||
data_pos = CMD_ABD_DATA_REPLY_START;
|
||||
for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE * // NOLINT
|
||||
((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE));
|
||||
index += CMD_ABD_DATA_REPLY_SIZE) {
|
||||
memcpy(&this->cmd_reply_.data[data_element], &buffer[data_pos + index],
|
||||
sizeof(this->cmd_reply_.data[data_element]));
|
||||
byteswap(this->cmd_reply_.data[data_element]);
|
||||
data_element++;
|
||||
}
|
||||
break;
|
||||
case (CMD_WRITE_SYS_PARAM):
|
||||
ESP_LOGD(TAG, "LD2420 reply - set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result);
|
||||
break;
|
||||
case (CMD_READ_VERSION):
|
||||
memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]);
|
||||
ESP_LOGD(TAG, "LD2420 reply - module firmware version: %7s %s", this->ld2420_firmware_ver_, result);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
|
||||
uint8_t error = 0;
|
||||
uint8_t ack_buffer[64];
|
||||
uint8_t cmd_buffer[64];
|
||||
uint16_t loop_count;
|
||||
this->cmd_reply_.ack = false;
|
||||
if (frame.command != CMD_RESTART)
|
||||
this->set_cmd_active_(true); // Restart does not reply, thus no ack state required.
|
||||
uint8_t retry = 3;
|
||||
while (retry) {
|
||||
// TODO setup a dynamic method e.g. millis time count etc. to tune for non ESP32 240Mhz devices
|
||||
// this is ok for now since the module firmware is changing like the weather atm
|
||||
frame.length = 0;
|
||||
loop_count = 1250;
|
||||
uint16_t frame_data_bytes = frame.data_length + 2; // Always add two bytes for the cmd size
|
||||
|
||||
memcpy(&cmd_buffer[frame.length], &frame.header, sizeof(frame.header));
|
||||
frame.length += sizeof(frame.header);
|
||||
|
||||
memcpy(&cmd_buffer[frame.length], &frame_data_bytes, sizeof(frame.data_length));
|
||||
frame.length += sizeof(frame.data_length);
|
||||
|
||||
memcpy(&cmd_buffer[frame.length], &frame.command, sizeof(frame.command));
|
||||
frame.length += sizeof(frame.command);
|
||||
|
||||
for (uint16_t index = 0; index < frame.data_length; index++) {
|
||||
memcpy(&cmd_buffer[frame.length], &frame.data[index], sizeof(frame.data[index]));
|
||||
frame.length += sizeof(frame.data[index]);
|
||||
}
|
||||
|
||||
memcpy(cmd_buffer + frame.length, &frame.footer, sizeof(frame.footer));
|
||||
frame.length += sizeof(frame.footer);
|
||||
for (uint16_t index = 0; index < frame.length; index++) {
|
||||
this->write_byte(cmd_buffer[index]);
|
||||
}
|
||||
|
||||
delay_microseconds_safe(500); // give the module a moment to process it
|
||||
error = 0;
|
||||
if (frame.command == CMD_RESTART) {
|
||||
delay_microseconds_safe(25000); // Wait for the restart
|
||||
return 0; // restart does not reply exit now
|
||||
}
|
||||
|
||||
while (!this->cmd_reply_.ack) {
|
||||
while (available()) {
|
||||
this->readline_(read(), ack_buffer, sizeof(ack_buffer));
|
||||
}
|
||||
delay_microseconds_safe(250);
|
||||
if (loop_count <= 0) {
|
||||
error = LD2420_ERROR_TIMEOUT;
|
||||
retry--;
|
||||
break;
|
||||
}
|
||||
loop_count--;
|
||||
}
|
||||
if (this->cmd_reply_.ack)
|
||||
retry = 0;
|
||||
if (this->cmd_reply_.error > 0)
|
||||
handle_cmd_error(error);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t LD2420Component::set_config_mode(bool enable) {
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
|
||||
if (enable) {
|
||||
memcpy(&cmd_frame.data[0], &CMD_PROTOCOL_VER, sizeof(CMD_PROTOCOL_VER));
|
||||
cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER);
|
||||
}
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command);
|
||||
return this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
// Sends a restart and set system running mode to normal
|
||||
void LD2420Component::send_module_restart() { this->ld2420_restart(); }
|
||||
|
||||
void LD2420Component::ld2420_restart() {
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_RESTART;
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending restart command: %2X", cmd_frame.command);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
void LD2420Component::get_reg_value_(uint16_t reg) {
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_READ_REGISTER;
|
||||
cmd_frame.data[1] = reg;
|
||||
cmd_frame.data_length += 2;
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
void LD2420Component::set_reg_value(uint16_t reg, uint16_t value) {
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_WRITE_REGISTER;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], ®, sizeof(CMD_REG_DATA_REPLY_SIZE));
|
||||
cmd_frame.data_length += 2;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE));
|
||||
cmd_frame.data_length += 2;
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGI(TAG, "Command failed: %s", ERR_MESSAGE[error]); }
|
||||
|
||||
int LD2420Component::get_gate_threshold_(uint8_t gate) {
|
||||
uint8_t error;
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_READ_ABD_PARAM;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_MOVE_THRESH[gate], sizeof(CMD_GATE_MOVE_THRESH[gate]));
|
||||
cmd_frame.data_length += 2;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate]));
|
||||
cmd_frame.data_length += 2;
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending read gate %d high/low theshold command: %2X", gate, cmd_frame.command);
|
||||
error = this->send_cmd_from_array(cmd_frame);
|
||||
if (error == 0) {
|
||||
this->current_config.move_thresh[gate] = cmd_reply_.data[0];
|
||||
this->current_config.still_thresh[gate] = cmd_reply_.data[1];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
int LD2420Component::get_min_max_distances_timeout_() {
|
||||
uint8_t error;
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_READ_ABD_PARAM;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
|
||||
sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
|
||||
cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
|
||||
sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
|
||||
cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
|
||||
sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
|
||||
cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command);
|
||||
error = this->send_cmd_from_array(cmd_frame);
|
||||
if (error == 0) {
|
||||
this->current_config.min_gate = (uint16_t) cmd_reply_.data[0];
|
||||
this->current_config.max_gate = (uint16_t) cmd_reply_.data[1];
|
||||
this->current_config.timeout = (uint16_t) cmd_reply_.data[2];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void LD2420Component::set_system_mode(uint16_t mode) {
|
||||
CmdFrameT cmd_frame;
|
||||
uint16_t unknown_parm = 0x0000;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_WRITE_SYS_PARAM;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_SYSTEM_MODE, sizeof(CMD_SYSTEM_MODE));
|
||||
cmd_frame.data_length += sizeof(CMD_SYSTEM_MODE);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &mode, sizeof(mode));
|
||||
cmd_frame.data_length += sizeof(mode);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm));
|
||||
cmd_frame.data_length += sizeof(unknown_parm);
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending write system mode command: %2X", cmd_frame.command);
|
||||
if (this->send_cmd_from_array(cmd_frame) == 0)
|
||||
set_mode_(mode);
|
||||
}
|
||||
|
||||
void LD2420Component::get_firmware_version_() {
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_READ_VERSION;
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
|
||||
ESP_LOGD(TAG, "Sending read firmware version command: %2X", cmd_frame.command);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, // NOLINT
|
||||
uint32_t timeout) {
|
||||
// Header H, Length L, Register R, Value V, Footer F
|
||||
// |Min Gate |Max Gate |Timeout |
|
||||
// HH HH HH HH LL LL CC CC RR RR VV VV VV VV RR RR VV VV VV VV RR RR VV VV VV VV FF FF FF FF
|
||||
// FD FC FB FA 14 00 07 00 00 00 01 00 00 00 01 00 09 00 00 00 04 00 0A 00 00 00 04 03 02 01 e.g.
|
||||
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_WRITE_ABD_PARAM;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MIN_GATE_REG,
|
||||
sizeof(CMD_MIN_GATE_REG)); // Register: global min detect gate number
|
||||
cmd_frame.data_length += sizeof(CMD_MIN_GATE_REG);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &min_gate_distance, sizeof(min_gate_distance));
|
||||
cmd_frame.data_length += sizeof(min_gate_distance);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_MAX_GATE_REG,
|
||||
sizeof(CMD_MAX_GATE_REG)); // Register: global max detect gate number
|
||||
cmd_frame.data_length += sizeof(CMD_MAX_GATE_REG);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &max_gate_distance, sizeof(max_gate_distance));
|
||||
cmd_frame.data_length += sizeof(max_gate_distance);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_TIMEOUT_REG,
|
||||
sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
|
||||
cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &timeout, sizeof(timeout));
|
||||
;
|
||||
cmd_frame.data_length += sizeof(timeout);
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
|
||||
ESP_LOGD(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
void LD2420Component::set_gate_threshold(uint8_t gate) {
|
||||
// Header H, Length L, Command C, Register R, Value V, Footer F
|
||||
// HH HH HH HH LL LL CC CC RR RR VV VV VV VV RR RR VV VV VV VV FF FF FF FF
|
||||
// FD FC FB FA 14 00 07 00 10 00 00 FF 00 00 00 01 00 0F 00 00 04 03 02 01
|
||||
|
||||
uint16_t move_threshold_gate = CMD_GATE_MOVE_THRESH[gate];
|
||||
uint16_t still_threshold_gate = CMD_GATE_STILL_THRESH[gate];
|
||||
CmdFrameT cmd_frame;
|
||||
cmd_frame.data_length = 0;
|
||||
cmd_frame.header = CMD_FRAME_HEADER;
|
||||
cmd_frame.command = CMD_WRITE_ABD_PARAM;
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &move_threshold_gate, sizeof(move_threshold_gate));
|
||||
cmd_frame.data_length += sizeof(move_threshold_gate);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.move_thresh[gate],
|
||||
sizeof(this->new_config.move_thresh[gate]));
|
||||
cmd_frame.data_length += sizeof(this->new_config.move_thresh[gate]);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &still_threshold_gate, sizeof(still_threshold_gate));
|
||||
cmd_frame.data_length += sizeof(still_threshold_gate);
|
||||
memcpy(&cmd_frame.data[cmd_frame.data_length], &this->new_config.still_thresh[gate],
|
||||
sizeof(this->new_config.still_thresh[gate]));
|
||||
cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]);
|
||||
cmd_frame.footer = CMD_FRAME_FOOTER;
|
||||
ESP_LOGD(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command);
|
||||
this->send_cmd_from_array(cmd_frame);
|
||||
}
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void LD2420Component::init_gate_config_numbers() {
|
||||
if (this->gate_timeout_number_ != nullptr)
|
||||
this->gate_timeout_number_->publish_state(static_cast<uint16_t>(this->current_config.timeout));
|
||||
if (this->gate_select_number_ != nullptr)
|
||||
this->gate_select_number_->publish_state(0);
|
||||
if (this->min_gate_distance_number_ != nullptr)
|
||||
this->min_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.min_gate));
|
||||
if (this->max_gate_distance_number_ != nullptr)
|
||||
this->max_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.max_gate));
|
||||
if (this->gate_move_sensitivity_factor_number_ != nullptr)
|
||||
this->gate_move_sensitivity_factor_number_->publish_state(this->gate_move_sensitivity_factor);
|
||||
if (this->gate_still_sensitivity_factor_number_ != nullptr)
|
||||
this->gate_still_sensitivity_factor_number_->publish_state(this->gate_still_sensitivity_factor);
|
||||
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
|
||||
if (this->gate_still_threshold_numbers_[gate] != nullptr) {
|
||||
this->gate_still_threshold_numbers_[gate]->publish_state(
|
||||
static_cast<uint16_t>(this->current_config.still_thresh[gate]));
|
||||
}
|
||||
if (this->gate_move_threshold_numbers_[gate] != nullptr) {
|
||||
this->gate_move_threshold_numbers_[gate]->publish_state(
|
||||
static_cast<uint16_t>(this->current_config.move_thresh[gate]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LD2420Component::refresh_gate_config_numbers() {
|
||||
this->gate_timeout_number_->publish_state(this->new_config.timeout);
|
||||
this->min_gate_distance_number_->publish_state(this->new_config.min_gate);
|
||||
this->max_gate_distance_number_->publish_state(this->new_config.max_gate);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
272
esphome/components/ld2420/ld2420.h
Normal file
272
esphome/components/ld2420/ld2420.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
#include "esphome/components/select/select.h"
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
#include "esphome/components/button/button.h"
|
||||
#endif
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
// Local const's
|
||||
static const uint16_t REFRESH_RATE_MS = 1000;
|
||||
|
||||
// Command sets
|
||||
static const uint8_t CMD_ABD_DATA_REPLY_SIZE = 0x04;
|
||||
static const uint8_t CMD_ABD_DATA_REPLY_START = 0x0A;
|
||||
static const uint16_t CMD_DISABLE_CONF = 0x00FE;
|
||||
static const uint16_t CMD_ENABLE_CONF = 0x00FF;
|
||||
static const uint8_t CMD_MAX_BYTES = 0x64;
|
||||
static const uint16_t CMD_PARM_HIGH_TRESH = 0x0012;
|
||||
static const uint16_t CMD_PARM_LOW_TRESH = 0x0021;
|
||||
static const uint16_t CMD_PROTOCOL_VER = 0x0002;
|
||||
static const uint16_t CMD_READ_ABD_PARAM = 0x0008;
|
||||
static const uint16_t CMD_READ_REG_ADDR = 0x0020;
|
||||
static const uint16_t CMD_READ_REGISTER = 0x0002;
|
||||
static const uint16_t CMD_READ_SERIAL_NUM = 0x0011;
|
||||
static const uint16_t CMD_READ_SYS_PARAM = 0x0013;
|
||||
static const uint16_t CMD_READ_VERSION = 0x0000;
|
||||
static const uint8_t CMD_REG_DATA_REPLY_SIZE = 0x02;
|
||||
static const uint16_t CMD_RESTART = 0x0068;
|
||||
static const uint16_t CMD_SYSTEM_MODE = 0x0000;
|
||||
static const uint16_t CMD_SYSTEM_MODE_GR = 0x0003;
|
||||
static const uint16_t CMD_SYSTEM_MODE_MTT = 0x0001;
|
||||
static const uint16_t CMD_SYSTEM_MODE_SIMPLE = 0x0064;
|
||||
static const uint16_t CMD_SYSTEM_MODE_DEBUG = 0x0000;
|
||||
static const uint16_t CMD_SYSTEM_MODE_ENERGY = 0x0004;
|
||||
static const uint16_t CMD_SYSTEM_MODE_VS = 0x0002;
|
||||
static const uint16_t CMD_WRITE_ABD_PARAM = 0x0007;
|
||||
static const uint16_t CMD_WRITE_REGISTER = 0x0001;
|
||||
static const uint16_t CMD_WRITE_SYS_PARAM = 0x0012;
|
||||
|
||||
static const uint8_t LD2420_ERROR_NONE = 0x00;
|
||||
static const uint8_t LD2420_ERROR_TIMEOUT = 0x02;
|
||||
static const uint8_t LD2420_ERROR_UNKNOWN = 0x01;
|
||||
static const uint8_t LD2420_TOTAL_GATES = 16;
|
||||
static const uint8_t CALIBRATE_SAMPLES = 64;
|
||||
|
||||
// Register address values
|
||||
static const uint16_t CMD_MIN_GATE_REG = 0x0000;
|
||||
static const uint16_t CMD_MAX_GATE_REG = 0x0001;
|
||||
static const uint16_t CMD_TIMEOUT_REG = 0x0004;
|
||||
static const uint16_t CMD_GATE_MOVE_THRESH[LD2420_TOTAL_GATES] = {0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015,
|
||||
0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B,
|
||||
0x001C, 0x001D, 0x001E, 0x001F};
|
||||
static const uint16_t CMD_GATE_STILL_THRESH[LD2420_TOTAL_GATES] = {0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025,
|
||||
0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B,
|
||||
0x002C, 0x002D, 0x002E, 0x002F};
|
||||
static const uint32_t FACTORY_MOVE_THRESH[LD2420_TOTAL_GATES] = {60000, 30000, 400, 250, 250, 250, 250, 250,
|
||||
250, 250, 250, 250, 250, 250, 250, 250};
|
||||
static const uint32_t FACTORY_STILL_THRESH[LD2420_TOTAL_GATES] = {40000, 20000, 200, 200, 200, 200, 200, 150,
|
||||
150, 100, 100, 100, 100, 100, 100, 100};
|
||||
static const uint16_t FACTORY_TIMEOUT = 120;
|
||||
static const uint16_t FACTORY_MIN_GATE = 1;
|
||||
static const uint16_t FACTORY_MAX_GATE = 12;
|
||||
|
||||
// COMMAND_BYTE Header & Footer
|
||||
static const uint8_t CMD_FRAME_COMMAND = 6;
|
||||
static const uint8_t CMD_FRAME_DATA_LENGTH = 4;
|
||||
static const uint32_t CMD_FRAME_FOOTER = 0x01020304;
|
||||
static const uint32_t CMD_FRAME_HEADER = 0xFAFBFCFD;
|
||||
static const uint32_t DEBUG_FRAME_FOOTER = 0xFAFBFCFD;
|
||||
static const uint32_t DEBUG_FRAME_HEADER = 0x1410BFAA;
|
||||
static const uint32_t ENERGY_FRAME_FOOTER = 0xF5F6F7F8;
|
||||
static const uint32_t ENERGY_FRAME_HEADER = 0xF1F2F3F4;
|
||||
static const uint8_t CMD_FRAME_STATUS = 7;
|
||||
static const uint8_t CMD_ERROR_WORD = 8;
|
||||
static const uint8_t ENERGY_SENSOR_START = 9;
|
||||
static const uint8_t CALIBRATE_REPORT_INTERVAL = 4;
|
||||
static const int CALIBRATE_VERSION_MIN = 154;
|
||||
static const std::string OP_NORMAL_MODE_STRING = "Normal";
|
||||
static const std::string OP_SIMPLE_MODE_STRING = "Simple";
|
||||
|
||||
enum OpModeStruct : uint8_t { OP_NORMAL_MODE = 1, OP_CALIBRATE_MODE = 2, OP_SIMPLE_MODE = 3 };
|
||||
static const std::map<std::string, uint8_t> OP_MODE_TO_UINT{
|
||||
{"Normal", OP_NORMAL_MODE}, {"Calibrate", OP_CALIBRATE_MODE}, {"Simple", OP_SIMPLE_MODE}};
|
||||
static constexpr const char *ERR_MESSAGE[] = {"None", "Unknown", "Timeout"};
|
||||
|
||||
class LD2420Listener {
|
||||
public:
|
||||
virtual void on_presence(bool presence){};
|
||||
virtual void on_distance(uint16_t distance){};
|
||||
virtual void on_energy(uint16_t *sensor_energy, size_t size){};
|
||||
virtual void on_fw_version(std::string &fw){};
|
||||
};
|
||||
|
||||
class LD2420Component : public Component, public uart::UARTDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
#ifdef USE_SELECT
|
||||
void set_operating_mode_select(select::Select *selector) { this->operating_selector_ = selector; };
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void set_gate_timeout_number(number::Number *number) { this->gate_timeout_number_ = number; };
|
||||
void set_gate_select_number(number::Number *number) { this->gate_select_number_ = number; };
|
||||
void set_min_gate_distance_number(number::Number *number) { this->min_gate_distance_number_ = number; };
|
||||
void set_max_gate_distance_number(number::Number *number) { this->max_gate_distance_number_ = number; };
|
||||
void set_gate_move_sensitivity_factor_number(number::Number *number) {
|
||||
this->gate_move_sensitivity_factor_number_ = number;
|
||||
};
|
||||
void set_gate_still_sensitivity_factor_number(number::Number *number) {
|
||||
this->gate_still_sensitivity_factor_number_ = number;
|
||||
};
|
||||
void set_gate_still_threshold_numbers(int gate, number::Number *n) { this->gate_still_threshold_numbers_[gate] = n; };
|
||||
void set_gate_move_threshold_numbers(int gate, number::Number *n) { this->gate_move_threshold_numbers_[gate] = n; };
|
||||
bool is_gate_select() { return gate_select_number_ != nullptr; };
|
||||
uint8_t get_gate_select_value() { return static_cast<uint8_t>(this->gate_select_number_->state); };
|
||||
float get_min_gate_distance_value() { return min_gate_distance_number_->state; };
|
||||
float get_max_gate_distance_value() { return max_gate_distance_number_->state; };
|
||||
void publish_gate_move_threshold(uint8_t gate) {
|
||||
// With gate_select we only use 1 number pointer, thus we hard code [0]
|
||||
this->gate_move_threshold_numbers_[0]->publish_state(this->new_config.move_thresh[gate]);
|
||||
};
|
||||
void publish_gate_still_threshold(uint8_t gate) {
|
||||
this->gate_still_threshold_numbers_[0]->publish_state(this->new_config.still_thresh[gate]);
|
||||
};
|
||||
void init_gate_config_numbers();
|
||||
void refresh_gate_config_numbers();
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
void set_apply_config_button(button::Button *button) { this->apply_config_button_ = button; };
|
||||
void set_revert_config_button(button::Button *button) { this->revert_config_button_ = button; };
|
||||
void set_restart_module_button(button::Button *button) { this->restart_module_button_ = button; };
|
||||
void set_factory_reset_button(button::Button *button) { this->factory_reset_button_ = button; };
|
||||
#endif
|
||||
void register_listener(LD2420Listener *listener) { this->listeners_.push_back(listener); }
|
||||
|
||||
struct CmdFrameT {
|
||||
uint32_t header{0};
|
||||
uint16_t length{0};
|
||||
uint16_t command{0};
|
||||
uint8_t data[18];
|
||||
uint16_t data_length{0};
|
||||
uint32_t footer{0};
|
||||
};
|
||||
|
||||
struct RegConfigT {
|
||||
uint16_t min_gate{0};
|
||||
uint16_t max_gate{0};
|
||||
uint16_t timeout{0};
|
||||
uint32_t move_thresh[LD2420_TOTAL_GATES];
|
||||
uint32_t still_thresh[LD2420_TOTAL_GATES];
|
||||
};
|
||||
|
||||
void send_module_restart();
|
||||
void restart_module_action();
|
||||
void apply_config_action();
|
||||
void factory_reset_action();
|
||||
void revert_config_action();
|
||||
float get_setup_priority() const override;
|
||||
int send_cmd_from_array(CmdFrameT cmd_frame);
|
||||
void report_gate_data();
|
||||
void handle_cmd_error(uint8_t error);
|
||||
void set_operating_mode(const std::string &state);
|
||||
void auto_calibrate_sensitivity();
|
||||
void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number);
|
||||
uint8_t calc_checksum(void *data, size_t size);
|
||||
|
||||
RegConfigT current_config;
|
||||
RegConfigT new_config;
|
||||
int32_t last_periodic_millis = millis();
|
||||
int32_t report_periodic_millis = millis();
|
||||
int32_t monitor_periodic_millis = millis();
|
||||
int32_t last_normal_periodic_millis = millis();
|
||||
bool output_energy_state{false};
|
||||
uint8_t current_operating_mode{OP_NORMAL_MODE};
|
||||
uint16_t radar_data[LD2420_TOTAL_GATES][CALIBRATE_SAMPLES];
|
||||
uint16_t gate_avg[LD2420_TOTAL_GATES];
|
||||
uint16_t gate_peak[LD2420_TOTAL_GATES];
|
||||
uint8_t sample_number_counter{0};
|
||||
uint16_t total_sample_number_counter{0};
|
||||
float gate_move_sensitivity_factor{0.5};
|
||||
float gate_still_sensitivity_factor{0.5};
|
||||
#ifdef USE_SELECT
|
||||
select::Select *operating_selector_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
button::Button *apply_config_button_{nullptr};
|
||||
button::Button *revert_config_button_{nullptr};
|
||||
button::Button *restart_module_button_{nullptr};
|
||||
button::Button *factory_reset_button_{nullptr};
|
||||
#endif
|
||||
void set_min_max_distances_timeout(uint32_t max_gate_distance, uint32_t min_gate_distance, uint32_t timeout);
|
||||
void set_gate_threshold(uint8_t gate);
|
||||
void set_reg_value(uint16_t reg, uint16_t value);
|
||||
uint8_t set_config_mode(bool enable);
|
||||
void set_system_mode(uint16_t mode);
|
||||
void ld2420_restart();
|
||||
|
||||
protected:
|
||||
struct CmdReplyT {
|
||||
uint8_t command;
|
||||
uint8_t status;
|
||||
uint32_t data[4];
|
||||
uint8_t length;
|
||||
uint16_t error;
|
||||
volatile bool ack;
|
||||
};
|
||||
|
||||
int get_firmware_int_(const char *version_string);
|
||||
void get_firmware_version_();
|
||||
int get_gate_threshold_(uint8_t gate);
|
||||
void get_reg_value_(uint16_t reg);
|
||||
int get_min_max_distances_timeout_();
|
||||
uint16_t get_mode_() { return this->system_mode_; };
|
||||
void set_mode_(uint16_t mode) { this->system_mode_ = mode; };
|
||||
bool get_presence_() { return this->presence_; };
|
||||
void set_presence_(bool presence) { this->presence_ = presence; };
|
||||
uint16_t get_distance_() { return this->distance_; };
|
||||
void set_distance_(uint16_t distance) { this->distance_ = distance; };
|
||||
bool get_cmd_active_() { return this->cmd_active_; };
|
||||
void set_cmd_active_(bool active) { this->cmd_active_ = active; };
|
||||
void handle_simple_mode_(const uint8_t *inbuf, int len);
|
||||
void handle_energy_mode_(uint8_t *buffer, int len);
|
||||
void handle_ack_data_(uint8_t *buffer, int len);
|
||||
void readline_(int rx_data, uint8_t *buffer, int len);
|
||||
void set_calibration_(bool state) { this->calibration_ = state; };
|
||||
bool get_calibration_() { return this->calibration_; };
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
number::Number *gate_timeout_number_{nullptr};
|
||||
number::Number *gate_select_number_{nullptr};
|
||||
number::Number *min_gate_distance_number_{nullptr};
|
||||
number::Number *max_gate_distance_number_{nullptr};
|
||||
number::Number *gate_move_sensitivity_factor_number_{nullptr};
|
||||
number::Number *gate_still_sensitivity_factor_number_{nullptr};
|
||||
std::vector<number::Number *> gate_still_threshold_numbers_ = std::vector<number::Number *>(16);
|
||||
std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(16);
|
||||
#endif
|
||||
|
||||
uint16_t gate_energy_[LD2420_TOTAL_GATES];
|
||||
CmdReplyT cmd_reply_;
|
||||
uint32_t timeout_;
|
||||
uint32_t max_distance_gate_;
|
||||
uint32_t min_distance_gate_;
|
||||
uint16_t system_mode_{CMD_SYSTEM_MODE_ENERGY};
|
||||
bool cmd_active_{false};
|
||||
char ld2420_firmware_ver_[8];
|
||||
bool presence_{false};
|
||||
bool calibration_{false};
|
||||
uint16_t distance_{0};
|
||||
uint8_t config_checksum_{0};
|
||||
std::vector<LD2420Listener *> listeners_{};
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
183
esphome/components/ld2420/number/__init__.py
Normal file
183
esphome/components/ld2420/number/__init__.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
UNIT_SECOND,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ICON_MOTION_SENSOR,
|
||||
ICON_TIMELAPSE,
|
||||
ICON_SCALE,
|
||||
)
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
|
||||
LD2420TimeoutNumber = ld2420_ns.class_("LD2420TimeoutNumber", number.Number)
|
||||
LD2420MoveSensFactorNumber = ld2420_ns.class_(
|
||||
"LD2420MoveSensFactorNumber", number.Number
|
||||
)
|
||||
LD2420StillSensFactorNumber = ld2420_ns.class_(
|
||||
"LD2420StillSensFactorNumber", number.Number
|
||||
)
|
||||
LD2420MinDistanceNumber = ld2420_ns.class_("LD2420MinDistanceNumber", number.Number)
|
||||
LD2420MaxDistanceNumber = ld2420_ns.class_("LD2420MaxDistanceNumber", number.Number)
|
||||
LD2420GateSelectNumber = ld2420_ns.class_("LD2420GateSelectNumber", number.Number)
|
||||
LD2420MoveThresholdNumbers = ld2420_ns.class_(
|
||||
"LD2420MoveThresholdNumbers", number.Number
|
||||
)
|
||||
LD2420StillThresholdNumbers = ld2420_ns.class_(
|
||||
"LD2420StillThresholdNumbers", number.Number
|
||||
)
|
||||
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
|
||||
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
|
||||
CONF_STILL_THRESHOLD = "still_threshold"
|
||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
||||
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
|
||||
CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity"
|
||||
CONF_GATE_SELECT = "gate_select"
|
||||
CONF_PRESENCE_TIMEOUT = "presence_timeout"
|
||||
GATE_GROUP = "gate_group"
|
||||
TIMEOUT_GROUP = "timeout_group"
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Inclusive(CONF_PRESENCE_TIMEOUT, TIMEOUT_GROUP): number.number_schema(
|
||||
LD2420TimeoutNumber,
|
||||
unit_of_measurement=UNIT_SECOND,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_TIMELAPSE,
|
||||
),
|
||||
cv.Inclusive(CONF_MIN_GATE_DISTANCE, TIMEOUT_GROUP): number.number_schema(
|
||||
LD2420MinDistanceNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_MAX_GATE_DISTANCE, TIMEOUT_GROUP): number.number_schema(
|
||||
LD2420MaxDistanceNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_GATE_SELECT, GATE_GROUP): number.number_schema(
|
||||
LD2420GateSelectNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_STILL_THRESHOLD, GATE_GROUP): number.number_schema(
|
||||
LD2420StillThresholdNumbers,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Inclusive(CONF_MOVE_THRESHOLD, GATE_GROUP): number.number_schema(
|
||||
LD2420MoveThresholdNumbers,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Optional(CONF_GATE_MOVE_SENSITIVITY): number.number_schema(
|
||||
LD2420MoveSensFactorNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
),
|
||||
cv.Optional(CONF_GATE_STILL_SENSITIVITY): number.number_schema(
|
||||
LD2420StillSensFactorNumber,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_SCALE,
|
||||
),
|
||||
}
|
||||
)
|
||||
CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(f"gate_{x}"): (
|
||||
{
|
||||
cv.Required(CONF_MOVE_THRESHOLD): number.number_schema(
|
||||
LD2420MoveThresholdNumbers,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
cv.Required(CONF_STILL_THRESHOLD): number.number_schema(
|
||||
LD2420StillThresholdNumbers,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_MOTION_SENSOR,
|
||||
),
|
||||
}
|
||||
)
|
||||
for x in range(16)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
LD2420_component = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
if gate_timeout_config := config.get(CONF_PRESENCE_TIMEOUT):
|
||||
n = await number.new_number(
|
||||
gate_timeout_config, min_value=0, max_value=255, step=5
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_timeout_number(n))
|
||||
if min_distance_gate_config := config.get(CONF_MIN_GATE_DISTANCE):
|
||||
n = await number.new_number(
|
||||
min_distance_gate_config, min_value=0, max_value=15, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_min_gate_distance_number(n))
|
||||
if max_distance_gate_config := config.get(CONF_MAX_GATE_DISTANCE):
|
||||
n = await number.new_number(
|
||||
max_distance_gate_config, min_value=1, max_value=15, step=1
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_max_gate_distance_number(n))
|
||||
if gate_move_sensitivity_config := config.get(CONF_GATE_MOVE_SENSITIVITY):
|
||||
n = await number.new_number(
|
||||
gate_move_sensitivity_config, min_value=0.05, max_value=1, step=0.025
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_move_sensitivity_factor_number(n))
|
||||
if gate_still_sensitivity_config := config.get(CONF_GATE_STILL_SENSITIVITY):
|
||||
n = await number.new_number(
|
||||
gate_still_sensitivity_config, min_value=0.05, max_value=1, step=0.025
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_still_sensitivity_factor_number(n))
|
||||
if config.get(CONF_GATE_SELECT):
|
||||
if gate_number := config.get(CONF_GATE_SELECT):
|
||||
n = await number.new_number(gate_number, min_value=0, max_value=15, step=1)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_select_number(n))
|
||||
if gate_still_threshold := config.get(CONF_STILL_THRESHOLD):
|
||||
n = cg.new_Pvariable(gate_still_threshold[CONF_ID])
|
||||
await number.register_number(
|
||||
n, gate_still_threshold, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_still_threshold_numbers(0, n))
|
||||
if gate_move_threshold := config.get(CONF_MOVE_THRESHOLD):
|
||||
n = cg.new_Pvariable(gate_move_threshold[CONF_ID])
|
||||
await number.register_number(
|
||||
n, gate_move_threshold, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_move_threshold_numbers(0, n))
|
||||
else:
|
||||
for x in range(16):
|
||||
if gate_conf := config.get(f"gate_{x}"):
|
||||
move_config = gate_conf[CONF_MOVE_THRESHOLD]
|
||||
n = cg.new_Pvariable(move_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, move_config, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_move_threshold_numbers(x, n))
|
||||
|
||||
still_config = gate_conf[CONF_STILL_THRESHOLD]
|
||||
n = cg.new_Pvariable(still_config[CONF_ID], x)
|
||||
await number.register_number(
|
||||
n, still_config, min_value=0, max_value=65535, step=25
|
||||
)
|
||||
await cg.register_parented(n, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_gate_still_threshold_numbers(x, n))
|
73
esphome/components/ld2420/number/gate_config_number.cpp
Normal file
73
esphome/components/ld2420/number/gate_config_number.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "gate_config_number.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
static const char *const TAG = "LD2420.number";
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
void LD2420TimeoutNumber::control(float timeout) {
|
||||
this->publish_state(timeout);
|
||||
this->parent_->new_config.timeout = timeout;
|
||||
}
|
||||
|
||||
void LD2420MinDistanceNumber::control(float min_gate) {
|
||||
if ((uint16_t) min_gate > this->parent_->new_config.max_gate) {
|
||||
min_gate = this->parent_->get_min_gate_distance_value();
|
||||
} else {
|
||||
this->parent_->new_config.min_gate = (uint16_t) min_gate;
|
||||
}
|
||||
this->publish_state(min_gate);
|
||||
}
|
||||
|
||||
void LD2420MaxDistanceNumber::control(float max_gate) {
|
||||
if ((uint16_t) max_gate < this->parent_->new_config.min_gate) {
|
||||
max_gate = this->parent_->get_max_gate_distance_value();
|
||||
} else {
|
||||
this->parent_->new_config.max_gate = (uint16_t) max_gate;
|
||||
}
|
||||
this->publish_state(max_gate);
|
||||
}
|
||||
|
||||
void LD2420GateSelectNumber::control(float gate_select) {
|
||||
const uint8_t gate = (uint8_t) gate_select;
|
||||
this->publish_state(gate_select);
|
||||
this->parent_->publish_gate_move_threshold(gate);
|
||||
this->parent_->publish_gate_still_threshold(gate);
|
||||
}
|
||||
|
||||
void LD2420MoveSensFactorNumber::control(float move_factor) {
|
||||
this->publish_state(move_factor);
|
||||
this->parent_->gate_move_sensitivity_factor = move_factor;
|
||||
}
|
||||
|
||||
void LD2420StillSensFactorNumber::control(float still_factor) {
|
||||
this->publish_state(still_factor);
|
||||
this->parent_->gate_still_sensitivity_factor = still_factor;
|
||||
}
|
||||
|
||||
LD2420MoveThresholdNumbers::LD2420MoveThresholdNumbers(uint8_t gate) : gate_(gate) {}
|
||||
|
||||
void LD2420MoveThresholdNumbers::control(float move_threshold) {
|
||||
this->publish_state(move_threshold);
|
||||
if (!this->parent_->is_gate_select()) {
|
||||
this->parent_->new_config.move_thresh[this->gate_] = move_threshold;
|
||||
} else {
|
||||
this->parent_->new_config.move_thresh[this->parent_->get_gate_select_value()] = move_threshold;
|
||||
}
|
||||
}
|
||||
|
||||
LD2420StillThresholdNumbers::LD2420StillThresholdNumbers(uint8_t gate) : gate_(gate) {}
|
||||
|
||||
void LD2420StillThresholdNumbers::control(float still_threshold) {
|
||||
this->publish_state(still_threshold);
|
||||
if (!this->parent_->is_gate_select()) {
|
||||
this->parent_->new_config.still_thresh[this->gate_] = still_threshold;
|
||||
} else {
|
||||
this->parent_->new_config.still_thresh[this->parent_->get_gate_select_value()] = still_threshold;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
78
esphome/components/ld2420/number/gate_config_number.h
Normal file
78
esphome/components/ld2420/number/gate_config_number.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "../ld2420.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420TimeoutNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420TimeoutNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float timeout) override;
|
||||
};
|
||||
|
||||
class LD2420MinDistanceNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MinDistanceNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float min_gate) override;
|
||||
};
|
||||
|
||||
class LD2420MaxDistanceNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MaxDistanceNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float max_gate) override;
|
||||
};
|
||||
|
||||
class LD2420GateSelectNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420GateSelectNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float gate_select) override;
|
||||
};
|
||||
|
||||
class LD2420MoveSensFactorNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MoveSensFactorNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float move_factor) override;
|
||||
};
|
||||
|
||||
class LD2420StillSensFactorNumber : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420StillSensFactorNumber() = default;
|
||||
|
||||
protected:
|
||||
void control(float still_factor) override;
|
||||
};
|
||||
|
||||
class LD2420StillThresholdNumbers : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420StillThresholdNumbers() = default;
|
||||
LD2420StillThresholdNumbers(uint8_t gate);
|
||||
|
||||
protected:
|
||||
uint8_t gate_;
|
||||
void control(float still_threshold) override;
|
||||
};
|
||||
|
||||
class LD2420MoveThresholdNumbers : public number::Number, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420MoveThresholdNumbers() = default;
|
||||
LD2420MoveThresholdNumbers(uint8_t gate);
|
||||
|
||||
protected:
|
||||
uint8_t gate_;
|
||||
void control(float move_threshold) override;
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
33
esphome/components/ld2420/select/__init__.py
Normal file
33
esphome/components/ld2420/select/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG
|
||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||
|
||||
CONF_OPERATING_MODE = "operating_mode"
|
||||
CONF_SELECTS = [
|
||||
"Normal",
|
||||
"Calibrate",
|
||||
"Simple",
|
||||
]
|
||||
|
||||
LD2420Select = ld2420_ns.class_("LD2420Select", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Required(CONF_OPERATING_MODE): select.select_schema(
|
||||
LD2420Select,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
LD2420_component = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
if operating_mode_config := config.get(CONF_OPERATING_MODE):
|
||||
sel = await select.new_select(
|
||||
operating_mode_config,
|
||||
options=[CONF_SELECTS],
|
||||
)
|
||||
await cg.register_parented(sel, config[CONF_LD2420_ID])
|
||||
cg.add(LD2420_component.set_operating_mode_select(sel))
|
16
esphome/components/ld2420/select/operating_mode_select.cpp
Normal file
16
esphome/components/ld2420/select/operating_mode_select.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "operating_mode_select.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
static const char *const TAG = "LD2420.select";
|
||||
|
||||
void LD2420Select::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_operating_mode(value);
|
||||
}
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
18
esphome/components/ld2420/select/operating_mode_select.h
Normal file
18
esphome/components/ld2420/select/operating_mode_select.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "../ld2420.h"
|
||||
#include "esphome/components/select/select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420Select : public Component, public select::Select, public Parented<LD2420Component> {
|
||||
public:
|
||||
LD2420Select() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
35
esphome/components/ld2420/sensor/__init__.py
Normal file
35
esphome/components/ld2420/sensor/__init__.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import CONF_ID, DEVICE_CLASS_DISTANCE, UNIT_CENTIMETER
|
||||
from .. import ld2420_ns, LD2420Component, CONF_LD2420_ID
|
||||
|
||||
LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component)
|
||||
|
||||
CONF_MOVING_DISTANCE = "moving_distance"
|
||||
CONF_GATE_ENERGY = "gate_energy"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LD2420Sensor),
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
|
||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
if CONF_MOVING_DISTANCE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE])
|
||||
cg.add(var.set_distance_sensor(sens))
|
||||
if CONF_GATE_ENERGY in config:
|
||||
sens = await sensor.new_sensor(config[CONF_GATE_ENERGY])
|
||||
cg.add(var.set_energy_sensor(sens))
|
||||
ld2420 = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
cg.add(ld2420.register_listener(var))
|
16
esphome/components/ld2420/sensor/ld2420_sensor.cpp
Normal file
16
esphome/components/ld2420/sensor/ld2420_sensor.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "ld2420_sensor.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
static const char *const TAG = "LD2420.sensor";
|
||||
|
||||
void LD2420Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LD2420 Sensor:");
|
||||
LOG_SENSOR(" ", "Distance", this->distance_sensor_);
|
||||
}
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
34
esphome/components/ld2420/sensor/ld2420_sensor.h
Normal file
34
esphome/components/ld2420/sensor/ld2420_sensor.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "../ld2420.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420Sensor : public LD2420Listener, public Component, sensor::Sensor {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_distance_sensor(sensor::Sensor *sensor) { this->distance_sensor_ = sensor; }
|
||||
void on_distance(uint16_t distance) override {
|
||||
if (this->distance_sensor_ != nullptr) {
|
||||
if (this->distance_sensor_->get_state() != distance) {
|
||||
this->distance_sensor_->publish_state(distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
void on_energy(uint16_t *gate_energy, size_t size) override {
|
||||
for (size_t active = 0; active < size; active++) {
|
||||
if (this->energy_sensors_[active] != nullptr) {
|
||||
this->energy_sensors_[active]->publish_state(gate_energy[active]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
sensor::Sensor *distance_sensor_{nullptr};
|
||||
std::vector<sensor::Sensor *> energy_sensors_ = std::vector<sensor::Sensor *>(LD2420_TOTAL_GATES);
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
38
esphome/components/ld2420/text_sensor/__init__.py
Normal file
38
esphome/components/ld2420/text_sensor/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_CHIP,
|
||||
)
|
||||
|
||||
from .. import ld2420_ns, LD2420Component, CONF_LD2420_ID
|
||||
|
||||
LD2420TextSensor = ld2420_ns.class_(
|
||||
"LD2420TextSensor", text_sensor.TextSensor, cg.Component
|
||||
)
|
||||
|
||||
CONF_FW_VERSION = "fw_version"
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(LD2420TextSensor),
|
||||
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
|
||||
cv.Optional(CONF_FW_VERSION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
if CONF_FW_VERSION in config:
|
||||
sens = await text_sensor.new_text_sensor(config[CONF_FW_VERSION])
|
||||
cg.add(var.set_fw_version_text_sensor(sens))
|
||||
ld2420 = await cg.get_variable(config[CONF_LD2420_ID])
|
||||
cg.add(ld2420.register_listener(var))
|
16
esphome/components/ld2420/text_sensor/text_sensor.cpp
Normal file
16
esphome/components/ld2420/text_sensor/text_sensor.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "text_sensor.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
static const char *const TAG = "LD2420.text_sensor";
|
||||
|
||||
void LD2420TextSensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LD2420 TextSensor:");
|
||||
LOG_TEXT_SENSOR(" ", "Firmware", this->fw_version_text_sensor_);
|
||||
}
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
24
esphome/components/ld2420/text_sensor/text_sensor.h
Normal file
24
esphome/components/ld2420/text_sensor/text_sensor.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "../ld2420.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ld2420 {
|
||||
|
||||
class LD2420TextSensor : public LD2420Listener, public Component, text_sensor::TextSensor {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_fw_version_text_sensor(text_sensor::TextSensor *tsensor) { this->fw_version_text_sensor_ = tsensor; };
|
||||
void on_fw_version(std::string &fw) override {
|
||||
if (this->fw_version_text_sensor_ != nullptr) {
|
||||
this->fw_version_text_sensor_->publish_state(fw);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
text_sensor::TextSensor *fw_version_text_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace ld2420
|
||||
} // namespace esphome
|
|
@ -224,6 +224,12 @@ uart:
|
|||
tx_pin: 14
|
||||
rx_pin: 27
|
||||
baud_rate: 115200
|
||||
- id: ld2420_uart
|
||||
tx_pin: 17
|
||||
rx_pin: 16
|
||||
baud_rate: 115200
|
||||
parity: NONE
|
||||
stop_bits: 1
|
||||
- id: gcja5_uart
|
||||
rx_pin: GPIO10
|
||||
parity: EVEN
|
||||
|
@ -1441,6 +1447,9 @@ sensor:
|
|||
still_energy:
|
||||
name: g8 still energy
|
||||
|
||||
- platform: ld2420
|
||||
moving_distance:
|
||||
name: "Moving distance (cm)"
|
||||
- platform: sen21231
|
||||
name: "Person Sensor"
|
||||
i2c_id: i2c_bus
|
||||
|
@ -3615,6 +3624,10 @@ ld2410:
|
|||
id: my_ld2410
|
||||
uart_id: ld2410_uart
|
||||
|
||||
ld2420:
|
||||
id: my_ld2420
|
||||
uart_id: ld2420_uart
|
||||
|
||||
lcd_menu:
|
||||
display_id: my_lcd_gpio
|
||||
mark_back: 0x5e
|
||||
|
|
Loading…
Reference in a new issue