I want to make a digital clock like the ones used for timing at races. The idea was to have the Arduino (Uno) turn on and off a bunch of leds (WS2812B) in the pattern representing each digit of a digital clock.
Knowing nothing abut the Arduino I just assumed it would be accurate (down to a second) without drifting over the course of a couple of days. But my first tests it dosn't seem to suggest that's the case and after a bit of googling it seems it's a known issue.
I don't have all the leds for the project yet, so I've tested the core idea with 12 leds. Here the program that runs, it supposed to light each led up every second and for every 12 leds it will change the color. Hence it's easier to count if it's on time, and it seems to loose time even after just one minut.
#include <FastLED.h>
#define NUM_LEDS 12
#define DATA_PIN 5
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS]; //Define array
CRGB colorArray[] = { //Define colors to be used by leds
CRGB::Red,
CRGB::Blue,
};
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
unsigned long secCount = 0;
unsigned long totalTime = 1;
const unsigned long period = 1000; //the value is a number of milliseconds
const byte btnPin = 9;
bool btnState = false;
void setup() {
delay( 3000 ); // power-up safety delay
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS); //Adding the leds
Serial.begin(115200); //start Serial in case we need to print debugging info
pinMode(btnPin, INPUT);
startMillis = millis(); //initial start time
FastLED.clear(true);
}
void loop()
{
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
if (digitalRead(btnPin) == HIGH) {
delay(10);
if (digitalRead(btnPin) == HIGH) {
btnState = true;
}
}
if (btnState == true){
if (currentMillis - startMillis >= period) //test whether the period has elapsed
{
startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
if ((totalTime/13) & 0x01){
leds[secCount] = colorArray[1];}
else{
leds[secCount] = colorArray[0];
}
FastLED.show();
secCount = secCount + 1;
if (secCount > 12){
secCount = 0;
}
totalTime = totalTime + 1;
}
}
}
So how would you suggest to approach this to get the most accurate setup (accurate meaning not loosing seconds over the course of a couple of days). I saw one thread talking about using a GPS module to adjust the time, but I can't quite grasp how to implement this?
The Arduino is a teaching/learning tool. It is not an accurate clock, nor is it advertised to be.
To keep accurate time over days, months and years, use a DS3231 RTC (Real Time Clock) module. That won't work for accurately timing races, though, because it takes a variable amount of time to read out the RTC. Another option is to calibrate the Arduino oscillator using a GPS module.
For more detailed suggestions, clearly state your expectations: length of timed interval, required precision and accuracy. The more detail you provide, the better.
I'm a little puzzled as what more to add, than what's said already. I need some way to keep time within a second accuracy over the course of a few days (lets say 4 just to be on the safe side). Meaning that the visual clock should represent 60 minutes and 0 seconds after a hour has passed, not 59 minutes and 58 seconds, not 60 minutes and 3 seconds.
If I need a module like a RTC that's not a problem (but not an option based on your reply).
I think, there is a simple solution. DS3231 has an output 32768Hz by default. It can be used to drive a timer like T2 and with the timer a very acccurate result can be obtained.
Good suggestion. Arduino RTC code is available to bypass the module RTC functions, or use the DS3231 1 Hz output, count total seconds from the start time, and convert later.
I just discovered a brand new ChronoDot V3 module based on the MAX31328 chip, which should be more stable and accurate than the DS3231. The use is similar. It has the 32k output.
There is an "old trick" that can be "perfect". The power line frequency (50 or 60Hz) is super-accurate (in developed countries). Analog electric clocks use a synchronous motor and the power utility actually makes tiny corrections so if it's a fraction of a second fast for awhile, they slow it down a bit, etc. As long as you don't lose power it can be accurate for years.
I have a digital electric clock that works the same way. I know that's how it works because there is a back-up battery and when the power goes-out it's off by a several minutes when the power comes back on. The internal oscillator is terrible, and probably not a crystal.
Of course, computers and cell phones are synchronized to an atomic clock, somewhere on the network. GPS also transmits a super-accurate atomic clock.
With most racing, you need milliseconds in case of a near-tie. And there is an additional complication in-that you normally need an interrupt (or a "separate clock") so you can "catch" the time no matter where the program is in its program-loop. And, you can't be stuck processing the 1st interrupt when 2nd place breaks the beam (or however it's triggered).
That sounds interesting, but i have gotten myself a DS3231 module now, I'm just quite fuzzy as where to start with the suggestion about using the output frequency.
I don't really need to know the time in terms of day, hour and minute, I just need to know how much time has passed since I started the counter
Just to clarify I wont be needing milliseconds accuracy - it's to display time raced during a running event. The acutual timing is happening with a seperate system. So second-level accuracy is perfectly fine
I think, there is a simple solution. DS3231 has an output 32768Hz by default. It can be used to drive a timer like T2 and with the timer a very acccurate result can be obtained.
That's what I would like a bit of pointers on how to implement
The derivatives of the 32768Hz output will not get an even base 10 number of fractional seconds. For example you can get a 1024 count per second output but not 1000 (milliseconds). You have to do some binary "jumping through the hoops" to get hundredths or tenths of a second.
Like I've already stated (a few time) I would need accuracy down to a second without drifting over the course of a few days (let go with four for now).
Then all you need to do is to program the DS3231 SQW output for 1 Hz, and use any digital pin (with INPUT_PULLUP) to count seconds from event start. That will take around 5 lines of code.