sistema-archivos-tarjeta-sd-nanoframework

Tarjetas SD y datalogging en nanoFramework

  • 6 min

Un sistema de archivos es una forma organizada de guardar y leer ficheros desde el microcontrolador.

Ya sabemos guardar nuestra contraseña del Wi-Fi en la memoria interna. Pero, ¿qué pasa si queremos guardar un historial de temperatura de todo un año? ¿O si queremos que nuestro dispositivo sirva archivos HTML e imágenes grandes desde un servidor web?

La memoria interna del ESP32 es limitada y comparte espacio con otras particiones. Para grandes volúmenes de datos, podemos usar una tarjeta SD.

En nanoFramework, trabajar con una SD es sorprendentemente parecido a trabajar con un pendrive en Windows: el sistema la “monta” como una unidad de disco (normalmente D:\) y nosotros usamos System.IO para leer y escribir.

Para este tutorial necesitas instalar el paquete NuGet: nanoFramework.System.IO.FileSystem

Unidades lógicas: almacenamiento interno y SD

Antes de conectar cables, entendamos cómo organiza nanoFramework el almacenamiento. A diferencia de Arduino (donde usas librerías distintas como SPIFFS.h o SD.h), aquí todo se unifica bajo el mismo paraguas de System.IO.

El sistema asigna Letras de Unidad a los distintos almacenamientos físicos:

  1. I:\ (Internal): Es la partición de usuario en la flash cuando el target la incluye. Es pequeña y resulta adecuada para configuración.
  2. D:\, E:\… (External): Son dispositivos extraíbles, como tarjetas SD o memorias USB (en chips STM32). Son enormes (GBs), pero pueden no estar presentes si el usuario saca la tarjeta. Ideal para Logs y Assets.

Conexión mediante SPI o SDMMC

Para conectar una tarjeta SD al ESP32 tenemos dos modos:

  1. SPI (1-bit): Usa 4 cables (MISO, MOSI, SCK, CS). Es más lento, pero compatible con todos los pines y placas. Es el más común en el mundo maker.
  2. SDMMC (4-bit): Usa un protocolo nativo más rápido. Requiere pines específicos y es más complejo de rutear en la placa, pero ofrece mayor velocidad.

Para este curso, asumiremos que usas un módulo lector de tarjetas microSD estándar conectado por SPI.

  • CS: GPIO 5 (habitual en VSPI)
  • SCK: GPIO 18
  • MISO: GPIO 19
  • MOSI: GPIO 23
  • VCC: la tensión indicada por el módulo (la tarjeta trabaja a 3,3 V; algunos adaptadores incorporan regulador)
  • GND: GND

Detectar la tarjeta con StorageEventManager

A diferencia de la memoria interna I:\ que siempre está ahí, la tarjeta SD es extraíble. No podemos intentar escribir en D:\ nada más arrancar, porque quizás el usuario aún no ha metido la tarjeta o el sistema operativo aún no la ha montado.

Para gestionar esto, nanoFramework nos ofrece la clase estática StorageEventManager.

using System;
using System.IO;
using System.Diagnostics;
using nanoFramework.System.IO.FileSystem; // Namespace necesario

public class Program
{
    public static void Main()
    {
        // 1. Nos suscribimos a los eventos de almacenamiento
        StorageEventManager.RemovableDeviceInserted += StorageEventManager_RemovableDeviceInserted;
        StorageEventManager.RemovableDeviceRemoved += StorageEventManager_RemovableDeviceRemoved;

        Debug.WriteLine("Sistema listo. Inserta una tarjeta SD...");

        // En un programa real, aquí no haríamos nada hasta que salte el evento.
        // Ojo: Si la tarjeta YA estaba dentro al arrancar, el evento saltará casi inmediatamente
        // tras el inicio.
        
        Thread.Sleep(Timeout.Infinite);
    }

    private static void StorageEventManager_RemovableDeviceInserted(object sender, RemovableDeviceEventArgs e)
    {
        Debug.WriteLine($"¡Dispositivo detectado en la ruta: {e.Path}!");
        
        // e.Path suele ser "D:\" o similar
        // Ahora sí es seguro escribir
        EscribirLog(e.Path);
    }

    private static void StorageEventManager_RemovableDeviceRemoved(object sender, RemovableDeviceEventArgs e)
    {
        Debug.WriteLine($"Dispositivo extraído: {e.Path}");
        // Aquí deberíamos detener cualquier escritura en curso para evitar corrupciones
    }
}
Copied!

Configuración del Hardware: Dependiendo de tu placa y versión de firmware (image), es posible que necesites configurar los pines SPI antes de que el sistema reconozca la SD. En versiones modernas (nanoFramework.System.Device.Mmc), puedes necesitar montar la tarjeta explícitamente por código si tu firmware no tiene una configuración por defecto. Revisa la documentación de tu placa específica.

Crear un datalogger CSV

Vamos a crear el “Hola Mundo” de los sistemas industriales: un registrador de datos. Guardaremos una línea de texto con la fecha y un valor simulado en un archivo .csv cada vez que se detecte la tarjeta.

El formato CSV es ideal porque puedes sacar la tarjeta, meterla en tu PC y abrirla directamente con Excel.

private static void EscribirLog(string unidad)
{
    // Construimos la ruta completa. Ej: "D:\datalog.csv"
    string rutaArchivo = Path.Combine(unidad, "datalog.csv");

    try
    {
        // Simulamos la lectura de un sensor
        double temperatura = 24.5;
        double humedad = 60.2;
        string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");

        // Formato CSV: Fecha,Temperatura,Humedad
        string linea = $"{timestamp},{temperatura},{humedad}\r\n";

        // AppendAllText es maravilloso:
        // 1. Si el archivo no existe, lo crea.
        // 2. Si existe, añade el texto al final (no sobrescribe).
        // 3. Abre, escribe y cierra el archivo de forma segura.
        File.AppendAllText(rutaArchivo, linea);

        Debug.WriteLine($"Datos guardados en {rutaArchivo}");
        
        // Vamos a leerlo para verificar (solo las últimas líneas en un caso real)
        if (File.Exists(rutaArchivo))
        {
            Debug.WriteLine("Tamaño actual del archivo: " + new FileInfo(rutaArchivo).Length + " bytes");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"Error escribiendo en SD: {ex.Message}");
    }
}
Copied!

Listar archivos y directorios

System.IO es muy potente. Podemos explorar qué hay dentro de la tarjeta. Esto es útil si, por ejemplo, quieres buscar un archivo de configuración específico o reproducir todas las canciones .mp3 de una carpeta.

public static void ListarContenido(string ruta)
{
    // Obtener directorios
    var directorios = Directory.GetDirectories(ruta);
    foreach (var dir in directorios)
    {
        Debug.WriteLine($"[DIR] {dir}");
    }

    // Obtener archivos
    var archivos = Directory.GetFiles(ruta);
    foreach (var archivo in archivos)
    {
        Debug.WriteLine($"[FILE] {archivo}");
    }
}
Copied!

Buenas prácticas con tarjetas SD

Trabajar con sistemas de archivos físicos tiene sus riesgos:

  1. Corrupción de Datos: Si se va la luz o extraes la tarjeta MIENTRAS se está escribiendo (File.AppendAllText), el sistema de archivos (FAT32) puede corromperse.
  • Solución: Intenta mantener los archivos abiertos el menor tiempo posible. AppendAllText es bueno porque abre y cierra rápido.
  1. FAT32: nanoFramework generalmente soporta FAT16 y FAT32. Asegúrate de formatear tu tarjeta en FAT32 (no exFAT ni NTFS) en Windows antes de usarla.
  2. Compatibilidad: Aunque soporta tarjetas de 32GB o más, para microcontroladores suele ser más estable usar tarjetas de 4GB a 16GB de clase 10.

Con StorageEventManager y System.IO podemos registrar datos en una SD y reaccionar a su inserción o retirada. Para un equipo alimentado por batería, aún debemos reducir el tiempo que permanece activo.