El almacenamiento persistente es guardar datos aunque el microcontrolador se reinicie.
Hasta ahora, nuestro código vivía en la memoria RAM. La RAM es rápida, pero volátil: si quitas la energía, los datos desaparecen.
Un dispositivo puede necesitar guardar datos de forma permanente:
- Credenciales Wi-Fi (SSID/Pass).
- Tokens de autenticación (API Keys).
- Preferencias de usuario (Umbrales de alarma, colores de LEDs).
En Arduino solíamos usar la librería EEPROM o Preferences. En nanoFramework, subimos de nivel y utilizamos el espacio de nombres System.IO.
Vamos a usar una API familiar de archivos, aplicada a la memoria flash interna del ESP32.
Para este tutorial necesitas instalar el paquete NuGet: nanoFramework.System.IO.FileSystem
El sistema de archivos interno
El ESP32 tiene una memoria Flash (donde se guarda tu código). nanoFramework reserva una partición de esa memoria para que puedas usarla como si fuera un “disco duro” muy pequeño.
Dependiendo de cómo esté configurado tu firmware (aunque es el estándar), esta unidad interna suele montarse en la unidad I:\ (de Internal).
Esto significa que podemos crear, leer y escribir archivos en I:\config.txt usando clases estándar de .NET.
Guardar texto plano
Vamos a empezar por lo básico: guardar un mensaje simple.
using System;
using System.Diagnostics;
using System.IO; // <--- Espacio de nombres clave
public static void EscribirArchivo()
{
// Ruta en el almacenamiento interno
string ruta = "I:\\nota.txt";
try
{
// Verificar si existe
if (File.Exists(ruta))
{
Debug.WriteLine("El archivo ya existe. Borrando...");
File.Delete(ruta);
}
// Escribir texto
// WriteAllText abre, escribe y cierra el archivo automáticamente
File.WriteAllText(ruta, "Hola desde el pasado (antes del reinicio)");
Debug.WriteLine("Archivo guardado correctamente.");
}
catch (Exception ex)
{
Debug.WriteLine($"Error escribiendo archivo: {ex.Message}");
}
}
public static void LeerArchivo()
{
string ruta = "I:\\nota.txt";
if (File.Exists(ruta))
{
string contenido = File.ReadAllText(ruta);
Debug.WriteLine($"Leído del disco: {contenido}");
}
else
{
Debug.WriteLine("El archivo no existe.");
}
}Si ejecutas EscribirArchivo(), reinicias tu ESP32 (cortando corriente) y luego ejecutas LeerArchivo(), verás que el mensaje sigue ahí. Justo lo que queríamos.
Guardar configuración JSON
Para una configuración con varios campos, podemos combinar System.IO con nanoFramework.Json.
¿Recuerdas la lección de JSON? Vamos a combinar System.IO con nanoFramework.Json para crear un sistema de configuración profesional.
Definir la clase de configuración
public class AppConfig
{
public string Ssid { get; set; } = "MiWifi";
public string Password { get; set; } = "1234";
public int IntervaloLectura { get; set; } = 60; // Segundos
public string DeviceName { get; set; } = "Sensor_01";
}Crear el gestor de configuración
Vamos a crear una clase estática que se encargue de cargar y guardar esta configuración.
using System.IO;
using nanoFramework.Json;
public static class ConfigurationManager
{
private const string RutaConfig = "I:\\appsettings.json";
public static void Guardar(AppConfig config)
{
// 1. Serializar el objeto a texto JSON
string json = JsonConvert.SerializeObject(config);
// 2. Guardar en disco
File.WriteAllText(RutaConfig, json);
}
public static AppConfig Cargar()
{
// Si no existe el archivo, devolvemos una config por defecto
if (!File.Exists(RutaConfig))
{
return new AppConfig();
}
// 1. Leer del disco
string json = File.ReadAllText(RutaConfig);
// 2. Deserializar
try
{
return (AppConfig)JsonConvert.DeserializeObject(json, typeof(AppConfig));
}
catch
{
// Si el archivo está corrupto, devolvemos default
return new AppConfig();
}
}
}Usarlo desde Main
Ahora tu programa principal es mucho más limpio y robusto.
public static void Main()
{
// Cargar configuración al inicio
AppConfig miConfig = ConfigurationManager.Cargar();
Debug.WriteLine($"Iniciando dispositivo: {miConfig.DeviceName}");
Debug.WriteLine($"Conectando a Wi-Fi: {miConfig.Ssid}...");
// ... lógica de conexión ...
// Supongamos que recibimos una orden por MQTT para cambiar el intervalo
// ...
miConfig.IntervaloLectura = 120;
// Guardamos para que el cambio persista tras reiniciar
ConfigurationManager.Guardar(miConfig);
Debug.WriteLine("Configuración actualizada guardada.");
Thread.Sleep(Timeout.Infinite);
}La memoria flash se desgasta
Es muy importante entender el hardware que hay debajo. La memoria Flash de un ESP32 tiene un número limitado de ciclos de escritura (típicamente entre 10,000 y 100,000).
- Lectura: Infinita. Puedes leer
Cargar()todas las veces que quieras. - Escritura: Limitada.
Mala práctica: Guardar la configuración dentro de un bucle cada segundo. Destruirás la memoria Flash de tu chip en pocas semanas. Buena práctica: Guardar solo cuando algo cambia (ej. el usuario cambia una configuración desde la web) o cada intervalos muy largos (ej. contadores acumulados una vez cada hora).
No guardes contraseñas o tokens como JSON en texto plano sin analizar el riesgo. El sistema de archivos aporta persistencia, no cifrado.
Tarjetas SD para más capacidad
¿Y si queremos guardar un Log de Datos (Datalogger)? Si queremos guardar la temperatura cada 10 segundos durante un año, la memoria interna de 4MB se llenará enseguida.
Para eso usamos tarjetas SD. nanoFramework soporta tarjetas SD conectadas por SPI o SDMMC.
El código es casi idéntico, solo cambia la unidad. Al montar la tarjeta SD, normalmente aparecerá como D:\ (o la letra que asigne el sistema).
// Escribir en la tarjeta SD
File.AppendAllText("D:\\datalog.csv", $"{DateTime.UtcNow},24.5\r\n");Nota: Para usar la SD necesitas código de inicialización adicional para montar la unidad (StorageEventManager), pero la lógica de lectura/escritura es la misma System.IO.