Даталоггер погреба я кормил от повербанка и гордился им полтора суток: в ночь на второй день батарея села, лог оборвался на 3:40 — и как раз утренние значения были самыми интересными. Арифметика объяснила всё безжалостно: плата с картой тянет около 120 мА бодрствования, в сутках 86 400 секунд, и повербанку на 10 000 мА·ч с потерями на конвертерах такой клиент не по зубам. Данные пишутся 100-200 секунд в день, а плату я держал включённой все 86 400 — оставшиеся секунды она грела воздух.
Решение — глубокий сон ESP32: между записями чип выключает почти всё и опускается до 10-150 мкА, в тысячи раз ниже бодрствования. Ритм даталоггера укладывается в одну строку: таймер разбудил — прочитал датчик — дописал строку на карту — уснул на десять минут. Ни delay, ни millis-планировщика: после записи управление уходит в сон и в loop не возвращается вовсе. В этом уроке — API сна, расчёт срока от 18650 и две ловушки: devkit-плата, которая не даёт заявленных микроампер, и файловая система, страдающая первой при пропадании питания.
Арифметика: мА против мкА
API сна умещается в две строки: esp_sleep_enable_timer_wakeup(N) заводит будильник в микросекундах, esp_deep_sleep_start() выключает свет. После пробуждения ESP32 начинает не с loop, а с нуля: setup выполняется заново, обычная RAM обнуляется. Счётчик циклов при этом выживает, если переменную объявить с модификатором RTC_DATA_ATTR — под неё выделяется место в 8 КБ памяти RTC-домена, которая питается даже во сне. Туда я кладу номер цикла и последний индекс записи; на Uno ту же задачу решал счётчик включений из урока про EEPROM, здесь энергонезависимая память не нужна вовсе.
Числа с условиями, без которых план бессмыслен. Сам модуль ESP32-WROOM во сне ест 10-15 мкА; голый чип в минимальном режиме опускается к 10 мкА; обвязка с подтяжками и неактивными плавающими пинами может поднять расход до 150 мкА. Бодрствование с выключенным WiFi — 100-120 мА, и карта в момент записи добавляет импульсы 30-100 мА на каждое обращение к сектору. Главная ложка дёгтя — devkit: USB-чип CP2102, стабилизатор AMS1117 и светодиод питания съедают ещё 10-20 мА даже во сне, в тысячу раз больше самого чипа. Глубокий сон на devkit экономит работу скетча, но не батарейку — для реального срока берите голый модуль WROOM или devkit с оторванным светодиодом и питанием мимо AMS1117.
Для полноты картины: у ESP32 есть и промежуточные режимы. Light sleep сохраняет RAM и скетч продолжается с места остановки, но ест 0,8-2 мА — в сотни раз больше глубокого; modem sleep экономит только при активном WiFi и логгеру бесполезен. Выбор между light и deep упирается в вопрос: нужен ли скетчу контекст между записями. Логгеру не нужен — он живёт одним циклом от пробуждения до пробуждения, и глубокий сон отдаёт все свои микроамперы. А вот если между записями надо слушать сеть или кнопку, смотрите в сторону light sleep и внешних источников пробуждения.
#include <SPI.h>#include <SD.h> const int cs = 5; // CS на GPIO5, не на стрэп-пин 12const int sensorPin = 34; // аналоговый вход ESP32 RTC_DATA_ATTR int cyc = 0; // переживает глубокий сон void setup() { Serial.begin(115200); // без ожидания: секунды стоят денег cyc = cyc + 1; int v = analogRead(sensorPin); // датчик, 0-4095 if (!SD.begin(cs)) { // карта монтируется заново каждый раз Serial.println("SD error"); } else { File f = SD.open("log.csv", FILE_APPEND); if (f) { f.print(cyc); f.print(","); f.println(v); f.close(); // close ДО сна, не после } } Serial.print("cyc="); Serial.println(cyc); esp_sleep_enable_timer_wakeup(600ULL * 1000000ULL); // 10 минут esp_deep_sleep_start(); // дальше код не выполняется} void loop() { } // сюда управление не попадаетloop() пустой — и это переворот привычки из урока про millis вместо delay: там мы разгребали loop от блокировок ради отзывчивости, здесь отзывчивость не нужна — бодрствование длится три секунды, и всё это время скетч занят только датчиком и записью. После esp_deep_sleep_start() выполнение не возвращается: следующая строка кода, которая исполнится, — первая строка setup при пробуждении. FILE_APPEND — режим дозаписи из ESP32-библиотеки SD; на Uno его нет, там FILE_WRITE, который ставит курсор в конец, — поведение то же.
Теперь расчёт срока. Средний ток равен току бодрствования, умноженному на долю бодрствования, плюс току сна на долю сна. Мой цикл: 3 секунды бодрствования каждые 600 секунд, доля 0,5 процента; 0,005 × 120 мА + 0,995 × 0,015 мА ≈ 0,62 мА. Батарейка 18650 на 2400 мА·ч: 2400 / 0,62 / 24 ≈ 160 суток — пять месяцев без подзарядки. Та же прошивка на devkit, где во сне ест 15 мА: 2400 / 15,6 / 24 ≈ 64 часа, меньше трёх суток. Разница не в коде, а в железе, и её надо посчитать до сборки, а не после первого оборванного лога. Запас тоже заложите: паспортные 2400 мА·ч у дешёвых 18650 — это маркетинг; честная ёмкость у них 1200-1500, и срок делится пополам. Я беру банки с заявкой 3000+ от приличных производителей и всё равно считаю срок по 2400.
| Интервал записи | Бодрствует в сутки | Средний ток | Срок от 18650 на 2400 мА·ч |
|---|---|---|---|
| 1 минута | 72 мин | ≈ 6 мА | ≈ 17 суток |
| 5 минут | 14,4 мин | ≈ 1,2 мА | ≈ 82 суток |
| 10 минут | 7,2 мин | ≈ 0,62 мА | ≈ 5 месяцев |
| 30 минут | 2,4 мин | ≈ 0,22 мА | ≈ 15 месяцев |
Грабли пробуждения
Первая грабель — Serial.begin без ожидания. Каждая лишняя секунда бодрствования в цикле стоит срока: при интервале 10 минут одна лишняя секунда за цикл превращается в 144 лишних секунды в сутки при 120 мА — это около 5 мА·ч в день, треть базового расхода. Печать в Serial успевает пролететь без задержек, зато SD.begin честно требует своих 100-300 мс на монтирование — эти миллисекунды я закладываю в три секунды бодрствования, а не сверху.
Вторая грабель страшнее: CS карты на GPIO12. Это стрэп-пин MTDI — если при включении питания на нём HIGH, контроллер настраивает напряжение флэш-памяти на 1,8 В, и плата не стартует вообще: Serial молчит, светодиод питания горит, скетч не грузится. Мой логгер после пайки выглядел мёртвым ровно по этой причине; перепайка CS на GPIO5 подняла его за минуту. Список пинов, которые нельзя занимать на старте, есть в уроке про распиновку devkit — держите его рядом с паяльником.
Про WiFi: перед сном радиомодуль выключается сам, WiFi.begin вызывать не нужно. Если логгеру нужна отправка данных в сеть, держите её внутри бодрствования: включить — отправить — выключить — уснуть; расклад сил и денег на такой цикл посчитан в уроке про бюджет глубокого сна.
Смотрите в модели не на значения, а на ритм: секунда работы, потом ни одной строки в Serial, потом счётчик циклов подпрыгивает. На реальной плате та же тишина длится десять минут и стоит 10-15 мкА вместо 120 мА — в симуляторе это показать нечем, честно сказать: токов в движке нет. Потенциометр изображает ваш выбор интервала из таблицы: чем длиннее пауза, тем дальше уедет батарейка.
Формат строки на карте — цикл,значение — собирается по правилам урока про CSV и конфиг, а каркас, который здесь дорос до батарейки, родом из урока про логирование. Если логгер нужен не на месяцы, а на годы, посчитайте интервал по таблице и заложите саморазряд: литий-ион теряет 3-5 процента в месяц, и на горизонте года это уже не мелочь. И проверьте держатель: пружинный контакт, карта нигде не болтается — самый дешёвый элемент конструкции роняет самый дорогой лог.
- 1. Сколько ест сам модуль ESP32-WROOM в глубоком сне?
- 2. Что происходит с обычной RAM после пробуждения из глубокого сна?
- 3. Почему devkit-плата не даёт заявленных микроампер во сне?
- 4. Правильный порядок действий перед esp_deep_sleep_start()
- 5. Почему CS карты не вешают на GPIO12?
- Ритм логгера: таймер разбудил — прочитал датчик — дописал и close — уснул; вся жизнь в setup, loop пустой
- Чип во сне 10-15 мкА, бодрствование с записью 100-120 мА; devkit с CP2102 и LED ест 10-20 мА даже во сне
- Интервал правит срок: 3 секунды записи в минуту — около 17 суток от 18650, в 10 минут — около 5 месяцев, в 30 минут — год и больше
- RTC_DATA_ATTR выживает сон: счётчик циклов и индексы держите там, обычная RAM умирает при каждом пробуждении
- close до сна, не после: сбой питания посреди записи сектора ломает FAT всего файла, держатель батарейки — с фиксацией
- CS карты — GPIO5: GPIO12 стрэп-пин MTDI, с HIGH на нём плата не стартует вовсе
Частые вопросы по теме урока
- Сколько ест сам модуль ESP32-WROOM в глубоком сне?
- Модуль в глубоком сне тянет единицы-десятки микроампер; до нуля не бывает — RTC-домен и обвязка всегда потребляют. Зато devkit добавляет к этому 10-20 мА своих микросхем. 10-15 мкА
- Что происходит с обычной RAM после пробуждения из глубокого сна?
- Глубокий сон — это рестарт: setup с нуля и пустая RAM. Выживает только то, что объявлено RTC_DATA_ATTR и живёт в 8 КБ RTC-домена. Обнуляется, setup выполняется заново
- Почему devkit-плата не даёт заявленных микроампер во сне?
- Сон экономит работу чипа, но USB-чип, стабилизатор и LED платы висят на питании всегда. Для реального срока нужен голый модуль или доработка devkit. CP2102, AMS1117 и светодиод питания едят 10-20 мА даже во сне
- Правильный порядок действий перед esp_deep_sleep_start()
- close фиксирует размер файла в каталоге FAT. Всё, что не закрыто до сна, умирает вместе с RAM, а сбой питания посреди сектора роняет весь файл. open — print — close — и только потом сон
- Почему CS карты не вешают на GPIO12?
- GPIO12 — стрэп-пин MTDI: уровень на нём при включении задаёт напряжение флэш-памяти. Карта с CS там роняет старт всей платы, традиционное место для CS — GPIO5. Это стрэп-пин: HIGH на старте переключает питание флэша, и плата не стартует