Introduction
for latest version with update ,
you can visit the code gitHub:
https://github.com/ArabicRobotics/ArduinoMultithreading
Arduino is a very famous device... you can control anything using it and computer. Arduino has many capabilities and controllers, pins which can do tasks, this device can execute one instruction per time, which is great if you will use just one pin or even use one loop which is provided by default in the IDE. But what if you have several items and pins you want to read from and write to, or even make several loops at the same time which is impossible with Arduino because it does not support multitasking.
Here Is How Arduino Currently Works
Arduino finishes one task, loop, function, then moves to the next. Then, moves to the next one and so on.
What Is the Problem With That ?
The issue in this scenario is you cannot go through all running tasks or even control the Arduino anyway like listening to serial port, which will be impossible until the current task finishes.
Background
In my last project, I needed to run several loops at the same time. while listening to the serial port and make an action based on serial reading data. These loops deal with several pins and do actions in Arduino board. So I created this trick to force Arduino to do so, and I decided to share it with you.
Solution
I solved this problem is to create a Structure, I named it "State
". This state keeps the state for every running loop or function and keeps moving to the next one with timeout for everyone... so it can handle more than one segment with a delay for each one. When you read and apply this technique, you can run your code in this way in Arduino:
Using this code, you can as an example: using Arduino Pins, read/write run parallel loops, read from and write to ports like Serial port or run any Arduino commands all together without waiting for any loop to finish.
Core Idea
Code based on State
Structure, which keeps details about task, loop or function like:
Task variables
local variables (optional) Start Time
which is start execution time for the task End time
is the end time to run task and move to the next IsEmpty
which identifies if this task is running or not
When the code runs, we start this state for every loop, so we can pause the loop (and keep its state and local variables) and run others, then back to it with its last state and so on.
Using the Code
For more flexibility, I added the feature of time out and the delay between tasks, meaning you can control when the loop makes iteration even if the task in the turn, using: Previous Millis and current Millis.
This code is based on main structure "state
" as mentioned.
The Code Is Divided Into Four Parts
- Part 1 - Task State Structure
- Part 2 - Example Function contains loops using State Structure
- Part 3 - Call the function calls the state enabled function into the Arduino main loop
- Part 4 - (optional) Serial Example, adding listening to Serial Port while running the State-enabled function
Part 1: Task State Structure
Structure State Members
Variables
Mainly, it contains the time to run and duration to run loops and duration of pause to run other piece of codes, as a very important variable: (Is Empty), which is flag to detect if the function 1st run or not , which allows us to initialize values before running the state.
Recommended variables, usually the operation has loop (i
or j
) or both, if it has, then we can declare variables in the state as well.
Full Initialization Code
long OnTime = 20; long OffTime = 20; unsigned long previousMillis; int iloopState = 0; int jloopState = 0; unsigned long currentMillis = millis(); bool IsEmpty = true; A flag that process started or not yet ,
State Member Functions
Mainly, it contains three functions:
KeepState()
which saves the running function state until next round RestoreSession()
restores the values to continue execute reset()
resets the state to initial values
Functions Body
void keepState(int looperi = 0, int looperj = 0)
{
IsEmpty = false;
iloopState = looperi;
jloopState = looperj;
previousMillis = millis(); }
void restoreSession()
{
currentMillis = millis();
}
void reset(LightingMood mood = liIdel)
{
iloopState = 0;
jloopState = 0;
IsEmpty = true;
}
Part 2: Example Function
In this example, we will blink two LEDS at the same time with some delay between them to show the state working fine.
Mainly, function will start by restoring the execution state, then do some work and keep the session state and local variables for the next run and exit the function. Here is an example image to clarify the idea:
Also check for Empty is very important (IsEmpty
) to init variables of the state if it is fun for the first time.
Here is a full example of using the state and run Blinking example by State way and beside other codes. Using structure to blink led: First, we define a state before using it.
state blinkState;
Then, use it in the body of function.
void blink()
{
if (blinkState.IsEmpty == true)
{
On(); blinkState.iloopState = 1; blinkState.keepState(blinkState.iloopState); return; }
if (blinkState.iloopState==1) {
if (millis() < blinkState.previousMillis +
blinkState.OnTime) return; Off(); blinkState.iloopState = 0; blinkState.keepState(blinkState.iloopState); return; }
if (blinkState.iloopState==0) {
if (millis() < blinkState.previousMillis + blinkState.OffTime) return;
On(); blinkState.iloopState = 1; blinkState.keepState(blinkState.iloopState); ; return;
}
};
Several State Functions
To create more than one state enabled function, you have to declare one state for everyone like:
state blinkState; state blink2State; blink2State.OffTime = 10;
Part 3: Call the Function
To call this function in main Arduino loop, you can just call blink function like which means the loop will execute all statements before and after blink()
without getting stuck on the blinking loop.
void loop() {
blink();
}
For more than one function, we can run it together like this way:
void loop() {
blink();
blink2();
}
Part 4: Serial Example
You can do the same to serial Read
like this example function:
unsigned long lastMillis = millis();
unsigned long prevTimeOut = millis();
int timeOut = 3000;
void serialLoop()
{
if (unsigned long currentTime = millis() > prevTimeOut +timeOut)
{
while (Serial.available())
{
Serial.println("Serial Available : ");
Serial.println();
x = (char)Serial.read();
}
prevTimeOut = millis();
Serial.println("Time Out");
}
else
{
}
}
Adding serial listening while other functions are running in the main loop:
void loop() {
blink(); serialLoop();
}
Points of Interest
With this trick, you can go around and force the Arduino to break the time between loops and functions without waiting for one to finish a task or loop.
History
You can add as many tasks as you like: loops for lights feed in and out, blink and led strips all at the same time by the same way. I will add the code with more features in GitHub: GitHub ArduinoMultithreading.