#ifndef LD2420_RADAR_H
#define LD2420_RADAR_H
#include <Arduino.h>
/*
Перечисление направления движения цели относительно радара.
Здесь мы оцениваем только радиальную составляющую:
- approaching -> объект приближается к радару
- leaving -> объект удаляется от радара
- static -> заметного движения по дистанции нет
- unknown -> данных недостаточно
*/
enum RadarDirection {
RADAR_DIRECTION_UNKNOWN = 0,
RADAR_DIRECTION_STATIC,
RADAR_DIRECTION_APPROACHING,
RADAR_DIRECTION_LEAVING
};
/*
Упрощённый статус сенсорного слоя.
online:
Есть ли признак того, что UART от радара вообще живой.
stale:
Данные давно не обновлялись, значит состояние устарело.
presence:
Отфильтрованное присутствие с удержанием presence_hold_ms.
raw_presence:
Сырое присутствие из последних сообщений ON/OFF/Range.
raw_distance_zone:
Какой Range пришёл от радара напрямую.
median_distance_zone:
После медианного фильтра по окну последних измерений.
filtered_distance_zone:
После ограничения скачка между соседними измерениями.
distance_m:
Отфильтрованная и откалиброванная дистанция в метрах.
radial_speed_m_s:
Оценка радиальной скорости в м/с.
activity_score:
Грубая активность 0..10 на основе модуля скорости.
confidence:
Оценка качества текущего состояния сенсора, а не вероятность
обнаружения человека.
state_json_cache:
Кэш JSON-строки. Формируется по запросу через get_state_json().
*/
struct RadarState {
uint32_t timestamp_ms = 0;
bool online = false;
bool stale = true;
bool presence = false;
bool raw_presence = false;
uint16_t raw_distance_zone = 0;
uint16_t median_distance_zone = 0;
uint16_t filtered_distance_zone = 0;
float distance_m = -1.0f;
float radial_speed_m_s = 0.0f;
RadarDirection direction = RADAR_DIRECTION_UNKNOWN;
uint8_t activity_score = 0;
float confidence = 0.0f;
uint32_t last_on_ms = 0;
uint32_t last_off_ms = 0;
uint32_t last_range_ms = 0;
uint32_t last_update_ms = 0;
uint32_t line_counter = 0;
uint32_t parse_error_counter = 0;
uint32_t ignored_range_counter = 0;
String state_json_cache;
};
/*
Настройки драйвера радара.
uart_rx_pin / uart_tx_pin:
Пины ESP32, на которые заведён UART радара.
baud_rate:
Скорость UART. Для твоего экземпляра сейчас 115200.
presence_hold_ms:
Сколько миллисекунд удерживать presence=true после исчезновения
сырых сообщений о присутствии.
stale_after_ms:
Через сколько миллисекунд без данных считать сенсор stale.
median_window_size:
Размер окна медианного фильтра.
Для простоты поддерживаются значения до 9.
max_zone_step_per_sample:
Максимально допустимый скачок filtered_distance_zone за один шаг.
Если медиана прыгнула сильнее, скачок будет ограничен.
distance_ema_alpha:
Коэффициент EMA для дистанции. Чем больше, тем быстрее реакция
и меньше сглаживание.
speed_ema_alpha:
Коэффициент EMA для скорости.
speed_epsilon_m_s:
Порог, ниже которого скорость считается статической.
min_valid_zone / max_valid_zone:
Диапазон допустимых Range. Всё вне диапазона отбрасывается.
zone_to_meter_k:
Коэффициент перевода зоны в метры.
zone_to_meter_b:
Смещение перевода зоны в метры:
distance_m = zone * k + b
enable_debug_unknown_lines:
Если true, можно будет смотреть строки, которые не распарсились.
*/
struct RadarConfig {
int uart_rx_pin = 4;
int uart_tx_pin = 15;
uint32_t baud_rate = 115200;
uint32_t presence_hold_ms = 1500;
uint32_t stale_after_ms = 2000;
uint8_t median_window_size = 5;
uint16_t max_zone_step_per_sample = 4;
float distance_ema_alpha = 0.35f;
float speed_ema_alpha = 0.25f;
float speed_epsilon_m_s = 0.08f;
uint16_t min_valid_zone = 1;
uint16_t max_valid_zone = 200;
float zone_to_meter_k = 0.7f;
float zone_to_meter_b = 0.0f;
bool enable_debug_unknown_lines = false;
};
/*
Класс первого сенсорного слоя для HLK-LD2420 в текстовом режиме UART.
Назначение класса:
- читать строки вида ON / OFF / Range N
- фильтровать поток
- хранить актуальное состояние радара
- отдавать состояние как структуру и как JSON
Этот слой намеренно не занимается:
- Wi-Fi / HTTP / MQTT
- объединением нескольких радаров
- распознаванием людей/котов
- логикой комнаты/дома
*/
class Ld2420Radar {
public:
Ld2420Radar();
/*
Инициализация драйвера.
uart_port:
Ссылка на UART-порт, например Serial2.
config:
Пользовательская конфигурация.
*/
void begin(HardwareSerial &uart_port, const RadarConfig &config);
/*
Обновление состояния радара.
Должен вызываться часто в loop().
*/
void update();
/*
Есть ли новые осмысленные данные после последнего чтения.
Полезно, если позже захочешь отправлять обновления только по событию.
*/
bool has_new_data() const;
/*
Сброс флага новых данных после обработки.
*/
void clear_new_data_flag();
/*
Получить текущее состояние сенсора.
*/
const RadarState &get_state() const;
/*
Получить JSON-строку текущего состояния.
Формат, например:
{
"timestamp_ms":123,
"online":true,
...
}
*/
String get_state_json();
/*
Обновить конфигурацию на лету.
Полезно для будущей интеграции с системой настроек.
*/
void set_config(const RadarConfig &config);
/*
Получить текущую конфигурацию.
*/
const RadarConfig &get_config() const;
/*
Включить/выключить режим калибровочного лога.
Если включён, можно удобно печатать короткие строки для ручной
калибровки зависимости Range -> distance.
*/
void set_calibration_mode(bool enabled);
/*
Узнать, включён ли режим калибровки.
*/
bool is_calibration_mode() const;
/*
Сформировать компактную строку для режима калибровки.
Например:
raw=57 median=56 filtered=55 distance=3.24 speed=-0.12
*/
String get_calibration_line() const;
private:
static const uint8_t max_supported_median_window = 9;
HardwareSerial *uart;
RadarConfig config;
RadarState state;
String line_buffer;
bool new_data_available;
bool calibration_mode;
uint16_t zone_window[max_supported_median_window];
uint8_t zone_window_count;
uint8_t zone_window_index;
bool has_filtered_zone;
uint16_t last_filtered_zone;
bool has_smoothed_distance;
float smoothed_distance_m;
bool has_smoothed_speed;
float smoothed_speed_m_s;
void reset_runtime_state();
void read_uart_lines();
void handle_line(const String &line);
void handle_on_line();
void handle_off_line();
void handle_range_line(const String &line);
void push_zone_sample(uint16_t zone);
uint16_t get_median_zone() const;
uint16_t apply_zone_step_limit(uint16_t candidate_zone);
float apply_distance_smoothing(float new_distance_m);
void update_speed(float filtered_distance_m, uint32_t now_ms);
void update_direction();
void update_activity_score();
void apply_presence_hold();
void update_stale_flag();
void update_confidence();
bool is_zone_valid(uint16_t zone) const;
float zone_to_distance_m(uint16_t zone) const;
const char *direction_to_string(RadarDirection direction) const;
String escape_json_string(const String &value) const;
};
#endif