The Audiobox is an appliance‑style audio player for a limited-access user such as a child, an elderly person, or a visually impaired user:
- No screen
- No keyboard
- No Wi‑Fi required
- One primary Play/Pause button
- One Next button, one Prev button
- USB stick–based content management
This document is a complete, end‑to‑end software playbook for recreating the Audiobox system on a fresh Raspberry Pi.
It covers design intent, architecture, and exact implementation steps, including GPIO button wiring.
- Predictable behavior
- No auto‑play surprises
- All maintenance possible via USB stick
- Works offline
- Uses simple, inspectable tools (shell scripts, systemd)
- mplayer running in slave + idle mode
- FIFO pipe for playback control
- Shell scripts representing button actions
- USB manager handling content updates and reboot
- GPIO button handler that runs the shell scripts on button presses
- systemd services to glue everything together
The Pi behaves like an appliance, not a computer.
SETUP REQUIRED: Replace "USER" with your actual Linux username in all configuration files before installation.
/home/USER/
├── audiobooks/ # Audio files live here
├── start_audiobox_player.sh # Starts mplayer idle on boot
├── audiobox_ctl.sh # Writes commands into FIFO
├── load_playlist.sh # Loads and starts playlist
├── btn_playpause.sh # Smart Play/Pause logic
├── btn_next.sh # Next track/book
├── btn_prev.sh # Previous track/book
├── audiobox_gpio.py # GPIO listener -> runs button scripts
├── usb_manager.py # USB Add/Delete/Reboot logic
├── usb-import.log # USB operation log
└── .audiobox_state # PLAYING / PAUSED
FIFO:
/tmp/audiobox_fifo
USB mount point:
/media/usb
- Mature
- Supports many formats (MP3, WAV, M4A, AMR, OGG, etc.)
- Works well headless
- Slave mode allows external control
mplayer runs with:
-slave-idle- FIFO input
This means:
- It never exits
- It never auto‑plays
- It waits for commands
On boot:
- mplayer starts idle
- No playlist loaded
- No sound
- Device waits for button press
This avoids confusion for the user.
Behavior depends on state:
| State | Button action |
|---|---|
| Boot / idle | Load playlist and start from beginning |
| Playing | Pause |
| Paused | Resume |
| Playlist finished | Load playlist and start from beginning |
mplayer does not provide request/response IPC via FIFO.
Solution:
- Inspect
/proc/<mplayer_pid>/fd - If no file under
~/audiobooksis open → player is idle - Otherwise → mid‑file (playing or paused)
This avoids fragile log parsing.
Runs mplayer continuously in background.
Responsibilities:
- Reset state file
- Create FIFO
- Start mplayer idle
- Loads all files from
~/audiobooks - Ignores dotfiles (
._*,.DS_Store) - Plays once (no looping)
USB_ROOT/
├── Add/
│ └── new_books.*
├── library.txt
└── Reboot.txt
Written by the Pi.
Inventory:
book1.mp3
book2.wav
Deletion:
Caregiver adds filenames under Deletion: to remove them.
### Remove the "–" before Reboot to reboot the soundbox. ###
– Reboot
If dash is removed from second line:
- Pi speaks “Rebooting now.”
- Restores file to default
- Runs
sync - Reboots safely
Responsibilities (in order):
- Handle reboot trigger
- Copy files from
Add/ - Delete files listed in
library.txt - Rewrite
library.txt - Ignore dotfiles
Triggered automatically via systemd when /media/usb appears.
All USB operations logged to:
/home/USER/usb-import.log
EG STARTS arcade buttons typically use a microswitch with three terminals:
- COM (common)
- NO (normally open)
- NC (normally closed)
For Audiobox, use COM + NO only (ignore NC).
Use the Pi’s internal pull-up resistors.
For each button:
- COM → GND
- NO → GPIO pin
Do not connect to 3.3V.
- Play/Pause → GPIO17
- Next → GPIO27
- Prev → GPIO22
- Ground → any Pi GND pin (e.g., physical pin 6)
Install dependency:
sudo apt update
sudo apt install -y python3-gpiozeroCreate /home/USER/audiobox_gpio.py:
#!/usr/bin/env python3
from gpiozero import Button
from signal import pause
import subprocess
import time
# BCM pin numbers
PIN_PLAYPAUSE = 17
PIN_NEXT = 27
PIN_PREV = 22
SCRIPT_PLAYPAUSE = "/home/USER/btn_playpause.sh"
SCRIPT_NEXT = "/home/USER/btn_next.sh"
SCRIPT_PREV = "/home/USER/btn_prev.sh"
BOUNCE = 0.10
MIN_INTERVAL = 0.20
_last = {"pp": 0.0, "n": 0.0, "p": 0.0}
def run_script(path: str) -> None:
subprocess.Popen([path])
def guarded(key: str, fn) -> None:
now = time.monotonic()
if now - _last[key] >= MIN_INTERVAL:
_last[key] = now
fn()
playpause = Button(PIN_PLAYPAUSE, pull_up=True, bounce_time=BOUNCE)
next_btn = Button(PIN_NEXT, pull_up=True, bounce_time=BOUNCE)
prev_btn = Button(PIN_PREV, pull_up=True, bounce_time=BOUNCE)
playpause.when_pressed = lambda: guarded("pp", lambda: run_script(SCRIPT_PLAYPAUSE))
next_btn.when_pressed = lambda: guarded("n", lambda: run_script(SCRIPT_NEXT))
prev_btn.when_pressed = lambda: guarded("p", lambda: run_script(SCRIPT_PREV))
pause()Make executable:
chmod +x /home/USER/audiobox_gpio.pyTest interactively (Ctrl+C to stop):
python3 /home/USER/audiobox_gpio.pyCreate /etc/systemd/system/audiobox-gpio.service:
[Unit]
Description=Audiobox GPIO button handler
After=multi-user.target audiobox-player.service
Wants=audiobox-player.service
[Service]
Type=simple
User=USER
WorkingDirectory=/home/USER
ExecStart=/usr/bin/python3 /home/USER/audiobox_gpio.py
Restart=always
RestartSec=1
[Install]
WantedBy=multi-user.targetEnable/start:
sudo systemctl daemon-reload
sudo systemctl enable audiobox-gpio.service
sudo systemctl restart audiobox-gpio.service
sudo systemctl status audiobox-gpio.serviceIf you see errors like:
Assignment outside of sectionMissing '='
Then the unit file contains stray characters or broken lines. Fix by replacing the file entirely with the exact unit content above, then run:
sudo systemctl daemon-reload
sudo systemctl restart audiobox-gpio.serviceTo inspect with line numbers:
nl -ba /etc/systemd/system/audiobox-gpio.serviceAll formats supported by mplayer, including:
- MP3
- WAV
- M4A
- OGG
- AMR
- Appliance behavior
- Simplicity over cleverness
- Files over GUIs
- Inspectable state
A durable audiobook player that:
- A blind person can use
- A non‑technical caregiver can maintain
- Recovers from errors
- Requires no ongoing supervision