A while back my parents said: we’ve got a telly in the dining room now. Could we play music through it so we can put songs on while we’re doing things around the house?
Sure, I said.
Dad Music TV
So, we have a set of constraints. We need to find or build a thing which can play music through a telly, and which they can put new music on without my help. I’m sure there are devices out there which can do this, but I thought it might be fun to build something. And I set the biggest constraint of all on the device, which is this: it is not connected to the network. Wifi is annoying to configure, and on the Raspberry Pi it basically requires a powered hub.1 No ethernet network connection while it’s running. Non-negotiable requirement.
Therefore, there are two complexities here: how do you get music onto this thing? And how do you control it?
My thought was: we’ll base this on a Raspberry Pi, because they’re nice and standard and you can buy nice looking cases for them. That way, it needs only two wires: power, and HDMI into the telly, and that’s it. How you put music on it is: you unplug it, put the SD card into a computer, copy music onto the SD card, then put the card back in the Pi and turn it on. Ta daaah; easy.2 This requires something on the Pi which knows how to play music, and knows how to look at a folder of mp3s and index them. Sounds like a job for mpd!
And indeed that works. We create a folder named
MUSIC at the root of the Pi’s SD card3, and stick a file named
.hidden in the root to ensure that only the
MUSIC folder shows up. Now, when my dad puts the SD card into his machine, it’ll pop up a file browser window which only shows the
MUSIC folder. Winner. Configure
mpd on the Pi to treat
/MUSIC as its music root directory, and that all works; whenever the Pi starts up,
mpd reindexes all the music in the folder, and you can’t add music without taking out the SD card which means that you have to restart the Pi. Now we can put music on the device and there’s a daemon which knows how to play it. Step 1: done.
It should really show something on the screen while it’s playing, though, shouldn’t it? So, a trivial little Python script which (a) uses SDL to (b) display things on the framebuffer while (c) being an mpd client, so when mpd does anything, the screen display changes. This is pretty easy stuff, thanks to an Adafruit writeup about using the framebuffer on the Pi.
Now, how do we control it? It has no network. There is a thing named HDMI-CEC which allows a TV to pass commands down an HDMI cable to a connected device. This would be the obvious way to do it, and the Pi supports it; just have the TV’s remote control buttons for left/right/up/down/whatever get sent to the Pi, which then reacts to them appropriately.
Sadly, their telly doesn’t do CEC. So that’s that idea kiboshed. Shame; this would have been a good way to do it.
So, next step. Bluetooth it is! For that, we need a bluetooth USB dongle for the Pi. I have two, but one of them I couldn’t get to work. The one that does work names itself in lsusb as
0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode). So now what we need is some way to control
mpd on the Pi over Bluetooth. Well… Bluetooth supports what it calls “RFCOMM” connections, which basically are a socket, like telnet, but which goes over Bluetooth. So what if we make a little “proxy” server which listens on a Bluetooth socket and connects to
mpd‘s socket API? So when someone connects over Bluetooth to it, they can act just like they’re connected to
mpd normally because they send an
mpd command to the Bluetooth socket, the little proxy sends it on to
mpd responds, and the proxy sends the output back? That way, all you need is an
mpd client where you can switch out its “use a socket to connect” code with “use a Bluetooth socket to connect” code and Robert’s your mother’s brother. And indeed, that works; another relatively trivial bit of Python code to just open the two sockets and shuffle data between them.
Now we need an mpd client which can connect over Bluetooth. Both my parents have Android phones, so that’s the obvious place to have the client. I don’t like Java, so taking an existing open source Android mpd client and altering it is sadly not on the cards. One way to do this would be something like Cordova/PhoneGap, but nobody seems to have written a Cordova mpd app which makes socket connections itself, sadly.4 So, time to write a relatively noddy mpd client for Android. The ideal here is Kivy; it’s Python, it does all the GUI stuff, and they have both good documentation and the “buildozer” utility for packaging a Kivy app for Android. This also has the advantage that, because it’s in Python and I understand it, I can write the client with a switchable socket back-end, so on Android it uses Bluetooth, and on my desktop it uses an ordinary socket to connect over the network, so I can test the client in a nice convenient environment. I can also have the client connect to
mpd running on my actual computer, so I can use other mpd clients to confirm that it does what it’s trying to do, that the screen display thing changes when it’s supposed to, and so on: this makes testing really, really easy because I don’t have to push any of it to the Pi or the Android phone while I’m working on it or to test it out. Having a test environment like this makes life much nicer.
And, indeed, all of this works! It’s not quite finished yet, but all of this stuff hangs together. I’m quietly pleased with the amount of different stuff that had to come together for this project; Raspbian on a Raspberry Pi, mpd and its pretty-good API, creating Bluetooth server sockets with PyBluez on the Pi, proxying between two sockets, rendering a screen with SDL to the framebuffer, supervisord to ensure that all the server-side stuff gets run and keeps running and reports errors, building apps with Kivy, Android packaging for Kivy, using PyJnius from Python to use the underlying Android Java API, Bluetooth clients… I knew about all of this stuff at a high level, but there’s something satisfying about pulling it all together into a working product.
Code is at github.com/stuartlangridge/dadmusictv for both the Pi server and the Android client if you’re very keen to look at it. I’ve written down instructions, but they’re really for me as reference if I’m honest; setting this up yourself will require some jiggery-pokery, I don’t doubt. Nonetheless, this was a fun project to do. Easter hacking time 2015: 10 marks out of 10.
- you will say, no it doesn’t. and I will not believe you. and I’ve tried. A wifi dongle plugged into a powered hub which is then plugged into the Pi works always. A dongle plugged directly into the Pi works sometimes. At best. No network. ↩
- actually, that’s probably easier than faffing about copying it over the network, to be honest; no passwords, no hassle, no dodgy wifi ↩
- it’s running Raspbian, obviously; there is now a build of proper Ubuntu (not snappy) for the Pi 2, but Raspbian is fine here and at this level Ubuntu and Debian are pretty much identical ↩
- there are a million billion mpd clients where the “client” running on the web is actually talking to a server which then in turn connects to mpd, but we can’t do that here because there’s nowhere to run the server, and making a webview use a bluetooth socket rather than a tcp socket sounds really low-level and difficult ↩