Many Timers at once Arduino

Hello Everyone..

For my current project it is required that i have the ability to run 10 timers at once. I have made a simple sketch for one timer using the millis(); function, the carryOver variable is simply used in case of an overflow of the millis() function.

If i need ten timers, i would have to create 10 times the variables that are currently used in my code. Since i only have a basic knowledge of programming i would like to know if there is any simpler way to do what i require.

Thanks In Advance!

Here is the code:

// include the library code:

unsigned long carryOver=0;
unsigned long previousTime = 0;
unsigned long elapsedTime = 0;
int timeStart=0;
int timeOver=0;

void setup() {
Serial.begin(9600);
pinMode(5,INPUT);
}

void loop(){

timeStart=digitalRead(5);

if (timeStart==1){timer();}
else{}

if (timeOver==1) {
Serial.println("sss");
timeOver=0;
previousTime = 0;
elapsedTime = 0;
}

}

void timer() {

if (millis()==0)
{carryOver=previousTime*2;}

else{}

previousTime = millis();

while(elapsedTime <=(6000))
{
elapsedTime = millis() - previousTime + carryOver;
Serial.println("success");
Serial.println(carryOver);
Serial.println(elapsedTime);
Serial.println(previousTime);
Serial.println(millis());
}
timeOver=1;

}

TimerTest.ino (757 Bytes)

Arrays?

if you don't want to learn how to do it, you can have a look at the TimedAction library

const byte TIMER_COUNT = 10;
const byte LED_PIN = 13;

unsigned long TimerStartTimes[TIMER_COUNT];
unsigned long TimerIntervals[TIMER_COUNT];
void (*TimerCallbacks[TIMER_COUNT])(void);  // Callback functions

void StartTimer(unsigned long interval, void (*callback)(void)) {
  for (byte i = 0; i < TIMER_COUNT; i++) {
    if (TimerStartTimes[i] == 0UL) {  // Unused timer
      TimerStartTimes[i] = millis();
      TimerIntervals[i] = interval;
      TimerCallbacks[i] = callback;
      //     Serial.print("Timer ");
      //     Serial.print(i);
      //     Serial.print(" set at ");
      //     Serial.print(TimerStartTimes[i]);
      //     Serial.print(" for ");
      //     Serial.print(TimerIntervals[i]);
      //     Serial.println(" milliseconds.");
      break;
    }
  }
}

void CheckTimers() {
  unsigned long currentTime = millis();
  for (byte i = 0; i < TIMER_COUNT; i++) {
    if ((TimerStartTimes[i] != 0UL) &&
        (currentTime - TimerStartTimes[i] >= TimerIntervals[i])) {
      TimerStartTimes[i] = 0;  // Unused Timer
      (TimerCallbacks[i])();
    }
  }
}

void LEDon(void) {
  // Serial.println("HIGH");
  digitalWrite(LED_PIN, HIGH);
  StartTimer(1000UL, LEDoff);
}

void LEDoff(void) {
  // Serial.println("LOW");
  digitalWrite(LED_PIN, LOW);
  StartTimer(1000UL, LEDon);
}

void setup() {
  // put your setup code here, to run once:
  // Serial.begin(19200);

  // WARNING: Since we use the "start time" value of zero to indicate
  // an unused timer entry we must be sure the millis() time has
  // reached at least 1 before we set any timers.  The easy
  // way is to delay 2 milliseconds at startup.
  delay(2);


  pinMode(LED_PIN, OUTPUT);

  // Turn on the LED and set the OFF timer
  LEDon();
}

void loop() {
  // put your main code here, to run repeatedly:
  CheckTimers();
}

Would you be able to create a class that holds all the info for one timer then just spawn off 10 of them? That's how I do it.

Code once, spawn like rabbits!

-jim lee

The demo Several Things at a Time illustrates the use of millis() to manage timing. It may help with understanding the technique.

If you use subtraction (as in the demo) you don't need to worry about millis() rolling over.

This sort of test

if (millis()==NNN)

will fail if you happen to miss the the moment when it is exactly equal. Testing for >= is safer.

I wonder if this properly describes the problem ...

it is required that i have the ability to run 10 timers at once

My guess is that what you really want is to run 10 different things at different time intervals. And that is the style of the code in my link. There is a more extensive example in Planning and Implementing a Program

...R

if (millis()==0)
{carryOver=previousTime*2;}

else{}

Things are getting weird here. millis() will only ever be zero every 49 days, and possibly not even then if you happen to be doing something else.

And what's with:

else{}

?

the carryOver variable is simply used in case of an overflow of the millis() function.

millis() overflow ... a bad thing?

I get the feeling we're talking to ourselves here..

-jim lee

Hello all,

Sorry for the late reply, since I'm new i didnt know how to find my post.(figured it out). I managed to turn them into ARRAYS thanks AWOL .

This is my current code:

// include the library code:

unsigned long carryOver[10];
unsigned long previousTime[10];
unsigned long elapsedTime[10];
int timeStart[10];
int timeOver[10];


void setup() {
Serial.begin(9600);
pinMode(5,INPUT);
pinMode(6,INPUT);
}


void loop(){

timeStart[0]=digitalRead(5);
timeStart[1]=digitalRead(6);

 Serial.println("TS0");
 Serial.println(timeStart[0]);
 Serial.println("TS1");
 Serial.println(timeStart[1]);

for (int n=0; n <= 9; n++) {
if (timeStart[n]==HIGH){timer(n);}
else{}

if (timeOver[n]==1) {  
 Serial.println("sss");
 timeOver[n]=0;
 previousTime[n] = 0;
 elapsedTime[n] =  0;
 }
}

 
}



int timer(int n) {

if (millis()==0)
{carryOver[n]=previousTime[n]*2;}

else{}


previousTime[n] = millis();
elapsedTime[n]=0;
while(elapsedTime[n] <=(2000))
{
 elapsedTime[n] =  millis() - previousTime[n] + carryOver[n];
 Serial.println("success");
 Serial.println(n);
 Serial.print("C0: "); 
 Serial.println(carryOver[n]); 
 Serial.println(n);
 Serial.print("ET: ");
 Serial.println(elapsedTime[n]);
 Serial.println(n);
 Serial.print("PT: ");
 Serial.println(previousTime[n]);
 Serial.println(millis());
}
timeOver[n]=1;
timeStart[0]=LOW;


}

However i thought that calling the timer function would make it run in parallel with the loop. However that is not the case.. the timer stops everything else....

Thank You for the replies .. I shall try reading the rest of the replies on this post for a solution to my current issue...

Robin2:
The demo Several Things at a Time illustrates the use of millis() to manage timing. It may help with understanding the technique.

If you use subtraction (as in the demo) you don't need to worry about millis() rolling over.

The thing is.. Lets say my previous time was 60.. and then the millis rollover to zero..
elapsedTime[n] = millis() - previousTime[n] ;

wouldnt elapsed time become 0 - 60 = 60; (for unsigned long)
then 1 - 60 = 59;

this way my elapsed time would increase by 60 because first it would count down from 60 to 0, then it would start increasing...

This sort of test

if (millis()==NNN)

will fail if you happen to miss the the moment when it is exactly equal. Testing for >= is safer.

this test was used to worry about the rollover..... I will try and figure out a change for this

I wonder if this properly describes the problem ...My guess is that what you really want is to run 10 different things at different time intervals. And that is the style of the code in my link. There is a more extensive example in Planning and Implementing a Program

ok going to look into this
...R

Yes Since this is for my project i will have to work with the assumption that the arduino is working all the time for long periods.

i guess i just put the else statement because i thought we have to put an else statement for all if statements..
since this is just a part of my overall project i may have to include something over there in later stages.

If the timer will be running when the overflow happens, this may happen..

eg.

The thing is.. Lets say my previous time was 60.. and then the millis rollover to zero..
elapsedTime[n] = millis() - previousTime[n] ;

wouldnt elapsed time become 0 - 60 = 60; (for unsigned long)
then 1 - 60 = 59;

this way my elapsed time would increase by 60 because first it would count down from 60 to 0, then it would start increasing...

Please do correct me if i'm wrong!

then 1 - 60 = 59;

no, not with unsigned arithmetic. plug in your arduino and do something like this:

void setup() {
  unsigned long a, b;

  Serial.begin(115200);
  a = 1ul;
  b = 60ul;

  Serial.println(a);
  Serial.println(b);
  Serial.println(a - b);
  Serial.println(b - a);
}

void loop() {}

and see what you get (with your console set at 115200 bauds)

Sorry i didnt see the code before... hmmm when i ran it i got random values.. i just changed the serial number to serial.begin(9600) this:

1
60
4294967237
59

Which means the rollover would still be an issue for me ....
thanks for the code to confirm :slight_smile:

No if you do a subtraction, the rollover won't because 1-60 = 4294967237 not 59

it would become a pb only if you have a start time and a end time that span more than 49 days.

the timer would start whenever the option is chosen... so if someone chooses to start the timer near the 50 day mark, at that point

previoustime = current millis();

and if the timer has to go on till after the overflow, then according to my code:

elapsed time = millis() - previous time = 4294967237 which would still end up being a high value in turn stopping the timer abruptly.

currently i think i might change the unsigned long to signed and just check for the condition if (elapsed time<0)... that would fix the flaw in checking if (millis()==0)... because if i keep it as unsigned then my timer will still stop abruptly if the calculation is done before the checking of the condition.

No you don't need to do that...

An unsigned long goes to 4 294 967 295 (232 - 1).

say your previoustime = 4294967294 - so 2ms before the rollover

then you compare doing millis()-previoustime. Say millis() returns 100 because it rolled over.

You will do (100 - 4294967294) which in unsigned arithmetics gives 102 which is indeed the number of ms elapsed between your two events.

so if your code does if (millis() - previoustime >= threshold) { // do something} all will be fine even through rollover. you will only do your action after the threshold.

makes sense?

here is a quick code to prove the maths

void setup() {
  Serial.begin(115200);
  unsigned long t1 = 4294967294ul;
  unsigned long t2 = 100ul;
  unsigned long delta = t2-t1;

  Serial.println(delta);
  
}

void loop() {}

harshikdinesh:
and if the timer has to go on till after the overflow, then according to my code:

elapsed time = millis() - previous time = 4294967237 which would still end up being a high value in turn stopping the timer abruptly.

But that is not a sensible way to do it.

if you do this

if (millis() - previousTime >= requiredInterval) {

it will work properly even when millis() rolls over

It is much easier to understand what happens if you do some back-of-the-envelope calculations using byte values.

...R

makes sense thanks so much and a HAPPY NEW YEAR TO ALL!!

else{}

This is just confusing. Don't just throw that in where-ever you have an "if".

That's like saying:

if it is raining
take an umbrella
else
do nothing

...

if you are hungry
eat a snack
else
do nothing

It's just clutter.


The thing is.. Lets say my previous time was 60.. and then the millis rollover to zero..
elapsedTime[n] = millis() - previousTime[n] ;

wouldnt elapsed time become 0 - 60 = 60; (for unsigned long)
then 1 - 60 = 59;

this way my elapsed time would increase by 60 because first it would count down from 60 to 0, then it would start increasing...

Please do correct me if i'm wrong!

Did you read the link I gave you?