Насос в теплице включался по DS1307 в 7:00 — и месяц работал идеально, а потом я заметил, что полив сдвинулся к 7:12. Никто ничего не перепрошивал: просто кварц на модуле тикал чуть быстрее эталона, и эти микросекунды копились каждый день. Дрейф часов — не поломка, а свойство: у любого кварца есть допуск в ppm, миллионных долях, и вопрос только в том, сколько их у вашего модуля и заметны ли они на вашей задаче. Для сигнала на ёлку — нет, для полива по расписанию — уже да.
Считается это на салфетке: ppm умножить на секунды суток. Кристалл с допуском 20 ppm уходит на 20 × 86,4 = 1,7 секунды в сутки — около 53 секунд за месяц. DS3231 с его термокомпенсированным генератором держит ±2 ppm — это секунды за год, поэтому он и дороже. Ниже — скетч-тест, который за сутки показывает расхождение RTC против millis, таблица ухода для разных модулей и правило компенсации, когда менять модуль уже поздно.
Тест дрейфа: RTC против millis
Идея теста: запомнить показание RTC и millis() в момент старта, а дальше раз в час печатать, насколько разошлись два времени. RTC меряет unixtime — секунды от эпохи, millis — миллисекунды от включения; разница в секундах между ними и есть накопленный дрейф. За одни сутки видно направление и порядок, за неделю — уже статистику по температуре: гараж ночью холоднее, кварц там тикает иначе, и график дрейфа повторяет график погоды.
Оговорка, без которой тест врёт: millis() — тоже не эталон. На Uno стоит керамический резонатор с допуском в десятые доли процента — это часы, уходящие на десятки секунд в сутки, то есть в тысячи раз хуже любого RTC. Поэтому расхождение в тесте — сумма дрейфов обоих свидетелей, и если оно сотни секунд в сутки, виноват millis, а не модуль. Честный эталон — телефон или NTP: сверка с телефоном и синхронизация по сети дают абсолютную отметку, а millis тут — быстрый санитарный тест на то, что модуль жив и держит направление.
#include <Wire.h>#include <RTClib.h> RTC_DS1307 rtc; // у вас может быть DS3231: замените класс DateTime t0;unsigned long ms0;int lastHour = -1; void setup() { Serial.begin(9600); if (!rtc.begin()) { Serial.println("модуль RTC не найден: шина I2C, адрес 0x68"); while (true) {} } t0 = rtc.now(); ms0 = millis(); Serial.println("тест дрейфа: раз в час печатаю расхождение");} void loop() { DateTime now = rtc.now(); int hour = now.hour(); if (hour != lastHour) { lastHour = hour; long rtcSec = now.unixtime() - t0.unixtime(); long msSec = (millis() - ms0) / 1000UL; long drift = rtcSec - msSec; Serial.print("прошло RTC "); Serial.print(rtcSec); Serial.print(" c, millis "); Serial.print(msSec); Serial.print(" c, расхождение "); Serial.print(drift); Serial.println(" c"); } delay(1000);}Раз в час — потому что внутри часа дрейф не меняется заметно, а журнал за сутки занимает двадцать четыре строки, которые читаются с одного взгляда. Положительное расхождение — модуль спешит, отрицательное — отстаёт. Проверять «за месяц» не обязательно: сутки дают вам секунды, и ppm восстанавливается делением на 86,4 — расхождение 1,7 секунды за сутки означает ровно 20 ppm. Дальше применяется компенсация: цель полива сдвигается на измеренное отставание, либо раз в неделю-месяц RTC подтягивается rtc.adjust() по эталону.
Компенсация в скетче — одна константа: если модуль отстаёт на 2 секунды в сутки, цель будильника сдвигается на эти 2 секунды за каждые сутки с последней синхронизации, или проще — раз в месяц rtc.adjust() по телефону. Грубовато, но для DS1307 в теплице хватало. Красивый путь — сразу DS3231: его TCXO компенсирует температуру сам, и скетч-тест с этого урока покажет нули, на которых глаз отдыхает. Почему два модуля с одним адресом 0x68 отличаются в десять раз — таблица ниже.
| Источник времени | Допуск | Уход за месяц | Приговор |
|---|---|---|---|
| DS1307 + обычный кристалл | ±20-50 ppm | 50-130 с | полив уползает на минуты — компенсировать или менять |
| DS3231M (MEMS) | ±5-10 ppm | 13-26 с | терпимо, но на морозе заметно хуже TCXO-версии |
| DS3231SN (TCXO) | ±2 ppm | 5-6 с | секунды за год: эталон для автономных задач |
| millis() на Uno | ~0,5 % | ~6,5 часов | не часы: резонатор в тысячи раз хуже RTC |
- 1. Кварц с допуском 20 ppm. Сколько уйдут часы за месяц?
- 2. Тест против millis показал уход в сотни секунд за сутки. Кто виноват?
- Дрейф считается на салфетке: ppm × 86,4 = секунды в сутки; 20 ppm — 1,7 с/сутки, 53 с/месяц
- DS3231SN держит ±2 ppm и меряет сам температуру; DS3231M (MEMS) хуже в 3-5 раз, особенно на морозе
- millis — санитарный тест, не эталон: резонатор Uno уходит на часы за месяц
- Компенсация — сдвиг цели на измеренное отставание либо периодическая rtc.adjust() по телефону/NTP
Частые вопросы по теме урока
- Кварц с допуском 20 ppm. Сколько уйдут часы за месяц?
- 20 ppm × 86,4 с в сутки = 1,7 с в сутки, месяц — около 53 секунд. Шесть часов — это millis на Uno с его резонатором, а не RTC. Около 53 секунд
- Тест против millis показал уход в сотни секунд за сутки. Кто виноват?
- millis — свидетель с криминальным прошлым: 0,5 % допуска — это десятки секунд в час. Честный эталон — телефон или NTP, RTC против millis — только санитарный тест. Скорее millis: резонатор Uno на порядки хуже любого RTC