DÍA 222 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Fin de ASCII: Serialización Binaria

Cuando usas comunicación binaria, envías los bytes exactos tal como existen en la memoria RAM del procesador. Un número flotante de 32 bits (como 25.50) ocupa exactamente 4 bytes en memoria. Si lo envías como ASCII ("25.500000"), ocupa 9 bytes. Si tienes una trama de telemetría completa, pasar de texto a binario reduce el uso de memoria y ancho de banda en más de un 60%.

Anatomía de una Trama (Packet Structure)

A diferencia del texto, donde buscas un salto de línea (\n) para saber que terminó el mensaje, en un protocolo binario una trama tiene una estructura posicional matemática estricta:

  1. Header / Sync Word (2 bytes): Una secuencia mágica y constante (ej. 0xAA 0x55) que le dice al receptor: "¡Ojo! Aquí empieza un paquete válido, no es ruido eléctrico".
  2. Command ID (1 byte): Qué tipo de mensaje es (0x01 = Nueva Venta, 0x02 = Heartbeat, 0x03 = Comando AWS).
  3. Length (1 byte): Cuántos bytes mide la Carga Útil (Payload) que viene a continuación.
  4. Payload (N bytes): La estructura struct cruda de C con los datos del gas.
  5. Checksum / CRC (1 o 2 bytes): Un código matemático para que el ESP32 verifique si el ruido del cable alteró los bits durante el viaje.

El Peligro Oculto: Struct Padding

Los compiladores modernos de C (GCC) están diseñados para ser rápidos, no eficientes en espacio. Por defecto, agregan bytes de "basura" (Padding) entre las variables de tus structs para alinearlas en direcciones de memoria pares de 32 bits. Si envías un struct normal por UART, enviarás basura, y el ESP32 lo decodificará mal.
La regla de oro de los protocolos binarios es obligar al compilador a empacar los bytes sin dejar espacios vacíos, usando __attribute__((packed)) o #pragma pack(1).

ARQUITECTURA DE LA TRAMA BINARIA (PACKET FRAME) SYNC 0xAA55 (2 Bytes) LEN 12 (1 Byte) CMD 0x01 (1 Byte) PAYLOAD (Estructura de Venta Gas LP) Longitud Variable: N Bytes CRC8 0x8B (1 Byte) ANATOMÍA DEL PAYLOAD (PACKED STRUCT) uint32_t Ticket float Litros float Precio ¡CERO PADDING! #pragma pack(1) Obliga a juntar los bits en RAM sin dejar huecos.

⚙️ Ejercicios Prácticos

Circuito: Físicamente, nuestro STM32 y nuestro ESP32 se siguen conectando por los mismos pines UART (TX/RX), y deben compartir Tierra (GND). Solo aumentaremos la velocidad del puerto (Baud Rate) de los obsoletos 115,200 bps a veloces 921,600 bps, aprovechando que ya no manejaremos textos.

Ejercicio 1: Crearemos el archivo de cabecera compartido (protocolo_iot.h). Este archivo será incluido exactamente igual en el proyecto de CubeIDE del STM32 y en el proyecto ESP-IDF del ESP32, asegurando que ambos hablen el mismo lenguaje binario.


/**
 * @file protocolo_iot.h
 * @brief Cabecera Universal Compartida STM32 <--> ESP32
 */

#ifndef PROTOCOLO_IOT_H
#define PROTOCOLO_IOT_H

#include <stdint.h>

// =======================================================
// 1. CONSTANTES DEL PROTOCOLO DE CAPA DE ENLACE
// =======================================================
#define FRAME_SYNC_WORD     0xAA55  // Firma de inicio de trama
#define PROTOCOL_VERSION    0x01

// Definición de Comandos (CMD_ID)
typedef enum {
    CMD_ACK             = 0x00, // Respuesta general de acuse de recibo
    CMD_ENVIAR_VENTA    = 0x01, // STM32 pide a ESP32 subir venta a MQTT
    CMD_HEARTBEAT       = 0x02, // STM32 envía signos vitales
    CMD_RECIBIR_PRECIO  = 0x03, // ESP32 notifica a STM32 nuevo precio de nube
    CMD_WIFI_STATUS     = 0x04  // STM32 pregunta al ESP32 si hay internet
} Comando_IoT_t;

// =======================================================
// 2. EMPAQUETADO DE ESTRUCTURAS (CERO PADDING)
// =======================================================
// El macro pragma pack(push, 1) le indica al compilador GCC que alinee 
// los bytes de las variables de a 1, eliminando la basura intermedia.
#pragma pack(push, 1)

// --- A) EL ENCABEZADO (HEADER) DE LA TRAMA ---
typedef struct {
    uint16_t sync_word;   // 2 bytes (Siempre 0xAA55)
    uint8_t  longitud;    // 1 byte  (Tamaño en bytes del payload)
    uint8_t  comando;     // 1 byte  (De la enumeración Comando_IoT_t)
} Trama_Header_t;         // Peso Total: 4 bytes exactos

// --- B) PAYLOADS ESPECÍFICOS ---

// Payload para CMD_ENVIAR_VENTA (Lo que medimos del gas)
typedef struct {
    uint32_t folio_ticket;  // 4 bytes
    float    litros_gas;    // 4 bytes
    float    monto_pago;    // 4 bytes
    uint32_t timestamp;     // 4 bytes (Tiempo UNIX)
} Payload_Venta_t;          // Peso Total: 16 bytes exactos

// Payload para CMD_RECIBIR_PRECIO (Lo que viene de AWS)
typedef struct {
    float nuevo_precio;     // 4 bytes
} Payload_Precio_t;         // Peso Total: 4 bytes exactos

// --- C) LA TRAMA COMPLETA UNIFICADA ---
typedef struct {
    Trama_Header_t header;
    
    // Usamos una Unión C para que la trama ocupe el espacio del payload más grande
    // sin desperdiciar memoria, dependiendo del comando enviado.
    union {
        Payload_Venta_t  venta;
        Payload_Precio_t precio;
        // ... otros payloads ...
    } payload;
    
    uint8_t crc8;           // 1 byte (Firma de seguridad final)
} Trama_Binaria_t;

#pragma pack(pop) // Restauramos el comportamiento normal del compilador C

#endif // PROTOCOLO_IOT_H
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Optimización Extrema del Ancho de Banda Local y Determinismo de Ejecución.

En el viejo modelo ASCII, para decirle al ESP32 que querías subir una venta, tenías que generar una larga cadena: `AT+MQTTPUB=0,"gas/ventas","{\"ticket\":145, \"litros\":25.5, \"monto\":267.75}",1,0\r\n`. Esta cadena pesa aproximadamente 85 bytes. El STM32 invertía valiosos milisegundos ejecutando `snprintf` para construirla. El puerto UART tardaba casi 1 milisegundo entero en escupirla. El ESP32 pasaba más tiempo buscando las comillas dobles y el salto de línea que procesando los datos.

Al diseñar el **Protocolo Custom Binario Empaquetado**, has obliterado el overhead. Para la misma venta, el STM32 ahora toma su estructura en C pura de 16 bytes, le pega un encabezado de 4 bytes y un checksum, dando un total de **21 bytes exactos**. No hay conversión de `float` a texto. El STM32 dispara el puntero de memoria directamente al DMA (Acceso Directo a Memoria) del UART. En microsegundos, la trama está en el ESP32. El ESP32 la recibe, verifica el CRC, revisa el campo `comando` y en tiempo constante (`O(1)`) sabe exactamente qué hacer. El uso de RAM y CPU entre ambos procesadores ha caído drásticamente, incrementando la inmunidad del sistema al ruido eléctrico industrial. Has diseñado el protocolo de una verdadera red interna (Intranet de Hardware).

📝 Resumen del Día