DÍA 199 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

La Topología del Despachador

A lo largo de esta fase hemos descubierto que un buen diseño de RTOS requiere especialización. No podemos tener una "Tarea de Todo". Nuestra arquitectura oficial se dividirá en 5 hilos jerárquicos. El concepto clave aquí es el Desacoplamiento (Decoupling): las tareas no llaman funciones de otras tareas. Se comunican exclusivamente dejando "sobres" en buzones (Mailboxes) o colas (Queues), o cediendo llaves (Mutexes).

Las 5 Tareas Base

  1. Task Centinela (Realtime): El auditor. Supervisa los Event Flags de "Latido" de todas las tareas. Si alguna falla, ejecuta un Reset duro de hardware.
  2. Task Metrología (High): El cirujano. Duerme esperando un Semáforo Binario de la interrupción del caudalímetro. Cuando despierta, calcula el gas, manda la lectura al Mailbox de telemetría y controla la válvula.
  3. Task UI Pantalla (Normal): El artista. Duerme y despierta a 20 FPS. Adquiere la telemetría del Mailbox y usa el Mutex SPI para dibujar la pantalla sin ser interrumpida.
  4. Task WiFi IoT (Low): El comunicador. Lee la telemetría, empaca JSONs asíncronos para AWS, y si AWS envía un comando, lo inyecta en la Cola de Comandos de la Metrología.
  5. Task EEPROM/Logs (Low): El notario. Duerme esperando un mensaje en la Cola de Logs. Usa el Mutex I2C para guardar cada transacción en memoria no volátil.
ARQUITECTURA DE INTEGRACIÓN MAESTRA RTOS (IPC MAP) Mutex SPI/I2C Mailbox (1 Slot) Telemetría (Lts, $) Cmd Queue INICIAR, PAUSAR EventFlags (WDG) T1: Centinela (Realtime) T2: Metrología (High) ISR Semáforo IN T3: Pantalla LCD (Normal) T4: WiFi / AWS (Low) T5: EEPROM Logs (Low) Put (Overwrite) Get() Get() Put Cmd Wait Cmd()

⚙️ Ejercicios Prácticos

Ejercicio 1: El archivo main.c final. Aquí no escribiremos las funciones detalladas por dentro, sino que prepararemos el "Main Integrador". Declararemos todos los objetos IPC y las tareas usando Asignación Estática (Día 196) para garantizar el cumplimiento normativo SIL-3.


/**
 * @file rtos_master_integration.c
 * @brief Orquestación Maestra del Despachador de Gas LP (Fase 4 Completa)
 */

#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include "dispenser_types.h" // Estructuras de Telemetría y Comandos

// =======================================================
// 1. OBJETOS IPC GLOBALES (Comunicación Inter-Proceso)
// =======================================================
osEventFlagsId_t   wdg_flags_id;
osMutexId_t        bus_hardware_mutex_id;
osMessageQueueId_t mailbox_telemetria_id;
osMessageQueueId_t queue_comandos_id;
osSemaphoreId_t    sem_pulso_sensor_id;

// =======================================================
// 2. DECLARACIONES DE MEMORIA ESTÁTICA (.bss)
// Evitamos Malloc para prevenir Fragmentación (Día 196)
// =======================================================

// --- Pilas de Tareas (Stacks) ---
uint8_t stack_centinela[256];
uint8_t stack_metro[1024];
uint8_t stack_ui[2048];
uint8_t stack_wifi[2048];
uint8_t stack_eeprom[512];

// --- TCBs (Task Control Blocks) ---
osThreadCb_t cb_centinela, cb_metro, cb_ui, cb_wifi, cb_eeprom;

// --- Memoria para Colas e IPCs ---
uint8_t mem_mailbox_telemetria[sizeof(Telemetria_t) * 1]; // 1 Slot! (Mailbox)
osMessageQueueCb_t cb_mailbox;

uint8_t mem_queue_comandos[sizeof(Comando_t) * 10];       // 10 Slots (FIFO)
osMessageQueueCb_t cb_queue_cmd;

osMutexCb_t cb_bus_mutex;
osEventFlagsCb_t cb_wdg_flags;
osSemaphoreCb_t cb_sem_sensor;

// =======================================================
// 3. ATRIBUTOS DE LAS TAREAS (Definiendo la Jerarquía)
// =======================================================
const osThreadAttr_t attr_centinela = { .name="T1_WDG",   .cb_mem=&cb_centinela, .cb_size=sizeof(cb_centinela), .stack_mem=&stack_centinela, .stack_size=sizeof(stack_centinela), .priority=osPriorityRealtime };
const osThreadAttr_t attr_metro     = { .name="T2_Metro", .cb_mem=&cb_metro,     .cb_size=sizeof(cb_metro),     .stack_mem=&stack_metro,     .stack_size=sizeof(stack_metro),     .priority=osPriorityHigh };
const osThreadAttr_t attr_ui        = { .name="T3_UI",    .cb_mem=&cb_ui,        .cb_size=sizeof(cb_ui),        .stack_mem=&stack_ui,        .stack_size=sizeof(stack_ui),        .priority=osPriorityNormal };
const osThreadAttr_t attr_wifi      = { .name="T4_WiFi",  .cb_mem=&cb_wifi,      .cb_size=sizeof(cb_wifi),      .stack_mem=&stack_wifi,      .stack_size=sizeof(stack_wifi),      .priority=osPriorityLow };
const osThreadAttr_t attr_eeprom    = { .name="T5_Log",   .cb_mem=&cb_eeprom,    .cb_size=sizeof(cb_eeprom),    .stack_mem=&stack_eeprom,    .stack_size=sizeof(stack_eeprom),    .priority=osPriorityLow };

// =======================================================
// 4. PROTOTIPOS DE TAREAS (La lógica va en otros archivos .c)
// =======================================================
extern void Task_Centinela(void *arg);
extern void Task_Metrologia(void *arg);
extern void Task_UI(void *arg);
extern void Task_WiFi(void *arg);
extern void Task_EEPROM(void *arg);

// =======================================================
// 5. INICIALIZACIÓN MAESTRA
// =======================================================
int main(void) {
    // Inicialización Bare-Metal (Relojes, Pines, Interrupciones)
    SystemClock_Config();
    Hardware_Peripherals_Init();
    
    // Iniciar el Kernel
    osKernelInitialize();
    
    // --- CREACIÓN DE OBJETOS IPC (Estática) ---
    const osEventFlagsAttr_t attr_flags = { .cb_mem=&cb_wdg_flags, .cb_size=sizeof(cb_wdg_flags) };
    wdg_flags_id = osEventFlagsNew(&attr_flags);
    
    const osMutexAttr_t attr_mutex = { .attr_bits=osMutexPrioInherit, .cb_mem=&cb_bus_mutex, .cb_size=sizeof(cb_bus_mutex) };
    bus_hardware_mutex_id = osMutexNew(&attr_mutex);
    
    const osSemaphoreAttr_t attr_sem = { .cb_mem=&cb_sem_sensor, .cb_size=sizeof(cb_sem_sensor) };
    sem_pulso_sensor_id = osSemaphoreNew(1, 0, &attr_sem); // Binario, inicia en 0
    
    const osMessageQueueAttr_t attr_mbx = { .cb_mem=&cb_mailbox, .cb_size=sizeof(cb_mailbox), .mq_mem=&mem_mailbox_telemetria, .mq_size=sizeof(mem_mailbox_telemetria) };
    mailbox_telemetria_id = osMessageQueueNew(1, sizeof(Telemetria_t), &attr_mbx);
    
    const osMessageQueueAttr_t attr_cmd = { .cb_mem=&cb_queue_cmd, .cb_size=sizeof(cb_queue_cmd), .mq_mem=&mem_queue_comandos, .mq_size=sizeof(mem_queue_comandos) };
    queue_comandos_id = osMessageQueueNew(10, sizeof(Comando_t), &attr_cmd);

    // --- CREACIÓN DE TAREAS ---
    osThreadNew(Task_Centinela,  NULL, &attr_centinela);
    osThreadNew(Task_Metrologia, NULL, &attr_metro);
    osThreadNew(Task_UI,         NULL, &attr_ui);
    osThreadNew(Task_WiFi,       NULL, &attr_wifi);
    osThreadNew(Task_EEPROM,     NULL, &attr_eeprom);
    
    // Encender el motor del universo
    osKernelStart();
    
    // Si la RAM estuvo bien calculada (Estática), jamás llegaremos aquí.
    while(1);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Hito de Arquitectura del Software del Sistema Integral.

Si miras el código `main.c` de tu Despachador Bare-Metal del Día 90, verás un abismo de diferencia. Aquel archivo era un caos de `while(1)`, banderas globales esparcidas y funciones que bloqueaban a otras. Hoy, tu `main.c` no tiene una sola línea de lógica de negocio. Es puramente declarativo. Es un "Manifiesto de Infraestructura".

Le has dicho al procesador STM32: "Quiero estos 5 trabajadores, con estas prioridades exactas de vida o muerte. Y aquí están los buzones, tuberías y candados por los cuales se comunicarán". Esto significa que a partir de mañana, puedes abrir el archivo `metrologia.c` y modificar la matemática de la válvula sin el más mínimo miedo de romper la actualización de la Pantalla LCD, porque el Sistema Operativo se encarga de proteger las fronteras. Has dominado el diseño de sistemas de Misión Crítica. Tu máquina respira.

📝 Resumen del Día