Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ How to do multiple things at once ... like cook bacon and eggs

How to do multiple things at once ... like cook bacon and eggs

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,100 posts)  Bio   Forum Administrator
Date Mon 07 Nov 2011 05:12 AM (UTC)

Amended on Sat 27 Jul 2013 12:11 AM (UTC) by Nick Gammon

Message
This page can be quickly reached from the link: http://www.gammon.com.au/blink


This question comes up practically every day on the Arduino forum - "how do I blink two LEDs at different rates?" or "how do I turn on and off two motors at different times?".

One of the problems is that beginners look at the "blink" tutorial program, which is:


void setup() 
  {                
  pinMode(13, OUTPUT);     
  }

void loop() 
  {
  digitalWrite(13, HIGH);   // set the LED on
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // set the LED off
  delay(1000);              // wait for a second
  }


Now this works fine, to blink one LED. But to blink two LEDs you run into problems. It's OK if you want to blink them both at once:


void setup() 
  {                
  pinMode(12, OUTPUT);     
  pinMode(13, OUTPUT);     
  }

void loop() 
  {
  digitalWrite(12, HIGH);   // set the first LED on
  digitalWrite(13, HIGH);   // set the second LED on
  delay(1000);              // wait for a second

  digitalWrite(12, LOW);    // set the first LED off
  digitalWrite(13, LOW);    // set the second LED off
  delay(1000);              // wait for a second
  }


Or, if you want to do one after the other:



void setup() 
  {                
  pinMode(12, OUTPUT);     
  pinMode(13, OUTPUT);     
  }

void loop() 
  {
  digitalWrite(12, HIGH);   // set the first LED on
  delay(1000);              // wait for a second
  digitalWrite(12, LOW);    // set the first LED off
  delay(1000);              // wait for a second

  digitalWrite(13, HIGH);   // set the second LED on
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // set the second LED off
  delay(1000);              // wait for a second  
  }


But what if you want to blink the two LEDs at different rates? Like, once a second for LED 1 and twice a second for LED 2?

This is where the delay function doesn't really help.

Let's look at an analogy. Say you want to cook breakfast. You need to cook:


  • Coffee - takes 1 minute
  • Bacon - takes 2 minutes
  • Eggs - takes 3 minutes



Now a seasoned cook would NOT do this:


  • Put coffee on. Stare at watch until 1 minute has elapsed. Pour coffee.
  • Cook bacon. Stare at watch until 2 minutes have elapsed. Serve bacon.
  • Fry eggs. Stare at watch until 3 minutes have elapsed. Serve eggs.


The flaw in this is that whichever way you do it, something is going to be cooked too early (and get cold).

In computer terminology this is blocking. That is, you don't do anything else until the one task at hand is over.

What you are likely to do is this:


  • Start frying eggs. Look at watch and note the time.
  • Glance at watch from time to time. When one minute is up then ...
  • Start cooking bacon. Look at watch and note the time.
  • Glance at watch from time to time. When another minute is up then ...
  • Put coffee on. Look at watch and note the time.
  • When 3 minutes are up, everything is cooked. Serve it all up.


In computer terminology this is non-blocking. That is, keep doing other things while you wait for time to be up.




So, adapting this idea to blinking two LEDs at different rates, we can't afford to use:


delay (1000);


... because this blocks. That is, you can't be doing anything else during those 1000 milliseconds.

Instead we note the time, like this:



unsigned long when_eggs_started;  // declare variable

...

when_eggs_started = millis ();  // note the time now


Later on we can see if 3 minutes are up by subtracting when we started from the time now. This, by the way, works even if the millis () function call wraps around after approximately 50 days.


if ((millis () - when_eggs_started) >= (1000UL * 60 * 3))
  {
  // eggs are cooked
  }


The "UL" after 1000 is to force the compiler to make an "unsigned long" literal, which can hold a larger number than the default of an int (integer) which can only hold -32768 to +32767.

Note that millis () returns milliseconds, thus 1000 milliseconds are a single second. So 1000 milliseconds * 60 * 3 is three minutes.


The full sketch that blinks two LEDs at different rates is:


// Which pins are connected to which LED
const byte greenLED = 12;
const byte redLED = 13;

// Time periods of blinks in milliseconds (1000 to a second).
const unsigned long greenLEDinterval = 500;
const unsigned long redLEDinterval = 1000;

// Variable holding the timer value so far. One for each "Timer"
unsigned long greenLEDtimer;
unsigned long redLEDtimer;

void setup () 
  {
  pinMode (greenLED, OUTPUT);
  pinMode (redLED, OUTPUT);
  greenLEDtimer = millis ();
  redLEDtimer = millis ();
  }  // end of setup

void toggleGreenLED ()
  {
   if (digitalRead (greenLED) == LOW)
      digitalWrite (greenLED, HIGH);
   else
      digitalWrite (greenLED, LOW);

  // remember when we toggled it
  greenLEDtimer = millis ();  
  }  // end of toggleGreenLED

void toggleRedLED ()
  {
   if (digitalRead (redLED) == LOW)
      digitalWrite (redLED, HIGH);
   else
      digitalWrite (redLED, LOW);

  // remember when we toggled it
  redLEDtimer = millis ();  
  }  // end of toggleRedLED

void loop ()
  {

  // Handling the blink of one LED.
  if ( (millis () - greenLEDtimer) >= greenLEDinterval)
     toggleGreenLED ();

  // The other LED is controlled the same way. Repeat for more LEDs
  if ( (millis () - redLEDtimer) >= redLEDinterval) 
    toggleRedLED ();

/* Other code that needs to execute goes here.
   It will be called many thousand times per second because the above code
   does not wait for the LED blink interval to finish. */

}  // end of loop


Of course, you don't have to blink LEDs. The functions toggleGreenLED and toggleRedLED could be turning on and off motors, taking temperature readings, whatever you want to do.

Also you can do more than two things. You can extend this idea to do as many things as you want. At the part where the comment "Other code that needs to execute goes here" is, you could be doing other things like testing if a switch button is pressed.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,100 posts)  Bio   Forum Administrator
Date Reply #1 on Sun 23 Dec 2012 05:07 AM (UTC)

Amended on Tue 29 Jan 2013 11:22 PM (UTC) by Nick Gammon

Message
Example of flashing lots of LEDs


Following on from a query on the Arduino forum, I've made a class specially designed to flash LEDs at different rates. You can download that as a library from:

http://gammon.com.au/Arduino/LED_flasher.zip

Install that in the usual way (unzip into your libraries folder).

I made a similar class to fade up and down LEDs connected to the PWM pins, here:

http://gammon.com.au/Arduino/LED_fader.zip





Example flashing sketch:


// Example of flashing multiple LEDs at different rates without delays
// Author: Nick Gammon
// Date: 23 December 2012

#include <LedFlasher.h>

// set up some LEDs
LedFlasher floodLight (8, 200, 300);
LedFlasher shuttleBayDoors (9, 300, 600);
LedFlasher impulseEngine (10, 900, 100);
LedFlasher strobe (11, 500, 1000);
LedFlasher navigation (12, 1000, 2000);
LedFlasher torpedoes (13, 250, 500);

void setup() 
  {      
  floodLight.begin ();
  shuttleBayDoors.begin ();
  impulseEngine.begin ();
  strobe.begin ();
  navigation.begin ();
  torpedoes.begin ();
  }  // end of setup

void loop() 
  {
  // update lights
  floodLight.update ();
  shuttleBayDoors.update ();
  impulseEngine.update ();
  strobe.update ();
  navigation.update ();
  torpedoes.update ();
  
  
  // do other useful stuff here ...

 
  }  // end of loop


For each LED we make an instance of the class, eg.


LedFlasher floodLight (8, 200, 300); // pin 8, off for 200 mS, on for 300 mS


Then we set that LED up in setup:


  floodLight.begin ();


That sets the pin to output, and starts the timing.

In loop we call "update" to flash the LED when required:


  floodLight.update ();


Ditto for any number of other LEDs.




Example fading sketch:


// Example of fading multiple LEDs at different rates without delays
// Author: Nick Gammon
// Date: 27 December 2012

#include <LedFader.h>

// set up some LEDs
//                 pin  min max  millis
LedFader strobe     (3, 10, 200,  1000);
LedFader navigation (5, 10, 200,   500);
LedFader torpedoes  (6, 10, 200,   250);

void setup() 
  {      
  strobe.begin ();
  navigation.begin ();
  torpedoes.begin ();
  }  // end of setup

void loop() 
  {
  // update lights
  strobe.update ();
  navigation.update ();
  torpedoes.update ();
  
  // do other useful stuff here ...
 
  }  // end of loop


[EDIT] Turned class into a library.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,100 posts)  Bio   Forum Administrator
Date Reply #2 on Fri 10 Apr 2015 06:08 AM (UTC)

Amended on Fri 10 Apr 2015 06:09 AM (UTC) by Nick Gammon

Message
Also see my post about State Machines: http://www.gammon.com.au/statemachine

For more complex situations (eg. handling key presses, flashing LEDs, turning motors on and off, and so on) a state machine may well help keep track of what is happening.

A garden watering system, or fish-tank feeder, may well represent this sort of case. You have timed events (like, how long the garden is watered for) plus managing key presses, and turning on and off indicator LEDs.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


103,692 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.