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

#include <Arduino.h>

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;

    float distance_ema_alpha = 0.35f;

    int min_valid_zone = 1;
    int max_valid_zone = 200;

    float zone_to_meter_k = 0.7f;
    float zone_to_meter_b = 0.0f;

    /*
        Порог минимального движения, ниже которого
        текущая активность почти не растёт.
    */
    float activity_min_speed_m_s = 0.03f;

    /*
        Скорость, при которой моментная активность
        выходит примерно к 10.
    */
    float activity_max_speed_m_s = 1.20f;

    bool enable_debug_unknown_lines = false;
};

class Ld2420Radar {
public:
    Ld2420Radar();

    bool begin(HardwareSerial &serial, const RadarConfig &config);
    void update();

    String get_state_json() const;

private:
    static const size_t LINE_BUFFER_SIZE = 160;
    static const size_t SECOND_HISTORY_SIZE = 600; // 10 минут по 1 секунде

    HardwareSerial *serial_port = nullptr;
    RadarConfig cfg;

    char line_buffer[LINE_BUFFER_SIZE];
    size_t line_length = 0;

    bool initialized = false;

    bool online = false;
    bool raw_presence = false;
    bool presence = false;

    float raw_distance_m = 0.0f;
    float filtered_distance_m = 0.0f;

    float current_speed_m_s = 0.0f;
    uint8_t activity_score_current = 0;

    uint32_t last_frame_ms = 0;
    uint32_t last_presence_ms = 0;
    uint32_t last_distance_sample_ms = 0;

    float last_distance_for_speed_m = 0.0f;
    bool has_distance_for_speed = false;

    uint8_t second_scores[SECOND_HISTORY_SIZE];
    uint16_t second_scores_head = 0;
    uint16_t second_scores_count = 0;
    uint32_t last_second_push_ms = 0;

    String activity_score_dynamics = "constant";

private:
    void reset_runtime_state();

    void process_incoming_byte(char ch);
    void process_line(const char *line_cstr);

    void update_online_state(uint32_t now_ms);
    void update_presence_state(uint32_t now_ms);
    void update_second_history(uint32_t now_ms);

    bool parse_ascii_line(const String &line);

    bool extract_number_by_keys(const String &line, const char *keys[], size_t key_count, float &value_out) const;
    bool extract_bool_by_keys(const String &line, const char *keys[], size_t key_count, bool &value_out) const;

    bool parse_number_after_key(const String &line, const String &key, float &value_out) const;
    bool parse_bool_after_key(const String &line, const String &key, bool &value_out) const;

    String normalize_line(const String &line) const;
    float clampf(float value, float min_value, float max_value) const;
    int clampi(int value, int min_value, int max_value) const;

    float zone_to_meters(float zone) const;
    void apply_new_distance(float distance_m, uint32_t now_ms);

    uint8_t compute_current_activity_score(bool detected_presence, float distance_m, float speed_m_s) const;
    uint8_t compute_minute_activity_score() const;
    String compute_dynamics() const;

    bool is_distance_plausible(float distance_m) const;
};