If you haven't seen my GPS clock, it's the starting point for this project.

The Pi Zero W clock takes the GPS clock and lops off the microcontroller and GPS stuff. What's left is the MAX6951 LED display controller. This chip is driven by SPI, which, quite conveniently, the Pi Zero supports rather easily.

Since I also already have a pile of 5V power supplies that terminate in 2.1mm barrel connectors, it's a simple matter to keep the power jack on the board and just supply 5V to the Pi over the GPIO header.

The Pi will simply boot up and run a small daemon to drive the SPI display to show the current time. The Pi will use NTP over WiFi in the usual manner to get time.

The accuracy that's likely to be achieved will be easily an order of magnitude or so worse than the GPS clock, but keep in mind that the display granularity we're going to support will be only 100 ms. The GPS clock claims an accuracy on the order of a hundred microseconds. This clock will likely only be accurate to within a few milliseconds. That's still quite good enough for a clock display for humans. If you want to do better, you can set up a stratum 0 server on your local LAN and use that as a source. If you do that - even over WiFi - your system clock accuracy should be within a few hundreds of microseconds (if not under). Alternatively, you could connect a GPS receiver module's serial I/O and PPS up to the pi (see instructions for that in the Pi NTP server project), and likely get much better.

The daemon for the display will take some extra pains to set itself up for realtime priority and lock itself in memory, but a Pi Zero with nothing else to do likely won't have any trouble keeping up.

The one issue with this concept is that there's a chicken-and-egg problem with getting it on WiFi. You need to talk to the zero to configure it to use WiFi, and there's no way to talk to it wirelessly before you do. PiBakery offers an ideal way to work around this problem. You can use a PiBakery recipe to write a custom Raspbian image out to an SD card. On first boot, the PiBakery scripts will configure things as you request, including a WiFi network and password and optionally changing the default user password. The PiBakery recipe included in the project source tree will also enable SPI, the serial console, and will download the clock daemon source, compile and install it, and configure systems to launch it at boot. It will then reboot the Pi.

To facilitate serial console configuration, the clock board will break out the console lines to a 3 pin header (and provide a diode-pullup level shifter on the input line). You can connect it up to a TTL serial port (like an FTDI-be-gone) and USB to a host computer from there. Alternatively (if you're not using the Pi Zero's USB port for something else), you can configure the USB OTG support to provision a "gadget" serial port. In this case, you can connect an ordinary microUSB cable between the zero and a host and the host will see a USB serial adapter. Use terminal software to open that virtual serial port and you'll get a login prompt from your Pi Zero.

Once you have the Pi on the network (and your network has Internet connectivity), the default NTP configuration should be good enough to properly set the time. You can change the daemon's default command line arguments if you wish by editing the systemd config file and you can change the clock's timezone by changing the system timezone.

The circuit on the board is fairly simple. The 2.1mm jack receives 5 volts from an external supply. To protect against spikes there is a 5 volt TVS diode and a bulk capacitor. Next there's a ferrite bead to try and prevent power noise from being radiated from the supply cable. The 5 volt rail is then run directly to the 5 volt supply pins on the Pi GPIO header. You can power the system from either the 2.1mm jack or the Pi's power input USB connector.

The MAX6951 has the ability to supply constant current to the LED segments. What this means in practice, however, is that it must drop the supply voltage down to some small voltage above the LED forward voltage plus a small amount of headroom. To make a long story short, it's better to run the chip (and by extension, the display) from 3.3 volts than 5. The Pi has a 3.3 volt supply and provides that power on two pins of the GPIO header. However, the amount of current you're allowed to draw from that supply is not sufficient to power the display (in theory the average current could be a third of an amp or more). So we must provide a buck converter to supply the display. The good news, however, is we don't need to adapt the logic signals from the GPIO header, because they're 3.3 volt levels too (and a common ground is enough for them to function).

We also add a pull-up resistor on the !CS line to insure that it's kept de-asserted while the Pi is booting up (or other times with the clock daemon is not running and the GPIO pin will be high-impedance). When !CS is high, the state of the data and clock pins does not matter. Also, there is a diode and pull-up level shifter on the serial console input pin so that the serial console port is tolerant of 5 volt TTL. The serial output pin is 3.3v logic, but that works for most TTL serial adapters out there.

The LED matrix is straight out of the MAX6951 datasheet. We use individual LEDs wired for a phantom "digit 7" to make up the intra-digit colons and the AM and PM indicators.

The software simply communicates with /dev/spidev0.0 with the MAX6951. We configure the SPI port for a 20 MHz clock speed (the maximum in the MAX6951 datasheet is 26 MHz), and SPI mode 0 (clock is active-high, data is latched on leading edge, so the Pi should change it on the trailing edge). Each SPI transaction is just two 8 bit bytes - a register number followed by the data for that register.

The software will take some pains to insure that it's locked into memory and it sets itself up for the realtime scheduler at half full priority. It uses the high resolution timer system to schedule a thread to make a callback just before each tenth of a second (so that it lands in the right method on-time). The update timing tends to have a bit of jitter to it, but even so preliminary observations are that it usually happens within 100 microseconds of when it's scheduled to do so. This is typically going to be dwarfed by the accuracy NTP will be able to maintain.