Время в Raspberrypi - 4

В одной из своих прошлых статей я писал о том, что в RaspberryPI нет RTC, и к чему это может привести. Можно купить и установить отдельный модуль, чтобы решить эту проблему. Однако, как оказалось, и с этим подходом могут быть проблемы:

  • я где-то потерял его
  • нужна отдельная батарейка, за которой нужно следить и раз в несколько лет менять

Ещё одним решением может быть GPS модуль! С помощью него можно получать не только координаты, но и текущее время.

GPS

GPS модуль идеально подходит для r2cloud по нескольким причинам. Во-первых, станция уже должна быть на улице, чтобы принимать сигналы. Во-вторых, время начальной синхронизации не критично - r2cloud просто стартует чуть позже. В-третьих, можно загружать координаты станции напрямую из GPS модуля. Это позволит сделать станцию чуть более мобильной.

В качестве GPS модуля я выбрал Adafruit Ultimate GPS HAT for Raspberry Pi. Но в теории может подойти абсолютно любой. Этот удобен тем, что достаточно крепко крепится к плате и ему не нужен отдельный провод.

Основная информация передаётся к текстовом виде по UART, а высокоточная синхронизация (PPS) через пин №4. Мне такая точность не особенно нужна, да и процессор RaspberryPI не может быстро обрабатывать такой сигнал, но почему бы и нет?

Интересный факт: для того, чтобы повысить точность часов, нужно уменьшить задержки между получением сигнала со спутника и выставлением времени в Linux. Ни UART, ни даже USB здесь не подходят - слишком медленно. Для этого используется отдельный провод: если напряжение на нём выростает от 0В до 3.3В, то значит началась новая секунда.

Конфигурация

Для работы нужно сконфигурировать несколько сервисов.

PA1616D
PA1616D
/dev/serial0
/dev/serial0
/dev/pps0
/dev/pps0
GPSD
GPSD
chrony
chrony
systemd
systemd
Linux
Linux
r2cloud
r2cloud

RaspberryPI

Поскольку чип общается с RaspberryPI по UART, то нужно отключить поддержку логина через UART. Это кстати не самая лучшая идея, потому что в поле хотелось бы иметь запасной план. Однако, я не нашёл плат, которые бы работали по I2C или SPI.

Для начала нужно запустить стандартную утилиту:

sudo raspi-config

Потом выбрать пункт: “Interface Options” -> “Serial Port”.

На вопрос “нужно ли дать возможность логиниться?” нужно выбрать “No”. Но при этом нужно выбрать “Yes” на следующий вопрос - hardware должно быть доступно.

Помимо этого нужно добавить специальное устройство для PPS. Для этого нужно отредактировать файл:

sudo vi /boot/firmware/config.txt

И добавить следующую строчку:

# For Ultimate GPS HAT from Adafruit 
dtoverlay=pps-gpio,gpiopin=4

GPSD

Этот демон считывает данные из чипа и занимается расчётом времени и координат. Сначала его нужно установить:

sudo apt update
sudo apt install gpsd

Потом отредактировать конфигурацию:

sudo vi /etc/default/gpsd

И добавить следующее:

START_DAEMON="true"

DEVICES="/dev/serial0 /dev/pps0"

# Other options you want to pass to gpsd
GPSD_OPTIONS="--nowait --passive --speed 9600"

Чип, кстати, поддерживает скорость до 57600 бод, что позволяет ещё быстрее получать данные по UART.

Чтобы проверить работу, нужно перезагрузить RaspberryPI и запустить утилиту gpsmon. Она может в реальном времени показывать координаты, время, количество спутников на небе и многое другое.

chrony

Сам по себе GPSD не обновит системное время. Для этого нужен отдельный сервис. Есть несколько сервисов, которые могут это сделать:

  • systemd-timesyncd. Это легковесный процесс, который является NTP клиентом и может синхронизировать время только с удалённого сервера
  • NTPD. Старый-добрый сервис, в котором нет множества (нужных ли?) фич
  • chrony. Современный, молодёжный сервис, который я решил в итоге поставить.

chrony умеет общаться с GPSD и получать оттуда время.

sudo apt update
sudo apt install chrony

Его конфигурация находится в файле:

sudo vi /etc/chrony/chrony.conf

Далее нужно добавить следующую конфигурацию (взято из Интернета):

refclock SHM 0 poll 8 refid GPS precision 1e-1 offset 0.502 delay 0.2 noselect
refclock PPS /dev/pps0 lock GPS maxlockage 2 poll 4 refid PPS precision 1e-7 prefer offset 65.62e-9

После этого нужно перезагрузить сервис:

sudo systemctl restart chronyd.service

И проверить работу командой chronyc sources -v:

Если всё успешно, то видно, что chrony использует PPS, как самый точный способ получения времени. При этом команда timedatectl показывает, что синхронизация времени есть:

sync-chrony

Однако, после перезагрузки r2cloud не поднялся.

Несмотря на то, что timedatectl показывает, что время синхронизировано, systemd-time-wait-sync.service не смог отловить этот момент и пробросить дальше. Все сервисы, которым нужно точное время, продолжают ждать. Оказывается, systemd-time-wait-sync.service не всегда получает синхронизацию и рассчитан на работу в связке со стандартным systemd-timesyncd. К счастью, есть решение - можно создать файлик в специальной директории и сервис будет считать, что время синхронизировано.

Для этого я сделал отдельный service unit для systemd: /lib/systemd/system/systemd-time-wait-sync-chrony.service

[Unit]
Description=Propagate chrony sync to system sync

DefaultDependencies=no
Before=time-sync.target shutdown.target
Wants=time-sync.target
Conflicts=shutdown.target

[Service]
Type=oneshot
ExecStart=/bin/chronysync
TimeoutStartSec=infinity
RemainAfterExit=yes

[Install]
WantedBy=sysinit.target

И скрипт /bin/chronysync содержит следующее:

#!/bin/bash

FILE=/run/systemd/timesync/synchronized
if [[ -f "$FILE" ]];
then
echo "already synchronized"
:
else
	SYNCED=0
	while [ $SYNCED == 0 ]
	do
		SYNCED=$(/usr/bin/timedatectl status | grep -c "System clock synchronized: yes")
		sleep 1
	done
	echo "synchronized"
	mkdir -p /run/systemd/timesync/
	touch /run/systemd/timesync/synchronized
fi

Заключение

В результате сразу же после старта RaspberryPI пытается получить текущее время с GPS, синхронизируется несколько минут со спутниками и, как только время получено, стартует r2cloud. Для полной мобильности не хватает:

  • считывать координты напрямую с GPS приёмника, а не из конфигурации
  • откуда-то получать свежие TLE спутников