I just got a HyperPIxel Raspberry Pi touch-screen LCD display from Pimoroni. This neat display is the same size as the Raspberry Pi itself, and covers all its GPIO pins but it is a touchscreen and it does shift 800×480 pixel video at 60 frames a second. I have plans to do something with a camera and this screen, but as a first project I fell back to my standard: can I make a radio from it?
Being a touch screen, some kind of GUI would be a good idea, and the project I hacked together gave me an excuse to play with Tkinter, the default, but far from ideal, GUI toolkit that Python has installed by default.
I had problems getting the HyperPixel to work at first – I had to do a clean install of Raspbian Jesse, presumably because some other project was interfering with some of the GPIO pins.
First I installed mpc and mpd which I use on the Raspberry Pi to play radio streams or mp3 files:
sudo apt-get install mpc mpd
If you’ve not used mpc before, open the mpd config file with nano like this:
sudo nano /etc/mpd.conf
Then edit these lines to remove some # signs (uncomment) and change the mixer_type from hardware to software so it looks like this:
audio_output {
type "alsa"
name "My ALSA Device"
device "hw:0,0" # optional
mixer_type "software" # optional
mixer_device "default" # optional
mixer_control "PCM" # optional
mixer_index "0" # optional
}
Now add some radio station URLs using the mpc add
command. I added BBC 6music, BBC World Service News, FIP, Radio 4 and others. For example to add FIP I typed
mpc add http://icecast.radiofrance.fr/fip-midfi.mp3
You can find BBC radio URLs on my big list here.
I added 7 stations but you can add more or fewer. They get added in order but you can check your playlist of stations or MP3 files by typing
mpc playlist
And you can change the order using the move command, for example to move station 4 to preset 2 just type:
mpc move 4 2
I then made some 100×100 pixel GIF images (see end of post) for the station logos and wrote some Python/Tkinter code to display the buttons to play each station, a stop/pause button and one to exit the app as it runs full screen. The volume buttons don’t work on my installation for some reason I haven’t fathomed yet.
You could add loads of extras to this like weather and news headlines – or ‘now playing’ information using the frankly mad workaround I used on my web-interface radio back in 2014 (it’s harder than you’d think!)
I did add a digital clock, which accounts for the cut-and-shunt abomination of Python code you see below. If someone can make it more elegant, I’d be highly delighted. It does, however, work.
Update – running automatically at boot
If you want to make this run automatically at boot, you need to do a couple of things. You need to change all the references to the .gif files to have absolute paths to the folder where you put the image files. Then edit an autostart file with:
sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart
to add a line something like this:
@python3 /home/pi/Desktop/HyperPixelRadio/radio-gui.py
(you will need to change the path to where you saved the main Python program).
**Update June 2020**
In recreating this on a fresh Raspbian Buster install and a new, bigger HyperPixel display and I’ve had a few… issues.
I could not get MPC to play out of the analogue audio jack at all – until I unplugged the HDMI display. Even when VLC and everything else was playing via analogue out, MPC persisted with playing out of HDMI. No idea why.
Also, to get it to run at startup I needed to add
@python3 /home/pi/Desktop/HyperPixelRadio/radio-gui.py
to a different file with this command:
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart
I also need to work out how to rotate the screen…
New version of the program with IP address and shutdown buttons
#!/usr/bin/env python3 from tkinter import Tk, Label, Button, PhotoImage import os, time, subprocess time1 = '' IP = subprocess.check_output(["hostname", "-I"]).split()[0] class MyFirstGUI: def __init__(self, master): global fiplogo self.master = master master.title("HyperPixelRadio") fiplogo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/fip100.gif") self.label = Label(master, text="HyperPixel Radio by @blogmywiki", font=('Lato Heavy',25), fg = 'blue') self.label.grid(columnspan=7, pady=20) self.fip_button = Button(master, image=fiplogo, command=self.fip, height=100, width = 100) self.fip_button.image = fiplogo self.fip_button.grid(row=1, pady=10, padx=4) r2logo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/radio2.gif") self.r2_button = Button(master, image=r2logo, command=self.r2, height=100, width = 100) self.r2_button.image = r2logo self.r2_button.grid(row=1, column=1, padx=4) r4logo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/radio4.gif") self.r4_button = Button(master, image=r4logo, command=self.r4, height=100, width = 100) self.r4_button.image = r4logo self.r4_button.grid(row=1, column=2, padx=4) x4logo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/4extra.gif") self.x4_button = Button(master, image=x4logo, command=self.x4, height=100, width = 100) self.x4_button.image = x4logo self.x4_button.grid(row=1, column=3, padx=4) r5logo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/5live.gif") self.r5_button = Button(master, image=r5logo, command=self.r5, height=100, width = 100) self.r5_button.image = r5logo self.r5_button.grid(row=1, column=4, padx=4) r6logo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/6music.gif") self.r6music_button = Button(master, image=r6logo, command=self.r6music, height=100, width = 100) self.r6music_button.image = r6logo self.r6music_button.grid(row=1, column=5, padx=4) wslogo = PhotoImage(file="/home/pi/Desktop/HyperPixelRadio/bbcws.gif") self.ws_button = Button(master, image=wslogo, command=self.ws, height=100, width = 100) self.ws_button.image = wslogo self.ws_button.grid(row=1, column=6, padx=4) self.down_button = Button(master, text="< VOL", command=self.down, height=5, width=10) self.down_button.grid(row=3) self.up_button = Button(master, text="shut down", command=self.shutdown, height=5, width=10) self.up_button.grid(row=3, column=1) self.close_button = Button(master, text="close app", command=self.close, height=5, width=10) self.close_button.grid(row=3, column=2) self.ip_button = Button(master, text="show IP", command=self.ipaddr, height=5, width=10) self.ip_button.grid(row=3, column=3) #spare button could be for 'now playing' info self.ip_button = Button(master, text=" ", command=self.nowPlaying, height=5, width=10) self.ip_button.grid(row=3, column=4) self.stop_button = Button(master, text="pause", command=self.stop, height=5, width = 10) self.stop_button.grid(row=3, column=5) self.up_button = Button(master, text="VOL >", command=self.up, height=5, width=10) self.up_button.grid(row=3, column=6) def ipaddr(self): self.label.config(text=IP) def nowPlaying(self): self.label.config(text="now playing info here") def fip(self): print("fip!") self.label.config(text='fip - France Inter Paris') os.system("mpc play 1") def r4(self): print("BBC Radio 4 FM") self.label.config(text='BBC Radio 4 FM') os.system("mpc play 2") def x4(self): print("BBC Radio 4 Extra") self.label.config(text='BBC Radio 4 Extra') os.system("mpc play 7") def r6music(self): print("BBC 6music") self.label.config(text='BBC Radio 6Music') os.system("mpc play 3") def ws(self): print("BBC World Service News Stream") self.label.config(text='BBC World Service News') os.system("mpc play 4") def r2(self): print("BBC Radio 2") self.label.config(text='BBC Radio 2') os.system("mpc play 5") def r5(self): print("BBC Radio 5 Live") self.label.config(text='BBC Radio 5 Live') os.system("mpc play 6") def stop(self): print("stop MPC player") self.label.config(text='-paused-') os.system("mpc stop") def close(self): os.system("mpc stop") root.destroy() def up(self): os.system("mpc volume +30") def down(self): os.system("mpc volume -30") def shutdown(self): print("shutting down system") os.system("sudo shutdown now") root = Tk() #root.configure(background='cyan3') root.configure(cursor='none') root.attributes('-fullscreen', True) my_gui = MyFirstGUI(root) time1 = '' clock = Label(root, font=('Lato Light', 48, 'bold')) clock.grid(row=4, columnspan=7, pady=20) def tick(): global time1 # get the current local time from the PC time2 = time.strftime('%H:%M:%S') # if time string has changed, update it if time2 != time1: time1 = time2 clock.config(text=time2) # calls itself every 200 milliseconds # to update the time display as needed # could use >200 ms, but display gets jerky clock.after(200, tick) tick() root.mainloop()
Original version of the program
from tkinter import Tk, Label, Button, E, W, PhotoImage import os, time class MyFirstGUI: def __init__(self, master): global fiplogo self.master = master master.title("HyperPixelRadio") fiplogo = PhotoImage(file="fip100.gif") self.label = Label(master, text="HyperPixel Radio by @blogmywiki", font=('Roboto',25), fg = 'blue') self.label.grid(columnspan=7, pady=20) self.fip_button = Button(master, image=fiplogo, command=self.fip, height=100, width = 100) self.fip_button.image = fiplogo self.fip_button.grid(row=1, pady=10) r2logo = PhotoImage(file="radio2.gif") self.r2_button = Button(master, image=r2logo, command=self.r2, height=100, width = 100) self.r2_button.image = r2logo self.r2_button.grid(row=1, column=1) r4logo = PhotoImage(file="radio4.gif") self.r4_button = Button(master, image=r4logo, command=self.r4, height=100, width = 100) self.r4_button.image = r4logo self.r4_button.grid(row=1, column=2) x4logo = PhotoImage(file="4extra.gif") self.x4_button = Button(master, image=x4logo, command=self.x4, height=100, width = 100) self.x4_button.image = x4logo self.x4_button.grid(row=1, column=3) r5logo = PhotoImage(file="5live.gif") self.r5_button = Button(master, image=r5logo, command=self.r5, height=100, width = 100) self.r5_button.image = r5logo self.r5_button.grid(row=1, column=4) r6logo = PhotoImage(file="6music.gif") self.r6music_button = Button(master, image=r6logo, command=self.r6music, height=100, width = 100) self.r6music_button.image = r6logo self.r6music_button.grid(row=1, column=5) wslogo = PhotoImage(file="bbcws.gif") self.ws_button = Button(master, image=wslogo, command=self.ws, height=100, width = 100) self.ws_button.image = wslogo self.ws_button.grid(row=1, column=6) self.down_button = Button(master, text="< VOL", command=self.down, height=5, width=10) self.down_button.grid(row=3) self.stop_button = Button(master, text="STOP", command=self.stop, height=5, width = 10) self.stop_button.grid(row=3, column=2) self.close_button = Button(master, text="close app", command=self.close, height=5, width=10) self.close_button.grid(row=3, column=4) self.up_button = Button(master, text="VOL >", command=self.up, height=5, width=10) self.up_button.grid(row=3, column=6) def fip(self): print("fip!") self.label.config(text='fip - France Inter Paris') os.system("mpc play 1") def r4(self): print("BBC Radio 4 FM") self.label.config(text='BBC Radio 4 FM') os.system("mpc play 2") def x4(self): print("BBC Radio 4 Extra") self.label.config(text='BBC Radio 4 Extra') os.system("mpc play 7") def r6music(self): print("BBC 6music") self.label.config(text='BBC Radio 6Music') os.system("mpc play 3") def ws(self): print("BBC World Service News Stream") self.label.config(text='BBC World Service News') os.system("mpc play 4") def r2(self): print("BBC Radio 2") self.label.config(text='BBC Radio 2') os.system("mpc play 5") def r5(self): print("BBC Radio 5 Live") self.label.config(text='BBC Radio 5 Live') os.system("mpc play 6") def stop(self): print("stop MPC player") self.label.config(text='-paused-') os.system("mpc stop") def close(self): os.system("mpc stop") root.destroy() def up(self): os.system("mpc volume +30") def down(self): os.system("mpc volume -30") root = Tk() #root.geometry('720x480') #root.configure(background='cyan3') root.attributes('-fullscreen', True) my_gui = MyFirstGUI(root) time1 = '' clock = Label(root, font=('Roboto', 48, 'bold')) clock.grid(row=4, columnspan=7, pady=20) def tick(): global time1 # get the current local time from the PC time2 = time.strftime('%H:%M:%S') # if time string has changed, update it if time2 != time1: time1 = time2 clock.config(text=time2) # calls itself every 200 milliseconds # to update the time display as needed # could use >200 ms, but display gets jerky clock.after(200, tick) tick() root.mainloop()
Update, Feb 2020
** I added Scala Radio… and Fip can currently be found at http://icecast.radiofrance.fr/fip-midfi.mp3
Well done for pulling all of the bits together.
If you fancy cover art and track details when playing FIP then you might like to try another solution (although I don’t know if touch will work out of the box) … then you could install LMS, Squeezelite and JiveLite … all available is simple package via piCorePlayer.
The Radio France (for FIP etc) plugin is from me.
Paul, did you got the Hyperpixel screen working with piCorePlayer then? I couldn’t find anyway of doing this as the drivers are not compatible.
Hi, thanks for sharing! This is the 1st python program I’ve got working with my new HyperPixel 4.0 – I’m loving it. I did try to run the script from SSH terminal, that did not work, but soon enough figured it had to be in the X environment.
I hope to take from this and make my HDMI switcher box (using libcec and cec-utils), to use on AV carts and classrooms.
Nice work.