Trying to figure out how to access my Pico Wireless Micro SD card.
The closest I have got is picowireless.is_sdcard_detected() which returns a value of True.
Does anyone know how to read/write the card?
Trying to figure out how to access my Pico Wireless Micro SD card.
The closest I have got is picowireless.is_sdcard_detected() which returns a value of True.
Does anyone know how to read/write the card?
Iāll caveat this response with a) I donāt have a Pico Wireless pack, so this answer may be complete nonsense and b) It looks like youāre using MicroPython (Iām assuming that from the is_sdcard_detected()
call) and this answer relates to C++ ā¦
With that out of the way, thereās an example in the pimoroni GitHub repo that shows a simple web server that returns a directory of an SD card over HTTP that looks to be fairly straightforward - maybe that will help?
Also - the product page on Pimoroniās website does say
Please note that SD card support in the C++ SDK is still quite experimental - if youāre planning on doing things with data you might have an easier time of it if you use CircuitPython!
Thereās a page on adafruitās website that shows some SD Card examples in CircuitPython - https://learn.adafruit.com/micropython-hardware-sd-cards?view=all - not sure if thatās useful/relevant but may be helpful?
D
Iāve not used the SD card on the wireless board but I have got the SD card working with MicroPython.
You do not even need an SD card reader. I soldered some pins onto the pad of the micro SD card adapter and connected directly to the Pico pins. In my example I used the other SPI set. You can find the full method here:
Raspberry Pi Pico - Review | element14 | RoadTests & Reviews
This is a long review but the section you need is about 80% through and headed
Pico, MicroPython and SD card storage
Here is the code, just adjust the pins to the wireless board connections:
import sdcard
import machine
import uos
sd_spi = machine.SPI(1, sck = machine.Pin(10, machine.Pin.OUT), mosi = machine.Pin(11, machine.Pin.OUT), miso = machine.Pin(12, machine.Pin.OUT))
sd = sdcard.SDCard(sd_spi, machine.Pin(9))
uos.mount(sd, "/sd")
print("Size: {} MB".format(sd.sectors/2048)) # to display card's capacity in MB
print(uos.listdir("/sd"))
print("\n=======================\n")
print("Basic SDcard Test \n")
with open("/sd/test2.txt", "w") as f: # Write - new file
f.write("First Message\r\n")
with open("/sd/test2.txt", "a") as f: # Append
f.write("Tony Goodhew\r\n")
with open("/sd/test2.txt", "a ") as f:
f.write("Leicester City Cup Winners!\r\n")
with open("/sd/test2.txt", "a ") as f:
for i in range(10):
f.write(str(i) + ", " + str(i*i*i) + ", " + str(i*i*i*i) + "\r\n")
with open("/sd/test2.txt", "a ") as f:
f.write("Looping all done!\r\n")
with open("/sd/test2.txt", "r") as f:
print("Printing lines in file: Method #1\n")
line = f.readline()
while line != '': # NOT EOF
print(line)
line = f.readline()
with open("/sd/test2.txt", "r") as f:
lines = f.readlines()
print("Printing lines in file: Method #2")
for line in lines:
print(line)
uos.umount("/sd")
You can get the SD card library here:
"""
MicroPython driver for SD cards using SPI bus.
Requires an SPI bus and a CS pin. Provides readblocks and writeblocks
methods so the device can be mounted as a filesystem.
Example usage on pyboard:
import pyb, sdcard, os
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
pyb.mount(sd, '/sd2')
os.listdir('/')
Example usage on ESP8266:
import machine, sdcard, os
sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
os.mount(sd, '/sd')
os.listdir('/')
"""
from micropython import const
import time
_CMD_TIMEOUT = const(100)
_R1_IDLE_STATE = const(1 << 0)
# R1_ERASE_RESET = const(1 << 1)
_R1_ILLEGAL_COMMAND = const(1 << 2)
# R1_COM_CRC_ERROR = const(1 << 3)
# R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
# R1_ADDRESS_ERROR = const(1 << 5)
# R1_PARAMETER_ERROR = const(1 << 6)
_TOKEN_CMD25 = const(0xFC)
_TOKEN_STOP_TRAN = const(0xFD)
_TOKEN_DATA = const(0xFE)
class SDCard:
def __init__(self, spi, cs):
self.spi = spi
self.cs = cs
self.cmdbuf = bytearray(6)
self.dummybuf = bytearray(512)
self.tokenbuf = bytearray(1)
for i in range(512):
self.dummybuf[i] = 0xFF
self.dummybuf_memoryview = memoryview(self.dummybuf)
# initialise the card
self.init_card()
def init_spi(self, baudrate):
try:
master = self.spi.MASTER
except AttributeError:
# on ESP8266
self.spi.init(baudrate=baudrate, phase=0, polarity=0)
else:
# on pyboard
self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
def init_card(self):
# init CS pin
self.cs.init(self.cs.OUT, value=1)
# init SPI bus; use low data rate for initialisation
self.init_spi(100000)
# clock card at least 100 cycles with cs high
for i in range(16):
self.spi.write(b"\xff")
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
for _ in range(5):
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
break
else:
raise OSError("no SD card")
# CMD8: determine card version
r = self.cmd(8, 0x01AA, 0x87, 4)
if r == _R1_IDLE_STATE:
self.init_card_v2()
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
self.init_card_v1()
else:
raise OSError("couldn't determine SD card version")
# get the number of sectors
# CMD9: response R2 (R1 byte + 16-byte block read)
if self.cmd(9, 0, 0, 0, False) != 0:
raise OSError("no response from SD card")
csd = bytearray(16)
self.readinto(csd)
if csd[0] & 0xC0 == 0x40: # CSD version 2.0
self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)
c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4
c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7
self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2))
else:
raise OSError("SD card CSD format not supported")
# print('sectors', self.sectors)
# CMD16: set block length to 512 bytes
if self.cmd(16, 512, 0) != 0:
raise OSError("can't set 512 block size")
# set to high data rate now that it's initialised
self.init_spi(1320000)
def init_card_v1(self):
for i in range(_CMD_TIMEOUT):
self.cmd(55, 0, 0)
if self.cmd(41, 0, 0) == 0:
self.cdv = 512
# print("[SDCard] v1 card")
return
raise OSError("timeout waiting for v1 card")
def init_card_v2(self):
for i in range(_CMD_TIMEOUT):
time.sleep_ms(50)
self.cmd(58, 0, 0, 4)
self.cmd(55, 0, 0)
if self.cmd(41, 0x40000000, 0) == 0:
self.cmd(58, 0, 0, 4)
self.cdv = 1
# print("[SDCard] v2 card")
return
raise OSError("timeout waiting for v2 card")
def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
self.cs(0)
# create and send the command
buf = self.cmdbuf
buf[0] = 0x40 | cmd
buf[1] = arg >> 24
buf[2] = arg >> 16
buf[3] = arg >> 8
buf[4] = arg
buf[5] = crc
self.spi.write(buf)
if skip1:
self.spi.readinto(self.tokenbuf, 0xFF)
# wait for the response (response[7] == 0)
for i in range(_CMD_TIMEOUT):
self.spi.readinto(self.tokenbuf, 0xFF)
response = self.tokenbuf[0]
if not (response & 0x80):
# this could be a big-endian integer that we are getting here
for j in range(final):
self.spi.write(b"\xff")
if release:
self.cs(1)
self.spi.write(b"\xff")
return response
# timeout
self.cs(1)
self.spi.write(b"\xff")
return -1
def readinto(self, buf):
self.cs(0)
# read until start byte (0xff)
for i in range(_CMD_TIMEOUT):
self.spi.readinto(self.tokenbuf, 0xFF)
if self.tokenbuf[0] == _TOKEN_DATA:
break
time.sleep_ms(1)
else:
self.cs(1)
raise OSError("timeout waiting for response")
# read data
mv = self.dummybuf_memoryview
if len(buf) != len(mv):
mv = mv[: len(buf)]
self.spi.write_readinto(mv, buf)
# read checksum
self.spi.write(b"\xff")
self.spi.write(b"\xff")
self.cs(1)
self.spi.write(b"\xff")
def write(self, token, buf):
self.cs(0)
# send: start of block, data, checksum
self.spi.read(1, token)
self.spi.write(buf)
self.spi.write(b"\xff")
self.spi.write(b"\xff")
# check the response
if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
self.cs(1)
self.spi.write(b"\xff")
return
# wait for write to finish
while self.spi.read(1, 0xFF)[0] == 0:
pass
self.cs(1)
self.spi.write(b"\xff")
def write_token(self, token):
self.cs(0)
self.spi.read(1, token)
self.spi.write(b"\xff")
# wait for write to finish
while self.spi.read(1, 0xFF)[0] == 0x00:
pass
self.cs(1)
self.spi.write(b"\xff")
def readblocks(self, block_num, buf):
nblocks = len(buf) // 512
assert nblocks and not len(buf) % 512, "Buffer length is invalid"
if nblocks == 1:
# CMD17: set read address for single block
if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
# release the card
self.cs(1)
raise OSError(5) # EIO
# receive the data and release card
self.readinto(buf)
else:
# CMD18: set read address for multiple blocks
if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
# release the card
self.cs(1)
raise OSError(5) # EIO
offset = 0
mv = memoryview(buf)
while nblocks:
# receive the data and release card
self.readinto(mv[offset : offset + 512])
offset += 512
nblocks -= 1
if self.cmd(12, 0, 0xFF, skip1=True):
raise OSError(5) # EIO
def writeblocks(self, block_num, buf):
nblocks, err = divmod(len(buf), 512)
assert nblocks and not err, "Buffer length is invalid"
if nblocks == 1:
# CMD24: set write address for single block
if self.cmd(24, block_num * self.cdv, 0) != 0:
raise OSError(5) # EIO
# send the data
self.write(_TOKEN_DATA, buf)
else:
# CMD25: set write address for first block
if self.cmd(25, block_num * self.cdv, 0) != 0:
raise OSError(5) # EIO
# send the data
offset = 0
mv = memoryview(buf)
while nblocks:
self.write(_TOKEN_CMD25, mv[offset : offset + 512])
offset += 512
nblocks -= 1
self.write_token(_TOKEN_STOP_TRAN)
def ioctl(self, op, arg):
if op == 4: # get number of blocks
return self.sectors
Call it sdcard.py
There is another on here:
micropython-infineon/sdcard.py at master Ā· micropython/micropython-infineon Ā· GitHub
(Iām not sure which chip (Pi Pico or ESP) is driving the SDcard reader on the Wireless board. The method above should work if it is the Pico.)
I hope this helps
Have fun
Thanks for the reply. I should have been more specific - yes, Iām using MicroPython. I have a look at the link you provided.
Thanks very much for your reply. Iāll have a good look at the info you provided and give it a try.
I can confirm that the sdcard on the pico wireless is driven by the pico on SPI0 so I have made the following adjustments to the code:
sd_spi = machine.SPI(0, sck = machine.Pin(18, machine.Pin.OUT), mosi = machine.Pin(19, machine.Pin.OUT), miso = machine.Pin(16, machine.Pin.OUT))
sd = sdcard.SDCard(sd_spi, machine.Pin(22))
I get the following error:
File āā, line 7, in
File ā/lib/sdcard.pyā, line 238, in readblocks
OSError: [Errno 5] EIO
I think this would indicate that I have made an error with the pins but I canāt see one. The documentation is a bit confusing using SDIO labels, it seems the board can be driven in either that or SPI modes. I am using known good cards between 2 and 16 Gig, all formatted FAT32 with 512 block size.
Has anybody got the card to work with micropython on the Wireless pack using sdpycard.py?
Has any worked example been published, I canāt find any equivalent to the ESP32 examples?
Grateful for any input,
RytonMike
Iāve found this to be the most frustrating of all my Pico add-on boards. Lack of clear documentation about the correct pins is not helping. It has been out long enough for this to have been sorted by now. The āhome-madeā SDcard adapter method works perfectly in MicroPython - build your own.
All we need is a simple example program, in MicroPython, which initialises the board, opens a write-file, writes a couple of records, closes the file, opens it for reading and gets the stored information back.
Iām glad itās not just me! Thanks for the comment Tonygo2.
By the way I found your workout for waveshare 1.3" IPS display very useful.
So come on Pimeroni, give us an example!
RytonMike
Just gave it a go and @Tonygo2 's SD card example seems to work fine for me on Wireless Pack with
sd_spi = machine.SPI(0, sck = machine.Pin(18, machine.Pin.OUT), mosi = machine.Pin(19, machine.Pin.OUT), miso = machine.Pin(16, machine.Pin.OUT))
sd = sdcard.SDCard(sd_spi, machine.Pin(22))
I saved the MicroPython driver as sdcard.py
using Thonny.
Could be the SD cards causing problems, perhaps - I think @gadgetoid said he found Pico was fairly fussy with SD cards that it liked? Also worth checking that the soldering on your Picoās header is up to scratch - OSError: [Errno 5] EIO
can happen when thereās a dodgy connection somewhere (similar to an IOError in standard Python).
Yep, just tried a few SD cards with varying results - the ones on the left worked fine and the ones on the right didnāt (the 1GB Sandisk did the [Errno 5] EIO
thing)
Hel Gibbons, Thanks
I just tried it with a SanDisk Ultra 8GB (10) SDHC I (Red/Grey) in the Pico Wireless SD slot and it worked perfectly.
Another thing fixed!
I get my SD cards from Amazon or Ebuyer.com - there are some very iffy cards on Ebay.
Thanks for the response, Hel. Iām on the air now as well!
FYI I struggled with this on a 2.8 LCD with a built in SD reader. I was getting the same type of messages while fussing around with it. I am using a 2040 Feather but for all intents its just a smaller Pico.
Granted this is circuit python it should give you an idea of how to get it up and running, I think in MP you would replace the adafruit_sdcard library with MPās sdcard.py. this is a great library for SD cards in MP peterhinchMP.
Hereās my CP code (version 8.2.4 latest as of August 2023). I probably have some extra libraries I need to remove that Iām not using, this was just a test to see if I could read images from the LCD from SD. You would ignore after sleep(3) as thatās really more pure CP. But you can see what its doing and port it to the MP equivalent.
Adafruit also has detailed images on how to connect and SD card reader to a Pico. Just search for it in the ālearnā section.
Last ditch is some of the waveshare LCDās have pure C code (not Arduino) that work but they are clunky, when I contacted their support they were lost and building code for me directly to try to get it work. I did get the onboard SDcard to read images from the LCD but it was ugly and I had to hack it a bit myself.
If you arenāt against Circuitpython (not a huge fan myself I use MP mostly or C/C++ pure in a pinch) the code below works great. Iām going to try it on a waveshare LCD that uses the same ili93xx driver. I think I have one. SD cards are a PITA. It should be a simple SPI setup but I noticed its not explained well and full examples are sparse/wrong or dont work on Pico. Iām using a 64GB SanDisk Extreme micro SD card formatted fat32 from my Mac OS using the built in disk util. I just named the sdcard ātestā.
I have an micro sd card module from Adafruit laying around. Iām going to wire that up to a plain ārealā Pico and load MP on it and see if I can get it working. After all I bought the reader for Pico with their tiny 2MB of space. I do own many Pimoroni Picoās with 16MB which is nice for storing images and reading them from that,but I understand people may want to read/write other data constantly (logger) that would breach even the 16MB on the Pimoroniās Picos. I read on hackster that some guy figured out how to take some of on that on board flash and use it as SRAM! (solves the buffer RAM size problem for displaying true color images on large (3.5" and above) LCDās. He did not port the code and its left āup to youā to figure it out. Iām hoping someone runs with it and builds into a module that would be game changer, imagine having 8MB of flash storage and 8MB of SRAM on the Pico?! Amazing. I cant wait until we get there. Sorry side rant. Iām going to wire up a pico with the SDcard module and get it working in MicroPython. Iāll share the code if and when I get it working! :) Cheers.
import busio
import sdcardio
import storage
import board <-----this would be machine in MP
import os
import displayio
from adafruit_display_text import label <---Need MP equivalents.
import adafruit_ili9341 <---Need MP equivalents.
import adafruit_sdcard <---Here is where I think you would use sdcard.py in MP
import digitalio
import time
displayio.release_displays()
spi = board.SPI() <--in CP this is all you need to setup SPI assuming the pins are plugged in correctly(mosi,miso,cs)
tft_cs = board.D9
tft_dc = board.D10
sd_cs = digitalio.DigitalInOut(board.D5)
spi = board.SPI()
sd= adafruit_sdcard.SDCard(spi, sd_cs) <------I think here you would use the sdcard.py implementation to do the equivalent.
sd= storage.VfsFat(sd) <---I read rumors that VfsFat doesnt work on Pico, maybe that's true in MP but in CP it works just fine. I doubt that's the case in current MP builds.
storage.mount(sd,'/sd')
time.sleep(3)
#######below is display setup in CP irrelevant SD card is functional at this point####
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.D6)
display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240)
filename = "/sd/fed.bmp"
....more code..
Note that a SD-card on SPI and another component (e.g. display) on the same bus tend to produce problems. This is documented in the CP sd-library suggesting that you need to initialize the sd card before the display. The reason is unclear, it probably has nothing to do with the python-layer but with the pico-sdk so you could also encounter this problem with MP or C++.
In one of my projects I either have the sd-card, or the display working, but not both. I solved the problem by downgrading CP, but the funny thing is: none of the related code has changed in the sources (and most of it is in libraries anyhow). There are also a number of open issues regarding sd-cards in the CP-repo.
So in the end, if the sd-card does not work, it might not even be the card itself.
Yeah @bablokb I had to use the same SPI bus because the micro SD slot was on the actual LCD and share the bus per the documentation. Like I said I figured it out with the above.
I also grabbed a genuine PicoW and loaded the latest stable MP version 1.20.0 and installed an Adafruit SD card reader module. I used SPI 1 in my testing using pins GP10 - GP13 for SPI 1. I grabbed a 64GB Sandisk Extreme and first formatted it blank with the SD tool from the vendor. I tried my code and it kept failing. I decided to take a look again at the micro SD card on my Mac.
Sure enough it was formatted as EXT-FAT. So I used the native tools in Mac OS X (latest version) to format the drive and I did a 2 pass erase on it and I selected FAT32 (note before doing this I was going to try to use disk util in Windows 10 virtual machine but I didnāt even get an option for FAT32 it went straight to EXT-FAT. I didnāt bother). Once the format on Mac laptop completed I had a blank 64GB FAT32 disk. I was getting optimistic at this point.
Installed the card and it didnāt work! Now I was getting No SD Card Found! So I remember my skills of I.T. Sys Admin Daysā¦tried the very first thing, just give the card a jiggle made sure it was fully inserted and all the cables/pins are plugged in snug. Ran my code boom now it works like a champ I can read/write/append/delete to my hearts content!
My code looks very similar to @Tonygo2 as this is the basic framework I found all over the web for examples.
The only difference is Iām using āDriver.pyā instead of sdcard.py as my library, only because I am using the following sdcard.py code from one of the core people who is a wizard at MP. This could very well be what is in the standard sdcard.py but I was sticking with what worked for me on that combo LCD/SD card and didnāt bother to check if they were different.
Driver that works like a champ at least on Sandisk cards:
sdcard.py - works on Pico/PicoW/rp2040
I have a 128GB Samsung EVO Iām going to format and give that a shot for giggles and see if it works.
Hereās my code and I stole @Tonygo2 's print out in MB (neat!) and added it in to my code. Feel free to edit, distribute, whatever!
from machine import Pin, SPI
import driver
from driver import SDCard
import uos
import utime
from uos import VfsFat
cs = machine.Pin(13, machine.Pin.OUT,value = 1)
#Using SPI1 from Raspberry Pico 2040 Pinouts:
# SCK = or better known as SPI1_SCK 10
# MOSI or bettern known as SPI1_TX = 11
# MISO or better known as SPI1_RX = 12
# SPI chip select pin known as SPI1_CSn
# Initialize the SD card
spi=machine.SPI(1,baudrate=40000000,sck=Pin(10),mosi=Pin(11),miso=Pin(12))
sd=SDCard(spi,Pin(13))
utime.sleep(2)
# Create a instance of MicroPython Unix-like Virtual File System (VFS),
vfs = uos.VfsFat(sd)
# Mount the SD card
uos.mount(vfs, "/sd")
# Debug print SD card directory and files
print(uos.listdir('/sd'))
#Print size in MB
print("Size: {} MB".format(sd.sectors/1024)) # to display card's capacity in MB.
# Create / Open a file in write mode.
# Write mode creates a new file.
# If already file exists. Then, it overwrites the file.
file = open("/sd/sample.txt","w")
# Write sample text
for i in range(20):
file.write("Sample text = %s\r\n" % i)
# Close the file
file.close()
# Again, open the file in "append mode" for appending a line
file = open("/sd/sample.txt","a")
file.write("Appended Sample Text at the END \n")
file.close()
# Open the file in "read mode".
# Read the file and print the text on debug port.
file = open("/sd/sample.txt", "r")
if file != 0:
print("Reading from SD card")
read_data = file.read()
print (read_data)
file.close()
Cheers,
Anglerfish27
I honestly didnāt think the Samsung 128GB card was going to work but it did! I saw about 34 MB used when the format completed so I worried it would jammed up. Nope it worked! See pics of the cards and output. The measurement in the print statement is off by a bit. Iām not concerned with it I can mess with the sector size (even though its 512 at format) and get it to report more accurate sizes. Now on to tinker on an ESP32 devkit that just came in the mail :)