Adafruit CircuitPython SD

The original adafruit_sd library is available for older M0 boards that don't have enough flash for the new updated sdcardio optimized library. It's slower, but works well enough for basic SD card usages!

Use the code and wiring examples in this section if:

  • Your board doesn't support sdcardio or sdioio

This is the case particularly for older and lower-capacity microcontroller like the M0 series.

The following section will show how to initialize the SD card and read & write data to it.  You'll want to put the correct lines to initialize it in mount_sd.py on your CIRCUITPY drive. For typical wiring information and exact mount_sd.py files for several different boards, check out next page.

Adafruit CircuitPython Module Install

You'll need to install the Adafruit CircuitPython SD library on your CircuitPython board.

First make sure you are running the latest version of Adafruit CircuitPython for your board.

Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle

Our CircuitPython starter guide has a great page on how to install libraries from the bundle.

Load the the following libraries into the lib folder on your CIRCUITPY drive:

  • adafruit_sdcard.mpy
  • adafruit_bus_device

Before continuing make sure your board's lib folder or root filesystem has the adafruit_sdcard.mpy file and adafruit_bus_device folder copied over.

Initialize & Mount SD Card Filesystem

Before you can use the microSD card you need to initialize its SPI connection and mount its filesystem. First import all the modules we'll need:

Download: file
import board
import busio
import adafruit_sdcard
import storage

Next create the SPI bus and a digital output for the microSD card's chip select line (be sure to select the right pin names for your wiring):

Download: file
# Use the board's primary SPI bus
spi = board.SPI()
# Or, use an SPI bus on specific pins:
#spi = busio.SPI(board.SD_SCK, MOSI=board.SD_MOSI, MISO=board.SD_MISO)

# Boards with built in SPI SD card slots will generally have a
# pin called SD_CS:
cs = board.SD_CS
# For breakout boards, you can choose any GPIO pin that's convenient:
#cs = board.D10

Note that when you use adafruit_sdcard, cs is a DigitalInOut object, not a microcontroller.Pin object. If you change your code to use sdcardio, you need to use a Pin object instead.

At this point you're ready to create the microSD card object and the filesystem object:

Download: file
sdcard = sdcardio.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)

Notice the sdcardio module has a SDCard class which contains all the logic for talking to the microSD card at a low level. This class needs to be told the SPI bus and chip select pin in its constructor.

After a SDCard instance is created it can be passed to the storage module's VfsFat class. This class has all the logic for translating CircuitPython filesystem calls into low level microSD card access. Both the SDCard and VfsFat class instances are required to mount the card as a new filesystem.

Finally you can mount the microSD card's filesystem into the CircuitPython filesystem. For example to make the path /sd on the CircuitPython filesystem read and write from the card run this command:

Download: file
storage.mount(vfs, "/sd")

At this point, you can read and write to the SD card using common Python functions like open, read, and write.  The filenames will all begin with "/sd/" to differentiate them from the files on the CIRCUITPY drive. If you're not familiar, here's an overview.

Reading & Writing Data

Once the microSD card is mounted inside CircuitPython's filesystem you're ready to read and write data from it  Reading and writing data is simple using Python's file operations like open, close, read, and write. The beauty of CircuitPython and MicroPython is that they try to be as similar to desktop Python as possible, including access to files.

For example to create a file and write a line of text to it you can run:

Download: file
with open("/sd/test.txt", "w") as f:
    f.write("Hello world!\r\n")

Notice the with statement is used to create a context manager that opens and automatically closes the file. This is handy because with file access you Python you must close the file when you're done or else all the data you thought was written might be lost!  

The open function is used to open the file by telling it the path to it, and the mode (w for writing). Notice the path is under /sd, /sd/test.txt. This means the file will be created on the microSD card that was mounted as that path.

Inside the context manager you can access the f variable to operate on the file while it's open. The write function is called to write a line of text to the file. Notice that unlike a print statement you need to end the string passed to write with explicit carriage returns and new lines.

You can also open a file and read a line from it with similar code:

Download: file
with open("/sd/test.txt", "r") as f:
    print("Read line from file:")
    print(f.readline(), end='')

When reading a line from a file with readline, the newline character '\n' at the end of the line is included. By printing with end='', the newline normally added by print is skipped. Otherwise, when reading back the file, it would appear that there were extra blank lines after every line of the file.

If you wanted to read and print all of the lines from a file you could call readline in a loop. Once readline reaches the end of the file it will return an empty string so you know when to stop:

Download: file
with open("/sd/test.txt", "r") as f:
    print("Printing lines in file:")
    line = f.readline()
    while line != '':
        print(line, end='')
        line = f.readline()

You can also just use the open file object in a for loop. Each time the loop runs, the loop variable will be assigned the content of the next line of the file:

Download: file
with open("/sd/test.txt", "r") as f:
    print("Printing lines in file:")
    for line in file:
        print(line, end='')

Finally one other very common file scenario is opening a file to add new data at the end, or append data. This works exactly the same as in Python and the open function can be told you'd like to append instead of erase and write new data (what normally happens with the w option for open). For example to add a line to the file:

Download: file
with open("/sd/test.txt", "a") as f:
    f.write("This is another line!\r\n")

Notice the a option in the open function--this tells Python to add data at the end of the file instead of erasing it and starting over at the top. Try reading the file with the code above to see the new line that was added!

Those are the basics to manipulating files on microSD cards with CircuitPython!  Once you have it working, put the correct lines in mount_sd.py in CIRCUITPY, then continue learning by checking out the example pages to print directory listings, log sensor data, play MP3 files and display bitmap graphics.

Example complete mount_sd.py

This example works on the AdaLogger M0:

Download: file
import adafruit_sdcard
import board
import busio
import digitalio
import storage

spi = board.SPI()
cs = digitalio.DigitalInOut(board.SD_CS)

sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")

Your board may be different. Refer to the following pages for more details.

This guide was first published on Jul 31, 2020. It was last updated on 2020-07-31 12:38:51 -0400.
This page (Adafruit CircuitPython SD) was last updated on Aug 05, 2020.