DÍA 216 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Protocolo Keep-Alive (PINGREQ / PINGRESP)

Los ingenieros de IBM que diseñaron MQTT sabían que las conexiones celulares (GPRS/3G) son frágiles. Para solucionar el problema de los Routers que cortan la conexión por inactividad (NAT Timeout), integraron un mecanismo a nivel de protocolo llamado Keep-Alive.

Cuando te conectas al Broker de AWS, defines un parámetro de tiempo (por ejemplo, 60 segundos). La regla es simple: si la máquina no tiene ninguna venta o latido JSON que enviar, está obligada a enviar un paquete vacío de control llamado PINGREQ (Ping Request) antes de que pasen esos 60 segundos. El Broker responderá inmediatamente con un PINGRESP (Ping Response). Este intercambio inútil de datos tiene un solo propósito: engañar al router de la gasolinera haciéndole creer que hay tráfico en la red, obligándolo a mantener el túnel TCP abierto (NAT Table Refresh).

El Doble Blindaje: Firmware ESP + Verificación STM32

El firmware ESP-AT de Espressif es tan avanzado que maneja el envío de PINGREQ automáticamente en segundo plano. Tú (en el STM32) no tienes que enviarlo manualmente.

Entonces, ¿por qué necesitamos programar una rutina de refresco? Porque si el ESP32 pierde la cobertura de RF, enviará el PINGREQ, pero AWS jamás responderá. El ESP32 se dará cuenta de que la conexión murió y lanzará una alarma asíncrona (+MQTTDISCONNECTED). Si tu STM32 estaba ocupado y no atrapó ese mensaje por el puerto UART, se quedará operando en la ignorancia. Por eso, el STM32 debe interrogar proactivamente al ESP32 sobre la salud del enlace interno.

MQTT KEEP-ALIVE Y PREVENCIÓN DE CIERRE POR NAT (ROUTERS) ESCENARIO 1: TÚNEL SILENCIOSO (DESCONEXIÓN INESPERADA) Máquina Gas Router NAT Timer: 60s AWS Broker Ninguna venta en 1 hora ¡ROUTER MATA EL TÚNEL POR INACTIVIDAD! ESCENARIO 2: MQTT PING (HEARTBEAT DE RED) Máquina Gas Router NAT Timer: RESET AWS Broker PINGREQ (2 bytes, cada 45s) PINGRESP (2 bytes) AT+MQTTCONN? STM32 audita al ESP32 ¡EL TÚNEL PERMANECE ABIERTO PARA LA PRÓXIMA VENTA!

⚙️ Ejercicios Prácticos

Circuito: Físicamente, seguimos aprovechando nuestra conexión UART. La diferencia es lógica: El firmware ESP-AT se encarga del Ping hacia la nube, y el STM32 se encargará del "Ping Local" (Status Check) hacia el ESP32.

Ejercicio 1: Crearemos la función Verificar_Enlace_MQTT(). Esta función será llamada por la máquina de estados periódicamente cuando no haya ventas. Enviaremos el comando de consulta AT+MQTTCONN?. Si el ESP32 responde afirmativamente, sabemos que el túnel está blindado. Si responde error, la FSM disparará la reconexión antes de que llegue un cliente.


/**
 * @file mqtt_link_watchdog.c
 * @brief Rutina de Refresco de Canal Activo (Validación Local del Keep-Alive)
 */

#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <string.h>
#include <stdio.h>

// Herramientas previas
extern bool AT_Capturar_Respuesta(const char* comando, const char* patron_buscado, char* buffer_salida, uint32_t timeout);

// =======================================================
// RUTINA DE AUDITORÍA DE ENLACE (LINK CHECK)
// =======================================================
/**
 * @brief  Interroga al ESP32 sobre la salud del túnel MQTT actual
 * @retval bool: true si el ESP32 confirma que sigue conectado al Broker, false si cayó.
 */
bool Verificar_Enlace_MQTT(void) {
    
    char buffer_respuesta[128];
    
    // Enviamos el comando de consulta.
    // El ESP32 responde típicamente con +MQTTCONN:0,4,1,"broker.aws.com",1883
    // El parámetro "4" indica estado CONECTADO. Si no hay conexión, suele arrojar error o cadena vacía.
    
    UART_Enviar_String("[WATCHDOG] Verificando salud del túnel MQTT local...\r\n");
    
    // Le damos 1 segundo al ESP32 para responder (Es una lectura de RAM local, debe ser instantánea)
    if (AT_Capturar_Respuesta("AT+MQTTCONN?\r\n", "+MQTTCONN:", buffer_respuesta, 1000)) {
        
        // Buscamos dentro de la respuesta si el LinkID 0 está en estado de Conexión (ej. estado 4 en ESP-AT v2)
        // Por seguridad universal, si el comando devuelve el nombre de nuestro broker, asumimos que está vivo.
        if (strstr(buffer_respuesta, "broker.aws.com") != NULL) {
            return true; // El túnel está perfectamente lubricado y operativo.
        }
    }
    
    // Si llegamos aquí, el ESP32 guardó silencio (se colgó) o no está conectado.
    UART_Enviar_String("[WATCHDOG] ALERTA: Túnel MQTT colapsado silenciosamente.\r\n");
    return false;
}

// =======================================================
// INTEGRACIÓN EN LA MÁQUINA DE ESTADOS (Bucle Principal)
// =======================================================
/*
void Thread_WiFi_Modem(void *argument) {
    
    uint32_t tick_ultimo_check = osKernelGetTickCount();
    const uint32_t INTERVALO_CHECK_MS = 60000; // Auditar enlace cada 60 segundos
    
    for (;;) {
        switch (estado_actual) {
            
            // ... (Fases de Conexión) ...
            
            case ESTADO_IDLE_READY:
                
                // 1. Tareas prioritarias: Ventas atrasadas
                if (Hay_Ventas_Pendientes()) {
                    Sincronizar_Venta_Nube();
                    tick_ultimo_check = osKernelGetTickCount(); // Refrescamos el timer de inactividad
                } 
                else {
                    // 2. Tiempos muertos: Ejecutar Rutina de Refresco de Canal (Ping Local)
                    if ((osKernelGetTickCount() - tick_ultimo_check) >= INTERVALO_CHECK_MS) {
                        
                        if (Verificar_Enlace_MQTT() == false) {
                            // El túnel murió por inactividad o caída de RF. 
                            // Transicionamos a reconexión inmediatamente.
                            estado_actual = ESTADO_WIFI_RECONECTAR;
                        }
                        
                        tick_ultimo_check = osKernelGetTickCount();
                    }
                }
                
                osDelay(100); 
                break;
        }
    }
}
*/
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Disponibilidad Total (Zero-Delay Startup) y Evitar Pérdidas de Conexión Silenciosas.

Un ingeniero novato asume que la red funciona hasta que una función de envío retorna un error. El problema es que en un sistema de despacho de combustible comercial, si la red murió hace una hora en silencio, el error saltará *exactamente* en el instante en que intentas cobrarle al cliente. Descubrir el error en el momento de la transacción retrasa el cobro 15 o 20 segundos mientras la máquina intenta reconectarse de emergencia, generando filas, enojo en los conductores y pitazos en la gasolinera.

Al implementar la **Rutina de Refresco de Canal (MQTT Ping Watchdog)**, tu STM32 audita la conexión cada 60 segundos durante los tiempos muertos. Si el módem ESP32 se "congeló" o el router cortó la conexión por NAT Timeout, tu máquina lo descubre de inmediato y ejecuta su secuencia de Self-Healing (Reconexión silenciosa). Para cuando el cliente llegue a la estación a cargar gas, el túnel TCP ya habrá sido reparado y restaurado en segundo plano, garantizando que el `AT+MQTTPUB` de la venta se ejecute con latencia cero. Has logrado que la tecnología sea invisible para el usuario.

📝 Resumen del Día