Зачем сон микроконтроллеру
ESP32 в активной работе ест 80–240 мА — от аккумулятора 18650 (~3000 мА·ч) протянет сутки-двое. Для автономного датчика, просыпающегося раз в час, это катастрофически мало. Deep sleep отключает почти всё: процессор, Wi-Fi, периферию — остаются только RTC-часы и слабые сторожа. Потребление падает до ~10–150 мкА — батарея живёт месяцами.
Три режима сна ESP32
- Light sleep — процессор остановлен, RAM и периферия живы; просыпается мгновенно по GPIO/таймеру; ~0.8 мА.
- Deep sleep — почти всё выключено, только RTC-подсистема; ~10–150 мкА; при пробуждении — полный рестарт.
- Hibernation — ещё глубже, даже RTC-таймер выключен; просыпается только по GPIO.
Для автономки — deep sleep: почти ноль потребления, а «рестарт» вместо пробуждения для скетча прозрачен.
Сон по таймеру
Простейший сценарий: просыпаться раз в N минут, сделать дело и снова спать:
const uint64_t SLEEP_US = 10 * 60 * 1000000ULL; // 10 минут в микросекундах
void setup() {
Serial.begin(115200);
readSensor();
sendData();
esp_sleep_enable_timer_wakeup(SLEEP_US);
esp_deep_sleep_start(); // дальше этой строки код не выполнится!
}
void loop() {} // не дойдёт никогда
После пробуждения ESP32 стартует с setup() заново — как при включении питания. Для кода, который должен помнить что-то между снами, есть RTC-память.
RTC-память: данные между снами
Обычные переменные стираются при рестарте. Пометив переменную RTC_DATA_ATTR, вы кладёте её в RTC-область, которая не обесточивается в deep sleep:
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float lastTemp = NAN;
void setup() {
bootCount++;
Serial.printf("пробуждение #%d, было %.1f\n", bootCount, lastTemp);
lastTemp = dht.readTemperature();
// ...
esp_deep_sleep_start();
}
bootCount накапливается через все сны — ведём статистику без EEPROM.
Пробуждение по GPIO: кнопка или датчик
Вместо таймера — внешний сигнал: датчик движения, дождевая капля, кнопка:
// проснуться когда GPIO2 станет HIGH
esp_sleep_enable_ext0_wakeup(GPIO_NUM_2, 1);
// или по любому из нескольких пинов — ext1 (только RTC GPIO)
esp_sleep_enable_ext1_wakeup(
(1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5),
ESP_EXT1_WAKEUP_ANY_HIGH);
Только RTC-GPIO пины работают в deep sleep — на ESP32 это GPIO 0,2,4,12–15,25–27,32–39. Проверяйте карту вашей платы.
Причина пробуждения
После рестарта полезно знать, что нас разбудило:
void setup() {
esp_sleep_wakeup_cause_t why = esp_sleep_get_wakeup_cause();
switch (why) {
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("таймер"); break;
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("GPIO"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("мульти-GPIO"); break;
case ESP_SLEEP_WAKEUP_UNDEFINED: Serial.println("питание/reset"); break;
default: break;
}
}
Полный пример: метеостанция на батарее
#include <DHT.h>
#include <WiFi.h>
#include <HTTPClient.h>
RTC_DATA_ATTR int bootCount = 0;
DHT dht(4, DHT22);
void setup() {
bootCount++;
dht.begin();
float t = dht.readTemperature();
WiFi.begin("SSID", "pass");
int tries = 0;
while (WiFi.status() != WL_CONNECTED && tries++ < 40) delay(500);
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin("https://api.example.com/log?t=" + String(t));
http.GET();
http.end();
}
WiFi.disconnect(true);
esp_sleep_enable_timer_wakeup(30 * 60 * 1000000ULL); // 30 минут
esp_deep_sleep_start();
}
void loop() {}
Цикл: проснулся → измерил → отправил → спать. Активная фаза ~5–10 секунд, сон 30 минут — на 18650 хватит на год.
Сколько реально съедает
Проверьте амперметром:
- Актив с Wi-Fi: ~150–240 мА.
- Актив без Wi-Fi: ~50–80 мА.
- Deep sleep на голом чипе: ~10 мкА.
- На DevKit-плате: ~5–10 мА (светодиоды, USB-UART, стабилизатор едят!).
Для реальной автономки берите «голые» платы (ESP32-WROVER bare, FireBeetle) или отключайте питание периферии транзистором — иначе стабилизатор AMS1117 съест батарею быстрее чипа.
Типичные проблемы
- Устройство не спит:
loopс кодом вместоesp_deep_sleep_start; после начала сна ничего не выполнится. - Потребление выше 100 мкА: светодиод питания на DevKit, USB-UART чип, стабилизатор — они живы в сне.
- Не просыпается по GPIO: пин не RTC-GPIO или уровень неправильный (ext0 ловит уровень, не фронт).
- Данные теряются: переменная без
RTC_DATA_ATTRили в обычной памяти — рестарт обнуляет. - Wi-Fi долго не подключается: сокращайте попытки, кэшируйте канал/BSSID — экономит секунды активности.
Итог
Deep sleep превращает ESP32 из «50 мА активности» в «10 мкА сна»: esp_sleep_enable_timer_wakeup, RTC_DATA_ATTR для данных, EXT0/EXT1 для пробуждения по событию. Схема простая: в setup делаем всё дело, в конце — esp_deep_sleep_start, loop пустой. И помните — DevKit-платы съедают экономию своей обвязкой, для реальной автономки берите минимальные платы.
Полный пример: датчик с пробуждением по таймеру
#include <WiFi.h>
#include <DHT.h>
RTC_DATA_ATTR int wakeCount = 0;
RTC_DATA_ATTR float lastTemp = -100;
const uint64_t SLEEP_US = 10 * 60 * 1000000ULL; // 10 минут
void setup() {
Serial.begin(115200);
++wakeCount;
esp_sleep_wakeup_cause_t why = esp_sleep_get_wakeup_cause();
Serial.printf("пробуждение #%d, причина: %d\n", wakeCount, why);
DHT dht(4, DHT22);
dht.begin();
float t = dht.readTemperature();
if (!isnan(t)) lastTemp = t;
Serial.printf("температура: %.1f\n", lastTemp);
// отправка по Wi-Fi, если нужно
WiFi.begin("SSID", "pass");
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
// mqtt.publish("home/temp", String(lastTemp));
WiFi.disconnect(true);
}
esp_sleep_enable_timer_wakeup(SLEEP_US);
Serial.println("сон на 10 минут");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}
Пробуждение по кнопке
// RTC_GPIO 4 — проснуться при LOW (кнопка замыкает на GND)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 0);
esp_deep_sleep_start();
Пробуждение по нескольким пинам
// любой из двух пинов RTC_GPIO
esp_sleep_enable_ext1_wakeup(
(1ULL << GPIO_NUM_33) | (1ULL << GPIO_NUM_32),
ESP_EXT1_WAKEUP_ANY_HIGH);
Питание от аккумулятора
- DevKit-платы жрут в сне 5–10 мА (USB-UART, светодиод, стабилизатор).
- Голый чип (ESP32-WROOM) — ~10 мкА.
- Pro Mini/FireBeetle/Olimex — минимальная обвязка, микроамперный сон.
- Отключайте питание периферии транзистором — датчики тоже жрут в standby.
Сколько реально потребляет
| Режим | DevKit | Голый чип | |---|---|---| | Актив + Wi-Fi | 150–240 мА | 150–240 мА | | Актив без Wi-Fi | 50–80 мА | 50–80 мА | | Light sleep | ~2 мА | ~0.8 мА | | Deep sleep | 5–10 мА | ~10 мкА | | Hibernation | ~5 мА | ~5 мкА |
Типичные проблемы
- Просыпается сразу: пин в неправильном состоянии, шум на линии.
- Данные потеряны: переменная без
RTC_DATA_ATTR— обычная память обнуляется. - Не спит:
loopкрутится вместоesp_deep_sleep_start. - Долгое подключение к Wi-Fi: тратит батарею; сохраняйте канал/BSSID в RTC.
- Периферия жрёт: отключайте питание датчиков транзистором.