xTaskCreate: Passing and reading structs in function loop

sazanof
Posts: 22
Joined: Wed Sep 13, 2023 10:22 am

xTaskCreate: Passing and reading structs in function loop

Postby sazanof » Thu Oct 05, 2023 7:28 am

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!

MicroController
Posts: 1731
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: xTaskCreate: Passing and reading structs in function loop

Postby MicroController » Thu Oct 05, 2023 11:14 am

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.

sudeep-mohanty
Posts: 6
Joined: Tue Aug 15, 2023 10:49 am

Re: xTaskCreate: Passing and reading structs in function loop

Postby sudeep-mohanty » Thu Oct 05, 2023 11:28 am

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

sazanof
Posts: 22
Joined: Wed Sep 13, 2023 10:22 am

Re: xTaskCreate: Passing and reading structs in function loop

Postby sazanof » Thu Oct 05, 2023 1:03 pm

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

MicroController
Posts: 1731
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: xTaskCreate: Passing and reading structs in function loop

Postby MicroController » Thu Oct 05, 2023 2:54 pm

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.

Who is online

Users browsing this forum: No registered users and 93 guests