Simple software timer alternative to millis() [code example]

I just proposed a millis() or micros() based timer here. I think is a lot simpler than this one.

It does not suffer from the integer overflow occurring after 50 days (millis) or 7 hours (micros), so it can run indefinitely.

Usage
Just include the function chkTimer

In the sketch:

For each timer only one unsigned long must be declared, which contains the expiration time.

The function chkTimer does the rest. When true, the timer expired, which automatically sets a new expire time.

No problem to have hundreds of independent timers.

/* Function chkTimer resets and returns true when the timer expired. 
 * It stores the end time in the unsigned long variabele passed to &expireTime.  
 * When expired the value of loopTime is added to the expireTime so continuously 
 * polling this function checks true every loopTime.
 * The time scale can either be in milliseconds or in microseconds, depending 
 * on which line containing currentTime is commented out. 
 * All information the timer needs is stored in the variable, so to have more 
 * independent timers, just declare more variabeles.
 * Written by Femme Verbeek 30-4-2020 
 * Use for free
*/
boolean chkTimer (unsigned long &expireTime, unsigned long loopTime) {
unsigned long currentTime = millis();       // use this for milliseconds
//unsigned long currentTime = micros();       // use this for microseconds 
  if (currentTime >= expireTime)            //check the timer
  { if ( (bitRead(currentTime,31) == 1) && 
         (bitRead(expireTime,31) == 0) )    //check overflow
      return false;                         //integer overfow but not expired 
      else            {                     //expired         
        expireTime += loopTime;            //set new expireTime
        return true;  }                     //OK, chkTimer was triggered
  } else return false;                      //not expired
}

void setup() {
  Serial.begin(115200);
}

unsigned long mytimer1 = 0;       //start timing loop immediately
unsigned long mytimer2 = 5000;    //wait 5 seconds before starting timing loop   
unsigned long mytimer3 = 10000;   //wait 10 seconds before starting timing loop

void loop() {                     //timer intervals are Fibonacci numbers for maximum chaos
  if (chkTimer(mytimer1,377)) Serial.print("Hello ");
  if (chkTimer(mytimer2,610)) Serial.println("world ");
  if (chkTimer(mytimer3,1597)) Serial.println("and the universe "); 
}

You could also use a library such as FireTimer.h. It also handles timer overflow and can be installed via the Libraries Manager.

Power_Broker:
You could also use a library such as FireTimer.h. It also handles timer overflow and can be installed via the Libraries Manager.

@Power_Broker, the rule to use millis() and micros() is to not try to "handle" a rollover.

This:

// Handle overflow
if (currentTime < timeBench)
 timeDiff = (UL_MAX - timeBench) + currentTime;
else
 timeDiff = currentTime - timeBench;

will do the same thing as this:

timeDiff = currentTime - timeBench;

I can not make a Issue on your Github at the moment.

femmeverbeek:
I just proposed a millis() or micros() based timer here. I think is a lot simpler than this one.

@femmeverbeek, you are trying to "handle" a rollover, that is not how to use millis() or micros() as the others have already explained in the other thread.

I'm aware of that this timer-solution posted below does NOT keep multiple timeperiods in snyc like the other code-constructions that use

currentMillis = millis();  //get the current time[color=#222222][/color]
  if (currentMillis - startMillis >= period) .....

In most cases it doesn't matter if the periods of multiple timers are slightly async to each other.
examples: It is completely irrelevant if a LCD that show some status-information or code that polls for user-button-presses
execute every 1,0000000 seconds or after 1,02 seconds or even varying between 0,98 1,00 1,02 seconds

So the version below is easier to use and less to write because it reduces the code to write to a single line.

My question is: will the rollovers of millis() be handled properly like in the "standard"-use of millis()?

unsigned long DemoTimerA = 0; // variables that are used to store timeInformation
unsigned long DemoTimerB = 0; 
unsigned long DemoTimerC = 0; 
unsigned long DoDelayTimer = 0; 

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

void setup() 
{
  Serial.begin(115200);
  Serial.println("Program started");
}

void loop() 
{
  if (  TimePeriodIsOver(DemoTimerA,1000)  )
    {
      Serial.println(" TimerA overDue");
    }
       
  if (  TimePeriodIsOver(DemoTimerB,2000)  )
    {
      Serial.println("                TimerB overDue");
    }

  if (  TimePeriodIsOver(DemoTimerC,3000)  )
    {
      Serial.println("                               TimerC overDue");
    }

  if (  TimePeriodIsOver(DoDelayTimer,20000)  )
    {
      Serial.println("every 20 seconds execute delay(3500)... to make all other timers overdue");
      delay(3500);
    }    
}

best regards Stefan

StefanL38:
My question is: will the rollovers of millis() be handled properly like in the "standard"-use of millis()?

Yes, depending on what you call "standard".

The currentMillis - previousMillis >= interval will be okay when both are unsigned long, even when currentMillis has rolled over and previousMillis not.
That is how the 32-bit unsigned long calculation works.
Everything else is wrong.

The millis() is corrected now and then to keep it accurate to the millisecond. If the Arduino has a crystal instead of a resonator, it is possible to make clock that is maximum a few minutes wrong in a year.

There are two options:

  1. Normal, a delay in code delayes the millis() software timer as well, with: previousMillis = currentMillis
  2. Stay in sync with the time: previousMillis += interval

The second one can go wrong, when for example the interval is 1 second and there is a delay in the loop of 1 second.

I think your code is okay, but is it really smaller :wink:
Sometimes a led needs to blink, and the blinking needs to be turned on and off. Or a single-shot-timer is needed, or a millis() timer with a Finite State Machine. Using bare millis() timers isn't so bad.

Hi Koepel,

thank you for answering. Yes of course there are cases where bare millis() and other variants are good to use.
The most uses I have coded myself where of the type

currentMillis = millis();  
  if (currentMillis - startMillis >= period) 
  {
    digitalWrite(ledPin, !digitalRead(ledPin)); 
    startMillis = currentMillis;  
  }

and for this type it is shorter.

Copy & paste the function once into your code.
And for each use just a single line with one variablename and period to change.
Again: anybody is free to code that variant she/he likes best
best regards Stefan

Sometimes a led needs to blink, and the blinking needs to be turned on and off. Or a single-shot-timer is needed, or a millis() timer with a Finite State Machine. Using bare millis() timers isn't so bad.

For blinking leds it is not important that the timer sequence starts immediately after the event of enabling, the code can be much simpler.
e.g. This will flash the onboard led @5Hz for a duration of 1 sec, followed by 1 sec off.

void setup()
{ pinMode(13,OUTPUT);
}
boolean enable=false;
void loop()
{ enable=(millis()/1000 %2);                       //1 sec on, 1 sec off
  digitalWrite(13, (millis()/100 %2) && enable);   // 0.1 sec on, 0.1 sec off 
}

millis()/1000 is a number that counts seconds.
millis()/100 is a number that counts tenths of seconds.
The %2 is the remainder of 2, effectively the result = 0 or 1.

Using the same remainder method it is very simple to make any other pattern in flashing lights.
E.g. a running light of 6 leds connected to outputs 2..7

void setup()
{  for (int i=2; i<=7; i++) pinMode(i,OUTPUT);
}
void loop()
{  byte selector= 2+ (millis()/100) %6;
   for (int i=2; i<=7; i++) digitalWrite(i, selector==i); 
}

the remainder %6 counts from 0 to 5. For each of the values of selector one of the leds is burning.

femmeverbeek:
For blinking leds it is not important that the timer sequence starts immediately after the event of enabling, the code can be much simpler.

Did you mean

If for blinking leds it is not important that the timer sequence starts immediately after the event of enabling, the code can be much simpler

?

 if ( currentMillis - expireTime >= TimePeriod )
{
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

best regards Stefan
[/quote]

I would do it a little bit different then stefan does, just for a more accurate timing.
Ive edited the function that overwrites the oldMillis value.
Just take a look at what ive mentioned, and you might get a bit more accurate timing.

 if ( currentMillis - expireTime >= TimePeriod )
{
    expireTime = expireTime+TimePeriod; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

I also like to add a unsigned long that holds the seconds that was needed to switch once a cycle.
And the output this to the serial monitor, simple but useful.

unsigned long DemoTimerA = 0;
unsigned long DemoTimerB = 0;
unsigned long DemoTimerC = 0;
unsigned long inSec;
unsigned long currentMillis;



void setup()
{
  Serial.begin(9600);
  Serial.println("Program started");
}

void loop()
{
  if(timer(DemoTimerA, 10000)){ Serial.print(inSec); Serial.println(" Sec"); }
  if(timer(DemoTimerB, 1000)){ Serial.print(inSec); Serial.println(" Sec"); }
}






boolean timer (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    inSec = (currentMillis - expireTime)/1000;
    expireTime = expireTime+TimePeriod; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}