The time calculation is inaccurate. It's roughly correct, but well outside of the millisecond tolerance I'm aiming for.
The PPS signal is treated carelessly, such as toggling it while waiting for WiFi to establish. The PPS signal should only be pulsed if the time is accurate.
There are a bunch of other signals emulated, and a serial interface, all of which we don't care about. On the other hand, we don't want the WiFi credentials to be hard-coded, so a soft-AP interface would be good.
Environment
Although I'm starting over, I decided to continue with the Arduino environment if possible.
There are dozens of NTP implementations for Arduino and ESP8266, but looking at their code is disheartening. The included "NTP library" performs the time measurement by calling delay(10) in a loop and counting the iterations. So even if the rest of it was perfect, the result would be, at best, quantized to 10ms increments. I have to be realistic about the level of precision we can achieve – a few milliseconds is probably the best we can do – but we might as well implement the standard correctly, and to the best of our timing ability.
Another problem with beginner-friendly environments is that many of the tutorials and examples are produced by blindly copying and pasting code chunks from each other. The example NTP implementation inexplicably fills out some of the NTP header with junk, and many other examples and implementations have copied this exact code, without explanation.
In fact, you only need one single byte to be non-zero to form a working NTP request. The first byte needs to indicate that we're a client, and optionally the NTP version. The remainder of the 48 bytes can be left zero, they'll get filled out by the server.
NTP stamps
In essence, we send a request and wait for its response, measuring the time taken. The response has a couple of timestamps set by the server. We add half the time taken for the request to return and we've got ourselves an accurate timestamp. If the network route is symmetrical, this should be accurate to well within a millisecond.
... continue reading