Countdown timers and executing tasks in parallel on an Arduino

The examples in this article use an OLED display.

Countdown timer example

In its simplest form, a countdown timer can be coded like this, using the loop() for repetition (no for- or while-loop needed!):

int count = 10;

void loop() {
  if (count>=0) { // if counter not finished
    sprintf(buf, "%2d", count);
    display.draw2x2String(4,3,buf); // show the counter
    count = count - 1; // decrease counter
  delay(1000); // wait one second

Full example : oled_display_countdown_start_button.ino

This will countdown from 10 to 0 in ten seconds. If you prefer to display the number as a time, like MM:SS, you can realize that as follows:

int min = count/60;
int sec = count%60;
sprintf(buf, "%02d:%02d", min, sec);

Full example : oled_display_countdown_start_button2.ino

Both examples use a button to start the timer. This is realized by a waiting loop (a loop which does nothing, except for checking its condition) as the last line in setup():

while(digitalRead(START_BTN)==HIGH) ;

Note the empty statement: ‘;’ does nothing, thus waits for the button to be pressed (becomes LOW).

Parallel tasks, advanced timer example

About running tasks in the background: it is true that the loop() is actually a kind of ‘single task’ that does not allow (or is difficult) to run things in parallel or in the background.
However, because the Arduino is fairly fast, tasks that are performed in serial (one after the other) may still appear to the user as if they are being performed simultaneously.
It is important that you do not use delays, so that everything is executed as quickly as possible.
Instead of using delays, you can then keep the time and have things done at a certain time: eg. make an LED blink every 300ms and read all buttons at the same time (as quickly as possible).
The example oled_display_countdown_start_button_set_timer.ino described below uses that: three tasks (in the loop) are performed in serial, but for the user it looks like they occur in parallel, because they do not contain any delays.

void loop() {
  unsigned long currentMillis = millis();

  // task 1 - blink time on display every 300ms when setting it:
  if (currentMillis - previousMillis2 > 300 ) { // 300ms passed?
    previousMillis2 = currentMillis; // save the last time
    blink = !blink;
    // ... display blinking numbers when setting the time  ...

  // task 2 - update countdown timer every second
  if (currentMillis - previousMillis > interval) { // interval passed?
    previousMillis = currentMillis; // save the last time
    // ... display changing countdown timer on the display ...

  // task 3 - check for pressed buttons
  for (int i = 0; i < NUMBUTTONS; i++) {
    // Update the Bounce instance :
    if ( buttons[i].fell() ) { // a button was pressed
       // ... process actions for buttons ...
  } // end for

Here the time is kept with millis().

The example above is of course somewhat limited but in terms of coding not too complex. It is a timer of which the time can be set using up/down/set buttons. It can be paused while running and after it is finished, it can be start again.

Besides this you can also use timers and even a form of multi-theading. But that soon becomes complex …