AI Мониторинг температуры и влажности в квартире на минималках

AI

Команда форума
Редактор
Регистрация
23 Авг 2023
Сообщения
4,169
Реакции
0
Баллы
36
Ofline
Данная статья является продолжением статьи HT2000 Сбор и хранение данные по CO2 https://habr.com/ru/articles/871380/ про сенсор HT2000, который измеряет CO₂, температуру и влажность. В рамках той статьи обсуждался более доступный вариант измерительного прибора. Конкретно этот вариант измерителя будет построен на датчиках типа AHT2X или SHT2X (в интернете полно информации об этих датчиках). Задача прибора-измерителя - измерять показания температуры и влажности и передавать их на сервер для хранения данных по домашнему Wi-Fi.

Внешний вид прибора

Внешний вид прибора

В качестве микроконтроллера был выбран ESP8266, собранный на плате NodeMCU v3. Микроконтроллер ESP8266 умеет передавать данные по Wi-Fi - собственно, это и требуется для онлайн системы мониторинга. Кроме того, модуль имеет программный интерфейс I²C для считывания показаний датчика и аппаратный интерфейс SPI для оперативного отображения показателей.

Распиновка модуля микроконтроллера

Распиновка модуля микроконтроллера

Датчик температуры и влажности​


Ниже приведена сводная информация по наиболее популярным моделям датчиков серий AHT (Aosong) и SHT (Sensirion).


Параметр​

AHT20​

AHT21​

AHT25​

AHT30​

SHT20​

SHT30​

SHT35​

Производитель

Aosong (ASAIR)​

Aosong (ASAIR)​

Aosong (ASAIR)​

Aosong (ASAIR)​

Sensirion​

Sensirion​

Sensirion​

Интерфейс

I²C​

I²C​

I²C​

I²C​

I²C​

I²C​

I²C​

Напряжение питания

2.2 – 5.5В​

2.2 – 5.5В​

2.2 – 5.5В​

1.8 – 3.6В​

2.1 – 3.6В​

2.15 – 5.5В​

2.15 – 5.5В​

Точность влажности (±% RH)

±2%

±2%

±1.8%

±2%

±3%

±2%

±1.5%

Точность температуры (±°C)

±0.3°C

±0.3°C

±0.2°C

±0.3°C

±0.3°C

±0.2°C

±0.1°C

Диапазон температур

-40 … +85°C​

-40 … +85°C​

-40 … +85°C​

-40 … +85°C​

-40 … +125°C​

-40 … +125°C​

-40 … +125°C​

Диапазон влажности

0 … 100%​

0 … 100%​

0 … 100%​

0 … 100%​

0 … 100%​

0 … 100%​

0 … 100%​

Особенности

Популярная бюджетная модель​

Защита от конденсата​

Повышенная точность​

Сверхнизкое энергопотребление​

Классическая надежная модель​

Высокая скорость измерений​

Премиальная точность​

Я заказывал на AliExpress датчик AHT25, так как он, как видно из таблицы, более точный из серии AHT. Но что-то пошло не так, и мне привезли AHT30, поэтому я запаял его. Протокол общения у этих датчиков одинаковый.

Схема устройства​


Ниже приведена принципиальная схема устройства. Через USB-интерфейс напряжение подаётся на модуль зарядки на микросхеме TP4056 и заряжает буферный аккумулятор. Далее напряжение поступает на модуль DC/DC-преобразователя, который питает всю остальную схему (датчик, микроконтроллер и дисплей). На плате микроконтроллера установлен сглаживающий конденсатор на 100 мкФ.

Принципиальная схема устройства

Принципиальная схема устройства

Модуль зарядки​


Если у вас аккумулятор содержит модуль защиты то можно брать любой готовый модуль зарядки на микросхеме TP4056, однако я рекомендую взять все же с модулем защиты.

Модуль зарядки LiPol с защитой

Модуль зарядки LiPol с защитой

Ток зарядки у готовых модулей составляет 1 А, что может быть многовато для очень маленьких аккумуляторов, но его можно уменьшить заменой резистора, задающего ток зарядки на плате. Я такую модификацию не делал, поэтому не подскажу, какой именно резистор стоит заменить и на какой, подробнее можно глянуть datasheet на TP4056.

Подбор аккумулятора стоит начать с того, что готовое устройство потребляет средний ток 100 мА. Согласно документации, ESP8266 потребляет 170–300 мА (пиковые значения) во время передачи данных по Wi-Fi, в обычном режиме потребление составляет около 80 мА, а дисплей потребляет ещё 20 мА. Таким образом, несложной математикой можно рассчитать необходимое время работы устройства от аккумулятора. В моём случае я использовал старые аккумуляторы от ноутбука с потерянной емкостью, поэтому зарядный ток в 1 А меня устроил. У меня устройство работает от аккумулятора около 12 часов.

Я не использовал режим сна микроконтроллера, так как в этом не было необходимости. Однако лицензия проекта позволяет вам модифицировать код и реализовать такой функционал.

Важный недостаток данной зарядки в том, что при подключении к сети заряд аккумулятора всегда будет составлять 100%, что в конечном итоге приводит к его деградации. Альтернативой может быть зарядка до 85% или использование блокировочных диодов, но это уже выходит за рамки данного проекта.

DC/DC-преобразователь​


Я брал готовый модуль, который умеет преобразовывать напряжение как вверх, так и вниз, поэтому у модуля две катушки индуктивности. Учитывая энергопотребление устройства я искал модуль, который выдает пиковый ток 330 мА (выше были представлены нагрузочные расчеты). В принципе, если у кого-то будет использоваться аккумулятор на 12 В, то такой модуль тоже должен подойти. Например: солнечная батарея на 12 В → зарядное устройство для соответствующего типа аккумулятора и далее этот модуль. В таком случае нужно будет пересчитать сопротивление резистора R1 чтобы не вывести из строя ADC преобразователь контроллера ESP8266.

DC/DC-преобразователь

DC/DC-преобразователь

Контроль заряда батареи​


Модуль ESP8266 имеет на борту АЦП (аналого-цифровой преобразователь). На его вход можно подавать напряжение от 0 до 1 В. На модуле NodeMCU v3 распаян делитель напряжения, чтобы можно было подавать на вход от 0 до 3,3 В. В нашем случае нужно контролировать напряжение батареи от 0 до 4,2 В, поэтому был добавлен резистор R1 на 100 кОм, чтобы обеспечить такую возможность.

Датчик температура и влажности​


Датчики, представленные на маркетплейсах, имеют разные конфигурации. В моем случае нужна минимальная конфигурация: блокировочный конденсатор для шины I²C и два подтягивающих резистора 4,7 кОм. Существуют также более продвинутые модели со стабилизаторами и двумя транзисторами для преобразования напряжения. Они тоже подходят - в моей конструкции использовался именно такой датчик.

Датчик температуры и влажности

Датчик температуры и влажности

Дисплей​


В качестве дисплея я выбрал OLED-дисплей SSD1315 - это улучшенная версия SSD1306, и библиотечный драйвер от SSD1306 к нему подходит. Дисплей бывает в двух вариантах: с шиной I²C и с шиной SPI. Я выбрал второй вариант, так как у ESP8266 используется программный I²C, что может вызывать затормаживание при перерисовке экрана (в итоге к заметному мерцанию экрана), зато у контроллера ESP8266 есть аппаратный интерфейс SPI, что позволяет поднять скорость отрисовки экрана.

OLED дисплей

OLED дисплей

Программная часть​


В качестве системы программирования был выбран Arduino, так как в нем есть все необходимые библиотеки из коробки. Проект опубликован тут GitHub - Levon24/esp8266-temperature-humidity-monitor: It's simple project to monitorind temperature and humidity. The data will be send to mqtt broker. https://github.com/Levon24/esp8266-temperature-humidity-monitor с открытой лицензией.

Сам измерительный прибор должен отправлять каждые 30 секунд данные через Wi-Fi на MQTT брокер, который может быть расположен как локально, так и в облаке. У меня в наличие был Orange Pi Zero 2W на котором я поднял локальный MQTT брокер для системы мониторинга.

В коде настройка MQTT следующая:

Код:
const char *mqttHost = "mercury.home.lan";
const int mqttPort = 1883;
const char *mqttTopic = "sensors/cabinet";
const char *mqttClientId = "esp-cabinet";

где mercury.home.lan это локальный сервер с брокером mqtt, порт стандартный 1833. Топик для отправки сообщений в моем случае sensors/cabinet так как датчик расположен в кабинете. Если вы будете использовать облачный сервис, то следует добавить авторизацию.

Измерительный прибор отправляет json сообщения с отметкой времени, данных о температуре и влажности, а также напряжения батареи. Пример сообщения:

Код:
{
  "timestamp": 1776431035,
  "temperature": 25.36640,
  "humidity": 19.60335,
  "battery": 3.92082
}

Для того чтобы выдавать отметку времени timestamp модуль обращается к ntp серверу для получения времени в UTC и далее сохраняет текущее время в переменную unixTime и ставит отсчет времени на 12 часов для последующей синхронизации.

Код:
    if (timeSync == 0 && timeClient.update()) {
      Serial.println("Sync time.");
      unixTime = timeClient.getEpochTime();
      timeSync = 6 * 1800; // 12 hours
    } else {
      if (timeSync > 0) {
        timeSync--;
      }
    }

Время между синхронизациями поддерживает внутренний единственный доступный железный таймер Timer, который можно использовать для такой работы (другой железный таймер используется для поддержания работы Wi-Fi). Устанавливаем его для срабатывания 1 раз в секунду.

Код:
  // Timer
  InterruptTimer.attachInterruptInterval(1000000, TimerHandler);

И далее производится отсчет секунд по таймеру переменной unixTime.

Код:
// Time
ICACHE_RAM_ATTR void TimerHandler() {
  unixTime++;
}

Код, который отправляет сообщений по MQTT достаточно простой, сам json формируется как строка. Стоит помнить, что в библиотеке стоит ограничение по-умолчанию на payload размером 128 байт, но его можно поднять, в случае необходимости.

Код:
if (pubSubClient.connect(mqttClientId)) {
  char payload[128];

  snprintf(payload, sizeof(payload), "{\"timestamp\": %ld, \"temperature\": %.5f, \"humidity\": %.5f, \"battery\": %.5f}", (long) unixTime, temperature, humidity, batteryVoltage);
  if (pubSubClient.publish(mqttTopic, payload)) {
    Serial.println("MQTT send");
    //delay(1000);
  }
  
  pubSubClient.disconnect();
}

Раз в 2 секунды показания отображаются на дисплее, тут тоже все достаточно просто.

Код:
display.clear();
display.setFont(ArialMT_Plain_10);

if (WiFi.status() == WL_CONNECTED) {
  display.drawString(0, 0, "IP: " + WiFi.localIP().toString());
} else {
  display.drawString(0, 0, "IP: Disconnected");
}

char text[30];
display.setFont(ArialMT_Plain_24);

snprintf(text, sizeof(text), "%.4f °C", temperature);
display.drawString(0, 10, text);

snprintf(text, sizeof(text), "%.4f %%", humidity);
display.drawString(0, 30, text);

display.setFont(ArialMT_Plain_10);

snprintf(text, sizeof(text), "Battery: %.2f V > %.2f %%", batteryVoltage, batteryPercent);
display.drawString(0, 52, text);

display.display();

Время в 2 секунды выбрано как рекомендованное время для опроса датчика AHT2X. Собственно ниже код, который читает показания датчиков:

Код:
  float temperature = aht20.getTemperature();
  float humidity = aht20.getHumidity();

  int batteryValue = analogRead(A0);
  float batteryVoltage = ((float) batteryValue) * (BATTERY_MAX / 1023.0);
  float batteryPercent = (batteryVoltage - BATTERY_MIN) * (100.0 / (BATTERY_MAX - BATTERY_MIN));

Настройка брокера MQTT​


Для локального сбора данных телеметрии можно использовать MQTT-брокер. В качестве популярной реализации обычно используют Eclipse Mosquitto. Поскольку в моём распоряжении есть Orange Pi Zero 2W, я установлю брокер Eclipse Mosquitto именно на него.

Перед началом установки обновим систему:

sudo apt update && sudo apt upgrade -y

Далее установим необходимый пакеты:

sudo apt install mosquitto mosquitto-clients -y

По-умолчанию сервис слушает только локальный интерфейс, поэтому стоит добавить файл со следующим содержимым (1 строка это команда для вывода содержимого файла):

Код:
> sudo cat /etc/mosquitto/conf.d/default.conf
listener 1883 0.0.0.0
allow_anonymous true

Далее можно сервис перезапустить:

sudo systemctl restart mosquitto.service

По результату у нас будет готовый брокер сообщений, который можно проверить командой ниже:

Скрытый текст

Код:
$ sudo systemctl status mosquitto.service ● mosquitto.service - Mosquitto MQTT Broker     Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; preset: enabled)     Active: active (running) since Sun 2026-03-22 11:38:39 +04; 34min ago       Docs: man:mosquitto.conf(5)             man:mosquitto(8)    Process: 1136289 ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto (code=exited, status=0/SUCCESS)    Process: 1136291 ExecStartPre=/bin/chown mosquitto /var/log/mosquitto (code=exited, status=0/SUCCESS)    Process: 1136292 ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto (code=exited, status=0/SUCCESS)    Process: 1136293 ExecStartPre=/bin/chown mosquitto /run/mosquitto (code=exited, status=0/SUCCESS)   Main PID: 1136294 (mosquitto)      Tasks: 1 (limit: 4545)     Memory: 1.1M        CPU: 708ms     CGroup: /system.slice/mosquitto.service             └─1136294 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
Mar 22 11:38:39 mercury systemd[1]: Starting mosquitto.service - Mosquitto MQTT Broker...
Mar 22 11:38:39 mercury mosquitto[1136294]: 1774165119: Loading config file /etc/mosquitto/conf.d/default.conf
Mar 22 11:38:39 mercury systemd[1]: Started mosquitto.service - Mosquitto MQTT Broker.

Для просмотра сообщений, можно использовать команду (1ая строка это команда):

Код:
> mosquitto_sub -t "#" -v
sensors/kitchen {"timestamp": 1777045575, "temperature": 23.31867, "humidity": 42.32216, "battery": 3.85513}
sensors/cabinet {"timestamp": 1777045596, "temperature": 20.14351, "humidity": 53.22123, "battery": 4.20411}
sensors/kitchen {"timestamp": 1777045604, "temperature": 23.26317, "humidity": 42.35744, "battery": 3.85513}
sensors/cabinet {"timestamp": 1777045625, "temperature": 20.07809, "humidity": 52.88181, "battery": 4.20411}

Однако на данном этапе, скорее всего вы не увидите никакой информации, т.к. нужно подписаться на данные топики, чтобы MQTT брокер смашрутизировал данные. Для этого нужно добавить подписчика, который будет зачитывать данные и писать их уже в базу данных, о чем будет рассказано ниже.

ESP-Sensors Consumer​


Сам проект находится по ссылке GitHub - Levon24/esp-sensors-consumer: This is consumer for MQTT to store event messages to database https://github.com/Levon24/esp-sensors-consumer и аналогичен проекту GitHub - Levon24/HT2000: CO2 counter for HT2000 https://github.com/Levon24/HT2000 который описан в статье HT2000 Сбор и хранение данные по CO2 https://habr.com/ru/articles/871380/ но вместо чтения данных из usb данные читаются из MQTT брокера.

Данный сервис поднимается на сервере с базой данных для grafana, у меня это не тот же сервис, где находится MQTT брокер, а тот, где расположен датчик с HT2000. Я так сделал для своего удобства, вы же можете все на одном сервере разместить. Ну как сервере, это железка Orange Pi Zero 2W размером 65 на 30 мм и минимальной стоимостью.

Чтением данных из брокера занимается сервис EspSensorsService, в нем, для первой версии, прописаны в коде топики и сенсоры для сохранения данных в базу данных.

Код:
  private static final Map<String, String> sensors = Map.of(
    "sensors/kitchen", "kitchen",
    "sensors/cabinet", "cabinet"
  );

В последующих версиях я планирую сделать конфигурируемым этот маппинг, но для PoC решил оставить пока так, чтобы не усложнять сервис. Чтение данных из MQTT брокера построено по асинхронной архитектуре и добавлена поддержка пере-подсоединения:

Код:
  @PostConstruct
  public void init() throws MqttException {
    mqttClient = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
    mqttClient.setCallback(this);

    final MqttConnectOptions connectOptions = new MqttConnectOptions();
    connectOptions.setCleanSession(true);
    connectOptions.setAutomaticReconnect(true);
    connectOptions.setKeepAliveInterval(60);
    logger.info("Connecting to broker: {}.", brokerUrl);

    mqttClient.connect(connectOptions);
    logger.info("Initialized");
  }

Как только данные приходят, сервис кладет из в базу данных:

Код:
  @Override
  public void messageArrived(String topic, MqttMessage message) {
    final String payload = new String(message.getPayload());
    logger.info("MessageArrived: {} on topic: {}.", payload, topic);

    try {
      final EventDto eventDto = objectMapper.readValue(payload, EventDto.class);
      logger.debug("Message EventDto: {}.", eventDto);

      final Event event = new Event();

      final Timestamp timestamp;
      if (eventDto.timestamp() != null && eventDto.timestamp() > 0) {
        timestamp = new Timestamp(eventDto.timestamp() * 1000);
        logger.debug("Timestamp: {}.", timestamp);
      } else {
        timestamp = new Timestamp(System.currentTimeMillis());
      }
      event.setTimestamp(timestamp);

      final String sensor = sensors.get(topic);
      event.setSensor(sensor);

      event.setTemperature(eventDto.temperature());
      event.setHumidity(eventDto.humidity());
      event.setBattery(eventDto.battery());

      final Event s = eventService.save(event);
      logger.info("Saved event: {}.", s);
    } catch (Exception e) {
      logger.warn("Convert - Error: {}.", e.getMessage(), e);
    }
  }

Обратите внимание, что поле timestamp может не прийти, т.к. я планирую разработку датчиков, которые будут передавать показания через BLE протокол, я уже закупил пару контроллеров CH592F для изучения их в свободное время. Собственно предполагается, что у контроллеров с BLE не будет доступа к сервису NTP для получения точного времени.

Код:
  @Override
  public void messageArrived(String topic, MqttMessage message) {
    final String payload = new String(message.getPayload());
    logger.info("MessageArrived: {} on topic: {}.", payload, topic);

    try {
      final EventDto eventDto = objectMapper.readValue(payload, EventDto.class);
      logger.debug("Message EventDto: {}.", eventDto);

      final Event event = new Event();

      final Timestamp timestamp;
      if (eventDto.timestamp() != null && eventDto.timestamp() > 0) {
        timestamp = new Timestamp(eventDto.timestamp() * 1000);
        logger.debug("Timestamp: {}.", timestamp);
      } else {
        timestamp = new Timestamp(System.currentTimeMillis());
      }
      event.setTimestamp(timestamp);

      final String sensor = sensors.get(topic);
      event.setSensor(sensor);

      event.setTemperature(eventDto.temperature());
      event.setHumidity(eventDto.humidity());
      event.setBattery(eventDto.battery());

      final Event s = eventService.save(event);
      logger.info("Saved event: {}.", s);
    } catch (Exception e) {
      logger.warn("Convert - Error: {}.", e.getMessage(), e);
    }
  }

Разворачивание начальной структуры базы данных сделано аналогично как и в проекте HT2000, сам скрипт на разворачивание базы данных находится тут esp-sensors-consumer/scripts/init.sql at master · Levon24/esp-sensors-consumer https://github.com/Levon24/esp-sensors-consumer/blob/master/scripts/init.sql. После инициализации базы данных, будет запущен скрипт для создания единственной таблицы с событиями.

Grafana​


Установка и настройка Grafana изначально производились для прибора HT2000, что было описано в статье HT2000 Сбор и хранение данные по CO2 https://habr.com/ru/articles/871380/, и здесь я не вижу особого смысла повторять материал. Единственный момент: если всё было настроено для HT2000, то для первоначальной настройки необходимо добавить новый DataSource «esp-sensors» для базы данных MariaDB. Далее можно загрузить готовые графики, например, из файла esp8266-temperature-humidity-monitor/Docs/esp-sensors.json at master · Levon24/esp8266-temperature-humidity-monitor https://github.com/Levon24/esp8266-temperature-humidity-monitor/blob/master/Docs/esp-sensors.json, либо настроить их вручную.

Отображение графиков

Отображение графиков

Для ручной конфигурации я приведу пример запроса на температуру для датчика, расположенного на кухне.

Код:
SELECT $__timeGroup(timestamp, '1m') AS time, avg(temperature) AS value, min(temperature) AS min, max(temperature) AS max
FROM events
WHERE $__timeFilter(timestamp) 
  AND sensor = 'kitchen'
GROUP BY time

Наверное, можно создать шаблоны и использовать их, но я пока не разобрался как это делать. Я использовал функцию клонирования.

Итоги​


Данная статья — это попытка реализовать мониторинг квартиры с использованием относительно недорогих чипов. Необходимость в таком мониторинге возникла после покупки недорогих метеостанций и понимания, что их показания недостаточно точны — это выяснилось в ходе экспериментов с увлажнителями. Потребовались более точные результаты. Так и появился данный проект проверки концепции, на что-то большее я и не рассчитывал.

В статье рассмотрена только техническая составляющая измерительного комплекса и не затронута тема влажности воздуха и сухости кожи, а также их влияния на здоровье человека в отопительный сезон. После рождения дочери возникла задача быстро собрать такой мониторинг, поскольку простые бытовые модели за 100–300 рублей постоянно показывали «LL» на индикаторе (Low Level), и ориентироваться по ним было сложно.

Я также сделал вывод, что недорогие ультразвуковые увлажнители, несмотря на обильное визуальное выделение пара, недостаточно сильно увлажняют воздух. Именно поэтому на индикаторе требуется такая точность в процентах — чтобы улавливать незначительные изменения параметров. Небольшая батарея из 2–3 ультразвуковых увлажнителей не заменит хорошую мойку воздуха, которая за ночь способна испарить до 5–10 литров воды.

Берегите себя, всем мира и добра!
 
Назад
Сверху Снизу
Яндекс.Метрика Рейтинг@Mail.ru