By this point I really don’t need to explain how great the Raspberry Pi is - a nice useful lump of computing power for not very much money1 . The low price point does mean that you loose some features that come standard on larger computers though, and one of the less obvious omissions is a ‘real time clock’ module to keep track of time while the main computer is powered off.
The reason for omitting this is pretty obvious - even though RTC modules are fairly inexpensive, the extra circuitry and space for a battery would be hard to justify for a device that is going to be network connected and therefore able to sync its clock over NTP. For most things, this trade off doesn’t really matter; just add an init script to run
ntpdate after the network comes up but before
ntpd starts and the difference becomes an implementation detail2
The obvious exception for this is when you are in an environment where you don’t have a reliable network link, so can’t run ntpd. Fortunately, as previously mentioned, RTC modules are fairly inexpensive. I’m using a DS3231 TCXO3 I2C Module4 , although there are others available.
The software side of things is a bit fiddly to get setup properly. Because the DS3231 connects over I2C, the kernel doesn’t automatically detect the device and load the relevant driver. You need to tell the I2C bus that you’ve added the device, then the kernel will detect that it can load a driver for the device5
. Once the device is detected and drivers loaded, you use the
hwclock tool to set the system clock from the hardware clock. Ubuntu does include an init script at
/etc/init.d/hwclock.sh to run hwclock at boot, but this is of no use to us because the clock device will not be recognised by the kernel when this script run, so we need to do this for ourselves. I’m running Ubuntu Xenial6
on my Raspberry Pi 3, so I’m doing this using SystemD unit files.
My unit file looks like this:
[Unit] Description=Enable DS3231 I2C RTC [Service] Type=oneshot ExecStartPre=/bin/bash -c "echo ds1307 0x68 | tee /sys/class/i2c-adapter/i2c-1/new_device" ExecStart=/sbin/hwclock -s [Install] WantedBy=basic.target
ExecStartPre pokes some magic values7
into the right place in the right places in sysfs to get the I2C bus to detect the device, and the
hwclock to set the system time. I’m setting this unit to be
WantedBy basic.target, so the clock gets set correctly as early in the boot process as possible. We don’t need to do anything special for shutdown, the
hwclock.sh init script handles saving the system time back to the hardware clock for us. To enable the unit, write the file to
/etc/systemd/system/ds3231.system, and run
systemctl enable ds3231 to set it to run on boot.
If the RTC module is fresh from the factory, or hasn’t been plugged in in a while, you will need to set the time on the module. The easiest way to do this is to set the system clock, either using NTP or by hand if you really cannot ever get a network connection to this machine8
, then run
My Weird Use Case
The Pis I’m using this for are actually about as far from not having a reliable network connection as you can get - they are two Pi 3 B’s that run as a load balanced pair, and serve DHCP and DNS for my home network. DNS is where this gets tricky. I’m running Unbound as a caching recursive name server for machines on my network, and making use of its DNSSEC validation features. As it turns out DNSSEC, like other security protocols, needs a reasonably accurate clock to work properly. Unbound is less picky than some other tools in this regard, but gets really, really unhappy when you give it a root anchor file9
that doesn’t become valid for several years because your clock isn’t set properly. Unbound will return
SERVFAIL responses to all queries until you correct the clock, and even with log verbosity set to full, the only hint as to what is wrong is the super helpful “failed to prime trust anchor” message.
This presents a bit of a dependency loop: no DNS until the clock is set right, but I can’t use NTP to set the clock until I have DNS to look up a server to use. I could have done something crazy and hard-coded the IP of another machine on the network to act as a low-stratum NTP peer to get the clock close enough to get DNS working, or disabled DNSSEC in Unbound until I’ve set the clock, but for a few dollars an RTC module is much less hacky
- oh, and the whole democratisation of computer education and all that jazz
- this is, in fact, what Raspbian does
- temperature-compensated crystal oscillator - the frequency which a crystal oscillator ticks is dependant on temperature, so a TCXO adds extra circuity to account for this
- from Nicegear, although you can get them a bit cheaper from eBay
Confusingly, the DS3231 it uses the
- Ryan Finnies’ images from the Ubuntu wiki
If you are using something other than a DS3231, you will need to tweak the values you are echoing to the sysfs. If you are using an old school Rev 1 Pi B, you will need to
- Airgapped Bitcoin vault or something? You can set the clocks on another Pi and move them across
- Think of it as the equivalent of the root CA cert in the TLS PKI