Newer
Older
smart-home-server / devices / sensor / sensor.ino
#include <Arduino.h>

#include "ld2420_radar.h"
#include "bh1750_sensor.h"
#include "bme280_sensor.h"
#include "max4466_mic.h"

/*
    =========================
    GLOBAL OBJECTS
    =========================
*/

Ld2420Radar radar;
RadarConfig radar_config;

Bh1750Sensor light_sensor;
Bh1750Config light_config;

Bme280Sensor climate_sensor;
Bme280Config climate_config;

Max4466Mic mic;
Max4466Config mic_config;

/*
    Отдельные I2C-шины.
    0-я шина для BH1750.
    1-я шина для BME/BMP280.
*/
TwoWire i2c_climate = TwoWire(1);

/*
    Интервал печати состояния.
*/
static const uint32_t print_interval_ms = 500;
uint32_t last_print_ms = 0;


/*
    =========================
    RADAR CONFIGURATION
    =========================
*/

void setup_radar_config() {
    /* ---- UART ---- */
    radar_config.uart_rx_pin = 4;
    radar_config.uart_tx_pin = 15;
    radar_config.baud_rate   = 115200;

    /* ---- Присутствие и онлайн ---- */
    radar_config.presence_hold_ms   = 1500;
    radar_config.stale_after_ms     = 2000;
    radar_config.distance_ema_alpha = 0.6f;

    /* ---- Параметры, записываемые в радар при старте ---- */
    radar_config.min_gate        = 1;   // игнорировать ворота < 0.7 м
    radar_config.max_gate        = 8;   // детекция до ~5.6 м
    radar_config.radar_timeout_s = 5;

    /* ---- Пороги для записи в модуль (внутренняя логика радара) ---- */
    radar_config.move_threshold  = 10000;
    radar_config.still_threshold = 8000;

    /*
        Пороги присутствия по воротам — вычисляется в прошивке,
        поле pres из фрейма радара игнорируется.

        Настройка:
          1. Включи enable_debug_frames=true
          2. Посмотри [GATES] в пустой комнате
          3. Порог ворота = максимум_шума_этого_ворота * 2
          4. g0 и g1 всегда 0

        Текущие пороги подобраны под эту комнату:
          g0-g1: шум антенны и фон — игнорируем
          g2:    шум до ~30  → порог 800
          g3:    шум до ~220 → порог 500  (основной сигнал на 2 м)
          g4-g6: высокий шум от помех в комнате → высокие пороги
          g7+:   малый шум → порог 2000
    */
    static const uint32_t thresholds[LD2420_TOTAL_GATES] = {
        0,      // g0  ~0.0 м  шум антенны
        0,      // g1  ~0.7 м  фоновый шум
        800,    // g2  ~1.4 м
        500,    // g3  ~2.1 м
        15000,  // g4  ~2.8 м  (помехи в комнате)
        25000,  // g5  ~3.5 м  (помехи в комнате)
        15000,  // g6  ~4.2 м  (помехи в комнате)
        2000,   // g7  ~4.9 м
        2000,   // g8  ~5.6 м
        500,    // g9
        500,    // g10
        500,    // g11
        500,    // g12
        500,    // g13
        500,    // g14
        500,    // g15
    };
    memcpy(radar_config.presence_thresholds, thresholds,
           sizeof(radar_config.presence_thresholds));

    radar_config.presence_min_active_gates = 1;

    /* ---- activity_score ---- */
    radar_config.total_energy_max          = 150000;
    radar_config.activity_avg_window_s     = 60;
    radar_config.activity_trend_window_min = 10;

    /* ---- Отладка ---- */
    radar_config.enable_debug_frames = false;
}


/*
    =========================
    BH1750 CONFIGURATION
    =========================
*/

void setup_light_config() {
    /*
        Отдельная шина освещённости:
        SDA -> GPIO16
        SCL -> GPIO17
        ADDR -> VCC -> 0x5C
    */
    light_config.sda_pin = 16;
    light_config.scl_pin = 17;
    light_config.i2c_address = 0x5C;
    light_config.measurement_mode = 0x10;
    light_config.stale_after_ms = 2000;
    light_config.read_interval_ms = 200;
    light_config.lux_ema_alpha = 0.25f;
    light_config.lux_max = 500.0f;
}


/*
    =========================
    BME/BMP280 CONFIGURATION
    =========================
*/

void setup_climate_config() {
    /*
        Отдельная климатическая шина:
        SDA -> GPIO18
        SCL -> GPIO19
        SDO -> GND  => 0x76
        SDO -> VCC  => 0x77
    */
    climate_config.sda_pin = 18;
    climate_config.scl_pin = 19;
    climate_config.i2c_address = 0x76;

    climate_config.stale_after_ms = 3000;
    climate_config.read_interval_ms = 1000;

    climate_config.temperature_ema_alpha = 0.25f;
    climate_config.pressure_ema_alpha    = 0.20f;
    climate_config.humidity_ema_alpha    = 0.20f;
}


/*
    =========================
    MAX4466 CONFIGURATION
    =========================
*/

void setup_mic_config() {
    /*
        Аналоговый выход MAX4466 -> GPIO34
        GPIO34 — input only, ADC1_CH6, подходит для analogRead.

        Калибровка:
          db_noise_floor   — уровень тишины в вашем помещении (дБ).
          db_scale_range_db — диапазон от тишины до максимума (дБ).
          db_ref_mv        — опорное напряжение для расчёта дБ.

        Стартовые значения подходят для большинства помещений.
        Для точной калибровки:
          1. В тишине посмотрите db_raw в Serial — это ваш noise floor.
          2. При максимальном ожидаемом шуме посмотрите db_raw —
             разница с noise floor и есть db_scale_range_db.
    */
    mic_config.adc_pin           = 34;
    mic_config.adc_max_value     = 4095;
    mic_config.adc_vref_mv       = 3300.0f;

    mic_config.sample_count      = 256;
    mic_config.sample_interval_us = 100;

    mic_config.read_interval_ms  = 100;
    mic_config.stale_after_ms    = 2000;

    mic_config.db_ema_alpha      = 0.15f;
    mic_config.db_ref_mv         = 1.0f;
    mic_config.db_noise_floor    = 30.0f;
    mic_config.db_scale_range_db = 30.0f;

    mic_config.db_avg_window_s      = 30;
    mic_config.db_trend_window_min  = 10;

    mic_config.peak_reset_ms     = 60000;
}


/*
    =========================
    DEVICE INITIALIZATION
    =========================
*/

void init_radar() {
    setup_radar_config();
    bool ok = radar.begin(Serial2, radar_config);
    if (ok) Serial.println("Radar configured and initialized");
    else    Serial.println("Radar init failed — running with defaults");
}

void init_light_sensor() {
    setup_light_config();
    bool ok = light_sensor.begin(light_config);
    if (ok) Serial.println("BH1750 initialized");
    else    Serial.println("BH1750 init failed");
}

void init_climate_sensor() {
    setup_climate_config();
    bool ok = climate_sensor.begin(i2c_climate, climate_config);
    if (ok) {
        Serial.print("Climate sensor initialized: ");
        Serial.println(climate_sensor.get_sensor_type_string());
    } else {
        Serial.println("Climate sensor init failed");
    }
}

void init_mic() {
    setup_mic_config();
    mic.begin(mic_config);
    Serial.println("MAX4466 initialized");
}


/*
    =========================
    SETUP
    =========================
*/

void setup() {
    Serial.begin(115200);
    delay(1000);

    Serial.println();
    Serial.println("Radar + BH1750 + BME/BMP280 + MAX4466 project start");

    init_radar();
    init_light_sensor();
    init_climate_sensor();
    init_mic();
}


/*
    =========================
    LOOP
    =========================
*/

void loop() {
    radar.update();
    light_sensor.update();
    climate_sensor.update();
    mic.update();

    uint32_t now_ms = millis();

    if (now_ms - last_print_ms >= print_interval_ms) {
        last_print_ms = now_ms;

        Serial.println(radar.get_state_json());
        Serial.println(light_sensor.get_state_json());
        Serial.println(climate_sensor.get_state_json());
        Serial.println(mic.get_state_json());
    }
}