I am learning FreeRTOS and how to create/manage/delete tasks from another tasks. I have created a simple program for my ESP32 microcontroller. I have serial_message_receive task that is permamently running in the background and waiting for serial messages to come. When serial message is received, I create other FreeRTOS tasks based on the messages:
"task1" - starts task1 "task2" - starts task2
My controller.cpp
#include "Controller.h"
Controller controller_obj;
//static void Thermostat::normal_operation_task2(Thermostat* thermostat_obj);
Controller::Controller(){
printf("Thermostat object created \n");
}
void Controller::begin(){
this-> old_state = INITIAL;
this->main_task_handle = NULL;
this->secondary_task_handle = NULL;
printf("Initialise the object with the default values \n");
//xTaskCreate(task1,"thermostat normal operation task",10000,this,1,NULL); //receiving commands from main uart
}
void Controller::State_change_handle(e_thermostat_state state)
{
this-> new_state = state;
if(this-> old_state != this-> new_state ){ // if the new state is different than current state, delete the task
printf("different state, delete the previous task \n");
if ( this ->main_task_handle != NULL){
vTaskDelete(this ->main_task_handle);
}
}
else{
printf("same task \n"); // the same task is set, dont do anything
return;
}
switch(this->new_state)
{
case MODE1:
printf("NEW STATE = MODE1 \n");
this-> old_state = MODE1;
xTaskCreate(task1,"MODE1",10000,this,1,&this->main_task_handle); // receiving commands from main uart
break;
case MODE2:
printf("NEW STATE = MODE2\n");
this-> old_state = MODE2;
xTaskCreate(task2,"MODE2",10000,this,1,&this->main_task_handle); // receiving commands from main uart
break;
default:
printf("state not recognised \n");
}
}
My controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "stdint.h"
#include "stdio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
enum e_thermostat_state
{
INITIAL,
MODE1,
MODE2,
MODE3,
MAX_STATES
};
class Controller
{
private://only accesible for class
int test1;
e_thermostat_state new_state;
e_thermostat_state old_state;
TaskHandle_t main_task_handle;
TaskHandle_t secondary_task_handle;
public:
//accesible outside class
Controller(); // INIT OBJECT
void begin();
void State_change_handle(e_thermostat_state state);
static void task1(void* parameters)
{
Controller controller_obj = *((Controller*)parameters);
//CHECK IF TASK3 IS ACTIVE. IF IT IS ACTIVE, ALWAYS DELETE IT WHEN TASK1 IS STARTED
if(controller_obj.secondary_task_handle != NULL){
printf("task3 is active, delete it");
vTaskDelete(controller_obj.secondary_task_handle);
}
for(;;)
{
printf("hello from task 1\n");
vTaskDelay(1000/portTICK_RATE_MS);
}
}
static void task2(void* parameters)
{
Controller controller_obj = *((Controller*)parameters);
for(;;)
{
printf("hello from task 2\n");
if(controller_obj.secondary_task_handle ==NULL){
printf("task3 started from task2");
xTaskCreate(task3,"MODE2",10000,&controller_obj,1,&controller_obj.secondary_task_handle);
}
vTaskDelay(1000/portTICK_RATE_MS);
}
}
static void task3(void* parameters)
{
Controller controller_obj = *((Controller*)parameters);
for(;;)
{
printf("hello from task 3 which is created from task 2\n");
vTaskDelay(1000/portTICK_RATE_MS);
}
}
//static void normal_operation_task(void *argument); // DECLARE DEFAULT THERMOSTAT STATES AND VARS
};
#endif
The program logic explained:
When I call class begin method
void Controller::begin(){
this-> old_state = INITIAL;
this->main_task_handle = NULL;
this->secondary_task_handle = NULL;
printf("Initialise the object with the default values \n");
//xTaskCreate(task1,"thermostat normal operation task",10000,this,1,NULL); //receiving commands from main uart
}
I initialise 2 task handle variables ( main_task_handle and secondary_task_handle).
- main_task_handle is used to handle main tasks ( the tasks that are created after the serial message is received)
- secondary_task_handle is used to handle the secondary task (task3
which is created inside task2).
Whenever I change between task1 and task2 ( by sending a serial message) , a State_change_handle method is called. This method is going to delete the previous task and create a new one. For example, if task1 is currently running and I send a serial messsage "task2", task1 will be deleted and task2 will be started instead. I have tested this part and it works.
However, I am concerned with task3. Task3 is created from task2 and I pass a secondary_task_handle when creating a task:
xTaskCreate(task3,"MODE2",10000,&controller_obj,1,&controller_obj.secondary_task_handle);
When I can see that both task2 and task3 is running which is correct. Now when I switch back to task1, I check whether controller_obj.secondary_task_handle is != NULL. (if the secondary task exists) and if it exists, delete the secondary task. But the if statement is never executed. I do not understand why. Serial monitor:
Initialise the object with the default values
task1 selected
different state, delete the previous task
NEW STATE = MODE1
hello from task 1
hello from task 1
hello from task 1
hello from task 1
task2 selected
different state, delete the previous task
NEW STATE = MODE2
hello from task 2
task3 started from task2hello from task 3 which is created from task 2
hello from task 3 which is created from task 2
hello from task 2
hello from task 2
hello from task 3 which is created from task 2
hello from task 3 which is created from task 2
hello from task 2
task1 selected
different state, delete the previous task
NEW STATE = MODE1
hello from task 1
hello from task 3 which is created from task 2
hello from task 1
hello from task 3 which is created from task 2
hello from task 1
hello from task 3 which is created from task 2
hello from task 1
hello from task 3 which is created from task 2
hello from task 1
hello from task 3 which is created from task 2
UPDATE
I have managed to find the mistake. Inside a task, I must to cast void* parameter to the class object and was trying to do it as below:
Controller controller_obj = *((Controller*)parameters);
However, above is wrong and I think it is passing obj by value isntead of by reference. The correct way:
Controller* controller_obj = (Controller*)parameters;
Now it is working as expected