One reason being that commercial, GPS based NTP servers still cost upwards of $300. That looks wrong to me. The last components I bought online were:
- ESP32 board: $6.50
- DS3231 time module: $5.50
- NEO-6M GPS module: $15.00
The trigger reason still is that I once had a big power outage (yeah, we do get Hurricanes to wreak havoc on the local infrastructure every once in a while in the northern Philadelphia suburbs). I was able to get through with a generator and power was restored after a couple hours. What didn't get restored was my Internet connection. Now all the systems didn't agree on time and since nothing came out of stratum 16 ... things didn't work well. Half my infrastructure was paralyzed because there was no authoritative time source (watch out for authentication services that demand time sync).
Lesson learned: It is cool to have local, independent infrastructure. As long as it is as independent as you think it is. Depending on NTP will not fail immediately, but who tests that stuff by cutting the internet connection for hours or days?
Anyhow, so the plan is to build a cheap NTP server. For now the idea is
- Keep a DS3231 in sync with NEO-6M PPS to the accuracy of +/- 10us by manipulating its aging register (see below).
- Keep the RTC of the ESP32 in sync with the DS3231 using adjtime().
- Serve NTP on port 123 based on the RTC.
So I created a new DS3231 driver that allows getting the time, using a PPS pin (the DS3231 can produce that) to really know when that second happens, and then created a new MicroPython function time_pulse2_us(). Similar to the existing time_pulse_us() it measures a pulse. But this one takes two pins and two levels, then returns the time difference in microseconds between one pin reaching the requested level to the other pin following on its requested level. When I now measure the rising edge of the GPS module against the falling edge of the DS3231 module, I know how far the two are apart in microseconds. Adjust the aging register to slew the DS3231, rinse, repeat ...
Unfortunately the C code can't measure anything below 6us. It might be possible to get more accuracy by restricting the use of IO pins so they have to be on the same IO register ... but I really don't want to get there at this point. 10us is close enough, especially considering that the ESP32 won't respond to a udp request on some asyncio based server fast enough to make it worth the effort.
There is no code I can share at this moment. What I want at this point is some input. What else should I consider? What have I missed? Is this of interest to anyone?