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 codigo empiece a correr. No es tu programa: es la tarjeta microSD, o mas 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 estan respaldados por mediciones reales.

En esta guia 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 mas rapido. 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 version es una adaptacion al espanol 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 esta en 3 cambios

Antes de entrar en detalle, estos son los tres ajustes que mas 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 codigo corre en el instante en que la placa recibe energia. Es el de mayor impacto, lejos.
  2. Usa una tarjeta chica (2GB o menos, FAT16). Las tarjetas de ese tamano se formatean en FAT16 de forma automatica. En macOS esto baja la espera de arranque de 17 segundos a 0,6 segundos. El formato del sistema de archivos importa mas que la velocidad de la tarjeta.
  3. Usa las librerias sdcardio o sdioio, no adafruit_sdcard. Son mas rapidas, vienen integradas en el firmware y cambiar no cuesta nada.

Y si estas eligiendo hardware para un proyecto nuevo: prefiere un microcontrolador con periferico SDMMC nativo, como el RP2350, el ESP32-S3 o el STM32F405. El modo SDIO entrega lecturas de 2 a 4 veces mas rapidas que SPI sin escribir una sola linea de codigo adicional. Las tres ganancias de arriba aplican sin importar que placa uses.

Grafico de tiempo de arranque del mejor al peor caso segun USB, formato y sistema operativo

Ese grafico 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 que pasa esto (la parte que el tutorial original no explica del todo)

Vale la pena entender el mecanismo, porque asi sabes cuando 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 codigo 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 cuanto tiene que leer el host antes de soltar el control.

Elegir el driver correcto

CircuitPython tiene tres librerias para tarjetas SD y la diferencia de velocidad entre ellas es enorme. Elige segun tu placa:

  • sdioio: para placas con periferico SDMMC nativo. Requiere cablear las cuatro lineas de datos. Es la lectura SD mas rapida 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 codigo.
  • adafruit_sdcard: Python puro, alrededor de 5 veces mas lento que sdcardio. Usala solo cuando sdcardio no este compilada en el firmware de tu placa.

Una confusion comun: en el RP2350 el modulo correcto es sdcardio, no sdioio, porque ahi el SDIO viene integrado y no se expone como modulo separado. En cambio el ESP32-S3, el STM32F405 y el SAMD51 exponen el periferico SDMMC crudo, asi que usan sdioio. El RP2040 y el nRF52840 no tienen hardware SDMMC: siempre van por SPI sin importar como cablees.

Este diagrama te ayuda a decidir de un vistazo segun el rendimiento que necesites:

Diagrama de decision para elegir el modulo microSD en CircuitPython segun velocidad y MCU

Desactivar la presentacion 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 unico costo: no vas a poder acceder a la tarjeta desde tu computador mientras CircuitPython esta corriendo. Para un datalogger que solo registra datos, ese intercambio vale totalmente la pena.

La forma facil es por settings.toml. Agrega esta linea, requiere CircuitPython 10.2.1 o superior, y necesitas un ciclo de energia completo (desconectar y reconectar): un soft reset no alcanza.

TOML
CIRCUITPY_SDCARD_USB = false

La forma dificil es con un flag de compilacion personalizado de CircuitPython. Para builds custom, el flag es:

Text
CIRCUITPY_SDCARD_USB=0

Tamano y formato de la tarjeta

Como ya viste, el mayor impacto despues del USB esta en el formato. La regla practica: usa la tarjeta mas 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 relacion 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 mas de 2TB exFAT No soportado

Un dato util sobre FAT16: todas las tarjetas SDSC producen una tabla FAT16 de unos 64KB sin importar su capacidad. Los formateadores eligen el tamano de cluster mas 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 mas grandes para achicar la tabla FAT y reducir la amplificacion 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: como escribir importa tanto como la velocidad de la tarjeta

Aqui 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, estas 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 iteracion.
  • Usa modo binario (wb o ab), no texto. El modo texto hace codificacion 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 escribelos todos juntos.
  • flush() en lugar de close(). flush() confirma los datos sin cerrar el archivo. El intercambio de seguridad: si se corta la energia entre dos flush, pierdes lo que quedo en buffer desde el ultimo flush. Por eso conviene llamar a flush() en checkpoints naturales, no despues de cada byte.

Asi se ve el codigo 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 asi se ve el codigo rapido, 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 maxima del bus SPI

Si vas por SPI (por ejemplo con un lector microSD conectado a un Arduino o ESP32), podes 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: limite de la especificacion SPI para SD; no lo subas mas. Algunas tarjetas corren a 40 o 50 MHz pero arriesgas corrupcion 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 mas margen: el modo High Speed esta especificado hasta 50 MHz. A diferencia de SPI, subir el reloj SDIO si rinde de verdad, porque las lecturas escalan con el reloj y tienes 4 lineas de datos trabajando. Los 40 MHz estan confirmados funcionando en el ESP32-S3 con sdioio, donde la velocidad de lectura subio 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 mas 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 carisima no te da nada extra en este contexto.

Primer plano de una tarjeta microSD SanDisk Ultra de 16GB con el simbolo Clase 10 destacado

Ese circulo rojo marca el simbolo de Clase 10, que es justamente lo unico que te importa mirar al comprar.

SPI contra SDIO: cuando vale la pena la interfaz mas rapida

La nomenclatura confunde porque el mismo resultado (la velocidad SDIO) se alcanza con nombres de modulo distintos segun el chip. Esta tabla muestra exactamente que usa cada MCU por debajo:

MCU Periferico SD por hardware Modulo 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 mas 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 numeros medidos de escritura, lectura y registro de lineas por segundo. Sirven para calibrar expectativas: nota como la combinacion SDIO mas el driver correcto se despega del resto, y como adafruit_sdcard sobre SPI queda muy por detras.

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

Fijate en la ultima 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 libreria, que no cuesta nada, te puede dar un salto de mas de 30 veces.

Variantes y mejoras

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

  • Datalogger con marca de tiempo: combina la tarjeta microSD con un modulo RTC DS3231 por I2C para que cada linea del log lleve fecha y hora exactas, incluso despues de un corte de energia. Ideal para registrar temperatura, humedad o consumo a lo largo de dias.
  • Buffer en RAM antes de escribir: si tu sensor entrega datos muy rapido, acumula varias muestras en un bytearray en memoria y escribe a la SD solo cada cierto numero de muestras. Reduces los cruces de sector y le sacas mas throughput a la tarjeta, justo como muestran los benchmarks.
  • Rotacion de archivos por dia: en lugar de un unico log gigante, abre un archivo nuevo por fecha (por ejemplo log_2026_05_30.bin). Te queda mas ordenado para analizar y evitas tablas de directorio enormes que ralentizan el montaje.

Personalizacion para Chile

Las placas Adafruit del tutorial original (Metro RP2350, Feather RP2040 Adalogger) traen la ranura microSD integrada, pero no es la unica forma de aplicar esta guia. 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:

  • Modulo datalogger para tarjeta microSD (SKU G-014) por $2.500: 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 tamano completo y se monta directo sobre un Arduino Uno, comodo para prototipos.

Una recomendacion local concreta: para que esta guia rinda, no compres la tarjeta mas grande, compra la mas chica que te sirva. Una microSD Clase 10 de 2GB o menos, formateada en FAT16, te dara el mejor tiempo de arranque. Si solo encuentras tarjetas de 8GB o 16GB, igual funcionan, pero recuerda que el formato pesa mas que la capacidad.

Recursos

  • Tutorial original (ingles): 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 guia original de Adafruit.
  • Documentacion adicional: libreria sdcardio y libreria sdioio en la documentacion oficial de CircuitPython (docs.circuitpython.org).

Version chilena con componentes en stock local en MechatronicStore.