Adafruit CircuitPython Module Install
To use a microSD card with your Adafruit CircuitPython board you'll need to install the Adafruit_CircuitPython_SD module on your 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 introduction guide has a great page on how to install the library bundle for both express and non-express boards. Be sure to use the latest CircuitPython Bundle as it includes an updated version of the SD card module with a few necessary fixes!
If your board supports sdcardio, then this is the preferred method to do things. sdcardio is a built-in module on boards that support it, so you don't have to copy it over.
Remember for non-express boards like the Feather M0 Basic, you'll need to manually install the necessary libraries from the bundle:
- adafruit_sdcard.mpy
- adafruit_bus_device
If your board doesn't support USB mass storage, like the ESP8266, then use a tool like ampy to copy the file to the board. You can use the latest version of ampy and its new directory copy command to easily move module directories to the board.
Before continuing make sure your board's lib folder or root filesystem has the adafruit_sdcard.mpy and adafruit_bus_device modules copied over.
Usage
The following section will show how to initialize the SD card and read & write data to it from the board's Python prompt / REPL.
Next connect to the board's serial REPL so you are at the CircuitPython >>> prompt.
Initialize & Mount SD Card Filesystem Using sdcardio
Before using this method, verify that your board supports sdcardio using the support matrix. If it does not, try using adafruit_sdcard instead.
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:
import board import busio import sdcardio 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):
# 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) # For breakout boards, you can choose any GPIO pin that's convenient: cs = board.D10 # Boards with built in SPI SD card slots will generally have a # pin called SD_CS: #cs = board.SD_CS
If you are using some non-standard CircuitPython board, you may need to use specific GPIO pins for MISO and MOSI to get the SPI recognized appropriately
If your SPI chip select pin is different or not defined like SD_CD, put the appropriate pin number in the code, like board.D5.
Note that when you use sdcardio
, cs
is a Pin
object, not a DigitalInOut
object. If you change your code to use adafruit_sdcard
, you need to use a DigitalInOut
object instead.
At this point you're ready to create the microSD card object and the filesystem object:
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:
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, and all this worked, skip ahead to the section labeled Reading & Writing Data.
Initialize & Mount SD Card Filesystem Using adafruit_sdcard
Before you can use the microSD card you need to initialize its SPI connection and mount its filesystem. First import the necessary modules to initialize the SPI and CS line physical connections:
import board import busio import digitalio
Next create the SPI bus and a digital output for the microSD card's chip select line (be sure to select the right pin name or number for your wiring):
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Use board.SD_CS for Feather M0 Adalogger cs = digitalio.DigitalInOut(board.SD_CS) # Or use a digitalio pin like 5 for breakout wiring: #cs = digitalio.DigitalInOut(board.D5)
Now import modules to access the SD card and filesystem:
import adafruit_sdcard import storage
At this point you're ready to create the microSD card object and the filesystem object:
sdcard = adafruit_sdcard.SDCard(spi, cs) vfs = storage.VfsFat(sdcard)
Notice the adafruit_sdcard 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 digital IO pin in its initializer.
After a SDCard class 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:
storage.mount(vfs, "/sd")
The first parameter to the storage.mount command is the VfsFat class instance that was created above, and the second parameter is the location within the CircuitPython filesystem that you'd like to 'place' the microSD card. Remember the mount location as you'll need it to read and write files on the card!
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:
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.
Starting with CircuitPython 9, the SD card MUST be mapped to /sd on the CircuitPython file system. Create an empty sd directory in the root directory of the CIRCUITPY drive, especially if you get messages complaining of a missing mount point.
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:
with open("/sd/test.txt", "r") as f: print("Read line from file:") print(f.readline())
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:
with open("/sd/test.txt", "r") as f: print("Printing lines in file:") line = f.readline() while line != '': print(line) line = f.readline()
There's even a readlines function that will read all of the lines in the file and return them in an array of lines. Be careful though as this means the entire file must be loaded into memory, so if the file is very large you might run out of memory. If you know your file is very small you can use it though:
with open("/sd/test.txt", "r") as f: lines = f.readlines() print("Printing lines in file:") for line in lines: print(line)
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:
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!
That's all there is to manipulating files on microSD cards with CircuitPython!
Here are a few more complete examples of using a SD card from the Trinket M0 CircuitPython guides. These are great as a reference for more SD card usage.
List Files
Load this into code.py (CircuitPython) or main.py (MicroPython):
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries # # SPDX-License-Identifier: MIT import os import adafruit_sdcard import board import busio import digitalio import storage # Use any pin that is not taken by SPI SD_CS = board.D0 # Connect to the card and mount the filesystem. spi = busio.SPI(board.SCK, board.MOSI, board.MISO) cs = digitalio.DigitalInOut(SD_CS) sdcard = adafruit_sdcard.SDCard(spi, cs) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Use the filesystem as normal! Our files are under /sd # This helper function will print the contents of the SD def print_directory(path, tabs=0): for file in os.listdir(path): if file == "?": continue # Issue noted in Learn stats = os.stat(path + "/" + file) filesize = stats[6] isdir = stats[0] & 0x4000 if filesize < 1000: sizestr = str(filesize) + " by" elif filesize < 1000000: sizestr = "%0.1f KB" % (filesize / 1000) else: sizestr = "%0.1f MB" % (filesize / 1000000) prettyprintname = "" for _ in range(tabs): prettyprintname += " " prettyprintname += file if isdir: prettyprintname += "/" print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr)) # recursively print directory contents if isdir: print_directory(path + "/" + file, tabs + 1) print("Files on filesystem:") print("====================") print_directory("/sd")
Once it's loaded up, open up the REPL (and restart it with ^D if necessary) to get a printout of all the files included. We recursively print out all files and also the filesize. This is a good demo to start with because you can at least tell if your files exist!
Logging Temperature
But you probably want to do a little more, lets log the temperature from the chip to a file.
Here's the new script
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries # # SPDX-License-Identifier: MIT import time import adafruit_sdcard import board import busio import digitalio import microcontroller import storage # Use any pin that is not taken by SPI SD_CS = board.D0 led = digitalio.DigitalInOut(board.D13) led.direction = digitalio.Direction.OUTPUT # Connect to the card and mount the filesystem. spi = busio.SPI(board.SCK, board.MOSI, board.MISO) cs = digitalio.DigitalInOut(SD_CS) sdcard = adafruit_sdcard.SDCard(spi, cs) vfs = storage.VfsFat(sdcard) storage.mount(vfs, "/sd") # Use the filesystem as normal! Our files are under /sd print("Logging temperature to filesystem") # append to the file! while True: # open file for append with open("/sd/temperature.txt", "a") as f: led.value = True # turn on LED to indicate we're writing to the file t = microcontroller.cpu.temperature print("Temperature = %0.1f" % t) f.write("%0.1f\n" % t) led.value = False # turn off LED to indicate we're done # file is saved time.sleep(1)
When saved, the Trinket will start saving the temperature once per second to the SD card under the file temperature.txt
The key part of this demo is in these lines:
print("Logging temperature to filesystem") # append to the file! while True: # open file for append with open("/sd/temperature.txt", "a") as f: led.value = True # turn on LED to indicate we're writing to the file t = microcontroller.cpu.temperature print("Temperature = %0.1f" % t) f.write("%0.1f\n" % t) led.value = False # turn off LED to indicate we're done # file is saved time.sleep(1)
This is a slightly complex demo but it's for a good reason. We use with (a 'context') to open the file for appending, that way the file is only opened for the very short time its written to. This is safer because then if the SD card is removed or the board turned off, all the data will be safe(r).
The LED is used to let the person using this know that the temperature is being written, it turns on just before the write and then off right after.
After the LED is turned off the with ends and the context closes, the file is safely stored.
Text editor powered by tinymce.