Page 1 of 1

xTaskCreate: Passing and reading structs in function loop

Posted: Thu Oct 05, 2023 7:28 am
by sazanof
Hello!

I started working with FreeRTOS and came across a nuisance that I can't overcome yet. The problem is that when passing the data structure to the task, the data comes out random when reading. Tell me, please, what is the problem? :oops:

So when I passing int, it works fine:

Code: Select all

void task1_exec(void *pvParameters)
{
    while (1)
    {
        int params = (int)pvParameters;
        ESP_LOGW(ADC_TAG, "Run ntc termistor task: channel: %d", params);
        vTaskDelay(pdMS_TO_TICKS(3000));
    };
    vTaskDelete(NULL);
}
somewhere in function in app_main

Code: Select all

for (int i = 0; i < 4; i++)
    {
        if (adc[i].enabled == 1)
        {
            switch (adc[i].type)
            {
            case TYPE_NTC_THERMISTOR:
                if (adc[i].config != NULL)
                {
                    cJSON *config = cJSON_Parse(adc[i].config);
                    ntc_thermistor_config_t ntc_config = {
                        .channel = adc[i].channel,
                        .beta = cJSON_GetObjectItem(config, "coef")->valueint,
                        .width_bit = 12};
                    char *test_string = "Hello!";
                    xTaskCreate(task1_exec, "task1", STACK_SIZE, (void *)ntc_config.channel, 1, NULL);
                }

                break;

            default:
                break;
            }
        }
    }
console output:

Code: Select all

W (63647) ADC: Run ntc termistor task: channel: 5
W (63657) ADC: Run ntc termistor task: channel: 4
W (63657) ADC: Run ntc termistor task: channel: 6
W (63667) ADC: Run ntc termistor task: channel: 7
BUT when I want to pass struct:

Code: Select all

void task1_exec(void *pvParameters)
{
    while (1)
    {
        ntc_thermistor_config_t params = *((ntc_thermistor_config_t *)pvParameters);
        ESP_LOGW(ADC_TAG, "Run ntc termistor task: channel: %d, beta %d", params.channel, params.beta);
        vTaskDelay(pdMS_TO_TICKS(3000));
    };
    vTaskDelete(NULL);
}
somewhere in function in app_main

Code: Select all

for (int i = 0; i < 4; i++)
    {
        if (adc[i].enabled == 1)
        {
            switch (adc[i].type)
            {
            case TYPE_NTC_THERMISTOR:
                if (adc[i].config != NULL)
                {
                    cJSON *config = cJSON_Parse(adc[i].config);
                    ntc_thermistor_config_t ntc_config = {
                        .channel = adc[i].channel,
                        .beta = cJSON_GetObjectItem(config, "coef")->valueint,
                        .width_bit = 12};
                    xTaskCreate(task1_exec, "task1", STACK_SIZE, (void *)&ntc_config, 1, NULL);
                }
                break;
            default:
                break;
            }
        }
    }
output is wrong:

first run

Code: Select all

W (647) ADC: Run ntc termistor task: channel: 6, beta 3900
W (647) ADC: Run ntc termistor task: channel: 4, beta 3900
W (647) ADC: Run ntc termistor task: channel: 7, beta 3900
W (647) ADC: Run ntc termistor task: channel: 0, beta 396067
other

Code: Select all

W (51667) ADC: Run ntc termistor task: channel: 0, beta 397091
W (51667) ADC: Run ntc termistor task: channel: 0, beta 397091
W (54647) ADC: Run ntc termistor task: channel: 0, beta 397091
W (54657) ADC: Run ntc termistor task: channel: 0, beta 397091
W (54667) ADC: Run ntc termistor task: channel: 0, beta 397091
W (54667) ADC: Run ntc termistor task: channel: 0, beta 397091
W (57647) ADC: Run ntc termistor task: channel: 0, beta 397091
What is going on and how can I avoid data substitution? Thanks!

Re: xTaskCreate: Passing and reading structs in function loop

Posted: Thu Oct 05, 2023 11:14 am
by MicroController

Code: Select all

ntc_thermistor_config_t ntc_config = {
  .channel = adc[i].channel,
  .beta = cJSON_GetObjectItem(config, "coef")->valueint,
  .width_bit = 12};
xTaskCreate(task1_exec, "task1", STACK_SIZE, (void *)&ntc_config, 1, NULL);
This is not going to work. Note that ntc_config is a local variable which becomes invalid as soon as its scope ends, i.e. right after xTaskCreate is called. By the time the newly created task starts using the pointer to that local variable, it may long be gone or 'recycled' for other data.
You can malloc a struct for each task, and have each task free it when it's not needed anymore. Or make the structs globals.

Re: xTaskCreate: Passing and reading structs in function loop

Posted: Thu Oct 05, 2023 11:28 am
by sudeep-mohanty
Well, this is not an anomaly. The reason why the same value is printed after the first when you pass the address of the struct is because it is the same address that gets passed to each of the 4 tasks. And as it happens, the last value populated in the struct is printed by all the tasks in subsequent runs. This is because, the compiler optimizes the struct declaration on the stack and reuses the same memory for the struct in the loop. As opposed to this, when you pass the integer to the tasks, you pass it by value and not by its address. Which is why each of the tasks print different values.

It would be generally not advisable to pass memory declared on stack of another function (app_main) to other functions (task1_exec). I am assuming your aim here is to pass different struct values to each of the created tasks. So, you could -
- Create separate structs globally for each of your tasks
- Create a struct array globally for each task indexed by the loop counter
- Create the structs dynamically using malloc

Re: xTaskCreate: Passing and reading structs in function loop

Posted: Thu Oct 05, 2023 1:03 pm
by sazanof
Everything is made up of little things. I understand you, thank you! It breaks my brain whenever I try to think like a PHP JS developer.

A solution that seems to work (I will try in different ways):
In the top of .c file

Code: Select all

ntc_thermistor_config_t ntc_config[4];
in app_main set values of ntc_config

Code: Select all

for (int i = 0; i < 4; i++)
    {
        if (adc[i].enabled == 1)
        {
            switch (adc[i].type)
            {
            case TYPE_NTC_THERMISTOR:

                if (adc[i].config != NULL)
                {
                    cJSON *config = cJSON_Parse(adc[i].config);
                    ntc_config[i].channel = adc[i].channel,
                    ntc_config[i].beta = cJSON_GetObjectItem(config, "coef")->valueint,
                    ntc_config[i].width_bit = 12;
                    xTaskCreate(task1_exec, "ntc_task", STACK_SIZE, &ntc_config[i], 1, NULL);
                }

                break;
            default:
                break;
            }
        }
    }
Thank you for help!

However, this is not the last problem that will stand in my way in the development of ESP32

Re: xTaskCreate: Passing and reading structs in function loop

Posted: Thu Oct 05, 2023 2:54 pm
by MicroController
A solution that seems to work...
That's right. The variables you declare outside of any function are "global" variables. These exist permanently (and will always consume RAM) as they are "statically allocated" by the compiler.
Variables declared inside a "block", i.e. between { and }, including anything declared inside a function, are only valid until the block is exited. The compiler won't let you use these local variables after they're gone, but a pointer to one of them may still "escape" the scope.

The pointer-to-local-variable mechanism does work when sending events via the event loop because esp_event_post_to(...) internally makes a copy of the pointed-to/local variable's data before the function returns. A function's documentation should state the required lifetime (or "ownership") of memory it takes as an argument; if it doesn't, manual inspection of the function's code is inevitable.