Skip to main content

How to use Luos engine with FreeRTOS

Confirmed

What you will learn:

If you are used to develop your embedded system with RTOS, you can leverage Luos engine's features without changing the way you design your system. Indeed, Luos engine fits perfectly well with RTOS and this tutorial aims to help you set up your project and understand the benefits it brings.

RTOS
Distributed System
info

This tutorial uses FreeRTOS, but the main concepts can be used for any other RTOS. The configuration options may vary depending on the RTOS API you are using.

caution

This tutorial considers the reader as a FreeRTOS user. This is not a tutorial to learn how to use FreeRTOS.

In this tutorial, we will see how to use Luos with an RTOS and discover the benefits of this combination.

1. Set up a PlatformIO project

In this tutorial, we will use the examples available in the Luos engine's repository. You can download and unzip it or clone it using:

Git clone https://github.com/Luos-io/luos_engine.git

As a quick reminder, you will find in this project:

  • a src/ folder containing a main.c whose main purpose is to call services and Luos engine's initialization functions. This folder also contains the file stm32f0xx_it.c in which we define interrupt handlers.
  • a lib/ folder containing services source files.

2. Use FreeRTOS in your project

Open Visual Studio Code and open a Led project corresponding to your master board in Luos_engine/Examples/Projects/YOUR_BOARD.

caution

If you are not comfortable with the opening and compiling of a Luos engine project, please follow the Luos Get started tutorial first.

Now we need to import FreeRTOS source files in our project. To do that we will use the PlatformIO registry and add a dependency to FreeRTOS. Then at compilation time PlatformIO will download it and include it on our project.

To do that open the platformio.ini file of your project and add a FreeRTOS dependency:

lib_deps =
Luos_engine@^2.2.0
Led
bojit/PlatformIO-FreeRTOS

To properly work, this lib needs to know which MCU family you are working on (like Luos engine), so you have to add a compilation option to define your platform. For example, for STM32F0 you have to add the compilation flag -DSTM32F0.

info

More details about it on the PlatformIO library's readme.

In this tutorial, we will also use CMSIS_RTOS_V2 to have a generic RTOS API. You will have to find the lib corresponding to your provider's and MCU. If you prefer to directly use FreeRTOS API, you will have to adapt the code of this tutorial, no big deal...

info

You can find a ST version in our FreeRTOS example.

3. FreeRTOS configurations

Working with Luos FreeRTOS doesn't require any specific configuration, except for one thing: you must configure the available options in the file FreeRTOSConfig.h. FreeRTOS needs to handle three interrupters handlers (PendSV, SVC and systick handlers), so you must not redefine these three handlers (For STM32 user they are in stm32f0xx_it.c).

    #define vPortSVCHandler     SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
info

If you want more information about the options displayed in FreeRTOSConfig.h, please check the dedicated page.

The only exception of this standard configuration is that Luos engine uses the systick value provided by the HAL of your ship (allowing Luos to work in a bare metal configuration). By default FreeRTOS will pre-empt this systick and manage its own. To keep Luos working we have to provide this systick value to the HAL level

You have three solutions to solve this:

  • You write the systick IRQ handler by yourself increment the HAL systick then call the RTOS systick function. (We use this solution on our examples)
  • You can override the weak HAL systick function and use the RTOS systick instead of the HAL one.
  • You can use the RTOS hook option calling a callback at each systick allowing you to increment the HAL systick manualy.

Now we have to call the kernel routines from our main() function. We could directly use FreeRTOS functions, but ARM defined a standard API to interface with RTOS capabilities called CMSIS_RTOS_V2 and we will use it.

In our main, we have to remove all the Luos and services functions, becaus e we will transform them into tasks. Then we can call the OS API in our main.c file:

    #include "cmsis_os.h"

int main(void)
{
HAL_Init();

SystemClock_Config();

osKernelInitialize();

osKernelStart();
}

osKernelInitialize() sets some FreeRTOS internal variables and osKernelStart() launches the scheduler by calling vTaskStartScheduler().

Let's recall what we've done so far:

  • We have used a Luos LED example as a base project.
  • We imported FreeRTOS sources, configured them and started the kernel from the main() function.

If you compile and load this code in the target, nothing will happen for a simple reason: we launched the scheduler but the kernel has nothing to schedule because we don't make a task running our LED. Let's fix that by creating some tasks.

4. Create Luos tasks

Because we based our code on a LED project, on the lib/ folder, you should have a led service ready to be used.

Lets create a new file called freertos.c on your src/ folder. We will use this file to creates some tasks for our LED.

freertos.c:

// RTOS includes:
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"

// Luos engine includes:
#include "luos_engine.h"
#include "led.h"

// Luos engine task attributes:
const osThreadAttr_t LuosTask_attributes = {
.name = "LuosTask",
.stack_size = 128 * 4,
.priority = (osPriority_t)osPriorityNormal,
};

// Led service task attributes:
const osThreadAttr_t LedTask_attributes = {
.name = "Led",
.stack_size = 128 * 4,
.priority = (osPriority_t)osPriorityNormal,
};

// Start tasks functions:
void StartLuosTask(void *argument);
void StartLedTask(void *argument);

// RTOS initialization functions:
void RTOS_Init(void);

void MX_FREERTOS_Init(void)
{
// Init Luos and services
Luos_Init();
Led_Init();

// Tasks creation
osThreadNew(StartLuosTask, NULL, &LuosTask_attributes);
osThreadNew(StartLedTask, NULL, &LedTask_attributes);
}

void StartLuosTask(void *argument)
{
while (1)
{
Luos_Loop();

taskYIELD();
}
}

void StartLedTask(void *argument)
{
while (1)
{
Led_Loop();

taskYIELD();
}
}

First, we included files we need to call the RTOS and Luos engine APIs. Then we created 2 structures: LuosTask_attributes and LedTask_attributes. Those structures are used by RTOS to configure heap size and priority for each task.

Here we have 2 tasks:

  • Luos task: it is used to manage the messages passing between services and to orchestrate them.
  • Led task: it is used to drive a simple LED you can access from any other service.

These tasks are initialized in RTOS_Init() with the osThreadNew() routine. Notice that we called Luos_Init() and Led_Init() just before creating threads allow Luos engine and the led service to be properly setup to run.

We've created 2 threads for 1 services because the first one is dedicated to handle Luos engine. The 2 threads have the same priority and that's important because the scheduler will place them in the same queue, running one after the other.

Of course you can create as many threads you want inside your service and deal with your code as you want.

One last thing, in each task routine, we call the service loop then we yield to the next threads. Indeed, FreeRTOS scheduler can be called in preemptive or cooperative mode. The systick will call the scheduler each milliseconds, but this implies that you will switch from one thread to the other at this period. This can be way too slow for most applications and we can improve the reactivity of the system by calling taskYield after each service loop routine.

5. Test your project

To test your project you can add a pipe and Gate packages (you can find available packages on the Gate_SerialCom project corresponding to your board, add the code in the lib/ folder and instanciate it by creating a new task in freertos.c.

Now you can build your project in VSCode and load it to your board. Once the board is flashed, you can use the pyluos-shell command on your computer to control your LED through python.

6. Main advantages

From a developer using an RTOS point of view, Luos engine allow you to synchronize your features accross multiple tasks and multiple nodes. You can see it as a distributed IPC (Inter Process Communication).

Now you can have multiple Services across multiple nodes running in parallell on your favorite RTOS and synchronized through Luos engine.

Luos engine brought you APIs to develop distributed applications with ease:

  • Develop each application in a service
  • manage messages between services through Luos engine API
  • manage and monitor your network with our SaaS tools

From a developer using Luos engine point of view, FreeRTOS can help to manage services scheduling to comply with hard real-time constraints.

The author: Nicolas C.

Embedded Engineer
Nicolas C.

Experienced engineer in embedded software and hardware engineering with strong skills in embedded Linux, digital and analog hardware, and radiocommunication.

A feedback? Let's discuss on Discord | Github