We are starting a new series of posts aimed at seeing how to use FreeRTOS on a processor like Arduino, or on an ESP866 or ESP32.
In this first post, we will make an initial introduction and see how to use FreeRTOS on an Arduino AVR. However, the rest of the posts in the series will be done using an ESP32, within the ESP8266/ESP32 section.
In the blog, we have frequently talked about how to achieve an “asynchronous” behavior in a processor like Arduino. We have seen everything from the most basic Approach to multitasking in Arduino. Blink without delay to libraries like Arduino AsyncTask Library.
If we talk about achieving asynchronous tasks, sooner or later we were going to come across FreeRTOS because it is one of the best ways to manage the execution of concurrent tasks in a processor.
But, before diving in, let’s start from the beginning by seeing why this obsession with asynchrony, and why FreeRTOS is going to be of great help to achieve it.
Why use FreeRTOS
When you carry out a medium or large-sized project, there are several parts of the program that act simultaneously. One of the most important aspects of project design is to see how these parts relate to and interact with each other.
For example, a robot will need to control the speed of the motors, measure distances, make decisions, display data on a screen, or transmit it remotely.
An important aspect is that not all of these tasks need the same timing. For example, controlling the speed of the motors will need to be done more frequently than, for example, data transmission.
The other important point is that not all of these tasks have the same priority. For example, attending to a collision alert or motor overheating will have a higher priority than, for example, displaying data on the screen.
Managing all of this can be complicated. This is where FreeRTOS comes into play by providing a framework for the coordination and management of this type of tasks.
What is FreeRTOS
As its name suggests, FreeRTOS (Free Real-time operating system) is a real-time operating system for embedded devices. It is distributed under the MIT license and available on the project’s website at https://www.freertos.org/
Let’s see what this means. On the one hand, like any operating system, we have a program that controls access to the hardware and on which the rest of the tasks are executed.
For its part, “real-time” means that, unlike the operating systems we are used to (Windows, Linux, Android, etc), this one is focused on having a very fine and precise control of time. Something that is dispensable in the case of a computer, but very important in an embedded system.
FreeRTOS provides various functionalities and tools such as tasks, semaphores, stacks, or a notification bus. We will see all these functions in due time in future posts.
It is a robust and consolidated project, whose first versions date back to 2003, and has been ported to more than 35 architectures. It is mainly developed in C, with a few functions in assembly. In addition, it was designed to have a low consumption of resources and fast execution.
How FreeRTOS works
As expected, within the “Core” of FreeRTOS operation we find the concept of “Task”. In short, a task is a wrapper around a function that we pass to FreeRTOS to manage.
Closely related we have the concept of Scheduler which is the part of FreeRTOS that is responsible for managing and executing the different tasks that we have defined.
For this, each task has a state that can be available, running, suspended, or blocked. The Scheduler changes the states according to the following flow.
These states are,
- Running: The task is using the processor
- Available: The task is available to be executed, but has not yet done so because another task is running
- Blocked: Basically the task is “waiting”. For example, because it has done a vTaskDelay, it is waiting for a semaphore, etc. The task will become available when an event occurs (temporary, timeout, external event)
- Suspended: The task is “stopped”, for which you have to explicitly call vTaskSuspend(). It will be reactivated using xTaskResume()
Although we say that in FreeRTOS several tasks can be executed “in parallel” this is not really true. A processor can only execute a single task simultaneously per available core / thread.
To get the tasks to be executed, or rather, in a “non-blocking” way, what an operating system does is distribute the time the processor dedicates to each of the tasks that demand it. Or in other words, among those that are “available” for execution.
To put it simply, let’s imagine that we have two functions (tasks) executed without FreeRTOS. In this case each of the tasks would be executed sequentially, without any grace or mystery.
However, if we start using FreeRTOS, things work “in parallel”. Thus, if we have two tasks of the same priority 1, called A and B, the execution would be as follows.
Since both tasks are available and have the same priority, FreeRTOS alternates between them so that they can be executed. The small yellow fragments that have appeared are the time required by the Scheduler to do its job, periodically.
On the other hand, if we have two tasks with different priorities 1 and 2, the result of this execution schedule would be as follows.
As we can see, when the task with priority 2 becomes available it is executed ahead of the lower-priority task. The latter will have to wait until the higher-priority tasks become blocked or suspended.
Notation and syntax in FreeRTOS
One of the things that is most off-putting about FreeRTOS for novice users is the use of Hungarian notation for the names of variables and functions. In this way, the names of FreeRTOS variables and functions are preceded by their type, according to,
- Void is preceded by a v
- Char is preceded by c
- Short is preceded by s
- Long is preceded by an l
- Float is preceded by an f
- Double is preceded by a d
- Structures and variable types are preceded by an x
In addition to this,
- Pointers are preceded by an additional p
- Unsigned is preceded by an additional u
So, for example, you will see ‘xTaskCreate’, instead of simply ‘TaskCreate’. The initial ‘x’ indicates that this function returns a structure.
Hungarian notation is a practice whose usefulness was debatable at the time and is now totally discouraged. But FreeRTOS is a development of almost 20 years and, at the time, they made the decision to use it.
It would currently be unmanageable to change FreeRTOS to eliminate this anachronism because it would break compatibility with thousands and thousands of projects.
So, tough luck, we have to swallow it. Simply don’t be intimidated by the names of these functions and get used to ignoring the first letters.
More information on the FreeRTOS page - Free RTOS Coding Standard and Style Guide
FreeRTOS on Arduino
FreeRTOS is designed to run on processors slightly more powerful than an Arduino. Thus, as we will see in future posts, it is the standard OS on our beloved ESP8266 and ESP32.
However, it is possible to use FreeRTOS on Arduino under AVR architectures (Uno, Nano, Leonardo, Mega) thanks to the Arduino FreeRTOS Library developed by Richard Barry and maintained by Phillip Stevens along with other collaborators.
This library, available at this link, uses the Arduino Watchdog in a very original way to run the Scheduler at configurable intervals from 15ms to 500ms.
We will not deny that, despite the merit of this library for AVR processors, FreeRTOS fits “by force” on an Arduino. We will find it much more useful on the ESP8266 and ESP32, where it is included in a complete and native way.
Let’s see a basic example of “Hello World” with FreeRTOS. That, in the world of processors, is the equivalent of a “Blink”, that is, making an LED flash.
void TaskBlink(void *pvParameters)
vTaskDelay( 500 / portTICK_PERIOD_MS );
vTaskDelay( 500 / portTICK_PERIOD_MS );
, "Blink" // Task name
, 128 // Task stack size
, 2 // Priority, with 0 being the lowest priority, and 3 being the highest
, NULL );
If we upload this to an Arduino board, we will see, on the one hand, that the board’s LED turns on and off every 250ms and, at the same time, we are sending the value “millis” through the serial port.
In the code, we have simply created and associated a TaskBlink task responsible for making the board’s LED flash. On the other hand, we have the loop() which, when using FreeRTOS, internally becomes a task.
For now, we are going to leave it here, as an introduction. Today, we are not going to go into more detail (oh, what a shame). Although, of course, there is still much to explain about Tasks, Queues, Semaphores.
But, we will see all of this in the following FreeRTOS dedicated posts where we will be based on the ESP32 for its development. In these cases, it is where it is in its natural environment and we can unleash all its potential.
However, many of the things we see in the case of the ESP32 are applicable to Arduino thanks to the great Arduino FreeRTOS Library. Until next time!