I have been building an electronic system for pyrotechnic firing, and one of the things I knew I needed was accuracy to about 1/100s maintained for up to 20 minutes. I hoped at first that the Arduino’s clocks would be good for this level of accuracy. However, when I first transmitted a firing script from my Mega2560 based Controller to my first Uno based Field Module, and told them to go, the module’s idea of 1ms was significantly different from the Mega’s. When I measured it, they were drifting apart at the rate of about 10s/hour.
Although I didn’t know which was the truest, my first objective was to keep the two in sync. I decided that a reasonable approach was to broadcast a message from the Controller to all Field Modules every 10 seconds, containing nothing more than the Controller’s current millis() value. Each time the module received the value, it would store it, and it’s own current millis() value, compare these with the previous value (10s ago) and compute a scale factor required to convert its millis() value to that the Controller sent. I then wrote myMillis() which took millis() and multiplied it by the scale factor. I used myMillis() within my own code, and the two clocks now keep the same time. Only it probably isn’t good time.
I splashed out on a DS3231 RTC as I reckoned that this would solve my problems. It is temperature compensated, which is handy on a system that may operate at +30C in summer, or -10C in winter. I hadn’t read all the useful stuff on this forum before I bought it, so was a little surprised to find that despite its apparent accuracy, it can’t provide anything more granular than 1 second. That’s not much good, I thought. Then I found a couple of posts that suggested using the RTC’s SQW signal. One post suggested using this to interrupt the microcontroller. Unfortunately, as a software developer and not an electronic engineer, I found the details less than explicit, and one of the reasons for posting this is so the next person wondering how to do this can benefit from my experimentation.
My functional design was to use the RTC’s SQW output selected at a 1 second frequency, and use the same approach to synchronise the Controller’s Mega to the RTC as I had to synchronise the Field Module to the Controller. At each interrupt, the service routine would note the current value of millis() and compare this with the value it stored last second. Anything above or below 1,000 represents a drift and these drifts are added together. Another version of myMillis() this time takes the current value of millis() and subtracts the cumulative drift. Consequently, any 2 calls to myMillis() will result in a value which is very close to the actual number of milliseconds that have elapsed.
That’s the design. This is what I did.
My DS3231 RTC is on a ZS-042 board. This has 6 pins, of which I used only GND, VCC, SDA, SCL & SQW.
GND is connected directly to GND on the Mega.
VCC is connected to 3V3 on the Mega.
for these two, I used the block near the power jack.
SDA is connected to SDA on the Mega.
SCL is connected to SCL on the Mega.
For these two, I used the connectors near the USB connector.
SQW is connected to pin 18 on the Mega. Additionally, I have a 4k7 Ohm resistor between pin 18 and 5V on the Mega.
By way of explanation of the choices here, I have a CTE 5" LCD on its shield sitting on the Mega, and also have an XBee shield on flying wires soldered onto other pins, so have to use what is available and most convenient.
To test this that this connection functioned, I took some code that has already been posted here, modified it a bit and ran it. It appeared to work, in that I could set the time and retrieve it.
This was on a day when my office was getting seriously hot so I printed the drift value every 10 minutes and plotted this against the temperature (that the DS3231 supplies) on a graph. The correlation between temperature and drift on the Mega’s clock is surprisingly close and shown in the attached file.
I left the RTC running for a couple of weeks, comparing it regularly with my radio adjusted watch, and I have to say that it hasn’t shifted. On the other hand, the drift of the Mega’s clock is accumulating at a few seconds (gain) each day, so I am quite happy that my fireworks will go off very near to the 1/100s tick that they are meant to.
I’ll wrap this up by offering the code I used to play with the DS3231. It’s not particularly well commented, but it does show how you can keep track of elapsed times that are close to millisecond accuracy over protracted periods.