DÍA 225 DE 365

Fase 4: FreeRTOS y Conectividad IoT

📖 Teoría a Estudiar

El Sistema NVS (Non-Volatile Storage)

A diferencia del STM32 donde manipulamos la memoria Flash borrando "Sectores" enteros manualmente (Día 219), ESP-IDF nos abstrae de esa brutalidad matemática proporcionando una librería de alto nivel llamada NVS.

El NVS funciona exactamente igual que un Diccionario (Key-Value Pair). Tú no necesitas saber en qué dirección hexadecimal se guardan los datos. Simplemente le dices: "Guarda el texto 'MiWiFiSecreto' bajo la llave 'SSID'". El sistema NVS se encarga de ubicar un sector vacío, escribir el dato y protegerlo.

Conceptos Clave de la Bóveda NVS

ARQUITECTURA DE DATOS: NVS (NON-VOLATILE STORAGE) Particiones Flash Bootloader NVS (Data) 24 KB ~ 1 MB App (Firmware) Estructura Lógica (API NVS) Namespace: "config_red" Key: "ssid" Value: "Gasolinera_Centro_2G" Key: "pass" Value: "S3cr3to_Industrial99" Namespace: "maquina_status" Key: "disp_id" Value: "DISP_GDL_01" Key: "reboots" Value: 42 (int32_t)

⚙️ Ejercicios Prácticos

Circuito: El uso de NVS no requiere componentes externos, utiliza la misma memoria Flash QSPI montada en el módulo ESP32 (generalmente de 4MB u 8MB).

Ejercicio 1: Crearemos una librería interna llamada nvs_manager.c. Programaremos una función para inyectar (escribir) las credenciales recibidas desde el Portal Cautivo, y otra función para extraerlas (leerlas) durante el arranque de la máquina, determinando si debemos encender la antena en modo Access Point o Modo Estación.


/**
 * @file nvs_manager.c
 * @brief Gestor de Almacenamiento Persistente para configuración IoT (ESP-IDF)
 */

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "nvs_flash.h"
#include "nvs.h"

// =======================================================
// ESCRITURA: GUARDAR CREDENCIALES (Llamado por el Portal)
// =======================================================
/**
 * @brief  Guarda el SSID y el Password en la memoria permanente del ESP32
 * @retval bool: true si la escritura en Flash fue exitosa
 */
bool Guardar_Credenciales_WiFi(const char* ssid, const char* password) {
    
    nvs_handle_t my_handle;
    esp_err_t err;

    // 1. Abrir (o crear) el Namespace "config_red" en Modo Lectura/Escritura
    err = nvs_open("config_red", NVS_READWRITE, &my_handle);
    if (err != ESP_OK) {
        printf("[NVS ERROR] No se pudo abrir la Bóveda NVS.\n");
        return false;
    }

    // 2. Escribir las cadenas (Key, Value)
    // Nota: Las llaves máximo pueden tener 15 caracteres.
    err = nvs_set_str(my_handle, "ssid", ssid);
    err |= nvs_set_str(my_handle, "pass", password); // Operador OR para acumular errores

    if (err != ESP_OK) {
        printf("[NVS ERROR] Falló la escritura en caché RAM.\n");
        nvs_close(my_handle);
        return false;
    }

    // 3. EL COMMIT OBLIGATORIO
    // Sin esta instrucción, al irse la luz los datos desaparecerán.
    err = nvs_commit(my_handle);
    if (err != ESP_OK) {
        printf("[NVS ERROR] Falló la quema en Silicio (Commit).\n");
    } else {
        printf("[NVS SUCCESS] ¡Credenciales selladas en la memoria Flash!\n");
    }

    // 4. Cerrar el manejador para liberar recursos de memoria
    nvs_close(my_handle);
    
    return (err == ESP_OK);
}

// =======================================================
// LECTURA: EXTRAER CREDENCIALES (Llamado al arrancar)
// =======================================================
/**
 * @brief  Extrae las credenciales. Si no existen, retorna false (La máquina es virgen).
 * @param  ssid_out: Buffer donde se copiará el nombre de la red
 * @param  pass_out: Buffer donde se copiará la contraseña
 * @retval bool: true si existen credenciales, false si la memoria está vacía.
 */
bool Leer_Credenciales_WiFi(char* ssid_out, char* pass_out, size_t max_len) {
    
    nvs_handle_t my_handle;
    esp_err_t err;
    
    // 1. Abrir en Modo Solo Lectura (Protege contra escrituras accidentales)
    err = nvs_open("config_red", NVS_READONLY, &my_handle);
    if (err != ESP_OK) {
        // ESP_ERR_NVS_NOT_FOUND significa que el Namespace nunca ha sido creado.
        printf("[NVS INFO] Bóveda vacía. Dispositivo Virgen.\n");
        return false;
    }

    // 2. Extracción. NVS requiere que pases una variable con el tamaño disponible 
    // de tu buffer, y NVS lo modificará al tamaño real leído.
    size_t required_size_ssid = max_len;
    size_t required_size_pass = max_len;

    err = nvs_get_str(my_handle, "ssid", ssid_out, &required_size_ssid);
    if (err != ESP_OK) {
        nvs_close(my_handle);
        return false; 
    }

    err = nvs_get_str(my_handle, "pass", pass_out, &required_size_pass);
    if (err != ESP_OK) {
        nvs_close(my_handle);
        return false;
    }

    // 3. Cerrar
    nvs_close(my_handle);
    
    printf("[NVS SUCCESS] Credenciales leídas. SSID: %s\n", ssid_out);
    return true;
}

// =======================================================
// LÓGICA DE ARRANQUE EN EL MAIN
// =======================================================
/*
void app_main(void) {
    // 1. Inicializar la Bóveda Global (Día 221)
    ESP_ERROR_CHECK(nvs_flash_init());

    char ssid[32];
    char pass[64];

    // 2. Árbol de Decisión Inicial
    if (Leer_Credenciales_WiFi(ssid, pass, sizeof(ssid))) {
        
        printf("[BOOT] Modo Estación (Cliente). Conectando a %s...\n", ssid);
        Iniciar_WiFi_STA(ssid, pass);
        // ... (Iniciar MQTT) ...
        
    } else {
        
        printf("[BOOT] Modo Access Point. Iniciando Portal Cautivo...\n");
        Iniciar_WiFi_AP("DISPENSADOR_SETUP");
        iniciar_servidor_web(); // (Día 224)
        
        // La máquina se quedará aquí hasta que el usuario guarde datos 
        // y el Portal Cautivo ejecute esp_restart().
    }
}
*/
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Ciclo de Vida Autónomo y Persistencia de Configuración Dinámica.

El pecado capital del desarrollo IoT amateur es poner la contraseña de tu red doméstica quemada directamente en el código fuente de C (`#define WIFI_PASS "mi_perro_123"`). Si haces eso, el código de tu repositorio no es genérico, y si llevas la máquina a una exposición comercial, no podrás conectarla al internet del salón sin llevar tu laptop, abrir el entorno de desarrollo y recompilar todo el proyecto. Totalmente inviable para la industria.

Al implementar **NVS (Non-Volatile Storage)** combinado con el **Portal Cautivo**, tu firmware ahora es 100% agnóstico al lugar de instalación. Puedes compilar tu archivo `.bin` una sola vez y grabar miles de tarjetas electrónicas en la fábrica con el mismo software genérico. Cuando el Despachador de Gas arranca por primera vez en la gasolinera, la función `Leer_Credenciales_WiFi` falla lógicamente, la máquina comprende que "nació ayer", levanta su Portal Cautivo, absorbe las credenciales del técnico, y ejecuta la sagrada operación `nvs_commit()`. A partir de ese reset, la máquina es "adulta", lee su configuración del silicio, se conecta a la red de la empresa y comienza a producir dinero. Tu sistema ha adquirido memoria a largo plazo y autonomía de despliegue absoluto.

📝 Resumen del Día