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:
PUBACK. El ESP32 lo intercepta y le avisa al STM32: "Éxito". El STM32 purga la EEPROM física.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();
}
}
}
}
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.
TickCounts del RTOS) justo en las fronteras de transición (paso de ISR a Tarea, de UART a ESP32, de Cola interna a WiFi).