Project goal: Create a super-accurate (<1ms drift per year) standalone 7-segment LED clock whose time stays accurate and "sets itself" without access to the Internet or a host computer (i.e., without using NTP servers) by updating time via GPS time signals. Since GPS satellites have their own onboard atomic clocks, this is an extremely stable and accurate time source! The code only uses SPI.h and Wire.h comm libraries; everything else (interfacing with GPS module, Max7219, RTC DS3231, time calculations) is coded "manually" with NO OTHER additional libraries (as a challenge and learning experience). So the total code base ends up being quite light (about 7,300 bytes). This sketch maintains super-accurate time in an area where there is no access to internet but there is access to a GPS signal.
Project design: Arduino controller (Mega for prototyping, Nano for deployment); 8-digit 7-segment LED display module with Max 7219 controller chip; Ublox Neo-6M (clone) GPS module; DS3231 RTC module. External power supply via 5V USB phone charger.
Requirements: RTC clock time appears on the 7-segment LED display. RTC clock time is updated hourly by syncing with GPS module's 1Hz PPS signal (this achieves the <1ms accuracy)... so that the clock "sets itself" on a permanent basis without user intervention. User-defined UTC offsets (in code) display time in local time zone. User-defined daylight-savings rules, including fractional hour offsets (need to make sure it can work in Nepal!). If power is lost, unit maintains time through RTC battery backup.
[note: I know there are lots of libraries and solutions already on the shelf to handle these things, but I set myself a challenge to code without them!]
How it works:
- There are 2 ways to pull time and date from the GPS module. One is by reading various NMEA sentences and parsing them (this appears to be the most common approach if you check out other GPS clock projects). BUT... there is quite a bit of latency in the time it takes for the NMEA sentence to be sent, read, and parsed, especially at 9600 baud. I measured about 450ms (almost half a second!) latency when I compared the time set parsing NMEA sentences to the highly accurate 1Hz time-pulse output of the GPS module. We can do better than 450ms, right? YOU BET! Read on...
- The magic happens in that the Neo 6M GPS module is capable of outputting a 1Hz "Pulse Per Second" (PPS) signal. This signal is precisely synced to the UTC time received from the GPS satellites once the GPS module has a fix. So we will set time with this PPS reference (via digital reads), and NOT the NMEA sentences. (Make sure to get a module board that has the PPS pin on it... not all modules do!). Do we really need this absurd level of accuracy? Of course not! But... it's fun.
- To further minimize latency, when my code prepares to sync the RTC to GPS, it reads a standard NMEA sentence, then increments the second to be "primed and ready" for the next PPS signal. That way, as soon as the PPS signal is received, we immediately writes the new second to the time registers on the RTC
- When syncing with this PPS method, I get a 20 microsecond difference between the RTC squarewave output and the GPS's PPS signal right after doing the syncing. By doing the sync every hour, we assure a super-accurate RTC time register is maintained. The RTC 1Hz squarewave output in turn triggers an update of the RTC time to the LED display. Accounting for the time it takes to read the RTC time registers and then output those data to the LED module, I've calculated a total latency of about 400 microseconds best case and, as the RTC's accuracy drifts over the hour, around 1.2ms worst case.
Note: this was a terrific learning experience, showing how to program and control the various modules "manually" without libraries. The intricacies of setting time, and dealing with UTC offsets and daylight savings, were also huge coding and mental challenges. I know my code can certainly be improved in areas, but it does appear to work!
IMPLEMENTATION NOTES [SEE FOLLOW-UP POST FOR MORE ON THESE]
A) GPS Module and super accurate time-setting.
B) UTC Offsets for local time - without a library!
C) Daylight savings - without a library!
D) RTC modules - hidden hardware traps!
E) Interfacing with the Max7219 - without a library!
F) Interfacing with the DS3231 RTC chip - without a library!
G) IR Remote Features - without a library!
PROJECT PHASES
Phase 1: Prototype hardware and code on a Mega. [done]
Phase 2: Migrate to Nano. [done]
Phase 3: IR remote interface to toggle date/time, and toggle local/UTC time. [done]
Phase 4: Build enclosure for tabletop display.
CODE (Github):