#pragma once
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_BMP280.h>
/*
Конфигурация датчика BME280 / BMP280.
*/
struct Bme280Config {
uint8_t sda_pin = 18;
uint8_t scl_pin = 19;
uint8_t i2c_address = 0x76;
/*
Через сколько миллисекунд без успешного чтения
данные считаются устаревшими.
*/
uint32_t stale_after_ms = 3000;
/*
Интервал чтения датчика.
*/
uint32_t read_interval_ms = 1000;
/*
Коэффициенты сглаживания EMA.
*/
float temperature_ema_alpha = 0.25f;
float pressure_ema_alpha = 0.20f;
float humidity_ema_alpha = 0.20f;
/*
Окно тренда в минутах для каждого параметра.
Минимум 2, максимум TREND_MAX (10).
Тренд вычисляется по линейной регрессии скользящего
окна минутных снимков.
*/
uint8_t temperature_trend_window_min = 10;
uint8_t pressure_trend_window_min = 10;
uint8_t humidity_trend_window_min = 10;
/*
Порог наклона линейной регрессии для классификации динамики.
Значения изменения меньше порога считаются "constant".
temperature: °C/мин
pressure: hPa/мин
humidity: %/мин
*/
float temperature_trend_threshold = 0.05f; // °C/мин
float pressure_trend_threshold = 0.10f; // hPa/мин
float humidity_trend_threshold = 0.10f; // %/мин
/*
Порог стандартного отклонения для классификации "variable".
Если slope ≈ 0, но разброс велик — динамика непостоянная.
*/
float temperature_variable_std = 0.3f; // °C
float pressure_variable_std = 0.5f; // hPa
float humidity_variable_std = 1.0f; // %
};
enum BmeSensorType {
BME_SENSOR_TYPE_UNKNOWN = 0,
BME_SENSOR_TYPE_BMP280,
BME_SENSOR_TYPE_BME280
};
class Bme280Sensor {
public:
Bme280Sensor();
~Bme280Sensor();
/*
Инициализация собственной I2C-шины и попытка
подключения BME280/BMP280.
*/
bool begin(TwoWire &wire, const Bme280Config &config);
/*
Периодическое обновление состояния датчика.
*/
void update();
/*
Статус датчика.
*/
bool is_online() const;
bool has_valid_data() const;
bool is_stale() const;
/*
Тип найденного датчика.
*/
BmeSensorType get_sensor_type() const;
String get_sensor_type_string() const;
/*
Текущие значения (EMA-сглаженные).
*/
float get_temperature_c() const;
float get_pressure_hpa() const;
float get_humidity_percent() const;
/*
Есть ли влажность.
Для BMP280 всегда false.
*/
bool has_humidity() const;
/*
Динамика параметра:
"constant" | "increasing" | "decreasing" | "variable"
*/
const char* get_temperature_dynamics() const;
const char* get_pressure_dynamics() const;
const char* get_humidity_dynamics() const;
/*
Среднее изменение параметра в минуту за период тренда
(наклон линейной регрессии, единицы параметра/мин).
*/
float get_temperature_dynamics_val() const;
float get_pressure_dynamics_val() const;
float get_humidity_dynamics_val() const;
/*
Три отдельных JSON-объекта состояния.
*/
String get_temperature_json() const;
String get_pressure_json() const;
String get_humidity_json() const;
/*
Единый JSON (обратная совместимость).
*/
String get_state_json() const;
private:
Bme280Config _config;
TwoWire *_wire = nullptr;
Adafruit_BME280 *_bme280 = nullptr;
Adafruit_BMP280 *_bmp280 = nullptr;
BmeSensorType _sensor_type = BME_SENSOR_TYPE_UNKNOWN;
bool _initialized = false;
bool _online = false;
bool _has_valid_data = false;
uint32_t _last_read_attempt_ms = 0;
uint32_t _last_success_read_ms = 0;
float _temperature_c = 0.0f;
float _pressure_hpa = 0.0f;
float _humidity_percent = 0.0f;
float _filtered_temperature_c = 0.0f;
float _filtered_pressure_hpa = 0.0f;
float _filtered_humidity_percent = 0.0f;
private:
/* ---- Инициализация чипов ---- */
bool init_bme280();
bool init_bmp280();
/* ---- EMA-фильтры ---- */
void update_filtered_temperature(float value);
void update_filtered_pressure(float value);
void update_filtered_humidity(float value);
/* ====================================================
ТРЕНД (минутный, линейная регрессия)
По аналогии с Ld2420Radar / Max4466Mic.
==================================================== */
static constexpr uint8_t TREND_MAX = 10;
/* -- температура -- */
float _temp_trend_buf[TREND_MAX] = {};
uint8_t _temp_trend_idx = 0;
uint8_t _temp_trend_count = 0;
uint32_t _temp_trend_last_ms = 0;
float _temp_trend_slope = 0.0f; // °C/мин (сырой наклон)
float _temp_trend_std_dev = 0.0f;
int8_t _temp_trend_dir = 0; // -1 / 0 / +1
/* -- давление -- */
float _pres_trend_buf[TREND_MAX] = {};
uint8_t _pres_trend_idx = 0;
uint8_t _pres_trend_count = 0;
uint32_t _pres_trend_last_ms = 0;
float _pres_trend_slope = 0.0f;
float _pres_trend_std_dev = 0.0f;
int8_t _pres_trend_dir = 0;
/* -- влажность -- */
float _humi_trend_buf[TREND_MAX] = {};
uint8_t _humi_trend_idx = 0;
uint8_t _humi_trend_count = 0;
uint32_t _humi_trend_last_ms = 0;
float _humi_trend_slope = 0.0f;
float _humi_trend_std_dev = 0.0f;
int8_t _humi_trend_dir = 0;
/* ---- Тики ---- */
void _tick_temp_trend(uint32_t now_ms);
void _tick_pres_trend(uint32_t now_ms);
void _tick_humi_trend(uint32_t now_ms);
/*
Общая функция вычисления линейной регрессии.
buf/win/count/idx — кольцевой буфер.
out_slope — наклон (единицы/мин).
out_std_dev — стандартное отклонение остатков.
out_dir — знак: -1 / 0 / +1 (с учётом порога threshold).
*/
static void _compute_trend(
const float* buf, uint8_t win, uint8_t count, uint8_t idx,
float threshold, float variable_std,
float &out_slope, float &out_std_dev, int8_t &out_dir);
/*
Перевод dir + std в строку динамики.
*/
static const char* _dynamics_string(
int8_t dir, float std_dev, float variable_std, uint8_t count);
};