I recently worked with the PiFace Control and Display for my Santa Catcher, in which I used the PiFace CAD as an IR receiver and display without using any of the local controls.
So I decided to create an Internet Radio player which could be controlled using the PiFace CAD's buttons.
The result is mainly a software project, but I had fun diving a little deeper into the Python programming language.
For this project, I used following components:
- Raspberry Pi Model B running Raspbian
- PiFace Control and Display
- Enclosure for Pi and PiFace CAD
- Portable speaker
- 5V 1A micro USB power supply
As mentioned earlier, I started off by using Raspbian with wifi and ssh enabled.
These were the modifications on top of Raspbian to get everything I needed up and running:
- Updated Raspbian to latest version
sudo apt-get upgrade sudo apt-get update
- Enabled SPI support for the PiFace CAD
- Select option 8: "Advanced options"
- Select option A5: "SPI"
- Select "Yes"
- Select "Finish" to exit
- Installed the mpd (music player daemon) and mpc (music player controller) applications
sudo apt-get install mpd mpc
From this point on, everything is done via Python.
Using Python, I am able to control the mpc application using the PiFace CAD's buttons and display the status on its LCD.
You can find the full code a bit further down, but I'll try to explain some bits of code here.
I wanted to display some icons on the LCD to clarify the meaning of some values being displayed or to visualise the current state.
It is possible to define "custom bitmaps" to be displayed. This tool comes in very handy to create your own. (be sure to select 5x8 when using PiFace CAD)
Following function defines and stores custom bitmaps:
def custom_bitmaps(): speaker = pifacecad.LCDBitmap([1,3,15,15,15,3,1,0]) play = pifacecad.LCDBitmap([0,8,12,14,12,8,0,0]) stop = pifacecad.LCDBitmap([0,31,31,31,31,31,0,0]) playlist = pifacecad.LCDBitmap([2,3,2,2,14,30,12,0]) cad.lcd.store_custom_bitmap(0, speaker) cad.lcd.store_custom_bitmap(1, play) cad.lcd.store_custom_bitmap(2, stop) cad.lcd.store_custom_bitmap(3, playlist)
The custom bitmaps can then be displayed on the LCD as follows, using the id of the corresponding bitmap:
In order to display correct information on the LCD (volume & playlist position and size), I rely on the information coming from the mpc application itself.
A sample output looks as follows:
pi@piRadio ~ $ mpc status mms://streaming.q-music.be/QBE_HI [playing] #1/2 1187:22/0:00 (0%) volume:100% repeat: off random: off single: off consume: off
Using Python, it is possible to execute that command, take the output and parse the interesting bits of information.
def display_playlist(): playlist = subprocess.check_output("mpc status | grep playing", shell=True, stderr=subprocess.STDOUT) playlist = playlist[playlist.find("#")+1:playlist.find("/")+2] cad.lcd.set_cursor(4, 1) cad.lcd.write_custom_bitmap(3) cad.lcd.write(playlist)
This bit of code executes the "mpc status" command and only keeps the part of the output containing the word "playing".
Then, it parses the output and keeps the part after "#" and up to two character after the "/". This covers playlists of up to 99 items.
The playlist information is then written to the LCD, with an icon to clarify the meaning of the value.
def display_volume(): volume = subprocess.check_output("mpc status | grep volume", shell=True, stderr=subprocess.STDOUT) volume = volume[7:volume.find("%")+1] cad.lcd.set_cursor(12, 1) cad.lcd.write(volume)
Similar to the playlist parsing, this bit of code takes the output of the "mpc status" command containing the word "volume".
It parses the characters after the word "volume" up to and including the "%" character.
The volume is then written to the LCD.
The script needs to know when a button on the PiFace CAD has been pressed and trigger the appropriate action.
I recuperated this bit of code from one of the PiFace examples and extended it to fit my application.
First, a listener is defined which will register which button has been pressed:
listener = pifacecad.SwitchEventListener(chip=cad) for i in range(8): listener.register(i, pifacecad.IODIR_FALLING_EDGE, update_pin_text) listener.activate()
The listener executes the specified function in which it execute different actions depending on the button that was pressed:
def update_pin_text(event): global channel_pos global status if(event.pin_num == 0): status = "playing" os.system('mpc play') event.chip.lcd.set_cursor(0, 1) event.chip.lcd.write_custom_bitmap(1) event.chip.lcd.write(" ") display_channel() display_playlist() elif(event.pin_num == 1): status = "stopped" os.system('mpc stop') event.chip.lcd.set_cursor(0, 1) event.chip.lcd.write_custom_bitmap(2) event.chip.lcd.write(" ") clear_channel() elif(event.pin_num == 2 and status == "playing" and channel_pos > 1): os.system('mpc prev') clear_channel() display_channel() display_playlist() elif(event.pin_num == 3 and status == "playing" and channel_pos < len(channelLink)): os.system('mpc next') clear_channel() display_channel() display_playlist() elif(event.pin_num == 4): global backlight if(backlight == 1): event.chip.lcd.backlight_off() backlight = 0 else: event.chip.lcd.backlight_on() backlight = 1 elif(event.pin_num == 5): sleep(1) elif(event.pin_num == 6): os.system('mpc volume -5') display_volume() elif(event.pin_num == 7): os.system('mpc volume \+5') display_volume() else: sleep(1)
Above actions correspond to following button mapping:
The code still requires to be cleaned up a little, but it should be clear what I'm trying to do.
The script needs to be started automatically after the system booted. This is done by adding following entry to /etc/rc.local:
sudo python /home/pi/radio.py &