This project kind of grew rather than being designed. A good bit of the direction was based on parts I could get with the chip availability problems of 2022. Last summer, I decided to build a bunch of the cost optimized version of my LED Matrix display driver boards while I could get the parts. When the construction of the boards was complete, I built a test fixture to hold several of the displays, a miscellaneous LED controller board, the I2C mux board and ambient light sensor board. Pretty much anything that uses these displays uses all of these components. The ambient light sensor board is the only "new" design because the previous sensor was unavailable. Having these components all mounted nicely and wired up for doing the driver software development, it occurred to me that it would be a good start on the display for a time server. 

Display Test Fixture
Display Test Fixture

Initially, I did not want to use a Linux based system to run the server on. I have a couple of different eval boards for microcontrollers that have ethernet hardware support. The prospects of writing a GPS disciplined Network Time Protocol server to run on a network stack of unknown heritage were not appealing to me. This motivated me toward the Raspberry Pi. All of the display and user interface control would take place via the I2C bus. The GPS (an old Trimble Copernicus II board from previous projects) would interface via the UART and one GPIO pin for the 1PPS signal.

Only 4 of the displays can be run on an I2C bus segment because the driver chips (IS31FL3730) only support 4 choices for their address. Part of the cost reduction that I did on these boards was to remove the expensive I2C bus address translator chips from each display card. Now, I use an 8 way I2C bus multiplexer chip (PCA9547) on a board of it's own to split the I2C bus into up to 8 segments. Two of those segments are used to talk to the 7 characters. The discrete LEDs visible to the left of the top row, between the second and third displays on the top row and between characters on the bottom row are driven by a board using another IS31FL3730 chip. This was done because it is a simple way to control the "miscellaneous" LEDs with a part that I already have driver software written for. The ambient light sensor and the DIP switch board are on separate bus segments because it simplified the cabling. A single byte write to the mux chip is all that is required to select a segment, so the overhead is not bad.

Automatic intensity control for the LED displays is done via the ambient light sensor (VEML7700) chip. The ambient light level is read from the sensor and the result used to look up an intensity value for the LED driver chips from a short table. The contents of this table are empirically derived and there are only 5 different levels at this time. Originally, the ambient light sensor lived on the I2C mux board, but the sensor was completely unavailable (boo, hiss Rohm), so I designed a new board with sites for 2 different sensors from different vendors.

The user interface is done via a 6 wide DIP switch and 2 push buttons. The red button initiates system shutdown and the black one causes the last byte of the ethernet and WIFI addresses to be shown on the display. Only 3 of the switches in the  switch pack are used right now:

A TCA9534 I2C I/O expander chip is used to read the switch settings. All of the switch circuits are protected from ESD damage with TVS diodes to ground. Capacitors to ground protect against RF noise in the switch circuits.

I had a couple of Trimble Copernicus II GPS modules and bare boards left over from previous projects. This module is 10 years old and no longer available, but it speaks reasonably standard NMEA and I had them. The boards that I had left over have a local 3.3V LDO regulator and a place to put an RS232 level shifter chip. Since everything is in the same box and only a few inches apart, I bypassed the level shifter and just used the logic level UART signals for communicating with the Raspberry Pi. The 1 Pulse Per Second output is routed to one of the GPIO inputs on the Pi. There is a backup battery on the GPS board to maintain the almanac data to make initial satellite acquisition much quicker.

Power is supplied to the system via a 12V wall wart. Internally the 12V is regulated down to 5V and 3.3V. All of the I2C and display stuff runs on 3.3V while the Raspberry Pi and GPS run on 5V. The main regulator board uses a pair of buck regulators (ADP2303ARDZ) to minimize the heat dissipation. There is extensive filtering on the input side to keep conducted emissions down between the wall wart and the time server. Both of the regulators have a large zener diode to ground to protect against output over voltage in case of a failure of one of the regulators. A 1.5A fuse on the input of the regulator board should protect the system in the event of a regulator failure. Total power consumption is about 4 watts at 12V.

24 Hour Time Format in Green
24 Hour Time Format in Green
12 Hour Time Format with AM Indicator to the Left of the Time.
12 Hour Time Format with AM Indicator to the Left of the Time.
Red Display
Red Display
Bottom Edge View of Chassis Showing the Raspi, Power Supply and a Row of Displays.
Bottom Edge View of Chassis Showing the Raspi, Power Supply and a Row of Displays.
Top Edge View Showing Ambient Light Sensor, Displays, Discrete LEDs on Top and the Miscellaneous LED driver, GPS and I2C Mux Boards on the bottom Row.
Top Edge View Showing Ambient Light Sensor, Displays, and Discrete LEDs on Top and the Miscellaneous LED driver, GPS and I2C Mux Boards on the bottom Row.

Software

The Raspberry Pi is running the standard Raspberry PI OS. It is configured with static IP addressing on the ethernet port. It is possible to insert a USB WIFI device in one of the USB connectors adjacent to the ethernet cable if I want to make time service available to the non-airgapped part of my network, but this is not expected to be used.

GPSD is used for handling the NMEA data and 1 Pulse Per Second information from the GPS receiver in the Raspberry Pi.

The NTPD time server application is used for serving time out to the network (https://doc.ntp.org/documentation/4.2.8-series/ntpd/ ). NTPD gets its time information from GPSD and the 1PPS device. Setting up NTPD can be pretty complex. I found a fairly complete recipe to configure everything at: https://www.slsmk.com/how-to-setup-a-gps-pps-ntp-time-server-on-raspberry-pi/ This article is old enough that some things have changed, but it was really useful to get the big pieces together.

Controlling the displays is done through an application that I wrote using a low level driver library that I have written. The low level library handles converting an ASCII string to register commands to the driver boards and I2C mux to display the string. A hardware abstraction layer has made it easy to change between host machines. Most of the development of the library was done on a desktop PC using an Aardvark USB to I2C adapter. Porting the library was a matter of writing the hardware abstraction layer for the Linux I2C calls and the rest just worked.

Time for the display comes from the Linux time system calls. Time zone handling is done by the Linux system. Time can be retrieved in either local time or UTC time and is selected by one of the DIP switches on the box.

Once the display system is initialized, there is a loop that runs the following tasks:

The display control is written as a daemon that is started at boot time by the systemd service control.  Because the daemon is started by the OS, it would run as a root owned process, which is a security issue. There is a fairly elaborate dance required to change that over to running as an unprivileged user. This is all detailed in the excellent book: "The Linux Programming Interface" by Michael Kerrisk.

Linux daemons need to watch a couple of signals (SIGHUP and SIGTERM) for re-configuration requests or shutdown commands. This is done with a signal handler for each signal that can set a flag if the signal is received. That flag is monitored in the main loop of the display code.

Mechanical

The enclosure for the time server is made from aluminum. The base plate is 0.090" 6061 for rigidity (and I had it in my scrap pile). The outer skin was formed from 0.030" 5052 sheet aluminum. It is powder coated after all the parts were made and test fit. More planning would have resulted in a better fit and easier assembly, but that is not a surprise.