← До всіх проєктів
IoT / Веб Складний

Датчик на батарейці: BME280 + MQTT + deep sleep на місяці автономності

Плата прокидається раз на 10 хвилин, міряє температуру, вологість, тиск і заряд акумулятора, публікує їх у MQTT і знову засинає. Розбираємо, як вичавити максимум з батареї.

Складність
Складний
Час
3–4 h
Категорія
IoT / Веб

Задача

Поставити датчик на балконі чи в теплиці, де немає розетки, і не міняти батарею місяцями. Ключ — deep sleep: у цьому режимі ESP32 споживає ~10 мкА, а прокидається лише на 2–3 секунди для вимірювання й відправки.

Компоненти

  • Плата з низьким струмом сну: FireBeetle ESP32, LOLIN D32, TinyPICO або голий модуль ESP32-WROOM зі стабілізатором HT7333/MCP1700. Звичайні DevKit через USB-UART та AMS1117 споживають 5–15 мА навіть уві сні!
  • Датчик BME280 (I²C) — температура, вологість, тиск
  • Li-Ion 18650 або Li-Po 1000–3000 мА·год
  • Дільник напруги 2 × 100 кОм для вимірювання батареї
  • MQTT-брокер (Mosquitto, наприклад add-on у Home Assistant)

Підключення

Компонент ESP32
BME280 VIN / GND 3V3 / GND
BME280 SDA / SCL GPIO 21 / GPIO 22
Середня точка дільника BAT+ → 100k → • → 100k → GND GPIO 35

Бібліотеки

  • PubSubClient (Nick O'Leary)
  • Adafruit BME280 Library

Код

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_BME280.h>

const char* WIFI_SSID  = "YOUR_SSID";
const char* WIFI_PASS  = "YOUR_PASSWORD";
const char* MQTT_HOST  = "192.168.1.10";
const int   MQTT_PORT  = 1883;
const char* MQTT_TOPIC = "home/balcony/sensor";

const uint64_t SLEEP_MINUTES = 10;
const int BATTERY_PIN = 35;           // через дільник 100k/100k

RTC_DATA_ATTR uint32_t bootCount = 0; // переживає deep sleep

WiFiClient net;
PubSubClient mqtt(net);
Adafruit_BME280 bme;

void goToSleep() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
  esp_deep_sleep_start();
}

void setup() {
  unsigned long started = millis();
  Serial.begin(115200);
  bootCount++;

  if (!bme.begin(0x76)) {
    Serial.println("BME280 not found");
    goToSleep();
  }
  // Forced mode: датчик міряє один раз і засинає сам
  bme.setSampling(Adafruit_BME280::MODE_FORCED,
                  Adafruit_BME280::SAMPLING_X1,   // температура
                  Adafruit_BME280::SAMPLING_X1,   // тиск
                  Adafruit_BME280::SAMPLING_X1,   // вологість
                  Adafruit_BME280::FILTER_OFF);
  bme.takeForcedMeasurement();
  float t = bme.readTemperature();
  float h = bme.readHumidity();
  float p = bme.readPressure() / 100.0F;
  float vbat = analogReadMilliVolts(BATTERY_PIN) * 2 / 1000.0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    if (millis() - started > 10000) {   // не палимо батарею, якщо роутер недоступний
      Serial.println("WiFi timeout");
      goToSleep();
    }
    delay(50);
  }

  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  if (mqtt.connect("esp32-balcony")) {
    char payload[128];
    snprintf(payload, sizeof payload,
             "{\"t\":%.2f,\"h\":%.1f,\"p\":%.1f,\"vbat\":%.2f,\"boot\":%lu}",
             t, h, p, vbat, (unsigned long)bootCount);
    mqtt.publish(MQTT_TOPIC, payload, true);   // retained
    mqtt.loop();
    delay(50);
    mqtt.disconnect();
  }

  Serial.printf("Awake for %lu ms\n", millis() - started);
  goToSleep();
}

void loop() {}  // сюди ніколи не доходимо

Розрахунок автономності

Фаза Струм Тривалість
Активна (Wi-Fi + MQTT) ~120 мА ~3 с
Deep sleep (хороша плата) ~0.05 мА 597 с

Середній струм ≈ 120 × 3 / 600 + 0.05 ≈ 0.65 мА. Акумулятор 2000 мА·год → ≈ 3000 год ≈ 4 місяці.

Як прокидатися швидше (і жити довше)

  1. Статичний IP — WiFi.config(ip, gateway, subnet, dns) економить 0.5–1 с на DHCP.
  2. Запам'ятати канал і BSSID роутера в RTC_DATA_ATTR та передати їх у WiFi.begin(ssid, pass, channel, bssid) — ще мінус секунда.
  3. Рідше публікувати: відправляти дані лише коли температура змінилася більше ніж на 0.2 °C (попереднє значення зберігайте в RTC-пам'яті).
  4. ESP-NOW замість Wi-Fi (див. відповідний проєкт) — активна фаза скорочується до ~100 мс.

Home Assistant

Додайте MQTT-сенсори в configuration.yaml:

mqtt:
  sensor:
    - name: "Balcony temperature"
      state_topic: "home/balcony/sensor"
      unit_of_measurement: "°C"
      device_class: temperature
      value_template: "{{ value_json.t }}"
    - name: "Balcony battery"
      state_topic: "home/balcony/sensor"
      unit_of_measurement: "V"
      device_class: voltage
      value_template: "{{ value_json.vbat }}"

Типові проблеми

  • Батарея сідає за тиждень — плата з USB-UART/AMS1117 або світлодіод живлення. Виміряйте струм сну мультиметром.
  • BME280 показує +2 °C — самонагрів; саме тому використовується forced mode.
  • Показник батареї «стрибає» — додайте конденсатор 100 нФ між GPIO 35 і GND.