Я часто пользуюсь часами реального времени DS1307RTC в своих устройствах. Всё бы хорошо, но эти дешёвые часы реального времени со временем начинают спешить или отставать; ежедневное смещение иногда бывает более 10 секунд, что требует периодического извлечения контроллера и перепрошивки устройства для калибровки времени. К примеру, на одном устройстве модуль RTC у меня спешит на 11 секунд ежедневно, это 5.5 минут в месяц, более часа за год — что, конечно, требует регулярного переподключения устройства для калибровки. В связи с этим, давно думал как бы автоматизировать корректировку для оффлайн-усройств и, наконец, реализовал эту давнюю задумку.
Предлагаю вам ознакомиться с моей реализацией.
Исходный код здесь: https://gitlab.com/simplemicrocontrollers/rtctimestampbias
Файл rtctimestampbias.py
— это python скрипт для сохранения и сравнения временных меток (timestamp) в csv файл. Для его корректной работы потребуется установка модулей serial и csv, а также выведение временной метки микроконтроллера первой строкой на серийный монитор при инициализации.
Скрипт работает со следующими опциями:
python rtctimestampbias.py -l
— выводит список подключенных serial портов.
python rtctimestampbias.py -n 1
— выводит и сохраняет временную метку устройства по номеру порта (из предыдущего списка)
python rtctimestampbias.py -d ttyUSB0
— выводит и сохраняет временную метку устройства по имени порта (из предыдущего списка)
python rtctimestampbias.py -c
— производит расчёт выводит сравнение временных меток из файла csv. Сравниваются первая и последняя строки.
В файл rtctimestampbias.csv
сохраняются данные в формате csv из трёх стобцов, в таком виде:
2024-06-16 15:52:48.291780,1718542368,1718553163
Первое значение: человекочитаемое время сохранения значения, на хостовом устройстве.
Второе значение: временная метка на хосте.
Третье значение: временная метка на микроконтроллере.
Для более точного расчёта погрешности рекомендуется сохранить метки периода, как минимум 1 дня, но желательно несколько дней или недель. Пример расчёта и вывода сравнения временных меток:
$ ./rtctimestampbias.py -c
First timestamp of the host: 1718542368
First timestamp of the device: 1718553163
Last timestamp of the host: 1719642409
Last timestamp of the device: 1719653345
Host diff: 1100041; device diff: 1100182; host/device total diff: 141
Resulting daily clock bias in seconds: 11.073076999987274
Здесь за пару недель работы устройства модуль часов реального времени стал спешить на 141 секунду (host/device total diff), результирующее суточное смещение часов в секундах: 11.073076999987274.
В репозитории с исходным кодом имеется скетч rtctimestampbias.ino
для примера того, как можно изменить исходный код микроконтроллера для регулярной корректировки.
В примере для корректировки используется библиотека Thread
(она обеспечивает псевдо-мультипоточность). В примере она обеспечивает запуск функции корректировки каждые 5 минут (переменная correctionCheck
).
Важно: в функции setup()
строка:
Serial.println(now());
Выводит метку времени на серийный монитор, для возможности её считывания скриптом.
Часть из моих устройств работает в условиях частого отключения электричества, иногда на несколько часов, чаще всего отключения происходят в дневное время, почти никогда — ночью. Поэтому в моём примере корректировка происходит в 3 часа ночи ежедневно (переменная correctionHour
), чтобы минимизировать пропуск корректировки из-за отключения электричества.
В этом примере, чтобы сделать ежедневную корректировку на 11 секунд, достаточно изменить значение переменной correctionBias с 0 на 11, тогда в функции correctionLoop()
будет происходить ежедневное вычитание значения correctionBias
из памяти часов реального времени.
Пример расчёта и вывода сравнения временных меток с другого устройства:
$ ./rtctimestampbias.py -c
First timestamp of the host: 1718532768
First timestamp of the device: 1718543561
Last timestamp of the host: 1719816178
Last timestamp of the device: 1719826992
Host diff: 1283410; device diff: 1283431; host/device total diff: 21
Resulting daily clock bias in seconds: 1.4137105929340963
Здесь за пару недель работы устройства модуль часов реального времени стал спешить на 21 секунду (host/device total diff), результирующее суточное смещение часов в секундах: 1.4137105929340963.
В моём примере скетча, чтобы сделать ежедневную корректировку на 1.4 секунды, необходимо изменить значение переменной correctionBias
с 0 на 1, а также раскомментировать строку в функции correctionLoop()
:
// if ((day() % 5) == 0) newTimestamp = newTimestamp - 1; // minus 1 sec every 5 days (-0.2sec everyday)
Изменив её на:
if ((day() % 5) == 0) newTimestamp = newTimestamp - 2; // minus 2 sec every 5 days (-0.4sec everyday)
Тогда помимо ежедневного вычитания 1 секунды в день, каждые 5 дней будет происходить ежедневное вычитание 2 секунду, что будет эквивалентно вычитанию 1.4 секунд в день.
Конечно, в случае, если устройство работает без перебоев электричества, на автономном питании или на источнике бесперебойного питания, то этих сложностей с вычитанием разных значений в 3 часа ночи не потребуется, достаточно будет вычитать 1 секунду в соответствующий смещению период времени. К примеру, в случае результирующего суточного смещения часов в 1.4137105929340963 секунд, достаточно будет вычитать 1 секунду каждые 61115.761 секунд (количество секунд в дне / смещение; 86400 / 1.4137105929340963). Для этого в моём примере надо будет изменить значение следующих переменных на:
int correctionBias = 1;
long correctionCheck = 61115761;
А функцию correctionLoop()
привести к следующему виду:
void correctionLoop() {
tmElements_t RTCtime;
RTC.read(RTCtime);
time_t RTCtimestamp;
RTCtimestamp = makeTime(RTCtime);
tmElements_t timeNew;
time_t newTimestamp = RTCtimestamp - correctionBias;
breakTime(newTimestamp, timeNew);
RTC.write(timeNew);
setSyncProvider(RTC.get);
}
Тогда каждые 61115761 миллисекунд будет вычитаться одна секунда. Если часы реального времени отстают, а не спешат, то, соответственно, значение correctionBias
должно быть отрицательным.
Возможно, мой опыт пригодится ещё кому-то, кто часто пользуется часами реального времени и сталкивался с проблемой погрешности.