Newer
Older
smart-home-server / devices / sensor / ld2420_radar.h
#pragma once

#include <Arduino.h>
#include <Stream.h>

/* ------------------------------------------------------------------ */
/* -----------------------  Конфигурация радара  ---------------------*/
/* ------------------------------------------------------------------ */

struct RadarConfig {
    uint8_t   uart_rx_pin;
    uint8_t   uart_tx_pin;
    uint32_t  baud_rate = 115200;

    /* Хранение данных */
    uint16_t  presence_hold_ms      = 1500;   // Как долго держать флаг «presence» после последнего сигнала
    uint16_t  stale_after_ms        = 2000;   // Если не пришло ни одной строки – считаем offline

    /* Фильтры */
    uint8_t   median_window_size    = 5;
    uint8_t   max_zone_step_per_sample = 4;

    /* EMA‑сглаживание (α) */
    float     distance_ema_alpha    = 0.35f;
    float     speed_ema_alpha       = 0.25f;

    /* Диапазон зон, которые считаем «действительными» */
    uint16_t  min_valid_zone        = 1;
    uint16_t  max_valid_zone        = 200;

    /* Перевод зоны в метры (параметры калибровки) */
    float     zone_to_meter_k       = 0.7f;   // slope
    float     zone_to_meter_b       = 0.0f;   // intercept

    /* Шкала активности: скорость, с которой считается «активным» */
    float     activity_min_speed_m_s = 0.03f;
    float     activity_max_speed_m_s = 1.20f;

    bool      enable_debug_unknown_lines = false;
};

/* ------------------------------------------------------------------ */
/* --------------------------  Состояние радара  ---------------------*/
/* ------------------------------------------------------------------ */

struct RadarState {
    bool online              = false;   // Приём данных
    bool presence            = false;   // Присутствует человек
    uint8_t activity_score   = 0;       // Средняя активность за минуту (0…10)
    uint8_t activity_score_current = 0; // Активность в момент запроса
    const char *activity_score_dynamics = "constant";
    float distance_m         = NAN;
};

/* ------------------------------------------------------------------ */
/* ------------------------  Основной класс радара  -------------------*/
/* ------------------------------------------------------------------ */

class Ld2420Radar {
public:
    /* Инициализация */
    void begin(Stream &uart, const RadarConfig &cfg);

    /* Обновление состояния – вызывать каждый цикл (или в таймере) */
    void update();

    /* Получить JSON‑строку текущего состояния */
    String get_state_json() const;

private:
    /* Внутренние данные */
    Stream * _uart = nullptr;
    RadarConfig _cfg;
    RadarState  _state{};

    /* Тайминг */
    uint32_t _last_line_ms   = 0;          // Когда пришла последняя строка
    uint32_t _presence_start_ms = 0;       // Когда появился первый сигнал

    /* Буфер для чтения строки */
    static const size_t LINE_BUF_SIZE = 64;
    char _line_buf[LINE_BUF_SIZE];
    size_t _line_pos = 0;

    /* Хранение последних N оценок активности (N=10) */
    static const uint8_t ACTIVITY_HISTORY = 10;
    uint8_t _activity_hist[ACTIVITY_HISTORY] = {0};
    uint8_t _hist_idx = 0;
    bool    _hist_filled = false;

    /* Публичные вспомогательные функции (для тестов) */
    void _parse_line(const char *line, size_t len);
    void _update_online_state();
    void _update_presence_state(uint16_t zone);
    void _update_distance_speed(float distance_m, float speed_m_s);
    void _calc_activity_score_current();
    void _calc_activity_score_average();
    void _determine_dynamics();

    /* Утилиты */
    static bool _is_hex_digit(char c) {
        return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F');
    }
};