Introduction: Multi-task Your Arduino
This instructable describes a method of time-slicing that creates the illusion that your arduino is performing multiple tasks at the same time.
Using this technique the arduino in the above video is simultaneously:
- toggling a LED every 300mS (milliseconds)
- scrolling a line of text every 250mS
- changing a custom bar graph every 100mS
The included arduino code is fully annotated should you wish to use this project as a template.
Step 1: Parts List and Schematic
Very few parts are required to replicate this project:
- 1 x arduino Uno R3 microcontroller.
- 1 x I2C LCD 16x2 display
- 1 x arduino-to-PC USB cable
- 4 x arduino male-to-female jumper wires
All parts may be obtained from https://www.aliexpress.com/
Photo 1 shows how to connect the jumper wires.
Step 2: Code
To install my code, download the file "multitasking.ino" and insert the contents into a new arduino sketch. Save the sketch as "multitasking" (without the quotes) then upload it to your arduino.
You will also need to install an I2C “library” for your LCD display. I used a library from http://arduino-info.wikispaces.com/LCD-Blue-I2C#v1 . If you have a different chipset "Google” the text located in the red area shown in photo 1.
Once you have located a suitable library download the zip file and install it using the instructions found at https://www.arduino.cc/en/Guide/Libraries.
The instruction set for most LCD displays can be found at https://www.arduino.cc/en/Reference/LiquidCrystal.
Attachments
Step 3: Theory
An empty loop(){} within your arduino code will cause the arduino to “wheel-spin” several thousand times every second. Let’s see what happens when we add a task ...
Single-tasking:
The following code will display “Hello World!” on your Serial Monitor once every second:
Serial.println(“Hello World!”); delay(1000);
Each loop(){} now takes over 1 second to complete due to the delay(1000) instruction.
Assuming that your arduino has a 16MHz crystal, the delay(1000) instruction is wasting 16,000,000 clock cycles . Other tasks are possible if we eliminate this delay.
Multi-tasking:
Let’s rewrite the above piece of code:
if (Flag1) { Serial.println(“Hello World!”); Flag1=false; }
“Hello World! is printed after which the loop(){} reverts to rotating at full speed until Flag1=true.
Since the loop(){} has nothing to do, and is whizzing around at full speed, let’s add another task.
if (Flag2) { digitalWrite(LED, !digitalRead(LED)); Flag2=false; }
This piece of code causes the LED to “toggle” after which the loop(){} again reverts to full speed until Flag2=true.
Ignoring how the flags are getting set, we are now able to perform multiple tasks in the SAME time period.
Long tasks require special coding techniques
Multitasking short tasks, such as blinking a LED, is easy ... multi-tasking long tasks, such as scrolling a text message, requires a different approach.
If you look at the video you will see lots of things happening while the text slowly scrolls across the screen. In order to create the illusion of multi-tasking, long tasks must be rewritten as a series of smaller tasks such that a different section of the code is implemented each time through the loop.
This is achieved through the use of “static” variables [1] to track how far the task has progressed. My “scrollMessage()” and “bargraph()” functions demonstrate this technique.
[1]
When a subroutine is called all “variables” forget their previous values unless they have been declared “static”, in which case the previous value is available when the subroutine is next called.
Step 4: The Task Scheduler
The secret to setting the flags is to create a 1 millisecond (mS) “heartbeat” using one of the Arduino Uno R3 timers. I chose to use Timer/Counter 2 (8-bit) which leaves Timer/Counter 1 (16-bit) free for other tasks.
1mS is achieved by dividing the 16,000,000Hz clock by 128 to obtain an 8 microsecond (uS) clock interval. If we now count these 8uS pulses and generate an interrupt when the count reaches 125 then 125*8uS (or 1mS) will have elapsed [1].
The flags are set by placing the following code in the interrupt service routine (ISR):
// ----- count the number of milliseconds that have elapsed Counter1++; Counter2++; // ----- print “Hello World!” if (Counter1 > 1000 - 1) //1 second { Counter1 = 0; Flag1 = true; //the loop(){} sees this flag and prints the message } // ----- toggle LED if (Counter2 > 300 - 1) //300 milliseconds { Counter2 = 0; Flag2 = true; //the loop(){} sees this flag and toggles the LED }
Once a counter reaches its target value the counter is cleared and a flag is set. The above code takes very little time to execute as there are very few instructions.
The main loop(){} sees every flag that the task scheduler sets and performs the task.
Adding additional tasks is easy... simply create an additional counter and flag, then mimic the above code.
Photo 1 shows the relationship between the three tasks that my arduino is performing.
Notice that:
- sometimes loop(){} has no tasks to perform.
- sometimes loop(){} only performs one or two tasks.
- each of the tasks is called a different number of times
- the bar graph is advanced every 100mS
- the text is scrolled every 250mS
- the LED toggles every 300mS
[1]
We actually load 125-1=124 into the “compare match register” because the interrupt doesn’t occur until the next (125th) clock pulse.
Step 5: Key Points
To successfully multi-task:
- Avoid the delay() or delayMicroseconds() functions. Instead
- create an interrupt driven “heartbeat” and
- use a task scheduler.
- Long tasks should be treated as as a series of smaller “chunks” with
- “static” variables tracking the progress.
Click here to view my other instructables.
14 Comments
10 months ago
Great explanation huge thanks 👍
Reply 10 months ago
Glad you like it ... thanks for commenting :)
Question 4 years ago on Step 4
I means that like many devies are connected to the arduino and i want that it switch on one device and takes required data and then switch off that and hen turns on other device and then repeats this process for whole devices which are connected.
Answer 4 years ago
You need the following information before attempting to multi-task:
(1) how long does each task take?
(2) can the task be broken into bits?
(3) how often does each task need to be performed?
Good luck :)
Question 4 years ago
I want to ask that I hade three or more different Projects(Like in the lcd display project you have working on a single thing LCD but I want to muti-Tasking between different Modles) like i made for measuring Temperature,Humidity andand a GPS which provide the corrdenates and i want to get all these information at the same time Projects on single Arduino so that it divides time between them and run all of them how we can do this.
Answer 4 years ago
You need the following information before attempting to multi-task:
(1) how long does each task take?
(2) can the task be broken into bits?
(3) how often does each task need to be performed?
Good luck :)
Question 4 years ago on Step 2
Great explanation. I have a project where I'm flashing 110 RGB lights (essentially 330 different LEDs), in various patterns on a star shape object I made. Every one of my functions has delays between the on and off of the light flashing, and I have, so far, about 10 of these functions., eg here's one function.
void BlinkSpinAccel()
{ for (byte k = 0; k < 3; k++)
{ PORTB = rgb[k];
for (int i = 0; i < 10; i++) //
for (int p = 0; p < 8; p++)
{ CS.portWrite(p, 0xFFFF);
delay(DelayArray[i]);
CS.portWrite(p, 0x0000);
delay(DelayArray[i]);
}
}
}
How should I approach this using your interrupt technique? I want to simultaneously rotate one bunch of LEDS one directions and another bunch the other direction. The function above has an array with increasing values in it so that the delay changes on every pass, but most delays are just "delay 200;" I'm using centipede (CS) library for my I2C commands, writing to 7 port expanders. in an LED matrix. (Port B handles the 3 colors).
Answer 4 years ago
Thank you for your comment :)
You could try an array-of-arrays-of-arrays" such as "lamps[pattern][delay][direction]".
Such an array would allow any number of different of delays, and directions, to be applied to any pattern. Each lamp delay would require the use of "static" counters.
Good luck ...
5 years ago
Thank you,
Great explanation
Reply 5 years ago
Thank you :)
5 years ago
Wow! This is a very good explanation. I like a lot the way you explain.
It's a "Arduino for dummies". Thank you so much.
Reply 5 years ago
Thank you for your feedback. It's nice to know that the article was helpful :)
5 years ago
Great instructable, well done.
Reply 5 years ago
Thank you :)