Синхронізуємо час на модулі DS3231 із комп’ютером

Навіщо це все потрібно?
Почну з питання, відповідь на яке очевидна: що найважливіше в будь-якому годиннику? Зрозуміло, це точність ходу. Якими б багатофункціональними та естетичними не були будь-які години, вони – барахло, якщо йдуть неточно. Тому, якщо говорити про точність, ні для кого не секрет, що на даний момент модуль DS3231 – один із найбільш конкурентоспроможних модулів на ринку: він відносно дешевий, досить точний, на відміну від, наприклад, «доісторичного» DS3107, і відносно невеликий. Тому застосування цього модуля в саморобках – абсолютно логічний та передбачуваний крок. Залишилася дрібниця – навчитися виставляти на ньому час досить точно і з мінімальними витратами праці. Цим я хочу поділитися.
Якщо годинник – точний, як часто знадобиться синхронізація?
Давайте прикинемо: модуль годинника має заявлену точність до 2 ppm в діапазоні температур від 0 до 40 градусів Цельсія (а в більшості жител температура саме така).

60сек * 60хв * 24 години * 365 днів = 31 536 000 секунд на рік.
На кожен із цих мільйонів 2 секунди може йти в той чи інший бік. 31,5 мільйона ділимо на мільйон і множимо на 2: отримуємо 63 секунди на рік (максимум). Прийнятний варіант? Дуже. Але 1 раз на півроку я синхронізував би час, щоб воно вкладалося в 1 хвилину.
Якими способами взагалі можна встановлювати час на годиннику модуля?
Традиційно, починаючи з модуля DS3107, час встановлювався за допомогою скетчу Arduino з прикладів використання бібліотеки. Алгоритм такий: відкриваємо скетч, тиснемо «компілювати та закачати», і при першому запуску контролера час встановлюється.Залишилось питання: який час? Звідки Arduino може дізнатися, який час встановлювати? А дуже просто – час компіляції скетчу. Однак із таким підходом я бачу кілька недоліків:
- час компіляції залежить від "мочі" комп'ютера;
- час закачування залежить від швидкості передачі скомпілюваного скетчу в плату Arduino;
- закачаний скетч – «одноразовий» (застаріває відразу після закачування в Arduino).
Що ще можна вигадати? Можна, наприклад, виставляти необхідний час у скетчі вручну, передбачити кнопку, натискання на яку впотрібний моментвиставить «руками» вказаний час, наприклад, через 2 хвилини від поточного моменту: поки «заллється» скетч, поки підготуємося відстежити вручну цей потрібний момент натискання кнопки, якраз та пара хвилин і пройде. А далі, дивлячись на годинник у комп'ютері, чекати «того самого» моменту, щоб натиснути кнопку. Плюси – складніші за попередній спосіб, але все ще відносно просто, проте точніше, ніж перший спосіб. Мінуси – цей спосіб ще незручніше, довше, все одно скетч «одноразовий».
Хто винен і що робити?
Задавши собі ці два риторичні питання, я поліз до Інтернету шукати, хто вже написав синхронізацію часу модуля годинника з комп'ютером. І, як відомо, хто шукає, той завжди знаходить. Знайшовся варіант із Instructables. Теоретично все просто: звичайний «батник» парсить поточний повний час, отриманий «першим»способом (бо крім самого часу потрібна ще й дата), збільшує час на 2 секунди, і «ганяє» порожній цикл до моменту, коли настане це новий, «плюс_дві_секундний» час, щоб «викинути» дані в COM порт. Причому «новий плюс_дві_секундний» час відстежується в інший спосіб (через %time%, якщо комусь цікаво). Але про «косяки» такого рішення пізніше. Дані, викинуті в COM порт, Arduino парсит і після цього встановлює час у модулі. Начебто все просто, логічно та зручно. Але є дуже погане слово «АЛЕ». Все це писав ніби німець, і регіональні стандарти в Windows у нього відрізняються від «наших», а зокрема, дробова частина відокремлюється точкою, а не комою. При запуску з вітчизняними регіональними стандартами "батник" не працює, тому що в ньому час виходу з порожнього циклу описується умовою порівняння з XX: XX: XX.xxx. Ну так треба замість крапки поставити кому - і все, «я все полагодив». А ось і не все (можете перевірити, хто ще пам'ятає, що за таке зло – програмувати у «батниках»). Потрібно виправляти «батник» серйозніше. І я його виправив, використовуючи «мати-перемати» та «мануалку» для DOS. "Батник" виправив, але скетч все одно не працював - час не встановлювався. Тобто дані в порт надсилалися, Arduino їх бачив, але «щось пішло не так».
Погляньмо, що шле «батник» в Arduino і в якому форматі (довідково).
Дані надсилаються у форматі S**
- 2 байти перекладу каретки. Разом, 31 байт. Начебто небагато, надійдуть дані швидко.
Однак є й незручність – як бачимо, не триває день тижня. Лише день місяця. Для реалізації годинника з будильниками, що залежать від днів тижня, буде «косяк». День тижня доведеться виставляти «ручками» у скетчі, що знову натякає на деяку «одноразовість» скетчу, його неповноцінність.
Складаючи фактори – неповноцінність скетчу «із заводу», його відмова нормально працювати, необхідність виправлення «батника» для «наших» широт – я вирішив розробляти все своє. А якщо так, то я можу усувати недоліки та оптимізувати формат даних.
Software та hardware.
Для того, щоб все запрацювало, потрібні дві складові: програма для Windows і апаратно-програмна зв'язка Arduino.
Спочатку загальні дані щодо протоколу обміну. Коли я став вільний вибирати формат даних для пересилання, я вирішив, що пересилання 31 байта інформації не раціональне, і скоротив дані до 4 байт. І що, вистачило? Що можна помістити в 4 байти? Так, вистачило. Помістилося все, що треба. Упевнений, багато хто здогадався, що це за 4 байти. Хто не здогадався – процитую фрагмент статті з Вікіпедії:
Отже, ціле число, що зберігає UNIX час, займає 4 байти, чого вистачить до 2147483648 секунд. А потім можливі потенційні проблеми. Чому потенційні? Тому що це поріг, при досягненні якого числоможе бути інтерпретовано, як негативне (що і сталося з айфонами багатьох цікавих товаришів свого часу). Може, але не обов'язково буде – залежить від того, чи ростуть руки програмістів із місця, передбаченого природою. Зазначене число секунд відповідає 03:14:08 19-січ-2038. До цього часу можна неспішно переходити на 64-бітну версію ОС, де час зберігатиметься в 8-байтній змінній, чого без проблем вистачить на наступні 292 мільярди років. Існує ймовірність, що на наш час цього вистачить. А потім доведеться оновлюватись до 128-бітної версії UNIX.
Які проблеми я вирішив, дійшовши такого варіанту? Перше, сильно знизив кількість байт, що передаються, що на мілісекунди збільшує точність установки часу.Здорово, правда? І друге: я (ймовірно) полегшив сумісність із Linux. На мій сором, я ніяк не можу звикнути до Linux, і користуюся в основному лише Windows. Для цієї самої Windows я можу написати програму пересилання, а для Linux – ні. Але вважаю, що в Linux можна отримати значення UNIX-часу набагато легше, ніж у Windows, і переслати це число в COM порт.
Ніякихдодатковихданих, на кшталт дня тижня і так далі, передавати не потрібно. Тільки час UNIX. Решта робиться в Arduino.
Тепер трохи конкретики безпосередньо пропершускладову - програму для Windows. Програма написана у старій-добрій Delphi. При запуску спливаюче вікно просить вибрати COM порт для надсилання даних. Вибираємо. Інші налаштування слід залишити «дефолтними».




Для чого кнопка носить? Для того, що в повній версії після встановлення часу Arduino увійде в нескінченний цикл відображення того самого свіжовстановленого часу, тобто, по суті, стане годинником. Причому годинами, зробленими на швидку руку, у зв'язку з чим вони не зможуть замінити нормальний годинник через кілька причин (вибірка секунд реалізована через delay, пропаде відображення часу при відключенні живлення). Адже мета - переконатися, що час синхронізований правильно, не більше. Отже, для синхронізації наступного модуля годинника без ресета не обійтися (точніше, можна обійтися, якщо «пересмикнути» USB кабель). Інакше кажучи, призначення кнопки – суто утилітарне. За бажання можна обійтися і без неї.
Як же прошивати Arduino, адже версії «заліза» дві, а скетч один? Для компіляції «правильної» версії прошивки в заголовку скетчу потрібно встановити бажане значення параметраfullVersion:trueдля «повної» версії, абоfalse- для "урізаної". Компілятор таким чином визначить, для якої версії заліза компілювати прошивку.
Отже, схема підключення є, потрібний код скетчу. Зверніть увагу, що для нормальної роботи скетчу з повною версією потрібна бібліотекаLiquidCrystal I2Cby Frank de Brabander(встановлюється з репозиторію за допомогою Менеджера Бібліотек). Також потрібна бібліотека для підтримки модуля годинника, причому не будь-яка :). Качать тут: https://github.com/jarzebski/Arduino-DS3231. Із бібліотеками розібралися.
Пара фото "повної" версії готового девайса.


Де скачати скетч та програму?
Скетч качати тут (Dropbox). Програму для Windows качати тут (Dropbox).
"Плюси і мінуси".
Мені дуже сподобалося, як тепер встановлюється час у модулях! При необхідності встановити час мені не доводиться згадувати щоразу, який там скетч мені потрібен і замислюватися, наскільки точно буде встановлено час у модулі. Більше того, скоро буде огляд саморобного годинника, куди я вбудував такий метод синхронізації – настільки метод мені сподобався. Сподіваюся, комусь із читачів метод також буде доречним.
Проект – вільний, некомерційний. Кожен має право використовувати дані з огляду з будь-якою метою, крім комерційних.