Free memory does not return to its previous state

Please help is possible:

I have an application running with several FreeRTOs tasks.

Everything works very well.

However, there is one observation that I cannot understand why.


void SetupCoreTasks(){

          xTaskCreatePinnedToCore(v_VerificaWifi, "v_VerificaWifi", MEM_v_VerificaWifi, NULL, tskIDLE_PRIORITY + 1, &Handle_VerificaWifi, 1);
          if (Display.Sinal){xTaskCreatePinnedToCore(v_SinalWifi, "v_SinalWifi", MEM_v_SinalWifi, NULL, tskIDLE_PRIORITY + 1, &Handle_SinalWifi, 1);}

          xTaskCreatePinnedToCore(v_DataHora, "v_DataHora", 1800, NULL, tskIDLE_PRIORITY + 1, &Handle_DataHora, 0);
          xTaskCreatePinnedToCore(v_DisplayHora, "v_DisplayHora", 2000, NULL, tskIDLE_PRIORITY + 1, &Handle_DisplayHora, 0);
          xTaskCreatePinnedToCore(v_Alarme, "v_Alarme", MEM_Alarme, NULL, tskIDLE_PRIORITY + 1, &Handle_Alarme, 0);

          if (Display.Uptime) {xTaskCreatePinnedToCore(v_DisplayUptime, "v_DisplayUptime", 1500, NULL, tskIDLE_PRIORITY, &Handle_DisplayUptime, 0);}
          if (DataLogger_.Ativar) {
              xTaskCreatePinnedToCore(v_Datalogger, "v_Datalogger", 3000, NULL, tskIDLE_PRIORITY + 1, &Handle_Datalogger, 0);
              StatusSDCard(true);
          }else{
              StatusSDCard(false);
          }
          if(mqtt.ATIVAR){
            xTaskCreatePinnedToCore(v_MQTT, "v_MQTT", 8000, NULL, tskIDLE_PRIORITY + 1, &Handle_MQTT, 1);
            xTaskCreatePinnedToCore(v_MQTT_Conn, "v_MQTT_Conn", 8000, NULL, tskIDLE_PRIORITY + 1, &Handle_MQTT_Conn, 1);
          }
          if (Display.Memoria){
              //xTaskCreatePinnedToCore(v_PrintTask, "v_PrintTask", 1800, NULL, tskIDLE_PRIORITY, &Handle_PrintTask, tskNO_AFFINITY);
              delay(3000);
              xTaskCreatePinnedToCore(v_PrintMem, "v_PrintMem", 3000, NULL, tskIDLE_PRIORITY, &Handle_PrintMem, tskNO_AFFINITY);
          }
          if (DataLogger_.Restart){
              xTaskCreatePinnedToCore(v_RotinaRestart, "v_RotinaRestart", 1600, NULL, tskIDLE_PRIORITY, &Handle_PrintMem, tskNO_AFFINITY);
          }

}

void v_PrintMem(void *parameters) {
      static uint32_t mh = UINT32_MAX;
      while(1){
            uint32_t min_heap_atual = esp_get_free_heap_size();
            uint32_t min_heap = esp_get_minimum_free_heap_size();
            Serial.printf("Memoria atual: %d, Menor valor: %d\n", min_heap_atual, min_heap);
            vTaskDelay(pdMS_TO_TICKS(2000));
      }
}

I am performing several tasks and this consumes certain memory, as I am monitoring it on the serial monitor.


image

The problem is the following:

When a certain task is performed and then deleted, for example an alarm. The memory that was free does not return to being exactly what it was, it always returns a little smaller.

Free memory after starting: 73740
The memory is free after executing the task of blinking the LED and having the task deleted: 72168

void v_Alarme(void *parameters) {
    UBaseType_t uxHighWaterMark;
    float tempReceived;
    while (1) {
            xQueueReceive(TempC_Queue, &tempReceived, portMAX_DELAY);
            if ((tempReceived < alarme.Inicio || tempReceived > alarme.Fim) && alarme.AlarmeEnviado == false)  {
                  if (alarme.Email) {
                      int msg = 2;
                      xTaskCreatePinnedToCore(v_EnviaEmail, "v_EnviaEmail", 10000,  (void *)&msg, tskIDLE_PRIORITY + 2, &Handle_EnviaEmail, 1);
                  } 
                  if (alarme.Led) {xTaskCreate(v_PiscaLed, "v_PiscaLed", MEM_PiscaLed, NULL, tskIDLE_PRIORITY + 1, &Handle_PiscaLed);}
                  if (alarme.Sonoro) {xTaskCreate(v_AlertaSonoro, "v_AlertaSonoro", MEM_AlertaSonoro, NULL, tskIDLE_PRIORITY + 1, &Handle_AlertaSonoro);}
                  if (alarme.WebHook) {
                      if (validarWebHook()){
                         xTaskCreate(v_WebHook, "v_WebHook", MEM_WebHook, NULL, tskIDLE_PRIORITY + 1, &Handle_WebHook);
                      } else {
                         xTaskCreate(vStatusError, "v_StatusError", 3000, (void *)true, 2, NULL);
                      }
                  }
                      alarme.AlarmeEnviado = true;
                      xTaskCreate(v_AlarmeEnviado, "v_AlarmeEnviado", MEM_AlarmeEnviado, NULL, 1, &Handle_AlarmeEnviado);
            }else if (tempReceived >= alarme.Inicio && tempReceived <= alarme.Fim) {
                  if (Handle_PiscaLed != NULL) {
                      xSemaphoreTake(xMutexLed, portMAX_DELAY);
                        vTaskDelete(Handle_PiscaLed);
                        Handle_PiscaLed = NULL;
                        digitalWrite(Led1, HIGH);
                        alarme.AlarmeEnviado=false;
                      xSemaphoreGive(xMutexLed);    
                  }
                  if (Handle_AlertaSonoro != NULL) {
                      xSemaphoreTake(xMutexSonoro, portMAX_DELAY);
                        digitalWrite(Sonoro1, LOW);
                        vTaskDelete(Handle_AlertaSonoro);
                        Handle_AlertaSonoro = NULL;
                        alarme.AlarmeEnviado=false;
                      xSemaphoreGive(xMutexSonoro);
                  }
            }
         vTaskDelay(900 / portTICK_PERIOD_MS);
    }
}

void v_AlarmeEnviado(void *pvParameters) {
     TickType_t now;
     now = xTaskGetTickCount();
     vTaskDelayUntil(&now,pdMS_TO_TICKS(alarme.Intervalo));
     alarme.AlarmeEnviado=false;
     vTaskDelete(NULL);
}

void v_PiscaLed(void *parameters){
      while(1){
              xSemaphoreTake(xMutexLed, portMAX_DELAY);
                  digitalWrite(Led1, HIGH);
              xSemaphoreGive(xMutexLed);
              vTaskDelay(500 / portTICK_PERIOD_MS);
              xSemaphoreTake(xMutexLed, portMAX_DELAY);
                  digitalWrite(Led1, LOW);
              xSemaphoreGive(xMutexLed);
              vTaskDelay(500 / portTICK_PERIOD_MS);
      }
}

void v_AlertaSonoro(void *parameters){
      while(1){
            xSemaphoreTake(xMutexSonoro, portMAX_DELAY);
                digitalWrite(Sonoro1, HIGH);
            xSemaphoreGive(xMutexSonoro);
            vTaskDelay(500 / portTICK_PERIOD_MS);
            xSemaphoreTake(xMutexSonoro, portMAX_DELAY);
                digitalWrite(Sonoro1, LOW);
            xSemaphoreGive(xMutexSonoro);                  
            vTaskDelay(500 / portTICK_PERIOD_MS);
      }
}

As you can see, the task was created and after returning to normal conditions it was deleted. However, the memory never becomes as free as it once was…even after a long time.

What is this problem? Why do MCU and FreeRTOs behave this way?

The problem this causes is that after a long run time, the memory becomes so low that it restarts and causes a stackoverflow.

Either a memory leak or fragmentation. Best to ask esp. This does not look like a problem directly related to FreeRTOS. Which memoy manager do you use?

I don’t know what a memory manager is?

Like this ?

FreeRTOS - Memory management options for the FreeRTOS small footprint, professional grade, real time kernel (scheduler)

Thank you for pointing out this documentation.

However, my knowledge is far from being able to apply this to my project and solve this problem. I don’t know how to apply this? I don’t even know where to start…

It looks like you are using SMP (Symmetric Multiprocessing), in which case there is some work being done in the port layer. Which ESP32 are you using? Are you using FreeRTOS code from our download, or generated by the ESP IDF?

Separately, why do you need to create and delete tasks, rather than reusing a task that has already been created?

So let’s go:

I am using Arduino IDE(I know it’s not ideal, but as the system was initially built on it, I ended up keeping it so I wouldn’t have to do so much work migrating to esp IDF).

I don’t know what SMP is (How did you see I’m using it)? Just analyzing the code?

What would be “Something in the port layer” what is that?

I am creating and editing tasks because this is an alarm. When the temperature is outside the desired range, it sends an email, lights up an LED or sounds.

In this case I didn’t see the need to keep the created task consuming memory.

Am I wrong in this case?

Regarding the latter: You need the memory for the task anyway sooner or later. Keeping it guarantees that it can be activated. So for a deterministic system it’s better to create the important resources once to avoid to run into out-of-memory situations (e.g. due to heap fragmentation).

I strongly recommend to read the FreeRTOS book to familiarize yourself with the basics. Your memory problems are either a consequence of improperly written application code of problems in your middle ware suite (esp provided layers). Unlikely that you will get help here unless you already have done preliminary work to encircle the problem.

Building of @RAc’s comment…There is the possibility that the daemon task simply isn’t running enough and therefore cannot actually free the memory used by the deleted task. This would stem from how your application code is written. The idle task does need to run periodically for clean up (see section 4.10).

I made the program this way, because I have a “Send Email” task that consumes a lot of memory. This task cannot coexist with other tasks, because there would not be enough memory to execute everything together.

Therefore I have a mutex created. Which waits for the email sending to be executed to perform other things.

Even while sending the email, I suspend some tasks to free up memory.

This is the reason why all tasks are not being created.

After reading and re-reading your answer, I thought a lot.

The question is this:

At no point do I question the perfection of FreeRTOs, I never question whether FreeRTOs is good or bad.

Of course I know and I’m absolutely sure that it was me who wrote something wrong in my program. And that’s exactly what I’m looking for here! Knowing where I went wrong!

Of course, I’ve already read the FreeRTOs book, I even took a course on FreeRTOs.

But that wasn’t enough for me to become an “Expert in FreeRTOs”.

If that were the case, there would be no forum…Everyone read the Book and it was resolved.

I ask that you be patient with my request for help and really help me if you can. I’ll be very grateful.

From what I see in the FreeRTOs book, it is not a good practice to create and delete tasks.

However, I have exactly this problem.

The task that is created and deleted is sending email, so it doesn’t send email all the time. Only when the alarm is triggered.

And this task consumes a lot of memory.

If you read the book and took classes, you would be expected to know what a memory manager is and where to look for regarding memory leaks…

Anyway, yes, I am fully aware that you are asking for pointers, and I would be happy to provide them, but you need to do your homework first. Your code base is already fairly large and contains code provided by third parties. Finding a memory leak in that code base is a very time consuming process. If you expect any of us to find the needle in your haystack, you are well advised to make the haystack as small as you possibly can, that is, narrow down your code base to a minimal configuration that exposes the problem.

I think that @kstribrn already gave you a good hint where to start looking.

Suspending a task does not free up any memory unless you are explicitly releasing a memory by calling vPortFree.

Did you verify that you run out of memory? Can you share the code of this task?

The same can be achieved by having the task blocked waiting for a task notification:

void EmailSendingTask( void *pvParameters )
{

    for( ;; )
    {
        /* Block indefinitely to wait for a notification. Note that this task
         * does not consume any CPU while blocked. */

        xTaskNotifyWait(0x00,      /* Don't clear any notification bits on entry. */
                        ULONG_MAX, /* Reset the notification value to 0 on exit. */
                        NULL,
                        portMAX_DELAY );

        /* Send email here. */
        SendEmail();

    }
}

A FUNDAMENTAL problem repeatedly with allocating and deallocating chunks of memory is that if you are not VERY careful, you can end up with fragmentation of the heap, and might not be able to get the full amount back, because some other side allocation took a piece out of the middle, so no big chunk exists anymore.

One solution that I see to the problem is to statically allocate the tasks and needed data structures, using two structs for the two modes that share memory via a union.

This puts all the “critical” memory usage under control so things will work.

Thank you for the suggestions.

I will organize myself to start implementing the suggestions made.

Here in Brazil, free courses and videos are very basic.

There is no one teaching FreeRTOs who have extensive experience with FreeRTOs.

I’ve actually been studying FreeRTOs for a long time and even after studying a lot about free-rtos I only learned now that deleting and creating a task is not a good practice.

This is an example of how everything taught here is very basic.

I’ll call you back here in the thread in about 2 days

The code I’m using to send emails has the option of using PSRAM memory, but unfortunately the hardware I’m running doesn’t have this feature.

A lot of courses teach you enough to get going but often not enough so that you can overcome your problem. Getting involved in a project and asking questions on the forums (like you’re already doing) is without a doubt one of the best ways to learn. And I think this question goes to show that - a lot of good learnings here.

So keep it up and we look forward to hearing back from you in a couple days :slight_smile:

One important thing to remember is that if your task allocates any memory (creating a semaphore, creating a timer, creating an array, etc) it is that tasks job to free that memory before it is deleted. FreeRTOS will not free it for you, you have to do it.