Tienes un datalogger que tarda una eternidad en arrancar? Conectas la placa, ves el LED encendido y pasan diez, quince, hasta diecisiete segundos antes de que tu código empiece a correr. No es tu programa: es la tarjeta microSD, o más bien la forma en que el sistema operativo del computador la escanea cada vez que la placa aparece como una unidad USB. La buena noticia es que se arregla con tres cambios chicos que no cuestan nada y que están respaldados por mediciones reales.

En esta guía vas a aprender a exprimir el rendimiento de las tarjetas microSD en proyectos con CircuitPython: cortar el retardo de arranque casi a cero, elegir el driver y el formato correctos, y estructurar tus escrituras para registrar datos más rápido. Todo aplica tanto a placas con ranura microSD integrada (como la Metro RP2350 o el Feather RP2040 Adalogger) como a un Arduino o ESP32 al que le sumas un lector microSD por SPI. Esta versión es una adaptación al español del trabajo de Mikey Sklar publicado en el Adafruit Learning System, con un par de explicaciones extra y precios locales.

Placas CircuitPython con ranura microSD integrada junto a varias tarjetas microSD

El 90% de la ganancia está en 3 cambios

Antes de entrar en detalle, estos son los tres ajustes que más mueven la aguja. Si solo haces estos tres, ya ganaste casi todo:

  1. Desactiva el USB Mass Storage. Este cambio elimina por completo la ventana lenta del arranque: tu código corre en el instante en que la placa recibe energía. Es el de mayor impacto, lejos.
  2. Usa una tarjeta chica (2GB o menos, FAT16). Las tarjetas de ese tamaño se formatean en FAT16 de forma automática. En macOS esto baja la espera de arranque de 17 segundos a 0,6 segundos. El formato del sistema de archivos importa más que la velocidad de la tarjeta.
  3. Usa las librerías sdcardio o sdioio, no adafruit_sdcard. Son más rápidas, vienen integradas en el firmware y cambiar no cuesta nada.

Y si estás eligiendo hardware para un proyecto nuevo: prefiere un microcontrolador con periférico SDMMC nativo, como el RP2350, el ESP32-S3 o el STM32F405. El modo SDIO entrega lecturas de 2 a 4 veces más rápidas que SPI sin escribir una sola línea de código adicional. Las tres ganancias de arriba aplican sin importar qué placa uses.

Gráfico de tiempo de arranque del mejor al peor caso según USB, formato y sistema operativo

Ese gráfico resume todo el asunto. El peor caso (USB encendido, FAT32, macOS) son 17 segundos. El mejor (USB apagado, FAT32) baja de 0,1 segundos. Las pruebas se corrieron en una Adafruit Metro RP2040 con una tarjeta SanDisk Clase 10 de 16GB en FAT32 y una de 64MB en FAT16.

Por qué pasa esto (la parte que el tutorial original no explica del todo)

Vale la pena entender el mecanismo, porque así sabes cuándo un cambio te va a servir. Cuando CircuitPython corre, varias placas comparten la tarjeta con tu computador por USB, igual que un pendrive: ves tus archivos directo en el Finder o el Explorador, sin sacar la tarjeta ni usar adaptador. Esa comodidad tiene un costo. Al conectar, el sistema operativo escanea de inmediato la tarjeta entera, y tu código queda esperando varios segundos antes de arrancar. Y mientras corre, el host sigue leyendo en segundo plano en cada escritura a la SD.

La pieza clave es la tabla FAT. El host la lee completa en cada montaje. Una tarjeta SDSC de 2GB o menos genera una tabla FAT16 de apenas 64KB. Una tarjeta grande en FAT32 puede tener una tabla de varios megabytes. La diferencia entre leer 64KB y leer 4MB es 64 veces. Por eso una tarjeta chica y vieja le gana a una grande y nueva en tiempo de arranque: no es velocidad, es cuánto tiene que leer el host antes de soltar el control.

Elegir el driver correcto

CircuitPython tiene tres librerías para tarjetas SD y la diferencia de velocidad entre ellas es enorme. Elige según tu placa:

  • sdioio: para placas con periférico SDMMC nativo. Requiere cablear las cuatro líneas de datos. Es la lectura SD más rápida disponible en CircuitPython.
  • sdcardio: viene integrada en el firmware, no hay nada que instalar. En placas RP2350 (Metro RP2350, Fruit Jam) la tarjeta se auto monta al arrancar sin escribir código.
  • adafruit_sdcard: Python puro, alrededor de 5 veces más lento que sdcardio. Úsala solo cuando sdcardio no esté compilada en el firmware de tu placa.

Una confusión común: en el RP2350 el módulo correcto es sdcardio, no sdioio, porque ahí el SDIO viene integrado y no se expone como módulo separado. En cambio el ESP32-S3, el STM32F405 y el SAMD51 exponen el periférico SDMMC crudo, así que usan sdioio. El RP2040 y el nRF52840 no tienen hardware SDMMC: siempre van por SPI sin importar cómo cablees.

Este diagrama te ayuda a decidir de un vistazo según el rendimiento que necesites:

Diagrama de decisión para elegir el módulo microSD en CircuitPython según velocidad y MCU

Desactivar la presentación USB Mass Storage

Este es el cambio de mayor impacto sobre el tiempo de arranque. Al impedir que la tarjeta SD aparezca como una unidad de almacenamiento masivo (MSC) frente al USB, eliminas todo el escaneo del sistema operativo.

El único costo: no vas a poder acceder a la tarjeta desde tu computador mientras CircuitPython está corriendo. Para un datalogger que solo registra datos, ese intercambio vale totalmente la pena.

La forma fácil es por settings.toml. Agrega esta línea, requiere CircuitPython 10.2.1 o superior, y necesitas un ciclo de energía completo (desconectar y reconectar): un soft reset no alcanza.

TOML
CIRCUITPY_SDCARD_USB = false

La forma difícil es con un flag de compilación personalizado de CircuitPython. Para builds custom, el flag es:

Text
CIRCUITPY_SDCARD_USB=0

Tamaño y formato de la tarjeta

Como ya viste, el mayor impacto después del USB está en el formato. La regla práctica: usa la tarjeta más chica que te alcance para tus datos. Una SDSC de 2GB o menos viene en FAT16 y reduce mucho el retardo de arranque, porque el host lee la tabla FAT completa en cada montaje y esa tabla es diminuta.

Esta es la relación entre capacidad, sistema de archivos y soporte en CircuitPython:

Tipo de tarjeta Capacidad Sistema de archivos por defecto Soporte en CircuitPython
SDSC 2GB o menos FAT16 Totalmente soportado
SDHC 4GB a 32GB FAT32 Totalmente soportado
SDXC 64GB a 2TB exFAT Solo placas con CIRCUITPY_FULL_BUILD=1
SDUC más de 2TB exFAT No soportado

Un dato útil sobre FAT16: todas las tarjetas SDSC producen una tabla FAT16 de unos 64KB sin importar su capacidad. Los formateadores eligen el tamaño de cluster más chico que mantenga el conteo de clusters bajo el techo de 65.525 de FAT16, lo que siempre cae cerca de 32.768 clusters por 2 bytes, o sea 64KB. Para tarjetas SDHC en FAT32, el formateador oficial de la SD Association usa clusters más grandes para achicar la tabla FAT y reducir la amplificación de escritura. Windows, en cambio, formatea por defecto con clusters de 4KB, lo que infla la tabla FAT pero desperdicia menos espacio en archivos chicos.

Patrones de escritura: cómo escribir importa tanto como la velocidad de la tarjeta

Aquí hay una trampa silenciosa que arruina el rendimiento de muchos dataloggers. Cada close() fuerza un flush de la tabla FAT y de la entrada de directorio, y eso es muy lento. Si abres y cierras el archivo en cada lectura, estás pagando ese costo miles de veces. Cuatro reglas para escribir bien:

  • Abre el archivo una sola vez, fuera del bucle de captura, no en cada iteración.
  • Usa modo binario (wb o ab), no texto. El modo texto hace codificación en cada escritura.
  • Agrupa las escrituras chicas. Cada cruce de frontera de sector es un read modify write en la tarjeta. Acumula los datos en un bytearray y escríbelos todos juntos.
  • flush() en lugar de close(). flush() confirma los datos sin cerrar el archivo. El intercambio de seguridad: si se corta la energía entre dos flush, pierdes lo que quedó en buffer desde el último flush. Por eso conviene llamar a flush() en checkpoints naturales, no después de cada byte.

Así se ve el código lento, el que abre y cierra en cada escritura:

Python
while capturing:
    with open("/sd/log.bin", "a") as f:	# text mode 
        f.write(data)  # opens and closes on every write

Y así se ve el código rápido, que abre una vez, escribe en binario y confirma con flush en checkpoints:

Python
f = open("/sd/log.bin", "ab")
while capturing:
    f.write(data)
    if checkpoint:
        f.flush()  # commits data without closing
f.close()

Frecuencia máxima del bus SPI

Si vas por SPI (por ejemplo con un lector microSD conectado a un Arduino o ESP32), puedes ajustar la frecuencia del reloj. Estos son los valores de referencia:

  • 4 MHz: seguro para cables largos o protoboard.
  • 8 MHz: valor conservador por defecto.
  • 16 a 24 MHz: buen techo para pistas cortas de PCB.
  • 25 MHz: límite de la especificación SPI para SD; no lo subas más. Algunas tarjetas corren a 40 o 50 MHz pero arriesgas corrupción silenciosa y no ganas nada: lo que te frena es el sistema de archivos, no el reloj.

Para ajustar la frecuencia con sdcardio en modo SPI:

Python
sd = sdcardio.SDCard(spi, board.SD_CS, baudrate=24_000_000)  # up to 25 MHz

En SDIO hay más margen: el modo High Speed está especificado hasta 50 MHz. A diferencia de SPI, subir el reloj SDIO sí rinde de verdad, porque las lecturas escalan con el reloj y tienes 4 líneas de datos trabajando. Los 40 MHz están confirmados funcionando en el ESP32-S3 con sdioio, donde la velocidad de lectura subió notoriamente frente a las placas SPI. Para ajustarla con sdioio:

Python
sd = sdioio.SDCard(
  clock=board.SDIO_CLOCK,
  command=board.SDIO_COMMAND,
  data=board.SDIO_DATA,
  frequency=40_000_000   # 40 MHz confirmed on ESP32-S3
  )

Rendimiento de la tarjeta y la trampa de las velocidades UHS

No te dejes llevar por las etiquetas llamativas. Cualquier tarjeta Clase 10 es más que suficiente: CircuitPython topa alrededor de 2 MB/s, y hasta las tarjetas baratas llegan a eso. Las velocidades UHS-I (U3, A1, A2, SDR50) no aplican en modo SPI y no te van a ayudar. Una tarjeta carísima no te da nada extra en este contexto.

Primer plano de una tarjeta microSD SanDisk Ultra de 16GB con el símbolo Clase 10 destacado

Ese círculo rojo marca el símbolo de Clase 10, que es justamente lo único que te importa mirar al comprar.

SPI contra SDIO: cuándo vale la pena la interfaz más rápida

La nomenclatura confunde porque el mismo resultado (la velocidad SDIO) se alcanza con nombres de módulo distintos según el chip. Esta tabla muestra exactamente qué usa cada MCU por debajo:

MCU Periférico SD por hardware Módulo CircuitPython Modo usado Placas de ejemplo
RP2350 SDMMC nativo sdcardio SDIO 4 bits (auto detectado) Metro RP2350, Fruit Jam
ESP32-S3 SDMMC nativo sdioio SDIO 4 bits (breakout cableado) Feather ESP32-S3 más breakout
STM32F405 SDMMC nativo sdioio SDIO 4 bits Feather STM32F405 Express
SAMD51 SDHC nativo sdioio SDIO 4 bits Grand Central M4, Metro M4
RP2040 Ninguno sdcardio SPI 1 bit solamente Feather RP2040 Adalogger
SAMD21 Ninguno sdcardio SPI 1 bit solamente Metro M0, Feather M0
nRF52840 Ninguno (QSPI es solo flash) sdcardio SPI 1 bit solamente Feather nRF52840

Benchmarks: throughput medido en placas y drivers reales

Para cerrar, estos son los números medidos de escritura, lectura y registro de líneas por segundo. Sirven para calibrar expectativas: nota cómo la combinación SDIO más el driver correcto se despega del resto, y cómo adafruit_sdcard sobre SPI queda muy por detrás.

Placa / Runtime Driver Interfaz Escritura Lectura Registro
Metro RP2350 con Arduino SdFat SdFat SDIO 568 kB/s 10.309 kB/s 74.162 líneas/s
Metro RP2350 con CircuitPython sdcardio SDIO 696 kB/s 1.333 kB/s 15.151 líneas/s
STM32F405 con CircuitPython sdioio SDIO 593 kB/s 2.287 kB/s medición no disponible
Fruit Jam (RP2350B) con CircuitPython sdcardio SDIO 490 kB/s aprox 980 kB/s aprox 12.000 líneas/s aprox
Feather ESP32-S3 más breakout con CircuitPython sdioio SDIO 4 bits 470 kB/s 1.456 kB/s 11.907 líneas/s
Feather RP2040 Adalogger con CircuitPython sdcardio SPI 468 kB/s 595 kB/s 9.978 líneas/s
Grand Central M4 con CircuitPython adafruit_sdcard SPI 120 kB/s aprox 160 kB/s aprox 160 líneas/s aprox
Feather RP2350 con CircuitPython adafruit_sdcard SPI 19 kB/s 24 kB/s 479 líneas/s

Fíjate en la última fila: adafruit_sdcard sobre SPI escribe a 19 kB/s, mientras que sdcardio sobre SDIO en la misma familia de chip llega a cientos de kB/s. Cambiar de librería, que no cuesta nada, te puede dar un salto de más de 30 veces.

Variantes y mejoras

Una vez que tienes la base andando, hay varias formas de llevar el proyecto más lejos:

  • Datalogger con marca de tiempo: combina la tarjeta microSD con un módulo RTC DS3231 por I2C para que cada línea del log lleve fecha y hora exactas, incluso después de un corte de energía. Ideal para registrar temperatura, humedad o consumo a lo largo de días.
  • Buffer en RAM antes de escribir: si tu sensor entrega datos muy rápido, acumula varias muestras en un bytearray en memoria y escribe a la SD solo cada cierto número de muestras. Reduces los cruces de sector y le sacas más throughput a la tarjeta, justo como muestran los benchmarks.
  • Rotación de archivos por día: en lugar de un único log gigante, abre un archivo nuevo por fecha (por ejemplo log_2026_05_30.bin). Te queda más ordenado para analizar y evitas tablas de directorio enormes que ralentizan el montaje.

Personalización para Chile

Las placas Adafruit del tutorial original (Metro RP2350, Feather RP2040 Adalogger) traen la ranura microSD integrada, pero no es la única forma de aplicar esta guía. Si ya tienes un Arduino o un ESP32 sin ranura, en MechatronicStore consigues lo necesario para sumar lectura de SD por SPI, que es justo el camino que cubren las secciones de driver sdcardio y frecuencia SPI:

  • Módulo datalogger para tarjeta microSD (SKU G-014) por $3.800: lector microSD compacto que se conecta por SPI. Perfecto para un datalogger con Arduino o ESP32.
  • Shield datalogger para tarjeta de memoria SD (SKU G-016) por $3.990: usa tarjeta SD de tamaño completo y se monta directo sobre un Arduino Uno, cómodo para prototipos.

Una recomendación local concreta: para que esta guía rinda, no compres la tarjeta más grande, compra la más chica que te sirva. Una microSD Clase 10 de 2GB o menos, formateada en FAT16, te dará el mejor tiempo de arranque. Si solo encuentras tarjetas de 8GB o 16GB, igual funcionan, pero recuerda que el formato pesa más que la capacidad.

Recursos

  • Tutorial original (inglés): SD card Optimization with CircuitPython por Mikey Sklar, Adafruit Learning System.
  • Datos completos de benchmark: el set de datos publicado por el autor, referenciado en la guía original de Adafruit.
  • Documentación adicional: librería sdcardio y librería sdioio en la documentación oficial de CircuitPython (docs.circuitpython.org).

Versión chilena con componentes en stock local en MechatronicStore.