Friday, January 17, 2025

Pico SD card Part 2 the software

For an index to all my stories click this text

A microcontroller like the Raspberry Pi Pico has a lot of memory for IOT projects. 256K Ram and 2MB on board Flash memory seems a lot of storage. And indeed it is if you are saving your programs in the flash memory and doing IOT projects.
If you are working with audio files like we were doing in the talking thermometer (click here http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html) and talking clock stories (click here http://lucstechblog.blogspot.com/2024/11/pico-audio-part-5-talking-clock.html ) then 2MB storage suddenly is tight.

Attaching an SD card is the option to get a lot of storage to your Pico.
The previous article in this series showed how to attach an SD card to the Raspberry Pi Pico. This story tells how to use it in MicroPython. You can read it here:
http://lucstechblog.blogspot.com/2025/01/pico-sdcard-part-1-hardware.html

First a word of caution !!!

I tested the SD card on a Raspberry Pi Pico W. Everything worked just fine. Then I switched to the Pico without wifi and it did not work anymore. Back to the Pico W and everything worked again !!! What could be the issue ?? After some search I found the solution and it is easy. I was testing the SD card on one of my first Pico's. The one I wrote my first book about the Pico on.  And then it became clear. Obvious I was working on a Pico with an old version of Python. So update your Pico so that you at least have MicroPython v1.19.1. installed.

Another thing to note is that the card must be formatted in the FAT32 format. Actually this is the standard format most cards are formatted in. When in doubt re-format the SDcard on your PC. Newer filesystems like NTFS and Linux filesystems are not recognized by the driver.

The SD card driver

To use an SD card with the Pico you need a driver for MicroPython. And for one reason or another the SDcard driver disappeared from the current MicroPython repositry on Github. Fortunately I succeeded in locating it in an old repositry:
https://github.com/micropython/micropython-esp32/tree/esp32/drivers/sdcard

For your convenience I have put up a copy of the driver on my own Github repositry. You can find it here:
https://github.com/Lucvolders/MicroPython/blob/main/Libraries/SDcard/sdcard.py

AND MIT LICENSE
https://github.com/micropython/micropython-esp32/blob/esp32/LICENSE

You need to install this driver manually.
First step is to click on sdcard.py so the file opens. Then copy the file. In Thonny open a new file and paste the copied file.



Then save it in your Pico's lib folder. Use save-as.for this.



Give this file the exact name: sdcard.py like I did as you can see in the picture. And save it in the lib folder. If you give the file another name or misspell a letter the driver will not work.

Storing data on the card

Now everything is in place we can start using the SD card for storing data and reading it back.

Here is a small program that writes one line of text to a file on the SD card.

import os
from machine import SPI, Pin
import sdcard

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

os.mount(sd, '/sd')

file = open('/sd/test.txt', 'a')
file.write('Testing the SD card')
file.close()

Let us look at the different parts of the program.

import os
from machine import SPI, Pin
import sdcard

The first 3 lines import the required libraries. We need the os and the sdcard library for the filesystem and the addressing of the SD card. The machine library is needed to control Pico's pins.

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)


These lines define the pins where the SD card is connected to. The previous story discussed how the SD card is connected to the Pico (or Pico W). Check that story for the details. You can find it here XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The last line makes an instance of the SDcard driver with the defined pins and we call it sd which is easy to refer to.

os.mount(sd, '/sd')

This line activates the SD card.

file = open('/sd/test.txt', 'a')

The first thing to do is to open a file. The open command begins with defining where the file is located and the name of the file. So the file is on the SD card (/sd) in it's home directory "/" and has the name "test.txt". You can use any name you like. Best thing is to use the same name conventions as on your PC so you can easily exchange data later on.

In this example we are going to write to the SD card's home directory which is addressed as "/". Later on I show how to make subdirectories just like a lib directory on the Pico itself.

When opening a file there are 3 options.

w for writea for append
r for read


If you want to overwrite an existing file use the w option. For adding text to a file use the a  option. For reading from a file use the r option.

As we have an empty SD card I could have used the w (write) option. The a (append) option will create the file if it does not exist.

file.write('Testing SD the card')

This is the line that writes the data ("Testing the SD card") to the card.

You can replace the text line with a variable which in a real life project you will certainly do.

file.close()

After writing you MUST close the file. If you do not issue this command any data that is still in the memory of the Pico and not written to the SD card will be lost.

Please note that you can only write strings to the SD card. If you want to store figures convert them to a string first.

Here is an example that would write the value 88.6 to the test.txt file.

file = open('/sd/test.txt', 'w')
a = str(88.6)
file.write(a)
file.close()


 

Reading data from the card

Reading data from an SD card is almost the the same as writing to the card.

file = open('/sd/test.txt', 'r')
data = file.read()
file.close()

print(data)

The file is opened, the data is read with file.read and the file is closed. Then we can print the data.

Please note that you can alter the names of the variables to your liking. You can use for example storedtemps in stead of file, or temp in stead of data. Use names that are meaningful.

Formatting the text

Look at the next example:

file.write('Testing SD the card')
file.write('for this story')

To read those lines you will need to use 2 read commands like this:

line1 = file.read()
line2 = file.read()

And later on you would print the lines:

print(line1)
print(line2)


This would show in the shell:

>>> %Run -c $EDITOR_CONTENT

   Testing SD the card for this story

>>>


Although you used 2 file.write() lines in the program they are stored as 1 line and there is no space between the "card"and "for" text.

To achieve that each line is printed on a new line on your screen you need to use a so called escape character. The escape character for a new line is \n. So you shouls alter the lines in:

file = open('/sd/test.txt', 'w')
file.write('Testing SD the card\n')
file.write('for this story')
file.close()

If you now read the file with:

file = open('/sd/test.txt', 'r')
line1 = file.read()
line2 = file.read()
file.close()

print(line1)
print(line2)


The shell shows the next lines.

>>> %Run -c $EDITOR_CONTENT

   Testing SD the card
   for this story

>>>


There are several escape characters available but "\n" is the most important one. The one you will use most.

For completeness I give you here all the escape characters that I tested :

\' — Single quote
\" — Double quote
\\ — Backslash
\n — New line
\r — Carriage return
\t — Horizontal tabulator





Storing a lot of data

Writing one or two lines to the SD card is ok. But what if you need to store lots of data.

Here is a small program that stores 10 random values.

import os
from machine import SPI, Pin
import sdcard
import random

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

os.mount(sd, '/sd')

file = open('/sd/testvalues.txt', 'w')
for i in range(1, 11):
    number = random.randint(15 , 30)
    numstring =  str(number)+"\n"
    file.write(numstring)
file.close()


The random function is imported at the beginning of the program.
In the for loop a random number is created with a value between 15 and 30. That number is converted to a string and "\n" is added to that string so every value is written on a new line.


And here is an example that shows how to read the values.

import os
from machine import SPI, Pin
import sdcard

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

os.mount(sd, '/sd')

file = open('/sd/testvalues.txt', 'r')
for i in range(1, 11):
   value = file.readline()
   value = int(value)
   print(value)
file.close()

 

The command file.readline() reads one line from the file and puts it in the variable value. This variable is a string as everything written to the SD card is a string. The variable is converted to an integer and then printed in the shell.

This only works if you know how many lines of text are present in the file. In this example we know that from the previous program where the data is written. In a real life situation you might not know this.

A better option would therefore be this:

import os
from machine import SPI, Pin
import sdcard
import random
import time
spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

os.mount(sd, '/sd')

file = open('/sd/testvalues.txt', 'r')

valuelist = []

for value in file:
    value = int(value)
    valuelist.append(value)
file.close()

print(valuelist)

A list (a kind of array) is defined with the name valuelist[]. The for loop reads all lines in the program and puts them into the list. If we wish, and yes in real life you would, you can pick an individual value from the list and perform some action on it.

If you need the fourth value from the list you can retrieve that with:

need = valuelist[3]

In this example you can stop reading the file when the value variable equals a certain number. You can achieve that with the next lines:

for value in file:
    value = int(value)
    if value == 15:
        break
    valuelist.append(value)
file.close()

print(valuelist)

If the read value is equal to 15 the break command is executed which stops the for loop.

Working with lists and files is also discussed in my book "Raspberry Pi Pico Simplified" and in "Raspberry Pico W Simplified" on which you can find a link at the bottom of this page.


The file system.

Just like on your PC you will want to see the contents of the SD card, make directories, remove directories, rename files, and delete files. That is all possible.

Before you can use the commands for the file system functions you will have to activate the SD card. Here is a small program that does that for you:

import os
from machine import SPI, Pin
import sdcard

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

os.mount(sd, '/sd')





This is how my file structure looks in Thonny.

Run the above lines TWICE !!



And suddenly the SD card appears in Thonny's file structure.



Click on the small + sign in front of the sd directory and the directory will open. Just as on your PC.



You can access certain file commands from Thonny by clicking with the right mouse button on a file. AS you can see you can create a new directory, delete the (highlited) file or copy that file to your PC.



You can change the PC's directory by clicking at the top in Thonny Files section on This computer and stroll through your directories like you are used to.


 

The file system with Thonny.

In Thonny's shell you can type the commands for manipulating files on your SD card. This only works if the SD card is still activated. The SD card is still activated when it is visible in Thonny's file structure.
If it is not visible anymore run the small activation program shown above.

Here are the commands you can use.

Directory

print(os.listdir('/sd'))



This will show the contents of the SD card as text. You will see the sub-directories and the files.

Change Directory

os.chdir("/sd/name")

This will change the working directory to the named subdirectory. Any command you will give next will effect the files in the subdirectory "name"



As you can see you do not have to give any parameters to os.listdir() as the file system knows that "/sd/Programs" is the working directory you are operating in.

Remove a file

os.remove("example.txt")

This command removes the file example.txt from the working directory.


Make a directory

os.mkdir("test")

This will create a new directory in the working directory. If you need to create a directory in the home directory of your SD card use:

os.mkdir("/sd/test")

Or first change the working directory back to the home directory with os.chdir("/sd")


Remove a directory

os.rmdir("/sd/test")

This command will remove the complete directory "test" including its contents.


Rename a file

If you need to rename a file use the following command:

os.rename("/sd/oldname", "/sd/newname")

Please not that you have to use the complete name. If a file has the name test.py and you try to rename it like: os.rename("/sd/test", "/sd/testnew.py") then you will get an error. If you rename it like this: os.rename("/sd/test.py", "/sd/newtest") then the file will not be recognized as a MicroPython file as the .py is missing inn the name.

A few footnotes.

All the above commands start with os. like os.mkdir() etc. That is because the import command at the top of the program is used as:

import os

If you use something like:

import os as filesys

Then you can start the commands like:

filesys.mkdir()

here is an example that shows what I mean.

import os as filesystem
from machine import SPI, Pin
import sdcard

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

filesystem.mount(sd, '/sd')

print(filesystem.listdir('/sd'))

The same way you can alter the name of the SD card in storage or any name you like.

import os as filesystem
from machine import SPI, Pin
import sdcard

spi = SPI(1,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

filesystem.mount(sd, '/storage')

print(filesystem.listdir('/storage'))

As you can see I changed the name of the SD card as well as the name with which I am using the filesystem.

And the last note is already obvious from the above program. You can use all the commands in a program. You can even use a variable as a parameter for filenames etc.

Next step.

The Pico's audio like described in the previous story in ehich the programs get the audio files from the SD card !!! Let's make an audio player that can play your favorite music or podcasts !!!

Till next time
have fun !!

Luc Volders

Friday, January 10, 2025

Pico SDcard part 1 the hardware

For an index to all my stories click this text

This story tells how to attach an SD card to your Pico. Attaching an SD card brings a tremendous amount of extra memory to your Pico. We can use that for storing sensor data,  images and webpages for the Pico webserver but also for storing audio. Audio ?? Yes Audio !!

This is a two part story. This part describes how to attach the SD card. Next part shows how to use the SD card with MicroPython.

If you follow this weblog you will know that I was using speech synthesises on the Raspberry Pi Pico and build a talking thermometer and a talking clock. You can read those stories on this weblog. However I wanted more. I wanted to build an audio player. Pico's sound is great and you really only need a few resistors and capacitors to achive it. The problem was capacity. Pico only has 2MB  flash memory. And although that is enough to store dozens of programs it is to little to store music files. So I attached an SD card for more storage.

SD card module

The easiest way to attach an SD card to your Pico is to use an SD card module.



This module has several advantages. At the top you can see a voltage regulator. The regulator brings 5V back to 3.3V. The card as well as the Pico operate at 3.3V. The regulator allows you to feed the module with 5V. Use the 5V pin for that. Then there is a 3.3V pin so you can power this adapter straight from Pico's 3.3V out pin.

There is also a range of smd resistors on the board. They will be discussed further on.



The module accepts standard SD cards. Using a micro SD adapter you can also use micro SD's. These adapters are often sold together with your micro SD card.

The module has a double set of header pins, but they are identical. No idea what the purpose is of the double set headers except that they give more stability when putting the adapter on a breadboard.

There are other modules available which are specifically build for micro sdcards. Personally I have not used them but I have seen their pictures and they use the same pins although they have a different pin-layout.
 
The module is attached to the Pico or Pico W with its's SPI interface. The module is next to GND and 3.3V attached with 4 pins to the Pico.

Mosi ==> GP15
SCK  ==> GP14
CS   ==> GP13
Miso ==> GP12


And here you can see how this module is connected.



SD card adapter

There is another option if you are not afraid to solder.



As you can see the sd card adapters pins almost perfectly match the pins of a standard header. So we can solder the header pins directly to an sd card adapter. This is my favorite way to use the SD-cards.



My soldering skills are not the best, but hey it works !!

There are now two options.



We can use female-male dupont wires to connect the sd card headers to the Pico. The female connecters plug into the headers and the male connectors into the beadboard next to Pico's GPIO pins.



Another option is to plug the headers direct into the breadboard. This is actually a better option as shorter wires are less prone to produce noise when we later attach the audio part.

In the picture there is a label on top of the Pico. I use it all the time for easily identifying the GPIO pins. You can find this label here: http://lucstechblog.blogspot.com/2021/03/raspberry-pico-pin-layout-help.html



And this is how the connections are made. Please note the slanted side of the SD-card adapter. On the previous photo the card is turned around. So please note where the connections are.



Here you can see the breadboard layout. This is the same as on the picture.

If you look closely to the SDcard Module as discussed earlier you can see that there are tiny resistors labeled R1 to R4. These are pull-up resistors and connected to Mosi, SCK, CS and Miso.

In my particular case I only connected a 10K pull-up resistor to the MISO pin. This is required !!! If you omit this resistor the SD card will not function properly. I did not connect pull-up resistors to the other pins and experienced no problems. If you do run into errors add the resistors.

For reference I hereby give you the pin connections again:


Mosi ==> GP15
SCK  ==> GP14
CS   ==> GP13
Miso ==> GP12
VCC  ==> 3V3 out
GND  ==> GND



Next step

If you are not confident with soldering you can use the module which fits direct on the breadboard. Otherwise solder the headers to an SD card adapter. When done you are ready for the next step. Using the card with your Pico and MicroPython.

In the next story I am going to show how to install the MicroPython drivers, get the SD card's directory, make and remove subdirectories, save programs, write files, delete files and rename files. I'll also show how to use the SD card as storage for your data.

Till next time,
have fun

Luc Volders










Friday, January 3, 2025

Replicating a statues leg

For an index to all my stories click this text

An acquaintance of mine brought me a leg from a pedestrial with a small statue that was broken and asked if I could make (3D print) a replica. Well here we go.


The picture does not show it well, but the thing was 3.4cm wide. And as you can see, at the left side some of the wood was damaged.
Drawing the thing was difficult as most of the parts were slightly bend or rounded. And drawing such a thing is not my forte.

So I decided to try a different approach.


I scanned the object with my flatbed scanner. That went rather well. There are some shades around the object so I manually cut out the contours in the Gimp (open source alternative for Photoshop).

When I had the contours I filled the scanned object with the color black. I saved the file as a JPG file in a resolution of 300DPI.

SVG

In order to get the scanned image into my 3D Cad program (Tinkercad) I had first to convert it into an SVG file as that is the only image file Tinkercad accepts.


For converting the JPG to SVG I used a website called convertio. It can be found here: https://convertio.co/
It is a free service in which you upload your file and indicate to which format you want it converted and after a little time you can download the file in the converted format. The time depends on how busy the site is and how large your file is but generally your request is finished within one or a few minutes. This small file just took a few seconds.


The SVG file can then be imported into Tinkercad and scaled. Don't forget to scale the height of the object. I had to keep the exact length, width and height so this was scaled to 3mm.

When scaled the leg was saved as an STL file.


The STL file was then imported into Cura and at last printed on my Creality CR20 Pro. It took 10 minutes to print.


And here are the original and the copy side by side.
I printed a few of these in black and white. The white versions can be painted with Acrylic paint but that is up to my acquaintance.

Conclusion.

I wanted to try if it was possible to scan a 3D object and reproduce it that way and indeed it was possible. Most of the work was editing the scanned picture in The Gimp. After that it was a piece of cake. The whole process (inclusive printing) took about 2 hour which was mostly due to the lack of experience.

Till next time
have fun

Luc Volders