Досветка в курятнике зимой — самое благодарное RTC-дело: включай лампу на пятнадцать часов, куры несутся без провалов. Первый скетч я написал на глазок: if (час >= 20 && час < 6) — и лампа не включилась никогда. Условие «больше 20 и меньше 6» не бывает истинным ни в какую минуту суток: ночной интервал через полночь в лоб не пишется. Вторая попытка через флаги и счётчики обросла ветками, сломалась на переводе часов и в итоге превратилась в паутину, которую я боялся трогать.
Решение — то же, что в будильнике: сутки в минутах от полуночи, а ночной интервал записывается разворотом сравнения. Плюс гистерезис порогов: включение не ровно в 20:00, а в 19:50, выключение не в 06:00, а в 06:10. Разница в десять минут — это не про дребезг (у часов его нет), это запас на скачки времени: NTP-подтяжка, ручной перевод, батарейка, дёрнувшаяся на секунду при замене. Ниже — скетч, таблица порогов под три сценария и разбор полночного разворота.
Интервал через полночь: главный тупик
Дневной интервал — из простых: он лежит внутри одних суток, и хватает проверки mins >= ON && mins < OFF. Ночной interval начинается вечером и кончается утром следующих суток — одной проверкой его не накрыть, поэтому условие разворачивается через ИЛИ: ночь — это «уже поздно» ИЛИ «ещё рано». mins >= ON || mins < OFF — при ON = 1190 (19:50) и OFF = 370 (06:10) это истинно с 19:50 до полуночи и с полуночи до 06:10, ровно то, что нужно. Универсальный скетч держит оба случая: если ON меньше OFF — обычное сравнение через И, если больше — полночный через ИЛИ.
Гистерезис здесь работает на будущее состояние: куры, аквариум и гирлянда не требуют секундной точности, а вот скачок времени на минуту-другую (синхронизация, DST, дребезг питания при записи) при совпадающих порогах щёлкнул бы реле туда-обратно. Разнесённые пороги делают режим инерционным: время может гулять в пределах десяти минут, а лампа этого не заметит. Про обратное — пороги от солнца, а не от часов, — у нас есть урок про восход и закат, расписание на неделю с буднями — в уроке про будни и выходные.
#include <Wire.h>#include <RTClib.h> RTC_DS3231 rtc;const int PIN_RELAY = 7; // пороги в минутах от полуночи, с запасом от ровных часовconst int NIGHT_ON = 19 * 60 + 50; // 19:50const int NIGHT_OFF = 6 * 60 + 10; // 06:10 bool night = false;int lastMode = -1; // 1 ночь, 0 день: для журнала void setup() { Serial.begin(9600); pinMode(PIN_RELAY, OUTPUT); if (!rtc.begin()) { Serial.println("DS3231 не найден"); while (true) {} } Serial.println("расписание: ночь 19:50 - 06:10");} void loop() { DateTime now = rtc.now(); int mins = now.hour() * 60 + now.minute(); if (NIGHT_ON > NIGHT_OFF) { // интервал через полночь: "уже поздно" ИЛИ "ещё рано" night = (mins >= NIGHT_ON) || (mins < NIGHT_OFF); } else { // обычный интервал внутри одних суток night = (mins >= NIGHT_ON) && (mins < NIGHT_OFF); } digitalWrite(PIN_RELAY, night ? HIGH : LOW); if (night != lastMode) { lastMode = night; Serial.print(night ? "ночь: реле on в " : "день: реле off в "); Serial.print(now.hour()); Serial.print(":"); Serial.println(now.minute()); } delay(1000);}Смена режима печатается один раз — в момент переключения, а не ежесекундно: lastMode хранит предыдущее состояние, и журнал читается как история суток. Реле переключается логикой «истина — включено» и больше нигде: ни в setup, ни по кнопке, — один источник правды, как в уроке про полив по расписанию. Если режимов больше двух (утро-день-ночь), таблица порогов разрастается в цепочку сравнений — для трёх-четырёх зон это всё ещё читается с листа, дальше лучше переходить на расписание недели с массивом слотов.
Частный вопрос, который всплывает всегда: «а почему не сравнивать прямо часы — час >= 20 || час < 6?» Можно, и для ровных полночных интервалов это сработает. Но минуты дают два преимущества: пороги с запасом (19:50, а не 20:00) и слоты короче часа — утренний прогон насоса с 05:40 до 06:15 в часах уже не выражается. Часы — частный случай минут, а не наоборот.
| Сценарий | Ночь ON | Ночь OFF | Запас |
|---|---|---|---|
| Досветка курятника | 19:50 | 06:10 | по 10 минут от краёв |
| Ночная подсветка аквариума | 21:40 | 07:20 | лунный режим короче светового |
| Гирлянда на фасаде | 17:30 | 23:30 | через полночь не идёт: обычный интервал |
| Обогрев теплицы (через часы) | 22:00 | 05:30 | полночный разворот обязателен |
- 1. Ночной интервал 19:50 — 06:10. Какое условие правильное?
- 2. Зачем пороги 19:50 и 06:10 вместо ровных 20:00 и 06:00?
- Дневной интервал — И: mins >= ON && mins < OFF; ночной через полночь — ИЛИ: mins >= ON || mins < OFF
- Пороги в минутах от полуночи: слоты короче часа и запас в 10 минут выражаются без хитростей
- Гистерезис расписания — не против дребезга, а против скачков времени: NTP, DST, замена батарейки
- Смена режима печатается по факту переключения: журнал суток читается с одного взгляда
Частые вопросы по теме урока
- Ночной интервал 19:50 — 06:10. Какое условие правильное?
- Интервал через полночь накрывается ИЛИ: «уже поздно» ловит вечер до полуночи, «ещё рано» — утро после. Вариант с И не бывает истинным никогда. mins >= 1190 || mins < 370
- Зачем пороги 19:50 и 06:10 вместо ровных 20:00 и 06:00?
- Часы иногда прыгают: NTP, DST, замена батарейки. Разнесённые пороги делают режим инерционным — скачок в пару минут просто не доходит до реле. Запас на скачки времени: синхронизация и перевод часов не перещёлкнут режим