DÍA 205 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

Anatomía de un Socket (IP + Puerto)

Una Dirección IP (como `198.51.100.14`) es como la dirección de un gran edificio de oficinas (El Servidor). Si envías un paquete solo a esa dirección, llegará a la recepción del edificio, pero el recepcionista no sabrá a quién entregarlo.

Un Puerto es el número de la oficina dentro de ese edificio. Si especificas el Puerto 80, el paquete va a la oficina del servidor web HTTP. Si especificas el Puerto 1883, va a la oficina del servidor MQTT.
La combinación matemática de una Dirección IP y un Puerto se llama Socket.

TCP: El Mensajero con Acuse de Recibo

En el nivel de Transporte de Internet existen dos protocolos principales: UDP y TCP.

El Comando AT+CIPSTART

Afortunadamente, el ESP32 encapsula toda la brutal complejidad de TCP en un solo comando de texto. Nosotros le diremos: AT+CIPSTART="TCP","api.miservidor.com",80. El ESP32 resolverá el dominio a una IP usando DNS, hará el Handshake de 3 vías y, si el servidor acepta abrir el túnel, nos responderá con un hermoso CONNECT OK.

CREACIÓN DE UN SOCKET TCP (AT+CIPSTART) CLIENTE (ESP32) IP: 192.168.1.55 Puerto Dinámico (Ej. 52144) STM32 Command INTERNET (Routers, Fibra Óptica, ISPs) SERVIDOR AWS api.mi-gas.com PUERTO DESTINO 80 (HTTP) Servicio a la escucha TÚNEL TCP SOCKET AT+CIPSTART=... 1. SYN (¿Estás ahí, puerto 80?) 2. SYN-ACK (Sí, estoy listo. ¿Y tú?) 3. ACK (¡Listo! Túnel Abierto) CONNECT OK

⚙️ Ejercicios Prácticos

Circuito: Físicamente seguimos con los procesadores conectados por UART. Lógicamente, para crear un Socket, tu ESP32 ya debe haber superado exitosamente la fase ESTADO_WIFI_CONECTAR de nuestra Máquina de Estados (Día 204).

Ejercicio 1: Modificaremos nuestra FSM de Red para incluir la apertura explícita del túnel TCP. Nos aseguraremos de que el ESP32 esté configurado para Conexión Única (Single Connection Mode), lo cual simplifica enormemente el manejo para sistemas embebidos M2M (Machine to Machine).


/**
 * @file tcp_socket_rtos.c
 * @brief FSM IoT: Apertura de Sockets TCP con el Comando AT+CIPSTART
 */

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

// Prototipo de envío seguro (Que espera OK o Timeout sin bloquear la CPU)
extern bool AT_Enviar_Comando(const char* comando, uint32_t timeout_ms);

// Función dedicada para leer respuestas específicas (Como "CONNECT" en vez de solo "OK")
extern bool AT_Esperar_Respuesta(const char* respuesta_esperada, uint32_t timeout_ms);

// Dirección del servidor del corporativo gasero
#define SERVIDOR_BACKEND "api.gas-corporativo.com"
#define PUERTO_TCP 80

void Thread_WiFi_Modem(void *argument) {
    
    // Asumimos que la FSM ya pasó por HARD_RESET, INIT y WIFI_CONECTAR
    FSM_WiFi_t estado_actual = ESTADO_TCP_CONECTAR; 
    
    char comando_tcp[128];
    uint8_t intentos_tcp = 0;
    
    for (;;) {
        switch (estado_actual) {
            
            // ... (Estados anteriores omitidos por brevedad) ...
            
            case ESTADO_TCP_CONECTAR:
                UART_Enviar_String("[TCP] Configurando Modo de Conexion Única...\r\n");
                
                // MUX=0 obliga al ESP32 a manejar solo 1 socket a la vez.
                // Es más fácil de gobernar por un STM32 que el modo multi-socket.
                AT_Enviar_Comando("AT+CIPMUX=0\r\n", 1000);
                
                UART_Enviar_String("[TCP] Abriendo Socket hacia el Servidor...\r\n");
                
                // Construimos el comando dinámico AT+CIPSTART
                // Estructura: AT+CIPSTART="TCP","api.miservidor.com",80
                snprintf(comando_tcp, sizeof(comando_tcp), 
                         "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", 
                         SERVIDOR_BACKEND, PUERTO_TCP);
                
                // Mandamos el comando por hardware
                HAL_UART_Transmit(&huart1, (uint8_t*)comando_tcp, strlen(comando_tcp), 100);
                
                // MAGIA: Negociar TCP (3-way handshake) a través de internet no es 
                // instantáneo. Puede tardar hasta 5 o 10 segundos. 
                // Le damos al ESP32 10,000 milisegundos de gracia. 
                // Si el servidor acepta, el ESP32 regresará "CONNECT" (y luego un OK).
                if (AT_Esperar_Respuesta("CONNECT", 10000)) {
                    
                    UART_Enviar_String("[TCP] TÚNEL ESTABLECIDO EXITOSAMENTE.\r\n");
                    intentos_tcp = 0;
                    
                    // Ahora la tubería está viva. Podemos pasar al estado de enviar datos
                    estado_actual = ESTADO_IDLE_READY; 
                    
                } else {
                    UART_Enviar_String("[TCP] ERROR: El Servidor rechazó la conexión o no existe.\r\n");
                    intentos_tcp++;
                    
                    if (intentos_tcp > 3) {
                        // Si fallamos mucho, quizás el DNS está roto o la red cayó.
                        // Usamos nuestra estrategia de Self-Healing.
                        estado_actual = ESTADO_WIFI_RECONECTAR; 
                    } else {
                        // Esperamos 2 segundos e intentamos abrir la tubería de nuevo
                        osDelay(2000); 
                    }
                }
                break;
                
            case ESTADO_IDLE_READY:
                // --- LA TUBERÍA ESTÁ ABIERTA ---
                // Aquí prepararemos nuestros strings JSON para dispararlos
                // a través del túnel con el comando AT+CIPSEND (Día 206).
                osDelay(500);
                break;
                
            // ... (Resto de estados) ...
        }
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Garantía de Transporte de Datos (Transport Layer Assurance).

Cuando un cliente paga $500 MXN en tu despachador de gas, ese dinero debe ser reportado a las oficinas centrales. Si el Despachador generara un paquete y lo lanzara al aire como si fuera un radio tradicional (usando protocolo UDP), el paquete podría perderse por interferencia electromagnética de la misma bomba del gas, y el dueño de la estación jamás sabría que se realizó esa venta.

Al implementar **Sockets TCP** a través del comando `AT+CIPSTART`, estás exigiendo un "Acuse de Recibo" a nivel de arquitectura de red. El STM32 no enviará ni un solo byte de la venta hasta que el servidor en la nube no responda al Handshake y el túnel se declare como abierto (`CONNECT OK`). Más adelante, si un cable de internet se corta a medio envío de la venta, el protocolo TCP del ESP32 lo detectará, cerrará el Socket y le reportará el estado de error de vuelta al STM32, el cual guardará pacientemente la venta en su EEPROM para reintentar la transmisión cuando la red de la gasolinera vuelva a estar estable. Datos inquebrantables garantizados.

📝 Resumen del Día