Сигнализатор протечки под мойкой я собрал на Pro Mini 3,3 В и питал от кроны — 550 мА·ч, свежей, купленной специально. Логика простая: раз в восемь секунд щупаем два контакта на землю, коротнуло — пищим. В скетче стоял delay(8000): мне казалось, что раз плата почти всё время молчит, то и есть почти ничего. Крона села за шесть дней. Чип с delay() жрал свои четыре миллиампера каждую миллисекунду паузы — ожидание не отдых, ожидание это работа вхолостую.
Второй тупик вышел тоньше. Я вспомнил про режимы сна из урока про watchdog и режимы сна, добавил set_sleep_mode(SLEEP_MODE_PWR_DOWN) и sleep_mode() — и чип уснул навсегда: в power-down его будят только прерывание, смена пина или watchdog, а будильника я не поставил. Механику я знал, инструмента собирания цикла «уснуть — проснуться — сделать дело» не было.
Инструмент — библиотека LowPower от Rocket Scream: она упаковывает watchdog-будильник и power-down в одну строку, без единого регистра в вашем коде. Разберём периоды, два режима сна — по кругу и до события, — и честную цену: сколько съедает активная фаза, чтобы сон успевал окупаться. Сон в симуляторе не эмулируется, поэтому модель в конце собрана из переменных — с честной оговоркой прямо в описании макетки. Зато цикл «активность — тишина» и переключение режимов в ней настоящие.
Три строки: powerDown по кругу
Вся магия выглядит так: LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); — и восемь секунд тишины. Первый аргумент — длительность, шаги фиксированные: 15, 30, 60, 120, 250, 500 миллисекунд, затем 1, 2, 4 и 8 секунд, а сверх того — SLEEP_FOREVER: спать до прерывания. Внутри powerDown библиотека сама настраивает сторожевой таймер: предел watchdog — те самые восемь секунд, и именно он будит чип, когда вы просите больше.
Суффиксы ADC_OFF и BOD_OFF — выключатели пожирателей. АЦП во сне не нужен, его отключение экономит копейки. А вот супервизор питания BOD ест порядка двадцати микроампер даже в power-down, и его отключение — самая крупная строка экономии после самого режима. Мои замеры и замеры сообщества сходятся на Pro Mini 3,3 В: 6-8 мкА в паузах после выпайки светодиода питания, при этом сам чип по даташиту берёт меньше микроампера при 3,3 В — остаток добирает обвязка платы.
Почему именно powerDown, а не лёгкий idle: в idle тактовая частота и таймеры продолжают жить, и счёт идёт на миллиамперы, а не микроамперы — разница с power-down на три порядка. Библиотека даёт и другие функции — powerIdle, powerStandby, — но в батарейных узлах я их не помню: либо полный power-down, либо SLEEP_FOREVER, середина не окупается ни в одном моём проекте.
Второй режим — SLEEP_FOREVER: чип засыпает без будильника, и будить его обязано прерывание на INT0 или INT1 либо смена любого пина через PCINT из прошлого урока триплета. Это режим узлов, которые живут от событий: кнопка, геркон, датчик с выходом-фронтом. Периодика и события комбинируются: спим по кругу, но цикл разрывает событие — об этом ниже и в симуляторе.
| Аргумент LowPower | Сколько тишины | Что внутри | Когда беру |
|---|---|---|---|
| SLEEP_15MS … SLEEP_500MS | 15-500 мс | watchdog на коротком пределе | паузы внутри активной фазы, когда loop нечем заняться |
| SLEEP_1S / SLEEP_2S | 1-2 с | watchdog | опрос датчиков с терпимым расходом и живой реакцией |
| SLEEP_4S / SLEEP_8S | 4-8 с | максимум watchdog — мой рабочий вариант | телеметрия и сигнализаторы: активная фаза в сотни раз короче сна |
| SLEEP_FOREVER | до прерывания | будильника нет | узлы от событий: кнопка, геркон, PCINT на любом пине |
После пробуждения первым сюрпризом встречает millis(): она не прибавила восьми секунд. Timer0, который кормит millis() и delay(), во power-down стоит, и вся хронометрия скетча — это ваш счётчик циклов сна: 75 снов по восемь секунд — десять минут, 450 — час. delay() в роли будильника бесполезен вдвойне: он не спит, а жжёт такты активного режима. Вся работа со временем после сна строится на подсчёте циклов — той же дисциплине, которой учит урок про планировщик задач, только тик там задаёт millis(), а здесь — количество снов.
Печать в Serial сразу после пробуждения я всегда закрываю Serial.flush(): flush дожидается отправки байт, и только потом чип снова уходит в powerDown — иначе заснёт посреди строки. И грабля измерений: если плата питается от USB-UART адаптера, вы меряете не крону, а компьютер. Ток батарейного узла смотрят мультиметром в разрыве питания, на пределе микроампер, когда сон уже настроен и активная фаза уложена в миллисекунды.
Активную фазу между снами держите короткой и однообразной: проснулся — прочитал датчик — принял решение — уснул. Всё, что умеет ждать, из фазы вычёркивайте: попытки дозвониться до молчащего модуля, паузы на всякий случай, повторные замеры для успокоения. Мой сигнализатор укладывался в 100 мс: двадцать на опрос контактов, пятьдесят на печать при событии, остаток — запас. Если в скетче живут несколько задач, раскладку по времени даёт урок про кооперативный цикл: LowPower в него встраивается одной строкой на каждом шаге планировщика.
// Сон по кругу на LowPower: отчёт каждые десять минут.// 75 снов по 8 секунд = 600 с. Прошивка для настоящей платы.#include <LowPower.h> const int SVETODIOD = 12;int cikli = 0; // снов прожито с последнего отчётаint minut = 0; // минут тишины всего void setup() { pinMode(SVETODIOD, OUTPUT); Serial.begin(9600); Serial.println("Spim po krugu: otchet raz v 10 minut"); Serial.flush();} void loop() { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); // 8 секунд тишины cikli = cikli + 1; if (cikli >= 75) { // 75 снов = десять минут cikli = 0; minut = minut + 10; digitalWrite(SVETODIOD, HIGH); // вспышка: узел жив delay(50); digitalWrite(SVETODIOD, LOW); Serial.print("minut="); Serial.println(minut); Serial.flush(); // досылаем строку до следующего сна }}Симулятор: по кругу или до события
Сон в симуляторе не эмулируется — вместо него модель: один оборот loop() с задержкой изображает весь восьмисекундный сон, время сжато в разы, и это честно оговорено в описании макетки. Тумблер D3 выбирает режим: включен — периодический, чип просыпается по кругу, вспыхивает светодиодом и раз в пять снов печатает отчёт; выключен — событийный: тишина, пока не нажмёте кнопку D2, это модель wake-прерывания.
Прислушайтесь к разнице характеров. Периодический режим просыпается всегда, даже когда делать нечего, — зато предсказуем и не требует свободных INT-пинов. Событийный молчит неделями — зато ему нужен канал пробуждения: на плате это INT0, INT1 или PCINT любого пина. Мой сигнализатор протечки живёт по схеме «спать по кругу, но событие будит сразу»: цикл из powerDown(SLEEP_8S) с проверкой щупов, а зуммер из урока про tone поёт только по делу.
После игры посчитайте свою арифметику на бумажке. Цикл сигнализатора: активная фаза 100 мс при 4 мА плюс сон восемь секунд при 0,008 мА — средний ток выходит около 0,058 мА, и крона на 550 мА·ч живёт не шесть дней, а больше года. Это и есть обещание заголовка: сон восемь секунд по кругу превращает дни в месяцы, и ни один регистр не попадает в ваш код. Ту же формулу прогоните для SLEEP_1S: при снах по секунде средний ток выходит около 0,37 мА — в шесть раз хуже, потому что активные фазы приходят вшестеро чаще и перестают тонуть в тишине.
Куда двигаться дальше: в третьем уроке триплета соберём метеодатчик целиком — с энергобюджетом по потребителям, коварным светодиодом питания и прогнозом в днях для банки 18650. А если восьмисекундных периодов мало и нужна привязка к реальному времени суток, посмотрите будильник DS3231: внешний RTC доспит недостающее с точностью, которой watchdog не даст, и сам умеет поднимать чип по расписанию.
- LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF) — power-down с watchdog-будильником в одну строку: периоды от 15 мс до 8 с плюс SLEEP_FOREVER
- Предел сна — восемь секунд: дольше живёт только счёт циклов; 75 снов = десять минут, 450 — час
- millis() во сне стоит — Timer0 в power-down остановлен: время считайте снами, а delay() будильником не служит
- 6-8 мкА у Pro Mini 3,3 В после выпайки светодиода, сам чип — меньше микроампера; Uno с тем же скетчем продолжит есть десятки миллиампер обвязкой
- SLEEP_FOREVER — сон до события: будят INT0/INT1 или PCINT любого пина; Serial.flush() перед сном, ток мерьте в разрыве батареи
- 1. Что будит чип из LowPower.powerDown(SLEEP_8S)?
- 2. Что показывает millis() сразу после восьмисекундного сна?
- 3. Нужно спать вечно и просыпаться только на кнопку. Что пишете?
- 4. Pro Mini со снятым светодиодом спит на 6-8 мкА. Что помешает Uno с тем же скетчем?
- 5. Сколько циклов SLEEP_8S нужно отсчитать на десять минут?
Частые вопросы по теме урока
- Что будит чип из LowPower.powerDown(SLEEP_8S)?
- Библиотека настраивает сторожевой таймер как будильник. Watchdog живёт на собственном источнике тактирования и досчитывает до восьми секунд даже в power-down. Тот же будильник из урока про watchdog, только вместо пары строк регистров — аргумент функции. watchdog внутри чипа: его предел и есть восемь секунд
- Что показывает millis() сразу после восьмисекундного сна?
- Timer0 в power-down остановлен, и millis() не растёт. Периоды дольше восьми секунд собирают счётчиком циклов сна: 75 × 8 с = десять минут. не сдвинулась: Timer0 во сне стоит, время считайте снами
- Нужно спать вечно и просыпаться только на кнопку. Что пишете?
- SLEEP_FOREVER не ставит будильник: из такого сна выводят только внешние события — прерывание INT0/INT1 или PCINT. Кнопку сажают на пин 2 или 3, либо на любой пин с PCINT. powerDown(SLEEP_FOREVER, ...) плюс прерывание на пине 2
- Pro Mini со снятым светодиодом спит на 6-8 мкА. Что помешает Uno с тем же скетчем?
- Чип Uno уснёт так же, но плата в сборе продолжит брать около 45 мА: светится power-LED, питается USB-чип, греется стабилизатор. Для батареек — Pro Mini или голый чип. обвязка платы: USB-чип, стабилизатор, светодиоды — десятки миллиампер при спящем чипе
- Сколько циклов SLEEP_8S нужно отсчитать на десять минут?
- Десять минут — 600 секунд; 600 / 8 = 75 снов. Отсюда и рабочая схема отчётов: счётчик циклов, а не millis(). 75