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 —

Advertisements

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