daniebker

Building a Pocket Music Player with a Pi Zero and Pirate Audio

· Daniel Baker

pirateplayer turns a Raspberry Pi Zero, a Pimoroni Pirate Audio HAT and a small UPS battery into a self-contained, pocketable music player. No phone, no streaming, no subscription: just your MP3, FLAC and WAV files in folders, a 240×240 screen, and four buttons.

I wanted something that behaves like an old-school MP3 player. Everything I tried off the shelf either lacked a decent interface or the Bluetooth kept dropping out. I wanted something that you turn it on, it picks up where you left off, and it gets out of the way. Everything here is written in Python and runs comfortably on a Pi Zero.

The hardware

The build is three boards stacked together. The Pirate Audio HAT does most of the work: it gives you a DAC for line or headphone output, an ST7789 colour LCD and four tactile buttons.

Part Role
Raspberry Pi Zero Runs the player, mpv and the UI
Pimoroni Pirate Audio HAT 240×240 ST7789 screen over SPI, I2S DAC, buttons A, B, X, Y
Waveshare UPS HAT (C) Battery, read directly over I2C (INA219 at 0x43)

With battery = "auto" the player probes for either UPS on startup. Supporting another board means adding one class with a read() method to battery.py.

Features

The player is small, but it covers everything I wanted from a daily-use device.

Four buttons, two screens

Four buttons cover everything because each one has a tap and a hold, and their meaning changes with the screen. A press becomes a hold after 0.6 seconds; holding A or B in the browser keeps scrolling. On the board, A is top-left, B bottom-left, X top-right and Y bottom-right.

Button Browser Now playing
A Up (hold to scroll) Volume up
B Down (hold to scroll) Volume down
X Open folder, or play the folder from this track Play / pause
X hold Play the whole folder, including subfolders Next track
Y Parent folder (at the top: go to now playing) Back to the browser, current track selected
Y hold Go to now playing Previous track, or restart if more than 3 s in
A + B hold 3 s Power off Power off

The Y behaviour on the now-playing screen is a small detail I like: going back to the browser opens the current track’s folder with that track highlighted, so you can pick a neighbour without hunting for it.

Under the hood

The code is a single main loop that ticks every 50 ms: read buttons, handle player events, check the battery, and redraw only when something changed. A few pieces are worth calling out.

A desktop simulator

You don’t need the hardware to try it. Run python3 -m pirateplayer --sim and a pygame window shows the board, with the screen in the middle and the four buttons on either side. Each button’s label updates to show what it does on the current screen.

Only the screen, buttons, battery and audio are simulated; the browser, playlists, state saving and UI are the same code that runs on the Pi. That made developing on a laptop much faster than redeploying to a Pi Zero for every change.

Control Action
Click a button, or A/Up, B/Down, X/Enter/Right, Y/Esc/Left Press it; hold for a long press
A and B held for 3 s Power off (closes the window)
C Connect or disconnect the charger
- / = Lower or raise the battery by 10%

Audio plays through pygame by default. Add --mpv to use the Pi’s real mpv playback code, --scale 3 for a bigger screen, or --music DIR to point it at another folder.

Getting it running

On Raspberry Pi OS Bookworm or later (Lite is fine), copy the project to the Pi and run the installer, then reboot:

./install.sh
sudo reboot

The script does the fiddly setup for you:

Then copy music into ~/Music, for example with rsync or scp. Logs are one command away with journalctl -u pirateplayer -f.

To change settings, copy config.example.toml to ~/.config/pirateplayer/config.toml. You can set the music folder, starting volume, screen timeout (0 keeps it on), display rotation, battery type, low-battery threshold and mpv audio device. If button Y does nothing, you have an early board: set y_pin = 20.

Wrapping up

pirateplayer is a focused little device: put files in folders, press X, and listen. It remembers where you were, saves its own battery, and shuts down safely when the charge runs out.

One honest caveat: battery percentage is estimated from voltage, so treat it as a rough guide rather than a precise reading. If you build one, or add support for another UPS board, I’d love to hear about it.