Pico Mounting SD Card

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico Mounting SD Card

Post by Roberthh » Wed Feb 03, 2021 1:58 pm

There was not much to implement, and I tried it out of own interest. And last not least - I have to learn dealing with cmake. And taht was at least a start.

kbradley89
Posts: 5
Joined: Sun Jan 31, 2021 10:02 pm

Re: Pico Mounting SD Card

Post by kbradley89 » Thu Feb 04, 2021 9:49 am

The build works like a charm, I can now log data on the built in flash and whenever someone wants to get the data they can press a button and it copies the data to the SD card. I'm doing this as I'm trying to reduce power consumption, and I've read that writing to an SD card has a relatively high power consumption. If anyone happens to know if limiting the SD card usage actually makes a difference to power consumption that would be great, otherwise I'll probably power my data logger on 3 AAs and find out whether logging to the built in memory or the SD card makes any difference to the run time of the device.

Thanks for your help.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Sun Jul 18, 2021 6:54 am

Hi,
I'm still struggling with the Pico and an SD card.

Here is what i did in Micropython 1.16

Code: Select all

>>> from machine import SPI, Pin
>>> import uos
>>> import sdcard
>>> sd = sdcard.SDCard(SPI(1), Pin(9))
>>> vfs = uos.VfsFat(sd)
>>> uos.mount(vfs, "/sd")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 19] ENODEV
From the previous posts I understood that the problem is a missing FAT support of uos?
Though, if that was true, why do I only get an error when I try to mount the file system?

Roberthh posted a link for Micropython WITH Fat support,
https://github.com/robert-hh/micropython/tree/rp2_fat
but it goes into the void for me.

Could someone share his wisdom with me? I would be very glad to log measuring values on an external medium, as the internal storage is rather limited.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico Mounting SD Card

Post by Roberthh » Sun Jul 18, 2021 7:58 am

FAT support is included in v1.16. otherwise VfsFat would not exist. I mounted already SD card on RP2, so it's possible. Error 19 indicates that the file system could not be detected. The SD card must only have a single FAT partition. Sometimes new cards have another small partition as the first one. That confused the driver. And not all cards work. I tested it with Samsung, Sandisk and Transcend cards in to he range of 8 to 64 GB.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Sun Jul 18, 2021 8:00 am

Thank you, Robert!

Good news:
Just now I succeeded in mounting another freshly formatted (FAT32) card in a folder "/test" I had created in the internal file system of the Pico.

Here is the code:

Code: Select all

from machine import SPI, Pin
import uos
import sdcard
import time

sd = sdcard.SDCard(SPI(1), Pin(9))
vfs = uos.VfsFat(sd)
uos.mount(vfs, "/test")

l = uos.listdir("/test")
print(l)
uos.umount("/test")
The unmount at the end is important, otherwise I get an error when restarting the program, as the card is already mounted.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Sun Jul 18, 2021 8:43 am

I have tested several SDcards, 4 out of 9 worked, the other produced errors:

Code: Select all

OSError: timeout waiting for v1 card    
or

Code: Select all

OSError: no SD card
in init_card

though the cards worked fine in Linux Mint on my computer, and though they were freshly formatted.

Maybe this is due to a timing problem?
I recall a similar behavior of some cards in Arduino.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Mon Jul 19, 2021 9:03 am

Here is a small test program that works for me:

Code: Select all

from machine import SPI, Pin
import uos
import sdcard
import time

sdfolder = "/SD"

def readfile(fname):
    f = open(fname, 'r')
    t = f.read()
    f.close()
    print(t)

def writefile(fname, text):
    f = open(fname, 'w')
    f.write(text)
    f.close()

def mountsdcard(sdfolder):
    create_mountpoint(sdfolder)
    sd = sdcard.SDCard(SPI(1), Pin(9))
    vfs = uos.VfsFat(sd)
    uos.mount(vfs, sdfolder)

def create_mountpoint(sdfolder):
    try:
        print("Creating SD mountpoint " + sdfolder)
        uos.mkdir(sdfolder)
    except:
        # remove trailing '/' before checking -> sdfolder[1:]
        if sdfolder[1:] in uos.listdir("/"):
            print ("Mountpoint already there: " + sdfolder)
        else:
            print("Could not create SD mountpoint + sdfolder")
        
        

mountsdcard(sdfolder)

l = uos.listdir(sdfolder)
print(l)

writefile(sdfolder + "/blah.txt", "Blahblah\npi = 3.14")
readfile(sdfolder + "/blah.txt")

uos.umount(sdfolder)
The test program mounts the card under “/SD” in the Pico file system.
If there is no “/SD” folder, it is created.
After this, it lists the directory on the card, writes a test file and reads it back.
At last the card is unmounted.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Sat Aug 14, 2021 4:14 pm

I am currently working on a set of tools that makes using an SD card easier.
There is an object defining the SD card with functions to mount, unmount, list files / folders etc.
The usage is like this:

Code: Select all

if __name__ == "__main__":
   
    sdfolder = "/SD"		   # (mountpoint)	
    SPIselect = 0                  # 0/1 for SPI0 or SPI1
    SD_CS = 5                      # Chip select for SD  (may be any GP pin)
    
    sd = SD(SPIselect, SD_CS, sdfolder)
    
    if sd.error:
        print(sd.error)
    else:

        print("SD folder: ", sd.sdfolder)
        
        l = sd.list_all()
        print("Files and folders on SD: ", l)
        
        
        l = sd.listfolders()
        print("Folders on SD: ", l)
        
        l = sd.listfiles()
        print("Files on SD: ", l)
            
        sd.unmount()
And the class and functions definition is here (not ready, but in part already usable)

Code: Select all

import uos
import sdcard
from machine import SPI, Pin, UART, I2C

# ------------------------------------------------------------------------

#     TOOLS

def is_folder(filename):
    # True if folder
    
    if uos.stat(filename)[0] & 0x4000:
        return True
    else:
        return False
    
def is_file(filename):
    # True if file, not folder
    if uos.stat(filename)[0] & 0x8000:
        return True
    else:
        return False
    
def list_folders(folder):
    # list subfolders of folder
    l = uos.listdir(folder)
    folders = []
    for f in l:
        f = folder + '/' + f
        
        if is_folder(f):
            folders.append(f)
    ##folders = [f for f in l if is_folder(f)]   ## this gives error if no folder there
    
    return folders

def list_files(folder):
    # list files in folder (subfolders excluded)
    l = uos.listdir(folder)
   
    files = []
    for f in l:
        f = folder + '/' + f
        
        if is_file(f):
            files.append(f)
    #files = [f for f in l if is_file(f)]
    return files
# ------------------------------------------------------------------------

# MAIN SD class, needs sdcard.py (driver)

class SD():
    def __init__(self, SPIselect, SD_CS, sdfolder):
        print("Defining SD")
        self.SPIselect = SPIselect
        self.SD_CS = SD_CS
        self.sdfolder = sdfolder
        self.error = ""
        
        # define SD
        try:
            self.sd = sdcard.SDCard(SPI(SPIselect), Pin(SD_CS))
            
            
        except OSError as e:
            self.error = (str(e))
            self.sd = None
        except:
            self.error = "SD card ERROR"
            self.sd = None
    
        # mount SD
        self.mountsdcard()
        
    def mountsdcard(self):
        print("Mounting SD")
        self._create_mountpoint()
        
        try:
            vfs = uos.VfsFat(self.sd)
            uos.mount(vfs, self.sdfolder)
            self.error = ""
        except OSError as e:
            
            if "EPERM" in str(e):
                print("SD card already mounted!")
                self.error = ""
            else:
                self.error += '\t' + "MOUNT ERROR"
        except Exception:
            self.error += '\t' + "MOUNT ERROR"
       
    def _create_mountpoint(self):
        # if sdfolder does not exist, create it (to use as mountpoint)
        if (self.sdfolder in list_folders('/') == False):
            print("Creating SD mountpoint " + sdfolder)
            uos.mkdir(self.sdfolder)      

    def unmount(self):
        print("Unmounting SD card")
        uos.umount(self.sdfolder)
              
    def list_all(self):
        return  uos.listdir(self.sdfolder)
        
    
    def listfolders(self):
        return list_folders(self.sdfolder)
    
    def listfiles(self):
        return list_files(self.sdfolder)
    
def readfile(fname):
    f = open(fname, 'r')
    t = f.read()
    f.close()
    return t
    print(t)

def writefile(fname, text):
    f = open( fname, 'a')
    f.write(text + '\n')
    f.close()

Feedback is welcome.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Pico Mounting SD Card

Post by Roberthh » Sat Aug 14, 2021 4:35 pm

That looks fine and useful. For comparison and ideas, you may also have a look at https://github.com/pfalcon/micropython-lib.git, folder upysh.

User avatar
jcf
Posts: 60
Joined: Wed Mar 03, 2021 11:14 am

Re: Pico Mounting SD Card

Post by jcf » Mon Aug 16, 2021 4:01 pm

Thanks Robert,
I had a look. Must confess that I did not understand everything, but it looks interesting.
Here is my more or less ready version of "filetools.py":

Code: Select all

''' filetools by jean-claude.feltes@education.lu
    for working with SD card'''

import uos
import sdcard
from machine import SPI, Pin

# ------------------------------------------------------------------------

#     FILE TOOLS

def is_folder(filename):
    # True if folder
    if uos.stat(filename)[0] & 0x4000:
        return True
    else:
        return False
    
def is_file(filename):
    # True if file, not folder
    if uos.stat(filename)[0] & 0x8000:
        return True
    else:
        return False
    
def list_folders(folder):
    # list subfolders of folder
    l = uos.listdir(folder)
    folders = []
    for f in l:
        fi = folder + '/' + f
        if is_folder(fi):
            folders.append(f)
    ##folders = [f for f in l if is_folder(f)]   ## this gives error if no folder there
    
    return folders

def list_files(folder):
    # list files in folder (subfolders excluded)
    l = uos.listdir(folder)
   
    files = []
    for f in l:
        fi = folder + '/' + f
        if is_file(fi):
            files.append(f)
    #files = [f for f in l if is_file(f)]
    return files
# ------------------------------------------------------------------------

# SD TOOLS
# MAIN SD class, needs sdcard.py (driver)

class SD():
    def __init__(self, SPIselect, SD_CS, sdfolder):
        print("Defining SD")
        self.SPIselect = SPIselect
        self.SD_CS = SD_CS
        self.sdfolder = sdfolder
        self.error = ""
        
        # define SD
        try:
            self.sd = sdcard.SDCard(SPI(SPIselect), Pin(SD_CS))
            
            
        except OSError as e:
            self.error = (str(e))
            self.sd = None
        except:
            self.error = "SD card ERROR"
            self.sd = None
    
        # mount SD
        self.mountsdcard()
        
    def mountsdcard(self):
        print("Mounting SD")
        self._create_mountpoint()
        
        try:
            vfs = uos.VfsFat(self.sd)
            uos.mount(vfs, self.sdfolder)
            self.error = ""
        except OSError as e:
            
            if "EPERM" in str(e):
                print("SD card already mounted!")
                self.error = ""
            else:
                self.error += '\t' + "MOUNT ERROR"
        except Exception:
            self.error += '\t' + "MOUNT ERROR"
       
    def _create_mountpoint(self):
        # if sdfolder does not exist, create it (to use as mountpoint)
        if (self.sdfolder in list_folders('/') == False):
            print("Creating SD mountpoint " + sdfolder)
            uos.mkdir(self.sdfolder)      

    def unmount(self):
        print("Unmounting SD card")
        uos.umount(self.sdfolder)
              
    def list_all(self):
        return  uos.listdir(self.sdfolder)
        
    
    def listfolders(self):
        return list_folders(self.sdfolder)
    
    def listfiles(self):
        return list_files(self.sdfolder)

    
    def readfile(self, fname):
        fname = self.sdfolder + '/' + fname
        f = open(fname, 'r')
        t = f.read()
        f.close()
        return t
       

    def _write2file(self, fname, text, mode = 'a', end = '\n'):
        # mode = 'a' (append) or 'w' (write)
        fname = self.sdfolder + '/' + fname
        f = open( fname, mode)
        f.write(text + end)
        f.close()

    def writefile(self, fname, text):
        # writes a new file to SD card
        self._write2file(fname, text, mode = 'w')
        
    def print(self, fname, text, end = '\n'):
        # works like print, but to a file on the SD card
        # appends text to file fname with line feed at the end
        self._write2file(fname, text, mode = 'a', end = end)
        
#-----------------------------------------------------------------------------------
if __name__ == "__main__":
    
    
    sdfolder = "/SD"
    SPIselect = 0                  # 0/1 for SPI0 or SPI1
    SD_CS = 5                      # Chip select for SD  (may be any GP pin)
    
    sd = SD(SPIselect, SD_CS, sdfolder)
    
    if sd.error:
        print(sd.error)
    else:

        print("SD folder: ", sd.sdfolder)
        
        l = sd.list_all()
        print("Files and folders on SD: ", l)
        
        
        l = sd.listfolders()
        print("Folders on SD: ", l)
        
        l = sd.listfiles()
        print("Files on SD: ", l)
        
        t = 'Blahblah ghjghg pi'
        sd.writefile('blah.txt', t)
        
        t = "This is appended to the file"
        sd.print('blah.txt', t)
        sd.print('blah.txt', t)
        
        t = sd.readfile('blah.txt')
        print(t)
        
        sd.unmount()

For me sd.print is especially interesting, as I can use it like print to Serial.
Also sd.error is a variable that I can use at any time to see if everything's right, especially if the sd card is mounted correctly.
Maybe sd.error should be updated also in sd.readfile or sd._write2file?

Post Reply