esphome/esphome/components/ld2450/ld2450.h

282 lines
9.9 KiB
C++

#pragma once
#include <iomanip>
#include <map>
#include <sstream>
#include "esphome/components/uart/uart.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_NUMBER
#include "esphome/components/number/number.h"
#endif
#ifdef USE_SWITCH
#include "esphome/components/switch/switch.h"
#endif
#ifdef USE_BUTTON
#include "esphome/components/button/button.h"
#endif
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
#ifdef USE_TEXT_SENSOR
#include "esphome/components/text_sensor/text_sensor.h"
#endif
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
#ifndef M_PI
#define M_PI 3.14
#endif
namespace esphome {
namespace ld2450 {
// Constants
static const uint16_t START_DELAY = 5000; // Sensor startup delay 5 sec.
static const uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec.
static const uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
static const uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
// Target coordinate struct
struct Target {
int16_t x;
int16_t y;
bool is_moving;
};
// Zone coordinate struct
struct Zone {
int16_t x1 = 0;
int16_t y1 = 0;
int16_t x2 = 0;
int16_t y2 = 0;
};
// LD2450 UART Serial Commands
static const uint8_t CMD_ENABLE_CONF = 0x00FF;
static const uint8_t CMD_DISABLE_CONF = 0x00FE;
static const uint8_t CMD_VERSION = 0x00A0;
static const uint8_t CMD_MAC = 0x00A5;
static const uint8_t CMD_RESET = 0x00A2;
static const uint8_t CMD_RESTART = 0x00A3;
static const uint8_t CMD_BLUETOOTH = 0x00A4;
static const uint8_t CMD_SINGLE_TARGET = 0x0080;
static const uint8_t CMD_MULTI_TARGET = 0x0090;
static const uint8_t CMD_SET_BAUD_RATE = 0x00A1;
static const uint8_t CMD_QUERY_ZONE = 0x00C1;
static const uint8_t CMD_SET_ZONE = 0x00C2;
enum BaudRateStructure : uint8_t {
BAUD_RATE_9600 = 1,
BAUD_RATE_19200 = 2,
BAUD_RATE_38400 = 3,
BAUD_RATE_57600 = 4,
BAUD_RATE_115200 = 5,
BAUD_RATE_230400 = 6,
BAUD_RATE_256000 = 7,
BAUD_RATE_460800 = 8
};
// Convert Baud Rate enum to int
static const std::map<std::string, uint8_t> BAUD_RATE_ENUM_TO_INT{
{"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400},
{"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400},
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800}};
// Zone Type struct
enum ZoneTypeStructure : uint8_t { ZONE_DISABLED = 0, ZONE_DETECTION = 1, ZONE_FILTER = 2 };
// Convert Zone Type int to Enum
static const std::map<ZoneTypeStructure, std::string> ZONE_TYPE_INT_TO_ENUM{
{ZONE_DISABLED, "Disabled"}, {ZONE_DETECTION, "Detection"}, {ZONE_FILTER, "Filter"}};
// Convert Zone Type enum to int
static const std::map<std::string, uint8_t> ZONE_TYPE_ENUM_TO_INT{
{"Disabled", ZONE_DISABLED}, {"Detection", ZONE_DETECTION}, {"Filter", ZONE_FILTER}};
// LD2450 UART 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};
enum PeriodicDataStructure : uint8_t {
TARGET_X = 4,
TARGET_Y = 6,
TARGET_SPEED = 8,
TARGET_RESOLUTION = 10,
};
enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
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
SUB_BINARY_SENSOR(target)
SUB_BINARY_SENSOR(moving_target)
SUB_BINARY_SENSOR(still_target)
#endif
#ifdef USE_TEXT_SENSOR
SUB_TEXT_SENSOR(version)
SUB_TEXT_SENSOR(mac)
#endif
#ifdef USE_SELECT
SUB_SELECT(baud_rate)
SUB_SELECT(zone_type)
#endif
#ifdef USE_SWITCH
SUB_SWITCH(bluetooth)
SUB_SWITCH(multi_target)
#endif
#ifdef USE_BUTTON
SUB_BUTTON(reset)
SUB_BUTTON(restart)
#endif
#ifdef USE_NUMBER
SUB_NUMBER(presence_timeout)
#endif
public:
LD2450Component();
void setup() override;
void dump_config() override;
void loop() override;
void set_presence_timeout();
void set_throttle(uint16_t value) { this->throttle_ = value; };
void read_all_info();
void query_zone_info();
void restart_and_read_all_info();
void set_bluetooth(bool enable);
void set_multi_target(bool enable);
void set_baud_rate(const std::string &state);
void set_zone_type(const std::string &state);
void publish_zone_type();
void factory_reset();
uint16_t convert_seconds_to_ms(uint16_t value) { return value * 1000; };
#ifdef USE_TEXT_SENSOR
void set_direction_text_sensor(int target, text_sensor::TextSensor *s);
#endif
#ifdef USE_NUMBER
void set_zone_coordinate(uint8_t zone);
void set_zone_x1_number(int zone, number::Number *n);
void set_zone_y1_number(int zone, number::Number *n);
void set_zone_x2_number(int zone, number::Number *n);
void set_zone_y2_number(int zone, number::Number *n);
#endif
#ifdef USE_SENSOR
void set_move_x_sensor(int target, sensor::Sensor *s);
void set_move_y_sensor(int target, sensor::Sensor *s);
void set_move_speed_sensor(int target, sensor::Sensor *s);
void set_move_angle_sensor(int target, sensor::Sensor *s);
void set_move_distance_sensor(int target, sensor::Sensor *s);
void set_move_resolution_sensor(int target, sensor::Sensor *s);
void set_zone_target_count_sensor(int zone, sensor::Sensor *s);
void set_zone_still_target_count_sensor(int zone, sensor::Sensor *s);
void set_zone_moving_target_count_sensor(int zone, sensor::Sensor *s);
#endif
void reset_radar_zone();
void set_radar_zone(int32_t zone_type, int32_t zone1_x1, int32_t zone1_y1, int32_t zone1_x2, int32_t zone1_y2,
int32_t zone2_x1, int32_t zone2_y1, int32_t zone2_x2, int32_t zone2_y2, int32_t zone3_x1,
int32_t zone3_y1, int32_t zone3_x2, int32_t zone3_y2);
protected:
ESPPreferenceObject pref_;
int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len);
void set_config_mode_(bool enable);
void handle_periodic_data_(uint8_t *buffer, int len);
bool handle_ack_data_(uint8_t *buffer, int len);
void process_zone_(uint8_t *buffer);
void readline_(int readch, uint8_t *buffer, int len);
void get_version_();
void get_mac_();
void query_zone_();
void restart_();
void send_set_zone_command_();
void convert_int_values_to_hex_(const int *values, uint8_t *bytes);
void save_to_flash_(float value);
float restore_from_flash_();
Target target_info_[MAX_TARGETS];
Zone zone_config_[MAX_ZONES];
int16_t decode_coordinate_(uint8_t low_byte, uint8_t high_byte) {
int16_t coordinate = (high_byte & 0x7F) << 8 | low_byte;
if ((high_byte & 0x80) == 0)
coordinate = -coordinate;
return coordinate; // mm
}
int16_t decode_speed_(uint8_t low_byte, uint8_t high_byte) {
int16_t speed = (high_byte & 0x7F) << 8 | low_byte;
return speed * 10; // mm/s
}
std::string convert_signed_int_to_hex_(int value) {
std::stringstream ss;
ss << std::hex << std::setw(4) << std::setfill('0') << (value & 0xFFFF);
return ss.str();
}
int16_t hex_to_signed_int_(const uint8_t *buffer, uint8_t offset) {
uint16_t hex_val = (buffer[offset + 1] << 8) | buffer[offset];
int16_t dec_val = static_cast<int16_t>(hex_val);
if (dec_val & 0x8000)
dec_val -= 65536;
return dec_val;
}
float calculate_angle_(float base, float hypotenuse) {
if (base < 0.0 || hypotenuse <= 0.0)
return 0.0;
float angle_radians = std::acos(base / hypotenuse);
float angle_degrees = angle_radians * (180.0 / M_PI);
return angle_degrees;
}
std::string get_direction_(int16_t speed) {
if (speed > 0)
return "Moving away";
if (speed < 0)
return "Coming closer";
return "Stationary";
}
int32_t uptime_millis_ = millis();
int32_t last_periodic_millis_ = millis();
int32_t presence_millis_ = 0;
int32_t still_presence_millis_ = 0;
int32_t moving_presence_millis_ = 0;
uint16_t throttle_;
uint16_t timeout_ = 5;
uint8_t zone_type_ = 0;
std::string version_;
std::string mac_;
bool get_timeout_status_(int32_t check_millis);
uint8_t count_targets_in_zone_(const Zone &zone, bool is_moving);
#ifdef USE_TEXT_SENSOR
std::vector<text_sensor::TextSensor *> direction_text_sensors_ = std::vector<text_sensor::TextSensor *>(3);
#endif
#ifdef USE_NUMBER
std::vector<number::Number *> zone_x1_numbers_ = std::vector<number::Number *>(MAX_ZONES);
std::vector<number::Number *> zone_y1_numbers_ = std::vector<number::Number *>(MAX_ZONES);
std::vector<number::Number *> zone_x2_numbers_ = std::vector<number::Number *>(MAX_ZONES);
std::vector<number::Number *> zone_y2_numbers_ = std::vector<number::Number *>(MAX_ZONES);
#endif
#ifdef USE_SENSOR
std::vector<sensor::Sensor *> move_x_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> move_y_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> move_speed_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> move_angle_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> move_distance_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> move_resolution_sensors_ = std::vector<sensor::Sensor *>(MAX_TARGETS);
std::vector<sensor::Sensor *> zone_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
std::vector<sensor::Sensor *> zone_still_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
std::vector<sensor::Sensor *> zone_moving_target_count_sensors_ = std::vector<sensor::Sensor *>(MAX_ZONES);
#endif
};
} // namespace ld2450
} // namespace esphome