Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Arduino Multitasking Using Loops

5.00/5 (5 votes)
13 Dec 2018CPOL5 min read 46.5K   469  
Simple way to create a Multitasking code for your Arduino project without using any external library.

Image 1

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.

Image 2

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:

Image 3

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.

Image 4

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

Image 5

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
C++
///////////// Timer  related
long OnTime = 20;     // it the time to start working in the task  milliseconds of on-time
long OffTime = 20;    // Duration of execution time for this task to move to the next.
//long OffTime=100;   // milliseconds of off-time
unsigned long previousMillis;            // Will store last time led was updated ..
int iloopState = 0;   // i loop (Recommended )
int jloopState = 0;   // j loop (recommended)
unsigned long currentMillis = millis();  //  Current time by Millisecond.
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
C++
void keepState(int looperi = 0, int looperj = 0)
{
     IsEmpty = false;
     iloopState = looperi;
     jloopState = looperj;
     previousMillis = millis(); // will store last time led was updated
}
 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:

Image 6

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.

C++
state blinkState; //declare state structure for the blink Function use.

Then, use it in the body of function.

C++
void blink()
{
  if (blinkState.IsEmpty == true)
  {       
  On();  //Set the led to custom function you declare like On() 
         //or by built-in functions for example : digitalWrite(LEDnumber,HIGH)
  blinkState.iloopState = 1; //we use the iloopsstate like 
                             //identify On/off state ( 1 = On , 0= off)
  blinkState.keepState(blinkState.iloopState);// store the iloopState 
                  // (1 in this case)  to the state, save it and return 
  return;         //return to continue other work.
  }
  if (blinkState.iloopState==1)// check if the led is on, 
                  // we should check for time to off, so we can turn it off or else return.
  {
  if (millis() < blinkState.previousMillis +  
  blinkState.OnTime) return; //check for the state time if the current time  
                             //exceeds the time to make the led on so we should switch it off 
                             //or else return to run other functions until this time come.
    Off();                   //Set the led to custom function you declare like Off() 
                             //or by built-in functions for example : digitalWrite(LEDnumber,LOW) 
  blinkState.iloopState = 0; // set the current state to off.
  blinkState.keepState(blinkState.iloopState); // keep or save the state.
  return;//return to continue other work.
  }
         
  if (blinkState.iloopState==0)  // Here the state started and the led is off, 
                                 // then we should check for time to turn it off or else return.
  {
  if (millis() < blinkState.previousMillis +  blinkState.OffTime) return;                  
  On();   //Set the led to custom function you declare like On() or by built-in functions for example 
  blinkState.iloopState = 1;     //set the state that the led is on.
  blinkState.keepState(blinkState.iloopState);	; // keep or save the state.
  return;
  }
};
Several State Functions

To create more than one state enabled function, you have to declare one state for everyone like:

C++
state blinkState;         // declare state structure for the blink Function use.
state blink2State;        // to use in another function...
				          // then you can Change data and timing for  blink2State :
blink2State.OffTime = 10; // and so on...

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.

C++
void loop() {
      // statements ...
      blink(); // this function will blink the leds without blocking
               // other statements in the main loop
      //statements ...

        }

For more than one function, we can run it together like this way:

C++
void loop() {
      // statements ...
      blink();  //this function will blink the leds without blocking
                //other statements in the main loop

      blink2(); // you create another one using blink2State
     //statements ...

        }

Part 4: Serial Example

You can do the same to serial Read like this example function:

C++
// define serial global timing variables: 
unsigned long lastMillis = millis();
unsigned long prevTimeOut = millis();
int timeOut = 3000;
void serialLoop() 
    {    
			if (unsigned long  currentTime = millis() >  prevTimeOut +timeOut)
			{
			   //////////// try to Read 
			   
				while (Serial.available()) 
			   {
				 Serial.println("Serial Available : ");
				 Serial.println();
				 x = (char)Serial.read();
			   }
				  prevTimeOut = millis();
				 Serial.println("Time Out");
			  }       
			  else
			  {
			   }   
			/////////////time out           
	}

Adding serial listening while other functions are running in the main loop:

C++
     void loop() {
           // statements ...
           blink();       // this function will blink the leds without blocking 
                          // other statements in the main loop 
		   serialLoop();  // This will be called every 3 seconds 
                          // (can be modified using timeOut variable) 
		   
          //statements ...
               
             }

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)