This one’s a doozy. Maybe not to read, or write, but to code it was by far the most ‘fun’ so far. My decisions at the time may have been lost to the sands of time, but I will try to capture them here as best I remember. Let’s start with NTP. NTP is very good for setting a clock, but it takes a call out to the network, and a response back along with a bunch of calculation you’ll see later to get a good time out of it. NTP is not suitable for repeated hammering if you want to append a time to, say, a log file. a Real-Time Clock is great for keeping time (just like your alarm clock) but not so great if you have to set it. You could send an offset to move it up or back a certain number of minutes, hours, whatever if you need to set it, but we’re already on the network so why not do it automatically. The code I write can work so much faster than I can and synchronizing clocks is not really something to be left to ‘eyeballing it’.
The most common RTC is the DS1307, it’s everywhere, like popcorn almost. The chip I ended up using however was the DS3231. In case you were wondering, the DS prefix is a holdover from when Dallas Semiconductor used to make them, they were bought by Maxim and that is where you can see new products coming from. The DS3231 is more accurate, but most importantly for me is that it will run off of 3.3v. You can see one of my favorite blogs go over how to use them here, my use case was getting it set by NTP, then using it to timestamp data. The ESP is supposed to have an RTC in it, but it uses a pretty bad oscillator, even then I don’t know how to use it. For this project I elected not to unravel the use of RTC libraries to write one for the ESP specifically and rather just use an off-the-shelf one.
The first challenge I had was getting the clock to read. That code was taken blatantly from a library’s example and trimmed down to accommodate what I needed. It included a bunch of checks to see if the time and date coming from the RTC made logical sense, which is useful when you’ve got a 50/50 chance you wired up sda and scl correctly. I left in pretty much everything from that example and adapted the functions to be printed, printed to serial, to return a string, whatever was needed. The code I provide isn’t that hard to follow if you view it in a way that allows you to collapse sections once you understand them (the code structure shows through more there).
With reading from the RTC done now I’ve got to read from NTP. Another example to the rescue. NTPClient had everything that was needed to get an NTP packet. That’s not really all that useful, but the act of constructing the packet and getting it back successfully was done. Now I have a packet (gotten from a straight UDP request) but the data in it is still Greek to me (actually I prefer to view things like that in HEX but…). The final piece to the puzzle was on the arduino code repository (uh, oh! that’s never recent) and miraculously worked, but I needed to modify it for our application. This was not designed for the ESP, so all the network initialization stuff had to go, substituted for our own. Thank god for API standardization, because the wifiUDP works just like the ethernetUDP. The sending of the NTP packet function was used wholesale (but the function call was moved so that it didn’t go every loop (I did pull apart an example program after all).
The code for converting from the NTP packet to a readable date/time value is complicated. I found through experimentation that one version of the code wanted seconds from 1970 and the other wanted seconds from 2000. I added line 284 (as of this version of the code) to compensate. I also adjusted for DST since that was different as well. The other functions I have in my code are really for formatting (and I’ve never made sprintf() do anything useful so I chucked it). Now I have welded three different programs together using 3 different example programs to set the RTC from NTP. After all that the rest seems trivial, but I’ll say that I have an update interval to set the RTC by and MQTT commands to request a timestamp or set the clock. I didn’t put in the ability to set the update interval, but that’s also do-able. This code, however terrible should really be broken out into a separate file so I can include it in many projects. I like having time, now any output could be a clock. If you really wanted to there’s a cron-like interface to use with arduino and you’ve got an alarm clock. Some nixie tubes and shift registers would get you a very pretty one (even use a decatron for seconds).
As should be expected the code is here.
The rest of this series can be gotten through from the home page here.