Pi OS 12 GPIO - BYO-NTP/recipes GitHub Wiki
date | server | os | gnss | daemon | 🗣️ |
---|---|---|---|---|---|
2025-06 | Raspberry Pi 4 Raspberry Pi 5 |
Pi OS 12 |
6M U7 M8Q M8T |
chrony NTPsec ntp |
discuss |
Install Pi OS 12 (Bookworm) per the instructions for Pi 4 or Pi 5.
Follow the instructions for your GNSS on the Assemble Pi GPIO page.
Liberate the serial port from the console:
raspi-config nonint do_serial_hw 0
raspi-config nonint do_serial_cons 1
systemctl disable --now [email protected]
systemctl mask [email protected]
Symlink the TTY at /dev/ttyAMA0
to /dev/gps0
where the NTP daemons expect it. Enable low_latency
on the serial port:
apt install -y setserial
cat > /etc/udev/rules.d/10-gps.rules <<EO_UDEV
# symlink /dev/ttyAMA0 to /dev/gps0
KERNEL=="ttyAMA0", SYMLINK+="gps0", GROUP="dialout", MODE="0660"
# configure the serial port for low latency.
KERNEL=="ttyAMA0", RUN+="/bin/setserial /dev/ttyAMA0 low_latency"
EO_UDEV
udevadm control --reload-rules
udevadm trigger
apt install -y pps-tools
grep -q pps-gpio /boot/firmware/config.txt || cat >> /boot/firmware/config.txt <<EOWD
# PPS from GPIO connected GNSS
dtoverlay=pps-gpio,gpiopin=18
EOWD
Reboot the Pi:
reboot
Verify that the serial port is found by the OS:
dmesg | grep -i uart
[ 0.048259] Serial: AMBA PL011 UART driver
[ 0.049274] 107d001000.serial: ttyAMA10 at MMIO 0x107d001000 (irq = 16, base_baud = 0) is a PL011 rev3
[ 3.155154] pl011-axi 1f00030000.serial: cts_event_workaround enabled
[ 3.165578] 1f00030000.serial: ttyAMA0 at MMIO 0x1f00030000 (irq = 126, base_baud = 0) is a PL011 AXI
In the output we see two serial ports. This is a Pi 5 so ttyAMA10
is the dedicated console port and ttyAMA0
is the GPIO serial port 0 that the GNSS is wired to.
Configure the serial port and read some data from /dev/gps0
: (Control-C to disconnect):
stty -F /dev/gps0 cs8 clocal -cstopb -parenb -echo raw 115200
cat /dev/gps0
$GNGGA,225430.00,4746.15203,N,12219.54384,W,1,12,99.99,128.2,M,-18.6,M,,*49
$GNZDA,225430.00,09,07,2025,00,00*71
$GNGGA,225431.00,4746.15203,N,12219.54384,W,1,12,99.99,128.2,M,-18.6,M,,*48
$GNZDA,225431.00,09,07,2025,00,00*70
$GNGGA,225432.00,4746.15203,N,12219.54384,W,1,12,99.99,128.2,M,-18.6,M,,*4B
$GNZDA,225432.00,09,07,2025,00,00*73
^C
It works!
Verify that the PPS module loaded:
lsmod | grep pps
pps_gpio 12288 0
Test that PPS pulses are present (Control-C to cancel):
ppstest /dev/pps0
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1752101802.415352718, sequence: 224 - clear 0.000000000, sequence: 0
source 0 - assert 1752101803.415361180, sequence: 225 - clear 0.000000000, sequence: 0
source 0 - assert 1752101804.415369716, sequence: 226 - clear 0.000000000, sequence: 0
^C
Yay, that works too.
Note: it can take up to 20 minutes for the GNSS to get a fix and start outputting PPS signals. If the board has a PPS LED, it will blink every second when it has a fix. If necessary, move the GPS unit to where it has a clear view of the entire sky.
The latency of each GNSS requires a custom offset in milliseconds. Export the GNSS specific offset from the table that matches your Pi & GNSS:
Pi | GNSS | NMEA | gpsd | PPS |
---|---|---|---|---|
M8T | 0.1 | 0.12 | ||
M8Q | 0.1 | 0.06 | ||
pi4 | 7M | 0.11 | ||
pi5 | 7M | 0.06 | ||
6M | 0.07 | .12 |
export GNSS_OFFSET_NMEA=""
Each section heading is a link with many more details about installing, configuring, and verifying that particular NTP daemon.
export NTP_REFCLOCKS=$(cat <<EO_CHRONY
refclock SHM 0 refid NMEA precision 4e-2 poll 2 offset $GNSS_OFFSET_NMEA
refclock PPS /dev/pps0 refid PPS precision 4e-8 poll 2 lock NMEA trust
EO_CHRONY
)
curl -sS https://byo-ntp.github.io/tools/chrony/install.sh | sh
export NTP_REFCLOCKS=$(cat <<EO_NTPSEC
refclock nmea refid NMEA minpoll 3 maxpoll 4 time2 $GNSS_OFFSET_NMEA baud 115200 prefer
refclock pps refid PPS minpoll 1 maxpoll 2
EO_NTPSEC
)
curl -sS https://byo-ntp.github.io/tools/ntpsec/install.sh | sh
or with gpsd:
export NTP_REFCLOCKS=$(cat <<EO_NTPSEC_GPSD
refclock shm unit 0 refid NMEA minpoll 3 maxpoll 4 time1 $GNSS_OFFSET_NMEA
refclock shm unit 2 refid PPS minpoll 1 maxpoll 2 prefer
EO_NTPSEC_GPSD
)
curl -sS https://byo-ntp.github.io/tools/ntpsec/install.sh | sh
Note, if the GNSS doesn't maintain a solid 3D fix, setting minpoll 4 maxpoll 6
can greatly reduce the inaccuracies when the fix is lost.
export NTP_REFCLOCKS=$(cat <<EO_NTP
server 127.127.20.0 minpoll 3 maxpoll 6 mode 80 prefer
fudge 127.127.20.0 refid NMEA time2 $GNSS_OFFSET_NMEA minjitter 0.02
server 127.127.22.0 minpoll 3 maxpoll 4 prefer
fudge 127.127.22.0 refid PPS minjitter 0.005
EO_NTP
)
curl -sS https://byo-ntp.github.io/tools/ntp/install.sh | sh
- watch the status in near real time:
- printf '\e[8;9;80t'; ssh -t pi5 watch -n2 chronyc sources
- printf '\e[8;9;80t'; ssh -t pi5 watch -n2 ntpq -c peer
- Measure the offset
- Gather statistics with telegraf + influxdb + grafana or similar.
- BYO-NTP: GNSS Timing Performance, GNSS, NTP daemons
- Linux: PPS, PPS - Pulse Per Second
- raspi-config: CLI
- https://pinout.xyz/pinout/uart
- tiagofreire - Raspberry Pi 5B NTP Server - Stratum 1 (with Uputronics GPS HAT)
- Austin - Pi 5 + PPS + chrony
- Austin - Pi + USB