Своими рукамипрактическая инструкция источник: fermozavr

Метеостанция в теплице: собираем датчик с сообщениями в Telegram

Редакция Фермозавра·9 апреля 2026 г.·8 мин чтения

Как меняется температура у растений, пока вы заняты другими делами? Подключим DHT22 к ESP32 и научим его сообщать о холоде, жаре и высокой влажности.

Метеостанция в теплице: собираем датчик с сообщениями в Telegram

В одной части теплицы уже жарко, возле двери прохладнее, а телефон пока ничего об этом не знает. Дадим ему простой источник новостей: ESP32 с датчиком температуры и влажности DHT22. Поставим датчик у растений и получим сообщение, когда показания выйдут за выбранные пределы.

Начинаем с лабораторной сборки: подключение, полный скетч и первые опыты. Она сообщает об измеренных условиях. Место датчика, рабочие пороги и связь проверим перед установкой в теплице.

Собираем точку наблюдения

  • Плата на классическом ESP32, например ESP32-DevKitC V4, с подходящим USB-кабелем.
  • DHT22 или модуль на его основе; назначение контактов сверяют по маркировке и документации конкретного изделия.
  • Резистор подтяжки DATA к питанию, если его нет на модуле. В схеме ниже — 10 кОм.
  • Стабилизированный USB-источник для платы, провода и макетная плата для сухого стенда.
  • Для установки — подходящий корпус электроники, кабельные вводы и защищённое размещение датчика воздуха.
ESP32 3V3 → DHT22 VCC
ESP32 GND → DHT22 GND
ESP32 GPIO4 → DHT22 DATA
DATA → резистор 10 кОм → ESP32 3V3

У отдельного датчика и готового модуля могут различаться порядок выводов и встроенная подтяжка. Обозначение GPIO4 не следует автоматически заменять надписью D4 на любой плате. Схема рассчитана на совместимый с питанием 3,3 В DHT22; если модуль требует иного питания, нужно согласовать и уровень сигнала.

Не переключайте питание датчика на 5 В как способ устранения ошибки, сохраняя подтяжку DATA к его питанию: так на вход ESP32 может попасть 5 В. Сначала проверьте контакты, распиновку, резистор и документацию. Для ESP32-DevKitC V4 производитель требует выбрать один способ питания — USB, 5V/GND либо 3V3/GND — и не соединять их одновременно.

Создаём бота, который будет писать вам

  1. Установите Arduino IDE и поддержку ESP32 по инструкции Espressif. Для этого примера зафиксированы Arduino-ESP32 3.3.12 и плата ESP32 Dev Module.
  2. Установите DHT sensor library 1.4.7, Adafruit Unified Sensor 1.1.15 и ArduinoJson 7.4.3.
  3. Создайте бота через официальный BotFather и сохраните токен. Откройте личный чат со своим ботом и отправьте /start.
  4. Получите chat.id из обновления своего бота методом getUpdates. Для группы добавьте туда бота, создайте доступное ему сообщение и используйте идентификатор соответствующего чата. У бота должно быть право отправлять сообщения.
  5. Заполните SSID, PASSWORD, BOT_TOKEN и CHAT_ID в скетче. Не публикуйте токен, файл прошивки с секретами и ссылки с токеном. При утечке отзовите токен через BotFather.

Для HTTPS пример использует встроенный набор доверенных сертификатов Arduino-ESP32 и синхронизацию времени. Если время ещё не получено, сообщение остаётся в очереди. При ошибках TLS не отключайте проверку сертификата: проверьте часы, доступность сети и актуальность платформы.

Какие новости присылает теплица

Пусть каждое важное изменение приходит отдельной новостью. Стало холодно — одно событие; затем выросла влажность — другое. Даже если температурное предупреждение уже было, влажность останется заметной. При ошибке датчика придёт сообщение о том, что условия сейчас неизвестны.

Для опыта используются пороги: холод ниже 5 °C с возвратом при 6 °C, жара выше 35 °C с возвратом при 34 °C, влажность выше 90% с возвратом при 85%. Небольшой зазор между входом и выходом убирает поток одинаковых предупреждений возле границы. После знакомства с алгоритмом замените числа по регламенту своей культуры.

Если сеть прервалась, последние 32 изменения ждут в оперативной памяти. Когда места больше нет, старые события уступают место новым, а в отчёт добавляется число пропусков. Для хранения истории после перезапуска следующим шагом станет запись в энергонезависимую память или на сервер.

Отправку удобно проверить по ответу Telegram: HTTP 200 и ok=true означают, что API принял сообщение. После этого событие покидает очередь. При неопределённом сетевом результате возможна повторная отправка. Время измерения в секундах после запуска поможет отличить задержавшуюся новость от свежей.

Скетч: от показания к сообщению

#include <Arduino.h>
#include <WiFi.h>
#include <NetworkClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <time.h>
#include <math.h>

constexpr char SSID[] = "CHANGE_ME", PASSWORD[] = "CHANGE_ME";
constexpr char BOT_TOKEN[] = "CHANGE_ME", CHAT_ID[] = "CHANGE_ME";
constexpr int DHT_PIN = 4;
constexpr uint32_t SAMPLE_MS = 10000, RETRY_MS = 60000, REPORT_MS = 3600000;
constexpr uint8_t COLD = 1, HOT = 2, WET = 4, SENSOR_BAD = 8;
constexpr size_t CAPACITY = 32;
struct Event { uint32_t uptime; uint8_t state; float t, h; };
Event events[CAPACITY];
size_t head = 0, count = 0;
uint32_t dropped = 0, sampledAt = 0, attemptedAt = 0, reportedAt = 0, reconnectAt = 0;
uint32_t retryMs = RETRY_MS;
bool sampled = false, attempted = false;
uint8_t state = SENSOR_BAD;
float temperature = NAN, humidity = NAN;
DHT dht(DHT_PIN, DHT22);

uint8_t classify(float t, float h, uint8_t previous) {
  if (!isfinite(t) || !isfinite(h) || h < 0 || h > 100) return SENSOR_BAD;
  uint8_t next = 0;
  // Учебные пороги; подбираются по регламенту хозяйства.
  if (t < ((previous & COLD) ? 6.0f : 5.0f)) next |= COLD;
  if (t > ((previous & HOT) ? 34.0f : 35.0f)) next |= HOT;
  if (h > ((previous & WET) ? 85.0f : 90.0f)) next |= WET;
  return next;
}
void enqueue(Event e) {
  if (count == CAPACITY) { head = (head + 1) % CAPACITY; --count; ++dropped; }
  events[(head + count) % CAPACITY] = e;
  ++count;
}
String describe(Event e) {
  String s = "Теплица. Измерение через " + String(e.uptime / 1000) + " с после запуска.\n";
  if (e.state & SENSOR_BAD) return s + "Ошибка датчика: условия неизвестны.";
  s += "T=" + String(e.t,1) + " °C; RH=" + String(e.h,1) + " %.\n";
  if (!e.state) s += "Показатели в заданных пределах.";
  if (e.state & COLD) s += "Ниже порога температуры. ";
  if (e.state & HOT) s += "Выше порога температуры. ";
  if (e.state & WET) s += "Выше порога влажности.";
  return s;
}
bool sendTelegram(const String& text) {
  if (WiFi.status() != WL_CONNECTED || time(nullptr) < 1735689600) return false;
  if (String(BOT_TOKEN) == "CHANGE_ME" || String(CHAT_ID) == "CHANGE_ME") return false;
  NetworkClientSecure client;
  client.useBuiltinCACertBundle();
  client.setHandshakeTimeout(5);
  HTTPClient http;
  http.setConnectTimeout(3000);
  http.setTimeout(5000);
  const String url = String("https://api.telegram.org/bot") + BOT_TOKEN + "/sendMessage";
  if (!http.begin(client, url)) return false;
  JsonDocument request;
  request["chat_id"] = CHAT_ID;
  request["text"] = text;
  String payload;
  serializeJson(request, payload);
  http.addHeader("Content-Type", "application/json");
  const int code = http.POST(payload);
  String response = http.getString();
  http.end();
  JsonDocument result;
  const bool parsed = !deserializeJson(result, response);
  if (code == 429 && parsed) {
    uint32_t seconds = result["parameters"]["retry_after"] | 60U;
    if (seconds < 60) seconds = 60;
    if (seconds > 86400) seconds = 86400;
    retryMs = seconds * 1000U;
  } else retryMs = RETRY_MS;
  const bool ok = code == 200 && parsed && result["ok"].is<bool>() && result["ok"].as<bool>();
  Serial.printf("Telegram HTTP=%d accepted=%d\n",code,ok); // Не печатаем токен и тело ответа.
  return ok;
}
void setup() {
  Serial.begin(115200);
  dht.begin();
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);
  WiFi.begin(SSID, PASSWORD);
  configTime(0, 0, "pool.ntp.org", "time.cloudflare.com");
  delay(2000); // Начальная пауза для датчика; сеть не ожидаем.
  // Ожидания Wi-Fi нет: измерения продолжаются и без сети.
}
void loop() {
  uint32_t now = millis();
  if (WiFi.status() != WL_CONNECTED && uint32_t(now-reconnectAt) >= 30000) {
    reconnectAt = now;
    WiFi.reconnect();
  }
  if (!sampled || uint32_t(now-sampledAt) >= SAMPLE_MS) {
    sampledAt = now;
    temperature = dht.readTemperature(); humidity = dht.readHumidity();
    uint8_t next = classify(temperature, humidity, state);
    if (!sampled || next != state) enqueue({now,next,temperature,humidity});
    state = next; sampled = true;
  }
  if (attempted && uint32_t(now-attemptedAt) < retryMs) return;
  if (WiFi.status() != WL_CONNECTED) return;
  bool hourly = uint32_t(now-reportedAt) >= REPORT_MS;
  if (!count && !dropped && !hourly) return;
  attempted = true; attemptedAt = now;
  if (dropped) {
    String s = "Очередь переполнилась: пропущено переходов " + String(dropped) + ".\n";
    s += describe({now,state,temperature,humidity});
    if (sendTelegram(s)) dropped = 0;
  } else if (count) {
    if (sendTelegram(describe(events[head]))) { head=(head+1)%CAPACITY; --count; }
  } else if (sendTelegram("Периодический отчёт.\n" + describe({now,state,temperature,humidity}))) {
    reportedAt = now;
  }
}

Опрос запланирован раз в десять секунд; ожидание сетевого ответа может его задержать. Между попытками отправки проходит хотя бы минута, а при ограничении Telegram учитывается retry_after. Когда событий нет, раз в час приходит обычный отчёт — удобный повод заглянуть в микроклимат.

Проведите опыт с отключённым Wi-Fi: измерения продолжаются, события ждут возвращения связи. Для контроля полного исчезновения станции нужен отдельный получатель, который замечает пропавшие отчёты. Там, где промедление критично, добавляют местную сигнализацию.

Первая новость из теплицы

  1. На сухом стенде проверьте показания и сравните их с контрольным прибором. Не принимайте цифру на экране за доказательство точности.
  2. Задайте тестовые пороги близко к текущим показаниям, затем проверьте вход в каждое состояние и возврат в норму. Верните рабочие настройки после проверки.
  3. Отключите Wi-Fi, вызовите изменение состояния, восстановите сеть. Событие должно сохраниться в очереди до подтверждения API.
  4. Проверьте неверный токен, недоступный чат и ошибку датчика. В журнале не должно появляться подтверждение accepted=1 при отказе.
  5. Проверьте переполнение очереди и перезапуск. Убедитесь, что выбранные ограничения хранения приемлемы для вашей задачи.
  6. Разместите датчик вне корпуса нагревающейся платы, в защищённой от прямого солнца и воды точке, соответствующей зоне наблюдения. Обеспечьте контакт с воздухом и проверьте влияние корпуса и места установки.

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

Куда двигаться после первого сообщения

Подключение датчика: руководство Adafruit DHT. Питание платы: ESP32-DevKitC V4. Получение обновлений и отправка сообщений: официальный Telegram Bot API. Когда сообщения приходят стабильно, сравните разные места установки датчика: это поможет выбрать точку, которая отвечает на ваш вопрос о теплице.

Автономное питание датчика пригодится для места, куда неудобно тянуть USB-провод.

Управление вентиляцией теплицы поможет перейти от наблюдения к движению форточки.

Источник: fermozavr

💬 Комментарии

Чтобы оставить комментарий, войдите или зарегистрируйтесь

Загрузка комментариев...

Похожие статьи