DÍA 226 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

RAM vs Flash ROM: La Batalla del Espacio

En el Día 224 usamos una cadena estática pequeña para nuestro formulario de WiFi: const char* html = "<html>...". Aunque parece inofensiva, tener que programar una página web entera pegando el HTML dentro de un archivo C, lidiando con barras invertidas para escapar comillas (\"), es una pesadilla de mantenimiento.

La industria soluciona esto usando el sistema de compilación de ESP-IDF (CMake). Te permite escribir tu página web cómodamente en un archivo separado (ej. dashboard.html) usando VS Code, con auto-completado de HTML/CSS. Al momento de darle al botón "Compilar", CMake transforma mágicamente ese archivo de texto en un arreglo de bytes binario (uint8_t) y lo **inyecta directamente en la partición `.rodata` (Read-Only Data) de la memoria Flash**.

Zero-Copy HTTP Transmission

Gracias a que la página está en la Flash, la memoria RAM de trabajo de nuestro FreeRTOS queda al 100% intacta. Cuando el celular del técnico solicita la página, el servidor HTTP no copia la página a la RAM; simplemente crea un puntero hacia la dirección hexadecimal de la memoria Flash y transfiere los bytes directamente al controlador WiFi. A esto se le conoce como **Transmisión Zero-Copy**.

INYECIÓN DE HTML: RAM DESBORDADA VS FLASH ZERO-COPY MÉTODO AMATEUR (Strings en RAM) main.c char html[] = "<html>...</html>"; Compilador SRAM (ESP32) Total: 520 KB HEAP OCUPADO Variable html[] (50KB) ¡Poco espacio para RTOS! HTTP Send MÉTODO INDUSTRIAL (Inyección CMake) dashboard.html Puro HTML/JS/CSS (50KB) CMakeLists.txt embed_txtfiles("dashboard.html") CMake Binary Packer FLASH ROM (Memoria Larga - 4MB) Sección .rodata: Arreglo de Bytes Binario (50KB) SRAM (ESP32) RAM 100% LIBRE Para MQTT/FreeRTOS HTTP Send (Zero-Copy) Lee directo de Flash hacia la Antena WiFi.

⚙️ Ejercicios Prácticos

Circuito: Esto es pura arquitectura de compilación de ESP-IDF. No cambian los cables. Aprovecharemos el servidor HTTP que configuramos en el Día 224.

Ejercicio 1: Aprenderemos a modificar el archivo maestro de configuración CMakeLists.txt para ordenar al compilador que tome nuestro archivo HTML, lo transforme en bytes, y genere variables en lenguaje C que apuntan mágicamente al inicio y al fin de ese archivo en la memoria Flash.


/* =======================================================
 * 1. EL ARCHIVO DE INYECCIÓN (CMakeLists.txt)
 * Ubicado en el directorio /main de tu proyecto ESP-IDF
 * ======================================================= */
/*
idf_component_register(SRCS "main.c" "captive_portal.c"
                       INCLUDE_DIRS "."
                       EMBED_TXTFILES "dashboard.html")  <--- ¡ESTA ES LA MAGIA!
*/

// Al agregar "EMBED_TXTFILES", CMake generará silenciosamente un archivo ensamblador
// y expondrá dos variables globales en tu código C con el nombre del archivo.

/**
 * @file panel_diagnostico.c
 * @brief Servidor Web Zero-Copy para Dashboard de Mantenimiento Local
 */

#include <string.h>
#include "esp_http_server.h"

// =======================================================
// IMPORTACIÓN DE VARIABLES GENERADAS POR CMAKE
// =======================================================
// El compilador nombra las variables usando: _binary_[nombre]_[ext]_start
// Estas variables NO están en RAM, son punteros absolutos a la memoria Flash (.rodata).
extern const uint8_t dashboard_html_start[] asm("_binary_dashboard_html_start");
extern const uint8_t dashboard_html_end[]   asm("_binary_dashboard_html_end");

// =======================================================
// MANEJADOR HTTP (El que sirve el Dashboard)
// =======================================================
esp_err_t dashboard_get_handler(httpd_req_t *req) {
    
    // 1. Informar al navegador que enviaremos código HTML
    httpd_resp_set_type(req, "text/html");
    
    // 2. Calcular el peso exacto del archivo restando los punteros de memoria Flash
    const size_t dashboard_size = (dashboard_html_end - dashboard_html_start);
    
    // 3. Transmisión Zero-Copy: 
    // Le pasamos al servidor HTTP el puntero a la Flash y el peso. 
    // FreeRTOS lo enviará por paquetes TCP sin copiar el archivo entero a la RAM.
    httpd_resp_send(req, (const char *)dashboard_html_start, dashboard_size);
    
    return ESP_OK;
}

// =======================================================
// RUTA DE LA API REST (Para alimentar el Dashboard con JS)
// =======================================================
// El HTML de arriba tendrá código JavaScript puro (fetch) que pedirá datos vivos 
// a esta pequeña ruta periódicamente sin recargar la página.
esp_err_t api_status_get_handler(httpd_req_t *req) {
    
    httpd_resp_set_type(req, "application/json");
    
    // Aquí el ESP32 pregunta al STM32 (usando nuestro protocolo del Día 222)
    // o consulta sus propias variables de estado interno.
    char json_response[128];
    snprintf(json_response, sizeof(json_response), 
             "{\"estado_nube\":\"ONLINE\", \"tickets_pendientes\":3, \"ram_libre\":%lu}", 
             esp_get_free_heap_size());
             
    httpd_resp_send(req, json_response, strlen(json_response));
    
    return ESP_OK;
}

// =======================================================
// REGISTRO DE RUTAS EN EL SERVIDOR
// =======================================================
void registrar_panel_diagnostico(httpd_handle_t server) {
    
    // Ruta principal (Ej. http://192.168.4.1/panel)
    httpd_uri_t uri_dashboard = {
        .uri      = "/panel",
        .method   = HTTP_GET,
        .handler  = dashboard_get_handler,
        .user_ctx = NULL
    };
    httpd_register_uri_handler(server, &uri_dashboard);
    
    // Ruta de los datos puros (Ej. http://192.168.4.1/api/status)
    httpd_uri_t uri_api = {
        .uri      = "/api/status",
        .method   = HTTP_GET,
        .handler  = api_status_get_handler,
        .user_ctx = NULL
    };
    httpd_register_uri_handler(server, &uri_api);
    
    printf("[PANEL] Dashboard embebido registrado exitosamente en memoria ROM.\n");
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Interfaz de Usuario Local Autónoma e Independencia del Backend.

Un error clásico es depender exclusivamente de "La Nube" para todo. Imagina que instalas tu Despachador en una zona rural sin señal 4G. El medidor está despachando gas y el sistema *Store & Forward* lo está guardando en la EEPROM, pero ¿cómo sabe el dueño de la gasolinera cuántos litros se han vendido hoy si la máquina no puede conectarse a internet para actualizar su aplicación de teléfono?

Al programar el **Dashboard Local Inyectado en Memoria**, tu máquina ya no necesita del internet para ser monitoreada. El ESP32 actúa como un servidor Web completo. El gerente solo tiene que acercarse a menos de 10 metros del despachador, conectarse al WiFi oculto del equipo y abrir `http://192.168.4.1/panel`. Verá una interfaz gráfica moderna (servida instantáneamente desde la memoria Flash mediante `EMBED_TXTFILES`) que le mostrará en tiempo real: litros vendidos, voltaje de batería, temperatura del tanque y errores de válvula, consultados directamente al STM32 mediante tu protocolo binario interno. Has logrado que tu hardware posea una App Autónoma incrustada en silicio, ahorrando miles de dólares en desarrollo de Apps para iOS/Android y certificaciones Bluetooth.

📝 Resumen del Día