The Adafruit PyPortal is a great little IoT device with lots of projects available. It’s an awesome H/W and S/W platform that inspires many creative ideas. The PyPortal uses an ATMEL (Microchip) ATSAMD51J20, and an Espressif ESP32 Wi-Fi coprocessor. PyPortal has a 3.2″ 320 x 240 color TFT resistive touch screen and has several built-in sensors. It also has support for a micro-SD Card. The device itself shows up as a USB drive when plugged into a computer — but not the micro-SD Card 🙁
Software support for PyPortal is via CircuitPython. There is a great Integrated Development Environment (IDE) available in the form of Mu. Mu is a simple Python editor that also has a built-in serial-port terminal. It allows command-line input of CircuitPython for instant programming, testing, and debugging. This feature is referred to as the REPL (Read-Evaluate-Print-Loop). Deployment of code to a PyPortal is immediate with Mu’s “Save” function.
As I was experimenting with my PyPortal, I wanted to put code and resources (images, fonts, code) on a removable micro-SD Card. Easy to do on my computer but not obvious how to do on PyPortal 🙁 I looked at several PyPortal sample apps and perused CircuitPython docs available on the Explore and Learn Area on the Adafruit websitet
I’m sharing what I learned in a hopefully concise manner to save others time and frustration 😉
First follow the PyPortal setup instructions! Update firmware to the latest version, install CicuitPython, and make sure you have installed the Adafruit CIrcuitPython Library Bundle. You should then have a “lib” folder at the root of the CIRCUITPY drive. Finally, insert a micro-SD Card formatted with the FAT or FAT32 filesystem.
OK, let’s test! In Mu, connect to the PyPortal via the serial terminal. You should type in the following in the REPL area:
Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 4.0.0-beta.6 on 2019-03-30; Adafruit PyPortal with samd51j20
>>> import board
>>> import storage
>>> import adafruit_sdcard
>>> import os
>>> import digitalio
>>> import busio
>>> spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
>>> cs = digitalio.DigitalInOut(board.SD_CS)
>>> sdcard = adafruit_sdcard.SDCard(spi, cs)
>>> vfs = storage.VfsFat(sdcard)
>>> storage.mount(vfs, "/sd")
>>> os.listdir('/sd')
['TESTdir', '.Trashes']
Another way to do this is to create “code.py” file on the PyPortal – as opposed to using the interactive REPL:
import board
import storage
import adafruit_sdcard
import os
import digitalio
import busio
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
cs = digitalio.DigitalInOut(board.SD_CS)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
os.listdir('/sd')
NOTE: per a comment, below, as of the late June 2019 PyPortal / CircuitPython update you automatically get an “/sd” directory if you configure a PyPortal object. You can skip all the initialization code above. You can do the “os.listdir(‘/sd’)” to confirm after doing (something like) “pyportal=PyPortal( <parameters> )
The “os.listdir()” line should show you files & directories on the CICUITPY drive. The “os.listdir(‘/sd’)” line should show any files or directories on the micro-SD Card. It may be empty if you have never written to the card or you may see something like “[‘.Trashes’]” as this is a hidden directory created on MacOS. You can also create a directory and check that it was created.
>>> os.listdir('/sd')
['.Trashes']
>>> os.mkdir('/sd/TESTdir')
>>> os.listdir('/sd')
['TESTdir', '.Trashes']
>>> # unmount the microSD-Card for removal
>>> storage.umount('/sd')
>>> os.listdir('/sd')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file/directory
You can now insert, mount, and unmount a micro-SD Card. You can also create directories. The last piece of the puzzle is to create, read, write and delete files 🙂 Start by mounting a micro-SD Card to “/sd” You can use CicuitPython built-in filesystem methods to manipulate files:
>>> os.listdir('/sd')
['TESTdir', '.Trashes']
>>> f = open('/sd/TESTfile.txt', 'w')
>>> f.write('Hello, World!')
13
>>> f.close()
>>> os.listdir('/sd')
['TESTdir', 'TESTfile.txt', '.Trashes']
>>> f = open('/sd/TESTfile.txt')
>>> f.read()
'Hello, World!'
>>> f.close()
>>> os.listdir('/sd')
['TESTdir', 'TESTfile.txt', '.Trashes']
>>> os.remove('/sd/TESTfile.txt')
>>> os.listdir('/sd')
['TESTdir', '.Trashes']
>>>
For more information, check out the Adafruit CircuitPython API reference. For micro-SDCard mounting check out the storage module. For OS-type functions look at the os module. There is a nice write-up of file manipulation (read, write, delete, etc.) in the MicroPython docs. Please leave questions and feedback in the comments.
Thanks for the post. For some reason I can’t get this code to work on my PyPortal as code.py.
It works as described in the REPL, however when running in code.py, it would not.
I manually tried to adjust timing, etc… but no luck.
I even tried downgrading to CP CircuitPython 4.0.0-beta.6, but that didn’t fix anything.
I’m wondering if it’s related to these issues:
https://github.com/adafruit/circuitpython/issues/245
https://github.com/adafruit/Adafruit_CircuitPython_SD/issues/13
I had tried a 64GB card with no luck. I am now trying an 8GB card.
Do you recall what size card you used?
Dan, I’m away from my PyPortal for a while but will check when I can. I used a SanDisk Extreme 32GB SD Card. If things work in REPL then I would look at the IMPORT libraries to ensure they are listed + available in “code.py”. What errors or symptoms are you seeing?
I got it to work by doing print(os.listdir(‘/sd’))
Thanks for this info – I was using the code & it was working great until I updated things.
I discovered that as of the late June 2019 PyPortal / CircuitPython update you automatically get an “/sd” directory after you configure a PyPortal object. Don’t make the calls listed above if you have the upgrade, or you’ll get an error:
ValueError: SCK in use. Thought I’d post it here in case anyone was using your great code before the mod & like me, they saw thing sstart to fail. Cheers!