Friday, February 28, 2025

Raspberry Pi Pico audio player

For an index to all my stories click this text.

After all the proceeding stories the time has come to build a complete stand-alone audio player with the Raspberry Pi Pico. The audio quality is not Hifi but it is really good !! Good enough as a player in your mancave, bedroom or even as a portable player. I have made a permanent setup and using it to play music and podcasts. My setup has a few neat extra features which will be described later on. 

The programming is done in MicroPython with Thonny as the IDE.

You can use a Pico or a Pico-W for this project. The pin connections and the program are the same.

To build this I am going to list the parts that are needed first:

- A Raspberry Pi Pico or Pico W
- 3 push buttons
- 4 10K resistors.
- 2 1K resistors
- 4 2K2 resistors
- 2 47nF capacitors.
- 1 SD card module or SD card adapter
- 1 SSD1306 Oled display
- 1 large on several small breadboards
- Dupont wires for the breadboard

Extra

- An active speaker (like a computer speaker) or
- a pair of earplug headphones.
- a 3.5mm contra connector for the headphone or speakers
- an optional small amplifier so you do not need an active speaker
- a pair of small speakers when using the amplifier
- alligator clips for connecting to the headphone or speakers
- Power Bank or USB power supply

To get this working we need a lot of drivers for MicroPython. We need drivers for playing the sounds, we need drivers for the SD card, we need drivers for the SSD1306 oled screen.

I bundled all these drivers on my Github depositry. You can find them here:
https://github.com/Lucvolders/MicroPython/tree/main/Libraries

Next to that you need to know that this audio player only plays 8k WAV files.  

I wrote several stories on each step that was needed to come to this project. I will list here the links to each step.

Converting audio files to an 8K WAV file with Audacity:
http://lucstechblog.blogspot.com/2024/10/audacity-pico-audio-part-1.html

Building the Raspberry Pi Pico audio hardware with just a few resistors and capacitors:
http://lucstechblog.blogspot.com/2024/10/audio-on-pico-part-2-hardware.html

Raspberry Pi pico audio playing software:
http://lucstechblog.blogspot.com/2024/10/pico-audio-part-3.html

SD card for the Raspberry Pi Pico (hardware):
http://lucstechblog.blogspot.com/2025/01/pico-sdcard-part-1-hardware.html

SD card for the Raspberry Pi Pico (software):
http://lucstechblog.blogspot.com/2025/01/pico-sd-card-part-2-software.html

I did not write a story on the SSD1306 oled screen. But that is in my book: Raspberry Pi pico Simplified ;) There is a link to that book on the bottom of this page. The driver is however in my Github depositry.

These stories describe the hardware in details with breadboard setup. The stories also cover the origin of the MicroPython drivers and how to collect them and install them. Like said before I bundled all the drivers in my Github repositry here:
https://github.com/Lucvolders/MicroPython/tree/main/Libraries

I urge you to read these stories so you know what is going on.

The breadboard setup.

Here is the breadboard setup. There are some expansions possible like adding the digital thermometer which was discussed in this story:
http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html



Please note that you are looking at the backside of the SD card. That is the side where the connector is soldered. If you are using an SD card module make sure to attach to the right pins.

As there might be some confusion from looking at the above picture I will show you all the connections here.

The Buttons

Button 01 is attached with a pull-up resistor to pin GP18
Button 02 is attached with a pull-up resistor to pin GP19
Button 03 is attached with a pull-up resistor to pin GP20

Audio

Audio left is attached to GP02
Audio right is attached to GP03

SSD1306

The oled screen is powered by VCC (3.3 Volt) and GND
SDA is attached to GP09
SCL is attached to GP08

SD-Card

The SD card is powered by VCC (3.3 Volt) and GND.

MISO is attached with a pull-up resistor to GP12
CS is attached to GP13
SCK is attached to GP14
MOSI is attached to GP15

You can find all the details about these connections in the previous stories in this series. Please pay special attention to the audio connections to the low-pass filter and the SD card connections.

As the pins where the hardware is attached have to be defined in MicroPython you can also find the pin numbers in the MicroPython program.

The audioplayer program

As you are used to on this weblog, I present the full code here which you can copy and paste into your favorite MicroPython editor.

'''
Audioplayer Plays 8k WAV files
With Oled screen for choosing audiofiles

Audio files on SD card
Button GP18 next number
Button GP19 previous number
Button GP20 play the song
'''

# Import garbage collect
import gc

import os as os
import time
import machine
from machine import SPI, Pin, Timer
import sdcard
from wavePlayer import wavePlayer

import ssd1306

sda=machine.Pin(8)
scl=machine.Pin(9)

i2c=machine.I2C(0,sda=sda, scl=scl,freq=400000)
print(i2c.scan())

oled = ssd1306.SSD1306_I2C(128, 64, i2c)

player = wavePlayer()

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

os.mount(sd, '/storage')

but01=machine.Pin(18, machine.Pin.IN)
but02=machine.Pin(19, machine.Pin.IN)
but03=machine.Pin(20, machine.Pin.IN)

playnow = -1

array=(os.listdir('/storage/Music'))
numbers = len(array)
print (numbers)

oled.fill(0)
oled.show()
oled.text("Raspberry Audio",0,0)
oled.text("player",40,10)
for i in range(0, 127):
    oled.pixel(0 + i, 20, 1)
oled.show()

try: 
   while True:

      
      if (but01.value()==0): 
          print("button 1 pressed")
          playnow = playnow + 1
          if (playnow == numbers):
              playnow = 0
          song = "/storage/Music/"+array[playnow]
          print(type(song))
          help= array[playnow]
          print("Now playing " + help[0:-4])
          
          oled.fill(0)
          oled.text("Raspberry Audio",0,0)
          oled.text("player",40,10)
          for i in range(0, 127):
              oled.pixel(0 + i, 20, 1)
          oled.text("This song is :",0,25)
          oled.text("                   ",0,35)
          oled.text(help[0:-4],0,35)
          oled.show()
          print(playnow)
          time.sleep(.5)
          # test how much memory is free
          gc.collect()
          print(gc.mem_free())  
    
      if (but02.value()==0):
          #player.stop()
          print("button 2 pressed")
          
          if (playnow == -1 or playnow ==0):
              playnow = 0
          else:
              playnow = playnow -1
          song = "/storage/Music/"+array[playnow]
          print(type(song))
          
          help= array[playnow]
          print("Now playing " + help[0:-4])
          oled.fill(0)
          oled.text("Raspberry Audio",0,0)
          oled.text("player",40,10)
          for i in range(0, 127):
              oled.pixel(0 + i, 20, 1)
          oled.text("This song is : ",0,25)
          oled.text("                   ",0,35)
          oled.text(help[0:-4],0,35)
          oled.show()
          print(playnow)
          time.sleep(.5)
          
      if (but03.value() ==0):
          help= array[playnow]
          print("Now playing " + help[0:-4])
          oled.fill(0)
          oled.text("Raspberry Audio",0,0)
          oled.text("player",40,10)
          for i in range(0, 127):
             oled.pixel(0 + i, 20, 1)
          oled.text("Now playing:",0,25)
          oled.text("                   ",0,35)
          oled.text(help[0:-4],0,35)
          oled.show()
          player.play(song)
   
except KeyboardInterrupt:
   player.stop()


All of this code has been explained in the previous chapters except a neat trick.

array=(os.listdir('/storage/Music'))
numbers = len(array)
print (numbers)

The audio files are stored in a subdirectory on the SD card with the name Music. This piece of code puts the directory entries (the music names) into an array and then prints that array on Thonny's console. The number of music tracks is put into the variable number. This variable tells us how many music tracks there are on the SD card.

          print("button 1 pressed")
          playnow = playnow + 1
          if (playnow == numbers):
              playnow = 0
          song = "/storage/Music/"+array[playnow]
          print(type(song))
          help= array[playnow]
          print("Now playing " + help[0:-4])


Pressing the first button increases the playnow variable. That variable points to the array that holds the directory items. The array entry is copied into the help variable and that is printed on the oled screen. This variable entry is the name of the music track that is going to be played if you press the third button.

If the increased number is higher as the number of array entries the number is set back to 0 which is the first array entry.

The same is done for the second button that decreases the variable so walks backwards through the array till it reaches 0.

The last button takes the array entry and plays it.

Operating the player.

First thing to do is saving this program into your Pico and naming it main.py When the Pico reboots MicroPython looks for a program with the name main.py and automatically starts that program. This way you can run this player stand-alone with a USB power supply or, for mobile use, with a power bank.

Once started the oled display shows the text:

Raspberry Audio
   Player


This tells that the program is ready. Press the first button and the names of the music on your SD card in the subdirectory Music will be shown one by one.
By pressing the second button you can scroll backwards through the names.
Pressing the third button will play the song which name is on the oled screen.


Any flaws ??

Well yes actually there is a flaw. Once you start playing a song you can not stop it. You'll have to sit it out before you can choose another one. A work around solution would be adding another button and connect pin 30 to GND when the button is pressed. Pin 30 is RUN and acts as a reset pin. This would actually reset the pico so it physically reboots. You can find how to build a reset button here: http://lucstechblog.blogspot.com/2021/02/raspberry-pico-rest-button.html

Expansions.

I can think of a few.

- First possible expansion is, like discussed above, adding a reset button.
- The player plays a song and then waits till you choose another song and press a button. An expansion would be the possibility to start playing at a certain song and then the next ones in line would play automatic.
- An expansion would be adding a Dallas DS18B20 digital thermometer. You would also need to attach another button that gives you the option to toggle between the audio player and the talking thermometer. You can find the talking thermometer in this story : http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html
- You could add yet another button that would activate a talking clock. You can find the talking clock in this story: http://lucstechblog.blogspot.com/2024/11/pico-audio-part-5-talking-clock.html
- How about adding the talking clock and have the audio player play your favorite song at waking time.
- Remove the buttons (except a reset button) and use a rotary encoder to walk through the menu items/songs etc and choose the item you need.
- Add extra buttons that you can use as preset buttons for playing certain songs. You would add the possibility to program these buttons when you change the SD card for one with different music.

These are just a few ideas I came up with in a few minutes. I believe you can come up with a few of your own......

Please send me a mail if you have build this or something that was based on this project. I am always curious about what my readers come up with.

Till next time,
have fun

Luc Volders


Saturday, February 22, 2025

Pico audio part 6: Talking dice

For an index to all my stories click this text

This is an ongoing series about audio on the Raspberry Pi Pico. This time I am going to build a talking dice. Just like the previous is made in MicroPython.

Actually this is the sixth story on how to play audio on the Raspberry Pi Pico with MicroPython. In this story shows how to build a talking dice. The audio on the Pico does not sound like the crappy audio on the ESP32 with Talkie. That sounded like voice synthesizes on the Commodore 64 and Atari computers if you still remember that. No this is real clear audio. And for the talking dice we are going to use your own recorded voice.

I urge you to read the proceeding stories in which audio recording, the necessary hardware and software were discussed.

Audacity. This is the first story in this series. With Audacity we can record our own voice using a microphone or a build-in microphone in your webcam. You can also convert mp3 files to the required 8K wav files for this project. Read that story here: http://lucstechblog.blogspot.com/2024/10/audacity-pico-audio-part-1.html

Picoaudio 2 the hardware. This story shows what hardware you need. Don't worry its just a few resistors and some capacitors. The total cost will be around 1 Euro/USD without the Raspberry Pi Pico. And as you know the Pico itself will set you back around 5 Euro/USD for the non-wifi version and about 8 Euro/USD for the wifi version.
http://lucstechblog.blogspot.com/2024/10/audio-on-pico-part-2-hardware.html
 
Picoaudio 3 the software. This story describes which MicroPython drivers you will need. In total 5 drivers. The story gives you the links to the download site. Thye story also shows a small program that will play your first sounds.
http://lucstechblog.blogspot.com/2024/10/pico-audio-part-3.html

There are 2 more stories in this series about building a talking thermometer and a talking clock. You can find those here:
http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html
http://lucstechblog.blogspot.com/2024/11/pico-audio-part-5-talking-clock.html

To store the audio clips Pico's memory is rather limited. So the audio is stored on an SD card. Therefore you will need to attach an SD card to the Pico.
 
It is really easy to attach an SD card to the Raspberry Pi Pico. This story shows how to do that: http://lucstechblog.blogspot.com/2025/01/pico-sdcard-part-1-hardware.html

Next to physically attaching the SD card you will also need to install the drivers in MicroPython. This story shows how to do that:
http://lucstechblog.blogspot.com/2025/01/pico-sd-card-part-2-software.html

So what you will need to build the talking dice are the following components:

- A Raspberry Pi Pico or Pico W
- 1 push button
- 2 10K resistors.
- 2 1K resistors
- 4 2K2 resistors
- 2 47nF capacitors.
- 1 SD card module or SD card adapter
- 1 large or several small breadboards
- Dupont wires foir the breaboard

Extra

- An active speaker (like a computer speaker) or
- a pair of earplug headphones.
- a 3.5mm contra connector for the headphone or speakers
- an optional small amplifier so you do not need an active speaker
- a pair of small speakers when using the amplifier
- alligator clips for connecting to the headphone or speakers
- Power Bank or USB power supply

The breadboard setup

As the breadboard setup was extensive discussed in the previous stories I will not go into details here. Just follow those stories.



Back to Audacity

In the first story in this series I showed how to record audio and convert it to an 8K wav file with Audacity. If you did not read that story please do it now.

Our program will speak out the words: one, two, three, four, five and six. Those we already recorded in the story about Audacity :

lucstechblog.blogspot.com/2024/10/audacity-pico-audio-part-1.html


We do want to record some extra sentences. The first one is: "Here we go". And the second one is: "You threw an eight"

So please record these sentences and save them on your SD card in the directory where the number.wav files are stored. Name these files "Here.wav"and "Threw.wav". Chose any name you might like better but keep in mind to alter the names in the program also.

If you do not know how to load and transfer files to an SD card please read this story:
http://lucstechblog.blogspot.com/2025/01/pico-sd-card-part-2-software.html
and this story:
http://lucstechblog.blogspot.com/2023/07/a-few-small-thonny-tips.html



I also added a sound effect of rolling dice which I found on Pixabay. You can find it here: https://pixabay.com/sound-effects/search/dice_roll/
It is called Dice_roll and lasts 1 second. Great for our project.
Make sure you convert it to an 8K wav file before transferring it to the Pico's
SD card. Name this file "Roll.wav"


The talking dice program

Here is the full program in MicroPython. Don't forget to put the required libraries in the lib folder.

'''
Talking dice by Luc Volders
http://lucstechblog.blogspot.com/
'''

import os as os
import machine
from wavePlayer import wavePlayer
from machine import SPI, Pin, Timer
import sdcard
import random

random.seed(None)

but01=machine.Pin(18, machine.Pin.IN)

player = wavePlayer()

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

os.mount(sd, '/sd')

while True:
    if (but01.value()==0):
        player.play("/sd/Sounds/Here.wav")
        player.play("/sd/Sounds/Roll.wav")
        player.play("/sd/Sounds/Threw.wav")
        number = random.randint(1,6)
        strnum = str(number)
        soundstr = "/sd/Sounds/"
        soundstr = soundstr + strnum
        soundstr = soundstr + ".wav"
        player.play(soundstr)

If you have read the previous stories in this series the program speaks for itself. The number.wav files and the "Here.wav", "Roll.wav" and "Threw.wav" files are all in the Sounds directory on the SD card.

The only part that needs some explanation is this part in the While True loop:

        number = random.randint(1,6)
        strnum = str(number)
        soundstr = "/sd/Sounds/"
        soundstr = soundstr + strnum
        soundstr = soundstr + ".wav"
        player.play(soundstr)

The player.play() command accepts a string to point to the file we want to play.
First step is to create a random number from 1 to 6.
We make a string from that number with the str() command.
The first part of the string is the location of the files which is here: "/sd/Sounds/"
We add to this the string with the number and then we add ".wav"
And then the string is put into the player.play() command.

That is all there is to it.

Sidenote

If you are building a dedicated project for this there is enough memory in the Pico to put all the files in just like we did in the first part of this series:
http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html picoaudio4 talking thermometer
That way you can leave the SD card and the accompanying resistor out.

Till next time.
Have fun

Luc Volders


Friday, February 7, 2025

Make charts from your data (Pico SD3)

For an index to all my stories click this text.

I wrote two stories on using an SD card on the Raspberry Pi Pico (and Pico W). The first story was about connecting the SD card. And the second story described how to use the SD card in MicroPython. You can read those stories here, and I urge you to do so.
http://lucstechblog.blogspot.com/2025/01/pico-sdcard-part-1-hardware.html
http://lucstechblog.blogspot.com/2025/01/pico-sd-card-part-2-software.html

After the previous story I received a mail from one of my readers. His question was simple. He wanted to know if I have a method to make a pleasant presentation from the stored data. Charts like line- or bar- charts would be perfect.

Well if you attached the SD card to a Pico W you could produce graphs on a webpage. I  described this in a story on this webpage. You can read that here: http://lucstechblog.blogspot.com/2019/11/esp-webpage-with-linechart.html
That story discussed a program in C++ but the web-page building and the Javascript code can easily be converted to MicroPython.

There is however a quick and easy method.

CSV files.

In the wifi and network world JSON is a standard for transferring files between computer systems and even between programming languages.

For other communications between computer systems there already was a standard and that is still widely used, It is called CSV files.

CSV means comma separated values. So you build a file with lines of data in which the data is separated by comma's.

An example would be a file in which the average temperatures in a certain time span are captured.

For the sake of this story I made such a file. Here is how it looks.

20:05,"15,00"
20:10,"14,94"
20:15,"14,88"
20:20,"14,81"
20:25,"14,94"
20:30,"15,00"
20:35,"15,06"
20:40,"15,06"



The first column shows the time. I measured the temperatures in my man-cave from 20:05 hour to 20:40 hour.
The second column shows the temperatures in degrees Celsius. And yes it was cold !!
The temperatures in the second column are set between quotation marks. If that was not the case 14,94 would get considered as two values 14 and 94 because of the comma in between. The way this file is build "14,94" is seen as a string and not as separate figures.

The program that captures the temperatures.

The breadboard setup for this program is the same as the breadboard setup in the first story about attching an SD acrd. Read that story here: http://lucstechblog.blogspot.com/2025/01/pico-sdcard-part-1-hardware.html

I just added a Dallas DS18B20 to the Pico (GPIO17). 
If you do not know how to add a Dallas DS18B20 thermometer chip you can look at the story about the talking thermometer: http://lucstechblog.blogspot.com/2024/11/pico-audio-part-4-talking-thermometer.html
Or better yet: buy one of my books about the Pico (listed at the bottom of this story) which describe many more sensors...........

The program starts a timer (tim1) that would start a function every 5 minutes (period=1000*60*5). The function captures the temperature and saves the values to the SD card. The program is written in MicroPython and you can find it at the bottom of this page.

There is a special statement in the program for formatting the time.

currenttime =(str("%2.2d" %(hour)) + ":" + str("%2.2d" %(minutes)))

%2.2d converts the string into 2 numbers each with 2 figures. The d defines the numbers as integers.

temperature = ("%2.2f" %(sensor.read_temp(roms[0])))

In this line the temperature is read and formatted in a (f) floating point number with 2 numbers in front and 2 numbers behind the comma.

I will not go over the details because The program is not what this story is about. You can replace the temperature readings with any sensor readings you need/want. You can also alter the timers period from 5 minutes to any interval you like/need. The program can be found at the end of this story.

You can stop the program anytime you like. The program writes the data once every 5 minutes to the SD card and then closes the tempvalues.csv file. You can only stop a program that writes to an SD card if the file is closed. Otherwise you might loose data or even corrupt the SD card.
In this case the file is closed direct after time and temperature is written to it. So each 5 minutes the file is opened for less then a second and then closed again. So no problem in stopping the program whenever you like.



Here you can see the program in action.



And here you can see that the program made a new file in my SD directory (called storage) called tempvalues.csv

Stop the program by clicking CTRL-C in Thonny or clicking on it's stop icon. Now you can transfer the program to your PC.



At the top of Thonny's directory structure choose a directory on your PC where you want to store the file. Then click with the right mouse button on the file and choose Download to.......

Office

A CSV file can be opened by any Office program. I am using OpenOffice. OpenOffice is free and compatible with the other Office packages like Microsoft Office etc. I even use it to write my books on and send the files to the print company.

The only thing you now have to do is to click on thye tempvalues files on your PC and Office will open automatically.



And there it is.



with your mouse select the rows and colums like the picture shows.



From the drop-down menu choose diagram. My Openoffice is in Dutch (like I am) so your office version might look different but the functionality will be the same.



Tell the program to use the first column as the labels. An you are done.
You can change the color of the colums and their appearance.



In OpenOffice you can change the appearance of the graph by clicking on it with the right mousebutton.



There are lots of options in the program so play with these.
I chose to alter the diagram in a line-diagram.



And there we are. A line diagram with a nice capture.

That is it for now. Enough material to play with and display your data in a fancy way.

I am sure there are other programs that can do the same and maybe even in a graphically nicer way. If you know of any drop me a line. It might be interesting to others too.

Till next time,
have fun


Luc Volders

import os
import sdcard
from machine import Timer, SPI, Pin
import time
import machine
import onewire
import ds18x20

# ===================================
# first initialise the Dallas DS18B20
# ===================================
dallaspin = machine.Pin(17)
sensor = ds18x20.DS18X20(onewire.OneWire(dallaspin))
 
roms = sensor.scan()
print('A ds18x20 is present')

# =================================
# Initialise the RTC and local time
# =================================
rtc = machine.RTC()

year = 2023
month = 2
date = 17
hour = 20
minutes = 00

rtc.datetime((year, month, date, 0, hour, minutes, 00, 0))
print(time.localtime())

# ===============
# Start the timer
# ===============
tim1=Timer()

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

os.mount(sd, '/sd')

# ===============================================
# Function that gets the time and temperature and
# writes it to SD
# ===============================================

def function1(noval):
    # get the time
    hour = time.localtime()[3]
    minutes = time.localtime()[4]
    currenttime =(str("%2.2d" %(hour)) + ":" + str("%2.2d" %(minutes)))
    print(currenttime)
    print(type(currenttime))
    
    # get the temperature
    sensor.convert_temp()
    time.sleep(1)
    temperature = ("%2.2f" %(sensor.read_temp(roms[0])))
    temperature = temperature.replace(".",",")
    print(temperature)
    print(type(temperature))
    
    # write to sd
    file = open('/sd/tempvalues.csv', 'a')
    # for Europe when decimal ,
    file.write(currenttime + "," + '"' + temperature + '"' + "\n")
    # for US with decimal .
    # file.write(currenttime + "," + temperature + "\n")
    file.close()
    
tim1.init(period=1000*60*5, mode=Timer.PERIODIC, callback=function1)  

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