freertos-tarea-idle-hooks

Qué hace la tarea Idle de FreeRTOS

  • 5 min

La Tarea Idle es la tarea de menor prioridad que FreeRTOS ejecuta cuando no hay ninguna otra tarea lista.

En los artículos anteriores hemos estado muy preocupados creando tareas, dándoles prioridades y haciendo que trabajen. Hoy vamos a plantearnos la pregunta contraria: ¿qué pasa cuando no hay trabajo?

Supongamos un escenario común: tenemos tres tareas en nuestro sistema, pero todas están esperando algo (están en estado Blocked):

La Tarea 1 espera un vTaskDelay de 1 segundo.

La Tarea 2 espera que llegue un byte por UART.

La Tarea 3 espera que un usuario pulse un botón.

En ese instante, ninguna tarea de aplicación quiere la CPU. El procesador puede seguir ejecutando instrucciones o entrar en un modo de bajo consumo, según el port y la configuración.

En ese momento aparece la tarea más discreta de FreeRTOS: la Tarea Idle (Tarea de Inactividad).

¿Qué es la tarea Idle?

Cuando arranca el Scheduler, FreeRTOS crea automáticamente una tarea especial por cada núcleo disponible.

  • Nombre: IDLE Task.
  • Prioridad: 0 (La más baja posible).
  • Estado: Siempre Ready (o Running).

Su única misión es existir. Es la “red de seguridad”. Si ninguna otra tarea del sistema (de prioridad 1 o superior) puede ejecutarse, el Scheduler automáticamente selecciona la Tarea Idle.

En su forma más básica, el código de la Tarea Idle se parece a esto:

// Pseudocódigo simplificado de la Tarea Idle
void prvIdleTask( void *pvParameters ) {
    for( ;; ) {
        // 1. Limpiar memoria de tareas borradas (Garbage Collection)
        prvCheckTasksWaitingTermination();

        // 2. Ejecutar Hooks de usuario (si existen)
        #if ( configUSE_IDLE_HOOK == 1 )
        {
            vApplicationIdleHook();
        }
        #endif

        // 3. Simplemente dar vueltas
        // (En algunos micros aquí se pone la CPU en modo bajo consumo)
    }
}
Copied!

El código que ejecutamos desde Idle no debe bloquearse con vTaskDelay, colas o semáforos. Tampoco debe tardar demasiado, porque retrasaría el mantenimiento interno y la entrada en bajo consumo.

Liberación diferida de memoria

Aunque parezca que la tarea Idle no hace nada, tiene una responsabilidad crítica: liberar la memoria de las tareas eliminadas dinámicamente.

Como veremos en el próximo artículo, cuando una tarea decide “suicidarse” llamando a vTaskDelete(NULL), se elimina a sí misma. Pero, paradójicamente, una tarea no puede liberar su propia memoria Stack mientras se está ejecutando (sería como serrar la rama sobre la que estás sentado).

Por tanto, la tarea se marca para borrar, pero es la Tarea Idle la encargada de liberar físicamente esa memoria RAM la próxima vez que se ejecute.

Si creas y destruyes tareas dinámicamente, debes asegurar que Idle reciba tiempo de CPU. Si una tarea más prioritaria acapara un núcleo, la memoria pendiente no se recuperará y puedes terminar agotando el heap.

Los Idle Hooks

Aquí viene la parte interesante para nosotros como desarrolladores. FreeRTOS nos permite “colarnos” dentro del bucle de la Tarea Idle para ejecutar nuestro propio código cuando el sistema no tiene nada mejor que hacer.

Esto se hace mediante una función callback llamada Idle Hook.

Para usar el hook estándar, configUSE_IDLE_HOOK debe estar a 1. En ESP-IDF también existen hooks de Idle específicos por núcleo; su disponibilidad desde Arduino depende de la versión y de cómo se hayan construido las librerías.

¿Para qué sirve un Idle Hook?

  1. Ahorro de energía: El port puede aprovechar Idle para entrar en bajo consumo. En ESP32 conviene usar la gestión de energía y el tickless idle de ESP-IDF, en lugar de insertar una instrucción de suspensión a mano.
  2. Medición de Carga de CPU: Podemos poner un contador dentro del Hook. Si el contador sube muy rápido, significa que el sistema está muy libre (pasa mucho tiempo en Idle). Si sube lento, estamos saturados.
  3. Tareas de fondo no prioritarias: Comprobaciones de integridad, cálculos de CRC en segundo plano, o simplemente parpadear un LED de “Heartbeat” para indicar que el sistema está vivo y no se ha colgado.

Ejemplo conceptual

// Esta función es llamada automáticamente por el sistema operativo
// cada vez que entra en la Tarea Idle.
void vApplicationIdleHook( void ) {
    // ¡IMPORTANTE! 
    // No podemos usar vTaskDelay ni nada que bloquee aquí.
    
    // Trabajo muy corto y no bloqueante.
    contadorIdle++;
}
Copied!

El caso particular del ESP32

En un ESP32 de dos núcleos, cada núcleo tiene su propia tarea Idle.

  • El Core 0 tiene su propia Tarea Idle.
  • El Core 1 tiene su propia Tarea Idle.

El Task Watchdog puede supervisar estas tareas Idle para detectar que un núcleo no les deja tiempo de ejecución. El tiempo límite y la reacción dependen de la configuración.

Cómo “hacer cosas” en Idle en ESP32

En lugar de usar el vApplicationIdleHook estándar (que a veces da problemas de compilación en Arduino dependiendo de la versión del Core), el ESP32 SDK ofrece callbacks específicos o simplemente una recomendación de diseño:

Si quieres una tarea de fondo con mínima prioridad, simplemente crea una tarea con Prioridad 0.

void TareaFondo(void *pvParameters) {
    while(1) {
        // Hago cosas poco importantes
        Serial.println("El sistema está relajado...");
        
        // Bloquear esta tarea para que Idle pueda hacer su trabajo.
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// En el setup:
xTaskCreate(TareaFondo, "Fondo", 2048, NULL, 0, NULL);
Copied!

Al ponerle prioridad 0, competirá con Idle cuando esté en estado Ready. El vTaskDelay es imprescindible para dejarle intervalos en los que pueda ejecutar el mantenimiento del kernel.