|
This Sachsenwerk Olympia 59-3W from 1959 learned to play web radio and mp3s |
|
No drilling required - Minimally invasive connection of the Raspberry to the tube radio |
This post describes how to setup your Raspberry Pi to work as a web radio and an mp3 player based on mpd and Arch Linux. This description is a part of the TubeNetRadio project described in more detail under
www.doc-diy.net/electronics/tubenetradio. In this project the Raspberry Pi is embedded in an old tube radio to add an internet radio and mp3 player functionality.
The user interface consists of just to buttons mounted in the back plate to avoid destroying the original look of the old radio. One button allows to step through radio stations stored in a file. The other one allows you to skip album-wise though your mp3 collection stored locally or remotely. In the following, the essential steps of the software setup are described. The hardware side is described under
www.doc-diy.net/electronics/tubenetradio.
Main installation steps
- Installation of Arch and Python
- Installation of mpd/mpc
- The buttons and the player function
- The start script
The idea behind Arch is to have a thin and fast booting operating system. The price to pay is that you need more steps for the setup than for example with Raspbian. Some packages are just not available off-the-shelf. Since one of the targets was to beat down the boot time close to the tube heat up time of the radio, Arch Linux was the perfect choice.
Copy a fresh Arch image on the SD card. At the time of writing this was
ArchLinuxARM-2014.01-rpi. Configure and update your system. Arch lacks pretty much everything at the beginning. Follow one of the tutorials on the net.
Install base-devel for gcc and others. You will need it to compile the python-pip tool which in turn installs the RPi.GPIO.
pacman -Sy base-devel
Install python2. Because python 3 and 2 are not compatible, the packages
are split. The old python 2 is marked with a 2, the new version has no marking
pacman -S python2 python2-pip
Install mpd along with the Python interface using
pacman -S mpd mpc
pacman -S python2-mpd
Important: In Jan 2014 python-mpd doesn't exist! Only the python2 version exists. This is the reason why you have to install the python2 stuff and not the new one. This will probably change soon.
To read out the buttons needed for the user interface you have to install RPi.GPIO by typing
python2-pip RPi.GPIO
RPi.GPIO is a comfortable Python interface to the GPIO connector on the Raspi board.
Starting mpd
Compared to Ubuntu, things turned out to be more complicated. I took me quite a time to sort out all the groups and user
settings, including adding group read permissions for /home/pi. In case of trouble, I recommend to set up mpd on a linux
PC and try to copy the settings of a working system.
Have a look at this guide:
https://wiki.archlinux.org/index.php/Music_Player_Daemon#Starting_mpd_as_a_user
https://wiki.archlinux.de/title/Music_Player_Daemon
Worth reading is also this:
http://crunchbang.org/forums/viewtopic.php?pid=182574
Use the following listing of the home folder as reference. The user and the group of each file are specified in brackets (generated with tree -uga). Although
alternative and better settings probably exist, the following proved to work for me.
[pi@alarmpi ~]$ tree -u -g -a
.
├── [pi users ] .bash_history
├── [pi users ] .bash_logout
├── [pi users ] .bash_profile
├── [pi users ] .bashrc
├── [mpd audio ] .mpd
│ ├── [mpd audio ] database
│ ├── [mpd mpd ] log
│ ├── [mpd mpd ] pid
│ ├── [mpd mpd ] state
│ └── [mpd audio ] sticker.sql
├── [root root ] lastradiopos
├── [root root ] lastsongpos
├── [pi audio ] music
│ ├── [pi users ] Dive Deep
│ │ ├── [pi users ] 01 - Morcheeba - Enjoy the Ride (ft Judy Tzuke).mp3
│ │ ├── [pi users ] 02 - Morcheeba - Riverbed (ft Thomas Dybdahl).mp3
│ │ ├── [pi users ] 03 - Morcheeba - Thumbnails.mp3
│ │ ├── [pi users ] 04 - Morcheeba - Run Honey Run (ft Bradley).mp3
│ ├── [pi users ] Love Deluxe
│ │ ├── [pi users ] 01 - Sade - No Ordinary Love.mp3
│ │ ├── [pi users ] 02 - Sade - Feel No Pain.mp3
│ │ ├── [pi users ] 03 - Sade - I Couldn't Love You More.mp3
│ ├── [pi users ] Smolik
│ │ ├── [pi users ] 01 - Smolik - SOS Songs feat. Gaba Kulka.mp3
│ │ ├── [pi users ] 02 - Smolik - Not Always Happy feat. Joao T. de Sousa.mp3
│ │ ├── [pi users ] 03 - Smolik - Memotion feat. Mika Urbaniak.mp3
│ └── [pi audio ] playlists
│ └── [pi users ] myradiostations.m3u
├── [pi users ] tubeNetRadio.py
└── [pi users ] tubeNetRadio.sh
After everything is set up correctly you should be able to launch mpd and manually
control it with the frontend called mpc. Try mpc update, mpc ls, mpc add "", mpc play...
For mpd to start always at boot, you have to type the following
systemctl enable mpd
and type
systemctl start mpd
to start it immediately. Some info about starting services on boot in Arch Linux can be found here:
https://wiki.archlinux.org/index.php/Systemd#Using_units
GPIO and the button routine
|
Raspberry Pi GPIO wiring plan for the buttons |
We have now mpd running and need some routine on top to poll the buttons and control the music according to the users needs. This is done using a single Python script which uses RPi.GPIO to understand the buttons and mpd-python to play the music. Using these two extensions we have everything on Python level.
Let me explain how the user interface works. There are just two buttons. One skips the radio stations which are stored in the file myradiostations.m3u in the music folder. The other button skips local mp3 music album-wise (not song-by-song) for faster navigation. All the functionality is contained in the following routine.
One issue that stole lots of time was the fact that the connection to the mpd server drops automatically and needs to reestablished or checked each time you want to perform an mpd operation. Skipping whole albums instead of songs adds some complexity to the script. It works basically by looking for the next song with a different album tag than the one currently played. For proper operation your songs have to be tagged.
The buttons have to be connected to GPIO02 and GPIO03 of the GPIO port. Resistors are not necessary since these pins have internal pull-ups.
#!/usr/bin/python2
# pyhton code for the tubeNetRadio project. tubeNetRadio is a Raspberry Pi
# based internet radio / mp3 player with a minimalistic user interface
# consisting of just two knobs (no display)
#
# Function:
#
# Button 1: skip to next radio station
# Button 2: skip to next album of available mp3 archive
#
# www.doc-diy.net
#
#
import mpd
import RPi.GPIO as GPIO
import os
import time
###############################################################################
# constants for readable code
RADIO = 0
ALBUMS = 1
PRESSED = 0
# name of files where the recently played track/radio is stored
lastsongfile = "/home/pi/lastsongpos"
lastradiofile = "/home/pi/lastradiopos"
# file with internet radio station playlist
myplaylist = "myradiostations" # refers to "myradiostations.m3u" in playlist folder
###############################################################################
# setup GPIO
# pinout in chip nomenclature (BCM)
buttonPin0 = 2
buttonPin1 = 3
GPIO.setmode(GPIO.BCM)
GPIO.setup(buttonPin0, GPIO.IN)
GPIO.setup(buttonPin1, GPIO.IN)
##############################################################################
# connect to mpd server
client = mpd.MPDClient() # create client object (this is how mpd works with
# python)
# Reconnect until successful
while 1:
try:
status = client.status()
#print("Initial connect")
break
except:
client.connect("localhost", 6600)
#print("Initial connect failed ...")
time.sleep(1)
###############################################################################
# set initial playmode. this setting decides if the player starts as mp3 player
# or internet radio
playmode = RADIO
# initialize mpd
client.clear() # clear playlist
client.update() # update library
client.load(myplaylist) # load playlist with radio stations
client.play() # play music
client.repeat(1) # repeat playlist
###############################################################################
# generate 'recently played' files if missing (at first start for example)
if not os.path.exists(lastsongfile):
with open(lastsongfile, 'w') as f:
f.write(str(1))
if not os.path.exists(lastradiofile):
with open(lastradiofile, 'w') as f:
f.write(str(1))
###############################################################################
# infinite button polling loop
while True:
input0 = GPIO.input(buttonPin0)
input1 = GPIO.input(buttonPin1)
# because mpd drops the connection automatically it has to be
# checked or restablished before any operation. otherwise the
# scripts stops
while 1:
try:
status = client.status()
break
except:
client.connect("localhost", 6600)
print("Reconnect ...")
###########################################################################
if input0 == PRESSED: # go to album mode or skip album if already in album mode
if playmode == RADIO:
playmode = ALBUMS
client.clear()
tracks=client.list('file') # get all files from data base
# (doesn't load playlists with radio
# stations)
for t in tracks:
client.add(t)
# play song lastly played
with open(lastsongfile, 'r') as f:
songpos = int(f.read())
client.play(songpos)
else:
# get current song id
songposcur = int(client.currentsong()['pos']) # get playlist
# position of current song
# create list of albums, replace empty entries with dummy
plsinfo = client.playlistinfo() # get all track information for
# playlist
plsalbums = []
for alb in plsinfo:
tmp = alb.get('album','-no-') # get albums and replace empty
# entries by -no-
plsalbums.append(tmp)
# get album of current song
songalbum = plsalbums[songposcur]
# go through album list and search for the next song with a
# differing album name
for x in range(songposcur, len(plsalbums)):
songpos = 0 # go to start of playlist, valid only if if
# statement below fails
if plsalbums[x] != songalbum:
songpos = x
break
client.play(songpos) # play first song of next album
# save current song id for next restart
songpos = int(client.currentsong()['pos'])
print(str(songpos))
with open(lastradiofile, 'w') as f:
f.write(str(songpos))
f.truncate() # cuts all previous contents like digits
time.sleep(0.1) # results in 10 Hz polling of button
Make the button Python script start automatically
We want the python script to start automatically after boot. Similarly to
mpd
we need to place the corresponding .service file in
/etc/systemd/system. Making this script run properly turned out to be a nightmare, probably due to the trial and error approach I followed. The final outcome that proved to work is here:
[Unit]
Description=autostart tubeNetRadio mpd script
After=default.target
[Service]
Type=oneshot
ExecStart=/home/pi/tubeNetRadio.sh
[Install]
WantedBy=multi-user.target
The first part specifies on what modules this service depends. The middle part says what command should be executed. The last part tells Arch what other modules need our service to start. Using some more elaborated settings smart things can be done. In makes sense to run this service after
mpd and
wlan started. Any improvements or comments are welcome.
The service has to be launched using
systemctl enable tubeNetRadio
systemctl start tubeNetRadio
Here is a listing of
/etc/systemd/system after the launch for reference
[pi@alarmpi ~]$ tree /etc/systemd/system
/etc/systemd/system
├── default.target.wants
│ └── tubeNetRadio.service -> /etc/systemd/system/tubeNetRadio.service
├── getty.target.wants
│ └── getty@tty1.service -> /usr/lib/systemd/system/getty@.service
├── multi-user.target.wants
│ ├── avahi-dnsconfd.service -> /usr/lib/systemd/system/avahi-dnsconfd.service
│ ├── cronie.service -> /usr/lib/systemd/system/cronie.service
│ ├── haveged.service -> /usr/lib/systemd/system/haveged.service
│ ├── mpd.service -> /usr/lib/systemd/system/mpd.service
│ ├── netctl-ifplugd@eth0.service -> /usr/lib/systemd/system/netctl-ifplugd@.service
│ ├── netctl@wlan0\x2dlothar\x2dbucher.service -> /etc/systemd/system/netctl@wlan0\x2dlothar\x2dbucher.service
│ ├── remote-fs.target -> /usr/lib/systemd/system/remote-fs.target
│ ├── rngd.service -> /usr/lib/systemd/system/rngd.service
│ └── sshd.service -> /usr/lib/systemd/system/sshd.service
├── netctl@wlan0\x2dlothar\x2dbucher.service
├── sockets.target.wants
│ └── avahi-daemon.socket -> /usr/lib/systemd/system/avahi-daemon.socket
└── tubeNetRadio.service
You might have noticed, the tubeNetRadio.service launches an intermediate shell script called tubeNetRadio.sh:
#!/bin/bash
sleep 1 && /usr/bin/python2 /home/pi/tubeNetRadio.py
This script just launches the actual
tubeNetRadio.py script after the delay of 1 second. The delay has been added because the service didn't start reliably, meaning that after some boots the buttons didn't react and I couldn't switch the radio stations or albums. The delay seems to help, but is a dirty workaround. I guess setting up the
tubeNetRadio.service with some more care will solve the problem and make the intermediate script and the delay unnecessary.
Some facts
- mpd is very robust and always plays some music, even if the button routine fails.
- The time from power-on to music is about 30 s for Internet radio
- Tube heat-up time is about 15 s
- The "analog" two button interface proved to work much better then any smart phone remote control dropping the connection again and again.
- Radio can be controlled by non-freaks;)
Download
All source files and some extra info can be downloaded from
https://github.com/doc-diy/tubeNetRadio or checked out directly with git:
git clone https://github.com/doc-diy/tubeNetRadio.git
Details of the electrical connections and the way the Raspi is mounted can be found under
www.doc-diy.net/electronics/tubenetradio
Mounting an additional partition
You might have noticed that the Arch image expands to about 2 GB with 1.6 GB already occupied. This leaves just 400 MB for your mp3 collection. To add some volume you can expand the partition to use the whole SD card or create a new partition filling the remaining space. I went for the second option because of a lower risk of breaking the Linux. A second partition can be also easier accessed when the SD card is plugged into a computer, especially when FAT32 has been chosen. I can recommend the programm gparted for all formatting operations if you are under Linux.
Adding a second partition requires the fstab to be modified, see the last line below. The second partition was called
/dev/mmcblk0p6 on my Arch Raspberry Pi.
#
# /etc/fstab: static file system information
#
# <file system> <dir> <type> <options> <dump> <pass>
/dev/mmcblk0p1 /boot vfat defaults 0 0
/dev/mmcblk0p6 /media/sdcard2gb/ vfat user,rw,umask=111,dmask=000 0 0
Setting the right options requires some experience. I recommend to have a look at this article:
https://wiki.archlinux.org/index.php/fstab
I created the folder
/media/sdcard2gb/ as the mount point, but you are free to choose. On the new partition the music resides in the folder
music. Finally I deleted the original
~/music folder in home and made a softlink with the same name to the new partition with the music. The link command could be
ln -s /media/sdcard2gb/music ~/music
Remember,
mpd was configured to look into the folder called
~/music for playable media.
Feel free to give me any feed back. I would be particularly interested in improving the launching script. Happy implementing!!!
Luk