Latest project is a time server.
So why? Well, they exist - you can buy a really nice LeoNTP server, with impressive specs. We see response times of 0.1ms, and it claims 100,000 requests a second. They also have a PPS output, and can do a calibrated 10MHz output apparently (I can't do that).
Can I get close using an ESP32? Well, sort of.
Challenges
It should be simple, in theory - a GPS module, capture CPU cycle count on PPS interrupt and use to get clock rate. Capture NMEA to get time for PPS. Capture cycle count on NTP packet and use reference cycle count, clock rate, and reference time to know exact time of day to fill in NTP reply. yay!
Of course it is never entirely that simple. I did all this and got a working system, but I could do better. My latency as measured on a FireBrick was 2.5ms. I did averaging of the PPS intervals to get a more consistent clock rate, and often the standard deviation on that was below 10ns! But not always. I then did a best of last 5 seconds in terms of a PPS interval to use as a reference - that way the odd delayed interrupt had no impact. That seems to work.
One thing I wanted was PPS interrupt at higher priority than Ethernet, but all GPIOs on an ESP32 are the same interrupt source. Bugger. Do a search and you see plenty of people pissed off about this. I found a fix, make the PPS a PCNT (Pulse count) which has an interrupt (count to 1) which can be set separately to the Ethernet interrupt. There is a trick to remember.
The hardware
The PCB design is not that hard now I have cracked Ethernet. A main PCB, with USB-C, DC input, PoE, and Ethernet.
I have since made an even more compact design.
This then connects to a GPS module, which you can hang out the window (or better, fit in a Stevenson screen). Link with 5 core cable (solid cat5 is ideal), and it can handle a few metres.
Then put both in a nice 3D printed case. See https://shop.revk.uk/ if you want to buy. £60 not £600!
Making it faster
The fact my response times where around 2.5ms was not ideal. I wanted better, but how.
First bodge was hook in to the Ethernet driver receive code and check for NTP packets as they come in, and do a direct reply. I got latency down to 1ms, yay!
But I can do better :-)
Scrap the Ethernet controller altogether. Write my own custom low latency driver. Dedicate CPU1 to PPS and Ethernet only. My custom driver can...
- Be reactive only - reply immediately to ARP, ND, and NTP.
- Being reactive, no need for mutex wrap on access to Ethernet chip.
- Work in interrupt (yes, nasty, but dedicated CPU) so not task switching.
- Know state of registers to avoid read-modify-write as used a lot in standard driver code.
- Do read fifo and write fifo with no clean-up between
And guess what - latency down to 0.1ms - bang on what I wanted. Indeed I have seen 0.077ms even.
Now, the logic is fun - it makes the Ethernet no use for anything but NTP. So I have made it (a) optional, and (b) normal Ethernet for first 2 mins so you can access it via Ethernet if needed (assuming you can control power/PoE to reset).
But once switched to low latency NTP only Ethernet, you have to use the WiFi for any access, management, MQTT, and so on.
Bugs
I found bugs in the Ethernet chip (KSZ8851SNL) which does not check IPv6 UDP checksums correctly!
I also found it almost impossible to convince the Ethernet chip to give me unicast, multicast, and broadcast packets - in spite of a lot of reading the data sheet and trial and error. It is now in promiscuous and relying on the switch to protect it. Even that makes no sense - you have to set "Allow any packet" and "Invert the input filter" which to mean means "allow no packets!". I may yet find a working setting. I have already spent a day on this.
I also found it used edge triggered interrupt with a check in task for ISR set as well on timer, I changed to level triggered and that seems to work without hanging.
I also found it nearly impossible to cleanly decommission the Ethernet driver in ESP IDF. I managed to take over interrupts and kill the task, and that was all. Anything more thorough was a nightmare. But that works.
What is it good for?
Well some people are putting in the UK NTP pool, it seems to meet the requirements well and work at a level similar to the rest of the pool.
But it is mainly aimed as being the main server in any business / office. Include with your pool NTP, but being local it will win.
I do not know if it would get close to even 10,000 requests a second, but that is not needed for a typical business, even a large one.