Sun 07 May 2023 | -- (permalink)
For Reasons, I recently needed to install
unbound
on a
Raspberry Pi running 64-bit Debian (not Raspian). For the most part
this was trivial: just download the appropriate Debian Raspberry Pi
image, follow the instructions, and
Shazam, you have something that feels frighteningly like a Debian
server running on a Raspberry Pi.
...until you reboot it, and the clock comes up very wrong, because the Raspberry Pi does not include a Real Time Clock (RTC) chip. Which is fine, you can set the time using NTP...except that you're running DNSSEC, which gets tetchy when signatures are six months in the future, so the DNS names of your NTP servers don't resolve, so you can't set your clock, so DNSSEC doesn't work, so ....
There are several ways to break this loop, but the cleanest one is just to add an RTC to the Pi. For a few bucks extra, you can add one good enough to use the Pi as an NTP server (for a few bucks more than that, you can give the Pi a GPS receiver and make it a stratum one NTP server, but I didn't feel a need to go there just yet).
There are a number of RTC add-ons for the Pi. I picked the Adafruit "PiRTC Precise DS3231 Real Time Clock for Raspberry Pi" for US$14.95. This sits on the GPIO pins of a Raspberry Pi, and is self-contained (no other wiring) other than a battery clip for a CR1220 battery. One can buy cheaper RTC packages for the Pi (including a cheaper one from Adafruit) but at these prices the difference between a cheap RTC and an expensive RTC gets lost in the shipping cost.
The PiRTC is a nice small board, and the assembled package fits inside a normal Raspberry Pi case (at least, it fit in the stock CanaKit case for my Pi4B).
The instructions for adding the PiRTC are, sadly, a bit of out date, and are for Raspian, not Debian, so here are the things I had to figure out for myself:
-
For some reason nobody actually documents which GPIO pins the PiRTC should sit on. It's the ones closest to the corner of the Pi board (the drilled out hole for the corner post is a hint).
-
For reasons I don't really understand, fiddling around with devicetree (
.dtb
) files to enable the driver didn't work for me on Debian (nor, apparently, does it work on Ubuntu). Fortunately, there's a simple solution to this that works well enough. -
Your Pi may have multiple I2C buses, in fact it probably does. For reasons unknown, none of the documentation I found guessed correctly about which I2C bus my PiRTC would turn out to be on (everything I found said it would be on bus 0 or bus 1, it turned out to be on bus 3).
Installation procedure, assuming you've already installed Debian on the Pi:
-
Power off the Pi. Insert the CR1220 battery into the PiRTC (the battery clip is marked with a
+
to show you which way is up), seat the PiRTC on the left end (closest to the corner) of the Pi's GPIO pins, and power the Pi back up. -
Add
i2c-dev
to/etc/modules
so that the I2C driver will be loaded automatically. You may also want to add it manually at this point by runningmodprobe i2c-dev
. -
You may also need to add
dtparam=i2c_arm=on
to/boot/firmware/config.txt
. I say "may" because the normal kernel upgrade process (which rewritesconfig.txt
) seems to have subsequently removed that line and nothing seems to mind, but if you have trouble finding I2C devices, try adding this. -
Install
chrony
andi2c-tools
if you haven't already done so.chrony
displaces the NTP client built intosystemd
; if you'd prefer to use thesystemd
code, knock yourself out, but you're on your own there, I wanted an NTP server. -
Run
i2cdetect -l
to find out how many I2C buses you have.$ sudo i2cdetect -l i2c-3 i2c bcm2835 (i2c@7e804000) I2C adapter i2c-1 i2c Broadcom STB : I2C adapter i2c-2 i2c bcm2835 (i2c@7e205000) I2C adapter i2c-0 i2c Broadcom STB : I2C adapter
-
Probe I2C buses with
i2cdetect -y $bus
until you find the bus with your PiRTC. In my case it was I2C bus 3:$ sudo i2cdetect -y 3 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
That
UU
sitting at bus address 0x68 is the PiRTC. TheUU
indicates that the kernel driver is up and using that port; if the kernel driver isn't up yet, it may show up as68
. If you see a lot of other ports available, be at least little suspicious: on the Pi where I tested this, I saw almost every port available on buses 0 and 1, no ports available on bus 2, and only port 0x68 available on bus 3. -
Now that you know what I2C bus and port the RTC is on, you need a very small kludge to poke the kernel into noticing the I2C device, loading the correct kernel module, and creating
/dev/rtc0
. On Raspian this is handled via the device tree, but for some reason that doesn't work on Debian (or Ubuntu, as documented by Will Hughes). To work around this, one just needs asystemd
unit file which does the needful beforechrony
starts up:[Unit] Description=Enable DS3231 I2C RTC Before=chronyd.service [Service] Type=oneshot ExecStart=/bin/sh -c "echo ds1307 0x68 >/sys/class/i2c-adapter/i2c-3/new_device && exec /sbin/hwclock -s" [Install] WantedBy=basic.target
Adjust the I2C bus number (
i2c-3
here, for bus 3) as needed. Theecho
voodoo tells the kernel to probe for the RTC thehwclock
command sets Linux's system clock from the RTC, and theBefore
constraint requests that all this happen beforechronyd
starts up.Install this unit file as
/etc/systemd/system/ds3231.service
and enable the service by running:$ systemctl enable ds3231
-
Reboot. If all goes well, the system will come up and find the RTC, which you can check by looking at
dmesg
output and by looking for/dev/rtc0
.
Since you haven't previously initialized the RTC, you will have to do
something manual to break the DNSSEC / NTP circular dependency for
this one last boot cycle (set the clock manually using the date
command, point /etc/resolv.conf
at some other name server for long
enough for chrony
to set the clock, whatever), after which you may
want to run hwclock -w
to initialize the RTC, but from this point on
it should be self-maintaining, with chrony
keeping the clock up to
date and regularly syncing its idea of the current time into the RTC.