need help adjusting millis () and LastTick intervals to achieve better accuracy

The arduino clock is drifting and gaining a couple of minutes per day. I have done calculations and decided that changing the "1000" to "1001" in the following code might help with tuning. I don't have a Real Time Clock.

if (millis() - lastTick >= 1000) {
	lastTick = millis();
	second++;

Can I change it to:

if (millis() - lastTick >= 1001) {
	lastTick = millis();
	second++;

or is 1000 the maximum number I can use for millis? Please advise. Thank you.

Millis is an unsigned long. The maximum number it can reach is 4,294,967,295 after which it overflows.

It is of course possible to adjust a bit this way; but the overall accuracy depends in the end on the stability of the crystal. You could get a much more precise adjustment if you do it on a minute level.

For any real accuracy get an RTC. They're small and cheap.

millis() returns an unsigned long, which is almost 50 days, but you're not using your 1000 or 1001 "for millis" (although I'm not sure what that means tbh.) It's checking to see if 1000ms has elapsed since lastTick, and all you want to do is make that gap a tiny bit longer at 1001ms. So yeah, why not?

(Your subject line makes no sense: you're not adjusting either of the functions. (Not that lastTick is a function: that would be lastTick().) You're calibrating an interval after which something will happen.)

kenwood120s:
millis() returns an unsigned long, which is almost 50 days, but you're not using your 1000 or 1001 "for millis" (although I'm not sure what that means tbh.) It's checking to see if 1000ms has elapsed since lastTick, and all you want to do is make that gap a tiny bit longer at 1001ms. So yeah, why not?

(Your subject line makes no sense: you're not adjusting either of the functions. (Not that lastTick is a function: that would be lastTick().) You're calibrating an interval after which something will happen.)

I am sorry. I am not a native speaker of english language. and I am new to arduino. but thanks for info.

The terminology comment was not as much about your English (which is fine) but about the programming language terms. Using the correct names goes a long way in getting a message across.

wvmarle:
Millis is an unsigned long. The maximum number it can reach is 4,294,967,295 after which it overflows.

It is of course possible to adjust a bit this way; but the overall accuracy depends in the end on the stability of the crystal. You could get a much more precise adjustment if you do it on a minute level.

For any real accuracy get an RTC. They're small and cheap.

Thanks a lot for the info. I appreciate. I am very new to arduino and I know very little about it. I have made a binary clock using the following code which I found online. I don't know how to edit it. Would you be so kind to help me change it so that the calculation goes on "minute level" like you just stated? Thanks. Here is my code:

/*
An open-source binary clock for Arduino. 
Based on the code from by Rob Faludi (http://www.faludi.com)
Code under (cc) by Daniel Spillere Andrade, www.danielandrade.net
http://creativecommons.org/license/cc-gpl
*/

int second=0, minute=0, hour=0; //start the time on 00:00:00
int munit,hunit,valm=0,valh=0,ledstats,i;

void setup() { //set outputs and inputs
pinMode(1, OUTPUT);pinMode(2, OUTPUT);pinMode(3, OUTPUT);pinMode(4, OUTPUT);pinMode(5, OUTPUT);
pinMode(6, OUTPUT);pinMode(7, OUTPUT);pinMode(8, OUTPUT);pinMode(9, OUTPUT);pinMode(10, OUTPUT);
pinMode(11, OUTPUT);pinMode(12, OUTPUT);pinMode(13, OUTPUT);

pinMode(0, INPUT);
}

void loop() {

static unsigned long lastTick = 0; // set up a local variable to hold the last time we moved forward one second
// (static variables are initialized once and keep their values between function calls)
// move forward one second every 1000 milliseconds

if (millis() - lastTick >= 1000) {
 lastTick = millis();
 second++;

}

// move forward one minute every 60 seconds
 if (second >= 60) {
 minute++;
 second = 0; // reset seconds to zero
}

// move forward one hour every 60 minutes
if (minute >=60) {
 hour++;
 minute = 0; // reset minutes to zero
}

if (hour >=24) {
 hour=0;
 minute = 0; // reset minutes to zero
}

 munit = minute%10; //sets the variable munit and hunit for the unit digits
 hunit = hour%10;


 ledstats = digitalRead(0);  // read input value, for setting leds off, but keeping count
 if (ledstats == LOW) {
    
 for(i=1;i<=13;i++){
 digitalWrite(i, LOW);}
  
 } else  {

 //minutes units
 if(munit == 1 || munit == 3 || munit == 5 || munit == 7 || munit == 9) {  digitalWrite(1, HIGH);} else {  digitalWrite(1,LOW);}
 if(munit == 2 || munit == 3 || munit == 6 || munit == 7) {digitalWrite(2, HIGH);} else {digitalWrite(2,LOW);}
 if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {digitalWrite(3, HIGH);} else {digitalWrite(3,LOW);}
 if(munit == 8 || munit == 9) {digitalWrite(4, HIGH);} else {digitalWrite(4,LOW);}

 //minutes 
 if((minute >= 10 && minute < 20) || (minute >= 30 && minute < 40) || (minute >= 50 && minute < 60))  {digitalWrite(5, HIGH);} else {digitalWrite(5,LOW);}
 if(minute >= 20 && minute < 40)  {digitalWrite(6, HIGH);} else {digitalWrite(6,LOW);}
 if(minute >= 40 && minute < 60) {digitalWrite(7, HIGH);} else {digitalWrite(7,LOW);}

 //hour units
 if(hunit == 1 || hunit == 3 || hunit == 5 || hunit == 7 || hunit == 9) {digitalWrite(8, HIGH);} else {digitalWrite(8,LOW);}
 if(hunit == 2 || hunit == 3 || hunit == 6 || hunit == 7) {digitalWrite(9, HIGH);} else {digitalWrite(9,LOW);}
 if(hunit == 4 || hunit == 5 || hunit == 6 || hunit == 7) {digitalWrite(10, HIGH);} else {digitalWrite(10,LOW);}
 if(hunit == 8 || hunit == 9) {digitalWrite(11, HIGH);} else {digitalWrite(11,LOW);}

 //hour
 if(hour >= 10 && hour < 20)  {digitalWrite(12, HIGH);} else {digitalWrite(12,LOW);}
 if(hour >= 20 && hour < 24)  {digitalWrite(13, HIGH);} else {digitalWrite(13,LOW);}

 }

 valm = analogRead(0);    // add one minute when pressed
 if(valm<800) {
 minute++;
 second=0;
 delay(250);
 }
  
 valh = analogRead(5);    // add one hour when pressed
 if(valh<800) {
 hour++;
 second=0;
 delay(250);
 }


}

kenwood120s:
millis() returns an unsigned long, which is almost 50 days, but you're not using your 1000 or 1001 "for millis" (although I'm not sure what that means tbh.) It's checking to see if 1000ms has elapsed since lastTick, and all you want to do is make that gap a tiny bit longer at 1001ms. So yeah, why not?

(Your subject line makes no sense: you're not adjusting either of the functions. (Not that lastTick is a function: that would be lastTick().) You're calibrating an interval after which something will happen.)

sorry. forgot to ask. How about micros ()? Does that return an unsigned long too? or does it overflow after some 50 days too? Can you please help me to calibrate the values on a minute level perhaps? Thanks.

Micros has 1000 for every 1 millis so of course overflows in 1/1000th of the time (just do the maths - 4,294,967,295 ms is about 50 days, 4,294,967,295 us is about 18 mins iirc).

To adjust minutes, adjust your counting every 60,000 milliseconds instead of every 1,000.

wvmarle:
Micros has 1000 for every 1 millis so of course overflows in 1/1000th of the time (just do the maths - 4,294,967,295 ms is about 50 days, 4,294,967,295 us is about 18 mins iirc).

To adjust minutes, adjust your counting every 60,000 milliseconds instead of every 1,000.

Thank you very much sir. But I really don't know how to edit that. Would you be willing to help me with that bit of the code that would do that? Thank you so much. So, do you think that the following would work?

if (millis() - lastTick >= 60000) {
	lastTick = millis();
	minute++;

Then perhaps, if I am counting in minutes, do I still need the following bit of the code?

// move forward one minute every 60 seconds
 if (second >= 60) {
 minute++;
 second = 0; // reset seconds to zero

Thanks. This is the full code, as it is now:

/*
An open-source binary clock for Arduino. 
Based on the code from by Rob Faludi (http://www.faludi.com)
Code under (cc) by Daniel Spillere Andrade, www.danielandrade.net
http://creativecommons.org/license/cc-gpl
*/

int second=0, minute=0, hour=0; //start the time on 00:00:00
int munit,hunit,valm=0,valh=0,ledstats,i;

void setup() { //set outputs and inputs
pinMode(1, OUTPUT);pinMode(2, OUTPUT);pinMode(3, OUTPUT);pinMode(4, OUTPUT);pinMode(5, OUTPUT);
pinMode(6, OUTPUT);pinMode(7, OUTPUT);pinMode(8, OUTPUT);pinMode(9, OUTPUT);pinMode(10, OUTPUT);
pinMode(11, OUTPUT);pinMode(12, OUTPUT);pinMode(13, OUTPUT);

pinMode(0, INPUT);
}

void loop() {

static unsigned long lastTick = 0; // set up a local variable to hold the last time we moved forward one second
// (static variables are initialized once and keep their values between function calls)
// move forward one second every 1000 milliseconds

if (millis() - lastTick >= 1000) {
	lastTick = millis();
	second++;

}

// move forward one minute every 60 seconds
	if (second >= 60) {
	minute++;
	second = 0; // reset seconds to zero
}

// move forward one hour every 60 minutes
if (minute >=60) {
	hour++;
	minute = 0; // reset minutes to zero
}

if (hour >=24) {
	hour=0;
	minute = 0; // reset minutes to zero
}

	munit = minute%10; //sets the variable munit and hunit for the unit digits
	hunit = hour%10;


	ledstats = digitalRead(0);  // read input value, for setting leds off, but keeping count
	if (ledstats == LOW) {
    
	for(i=1;i<=13;i++){
	digitalWrite(i, LOW);}
  
	} else  {

	//minutes units
	if(munit == 1 || munit == 3 || munit == 5 || munit == 7 || munit == 9) {  digitalWrite(1, HIGH);} else {  digitalWrite(1,LOW);}
	if(munit == 2 || munit == 3 || munit == 6 || munit == 7) {digitalWrite(2, HIGH);} else {digitalWrite(2,LOW);}
	if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {digitalWrite(3, HIGH);} else {digitalWrite(3,LOW);}
	if(munit == 8 || munit == 9) {digitalWrite(4, HIGH);} else {digitalWrite(4,LOW);}

	//minutes 
	if((minute >= 10 && minute < 20) || (minute >= 30 && minute < 40) || (minute >= 50 && minute < 60))  {digitalWrite(5, HIGH);} else {digitalWrite(5,LOW);}
	if(minute >= 20 && minute < 40)  {digitalWrite(6, HIGH);} else {digitalWrite(6,LOW);}
	if(minute >= 40 && minute < 60) {digitalWrite(7, HIGH);} else {digitalWrite(7,LOW);}

	//hour units
	if(hunit == 1 || hunit == 3 || hunit == 5 || hunit == 7 || hunit == 9) {digitalWrite(8, HIGH);} else {digitalWrite(8,LOW);}
	if(hunit == 2 || hunit == 3 || hunit == 6 || hunit == 7) {digitalWrite(9, HIGH);} else {digitalWrite(9,LOW);}
	if(hunit == 4 || hunit == 5 || hunit == 6 || hunit == 7) {digitalWrite(10, HIGH);} else {digitalWrite(10,LOW);}
	if(hunit == 8 || hunit == 9) {digitalWrite(11, HIGH);} else {digitalWrite(11,LOW);}

	//hour
	if(hour >= 10 && hour < 20)  {digitalWrite(12, HIGH);} else {digitalWrite(12,LOW);}
	if(hour >= 20 && hour < 24)  {digitalWrite(13, HIGH);} else {digitalWrite(13,LOW);}

	}

	valm = analogRead(0);    // add one minute when pressed
	 if(valm<800) {
	 minute++;
	 second=0;
	 delay(250);
	}
  
	valh = analogRead(5);    // add one hour when pressed
	 if(valh<800) {
	 hour++;
	 second=0;
	 delay(250);
	}


}

Add one millisecond to your interval and decide if that's good enough. Add two if its still too fast. If millisecond resolution is not accurate enough, time the one second with microseconds. For example

if (micros() - lastTick >= 1001500) {
	lastTick = micros();
	second++;

The seconds adjustment will roll through to minutes and hours. If unhappy with the performance, get an RTC as suggested.

You could get a much more precise adjustment if you do it on a minute level.

Please explain.

1 minute = 60000 ms so adjusting 1 ms = 1/60000th resolution.
1 second = 1000 ms so adjusting 1 ms = 1/1000th resolution.
That's why you can do it much more precise (and maybe be more accurate in the end - depending mostly on the stability of the 16 MHz crystal).

Try this code

unsigned long currentMicros;
unsigned long previousMicros;
unsigned long elapsedTime;

byte hundredths;
byte tenths;
byte secondsOnes;
byte oldSecondsOnes;
byte secondsTens;
byte minutesOnes = 6;
byte minutesTens = 5;
byte hoursOnes = 0;
byte hoursTens = 1;

void setup(){

  Serial.begin(115200); // make serial monitor match
  currentMicros = micros();
  previousMicros = currentMicros;
  Serial.println ("Setup Done");
}

void loop(){

  currentMicros = micros();

  // how long's it been?
  elapsedTime = currentMicros - previousMicros;
  //Serial.print ("Elapsed: ");  
  //Serial.println (elapsedTime);
  if ( elapsedTime >=10000UL){  // 0.01 second passed? Update the timers
    elapsedTime = 0;
    previousMicros  = previousMicros + 10000UL;
    hundredths = hundredths+1;
    if (hundredths == 10){
      hundredths = 0;
      tenths = tenths +1;
      if (tenths == 10){
        tenths = 0;
        secondsOnes = secondsOnes + 1;
        if (secondsOnes == 10){
          secondsOnes = 0;
          secondsTens = secondsTens +1;
          if (secondsTens == 6){ 
            secondsTens = 0;
            minutesOnes =  minutesOnes + 1;
            if (minutesOnes == 10){
              minutesOnes = 0;
              minutesTens = minutesTens +1;
              if (minutesTens == 6){
                minutesTens = 0;
                hoursOnes = hoursOnes +1;
                if (hoursOnes == 10){
                  hoursOnes = 0;
                  hoursTens = hoursTens =1;
                  if (hoursOnes == 4 && hoursTens ==2){
                    hoursOnes = 0;
                    hoursTens = 0;
                  }
                }
              } // minutesTens rollover check
            } // minutesOnes rollover check
          } // secondsTens rollover check
        } // secondsOnes rollover check
      } // tenths rollover check
    } // hundredths rollover check
  } // hundredths passing check



  if (oldSecondsOnes != secondsOnes){  // show the elapsed time
    oldSecondsOnes = secondsOnes;
    Serial.print ("Time: ");
    Serial.print (hoursTens);
    Serial.print(hoursOnes);
    Serial.print(":");
    Serial.print(minutesTens);
    Serial.print(minutesOnes);
    Serial.print(":");
    Serial.print(secondsTens);
    Serial.println(secondsOnes);

/*
 if ( hoursTens == 0 && hoursOnes == 6 ** minutesTens == 0 && minutesOnes == 0 && secondsTens == 0 && secondsOnes == 0){
// alarm time!
}
*/

  } // end one second check
} // end loop

cattledog:
Add one millisecond to your interval and decide if that's good enough. Add two if its still too fast. If millisecond resolution is not accurate enough, time the one second with microseconds. For example

if (micros() - lastTick >= 1001500) {
lastTick = micros();
second++;



The seconds adjustment will roll through to minutes and hours. If unhappy with the performance, get an RTC as suggested.

Please explain.

Thanks. I am going to try counting in micros() but I hear it overflows in just 71 minutes. Is that going to cause problems with my binary clock and my code setup? Thanks. Please see my full code above. Thanks

wvmarle:
1 minute = 60000 ms so adjusting 1 ms = 1/60000th resolution.
1 second = 1000 ms so adjusting 1 ms = 1/1000th resolution.
That's why you can do it much more precise (and maybe be more accurate in the end - depending mostly on the stability of the 16 MHz crystal).

Thanks. I understand your point but I still don't know how to incorporate this into my code. I tried something but my seconds woudn't reset to 0 when setting hours/minutes. I don't know how to write the code in order to properly count in minutes. Please see my code above.

billhowl:
Try this code

unsigned long currentMicros;

unsigned long previousMicros;
unsigned long elapsedTime;

byte hundredths;
byte tenths;
byte secondsOnes;
byte oldSecondsOnes;
byte secondsTens;
byte minutesOnes = 6;
byte minutesTens = 5;
byte hoursOnes = 0;
byte hoursTens = 1;

void setup(){

Serial.begin(115200); // make serial monitor match
 currentMicros = micros();
 previousMicros = currentMicros;
 Serial.println ("Setup Done");
}

void loop(){

currentMicros = micros();

// how long's it been?
 elapsedTime = currentMicros - previousMicros;
 //Serial.print ("Elapsed: ");  
 //Serial.println (elapsedTime);
 if ( elapsedTime >=10000UL){  // 0.01 second passed? Update the timers
   elapsedTime = 0;
   previousMicros  = previousMicros + 10000UL;
   hundredths = hundredths+1;
   if (hundredths == 10){
     hundredths = 0;
     tenths = tenths +1;
     if (tenths == 10){
       tenths = 0;
       secondsOnes = secondsOnes + 1;
       if (secondsOnes == 10){
         secondsOnes = 0;
         secondsTens = secondsTens +1;
         if (secondsTens == 6){
           secondsTens = 0;
           minutesOnes =  minutesOnes + 1;
           if (minutesOnes == 10){
             minutesOnes = 0;
             minutesTens = minutesTens +1;
             if (minutesTens == 6){
               minutesTens = 0;
               hoursOnes = hoursOnes +1;
               if (hoursOnes == 10){
                 hoursOnes = 0;
                 hoursTens = hoursTens =1;
                 if (hoursOnes == 4 && hoursTens ==2){
                   hoursOnes = 0;
                   hoursTens = 0;
                 }
               }
             } // minutesTens rollover check
           } // minutesOnes rollover check
         } // secondsTens rollover check
       } // secondsOnes rollover check
     } // tenths rollover check
   } // hundredths rollover check
 } // hundredths passing check

if (oldSecondsOnes != secondsOnes){  // show the elapsed time
   oldSecondsOnes = secondsOnes;
   Serial.print ("Time: ");
   Serial.print (hoursTens);
   Serial.print(hoursOnes);
   Serial.print(":");
   Serial.print(minutesTens);
   Serial.print(minutesOnes);
   Serial.print(":");
   Serial.print(secondsTens);
   Serial.println(secondsOnes);

/*
if ( hoursTens == 0 && hoursOnes == 6 ** minutesTens == 0 && minutesOnes == 0 && secondsTens == 0 && secondsOnes == 0){
// alarm time!
}
*/

} // end one second check
} // end loop

Thanks. But I am not sure how and where in my code to use this. Please help. I have included my full code in my main post above

arduinoware:
Thanks. I am going to try counting in micros() but I hear it overflows in just 71 minutes. Is that going to cause problems with my binary clock and my code setup? Thanks. Please see my full code above. Thanks

An overflow is never an issue if you follow the proper data type rules, and never try to time an interval that is longer than the overflow period.

Also, please stop begging for someone to tell you "how to incorporate" this or that into your code. That can't be done without essentially doing your job. Take some time to study the answers that have been given, which you already claimed to at least partially understand. Take the time to do the work that all programmers do.

Thanks. I am going to try counting in micros() but I hear it overflows in just 71 minutes. Is that going to cause problems with my binary clock and my code setup?

The use of subtraction and unsigned long variables for time makes the code unaffected by rollover. Seehttp://www.gammon.com.au/millis

You are doing it correctly with

static unsigned long lastTick = 0;
if (micros() - lastTick >= 1001500) {
 lastTick = micros();
 second++;

Note...

lastTick = micros();

...drift.

lastTick += 1001500;

...no drift.

Thanks. But did you mean?

lastTick += 1001500;

Or did you mean

lastTick = 1001500;;

With or wiithout the "+" sign in the equation? Cheers.

With or wiithout the "+" sign in the equation? Cheers.

Take the time to do the work that all programmers do.

Did you look in the reference section to understand what += means?
https://www.arduino.cc/en/Reference/IncrementCompound

Did you consider when this statement is true if lastTick is = to a fixed value and not += some increment?

if (micros() - lastTick >= 1001500) {

Can you answer your own question?