The site for those who love tinkering with Arduino, Raspberry Pi & Co.

Arduino and designing with Tasks

by

Arduino and Task: task-based design to make it easier for us to manage the project code

In the course of a maker's life, a person could come across projects that could turn out to be bigger than what one might initially think. In this article, I'm going to give some basic knowledge on the concept of task-based architectures, which allow you to simplify coding. I also add that to better understand the examples that I will illustrate it will be necessary to have a smattering of object oriented programming.

What are the tasks?

Tasks are units of work that must be carried out. Tasks can be recursively divided into sub-tasks, up to a point where the sub-task can no longer be divided. In this case we will have an atomic task.

How could a task be represented?

At this point we need to make a distinction between the design phase and the implementation phase. In the design phase we must take into account that the task is a dynamic entity and to design it we can represent its operation through a finite state diagram. In the implementation phase, the task is represented by a class for each expected task.

What are the advantages of the tasks?

The advantages at the implementation level are several: they make the code more understandable, help locate errors during debugging and have greater support for modifiability and reuse. Furthermore, using tasks can have a clear separation of duties.

How to perform multiple tasks in one tab?

If we have decided to use an architecture that includes tasks, it implies that we will be dealing with at least more than one task. How can we run multiple tasks (especially if performed on an Arduino uno board)? Well, a possible solution is to create a small scheduler that runs all our tasks. In this case, a cooperative Round Robin scheduler was used, ie at each cycle all the tasks are executed one after the other. Priority in this scheduler is given based on the ordering of tasks in the scheduler queue. Obviously, the scheduler is also represented by a class and the task queue is implemented through a vector.

Below we will see the code of the .ino file and how it is set up.

#include "Scheduler.h"
#include "Task.h"
#include "PrimoTask.h"
#include "SecondTask.h"

Scheduler scheduler;

void setup () {
    Serial.begin (9600);
    scheduler.init (50); // milliseconds
    // PrimoTask is a class that models the implementation of the first task 
    Task * task0 = new PrimoTask ();
    task0-> init (50);
    // task0 is put first in the scheduler queue, therefore it has higher priority
    scheduler.addTask (task0); 
    // SecondTask is a class that models the implementation of the second task created
    Task * task1 = new Second Task ();
    task1-> init (100);
    scheduler.addTask (task1);
}

void loop () {
    scheduler.schedule ();
}

Note that once the code has been written in the .ino file, it will only be modified if new tasks are added or if an existing one is removed. This is possible thanks to the fact that the behavior of the scheduler and the tasks is hidden through the encapsulation of object-oriented programming.

An example of scheduler implementation is presented below. Generally the code of the .h and .cpp files are in 2 separate files but for reasons of length I put it all in one block.

/ * start file Scheduler.h * /
#ifndef __SCHEDULER__
#define __SCHEDULER__

#include "Timer.h"
#include "Task.h"

#define NUM_TASKS 10

class Scheduler {
    public:
        void init (int basePeriod);  
        bool addTask (Task * task);  
        void schedule ();
    private:
        int basePeriod;
        int numTasks;
        Task * taskLis[NUM_TASKS]t;
        Timer timer;
};
#endif

/ * Scheduler.cpp file start * /
#include "Scheduler.h"
/ * initializes the scheduler, the period is the time how often it should check
which task should start * /
void Scheduler :: init (int basePeriod) {
    this-> basePeriod = basePeriod;
    timer.setupPeriod (basePeriod);
    numTasks = 0;   
}
/ * Add tasks to the list of tasks to manage * /
bool Scheduler :: addTask (Task * task) {
    if (numTasks <NUM_TASKS) {
        taskList[numTasks] = task;
        numTasks ++;
        return true;
    }
    return false;
}
/ * Here it does it all !!
1 waits for the evetnto from the timer to start 
2 for each task in the queue controls how often it should run
and executes if that time has passed, otherwise it does not execute it and passes to the next
* /
void Scheduler :: schedule () {
    timer.waitForNextTick ();
    for (int i = 0; i <numTasks; i ++) {
        if (taskList[i]-> updateAndCheckTime (basePeriod)) {
            taskList[i]-> tick ();            
        }
    }
}

As we can see, each task has been implemented in an assesting class, in the example the classes are PrimoTask and SecondoTask. These 2 classes derive, in turn, from an abstract class called Task. This was done to have the same interface exposed by both classes and to be easily managed by the scheduler class using their generalization.

How can we make 2 tasks communicate?

Having multiple tasks, it may be necessary for these tasks to exchange information. The simplest method is to create global variables, accessible by all tasks, which keep certain information in memory. We could not use this method in the presence of a preemptive scheduler as there may be some critical runs. Another way to exchange information between tasks could be by implementing an asynchronous message exchange model. To have a centralized view of the variables for passing values between tasks, we can create a class to manage the entire context of the application with the variables we need inside without losing them for the immense amount of files that we could have in our project.

This article is intended to give a "theoretical" smattering of task-based programming. Obviously there would be several aspects mentioned that could be explored. The goal I set myself with this article is to give the reader an extra tool to better manage projects that require highly complex software.

Ti è rimasta una domanda o un dubbio?

Iscriviti e chiedi nella community

Leave a Reply

Your email address will not be published. Required fields are marked *