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 pi@raspberrypi.local 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/