Питание ESP32 и режимы сна: от 260 мА до 10 микроампер
Датчик, который отправляет показания раз в полчаса, может прожить от пары батареек год — или разрядить их за неделю. Разница целиком в том, что происходит между отправками. Разбираем режимы сна ESP32, способы проснуться и главную практическую ловушку: почему готовая плата потребляет в сотни раз больше, чем обещает даташит.
Сколько потребляет
Цифры из даташита классического ESP32 при 3,3 В и 25 °C. У ESP32-C3, ESP32-S3 и ESP32-C6 значения свои, но порядок величин и соотношение между режимами те же:
| Режим | Что живёт | Ток |
|---|---|---|
| Активный, радио работает | всё | до 260 мА в пике передачи |
| Modem-sleep, 240 МГц | процессор, радио выключено | 30–68 мА |
| Modem-sleep, 80 МГц | то же, ниже частота | 20–31 мА |
| Light-sleep | ОЗУ и регистры сохраняются | 0,8 мА |
| Deep-sleep | RTC-таймер и RTC-память | 10 мкА |
| Deep-sleep с сопроцессором ULP | плюс ULP | 150 мкА |
| Hibernation | только RTC-таймер | 5 мкА |
| Питание снято (CHIP_PU на землю) | ничего | 1 мкА |
Разница между активным режимом и глубоким сном — примерно в двадцать тысяч раз. Отсюда единственная стратегия для автономных устройств: большую часть времени спать, просыпаться на секунды, сделать дело и заснуть снова.
Чем режимы отличаются
Modem-sleep выключает только радио. Процессор работает, программа выполняется. Включается автоматически, когда Wi-Fi простаивает.
Light-sleep останавливает процессор, но сохраняет содержимое ОЗУ и регистров.
Вызов esp_light_sleep_start() возвращает управление в следующую строку, будто
программа просто подождала. Всё состояние на месте, переменные целы.
Deep-sleep обесточивает почти всё: процессор, основную память, периферию.
Остаются RTC-контроллер, RTC-память и, если нужно, сопроцессор ULP. Пробуждение
равносильно перезагрузке: esp_deep_sleep_start() не возвращается никогда, а
выполнение начинается заново с setup().
Hibernation — крайний случай deep-sleep, где выключена и RTC-память. Живёт только таймер.
Сон по таймеру
#define SLEEP_SECONDS 30
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
delay(1000);
bootCount++;
Serial.printf("Пробуждение номер %d\n", bootCount);
// здесь полезная работа: прочитать датчик, отправить, записать
esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * 1000000ULL);
Serial.println("Засыпаю");
Serial.flush();
esp_deep_sleep_start(); // управление сюда не вернётся
}
void loop() {
}
Обратите внимание на три детали. Аргумент esp_sleep_enable_timer_wakeup() — в
микросекундах, отсюда множитель. Serial.flush() перед сном обязателен, иначе
последние строки не успеют уйти в порт. И весь код живёт в setup(): до loop()
дело просто не доходит.
Атрибут RTC_DATA_ATTR кладёт переменную в RTC-память, которая переживает глубокий
сон. Именно так между пробуждениями сохраняют счётчики и состояние. Но перезагрузку
по питанию или кнопке сброса такая переменная не переживёт — для этого нужен
NVS через Preferences.
Пробуждение по выводу
У классического ESP32 два механизма.
ext0 — один вывод и заданный уровень:
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0); // проснуться, когда GPIO 33 станет LOW
ext1 — несколько выводов сразу, задаются битовой маской:
esp_sleep_enable_ext1_wakeup(BIT64(GPIO_NUM_32) | BIT64(GPIO_NUM_33),
ESP_EXT1_WAKEUP_ANY_HIGH);
Оба работают только с выводами, относящимися к RTC. У классического ESP32 это GPIO 0, 2, 4, 12–15, 25–27 и 32–39.
У классического ESP32 для ext1 доступны только два режима: ESP_EXT1_WAKEUP_ANY_HIGH
(любой из выбранных выводов стал высоким) и ESP_EXT1_WAKEUP_ALL_LOW (все стали
низкими). Режима «любой стал низким» там нет — его добавили только в более поздних
чипах. Если нужна кнопка на землю, используйте ext0.
Узнать причину пробуждения можно так:
switch (esp_sleep_get_wakeup_cause()) {
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Таймер"); break;
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Вывод (ext0)"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Вывод (ext1)"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Касание"); break;
default: Serial.println("Обычное включение"); break;
}
Отличия других чипов
Карта возможностей заметно различается:
| Чип | ext0 | ext1 | Пробуждение по касанию | Выводы для пробуждения |
|---|---|---|---|---|
| ESP32 | Да | Да | Да | GPIO 0, 2, 4, 12–15, 25–27, 32–39 |
| ESP32-S3 | Да | Да | Да | GPIO 0–21 |
| ESP32-C6 | Нет | Да | Нет | 8 выводов RTC |
| ESP32-C3 | Нет | Нет | Нет | только GPIO 0–5 |
У ESP32-C3 выводов RTC нет вовсе, и функции esp_sleep_enable_ext0_wakeup() там
просто не существует — код с ней не скомпилируется. Вместо неё используется другой
механизм:
// только для ESP32-C3, только выводы GPIO 0–5
esp_deep_sleep_enable_gpio_wakeup(BIT64(GPIO_NUM_3), ESP_GPIO_WAKEUP_GPIO_LOW);
Это одна из тех вещей, из-за которых скетч, отлаженный на DevKit, отказывается работать на ESP32-C3 Super Mini.
Почему плата ест больше даташита
Десять микроампер в таблице относятся к чипу, а не к плате. На типовой отладочной плате рядом с ним стоят потребители, которые не засыпают вместе с ним:
- Стабилизатор питания — имеет собственный ток покоя и продолжает его потреблять, пока подано питание.
- Светодиод питания — горит постоянно, обычно несколько миллиампер.
- Мост USB-UART — потребляет, даже когда обмена нет. У плат на ESP32-C3 и ESP32-S3 с собственным USB отдельного моста нет, и это слагаемое отпадает.
В сумме типичная плата в глубоком сне потребляет единицы или первые десятки миллиампер вместо микроампер. Разброс большой: у экономичных плат без лишней обвязки цифра заметно ниже, и проверять её нужно на своём экземпляре.
Точной цифры Espressif не публикует: в документации на DevKit не указаны даже модели стабилизатора и моста. Независимые замеры дают порядок 5–15 мА, но проверять стоит на своей плате — разброс между клонами большой.
Что с этим делают:
- Выпаивают светодиод питания. Самый простой шаг, экономит несколько миллиампер.
- Берут модуль, а не плату. Голый WROOM с собственным стабилизатором с малым током покоя даёт те самые микроамперы.
- Считают честно. Если устройство должно жить год, сначала измерьте реальное потребление, а потом считайте ёмкость батареи.
Как считать время работы
Средний ток складывается из потребления во сне и коротких всплесков активности:
средний ток = ток сна + (ток работы × время работы) / период
Датчик, который просыпается на 3 секунды каждые 30 минут и тратит 120 мА на передачу:
(120 мА × 3 с) / 1800 с = 0,2 мА плюс ток сна
С голым модулем (0,01 мА) выходит около 0,21 мА, и аккумулятор 18650 на 2500 мАч теоретически протянет больше года. С обычным DevKit, потребляющим во сне 10 мА, средний ток станет 10,2 мА — и та же батарея сядет за десять дней. Разница исключительно в обвязке платы.
Что ещё экономит
Понизить частоту процессора, когда полная скорость не нужна:
setCpuFrequencyMhz(80); // вместо 240
Выключать Wi-Fi между сеансами связи:
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
Сокращать время подключения. Установка соединения — самая дорогая часть цикла: плата держит радио включённым несколько секунд. Помогает статический IP и запоминание канала и BSSID точки доступа: это убирает сканирование и получение адреса по DHCP. Про статический адрес — в статье Подключение ESP32 к Wi-Fi.
Что дальше
- Как сохранить данные между пробуждениями — Хранение данных на ESP32.
- Какие чипы экономичнее — Какую ESP32 выбрать.
- Выводы, пригодные для пробуждения — Пины ESP32.