https://youtu.be/oVZgFsWsKPM][1]
I'm a beginner in Arduino. I made a 12-hour common cathode 7-segment clock. I made a 7-segment display with LEDs, using Arduino Nano.
If I use a long period of 1,000 milliseconds, my clock loses 6 minutes every 24 hours. But if I use 994 milliseconds, it gains 30 seconds every 24 hours.
My code:
#include "SevSeg.h"
SevSeg Display;
const unsigned long period = 1000; //one second
const unsigned long led_period = 500; //LED blink millisecond
unsigned long startMillis;
unsigned long led_startMillis;
unsigned long currentMillis;
unsigned long led_currentMillis;
const int hrs_btn = A0;
const int min_btn = A1;
const int ledPin = A2;
int Hrs = 12;
int Min = 0;
int Sec = 0;
int Time;
int ledState = LOW;
void setup()
{
pinMode(hrs_btn, INPUT_PULLUP);
pinMode(min_btn, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
byte numDigits = 4;
byte digitPins[] = {10, 11, 12, 13};
byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
bool resistorsOnSegments = false;
bool updateWithDelays = false;
byte hardwareConfig = COMMON_CATHODE;
bool leadingZeros = true;
bool disableDecPoint = true;
Display.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);
Display.setBrightness(100);
}
void loop()
{
currentMillis = millis();
if (currentMillis - startMillis >= period)
{
Sec = Sec + 1;
startMillis = currentMillis;
}
led_currentMillis = millis();
if (led_currentMillis - led_startMillis >= led_period)
{
led_startMillis = led_currentMillis;
if (ledState == LOW)
{
ledState = HIGH;
if (digitalRead(hrs_btn) == LOW)
{
Hrs = Hrs + 1;
}
if (digitalRead(min_btn) == LOW)
{
Min = Min + 1;
Sec = 0;
}
}
else
{
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
if (Sec == 60)
{
Sec = 0;
Min = Min + 1;
}
if (Min == 60)
{
Min = 0;
Hrs = Hrs + 1;
}
if (Hrs == 13)
{
Hrs = 1;
}
Time = Hrs * 100 + Min;
Display.setNumber(Time);
Display.refreshDisplay();
}
3 Answers 3
All clocks (except perhaps the likes of atomic clocks) drift. If you want more accuracy, you need to build a correction factor in.
What I have done in the past is to allow a correction figure to be put in. This is the error in 24 hours. The code then divides this over 24 hours and adjusts the time on every loop.
It may help accuracy if you keep a millisecond variable in your code for adjustment.
-
There is already a millisecond variable in the code, but I appreciate your idea of a more complex solution. However, as the questioner does not know how to program, this is more difficult than using
micros()
as suggested elsewhere, and too difficult in this case.the busybee– the busybee01/09/2025 17:07:53Commented Jan 9 at 17:07
Your Arduino's oscillator is too slow by:
(24 hours - 6 minutes) / 24 hours = (24 * 60 - 6 minutes) / (24 * 60 minutes) = 0.995833333 (about 0.4%).
After changing the constant period
to 994, the oscillator is too fast:
(24 hours + 30 seconds) / 24 hours = (24 * 60 * 60 + 30 seconds) / (24 * 60 * 60 seconds) = 1,00034722222 (about 0.03%).
You can adjust the constant period
to some next integer, 995 or 996.
According to your follow-up question you consider using microseconds for better accuracy. I'm afraid that this will not really help you, as it is the hardware that is inaccurate. Depending mainly on temperature and supply voltage it will have varying frequencies. You will need another approach for a more accurate operation.
Anyway, these are the necessary changes to use microseconds:
- Replace the call of
millis()
bymicros()
where it is used for the time, in the first statement ofloop()
. - Multiply the constant
period
by 1000, there are 1000 microseconds in a milliseconds:const unsigned long period = 1000000;
- Rename the time variables
startMillis
andcurrentMillis
tostartMicros
andcurrentMicros
. This is not necessary for a working program, but for human readers of the code.
Then you can start to adjust the constant period
. Be prepared for multiple repetitions of adjustment.
And please don't be disappointed when the hardware cannot fulfill your expectations. It has a reason why digital clocks use a trimmed crystal oscillator, or receive their time from external and much more accurate sources.
-
Bro can u add some line to my orignal code to add delay of 15 second after every 12 hour in this way clock wil delay time 30 second in 24 hoursjagpal singh– jagpal singh01/09/2025 09:44:10Commented Jan 9 at 9:44
-
When i use 995 its loses more than 30 seconds but with 994 its gain 30 seconds i need 994.5 thats not possiblejagpal singh– jagpal singh01/09/2025 10:01:22Commented Jan 9 at 10:01
-
@jagpalsingh That's why I added the way to use microseconds. Please apply the proposed changes.the busybee– the busybee01/09/2025 10:18:13Commented Jan 9 at 10:18
-
Bro i did what u said to do . Adjust the constant to 994500 now waiting for 24 hours to see result. 1000000×1000÷994.5jagpal singh– jagpal singh01/09/2025 19:37:13Commented Jan 9 at 19:37
-
Thanks bro u r great it works for mejagpal singh– jagpal singh01/10/2025 05:50:50Commented Jan 10 at 5:50
As pointed out by hcheung in a comment, you shouldn't expect a usable
accuracy from the Arduino's millis()
or micros()
. This is because
these functions rely on the Arduino's clock source, which for the Nano
is a ceramic resonator. These resonators tend to have significant drift,
which is not a problem, as you can calibrate out the drift. The problem
is that the drift rate is not constant: depending on the temperature and
random changes within the resonator, it speeds up and slows down in a
somewhat unpredictable manner (partially predictable if you monitor the
temperature). A quartz-clocked Arduino would give far better results.
For more on this topic, including actual accuracy measurement, I
recommend the excellent blog post Arduino clock frequency
accuracy, by Joris van Rantwijk.
As a solution, hcheung recommends you use something like a DS3231 RTC, and I do second this recommendation.
If, however, you do not care about accuracy, and want to calibrate out
the Arduino's clock drift anyway, I suggest you use Adafruit's RTClib
library. Besides supporting actual RTC chips, like the DS3231, this
library provides a "soft" RTC in the class RTC_Micros
.
This class implements the same logic you are using for advancing your
clock, except that it does it right (your logic is slightly flawed).
Furthermore, the class has the method adjustDrift()
, which does
exactly the kind of drift adjustment you are trying to implement. The
method's parameter is a drift correction in ppm (parts per million),
where a positive correction makes the clock faster. Six minutes per day
is about 4,000 ppm.
Here is a simplified version of your sketch, based on RTC_Micros
. For
simplicity, I left out the code handling the LED blinking and time
adjustment.
#include <RTClib.h>
#include <SevSeg.h>
const int clockDriftCorrection = +4000; // in ppm
RTC_Micros rtc; // soft RTC
SevSeg Display;
void setup()
{
rtc.begin(DateTime()); // start at 12:00
rtc.adjustDrift(-clockDriftCorrection);
Display.begin(/* display settings... */);
Display.setBrightness(100);
}
void loop()
{
DateTime now = rtc.now();
Display.setNumber(now.twelveHour() * 100 + now.minute());
Display.refreshDisplay();
}
-
Good but now my clock is accurate same as my phone clock not even a second drift in last 18 hour'sjagpal singh– jagpal singh01/10/2025 11:26:43Commented Jan 10 at 11:26
Explore related questions
See similar questions with these tags.
micros()
instead ofmillis()
for a more fine-grained calibration, but do not expect too much accuracy from the Nano's ceramic resonator.