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