Как сделать дешевые WiFi датчики температуры/влажности

О чем этот текст

Тут я рассказываю как я собрал датчик температуры и влажности с WiFi. Вставляешь это устройство в розетку, оно подключается к WiFi сети и два раза в минуту отправляет данные на сервер.

Если все делать самому, то стоимость устройства получается меньше 400 рублей (без учета usb провода и блока питания).

В этом тексте я описываю как аппаратную часть устройства, так и программную.

В итоге у меня получилось работающее решение. Данные собираются и можно посмотреть на график с историей, но все можно сильно улучшить.

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

Подключенный WiFi датчик температуры и влажности

История

У меня есть несколько YouTube каналов которые я иногда смотрю. Один из них — это Chillichump. Человек живет недалеко от Лондона и у себя в садике в парнике выращивает острые перцы, а потом из них делает соусы. Канал отличный, автор явно фанатеет от всего этого, рассказывает он интересно, видео хорошего качества.

В основном автор рассказывает о перцах, какие бывают, как их выращивать, как собирать, как ферментировать и как готовить, но иногда он рассказывает про какие-то технические самоделки, которые он делает для того чтобы упростить или улучшить процесс создания острых соусов.

В начале 2019 года он выпустил видео о том как просто собрать датчик температуры и влажности который может отправлять данные на сервер. У него достаточно технологическая теплица: есть датчики, камеры, система автоматического полива. Всем этим он может управлять удаленно.

Вот его видео как собрать датчик температуры/влажности — https://www.youtube.com/watch?v=yqmOp7m4szA ("Easy web-enabled Temperature and Humidity monitoring for your greenhouse").

Судя по видео — все очень просто. Берется несколько деталей, все они спаиваются и получается работающая конструкция которая умеет отправлять данные на сервер по WiFi.

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

Так что я решил повторить то что описано в видео, спаять датчики и собирать с них данные.

Покупка устройств

Автор Chillichump в своем видео использует датчик AM2302 и Arduino совместимое устройство Wemos D1 Mini.

Я заказал на AliExpress штуки которые очень похожи на то что использовал автор:

Заказал и оплатил на сайте 13 августа, а 2 сентября я забрал посылку с почты (вообще пришло чуть раньше). Буду считать что пришло за 20 дней.

Датчики AM2302 и компьютеры на базе ESP8266

Я не заказывал, но вообще для сборки датчика нужны еще компоненты:

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

Схема подключения

У датчики AM2302 4 ноги, из которых нужно 3:

pin1 - питание 3v3
pin2 - данные
pin3 - не используется
pin4 - земля

У D1 Mini куча разъемов , в это проекте используются 3:

pin8 3v3 — питание
pin15 G - земля
pin20 D1 — GPIO5

Все подключается очень просто — питание к питанию, земля к земле, данные к GPIO5, но еще дополнительно между pin1 и pin2 датчика нужно подключить резистор на 10 кОм.

Для начала я все это быстро подключил на макетной плате:

Сенсор температуры и влажности на базе AM2302 и ESP8266 на макетной плате

Первая версия софта

На устройстве Wemos D1 Mini есть разъем Micro USB. Через него он получает питание.

Если этот USB подключить к ноутбуку, то можно заливать код на устройство, а устройство, в свою очередь, сможет отправлять данные на ноутбук.

Для написания кода и его заливки на устройство используется бесплатная программа. Arduino IDE.

В Arduino IDE нужно выбрать плату "LOLIN(WEMOS) D1 R2 & mini".

Arduino IDE -> Tools -> Board

А чтобы эта плата там появилась нужно в настройках указать "Additional Boards Manager URLs":

https://arduino.esp8266.com/stable/package_esp8266com_index.json

Arduino IDE preferences

А еще нужно добавить в Arduino IDE библиотеку для работы с датчиком "DHT sensor library". Вот ссылка на гитхаб https://github.com/adafruit/DHT-sensor-library.

Arduino IDE libraries

После того как в Arduino IDE добавлена плата и подключена библиотека — можно написать код который будет читать данные с датчика и отображать его. Тут еще нет никакого взаимодействия по WiFi, данные отправляются на компьютер по USB.

#include "DHT.h"

#define DHTPIN 5
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  Serial.println("Initialised...");

  dht.begin();
}

void loop() {
  delay(1500);

  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (isnan(h) || isnan(t)) {
    Serial.println("Error. Can't get humidity/temperature.");
    return;
  }

  Serial.print("Temperature: ");
  Serial.print(t);

  Serial.print(" ");

  Serial.print("Humidity: ");
  Serial.print(h);

  Serial.println();
}

Компилируем и загружаем код на устройство, включаем Serial Monitor в Arduino IDE и видим что каждые 1.5 секунды на экране появляется числа с температурой и влажностью.

Сборка устройств

Сборка на монтажной плате и первая первая софта показываю что все работает. Следующий этап — нужно спаять устройства.

Я пайки я использовал многожильный провод 0.75 мм, но это слишком толстый. Пришлось убирать некоторые жилы из-за этого сборка проходила дольше чем могла бы. Я использовал термоусадочные трубки чтобы закрыть контакты на датчике AM2302, но из-за того что к ногам AM2302 еще и припаивается резистор использовать трубки было неудобно.

Я снимал видео как я паял, потом ускорил это видео и выложил на YouTube. Час пайки за одну минуту — https://www.youtube.com/watch?v=w4MWFH6uB1g

Когда я все спаял и стал проверять я понял что я забыл заказать usb провода и блоки питания. Но мне повезло, в своем ящике со всяким компьютерным барахлом я нашел все что мне нужно. Правда, все USB провода и все блоки питания разные.

Датчики температуры-влажности с подключенным проводом Micro USB

USB блоки питания

Отправка данных по WiFi на сервер

Итак, есть спаянные устройства. Есть код который умеет читать данные с датчика и выводить его на компьютере через USB.

Но идея заключается в том чтобы датчик был автономным. Чтобы не нужно было его подключать к компьютеру по USB. Нужно чтобы USB провод от датчика вставляется в USB блок питания, блок питания — в розетку. А датчик подключался к WiFi и отправлял данные на сервер.

Через некоторое время чтения разной документации и примеров с кодом я написал программу в Arduino IDE чтобы этот датчик подключался к WiFi и отправлял данные на сервер с помощью метода POST протокола HTTP.

B теле запроса отправляется json вида

{"t": 13.30, "h": 69.70, "mac": "4C:11:AE:0D:7E:FE"}

В теле запроса отправляются не только данные о температуре и влажности, но еще и MAC адрес устройства. Этот адрес должен быть разным для разных устройств и по нему можно понять с какого датчика пришли показания.

Код работает, но в этом коде мне не нравится что я собираю json руками с помощью конкатенации строк, а не используют какую-нибудь хорошо отлаженную библиотеку.

В коде нужно заменить строки "my_wifi_name", "my_wifi_password", "http://example.com/endpoint", нужно указать реальные данные.

#include <DHT.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>

#define WIFI_NAME "my_wifi_name"
#define WIFI_PASSWORD "my_wifi_password"

#define SERVER_ENDPOINT "http://example.com/endpoint"
#define DELAY_SECONDS 30

#define DHTPIN 5
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);

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

    WiFi.begin(WIFI_NAME, WIFI_PASSWORD);

    while (WiFi.status() != WL_CONNECTED) {
        Serial.println("Connecting to Wi-Fi...");
        delay(500);
    }

    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    Serial.print("Mac address: ");
    Serial.println(WiFi.macAddress());

    dht.begin();
}

void loop() {
    delay(DELAY_SECONDS * 1000);

    if (WiFi.status()== WL_CONNECTED) {

        float t = dht.readTemperature();
        float h = dht.readHumidity();

        if (isnan(t) || isnan(h)) {
            Serial.println("Error. Can't get temperature/humidity.");
            return;
        }

        char body[200];

        char mac[100];
        String stringMac = WiFi.macAddress();
        stringMac.toCharArray(mac, 100);

        sprintf(body, "{\"t\": %.2f, \"h\": %.2f, \"mac\": \"%s\"}", t, h, mac);
        Serial.println(body);

        HTTPClient http;

        http.begin(SERVER_ENDPOINT);
        http.addHeader("Content-Type", "text/plain");
        int httpCode = http.POST(body);
        String payload = http.getString();
        Serial.println(httpCode);
        Serial.println(payload);

        http.end();
    } else {
        Serial.println("Error in WiFi connection");
    }
}

Я проверил работу этого кода в трех ситуациях:

Все эти 3 нештатные ситуации устройство отрабатывает нормально. Как только проблема пропадает, то все само начинает дальше работать. Устройство не нужно перегружать или еще что-то с ним делать.

Серверная часть

Итак, есть датчики которые умеют отправлять данные с помощью метода POST протокола HTTP.

Нужен сервер который умеет принимать такие данные, сохранять их и визуализировать.

Для этих целей я написал маленький сервер, и завернул его в docker.

Исходный код — https://github.com/bessarabov/oak-hollow.

Вот пример отправки данных на сервер с помощью "curl":

curl -X POST -d '{"t": 24.30,"h": 60.80,"mac": "AA:BB:CC:00:11:22"}' http://127.0.0.1:3527/api/dot

Датчик отправляет только MAC адрес, значение температуры и значение влажности. Сервер принимает эти значения, дописывает к ним текущую дату и время и сохраняет эти данные в файл, а еще записывает все это в sqlite базу данных.

Так же в этом репозитории есть еще и микросервис который визуализирует данные с помощью библиотеки Cubism.js.

Вот небольшое видео как меняются данные если нагревать датчик зажигалкой. Для этого видео я поправил код на датчике чтобы он отправлял данные не каждые 30 секунд, а каждые 2 секунды. https://twitter.com/bessarabov/status/1177932537601183752.

Если включить и положить все датчики рядом то видно что данные с них различаются:

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

Визуализация Cubusm.js данныех о температуре и влажности

Запуск в работу

Итого. У меня есть железки — датчики. На них залита программа которая читает значения и отправляет их на сервер. И есть сервер который получает данные и сохраняет их.

Осталось только установить эти датчики:

Сервер сохраняет данные в файлы. Я взял Jupyter и написал код чтобы нарисовать графики температуры влажности:

import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook

f=[]
df_list = []

for root, dirs, files in os.walk("oak-hollow/"):
    for filename in files:
        if filename == '4C:11:AE:0D:81:C2.jsonl':
            print(filename)
            full_filename = root+'/'+filename;
            f.append(full_filename)

for filename in sorted(f):
    df_list.append(pd.read_json(filename, lines=True))

df = pd.concat(df_list)

T = df.iloc[:, 2].values.reshape(-1, 1)
H = df.iloc[:, 0].values.reshape(-1, 1)

X = df.iloc[:, 3].values.reshape(-1, 1)

from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters()

plt.rcParams["figure.figsize"] = (20,10)

days = mdates.DayLocator()
hours = mdates.HourLocator()
x_fmt = mdates.DateFormatter('%Y-%m-%d')

fig, ax = plt.subplots()

ax.set_ylabel('temperature', color='green')
ax.plot(X, T, color='green')
ax.tick_params(axis='y', labelcolor='green')

ax.grid(True)

ax2 = ax.twinx()

ax2.set_ylabel('humidity', color='blue')
ax2.plot(X, H, color='blue')
ax2.tick_params(axis='y', labelcolor='blue')

ax.xaxis.set_major_locator(days)
ax.xaxis.set_minor_locator(hours)
ax.xaxis.set_major_formatter(x_fmt)

fig.autofmt_xdate()

plt.show()

Сначала этот датчик собирал данные в квартире, а потом я его отвез на дачу. Видно насколько эти два помещения отличаются.

В качестве эксперимента я поднял у себя Home Assistant и настроил его чтобы он получал данные с этих сенсоров. Это оказалось очень несложно сделать. Вот пример настроек для файла configuration.yaml:

sensor:
  - platform: rest
    name: dacha_sensors
    resource: http://server/oak-hollow/api/latest
    json_attributes:
      - 4C:11:AE:0C:A8:F2
      - 4C:11:AE:0D:7A:AB
    value_template: 'OK'
  - platform: template
    sensors:
      dacha_hall_temperature:
        value_template: '{{ states.sensor.dacha_sensors.attributes["4C:11:AE:0C:A8:F2"]["t"] }}'
        device_class: temperature
        unit_of_measurement: '°C'
      dacha_hall_humidity:
        value_template: '{{ states.sensor.dacha_sensors.attributes["4C:11:AE:0C:A8:F2"]["h"] }}'
        device_class: humidity
        unit_of_measurement: '%'
      dacha_base_temperature:
        value_template: '{{ states.sensor.dacha_sensors.attributes["4C:11:AE:0D:7A:AB"]["t"] }}'
        device_class: temperature
        unit_of_measurement: '°C'
      dacha_base_humidity:
        value_template: '{{ states.sensor.dacha_sensors.attributes["4C:11:AE:0D:7A:AB"]["h"] }}'
        device_class: humidity
        unit_of_measurement: '%'

Резюме

Получилось отлично. Сенсор очень просто сделать. Компоненты дешевые, работает отлично. Код программы доступен. Сенсор можно заинтегрировать куда угодно.

Но мое решение больше похоже на прототип чем на настоящий продукт. Вот несколько вещей что хотелось бы сделать иначе:

Иван Бессарабов
ivan@bessarabov.ru

7 октября 2019

Этот текст есть и на английском языке