Оффлайн калибровка погрешности для модулей часов реального времени

Я часто пользуюсь часами реального времени 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 должно быть отрицательным.

Возможно, мой опыт пригодится ещё кому-то, кто часто пользуется часами реального времени и сталкивался с проблемой погрешности.

Зачем?

Предлагаю взять нормальные часы (н.п. DS3231 или PCF2129) и прекратить сексуальное насилие над собственным и чужими мозгами :slight_smile:

4 лайка

Возможно, мой опыт пригодится ещё кому-то, кто часто пользуется часами реального времени и сталкивался с проблемой погрешности.

DS1307, в среднем, в два-три раза дешевле DS3231, PCF2129 - на порядок дороже. Конечно, это незначительные суммы с случае единичных устройств, но в случае имеющегося парка устройств с DS1307, работающих уже годами, лично для меня более приемлемо описанное решение.

Вопрос решается либо
а) автокорректировкой - периодически время корректируется на заранее заданную величину (поправку можно выяснить для каждого модуля экспериментально)
б) использованием МК с выходом в интернет и автокоррецией по NTP
в) наличием пары/тройки кнопок и режима ручной установки времени

Несколько тысяч? Кстати, ds3231 полностью совместимы с ds1307 по командам, при замене одного модуля на другой даже прошивку менять не придется

Да, моё решение - это именно реализация этого варианта.

В моём случае - десятки.

Слишком много букв для объяснения элементарного действа

https://arduino.ru/forum/obshchii/konsolnoe-prilozhenie-synchrotime-dlya-kalibrovki-rtc-ds3231-zs-042-modulei

При чем тут это? У него ds1307 :wink:

Извините за некрофилию.
У ds1307 имеется встроенная энергонезависимая память. Достаточно собрать калибровочный стенд, в который устанавливаются часы на калибровочный интервал времени (сутки, двое… подбирается исходя из требуемой точности) и этот же стенд записывает поправку в память.
Делается вспомогательный класс, который учитывая поправку в памяти часов их корректирует. Если использовать всю память часов только под корректировку, то 56 байт хватает примерно на год (каждый бит - это условный день в циклическом периоде, когда произведена корректировка, он меняет свое состояние со стертого на установленный). Поэтому достаточно обратиться раз в сутки к функции коррекции (можно чаще, можно реже), поправка будет сделана опираясь на взведенные биты дней в памяти.

Если микросхема памяти поддерживает независимые операции стирания байта и записи (для возможности только записи взводимых бит не изнашивая остальные биты), то ресурс памяти под калибровку будет больше (в 8 раз), чем просто запись каждого байта. Если количество циклов перезаписи памяти укладывается в срок жизни устройства, то можно сократить объем буфера памяти под лог корректировок (увеличив частоту записи).

Вопрос чисто академический, когда нет возможности перейти на более точный модуль.

Чёй то? А разработчики то и не в курсе. :roll_eyes:

Я прочитав первый пост с идеей увидел много лишних телодвижений, в которые входит установка поправки в код программы. Хотя это всего лишь параметр конкретного модуля часов. Всего лишь предлагаю доработать предложенную идею, чтобы код МК вообще ничего не знал о конкретной калибровке модуля. Его параметр прошит в сами часы на калибровочном стенде. Его тоже можно автоматизировать до “вставил модуль, подал питание, через сутки выключил, модуль переставил в изделие”.

Хватит ананировать с 1307, на них ничего хорошего всё равно не получится.
Используйте нормальные часы реального времени - и да прибудет с вами щасстее )))

Ещё раз: Оно ОЗУ, и оно , battery-backed. Не вводите людей в заблуждение.

1 лайк

А большего и не надо. После переустановки батарейки все равно сбрасывается время. Так что даже еще лучше. Спасибо за поправку (если оно действительно озу), на ресурс памяти тогда можно и не заморачиваться.

Грубо и необоснованно(и куда только модераторы смотрят :slightly_smiling_face:). Кому точность, а кому и долгое время работы(10 лет не шутка), а ежели кварц и температурный режим подобрать, то м…

По вашему DS3231 10 и более лет не способен проработать?

Кстати, спасибо за информацию о типе памяти на модуле часов. Очень удачно могу использовать там, где надо часто запоминать данные, но без работающих часов эта информация становится бесполезной. Либо работают часы и данные в их памяти являются опорниками для меток отработки расписаний, либо питание часов все, и расписания по-любому выполняться не могут.

Нет! Джва года максимум! :grinning_face:

Этот модуль поддерживает аккумуляторные батареи? Чтобы с внешним питанием жить долго и счастливо?

С внешним питанием всё живёт вечно(ну кроме ж… :face_without_mouth:), а вот только от батарейного питания - то год-два.