Fun with Arduino 13 Timer with millis(), no delay() , Multitasking

Our day / night light switching unit works fine, but for one thing: when we input a new cycle time via keyboard or via an analog input, we do not have any feedback on the monitor on what we are inputting. This is caused by the delay() instruction, which is easy to use, but which has a major drawback: delay() completely stalls the Arduino.

Luckily there is a solution: the Arduino has a built in internal clock which starts counting milliseconds when the Arduino starts up. millis() is the instruction to see what time it is.

Time_for_Action_smallTo see this at work, let’s do a simple LED blink,  using millis() in stead of delay(). When the LED turns on, we read the current time with the millis() instruction and we add INTERVAL milliseconds to calculate the next time_for_action. The loop() keeps running and every cycle we test if millis() went past time_for_action. If so, we can run some code, in this case we invert the LED-status.

Read on below the video …

 

 

#define INTERVAL 200 // [ms]
byte led; // LED status: 0=off, 1=on
unsigned long time_for_action;

void setup() {
  pinMode(13, OUTPUT); // 13 is the on board LED
}

void loop() {
  if (millis() > time_for_action) {
    time_for_action = millis() + (unsigned long)INTERVAL;
    led = !led;
    digitalWrite(13, led);
  }
}

Note how led = !led; inverts the variable led … “!” means NOT. Without delay() the Arduino never stalls … it keeps running the loop() and only when it is time_for_action it executes the code for the action. We can make multiple LEDs blink, each with its own fully independent timing, something we would never be able to achieve with delay()!

#define INTERVAL_1 200  // [ms]
#define INTERVAL_2 1100  // [ms]
byte led_1;
byte led_2;
unsigned long time_for_action_1;
unsigned long time_for_action_2;

void setup() {
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  if (millis() > time_for_action_1) {
    time_for_action_1 = millis() + (unsigned long)INTERVAL_1;
    led_1 = !led_1;
    digitalWrite(8, led_1);
  }
  if (millis() > time_for_action_2) {
    time_for_action_2 = millis() + (unsigned long)INTERVAL_2;
    led_2 = !led_2;
    digitalWrite(9, led_2);
  }
}

Want more timers? Just copy – paste the if { … } statements as many times as needed, change the numbers and you have them.

We can also add a toggle switch, and unlike we experienced in the code with delay(), we do not have to wait a complete cycle for the switch to be read and acted upon … the cycle can immediately be stopped.

#define INTERVAL_1 1000  // [ms]
#define INTERVAL_2 3000  // [ms]
byte led_1;
byte led_2;
unsigned long time_for_action_1;
unsigned long time_for_action_2;

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  if (digitalRead(2) == LOW) { // start the timers
    if (millis() > time_for_action_1) {
      time_for_action_1 = millis() + (unsigned long)INTERVAL_1;
      led_1 = !led_1;
    }
    if (millis() > time_for_action_2) {
      time_for_action_2 = millis() + (unsigned long)INTERVAL_2;
      led_2 = !led_2;
    }
  }
  else { // immediately stop the timers and switch the LEDs off
    time_for_action_1 = 0;
    time_for_action_2 = 0;
    led_1 = 0;
    led_2 = 0;
  }
  digitalWrite(8, led_1);
  digitalWrite(9, led_2);
}

Hey wait … something strange is happening here. When switched on, the LEDs should burn. But ever so often we see a short flicker and then the LEDs are off. How come? What we see here is an effect that most mechanical switches have, called bouncing. The contacts bounce a couple of times before they really are closed. This happens all within just a few milliseconds, but the Arduino is looping fast enough to see it, so first the switch read LOW, then the if is executed. The next loop the switch bounced and is HIGH and the else is executed … maybe this even repeats a few times.

To avoid this we can add a little delay immediately after reading the switch, a delay short enough not to be noticeable, and long enough for the bouncing to have finished.

#define INTERVAL_1 1000  // [ms]
#define INTERVAL_2 3000  // [ms]
byte led_1;
byte led_2;
unsigned long time_for_action_1;
unsigned long time_for_action_2;

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  if (digitalRead(2) == LOW) { // start the timers
    delay(50) // anti bounce
    if (millis() > time_for_action_1) {
      time_for_action_1 = millis() + (unsigned long)INTERVAL_1;
      led_1 = !led_1;
    }
    if (millis() > time_for_action_2) {
      time_for_action_2 = millis() + (unsigned long)INTERVAL_2;
      led_2 = !led_2;
    }
  }
  else { // immediately end the timers and switch the LEDs off
    time_for_action_1 = 0;
    time_for_action_2 = 0;
    led_1 = 0;
    led_2 = 0;
  }
  digitalWrite(8, led_1);
  digitalWrite(9, led_2);
}

END TEXT

— 0 —

11 thoughts on “Fun with Arduino 13 Timer with millis(), no delay() , Multitasking

  1. Hi, rudy
    I am really happy with your youtube chanel fun with an arduino 13, i just begginer for an arduino,, and i need your favour to help me how to create timer on for 5second after that off,, and timer off for 2second before on with millis program and use switch toggle for each fungtions to control it,, i want to change ic 555 in my project by using an arduino ,,,,
    i am really happy full of thank you for your future information
    Best regards,

    Christian

    Like

      • Hi, rudy
        I am glad for your faster ever for reply my comment, my appologize for my explaned not too clear,,

        To make the specifications clear:
        When S1 is switched, a signal goes from LOW to HIGH for 5s and then Stay HIGH , as a one shot.. and
        When S2 is switched, a signal goes from HIGH to LOW for 2s and then stay LOW , as a one shot…

        In millis please,

        I just let you know i am used a relay 5pins 5volt instead of switch toggle for automatic and several toggle switch for manually both of combine in my project..

        Thank you a lot for future reply

        Best regards,

        Christian

        Like

    • millis() rolls over back to zero approximately every 49 days. If your “time for action” is calculated to be very close to the end and on the next loop it rolls over you led will stop blinking if there anything else in the sketch that could cause the loop not to execute at least every millisecond. Example would be reading data from a one wire sensor.

      To keep example simple, say millis() return 0-255 and interval is 5. One loop time for action is calculated to be 255. Next loop millis () rolls over to 0 and (millis() > 255) will never be true. Even if you change it to “>=” any slow loop could cause it to miss. If you loop is guaranteed to take less than 1ms, then you are probably ok. But reading a DS18B20 temperature sensor can take at least 15ms due to one wire protocol timings.

      The normal pattern used should be:

      if (millis() – last_toggle >= INTERVAL) {
      }

      This takes advantage of unsigned integer rollover.

      Like

  2. This sounds like a strange specification. I guess you meant:
    If S1 is switched, a signal gooed from LOW to HIGH AFTER 5s and stays HIGH.
    If S2 is switched a signal goes to HIGN to LOW AFTER 2s and stays LOW.
    Another question: when do the signals change state again? When the switches are switched off?

    Like

  3. Hi, rudy
    Yes it is,, actually it is common timer delay ON or timer delay OFF from ic analog 555,, correct rudy, to change state only switched off ,, it is look like i watched fun with arduino 13 ( you did great program for blinking by use interval ) just switched off all the light is off..

    Thank you fot your future information

    Best regards,

    Christian

    Like

  4. Have a look at the videos that use a state transition diagram and you will be able to write the code yourself. I can’t write it for you, being too busy.

    Like

  5. Hi, rudy
    It is ok, i will try to do by my self,, good to see you in comment
    Take time with your busy day

    Best regards,

    Christian

    Like

  6. hi.
    I want two leds to blink when the motion sensor is high input.
    I don’t wanna do it with delay.
    let the first led flash with the event
    for the second led; Flash after 10 seconds.
    can you guide me.Thank you

    Like

    • Well … the whole idea and purpose of the video series is that you should be able to figure this out and write the code all by yourself now. If you first write down the specifications, as in what needs to happen when, then you’ve almost already written the code.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s