Accurate timing thoughts

I'm trying to design a fairly accurate timing system (nearest millisecond is the aim), and have been doing a few searches about what the arduino is capable of.

I know the standard millis() function is not entirely accurate, due to the fractional elements/corrections applied, so have been thinking about alternative ways to provide an accurate way of providing milliseconds, along with a way of recording hours/minutes.

I've settled on the Chornodot/DS3231 as the basis of the timing, as the temperature compensation will be needed for the varying temperatures this system will need to work in (typical scottish outdoors, which may vary from a few degrees below freezing to being left in direct sunshine!).

So far I'm thinking use Timer0 or 1, and change it so it uses the 32khz signal from the DS3231 for the basis of a custom millis() function.
I'm not sure whether to modify the existing Timer0 and related functions (I can't see any need just now for the micros(), but will it upset any other code?), or create new millis function wusing Timer1?

As for recording the time, when triggered, I need to pull the date/time from the DS3231, and record that along with the millis from the arduino. I know I risk the seconds rolling over during this process, so my plan is to use the DS3231's SQW output set at 1hz as an indicator that the seconds might be rolling over. Something along the lines of -

getTime(){
   sqw_warn = false;
   ms = custommillis()
   if (sqw == high) { // if this is high, then we're in the last 500ms
      sqw_warn = true // so set warning flag true
   }
   time = RTC.now;
   if (sqw_warn == true && sqw == low) { // if warning flag was set, and sqw has now changed then we've rolled over
      timecheck = RTC.now;
      if (timecheck == time){ // if times are the same, then time rolled over before we read the RTC so we need to subtract a second  (unless we've had some kind of major delay????)
         time = time - 6seonds; // need to find the proper code for doing this!!!
      }
   }
   serial.print(now.hour(), DEC);
   serial.print(now.second(), DEC);
   serial.print(ms);
}

That's my initial thoughts of doing this, but would it be better if the sqw_warn flag is set high, I read the time, thyen delay say 500ms, then re-read the time,and compare?

There's a reason why things like this have been made for decades already:

GoForSmoke:
There's a reason why things like this have been made for decades already:
DS1307 Real Time Clock Mini Board

But using that will still cause me with the same issues as the DS3231, i.e. I still need to rely on the Arduino to provide the milliseconds part of the time.

theoneandonlymc:
I'm trying to design a fairly accurate timing system (nearest millisecond is the aim),

I did something similar a few years ago (there we needed microsecond resolution, but we were also starting from a more accurate clock). In that case it was also a distributed clock synchronised using NTP. Even without the distributed element, combining multiple time sources to produce a high precision wall clock is surprisingly complicated to do well. It was an interesting project, but in hindsight we could have avoided it entirely and saved a ton of work if I'd been more critical about the requirements.

What are you actually trying to achieve?

Oh, I just looked up the DS3231 and it is an RTC. It also has a 32768 Hz clock signal pin.

You could set up a hardware interrupt to that and count however you want, 33 ticks will give you 1.00708 ms.

PS -- if you did that and used a global unsigned long it would roll over in 36.408888.... Hours.
1 tick would be .0305.... ms, that'd be your accuracy.

PeterH:
What are you actually trying to achieve?

It’s for timing different cycling events, with three different ways of timing needed.
One is time X number of laps, recording each split and the overall times all by one arduino.
Another is have one arduino record when the rider starts, and another arduino when the rider finishes.
Final is one arduino triggers a dual slalom startgate and records the time the gate gets triggered (there’s a random time delay in the start system), and another arduino needs to record the difference between two finish triggers.

Having each arduino at exactly the same time isn’t really needed, provided the time difference is consistent. I’ve got ideas on how to get the different DS3231s at the same time, but I need to decide on the best way to solve the already mentioned problems first.

GoForSmoke:
You could set up a hardware interrupt to that and count however you want, 33 ticks will give you 1.00708 ms.

Timer0 and Timer1 can be set to use an external clock signal (as default they use the main oscilator with prescaler), which with a 32768hz signal and interrupt overflow counting, provide me with 1024 splits per second. I know this still doesn’t provide me with true milliseconds, but the 1024 splits will be in sync with the RTC, and I can scale the 1024 to 1000.
The other option is to use the RTC 1hz as an interrupt, recording the millis() at each interupt, comparing, then scaling recorded trigger times if there is any difference between two consecutive millis().
The DS3231 also has options of 1024, 4096 and 8192hz on the SQW pin, which I could use, but then there is the problem of syncing the SQW signal to the RTC seconds.

It’s a case of there being numerous ways of acheiving the same thing, but figuring out what will be the best compromise to implement :-/

Note that the Uno does not have a crystal clock source for the 328 (unless you unsolder the ceramic resonator and replace it) and is not accurate enough anyway. An external 32768 Hz clock would fix that, but getting a board with a crystal would also work (don't use millis(), use micros()...)

I suggested the 32k signal because it would provide the highest resolution. But an interrupt every 30 microseconds might be a bit much even at 16 MHz.

The SQW is probably the way to go and you can pick your tolerance. Just connect to a digital pin capable of hardware interrupt and set the mode. In the ISR you cannot read millis() and frankly you don't to anyway! Just have it increment a global unsigned long or unsigned int and that value becomes your new_improved_milli and that is your whole ISR.

void time_ISR(void) {
my_time++;
}

Then instead of checking or saving millis() you check or save my_time and calculate real time from there. It's not like millis() are sacred or correct because if they were you wouldn't need an RTC in the first place!
Consider that during an ISR the timers are stopped, delay() and millis() are not available and millis() is not counting up. You not only can't synch interrupt to millis(), you don't want to.

That leaves you free to choose the interval that best suits you. Perhaps 1024 Hz is best because it will interrupt the Arduino less often.

Maybe you can synch your units on a radio pulse?

In the scenarios where one Arduino records start time and another the finish time, I'd think they should be perfectly synchronized. If they can be physically next to each other, then this should be possible.

I'd question whether an RTC is strictly needed. Not sure if custom hardware is an option, but running the system clock from a decent 16MHz crystal (say ±10ppm), along with the Time library, is equivalent to an RTC, as long as you don't need the battery backup of an RTC. If setting the time initially is an issue, maybe an RTC is good for that, read once during setup.

Configure a timer (not Timer0, I'd prefer not to futz with millis()) to generate an interrupt every millisecond, and have the ISR track milliseconds. I haven't tried this, so some experimenting would be needed, but I think the timer should then stay synchronized with the time kept by the Time library, which is based on millis(), so everything is clocked by the same source. When setting the initial time, I'd zero the timer and also reset its prescaler. This could be done either immediately before calling the setTime() function, or it'd be a trivial mod to the library.

Just thinking out loud, so FWIW.

Now I’ve had time to think about what’s been mentioned in this thread (thanks guys!), and try a couple different things, I’ve hopefully found a reasonable solution.

As a couple of you pointed out, the actual time isn’t needed, just the known time between two arduinos, so I’ve removed all the RTC stuff, and am now using just the square wave output of the DS3231 (Chronodot) set at 1024hz.
It’s connected to Interupt 1, with a simple ISR that simply increments a NewMillis variable. It does mean that NewMillis isn’t actually milliseconds, but that can be dealt with through a bit arithmetic later (either coded into the uno, or in software).

The results of this have shown just how innaccurate millis() are. With a simple bit code looped every 2second, which grabs millis() and corrects it to match NewMillis (multiply by 1.024), then subracts the two, NewMillis is gaining between 2-3 counts over millis() every loop.

As for synching two (or more) arduinos, I’m currently thinking of some way of connecting them to a shared switch, which when on, causes the arduinos to disable interrupts, sets NewMillis to zero, and then renables interrupts once the switch is turned of. Then the arduinos can be moved to their needed locations (which may be upto a couple miles apart). Of course this means each arduino needs a reliable power source, but that shouldn’t be too much of an issue.

At the end of the race you can bring the two units together and record any difference. That will tell you the degree of tolerance you can hold.

PS -- if the clocks run at different altitudes even 100 meters apart in height there is a so small you need a special clock to measure relativistic difference in their subjective times. They can -never- be exactly the same.

Alternatively you could just pick one Arduino that seems accurate enough to be used as your reference clock, and calibrate the other Arduinos against that. I mean just apply the appropriate scaling correction to elapsed time measurements, to compensate for the (presumably small) drift of the local clock.

or get a UP501D GPS receiver like this one, and using TinyGPS library, you can get accurate timing ( as well as position, height, speed, direction etc etc )

They are about the same price as buying a RTC chip, xtal, battery, holder, and PCboard.

Incidentally I was shocked at how sensative they are, see Arduino Forum

I have no interest in Fastrax who make the module, I am sure other makes with the same specs would be the same , I am just very impressed with my first play with GPS.