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)