inaccurate millis() or MCU?

I just ran into a strange occurrence. On Arduino clone, GBoard Pro (which uses a MEGA2560 chip), as well as on a Arduino Nano it seems that millis() isn't 100% accurate.

To demonstrate, I modified the "BlinkWithoutDelay" tutorial a bit, to display the "millis()" value when the LED state changes.

Code, with interval set to 1000:

/* Blink without Delay

 Turns on and off a light emitting diode(LED) connected to a digital  
 pin, without using the delay() function.  This means that other code
 can run at the same time without being interrupted by the LED code.

 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13, so no hardware is needed for this example.


 created 2005
 by David A. Mellis
 modified 8 Feb 2010
 by Paul Stoffregen

 This example code is in the public domain.


 http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
 */

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);   
Serial.begin(115200);  
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    Serial.println(millis());
  }
}

Output:

/* Blink without Delay

Turns on and off a light emitting diode(LED) connected to a digital pin, without using the delay() function. This means that other code can run at the same time without being interrupted by the LED code.

The circuit: * LED attached from pin 13 to ground. * Note: on most Arduinos, there is already an LED on the board that's attached to pin 13, so no hardware is needed for this example.

created 2005 by David A. Mellis modified 8 Feb 2010 by Paul Stoffregen

This example code is in the public domain.

http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay */

// constants won't change. Used here to // set pin numbers: const int ledPin = 13; // the number of the LED pin

// Variables will change: int ledState = LOW; // ledState used to set the LED long previousMillis = 0; // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long interval = 1000; // interval at which to blink (milliseconds)

void setup() { // set the digital pin as output: pinMode(ledPin, OUTPUT); Serial.begin(115200); }

void loop() { // here is where you'd put code that needs to be running all the time.

// check to see if it's time to blink the LED; that is, if the // difference between the current time and last time you blinked // the LED is bigger than the interval at which you want to // blink the LED. unsigned long currentMillis = millis();

if(currentMillis - previousMillis > interval) { // save the last time you blinked the LED previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW;

// set the LED with the ledState of the variable: digitalWrite(ledPin, ledState); Serial.println(millis()); } }

Code, with interval set to 999:

/* Blink without Delay

 Turns on and off a light emitting diode(LED) connected to a digital  
 pin, without using the delay() function.  This means that other code
 can run at the same time without being interrupted by the LED code.

 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13, so no hardware is needed for this example.


 created 2005
 by David A. Mellis
 modified 8 Feb 2010
 by Paul Stoffregen

 This example code is in the public domain.


 http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
 */

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 999;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);   
Serial.begin(115200);  
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    Serial.println(millis());
  }
}

Output:

1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 12000 13000 14000 15000 16000

So, the question is:

Is millis() inaccurate, or could it be the MCU?

Can anyone test this on another Arduino?

The problem is this:

if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis; << this allows the time capture to drift

change to this:
if(currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = previousMillis + interval; << this keep the time interval updates constant.

Try with micros() also, think you will see better results.

CrossRoads:
The problem is this:

if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis; << this allows the time capture to drift

Thanx!

Does the code really run that long, that it could “accumelate” another millisecond by the time “previousMillis” is set equal to “currentMillis”, only another line of code further?

If you set the interval to 999 then it has to exceed 999 to trigger the change. If you want 999 then in the comparison say >= not just >

Does the code really run that long, that it could "accumelate" another millisecond by the time "previousMillis" is set equal to "currentMillis", only another line of code further?

It builds up slowly, it is seen as seconds or more per day of time drift.

I just ran into a strange occurrence. On Arduino clone, GBoard Pro (which uses a MEGA2560 chip), as well as on a Arduino Nano it seems that millis() isn't 100% accurate.

Correct.

No, not "Correct". Comes down to better coding. This simple sketch counts up and tracks time - I've had it running against the US official time at http://www.time.gov/widget/ and track the time quite well

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

byte hundredths;
byte tenths;
byte secondsOnes;
byte oldsecondsOnes;
byte secondsTens;
byte minutesOnes;
byte minutesTens;


void setup(){

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

void loop(){

currentMicros = micros();

// how long's it been?
elapsedTime = currentMicros - previousMicros;
if ( elapsedTime >=10000UL){  // 0.01 second passed? Update the timers
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;
                        } // 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(minutesTens);
Serial.print(":");

Serial.print(minutesOnes);
Serial.print(":");

Serial.print(secondsTens);
Serial.print(":");

Serial.print(secondsOnes);
Serial.print(":");

} // end one second check

} // end loop

You can try changing to millis() and see how it does. With micros() it does quite well.

Just one thing you might not have thought of.

There is absolutely no way you can test the accuracy of millis() by comparing it to millis() results. More generally, you can't test something by comparing it to itself.

All your test showed was faulty coding, it had no chance, even in principle, of finding a fault with millis().

If the statement in the original post implies an expectation of 100% accuracy, then you are bound to be disappointed as this will never be the case. Coding issues aside, the accuracy of millis() is dependent on the accuracy of the system clock, which will generally be somewhere between 5000ppm (if the clock is controlled by a ceramic resonator) and 20ppm (if the clock is controlled by a decent crystal). If the internal RC oscillator is used, the datasheet says to expect ±10% accuracy.

If the statement in the original post implies an expectation of 100% accuracy, then you are bound to be disappointed as this will never be the case. Coding issues aside, the accuracy of millis() is dependent on the accuracy of the system clock, which will generally be somewhere between 5000ppm (if the clock is controlled by a ceramic resonator) and 20ppm (if the clock is controlled by a decent crystal). If the internal RC oscillator is used, the datasheet says to expect ±10% accuracy.

Correct, as I said before.