2022-04-01 Raspberrypi Webradio Player
Table of Contents
_________________
1. Raspberrypi webradio player
. 1. Concept
.... 1. Preparing OS
.... 2. on the host
. 2. on the pi
. 3. Configuring MPD
. 4. Autostart
.... 1. via systemd
.... 2. via ~/.profile
. 5. Creating a playlist
. 6. Programming
.... 1. webradio.py
. 7. Assembly
1 Raspberrypi webradio player
=============================
1.1 Concept
~~~~~~~~~~~
- raspberrypi music streamer as /minimum viable product/
- hardware and software should be as basic as possible
- stream only webradio urls (not streaming services like spotify)
- headless pi
- audio player: mpd + mpc[1]
- feedback via text to speech
- sound output via headphone jack (sorry audiophiles)
- one and only one button
- single press -> next channel
- long press (when powered on) -> power off
- single press (when halted) -> power on
once configured it's supposed to run on it's own
-> no bloat == less error prone
1.2 Preparing OS
~~~~~~~~~~~~~~~~
burn Raspberry Pi OS Lite[2] to a sd card
1.2.1 on the host
~~~~~~~~~~~~~~~~~
(host)$ touch /[path to sd card]/boot/ssh
put sd card in the pi, connect it via ethernet to your router,
power it on (theres is also a way to preconfigure the sd card with
your wifi credentials)
(host)$ ssh
[email protected]
1.2.2 on the pi
~~~~~~~~~~~~~~~
$ sudo raspi-config
- Localisation Options / Keyboard / ...
- Localisation Options / TimeZone / ...
- System Options / Password
- System Options / Wireless LAN
- Interface / SSH
- Boot / Auto Login / Console Autologin
$ sudo apt update && sudo apt dist-upgrade
$ sudo install mpd mpc pulseuadio python3-gpiozero espeak
We absolutely do *NOT* want to start mpd as a system-wide service
-> configuration and permission hell
$ sudo systemctl disable mpd.service
$ sudo reboot
1.3 Configuring MPD
~~~~~~~~~~~~~~~~~~~
$ mkdir ~/music
$ mkdir -p ~/.config/mpd/playlists && cd ~/.config/mpd/
$ touch mpd.conf mpd.db mpd.fifo mpd.log mpd.pid mpdstate
$ cat << EOF > ~/.config/mpd/mpd.conf
music_directory "/home/pi/music"
playlist_directory "/home/pi/.config/mpd/playlists"
db_file "/home/pi/.config/mpd/mpd.db"
log_file "/home/pi/.config/mpd/mpd.log"
pid_file "/home/pi/.config/mpd/mpd.pid"
state_file "/home/pi/.config/mpd/mpdstate"
audio_output {
type "pulse"
name "MPD"
}
EOF
1.4 Autostart
~~~~~~~~~~~~~
1.4.1 via systemd
-----------------
$ cat << EOF > ~/.config/systemd/user/webradio.service
[Unit]
Description=Webradio
After=network-online.target mpd.service
[Service]
Type=simple
ExecStart=/home/pi/webradio.py
Restart=on-failure
[Install]
WantedBy=default.target
EOF
$ systemctl enable mpd.service --user
$ systemctl enable webradio.service --user
1.4.2 via ~/.profile
--------------------
if it doesn't work with systemd
echo "[ ! -s ~/.config/mpd/mpd.pid ] && mpd && /home/pi/webradio.py &" >> ~/.profile
then try it with `systemctl start mpd.service --user' instead of `mpd'
1.5 Creating a playlist
~~~~~~~~~~~~~~~~~~~~~~~
If mpd is up and running you can add urls like so
$ mpc add
https://url1
$ mpc add
https://url2
$ mpc add
https://url3
$ mpc save webradio
or put them here
$ nano ~/.config/mpd/playlists/webradio.m3u
1.6 Programming
~~~~~~~~~~~~~~~
$ touch ~/webradio.py
$ chmod +x ~/webradio.py
1.6.1 webradio.py
-----------------
#!/usr/bin/env python3
import re, subprocess
from gpiozero import Button
from signal import pause
def speak(vol, message):
subprocess.run(["mpc", "vol", str(vol-50)])
subprocess.run(["espeak", "-ven+f3", "-s170", message])
subprocess.run(["mpc", "vol", str(vol)])
def get_song_info():
mpc_status = str(subprocess.run(
['mpc', 'status'],
stdout=subprocess.PIPE).stdout)
matched = re.search(r"(volume:)(\s*\d+)", mpc_status)
vol = matched.groups()[1]
matched = re.search(r"(\[playing\] #)(\d+)\/(\d+)", mpc_status)
num = matched.groups()[1]
lim = matched.groups()[2]
return int(num), int(vol), int(lim)
def init_player():
subprocess.run(["mpc", "clear"])
subprocess.run(["mpc", "load", "webradio"])
subprocess.run(["mpc", "repeat", "on"])
subprocess.run(["mpc", "single", "on"])
subprocess.run(["mpc", "random", "off"])
subprocess.run(["mpc", "play"])
def next_song():
try:
num, vol, lim = get_song_info()
num = num + 1
if num > lim: num = 1
speak(vol, "playing channel {0}".format(num))
subprocess.run(["mpc", "next"])
except Exception as e:
print(e)
# fails if mpd is not playing
subprocess.run(['mpc', 'play'])
def poweroff():
try:
num, vol, lim = get_song_info()
speak(vol, "shutting down")
subprocess.run(['sudo', 'poweroff'])
except Exception as e:
print(e)
# shutdown without tts feedback
subprocess.run(['sudo', 'poweroff'])
def btn_released():
# print("button released")
if not btn.was_held:
btn_pressed()
btn.was_held = False
def btn_held():
# print("button held")
btn.was_held = True
poweroff()
def btn_pressed():
# print("button pressed")
next_song()
if __name__ == "__main__":
Button.was_held = False
btn = Button(3, pull_up=True, hold_time = 2)
btn.when_held = btn_held
btn.when_released = btn_released
init_player()
pause()
1.7 Assembly
~~~~~~~~~~~~
- connect a button with jumper cables to GPIO3 and GND
(pins 5 and 6, see <
https://pinout.xyz/> for reference)
- connect speakers to headphone jack
- build an absolutely ridiculous case for the pi :^)
Footnotes
_________
[1]
https://musicpd.org/
[2]
https://www.raspberrypi.com/software/operating-systems/