How can I delete time gap in using 'millis()'?

In making LED program which operates instant period, I had noticed time gap between real time and millis().

For example, there is a LED program which changes its color every 2 minutes. Suppose time program started operating at 00:00, first color changing occurred at 02:00. But as the number of color changing increases, a period also increased such as 04:01, 06:02, 08:04 …

So, I write a simple code to test the time gap.
Here is a code:

extern volatile unsigned long timer0_millis;
#include "SoftReset.h"
#include <Adafruit_NeoPixel.h>
#define PIN 5
#define NUM_LEDS 24
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
    Serial.begin(9600);
    strip.begin();
    strip.show();
}
void loop()
{
  char reset = Serial.read();

  if(reset == 'r')
    soft_restart();
  
  if(millis() % 1000 == 0) // blinking every 1 seconds
    ledblink();
}
void ledblink() // blinking LEDs
{
  for(int i = 0; i < NUM_LEDS; i++)
    strip.setPixelColor(i, 255, 255, 255);
  strip.show();
  delay(10);
  for(int i = 0; i < NUM_LEDS; i++)
    strip.setPixelColor(i, 0, 0, 0);
  strip.show();
}

In this code, I used soft_restart() to compare real time and millis(). So, I pressed ‘r’ key in serial monitor and stopwatch starting button simultaneously.

As expected, stopwatch time and blinking time are not same as the time going on. So, I searched solution myself, I found time counting of millis() with arduino. - actually not 1 ms but 1.024 ms. So, I changed if(millis() % 1000 == 0) to if(millis() % 976 == 0) (976 is 1/1.024). But LED blinking time is shorter than real time (time interval rather increased second case(976) than first case(1000))

Nothing comes up to my mind to solve this problem without using RTC. How can I delete time gap ‘by changing code?’

I appreciate your reply and solutions, and please understand my poor english :slight_smile:

The Arduino's clock does not operate at exactly 16MHz and the error is likely to be different on different Arduinos. They are not intended to be accurate time keepers.

The correct solution is to use a Real Time Clock module.

You may be able to get a better approximation to real time if you use millis() to count up to one minute and adjust the exact number with experiment. Something like this

unsigned long mllisPerMinute = 60000;

if (millis() - minuteStartTime >= millisPerMinute) {
    minuteCount ++;
    minuteStateTime += millisPerMinute;
}

...R

what happens if you read millis() and the value isn't an exact multiple of 1000 (e.g. 21999, 41001)?

you claim millis() is off by 24 sec every 1000, 2.4%. A crystal is often more accurate the 50 parts per million.

The accuracy of a crystal is also varying with temperature because higher temperature equals higher clock. So in order to calibrate the accuracy of a crystal you also need to mind the temperature.

So if my ideal time of millis would be 200.

My thingies execute and their execution time takes 1 millis.

For a total time of 201 millis.

So, I figure if I want 200 millis but the time to execute the code is 1 millis, I'll need to run my code every 199 millis or ActualExecutionTime - 200millisDesiredExecutionTime = DifferenceOfExecutionTime so I do 200millisDesiredExecutionTime - DifferenceOfExecutionTime = 200millisDesiredExecutionTime.

Idahowalker:
So if my ideal time of millis would be 200.

If you need a cycle of 200 millis, you could measure how many millis it takes for the work to complete and then extract that from the 200 millis.

Danois90:
If you need a cycle of 200 millis, you could measure how many millis it takes for the work to complete and then extract that from the 200 millis.

A much simpler way, without any need for measurement is like this

if (millis() - startTime >= interval) {
   startTime += interval;
   
  // do other stuff
}

that will be slightly more consistent over time than this

if (millis() - startTime >= interval) {
   startTime = millis();
   
  // do other stuff
}

because it allows for situations where the IF is not tested exactly on the desired millisec value

However I don't think this has anything to do with the OP's problem.

...R