- Muchas notas - Fran Acién

20240506 - FreeRTOS and OOP in CPP

Lately I am having a lot of trouble with C++ and understanding static functions. I think I found a really elegant way to mix both concenpts: FreeRTOS and dinamic memory with OOP.

I was inspired by this CPP code that do that pretty nice.

Here is my code:

/*
This file contains the code for executing on the incoming commands from MC
*/

#include <Arduino.h>
#include <executor/Executor.h>
#include "sbcPacket/sbcPacket.h"

/* Constructor Set parameters*/
Executor::Executor(LoadSwitch* loadSwitches, Delayer* delayer){
    this->loadSwitches = loadSwitches;
    this->delayer = delayer;

    // Create FreeRTOS semaphore and task
    _asq_sem = xSemaphoreCreateBinary();
    _runSequenceTask = xTaskCreate(
        Executor::runSequenceHandlerTask,
        "runSequenceHandlerTask",
        500,
        this,
        2,
        &runSequencetaskHandle
    );

    configASSERT(_runSequenceTask);
}

void Executor::RunCommand(const uint8_t *buffer, size_t length) {
    ...
}


void Executor::runSequence(){
    while (true) {
        if (xSemaphoreTake(_asq_sem, portMAX_DELAY) == pdTRUE) {
            for (int i = 0; i < _sequenceLength; i++) {
                ...

                RunCommand(command, size);
            }
        }
    }
}

void Executor::runSequenceHandlerTask(void* instance){
    auto* app = static_cast<Executor*>(instance);
    app->runSequence();
}

void Executor::startSequence(){
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    xSemaphoreGiveFromISR(_asq_sem, &xHigherPriorityTaskWoken);

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); 
}
#ifndef EXECUTOR_H
#define EXECUTOR_H

#include <STM32FreeRTOS.h>

#include "bControllable/bControllable.h"
#include "loadSwitch/loadSwitch.h"
#include "delayer/delayer.h"

#define SEQ_MAX_LENGTH 50

class Executor {
    LoadSwitch* loadSwitches;
    Delayer* delayer;
    public:
        Executor(LoadSwitch* LoadSwitches, Delayer* delayer);
        void RunCommand(const uint8_t *buffer, size_t length); // probably there will be more overloaded versions of RunCommand
        
        void buildSequence(const uint8_t *buffer, size_t bufferLength);   // For building the autosequence
        void startSequence();                                             // Start the saved autosequence
        void runSequence();
    private:
        // List of the dynamic arrays that are used to contain the autosequence
        int _asqControllablesArray[SEQ_MAX_LENGTH] = {0};
        int _asqValueArray[SEQ_MAX_LENGTH] = {0};
        int _sequenceLength = 0;

        static void runSequenceHandlerTask(void* instance);
        SemaphoreHandle_t _asq_sem;      // Semaphore used for executing the autosequence
        portBASE_TYPE _runSequenceTask;
        TaskHandle_t runSequencetaskHandle;


};

#endif /* EXECUTOR_H */

If you dont want to analize the code. Basically is that you are passing the object (this) as a parameter to FreeRTOS, and then to run the functions. So the HandlerTask is static, but accessign dynamic objects. For doing this is necessary to use a TaskHandle_t.