Is there anyone used "millis()" function more than a few days ?

Hi everyone,

I just wondered. The "millis()" function starts the timing after Arduino started. Its maximum value is directly related with the used variable, unsigned long. When I calculate it and convert it in terms of seconds, I get the operating time is up to 50 days ,approximately.

Again, I just wondered,

  • Did anyone use this function more than a few days ?

  • If it is like that, did the reading value correspond to the real one ?
    (In fact, I am not expecting that it will give the real value, since there can be some noise due to the environment. However, I expect some very acceptable value.)

I will be glad to know your experiments about that.

Thanks in advanced.
Best Regards..

Just follow this write up and you will see there is no problem.

.

I built a display that used multiple Arduinos all running the same code and all using micros(); for timing. Within a minute you could see them getting out of sync. Actually, they went out of sync so fast I had to redo the code so one processor was "master" and ran the others as slaves to it.

So my vote is the timers are not all that accurate.

-jim lee

The timer is inaccurate for sure - I built analog RTC circuits that use millis() to keep track of the time. As long as the temperature is kept within a few degrees or so, they are consistent. My clocks use crystals with well matched capacitors, right next to the tiny85 microprocessor. I calibrate them against the WWVB clock by scaling the count to a measured value.

My oldest clock, has been running since September 2015, and it is accurate to no more than 15 minutes over 14 months. A companion DS3231 which I set at the same time, is less than a minute and a half off.

Two different issues have been mentioned here.

If you use subtraction with millis() - for example if (millis() - previousMillis >= interval) it will work properly for ever, even when the counter overflows at the end of about 49 days

The Arduino 16MHz oscillator does not run at precisely 16MHz so timekeeping with millis() will not stay in sync with a proper clock. If that matters you need to use a Real Time Clock (RTC) module.

...R

jimLee:
I built a display that used multiple Arduinos all running the same code and all using micros(); for timing. Within a minute you could see them getting out of sync. Actually, they went out of sync so fast I had to redo the code so one processor was "master" and ran the others as slaves to it.

So my vote is the timers are not all that accurate.

-jim lee

Timers are as accurate as the source that feeds them :wink:

I'm writing this sketch to measure and provide a correction value for millis, it's still a work in progress but works as is. Connect a button between pin 4 and ground, start Serial monitor, use an accurate clock (I prefer a sweep second hand), when the hand is dead on 12 press and release the button quickly and just let it run for a couple of hours, if the Arduino is off by more than 3 seconds, wait until the second hand is dead on 12 and press & release the button, it will tell you how much the Arduino is off and add or subtract that from the "milsPerMinute" variable, let it run for a few more hours and after it gets off by at least 2 or 3 seconds, do it again. I had one Chinese Nano that was off by a second every 14 minutes and was able to tune it to 1 second off in 54 hours, the final "milsPerMinute" was 60081. So a milli is really 998.652 micros.
Feel free to crack, hack and repack. :slight_smile:

extern volatile unsigned long timer0_millis; // temporary kludge
unsigned long startTime;
long milsPerMin = 60000, currentMils, diff;
unsigned int minutes, currentMin;
const byte btnPin = 4, ledPin = 9;
bool btn = true, oldBtn = true;

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin,OUTPUT);
  pinMode(btnPin,INPUT_PULLUP);
  delay(2);
  while(bitRead(PIND,btnPin));  // wait for button press
  while(! bitRead(PIND,btnPin)) // & release
  {
    timer0_millis = 0;          //reset millis()
    startTime = millis();
  }
}

void loop()
{
  // minute flash *************************************************
  if(millis() - startTime >= milsPerMin)
  {
    startTime += milsPerMin;                   
    minutes++;
    Serial.print(minutes); Serial.print("\t");
    Serial.println(millis());
    digitalWrite(ledPin,HIGH);
  }  
  if(millis() - startTime > 30)
    digitalWrite(ledPin,LOW);
  //***************************************************************
  
  // check for button press ***************************************
  btn = bitRead(PIND,btnPin);
  if(!btn && btn != oldBtn)
    update();
  oldBtn = btn;
}
  //***************************************************************
void update()
{
  currentMils = millis(); // save current millis()
  currentMin = minutes;   //   "     "    minutes
  if(currentMils % milsPerMin > milsPerMin / 2) // if past 1/2 minute, increment current minute
    currentMin += 1;
  diff = (currentMils - currentMin * milsPerMin) / currentMin; // difference in what it is & should be
  milsPerMin += diff; // apply correction to milsPerMinute
  minutes = 0;
  timer0_millis = 0; // reset millis, will eliminate this in future :-)
  startTime = millis();
  Serial.print(currentMin); Serial.print("\t");
  Serial.print(currentMils); Serial.print("\t");
  Serial.print(milsPerMin); Serial.print("\t");
  Serial.print(diff); Serial.println();
  delay(500); 
  oldBtn = btn;
  
}

Of course the resonator (oscillator) is still subject to drift with VCC and temperature variation.

That's similar to the method I use, but the Arduino ceramic resonnator has about ten times the drift of a quartz crystal. Also temperature stability is very important. Some RTC devices use a little heater.

LarryD:
Just follow this write up and you will see there is no problem.

Gammon Forum : Electronics : Microprocessors : millis() overflow ... a bad thing?

.

That's really well-experssed in terms of register-level, too. Thanks a lot for the knowledge..

jimLee:
I built a display that used multiple Arduinos all running the same code and all using micros(); for timing. Within a minute you could see them getting out of sync. Actually, they went out of sync so fast I had to redo the code so one processor was "master" and ran the others as slaves to it.

So my vote is the timers are not all that accurate.

-jim lee

The "micros()" command might be a bit worse than the "millis()" command, since it counts as the level ten to the power six. It's my opinion, of course :slight_smile:

sterretje:
Timers are as accurate as the source that feeds them :wink:

Also that depends on the thing what @sterretje :slight_smile: However, it's a better way to measure it. Thank you for the experiment..

ChrisTenone:
The timer is inaccurate for sure - I built analog RTC circuits that use millis() to keep track of the time. As long as the temperature is kept within a few degrees or so, they are consistent. My clocks use crystals with well matched capacitors, right next to the tiny85 microprocessor. I calibrate them against the WWVB clock by scaling the count to a measured value.

My oldest clock, has been running since September 2015, and it is accurate to no more than 15 minutes over 14 months. A companion DS3231 which I set at the same time, is less than a minute and a half off.

Which Arduino board did you use ? (I mean, that error may be changed by the board capabilities in terms of hardware inside of it.) However, it's acceptable value for over 14 months :slight_smile:

Robin2:
Two different issues have been mentioned here.

If you use subtraction with millis() - for example if (millis() - previousMillis >= interval) it will work properly for ever, even when the counter overflows at the end of about 49 days

The Arduino 16MHz oscillator does not run at precisely 16MHz so timekeeping with millis() will not stay in sync with a proper clock. If that matters you need to use a Real Time Clock (RTC) module.

...R

Thanks a lot, I'll consider it..

outsider:
I'm writing this sketch to measure and provide a correction value for millis, it's still a work in progress but works as is. Connect a button between pin 4 and ground, start Serial monitor, use an accurate clock (I prefer a sweep second hand), when the hand is dead on 12 press and release the button quickly and just let it run for a couple of hours, if the Arduino is off by more than 3 seconds, wait until the second hand is dead on 12 and press & release the button, it will tell you how much the Arduino is off and add or subtract that from the "milsPerMinute" variable, let it run for a few more hours and after it gets off by at least 2 or 3 seconds, do it again. I had one Chinese Nano that was off by a second every 14 minutes and was able to tune it to 1 second off in 54 hours, the final "milsPerMinute" was 60081. So a milli is really 998.652 micros.
Feel free to crack, hack and repack. :slight_smile:

extern volatile unsigned long timer0_millis; // temporary kludge

unsigned long startTime;
long milsPerMin = 60000, currentMils, diff;
unsigned int minutes, currentMin;
const byte btnPin = 4, ledPin = 9;
bool btn = true, oldBtn = true;

void setup()
{
 Serial.begin(9600);
 pinMode(ledPin,OUTPUT);
 pinMode(btnPin,INPUT_PULLUP);
 delay(2);
 while(bitRead(PIND,btnPin));  // wait for button press
 while(! bitRead(PIND,btnPin)) // & release
 {
   timer0_millis = 0;          //reset millis()
   startTime = millis();
 }
}

void loop()
{
 // minute flash *************************************************
 if(millis() - startTime >= milsPerMin)
 {
   startTime += milsPerMin;                  
   minutes++;
   Serial.print(minutes); Serial.print("\t");
   Serial.println(millis());
   digitalWrite(ledPin,HIGH);
 }  
 if(millis() - startTime > 30)
   digitalWrite(ledPin,LOW);
 //***************************************************************
 
 // check for button press ***************************************
 btn = bitRead(PIND,btnPin);
 if(!btn && btn != oldBtn)
   update();
 oldBtn = btn;
}
 //***************************************************************
void update()
{
 currentMils = millis(); // save current millis()
 currentMin = minutes;   //   "     "    minutes
 if(currentMils % milsPerMin > milsPerMin / 2) // if past 1/2 minute, increment current minute
   currentMin += 1;
 diff = (currentMils - currentMin * milsPerMin) / currentMin; // difference in what it is & should be
 milsPerMin += diff; // apply correction to milsPerMinute
 minutes = 0;
 timer0_millis = 0; // reset millis, will eliminate this in future :slight_smile:
 startTime = millis();
 Serial.print(currentMin); Serial.print("\t");
 Serial.print(currentMils); Serial.print("\t");
 Serial.print(milsPerMin); Serial.print("\t");
 Serial.print(diff); Serial.println();
 delay(500);
 oldBtn = btn;
 
}



Of course the resonator (oscillator) is still subject to drift with VCC and temperature variation.

Thanks a lot, it is also a good example to understand the effect of it :slight_smile:

ChrisTenone:
The timer is inaccurate for sure...

In my experience Teensy AVR boards have the best accuracy. Not good enough to replace a long term real-time clock but good enough for everything else.

forty69:
Which Arduino board did you use ? (I mean, that error may be changed by the board capabilities in terms of hardware inside of it.) However, it's acceptable value for over 14 months :slight_smile:

I didn't use an Arduino board, I used a tiny85 with a 16 MHz crystal.