DÍA 230 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Pipeline de Datos End-to-End

En el mundo IoT de grado industrial, un sistema funcional no se define por si "manda datos o no". Se define por el Determinismo y el Presupuesto de Latencia (Latency Budget). Debemos probar cuánto tiempo exacto le toma a una venta de gas ser confirmada criptográficamente.

El flujo arquitectónico (Data Journey) es el siguiente:

  1. Mundo Físico (T=0 ms): El usuario descuelga la pistola y la válvula se cierra al terminar el despacho. El sensor I2C/Pulsos envía la señal física.
  2. Metrología STM32 (T+1 ms): La ISR por hardware captura el pulso. La Tarea de Metrología en FreeRTOS totaliza los litros y crea un registro de Venta en la EEPROM.
  3. Trama Binaria (T+5 ms): La Venta se empaqueta en 21 bytes exactos (Protocolo Custom). El DMA dispara estos bytes a 921,600 bps hacia el ESP32.
  4. Core 1 ESP32 (T+7 ms): La Tarea UART del ESP32 atrapa la trama, verifica el CRC8 matemático para descartar ruido y pasa la venta al Core 0 vía FreeRTOS Queue.
  5. Core 0 ESP32 (T+10 ms): El procesador de Red convierte el struct binario en un JSON, lo encripta usando MbedTLS (AES-256) y lo inyecta al stack TCP/IP de LwIP.
  6. Tránsito WiFi/Celular (T+40 ms a T+200 ms): El módulo de radiofrecuencia (RF) emite la onda hacia el Router local, el cual navega a través de los nodos de internet hacia el Broker de AWS IoT.
  7. Confirmación QoS 1 (T+250 ms): AWS recibe, desencripta, verifica y responde con un paquete PUBACK. El ESP32 lo intercepta y le avisa al STM32: "Éxito". El STM32 purga la EEPROM física.
PRUEBA DE INTEGRACIÓN: LATENCIA Y PIPELINE END-TO-END MUNDO FÍSICO Válvula STM32 (Cortex-M4) ESP32 (Coprocesador) AWS IoT (Nube) T=0ms Cierre Metrología Task T+1ms (Totaliza) Protocolo Binario T+2ms (Struct 21b) UART DMA CORE 1 T+7ms (Rx) CORE 0 JSON & TLS T+10ms WiFi MQTTS AWS Broker T+120ms PUBACK CORE 1 T+180ms ACK Binario EEPROM Purge T+190ms (Latencia Final)

⚙️ Ejercicios Prácticos

Circuito Completo: Todo el hardware ensamblado: STM32F401, ESP32-WROOM, y la conexión de Control Fuera de Banda (OOB) en el pin EN para el Watchdog de Hardware (Día 228).

Ejercicio 1: Vamos a crear un Trazador de Latencia (Telemetry Tracer). Inyectaremos estampas de tiempo (Timestamps) en milisegundos a medida que el paquete viaja por la arquitectura, para imprimir en consola el tiempo exacto que tomó subir una venta desde el STM32 hasta recibir la confirmación de AWS.


/**
 * @file telemetria_tracer.c
 * @brief Trazador de Latencia End-to-End (STM32 <-> ESP32 <-> AWS)
 */

#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include "protocolo_iot.h" // Nuestra trama binaria
#include <stdio.h>

// =======================================================
// LÓGICA DE TRAZABILIDAD EN EL STM32 (MAESTRO)
// =======================================================
extern osMessageQueueId_t cola_ventas_pendientes;
extern osEventFlagsId_t uart_ack_flags;
#define FLAG_VENTA_ACK (1U << 0)

void Task_Conector_IoT(void *argument) {
    
    Trama_Binaria_t trama_tx;
    trama_tx.header.sync_word = FRAME_SYNC_WORD;
    trama_tx.header.comando = CMD_ENVIAR_VENTA;
    
    for(;;) {
        // 1. STM32 extrae una venta generada por la válvula (T=0)
        if(osMessageQueueGet(cola_ventas_pendientes, &trama_tx.payload.venta, NULL, osWaitForever) == osOK) {
            
            // MARCA DE TIEMPO T1: Inicio de Transmisión
            uint32_t t1_start = osKernelGetTickCount();
            printf("[TEST] T=%lums | Venta Foliada. Disparando UART al ESP32...\r\n", t1_start);
            
            // Calculamos CRC y enviamos binario
            trama_tx.header.longitud = sizeof(Payload_Venta_t);
            trama_tx.crc8 = Calcular_CRC8((uint8_t*)&trama_tx, sizeof(Trama_Header_t) + trama_tx.header.longitud);
            
            HAL_UART_Transmit(&huart1, (uint8_t*)&trama_tx, sizeof(Trama_Binaria_t), 100);
            
            // MARCA DE TIEMPO T2: Esperando el PUBACK desde el otro lado del mundo
            // Le damos 5 segundos máximo a AWS para responder, si no, retenemos en EEPROM.
            uint32_t result = osEventFlagsWait(uart_ack_flags, FLAG_VENTA_ACK, osFlagsWaitAny, 5000);
            
            if (result != (uint32_t)osErrorTimeout) {
                // MARCA DE TIEMPO T3: Éxito Absoluto
                uint32_t t3_end = osKernelGetTickCount();
                uint32_t latencia_total = t3_end - t1_start;
                
                printf("[TEST] T=%lums | ACK RECIBIDO DESDE AWS.\r\n", t3_end);
                printf("[TEST] == LATENCIA END-TO-END: %lu milisegundos ==\r\n", latencia_total);
                
                // Procedemos a borrar físicamente la venta de la EEPROM
                // EEPROM_Marcar_Sync(trama_tx.payload.venta.folio_ticket);
            } else {
                printf("[TEST] TIMEOUT FATAL. La red falló. Se reintentará luego.\r\n");
            }
        }
    }
}
        

/**
 * @file main.c (ESP-IDF)
 * @brief Trazabilidad del lado del Coprocesador
 */
/* ... Includes omitidos ... */

// =======================================================
// LÓGICA DE TRAZABILIDAD EN EL ESP32 (COPROCESADOR)
// =======================================================
void Task_MQTT_Cloud(void *pvParameters) {
    Payload_Venta_t venta_rx;
    char json_buf[128];
    
    for(;;) {
        if(xQueueReceive(cola_ventas_intercore, &venta_rx, portMAX_DELAY) == pdTRUE) {
            
            // MARCA ESP32
            uint32_t t_esp = xTaskGetTickCount() * portTICK_PERIOD_MS;
            printf("[ESP32] T_local=%lu | Recibido de STM32. Encriptando TLS...\n", t_esp);
            
            snprintf(json_buf, sizeof(json_buf), 
                    "{\"f\":%lu, \"l\":%.2f, \"m\":%.2f}", 
                    venta_rx.folio_ticket, venta_rx.litros_gas, venta_rx.monto_pago);
            
            // Usamos QoS 1. Esta función nativa de ESP-IDF se bloquea hasta obtener el PUBACK de AWS.
            int msg_id = esp_mqtt_client_publish(cliente_mqtt, "gas/ventas", json_buf, 0, 1, 0);
            
            if (msg_id != -1) {
                // Al llegar aquí, AWS certificó matemáticamente la entrega.
                uint32_t t_aws = xTaskGetTickCount() * portTICK_PERIOD_MS;
                printf("[ESP32] T_local=%lu | PUBACK recibido de AWS. Notificando a STM32...\n", t_aws);
                
                // Armamos la trama ACK Binaria y la mandamos de regreso al STM32 por el Core 1
                // Enviar_ACK_Binario_A_STM32();
            }
        }
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Certificación Operativa y Auditoría de Presupuesto de Latencia.

El diseño de sistemas en tiempo real no perdona las suposiciones. Puedes creer que tu código es rápido, pero en la industria del Gas LP, si el conductor del camión cisterna cierra la venta y el ticket físico tarda 15 segundos en imprimirse porque el sistema estaba atorado esperando la confirmación del servidor, el flujo de trabajo colapsa y los operadores culparán a tu máquina.

Al ejecutar la **Prueba de Integración End-to-End (Trazabilidad de Latencia)**, has demostrado empíricamente la superioridad de tu arquitectura concurrente de doble procesador. El STM32 no se bloquea esperando a AWS; delega el trabajo al ESP32. El paquete binario viaja en **~2 milisegundos**. El cifrado MbedTLS asimétrico en el Core 0 toma **~10 milisegundos**. El tránsito global hasta el servidor en Virginia (AWS) y su retorno (PUBACK) promedia unos **150 a 200 milisegundos**. Tu máquina consolida registros financieros de alta seguridad en un cuarto de segundo. Esta es la diferencia entre un proyecto universitario en Protoboard y una arquitectura industrial que podría ser desplegada hoy mismo en 10,000 gasolineras del país.

📝 Resumen del Día