DÍA 198 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Problema del Congelamiento Parcial (Task Lockup)

En FreeRTOS, las tareas operan en contenedores aislados de tiempo. Si una tarea llama a osMutexAcquire(candado, osWaitForever) y el candado jamás se libera, esa tarea queda muerta. El Kernel simplemente la ignora y ejecuta el resto del programa. Si tenías un perro guardián (IWDG) configurado, y lo estás refrescando desde otra tarea que sigue viva, el hardware jamás se enterará de que tu máquina se volvió un "zombie" parcial.

El Patrón del Centinela (Watchdog Task)

Para solucionar esto, aplicamos un patrón arquitectónico militar: El Pase de Lista. Creamos una Tarea Centinela, la cual tendrá la máxima prioridad (o una muy alta). Esta tarea será la ÚNICA autorizada en todo el código para reiniciar el perro guardián de hardware (IWDG).

El mecanismo opera utilizando las Banderas de Eventos (Event Flags) que aprendimos en el Día 195:

  1. Cada tarea esclava (Metrología, Pantalla, WiFi) debe levantar su propia bandera (Set Bit) en cada vuelta de su ciclo, gritando: "¡Estoy viva!" (Heartbeat).
  2. La Tarea Centinela se duerme esperando (osEventFlagsWait) con un límite de tiempo (Timeout). Exige que TODOS los bits estén encendidos mediante una lógica AND.
  3. Si todas las tareas levantan su bandera antes del Timeout, la Centinela despierta, borra las banderas, patea al IWDG de hardware y vuelve a dormir.
  4. Si una sola tarea se congela y no levanta su bandera, la Centinela despertará por "Timeout". En ese momento, sabrá exactamente qué tarea falló, escribirá un Log de error en la EEPROM, y deliberadamente se encerrará en un while(1) infinito. El IWDG de hardware expirará en unos milisegundos, salvando al sistema mediante un Reset de fábrica.
ARQUITECTURA DE TAREA CENTINELA (SOFTWARE + HARDWARE WDG) Task 1: METROLOGÍA Reporta cada 10ms Set Bit 0 Task 2: PANTALLA Reporta cada 50ms Set Bit 1 Task 3: WIFI (CONGELADA) NO REPONDE WATCHDOG FLAGS B0: 1 (Vivo) B1: 1 (Vivo) B2: 0 (MUERTO) Wait(1000ms) TASK CENTINELA TIMEOUT ERROR! Falta Bit 2. Aborta kick. while(1) { } NO KICK I W D G HARDWARE RESET!

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a integrar el silicio con el RTOS. Crearemos las Banderas de Eventos (Event Flags) para nuestros hilos. En lugar de usar `osWaitForever` (como hicimos en el arranque del Día 195), usaremos un Timeout Estricto de 1,000 milisegundos. Si la función retorna el código de error por tiempo expirado (osErrorTimeout), ejecutaremos nuestra rutina destructiva.


/**
 * @file sentinel_watchdog_rtos.c
 * @brief Implementación Híbrida de Watchdog Software-Hardware en CMSIS-OS v2
 */

#include "stm32f401xe.h"
#include "cmsis_os2.h"

// =======================================================
// MÁSCARAS DE HEARTBEAT (Latidos de Vida)
// =======================================================
#define ALIVE_METRO    (1U << 0)  // Bit 0
#define ALIVE_PANTALLA (1U << 1)  // Bit 1
#define ALIVE_WIFI     (1U << 2)  // Bit 2

// La suma de todas las máscaras que la Centinela exige
#define ALIVE_TODOS    (ALIVE_METRO | ALIVE_PANTALLA | ALIVE_WIFI)

osEventFlagsId_t wdg_flags_id;
IWDG_HandleTypeDef hiwdg;

// =======================================================
// TAREAS ESCLAVAS (Trabajadoras)
// =======================================================
void Thread_Metrologia(void *argument) {
    for(;;) {
        // ... Trabaja despachando gas ...
        
        // Reporta que sigue viva en cada vuelta
        osEventFlagsSet(wdg_flags_id, ALIVE_METRO);
        osDelay(10);
    }
}

void Thread_WiFi(void *argument) {
    for(;;) {
        // ... Tarea propensa a colgarse enviando JSONs ...
        
        // Reporta que sigue viva (Al menos una vez por segundo)
        osEventFlagsSet(wdg_flags_id, ALIVE_WIFI);
        osDelay(500);
    }
}

// =======================================================
// TAREA CENTINELA (Vigilante Supremo)
// =======================================================
void Thread_Centinela(void *argument) {
    
    // 1. Iniciamos el hardware IWDG a 2000 ms (2 segundos)
    hiwdg.Instance = IWDG;
    hiwdg.Init.Prescaler = IWDG_PRESCALER_32;
    hiwdg.Init.Reload = 2000; 
    HAL_IWDG_Init(&hiwdg);
    
    uint32_t flags_capturadas;
    
    for (;;) {
        // 2. MAGIA: Esperamos los latidos de todos por máximo 1 segundo.
        // La opción osFlagsWaitAll hace que al cumplirse, los bits se pongan a 0
        // automáticamente (auto-clear), preparándolos para la siguiente vuelta.
        flags_capturadas = osEventFlagsWait(wdg_flags_id, ALIVE_TODOS, 
                                            osFlagsWaitAll, 1000U);
        
        // 3. EVALUACIÓN DE VIDA O MUERTE
        if (flags_capturadas == (uint32_t)osErrorTimeout) {
            
            // ¡ALERTA ROJA! El segundo pasó y no recibimos todos los bits.
            // Una tarea ha entrado en Inanición, Deadlock o Bucle infinito.
            
            // NO pateamos el perro guardián.
            UART_Enviar_String("FATAL: TAREA ZOMBIE. EJECUTANDO RESET HARDWARE...\r\n");
            
            // Para asegurar el reset, encerramos al procesador en la muerte:
            __disable_irq(); // Apagamos interrupciones
            while(1) {
                // En 1 segundo restante, el IWDG de silicio reiniciará el chip.
            }
        } 
        else {
            // Todos vivos. Recompensamos al hardware pateando al perro.
            HAL_IWDG_Refresh(&hiwdg);
        }
    }
}

// =======================================================
// INICIALIZACIÓN
// =======================================================
int main(void) {
    SystemClock_Config();
    osKernelInitialize();
    
    // El objeto bandera
    wdg_flags_id = osEventFlagsNew(NULL);
    
    // Crear Hilos
    osThreadNew(Thread_Metrologia, NULL, &attr_alta);
    osThreadNew(Thread_WiFi, NULL, &attr_baja);
    
    // CRÍTICO: La Centinela debe tener prioridad ABSOLUTA (Realtime)
    // Nada debe impedirle evaluar la salud del sistema.
    const osThreadAttr_t attr_centinela = {
        .name = "Sentinel",
        .priority = osPriorityRealtime 
    };
    osThreadNew(Thread_Centinela, NULL, &attr_centinela);
    
    osKernelStart();
    while (1);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Tolerancia a Fallos de Software (Software Fault Tolerance) y Normativa SIL.

El estándar internacional IEC 61508 (Seguridad Funcional de Sistemas Electrónicos) no te pregunta "¿Tu programa tiene errores?", asume que los tiene. La pregunta que te hace el auditor es: "¿Qué hace tu máquina cuando el software falla?".

Si la tarea encargada de leer el sensor de presión (I2C) se queda atrapada en un bucle infinito (Ej. un ciclo while mal diseñado esperando un bit físico de un cable que se rompió), tu sistema de control de gas está legalmente "ciego". Al integrar una **Tarea Centinela Híbrida**, has diseñado un auditor insobornable. La Centinela exige que la Tarea de Presión se reporte cada segundo; si el cable se rompe y el bucle la atrapa, el latido `ALIVE_SENSOR` faltará. La Centinela detectará la anomalía, suspenderá las interrupciones cerrando las válvulas por defecto, e invocará un Reset Físico Limpio mediante el IWDG. Esta es la diferencia entre un microcontrolador de aficionado y una computadora industrial certificada.

📝 Resumen del Día