millis() vs micros() in the long run

I have two sketches, one uses millis() and the other uses micros() to do the same thing - blink two LEDs at different rates (1Hz and 0.5Hz). The on-time for each is the same, the off-times are different.

*____*____*____*____*____
*_________*_________*____

My micros() version LEDs go out of sync.
It’s the same as the millis() [w/ all occurrences of millis() to micros(), appended three 0’s to constants.]

Is it because micros() is actually based on 2 usecs?
Or is my implementation of rollover-proofing, in fact, flawed?
[Or something else… my if’s are inexpedient?]

void loop ()
{
  if (((grnFlag == true) && ((micros()-grnHItimer)>=grnOntime)) || ((grnFlag == false) && ((micros()-grnLOtimer)>=grnOfftime)))
  {
    toggleGLED();
  }
  if (((redFlag == true) && ((micros()-redHItimer)>=redOntime)) || ((redFlag == false) && ((micros()-redLOtimer)>=redOfftime)))
  {
    toggleRLED();
  }  
}

millis() version

const uint8_t grnLED = 9;
uint32_t grnHItimer;
uint32_t grnLOtimer;
boolean grnFlag;
const uint32_t grnOntime = 100;
const uint32_t grnOfftime = 1900;
const uint8_t redLED = 10;
uint32_t redHItimer;
uint32_t redLOtimer;
boolean redFlag;
const uint32_t redOntime = 100;
const uint32_t redOfftime = 900;
void setup ()
{
  pinMode(grnLED,OUTPUT);
  grnHItimer = millis();
  grnLOtimer = millis();
  grnFlag = true;         // grnFlag AND redFlag must init TRUE
                          // to start On together
  pinMode(redLED,OUTPUT);
  redHItimer = millis();
  redLOtimer = millis();
  redFlag = true;         // grnFlag AND redFlag must init TRUE
                          // to start On together
  digitalWrite(redLED,HIGH);  // init HIGH or long off_pd at start
  digitalWrite(grnLED,HIGH);  // init HIGH or long off_pd at start
}
void loop ()
{
  if (((grnFlag == true) && ((millis()-grnHItimer)>=grnOntime)) || ((grnFlag == false) && ((millis()-grnLOtimer)>=grnOfftime)))
  {
    toggleGLED();
  }
  if (((redFlag == true) && ((millis()-redHItimer)>=redOntime)) || ((redFlag == false) && ((millis()-redLOtimer)>=redOfftime)))
  {
    toggleRLED();
  }  
}
void toggleGLED()
{
  if (grnFlag == false)
  {
    digitalWrite(grnLED, HIGH);
    grnFlag = true;
  }
  else
  {
    digitalWrite(grnLED, LOW);
    grnFlag = false;
  }
  grnHItimer = millis();
  grnLOtimer = millis();
}

micros() version

const uint8_t grnLED = 9;
uint32_t grnHItimer;
uint32_t grnLOtimer;
boolean grnFlag;
const uint32_t grnOntime = 100000;
const uint32_t grnOfftime = 1900000;
const uint8_t redLED = 10;
uint32_t redHItimer;
uint32_t redLOtimer;
boolean redFlag;
const uint32_t redOntime = 100000;
const uint32_t redOfftime = 900000;
void setup ()
{
  pinMode(grnLED,OUTPUT);
  grnHItimer = micros();
  grnLOtimer = micros();
  grnFlag = true;         // grnFlag AND redFlag must init TRUE
                          // to start On together
  pinMode(redLED,OUTPUT);
  redHItimer = micros();
  redLOtimer = micros();
  redFlag = true;         // grnFlag AND redFlag must init TRUE
                          // to start On together
  digitalWrite(redLED,HIGH);  // init HIGH or long off_pd at start
  digitalWrite(grnLED,HIGH);  // init HIGH or long off_pd at start
}
void loop ()
{
  if (((grnFlag == true) && ((micros()-grnHItimer)>=grnOntime)) || ((grnFlag == false) && ((micros()-grnLOtimer)>=grnOfftime)))
  {
    toggleGLED();
  }
  if (((redFlag == true) && ((micros()-redHItimer)>=redOntime)) || ((redFlag == false) && ((micros()-redLOtimer)>=redOfftime)))
  {
    toggleRLED();
  }  
}
void toggleGLED()
{
  if (grnFlag == false)
  {
    digitalWrite(grnLED, HIGH);
    grnFlag = true;
  }
  else
  {
    digitalWrite(grnLED, LOW);
    grnFlag = false;
  }
  grnHItimer = micros();
  grnLOtimer = micros();
}
void toggleRLED()
{
  if (redFlag == false)
  {
    digitalWrite(redLED, HIGH);
    redFlag = true;
  }
  else
  {
    digitalWrite(redLED, LOW);
    redFlag = false;
  }
  redHItimer = micros();
  redLOtimer = micros();
}

using_millis_asymtime_3.ino (1.67 KB)

using_micros_asymtime_1.ino (1.74 KB)

us vs. ms time elapse between if statements?

The micros() function has a rollover each 70 minutes and millis() every 50 days or so. So if your sketch runs less than 70 minutes, any problem you see has nothing to do with rollover effect.

Your implementation is just doing wrong with millis() and micros() and doesn't keep sync.

Two problems with your sketches:

Problem with millis():
Millis is not counting up in equal steps of 1. Though millis() will count up by 1 in most cases, it sometimes counts up by 2. So perhaps 1 second = 954 times counting up by 1 and 23 times counting up by 2. 1000 milliseconds = 9541ms+232ms= 1000ms. Or something like that.

Problem with micros():
Though micros() is counting up in equal steps of 4 microseconds, your sketch has a problem, because 4 microseconds is a very short time. So after using "(micros()-grnHItimer)>=grnOntime)" in a comparison, the variable setting "grnHItimer = micros();" may happen 4 or 8 microseconds later than the usage in the comparison. So your timing runs out of sync as times go by.

It's just bad programming practice what you see in your sketch.
No problems with the Arduino board or with the functions.

If the timing has to be more accurate than 1 millisecond, avoid using millis().
And if using micros(), avoid syncing errors by using better programming. I.e. read micros() at the top of the loop function and store in an unsigned long variable, then use that variable further down in the loop function every time you need the current micros() timing.

I wonder if this is the problem

grnHItimer = millis();

I think it would be better to use

grnHItimer += grnOnTime;

as it measures all times from the original base time and does away with cumulative errors.

There is a long discussion about this issue in several things at a time.

...R

grnHItimer = millis();
grnLOtimer = millis();

can give two different timestamps, if the clock just ticks in between

Robin2:
I wonder if this is the problem

grnHItimer = millis();

I think it would be better to use

grnHItimer += grnOnTime;

as it measures all times from the original base time and does away with cumulative errors.

There is a long discussion about this issue in several things at a time.

...R

Absolutely... If you want to keep accurate time you increment the variable with the interval,
not reset your timebase by sampling millis () or micros () again - this inevitably causes drift
as your program takes time to run.

Its the difference between using a wall-clock and a stop-watch - use the wall-clock for drift
free timestamps. Well drift-free relative to your crystal or resonator in fact.

Robin2:
I wonder if this is the problem

grnHItimer = millis();

I think it would be better to use

grnHItimer += grnOnTime;

as it measures all times from the original base time and does away with cumulative errors.

There is a long discussion about this issue in several things at a time.

Thanks for the reference. As a matter of fact, my stuff is based on the work of the examples of one the Repliers who concedes that they "have the creep" mentioned.
(Too bad it got sideways with hurt feelings.)

If you want your timebase to remain constant, and in-sync, DO NOT constantly reset your start times to the current millis() or micros() value. Instead, add the interval to the last value. Otherwise, every time you're "late" doing an update, you're shifting every future event further into the future than it should be.

i.e.:

unsigned long start time;
unsigned const INTERVAL = 5000;   // Update every 5 seconds

void setup()
{
    starttime = millis();
}

void loop()
{
    if (millis() - starttime > INTERVAL)
    {
        // Do whatever the update does...
        starttime += INTERVAL;    // DON'T Do starttime = millis()!!
    }
}

Regards,
Ray L.

You are calling millis() and micros() multiple times in your comparisons. Your code assumes that the values won't change and this is mostly true of millis() because there are 16,000 instruction cycles between changes. Not nearly as true for micros() since there are only 16 instruction cycles between changes.

I generally get the time at the start of loop() and use that time for the whole cycle:

unsigned long currentTime = micros();
if (currentTime - startTime > INTERVAL) ...