Multi Tasking The Arduino Part 1 PDF
Multi Tasking The Arduino Part 1 PDF
Guide Contents
Guide Contents
Overview
Remember Blink?
Become a clock-watcher!
10
10
11
A classy solution
12
15
16
16
Defining a class:
16
18
Less is more!
20
A clean sweep
21
21
24
28
Conclusion:
Adafruit Industries
28
33
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 2 of 33
Overview
Bigger and Better Projects
Once you have mastered the basic blinking leds, simple sensors and sweeping servos, its
time to move on to bigger and better projects. That usually involves combining bits and
pieces of simpler sketches and trying to make them work together. The first thing you will
discover is that some of those sketches that ran perfectly by themselves, just dont play
well with others.
The Arduino is a very simple processor with no operating system and can only run one
program at a time. Unlike your personal computer or a Raspberry Pi, the Arduino has no way
to load and run multiple programs.
That doesnt mean that we cant manage multiple tasks on an Arduino. We just need to use
a different approach. Since there is no operating system to help us out, We have to take
matters into our own hands.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 3 of 33
Remember Blink?
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000);
// wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000);
// wait for a second
}
The simple Blink sketch spends almost all of its time in the delay() function. So, the
processor can't do anything else while it is blinking.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 4 of 33
void setup()
{
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop()
{
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000);
// wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000);
// wait for a second
for(pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to 180 degrees
{
// in steps of 1 degree
myservo.write(pos);
// tell servo to go to position in variable 'pos'
delay(15);
// waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=0; pos-=1)
// goes from 180 degrees to 0 degrees
{
myservo.write(pos);
// tell servo to go to position in variable 'pos'
delay(15);
// waits 15ms for the servo to reach the position
}
}
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 5 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 6 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 7 of 33
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 8 of 33
created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/
// constants won't change. Used here to
// set pin numbers:
const int ledPin = 13;
// the number of the LED pin
// Variables will change:
int ledState = LOW;
long previousMillis = 0;
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 9 of 33
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 10 of 33
// milliseconds of off-time
void setup()
{
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
We also have code that looks at the state and decides when and how it needs to change.
That is the Machine part. Every time through the loop we run the machine and the
machine takes care of updating the state.
Next, we'll look at how you can combine multiple state machines and run them concurrently.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 11 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 12 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 13 of 33
Then we'll create another state machine for a second LED that flashes at completely
different rates. Using two separate state machines allows us to blink the two LEDs
completely independent of one another. Something that would be surprisingly complicated
to do using delays alone.
// These variables store the flash pattern
// and the current state of the LED
int ledPin1 = 12;
// the number of the LED pin
int ledState1 = LOW;
// ledState used to set the LED
unsigned long previousMillis1 = 0;
// will store last time LED was updated
long OnTime1 = 250;
// milliseconds of on-time
long OffTime1 = 750;
// milliseconds of off-time
int ledPin2 = 13;
// the number of the LED pin
int ledState2 = LOW;
// ledState used to set the LED
unsigned long previousMillis2 = 0;
// will store last time LED was updated
long OnTime2 = 330;
// milliseconds of on-time
long OffTime2 = 400;
// milliseconds of off-time
void setup()
{
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
}
void loop()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState1 == HIGH) && (currentMillis - previousMillis1 >= OnTime1))
{
ledState1 = LOW; // Turn it off
previousMillis1 = currentMillis; // Remember the time
digitalWrite(ledPin1, ledState1); // Update the actual LED
}
else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >= OffTime1))
{
ledState1 = HIGH; // turn it on
previousMillis1 = currentMillis; // Remember the time
digitalWrite(ledPin1, ledState1); // Update the actual LED
}
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 14 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 15 of 33
A classy solution
Let's take another look at that last sketch. As you can see, it is very repetitive. The same
code is duplicated almost verbatim for each flashing LED. The only thing that changes
(slightly) is the varable names.
This code s a prime candidate for a little Object Oriented Programming (OOP).
Defining a class:
We start by declaring a Flasher class:
Then we add in all the variables from FlashWithoutDelay. Since they are part of the class,
they are known as member variables.
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin;
// the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState;
// ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
};
Next we add a co nstructo r. The constructor has the same name as the class and its job
is to initialize all the variables.
class Flasher
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 16 of 33
{
// Class Member Variables
// These are initialized at startup
int ledPin;
// the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState;
// ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
};
Finally we take our loop and turn it into a member functio n called Update(). Note that
this is identical to our original void loop(). Only the name has changed.
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin;
// the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState;
// ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 17 of 33
By simply re-arranging our existing code into the Flasher class, we have encapsulated all of
the variables (the state) and the functionality (the machine) for flashing a LED.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 18 of 33
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin;
// the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState;
// ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 19 of 33
Less is more!
Thats it each additional LED requires just two lines of code!
This code shorter and easier to read. And, since there is no duplicated code, it also
compiles smaller! That leaves you even more precious memory to do other things!
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 20 of 33
A clean sweep
What else can we do with it?
Lets apply the same principles to some servo code and get some action going.
First hook up a couple of servos on your breadboard as shown below. As long as we are at
it, let's hook up a third LED too.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 21 of 33
Here is the standard Servo sweep code. Note that it calls the dreaded delay(). We'll take
the parts we need from it to make a "Sweeper" state machine.
// Sweep
// by BARRAGAN <http://barraganstudio.com>
// This example code is in the public domain.
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created
int pos = 0;
void setup()
{
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}
void loop()
{
for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{
// in steps of 1 degree
myservo.write(pos);
// tell servo to go to position in variable 'pos'
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 22 of 33
delay(15);
// waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=1; pos-=1)
// goes from 180 degrees to 0 degrees
{
myservo.write(pos);
// tell servo to go to position in variable 'pos'
delay(15);
// waits 15ms for the servo to reach the position
}
}
The Sweeper class below encapsulates the sweep action, but uses the millis() function for
timing, much like the Flasher class does for the LEDs.
We also need to add Attach() and Detach() functions to associate the servo with a specific
pin:
class Sweeper
{
Servo servo;
// the servo
int pos;
// current servo position
int increment;
// increment to move for each interval
int updateInterval;
// interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 23 of 33
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 24 of 33
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo;
// the servo
int pos;
// current servo position
int increment;
// increment to move for each interval
int updateInterval;
// interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 25 of 33
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
void loop()
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 26 of 33
{
sweeper1.Update();
sweeper2.Update();
led1.Update();
led2.Update();
led3.Update();
}
Now we have 5 independent tasks running non-stop with no interference. And our loop() is
only 5 lines of code! Next we'll add a button so we can interact with some of these tasks.
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 27 of 33
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 28 of 33
The code below will check the button state on each pass of the loop. Led1 and sweeper2
will not be updated when the button is pressed.
#include <Servo.h>
class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin;
// the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time
// These maintain the current state
int ledState;
// ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 29 of 33
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};
class Sweeper
{
Servo servo;
// the servo
int pos;
// current servo position
int increment;
// increment to move for each interval
int updateInterval;
// interval between updates
unsigned long lastUpdate; // last update of position
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 30 of 33
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};
void loop()
{
sweeper1.Update();
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 31 of 33
if(digitalRead(2) == HIGH)
{
sweeper2.Update();
led1.Update();
}
led2.Update();
led3.Update();
}
The 3 LEDs will flash at their own rates. The 2 sweepers will sweep at their own rates. But
when we press the button, sweeper2 and led1 will stop in their tracks until we release the
button.
Since there are no delays in the loop, the button input has nearly instantaneous response.
So now we have 5 tasks executing independently and with user input. There are no delays
to tie up the processor. And our efficient Object Oriented code leaves plenty of room for
expansion!
Adafruit Industries
https://learn.adafruit.com/multi-tasking-the-arduino-part-1
Page 32 of 33
Conclusion:
In this guide we have demonstrated that it is indeed possible for the Arduino to juggle
multiple independent tasks while remaining responsive to external events like user input.
Weve learned how to time things using millis() instead of delay() so we can free up
the processor to do other things.
Weve learned how to define tasks as state machines that can execute independently
of other state machines at the same time.
And weve learned how to encapsulate these state machines into C++ classes to
keep our code simple and compact.
These techniques wont turn your Arduino into a supercomputer. But they will help you to
get the most out of this small, but surprisingly powerful little processor.
In the part 2 of this series, we'll build on these techniques and explore other ways to
make your Arduino responsive to external events while managing multiple tasks.
Adafruit Industries
Page 33 of 33