Page 1 of 1

ESP32 FreeRTOS class method to start and delete tasks does not work

Posted: Thu Sep 02, 2021 9:02 am
by zazas321
Hello. I am learning about Freertos and I have stumbled upon the error that my program hangs on vTaskDelete when trying to delete current running task and start another one. The full project can be downloaded and tested on ESP32 dev board: https://github.com/krupis/ESP32

My controller.cpp

Code: Select all


#include "Controller.h"

Controller::Controller(){
    printf("Thermostat object created \n");
}

void Controller::begin(){
    this->test1 = 0;
    this->old_state = INITIAL;
    this->new_state = INITIAL;
    this->main_task_handle = NULL;
    printf("Start the task1 \n");
    this->State_change_handle(TASK2);
}

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);
        }
        printf("PROGRAM STUCK \n");
    }
    else
    {
        printf("same task \n"); // the same task is set, dont do anything
        return;
    }
        
    switch(this->new_state) 
    {   
        case TASK1:
            printf("NEW STATE = MODE1 \n");
            this-> old_state = TASK1;
            xTaskCreate(task1,"MODE1",10000,this,1,&this->main_task_handle); // receiving commands from main uart
            break;

        case TASK2:
            printf("NEW STATE = MODE2\n");
            this-> old_state = TASK2;
            xTaskCreate(task2,"MODE2",10000,this,1,&this->main_task_handle); // receiving commands from main uart
            break;

        default:
            printf("state not recognised \n");
    }
}
controller.h

Code: Select all


#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,
    TASK1,
    TASK2,
	TASK3,
    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 = (Controller*)parameters;         
            for(;;)
            {   
                printf("test 1 =%i\n",controller->test1);
                printf("hello from task 1\n");
                vTaskDelay(1000/portTICK_RATE_MS);
            }
        }


        static void task2(void* parameters)
        {
            
            Controller* controller = (Controller*)parameters;
            for(;;)
            {   
                printf("hello from task 2\n");
                controller->test1 ++;
                printf("test 1 inside task2 = %i \n",controller->test1);
                vTaskDelay(1000/portTICK_RATE_MS);
                if(controller->test1 >= 10){
                    controller->test1 = 0;
                    controller->State_change_handle(TASK1);
                }
            }
        }



        
        //static void normal_operation_task(void *argument);                                 // DECLARE DEFAULT THERMOSTAT STATES AND VARS       
};




#endif



The logic of this program is very simple. In my main.c I create class object and call begin method. During begin method state_change_handle(TASK2) is called and the task2 is started sucesfully. After the counter reaches 10, state_change_handle(TASK1) is called from within task2 and that SHOULD delete task2 and start task1, however, when counte reaches 10 and state_change_handle method is called, the program just hangs after vTaskDelete. Serial monitor output:

Code: Select all

Start the task1
different state, delete the previous task
PROGRAM STUCK
NEW STATE = MODE2
hello from task 2
test 1 inside task2 = 1
hello from task 2
test 1 inside task2 = 2 
hello from task 2
test 1 inside task2 = 3 
hello from task 2
test 1 inside task2 = 4 
hello from task 2
test 1 inside task2 = 5 
hello from task 2
test 1 inside task2 = 6
hello from task 2
test 1 inside task2 = 7
hello from task 2
test 1 inside task2 = 8
hello from task 2
test 1 inside task2 = 9
hello from task 2
test 1 inside task2 = 10
different state, delete the previous task
Please could someone help me understand what is the issue here?

I am concerned with this part of the code:

Code: Select all

        static void task2(void* parameters)
        {
            
            Controller* controller = (Controller*)parameters;
            for(;;)
            {   
                printf("hello from task 2\n");
                controller->test1 ++;
                printf("test 1 inside task2 = %i \n",controller->test1);
                vTaskDelay(1000/portTICK_RATE_MS);
                if(controller->test1 >= 10){
                    controller->test1 = 0;
                    controller->State_change_handle(TASK1);
                }
            }
        }
      

Is this correct?

Code: Select all

Controller* controller = (Controller*)parameters;
Could there be some issues with the way I create this pointer to the controller object and then use it to call class method?


I have tried to print the task name before deleting the task and it returned the correct name which probably means the task handle and object is correct:

Code: Select all

            if(object->old_state != object->new_state ){ // if the new state is different than current state, delete the task
                if (object->main_task_handle != NULL){
                    printf("different state, delete the previous task \n");
                    printf(" deleting task with name = %s \n",pcTaskGetTaskName(object->main_task_handle));
                    vTaskDelete(object->main_task_handle);
                }
                printf("PROGRAM STUCK \n");
            }
 
serial monitor:

Code: Select all

test 1 inside task2 = 10
object test 1 = 10
different state, delete the previous task
 deleting task with name = MODE2
 

Re: ESP32 FreeRTOS class method to start and delete tasks does not work

Posted: Thu Sep 02, 2021 11:17 am
by zazas321
I think I have figured out what is the problem here. It is not correct to call a function inside a task that deletes the caller task.
For example:

Code: Select all

static void task1(void* parameters)
        {
            Controller* controller = (Controller*)parameters;         
            for(;;)
            {   
                controller->test1++;
                printf("hello from task 1\n");
                if(controller->test1 % 10 == 0){
                        printf("changing to TASK2\n");
                        controller->test1 = 1;
                        xTaskCreate(task2,"TASK2",10000,controller,1,&controller->main_task_handle); // receiving commands from main uart
                        vTaskDelete(0);

                        //controller->State_change_handle(TASK2);
                }
                vTaskDelay(1000/portTICK_RATE_MS);
            }
        }

As you can see from the code above, the new task is created before the current task is deleted. This is the correct behaviour


In my program, I am basically trying to do the opposite:
I am deleting a task before I had a chance to create a new task which means that all the stack memory is freed and nothing else will happen.
Note that deleting a task before creating a new task is fine if task deletion happens outside the task itself.

Re: ESP32 FreeRTOS class method to start and delete tasks does not work

Posted: Fri Sep 03, 2021 1:58 am
by ESP_Sprite
Sounds right. If you execute a vTaskDelete(NULL), that will be the very last function the task executes, as in, that function won't return and the task will be cleaned up. It's similar to calling e.g. exit() in a non-embedded program: functions like that don't return because execution stops.

Re: ESP32 FreeRTOS class method to start and delete tasks does not work

Posted: Fri Sep 03, 2021 4:58 am
by zazas321
Thanks for the reply. When I call my state_change_handle method that deletes the current running task and starts a new task from outside the task itself, it works fine. The issue only happens when state_change_handle is called from within the task itself. For example I can create task3 which is going to call state_change_handle and switch between task1 and task2 and that will work fine. Could you help me understand more why that happens?



Anyways, I am trying to find a convenient way to handle my tasks. In my project I am going to have multiple tasks and I need to have a control over which task runs when and I need to be able to delete other tasks and start new ones when needed. Therefore I have tried to write this state_change_handle method.

In summary:
My project will have 2 tasks that runs in the background and these tasks cannot be deleted.

My project will have 3 possible main tasks ( Only 1 main task can run at once )

My project will have 3 secondary tasks ( 1 secondary tasks can rut at once, 1 main and 1 secondary task can run concurently ).

My plan was to create 2 methods ( same as state_change_handle) to handle all main tasks and all secondary tasks.

Re: ESP32 FreeRTOS class method to start and delete tasks does not work

Posted: Sat Sep 04, 2021 1:54 am
by ESP_Sprite
I'd advise you to not make your life so hard. If you have two main tasks and only one can run at a time, why not stash the task code for that in seperate functions instead and call those functions from one single main task? Same for the secondary tasks.