DÍA 192 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Concepto FIFO (First-In, First-Out)

Una Cola de Mensajes (Message Queue) es un tubo o banda transportadora creada en la memoria RAM y administrada directamente por el Kernel del RTOS. Funciona bajo el principio FIFO: el primer dato que entra por un lado, es el primero que sale por el otro.

A diferencia de las variables globales, la Cola actúa como un buzón asíncrono. Un hilo Productor (ej. el que maneja el teclado o el WiFi) empuja un "Sobre" dentro de la cola. El hilo Consumidor (ej. el controlador de válvulas) saca el "Sobre", lo abre y lo procesa. Si el Productor empuja datos más rápido de lo que el Consumidor los puede leer, no pasa nada: los sobres se forman en la cola pacientemente.

Paso por Copia (Pass by Copy)

La característica más poderosa de las colas en FreeRTOS es que los datos se pasan por copia. Cuando la Tarea A mete un struct en la cola, el RTOS copia cada byte literal a su memoria interna. Inmediatamente después, la Tarea A puede borrar o modificar su estructura local sin afectar al mensaje que ya va viajando por la cola hacia la Tarea B. ¡Condiciones de carrera matemáticamente imposibles!

Bloqueo Inteligente (Sincronización)

Si la Tarea B intenta sacar un mensaje pero la cola está vacía, el RTOS no la deja esperando inútilmente (Busy-Wait). La transfiere automáticamente al estado BLOCKED. La Tarea B dormirá profundamente sin gastar CPU hasta el microsegundo exacto en que la Tarea A meta un mensaje en la cola. La cola funciona simultáneamente como canal de datos y mecanismo de sincronización.

COMUNICACIÓN INTER-PROCESO (IPC) MEDIANTE COLAS FIFO Tarea Productora (Ej. Módulo IoT / Teclado) Comando Local_A Cmd: INICIAR_VENTA Param: 20 Litros Put() Cola de Mensajes (RAM del Kernel) Vacío [Slot 3] Cmd: PAUSAR [Slot 2] Cmd: INICIAR 20 Lts [Slot 1] Get() Tarea Consumidora (Ej. Controlador Válvula) Comando Local_B Cmd: INICIAR Param: 20 Litros Paso por Copia Segura (Thread-Safe) El RTOS clona la estructura entera al entrar y al salir.

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a estructurar las comunicaciones del Despachador usando CMSIS-OS v2. Crearemos un struct (nuestro "Sobre") que contendrá un Comando de Operación y un Parámetro numérico. El Hilo WiFi (nuestro Servidor IoT) inyectará comandos en la Cola, y el Hilo de Metrología dormirá esperando a que aparezca un mensaje nuevo para actuar.


/**
 * @file colas_rtos.c
 * @brief Enrutamiento de Comandos IoT a Metrología mediante Queues CMSIS-OS v2
 */

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

// =======================================================
// 1. DEFINICIÓN DEL PROTOCOLO DE MENSAJES
// =======================================================
typedef enum {
    CMD_NULO = 0,
    CMD_INICIAR_VENTA,
    CMD_PAUSAR_VENTA,
    CMD_CERRAR_EMERGENCIA,
    CMD_CAMBIAR_PRECIO
} ComandoOperacion_t;

// Este es el "Sobre" que viajará por la Cola
typedef struct {
    ComandoOperacion_t comando;  // 4 bytes
    float parametro;             // 4 bytes (Ej. Litros a despachar, o Nuevo Precio)
} MensajeVenta_t;                // Total: 8 bytes por mensaje

// ID Global de la Cola
osMessageQueueId_t cola_comandos_id;

// =======================================================
// HILO PRODUCTOR: RED WIFI (IoT AWS)
// =======================================================
void Thread_WiFi_Cloud(void *argument) {
    
    MensajeVenta_t msg_tx; // Variable local en RAM de esta tarea
    
    for (;;) {
        // Simulamos recibir una instrucción desde el servidor AWS...
        if (AWS_Hay_Nuevo_Comando()) {
            
            // Empaquetamos el mensaje
            msg_tx.comando = CMD_INICIAR_VENTA;
            msg_tx.parametro = 50.0f; // El cliente compró 50 litros por la App
            
            // MAGIA RTOS: Ponemos la COPIA del mensaje en la cola
            // Parámetro 3: Prioridad del mensaje (0 = Normal)
            // Parámetro 4: Timeout. Si la cola está llena, no esperamos (0).
            osMessageQueuePut(cola_comandos_id, &msg_tx, 0, 0U);
            
            // ¡Seguridad! Como la Cola copió los datos, podemos reusar/borrar 'msg_tx' 
            // inmediatamente sin dañar el mensaje que ya va en camino.
        }
        osDelay(100); 
    }
}

// =======================================================
// HILO CONSUMIDOR: METROLOGÍA DE VÁLVULAS
// =======================================================
void Thread_Metrologia(void *argument) {
    
    MensajeVenta_t msg_rx; // Estructura local donde recibiremos el correo
    osStatus_t status;
    
    for (;;) {
        // MAGIA RTOS: Bloqueo Inteligente
        // La tarea se queda "dormida" (BLOCKED) aquí. No avanza ni gasta CPU.
        // Solo despertará en el microsegundo exacto en que haya un mensaje.
        // 'osWaitForever' significa que esperaremos indefinidamente.
        status = osMessageQueueGet(cola_comandos_id, &msg_rx, NULL, osWaitForever);
        
        if (status == osOK) {
            // ¡Ha llegado correo! Extraemos el comando y actuamos.
            switch (msg_rx.comando) {
                case CMD_INICIAR_VENTA:
                    Valvula_Preparar_Carga(msg_rx.parametro); // Ej. 50 Lts
                    Valvula_SetApertura(100.0f);
                    break;
                    
                case CMD_PAUSAR_VENTA:
                    Valvula_SetApertura(0.0f);
                    break;
                    
                case CMD_CAMBIAR_PRECIO:
                    Metro_ActualizarPrecioLocal(msg_rx.parametro);
                    break;
                    
                default:
                    break;
            }
        }
    }
}

// =======================================================
// INICIALIZACIÓN
// =======================================================
int main(void) {
    SystemClock_Config();
    osKernelInitialize();
    
    // CREAR LA COLA DE MENSAJES ANTES QUE LAS TAREAS
    // Argumento 1: Capacidad (¿Cuántos mensajes caben formados?) -> 10 mensajes.
    // Argumento 2: Tamaño físico de cada mensaje -> sizeof(MensajeVenta_t) (8 bytes).
    cola_comandos_id = osMessageQueueNew(10, sizeof(MensajeVenta_t), NULL);
    
    if (cola_comandos_id == NULL) {
        // Error: RAM del Kernel (Heap) insuficiente
        Error_Handler();
    }
    
    // Crear Hilos
    osThreadNew(Thread_WiFi_Cloud, NULL, &attr_wifi);
    osThreadNew(Thread_Metrologia,  NULL, &attr_metro);
    
    osKernelStart();
    while (1);
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Desacoplamiento de Subsistemas (Decoupling) y Arquitectura Escalable.

Un sistema de software mal diseñado se parece a un plato de espagueti: si mueves un fideo de un lado (el WiFi), otro fideo del lado opuesto se mueve (la Válvula) porque comparten docenas de variables globales enmarañadas. Si el día de mañana la estación de gas decide cambiar su sistema AWS por un servidor interno Modbus, tendrías que reescribir toda la lógica de la válvula para entender las nuevas variables globales de Modbus.

Al implementar el sistema nervioso central de tu Despachador utilizando **Colas de Mensajes (Message Queues)**, has logrado el **Desacoplamiento Total**. A la Tarea de Metrología no le importa si el comando `CMD_INICIAR_VENTA` provino de la nube WiFi, del teclado físico, de una tarjeta RFID o del puerto Modbus. Su única responsabilidad es dormir, abrir el buzón, leer el sobre estandarizado `MensajeVenta_t`, y ejecutar. Puedes añadir módulos IoT, cambiarlos o borrarlos, y tu código metrológico jamás tendrá que ser modificado o certificado de nuevo.

📝 Resumen del Día