FreeRTOS for multitasking with an Arduino-based PLC

Multitask in Arduino IDE environment using Free RTOS
March 20, 2019 by
FreeRTOS for multitasking with an Arduino-based PLC
Boot & Work Corp. S.L., Support Team

Introduction

The Industrial Arduino IDE and environment has many drivers and libraries available within an arms reach, but the Arduino environment is limited to just setup() and loop() and doesn't support multi-tasking effectively.

Most operating systems appear to allow multiple programs or threads to execute at the same time. This is called multi-tasking. In reality, each processor core can only be running a single program at any given point in time. A part of the operating system called the scheduler is responsible for deciding which program to run when and provides the illusion of simultaneous execution by rapidly switching between each program.

This is a simple, easy to use, and robust Free RTOS implementation that can just shim into the Arduino IDE as a Library and allow the use of the best parts of both environments, seamlessly. 

Free RTOS implementation into the Arduino IDE


Requirements


 

FreeRTOS usage in Industrial Automation 

Out of the box, the Arduino IDE does not support multi-tasking effectively. the good news it's easy to add support via a library to the Arduino IDE.

Most operating systems appear to allow multiple programs or threads to execute at the same time. This is called multi-tasking. In reality, each processor core can only be running a single program at any given point in time. A part of the operating system called the scheduler is responsible for deciding which program to run when and provides the illusion of simultaneous execution by rapidly switching between each program.

The first step then is to install the FreeRTOS library for Arduino in our IDE.

If you have never installed an Arduino IDE additional library, you can follow the steps shown on the Arduino website.

Now that the library is already installed in our Arduino IDE, we can access the FreeRTOS Arduino tasks examples that the library itself provides us.

To access the FreeRTOS Arduino tutorial examples you can follow the image shown below:


 FreeRTOS Arduino tutorial examples


Software 

This is the AnalogRead_DigitalRead example sketch. 

I0_0 = Digital input (Switch/Button)

I0_1 = Analog input (Sensor/Potentiometer)


 #include <Arduino_FreeRTOS.h>

#include <semphr.h>  // add the FreeRTOS functions for Semaphores (or Flags).


// Declare a mutex Semaphore Handle which we will use to manage the Serial Port.

// It will be used to ensure only only one Task is accessing this resource at any time.

SemaphoreHandle_t xSerialSemaphore;


// define two Tasks for DigitalRead & AnalogRead

void TaskDigitalRead( void *pvParameters );

void TaskAnalogRead( void *pvParameters );


// the setup function runs once when you press reset or power the board

void setup() {


  // initialize serial communication at 9600 bits per second:

  Serial.begin(9600);

  

  while (!Serial) {

    ; // wait for serial port to connect.

  }


  // Semaphores are useful to stop a Task proceeding, where it should be paused to wait,

  // because it is sharing a resource, such as the Serial port.

  // Semaphores should only be used whilst the scheduler is running, but we can set it up here.

  if ( xSerialSemaphore == NULL )  // Check to confirm that the Serial Semaphore has not already been created.

  {

    xSerialSemaphore = xSemaphoreCreateMutex();  // Create a mutex semaphore we will use to manage the Serial Port

    if ( ( xSerialSemaphore ) != NULL )

      xSemaphoreGive( ( xSerialSemaphore ) );  // Make the Serial Port available for use, by "Giving" the Semaphore.

  }


  // Now set up two Tasks to run independently.

  xTaskCreate(

    TaskDigitalRead

    ,  (const portCHAR *)"DigitalRead"  // A name just for humans

    ,  128  // This stack size can be checked & adjusted by reading the Stack Highwater

    ,  NULL

    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.

    ,  NULL );


  xTaskCreate(

    TaskAnalogRead

    ,  (const portCHAR *) "AnalogRead"

    ,  128  // Stack size

    ,  NULL

    ,  1  // Priority

    ,  NULL );


  // Now the Task scheduler, which takes over control of scheduling individual Tasks, is automatically started.

}


void loop()

{

  // Empty. Things are done in Tasks.

}


/*--------------------------------------------------*/

/*---------------------- Tasks ---------------------*/

/*--------------------------------------------------*/


void TaskDigitalRead( void *pvParameters __attribute__((unused)) )  // This is a Task.

{

  /*

    DigitalReadSerial

    Reads a digital input on pin 2, prints the result to the serial monitor

    This example code is in the public domain.

  */

  // digital pin 2 has a pushbutton attached to it. Give it a name:

  uint8_t pushButton = I0_0;

  // make the pushbutton's pin an input:

  pinMode(pushButton, INPUT);

  for (;;) // A Task shall never return or exit.

  {

    // read the input pin:

    int buttonState = digitalRead(pushButton);

    // See if we can obtain or "Take" the Serial Semaphore.

    // If the semaphore is not available, wait 5 ticks of the Scheduler to see if it becomes free.

    if ( xSemaphoreTake( xSerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )

    {

      // We were able to obtain or "Take" the semaphore and can now access the shared resource.

      // We want to have the Serial Port for us alone, as it takes some time to print,

      // so we don't want it getting stolen during the middle of a conversion.

      // print out the state of the button:

      Serial.println(buttonState);


      xSemaphoreGive( xSerialSemaphore ); // Now free or "Give" the Serial Port for others.

    }


    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability

  }

}


void TaskAnalogRead( void *pvParameters __attribute__((unused)) )  // This is a Task.

{

  for (;;)

  {

    // read the input on analog pin 0:

    int sensorValue = analogRead(I0_1);


    // See if we can obtain or "Take" the Serial Semaphore.

    // If the semaphore is not available, wait 5 ticks of the Scheduler to see if it becomes free.

    if ( xSemaphoreTake( xSerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )

    {

      // We were able to obtain or "Take" the semaphore and can now access the shared resource.

      // We want to have the Serial Port for us alone, as it takes some time to print,

      // so we don't want it getting stolen during the middle of a conversion.

      // print out the value you read:

      Serial.println(sensorValue);

      xSemaphoreGive( xSerialSemaphore ); // Now free or "Give" the Serial Port for others.

    }

    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability

  }

}


Programmable Logic Controllers

Open Source based solutions for IoT, automation, monitoring and control

PLC Based on Arduino, Raspberry Pi and ESP 32



​Search in our Blog

FreeRTOS for multitasking with an Arduino-based PLC
Boot & Work Corp. S.L., Support Team March 20, 2019

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>