BBC Radio on the Raspberry Pi controlled with a tiny web server

Objective

I wanted to be able to tune in to BBC radio stations on my Raspberry Pi and control it through my smart phone with an easy graphical interface.

In fulfilling this objective I got a bit carried away and wrote a small web server from scratch (as you do…) which was good fun, so in the spirit of helping people hack their Raspberry Pis I thought I’d explain how it all works. What follows is mostly an explanation of how web servers and web browsers work: it’s actually quite simple and interesting to mess around with. The actual software is useful and works well too!

The screenshot below shows the final interface.

Screenshot of radio player interface

Screenshot of radio player interface

The TL:DR version

Log in to your RPi, get the code from github, unpack it, install it and go to your Raspberry Pi (port 80) in a web browser:

$ cd /tmp
$ wget https://github.com/scp93ch/raspberry-radio/archive/master.zip
$ unzip master.zip
$ cd raspberry-radio-master
$ sudo ./install.sh

However, running random downloaded scripts as root on your computer is not generally a good idea, so read on to find out what it’s all about.

The radio server also works nicely with Volumio (my current favourite DLNA renderer) as they both use mpd at the back end to control the audio. You do have to make one small adjustment though: change the init script (/etc/init.d/radio_server) so that the radio server uses a different port to Volumio (which uses port 80). So for instance, change line 17 of the init script to say “--port 8080” and then use this port in the URL in your web browser (e.g. “your Raspberry Pi hostname or address:8080″ such as “192.168.1.10:8080″).

The radio scripts

First, you need to log in to your RPi and download the code from github. The simplest way to do with is to just download the code in a zip file but first change directory to /tmp so as to not make a mess somewhere important:

$ cd /tmp
$ wget https://github.com/scp93ch/raspberry-radio/archive/master.zip

We now need to unpack the zip file and change to the new directory that is created:

$ unzip master.zip
$ cd raspberry-radio-master

In the scripts folder you will see two shell scripts: bbc_radio_update and radio

I have blogged about these two radio scripts previously. In summary, the bbc_radio_update script finds out what the latest URLs of the BBC radio stations are (they change periodically) and stores the latest URLs in a file (/var/local/bbc_radio/urls) for the radio script to make use of. You don’t need to run the bbc_radio_update script yourself: it is automatically run by the radio script when the URLs change. The radio script is the one that actually plays and stops the radio from playing. It does this using mpd which may or may not be set up to work properly on your RPi (for some clues about setting it up, see the earlier post). The radio command can be used like:

$ radio bbc4
BBC Radio 4
[playing] #1/1   0:02/0:00 (0%)
$ radio 5
BBC 5 Live Sport Extra
[playing] #1/1   0:01/0:00 (0%)
$ radio stations
BBC1
BBC1x
BBC2
BBC3
BBC4
BBC4x
BBC5l
BBC5lx
BBC6
BBCan
$ radio status
BBC 5 Live Sport Extra
[playing] #1/1   0:01/0:00 (0%)
$ radio stop
$ radio reset
Fetching station URLs...

The install.sh script copied these two scripts into /usr/local/bin and makes the directory for the cached URL file.

The web server

The radio command works nicely if you are logged in to the RPi in a terminal window but it’s not very convenient otherwise. I wanted to be able to control the radio easily from my phone but didn’t want to go to the both of creating an app for the purpose so instead I have done it through a web page.

I previously wrote a small web server for moving my Where Clock and this seemed the obvious place to start. As well as being good fun to write your own web server, the standard packaged ones (such as Apache, nginx or lighttpd) deliberately make it difficult for people using pages delivered by a web server to cause effects on the computer hosting the web server (doing so is generally considered a security breach). As this is just a web server on a Raspberry Pi running on a home network we can break some of the rules and make it run as root and execute shell commands if we want.

Simple version

The final web server I wrote got a bit complicated because I wanted to make it robust (deal with any errors), make the interface look nice (so it has to manage Javascript, CSS and web fonts), be usable (so it provides additional information to the browser) and follow REST principles (using GET and POST properly). It ended up at nearly 250 lines which is not large really but is a lot to explain. To make things easier to understand, I have cut it down into a tiny version that is not robust, pretty, usable or REST but it basically works. The file is available in the download as webserver/radio_server_simple.py. Here it is:

#!/usr/bin/env python

# This code is copyright Stephen C Phillips (http://scphillips.com).
# It is licensed under GPL v3.

import socket
import subprocess

# Configuration 
PORT = 8080

def radio(cmd):
	"""Runs the 'radio' command (which runs mpc) and IGNORES the output."""

	print "Executing: radio " + cmd
	proc = subprocess.Popen(['radio', cmd], stdout=subprocess.PIPE)
	output = proc.communicate()[0]
	print output

# This is the web page that we send back to the web browser, regardless of what is requested
body = """
<html>
<head><title>BBC Radio</title></head>
<body>
<ul>
<li><a href="/radio/bbc1">BBC1</a></li>
<li><a href="/radio/bbc2">BBC2</a></li>
<li><a href="/radio/stop">Stop</a></li>
<li><a href="/radio/reset">Reset</a></li>
</ul>
</body>
</html>
"""

# Standard socket stuff
host = ''
port = PORT
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, port))
sock.listen(1)  # don't queue up any requests

# Loop forever, listening for requests:
while True:
	print "Waiting..."
	csock, caddr = sock.accept()
	print "Connection from: " + `caddr`
	
	req = csock.recv(1024)  # Get the request from the socket, 1kB max
	print "Request: " + req

	# The lines in a request each end with \r\n and the first line is what we want
	req = req.split("\r\n")[0]

	# Requests that we want will look like "GET /radio/bbc4 HTTP 1.1"
	if req.startswith("GET /radio/"):
		cmd = req.split("/")[2] # gets everything after the "/radio/"
		cmd = cmd.split(" ")[0] # gets the part before the space
		radio(cmd) # runs the radio command

	# Generally just return the same web page
	if req.startswith("GET /radio/") or req.startswith("GET / "):
		message = "HTTP/1.0 200 OK\r\n" + \
				"Content-Type: text/html\r\n" + \
				"\r\n" + \
				body
	# Any other requests (such as for the favicon) we return a 404 error code
	else:
		message = "HTTP/1.0 404 Not Found\r\n\r\n"

	# Send the message back to the web browser and close the child socket
	csock.sendall(message)
	csock.close()

Line 10: the only bit of configuration is the port number that the server is going to listen on. A port number is like a door number in a street. If you are a web browser (using the HTTP protocol) then you automatically try port/door number 80 and try to connect to it (knock on the door!). The convention is that a web server will be sitting on the other side waiting to talk to you. If there is already a web server sitting there (such as Apache or nginx running on the RPi) then you cannot have another one there as well or you wouldn’t know which one to talk to. Instead, the new web server must find an unused door/port number. Port 8080 is often used for a second web server. It has the advantage of being a number greater than 1024: only processes run by root can wait behind the low numbered ports/doors.

Line 12: we define a simple subroutine to execute the radio shell script using the Python subprocess module. We pass in the command’s argument (such as “bbc4″) grabbing hold of the standard output of the command (that you would normally see in the terminal). It prints both the command and the output.

Line 21: here is the simple web page that we will send back to the web browser for any successful request. This is a single page web server with the page embedded in the software rather than worrying about reading files from the filesystem. The page defines 4 hyperlinks all back to the same server to choose BBC Radio 1, Radio 2, stop or reset.

Line 35: getting a bit complicated here. We have to create the right sort of socket and then we “bind” that socket to the port we configured earlier. The door analogy has broken down already here… apart from that we define that we don’t allow any queues outside of the door: only 1 connection at a time.

Line 43: starts an infinite loop – one that never ends. We want the program just to continue running, waiting for requests from web browsers.

Line 45: is where it actually waits for a “knock on the door”, a connection with the sock.accept() command. When a web browser does connect the web server actually essentially says to the browser “okay, you got the right door for web pages, but you need to go down the road and speak to the guy at number 12345″ or something. This is the child socket that is created (csock) and theoretically, frees up the main web server to deal with another customer. In our case, it won’t make things any faster but we still have to go through the motions of using sockets properly.

Line 48: at the child socket “door” we receive what the web browser wants to ask us – the request. A request from a web browser is quite simple:

GET / HTTP/1.1
Host: raspberry1:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6

The first line says what the browser wants (it wants the page at “/” or the root) and what protocol is is using (HTTP version 1.1). Then there are a few more lines which may be helpful in some cases such as what types of content the browser will accept, what type of browser it is (Chrome on Windows in this case), whether it can deal with the content being compressed and by what method and what language the user prefers.

Line 52: all we want to know is what the browser wants to GET so we just grab the first line.

Line 55: the design of this simple web server is that GET requests for “/radio/something” will cause it to execute the shell command “radio something“. Note, executing shell commands using unchecked input from the web browser is a really bad idea in general. But, on we go… The next couple of lines extract the part of the string that we need and pass it to the subroutine to execute the radio command.

Line 61: if the request was a radio command or for the root page then we just want to return the same basic web page to the browser. To do this we have to send the HTTP header information followed by the web page we defined earlier. The header in this case is just two lines: “HTTP/1.0 200 OK\r\n” and “Content-Type: text/html\r\n”. Lines in HTTP must be terminated by a carriage return and a line feed (“\r\n”). The first line says what version of the protocol the server is using and gives the HTTP status code. The second line says what sort of content follows, in this case HTML. We then put one more empty line and then the web page.

Line 68: if the browser asked for anything else then we return a 404 Not Found code to tell it to give up on the idea. This does happen even in this limited scenario as a web browser will commonly request “/favicon.ico” (again and again) in order to display a nice icon for the site in the URL bar.

Line 71: here we actually send the message back down the child socket and then close it. The fact that we close the connection tells the browser that the end of the message has been reached. HTTP 1.1 allows for persistent connections to reduce the overhead of creating the connection every time but we’re not implementing that here!

Trying it out

To try the simple web server, choose the port you want it to run on (8080 is the default), make sure it is executable (use chmod +x) and just run it with “./radio_server_simple.py”. It will output what is going on to the terminal. Access the server from another computer using the RPi’s IP address or name, e.g. “http://raspberry1:8080″ or “http://192.168.1.2:8080″ and experiment! All the major web browsers now have developer modes where you can see what is going on. for Chrome, try pressing F12 and switching to the “Network” tab. You can then see the detail of all the requests and responses.

Note, if you stop the web server (with Ctrl-C for instance) and then immediately start it again the port will not have been freed by the operating system. You have to wait a short while (a few 10s of seconds) before it will run again.

Complicated version

So, what is wrong with the server above? Not much really for our purposes, but I do like to make things as good as I can so I did improve is quite a bit. If you want to open the full web server in another tab then I’ll explain the changes.

Lines 16-40: more configuration than before including some default values that can then be overwritten by command line options (try running the server with “--help” to see them.

Line 42: this server needs to be able to return more than just HTML files to the browser so it has to tell the browser what “Content-Type” it is sending back. It does this by just looking at the file extension of the file and using this hash.

Lines 51-62: we want this server to run permanently as a service so we need some proper logging so that we can find out what’s been going on with it and work out what happened if something goes wrong. This sets up a log file and logs some information. The log file is in /tmp/radio_server.log if you’ve run the server by hand or in /var/log/radio_server.log if you’ve run it using the init script.

Lines 66-114: the radio subroutine has got a lot bigger! It does the same thing initially but then does a lot of work parsing the output so that it can send a useful response to the web browser. The big change here is that the web browser will now be using AJAX calls instead of basic HTTP requests.

AJAX is what is used by almost any interactive web interface these days: if you click on a button and something in the page updates rather than the whole page reloading then that is using AJAX. Under the hood, all it actually is is the web browser still sending a normal HTTP request (a GET or a POST) but it is done through some Javascript running as part of the web page. The response that comes back is not a new page but instead is a bit of XML or JSON (or whatever actually) which the Javascript parses and acts upon.

What the proper web page for this system does is load a simple framework page and then use AJAX calls to do the rest. For instance, when the page loads it does a GET for “/stations” and the server returns some JSON that lists the available radio stations. The page then dynamically creates a button for each station.

The radio subroutine has to parse the output of the radio shell command to work out what to tell the browser. It returns a tuple of an HTTP status code and the JSON to send to the browser.

Lines 117-149: this is a new subroutine which reads a file from the file system in response to an HTTP GET request. This is the bread and butter of a web server: a browser asks for a page, it reads it from the disc and sends it back. Proper servers have caches and can do all sorts of clever things like server side includes and compression but this one is quite basic. I have though tried to make it fairly robust and deal properly with the browser asking for things that don’t exist (returning a 404) or which are outside of the area the server is supposed to be serving from (so that you cannot ask for “../../etc/passwd”) or something. Lines 145/6 are the lines where it looks at the file extension and guesses the Content-Type (defaulting to HTML). This is not a great method, but it works for us.

Lines 152-166: the socket part is a bit bigger because I have added some exception handling so that if the chosen port is already in use by another server then the error is logged and you can see what is going on.

Line 169 onwards: the main loop hasn’t changed much really. At line 183 there’s a new command to wait until the child socket is ready to be used as otherwise the Python script will sometimes try to read from the socket before the operating system is ready (a type of “race condition”). The main change is in what you might call the “API”: the commands that the browser sends to the server have changed to make them in line with the REST principles.

I’m not saying that this system definitely adheres to REST principles, in fact I am not sure how a web service that causes changes in the physical world should, but the main thing I made sure of is that any GET requests do not cause change on the server and that changes are caused through POST requests. So,

  • to get a list of stations you do “GET /stations”
  • to get what station is currently playing you do “GET /playing”
  • to select a station, you do a POST to “/playing”
  • to reset the radio player you POST to “/reset”

POST operations in HTTP have the address that you are POSTing to (in the header, see above) and then whereas GET requests don’t have anything in the request body, POST requests have the parameters (what you are POSTing) in the request body. This is dealt with (poorly) in the code by lines 204-210 and 217-225.

The only other addition to the web server is that it automatically keeps track of what the radio stations are called. This information is returned by mpd when you stream a station and the server records the full name of the station in the station_id hash. The full name is returned to the web browser so that it can display it in the status button.

Service init script

You can just run the complete web server in the same way as the simple one: from the command line. However, I’ve provided a so called “init script” so that the web server will start when the RPi boots. I’ve explained how these work previously.

The install script copies the web server into /usr/local/bin, puts the pages into /var/local/pages and the init script into /etc/init.d and sets it up. You may need to edit the options in the init script if, for instance, you want to change the port that the service uses.

Web pages

The web pages server by the web server are all in the pages directory and are installed into /var/local/pages.

The framework is App.js which provides a template for a web page that looks and behaves quite like a native app on a phone. I made use of a small part of the App.js framework (which has had several releases since). As it is such a simple looking web page there is actually very little apart from Javascript to do all the AJAX calls and program logic to update the browser’s view. The Javascript is all written using jQuery to make it simpler (jQuery has a lot of useful functions, especially for doing AJAX calls). I also made use of Font Awesome in order to create the spinning refresh icon.

I’m not going to explain the Javascript here (sorry) but there are a few comments in there and it is quite clearly written. If you are interested, the main file is index.html which makes use of some functions defined in main.js.

Conclusion

This “Raspberry Radio” software should give you a robust, easy to use system for getting the BBC radio stations to stream on your Raspberry Pi (or other Linux computer). I hope that by explaining how the web server works I have partially demystified a technology we all interact with every day and encourage you to experiment with the web servers and continue reading to understand it better and build your own tools. I would not however deploy a web server like one of these on a public network (e.g. the internet) as it is very likely there is a security flaw in there that could be exploited so that at a minimum someone could maliciously crash it but in the worst case take control of your machine. It’s fine to deploy it on your home network though (unless you really don’t get on with your family…).

Have fun! Stephen.

Hungry Children: update

Please sir can I have some more time?

Well, since I posted my open letter to the school about children going hungry at lunchtime, there have been over 4000 page views and no response from the school as yet (apart from an acknowledgement). I have done a fair bit of reading around the subject though – both comments here and on Facebook and other articles on the web.

Let me be clear, I am not out to bash Bitterne Park school. I think they do a good job but I think they have some problems because of the school’s buildings not being improved whilst the number of students increases. Whatever the reasons though (and I await their response), I don’t find it acceptable that children ever go hungry and I want something to change. There are some indications that the situation at Bitterne Park has got worse since the introduction of fingerprints for food (a.k.a cashless catering). That would be ironic as it was claimed it would speed service up. I argued that it could not speed it up as the slowest point in the queue was getting served. I didn’t expect it to slow it down though: has it?

What is more concerning though is that this doesn’t seem to be an isolated issue. Back in 2007 The Guardian wrote about school lunchtimes being cut. They attributed this to teachers contracts being changed so that only the senior management team are permitted to supervise lunchtimes. That means that a school must employ more lunchtime supervisors to manage the free-for-all, or alternatively cut down the time so that there is less chance of any unwanted incidents. Is that the right choice for the children’s health and well-being though? Various comments say that similar things happen at other schools in the city: if so, how have we let this happen so widely?

There are some great resources online around school lunches and making the food and the environment healthy for our children:

Fundamentally, an adult would not put up with it. Why should our children?

Children going hungry at Bitterne Park Secondary School

Please sir can I have some more time?
What follows is the text of a a letter sent to Bitterne Park Secondary School, Southampton on 29th September 2014.

Dear Mrs Trigger,

I am writing to you because one of my children was not able to eat any lunch last Thursday: a situation seemingly caused by inadequate facilities at the school. This is completely unacceptable. In discussing the situation with my three children I have found out about other aspects of the catering arrangements which also concern me. I am publishing this letter on my blog (http://blog.scphillips.com) in order that other parents may contribute their experience and so I am using the letters “T”, “F” and “R” to refer to my children in order to preserve some anonymity.

The context:

  • the lunch break is 35 minutes long, from 12:20 to 12:55
  • it is followed by a registration period of 5 minutes
  • there are 1500+ children at the school, plus numerous members of staff
  • there are three places to buy food: the main canteen, the “Taste Station” and “Pasta King”
  • there are two points to pay in the canteen and one each at the other two outlets

Basically, from what I understand, the queue to be served lunch in the canteen is so long that it takes most of the lunch break to queue and be served. Not wanting to spend the whole 35 minutes queueing, F prefers to socialise for most of the lunch break and queue up near the end of the period. Generally he eats his food on the way to and during the 5 minutes registration period outside of the classroom (with others) as his tutor does not mind. On Thursday last week the queue at the end of lunch was still too long and F did not get served and hence went hungry.

Another of my children, T, tells me that when she has school lunches she queues from the start of the period and very often has to throw food away uneaten as there is not enough time to eat it. When the bell goes, she eats some more of her food on the way to registration and then puts the rest in a bin before she gets there (along with the disposable container and cutlery that all the children use every day) as food is not permitted in classrooms.

My other child, R, only tried school lunches once. She spent almost all of the lunch break queueing, bought a curry, ate a few mouthfuls and then the bell went so she had to leave the rest. This was a while ago. She now knows that her tutor lets children eat their lunch in the classroom during registration time as her tutor thinks it is not right that they do not have enough time.

It just is not acceptable that children are regularly not able to get an adequate lunch. In my opinion, lunchtime should be an opportunity for social interaction as well as food, but even the basic necessity of being able to eat food is lacking. It seems clear that there is a serious problem at your school. I guess it has been caused by the catering facilities not keeping up with the expansion in school numbers and, increasingly, children not being allowed off-site in the lunch break. My main question is:

  • Do you agree there is a problem?
  • If so, what strategy is in place to change things so that children don’t go hungry and don’t have to throw away food due to lack of time?

In addition,

  • How long do you think someone should have to eat their lunch? (My children thought 20 minutes would be good)
  • How many people do you expect to serve during the period?
  • Why was the additional food outlet known as the “Snack Shack” opened just for the two days of the last OFSTED inspection?

Last term I corresponded with members of staff about the biometric system you introduced. One of the main reasons for installing it given in both the school’s information and by the vendor of the system was that it would speed up lunchtime queues. At the time I suggested that it most likely would not (as the evidence from my children suggested that actually getting served was the bottleneck) and enquired as to why the lunch break was so short. I ask again: why is the lunch break only 35 minutes? I do understand that extending the lunch break time would also mean extending the length of the school day. I can also see that this would cut into extra-curricular activity time after school, but, as I said last term, surely actually getting children fed is the most important thing and the school still seems to be failing in this regard.

I am aware that I am perhaps overloading this letter with questions but I am also concerned that there are vending machines in the school containing crisps, chocolate and fizzy drinks. Similar machines were removed when the government’s “Healthy School” scheme was running from September 2006 but returned sometime after April 2011 when the scheme closed. Why are these vending machines there? Surely the school should strive to be healthy regardless of government schemes?

I would appreciate it if you could carefully address each of my questions (highlighted).

Yours sincerely,
Dr S Phillips.


Since then I have written another short post about the topic more generally.

Fingerprints for Food

What follows is the text of a letter sent to Bitterne Park Secondary School, Southampton on 24th June 2014.

Dear Mrs Trigger,

Re: cashless catering

I approve of the cashless catering idea in principle: it removes any difference between those who have free school meals and those that don’t. It stops anyone stealing someone’s money or losing their own. I should also note that I am not irrationally worried about the technology: I work in IT research and fully understand how these systems work.

Basically my issue with the system is that it just is not necessary to use a fingerprint: a swipe card or PIN can be used with the same point of sale system and would be more appropriate. My main concern is that is normalises giving away personal data at a point in history when people in general are struggling to appreciate its value. We try to teach our children not to post things they would regret on Facebook and yet the school is telling them that handing over their fingerprints just to pay for food is just fine.

By law the children have to be educated about the system and be told about their right to refuse and what the alternative is. To my understanding this has not yet been done. Instead I have one of my children being told by their tutor that they need to bring the form back or they will not be able to get any food in September. And what about that form? There is no explanation as to what the alternative is, no option on the form to say you do not want to permit your child to take part and we were given just 4 days to return it.

From talking to parents of children due to join the school in the 2014 academic year, I understand that they have been asked up-front to give their consent for the fingerprint system. Again though, if this is the same information that we, as current parents, were sent then it does not describe the alternative or give an easy way to refuse. Will the new intake (every year) receive education on the reasons for requiring the data, their right to refuse and on personal data safety in general?

The alternative, I have now been told, would be to use a PIN: the student would type in their PIN and their photo would appear on the cashier’s screen as a check. I do wonder why the alternative cannot be using cash as they do now: it works, has no privacy implications and we all have it.

European and UK data protection law has a principle called “proportionality”: basically, if you want to request and store personal data then there has to be a good enough reason for it. I am not saying the use of this system is illegal – I am not a lawyer, and the system only records a few pieces of data about the fingerprint rather than a scan of the fingerprint itself – but it comes back to the psychological issue: the children do not understand that it is not the actual fingerprint and many will happily hand over the fingerprint for food happy in the knowledge that the school has asked them to and that therefore it is okay.

I am left wondering what problem this system has been brought in to solve. The salesman at the information evening repeatedly pointed out how fast it was compared to people fumbling for change and this was repeated in the letter about the system we received. The school seem to recognise that there is (unsurprisingly) a problem in getting 1600 people fed in 45 minutes [edit: apparently it is actually 35 minutes], however, my children tell me that it is not the paying for the food that holds the queues up but the getting served. In other words, even if the food was free it wouldn’t be any faster. I have received no good explanation as to why the lunch hour cannot be just that: 1 hour long.

So, we have a system, now ordered, to be used from September which seems unlikely to solve the problem of lunchtime queues and sends an inappropriate educational message to our children. We, as parents, are given no option to refuse on the form, the school has not yet discussed alternatives with the children (as they must do by law) or with the parents in general.

Mr (REDACTED) asked me if I had any input for the assembly round this week. I would suggest:

  • You must follow the law:
    • describe what the alternative is
    • let them know they have the right to refuse at any time, even once signed up to the system
  • You must not pressurise them into using the system:
    • My youngest daughter has been told by her tutor that she must bring the form back in or she will not get any food – this is unacceptable
  • I think you should explain why you want to install the system: I would also like to know. As described above, I don’t believe it will speed up the whole serving process.
  • You should point out that the system is not taking a police-style record of their fingerprint and that for this reason it is okay to do this.
  • I would take the opportunity to point out to the students that although the school is asking for this data, in general they should be very careful in giving away personal data such as this (or phone numbers, email addresses, date of birth, etc.).
    • It’s a bit tricky, as I think the fact the school is asking for this data unnecessarily is a bad educational message so you’ll have to do the best you can to turn this around.

Yours sincerely,
Dr S Phillips.


The response from the school did not address most of my points. I was informed that there was an “alternative pin system” (which I actually mention in my letter) and that they would support parents who felt unhappy about the fingerprint system (and presumably children who do not want it either?). I was told that they would like parents to support what they were trying to do “in terms of speeding up the lunchtime service” and to “reduce the amount of cash that students have to have within school”. My letter clearly explains in the very first sentence that I am in favour of cashless catering (I am in favour of cashless anything to be honest) and also that I dispute their claim that it will speed up the lunchtime service.

I was told that “Extending the lunch hour is not an option” as adding more time to the lunch break would mean a reduction in after-school club time. I would argue that giving children enough time to eat was more important than after-school clubs.

I was told that the system “is common to most of others schools in Southampton and across the UK” but I just don’t see the relevance of that. Cashless catering: fine. Fingerprints: not so good. You can have both cashless catering and no fingerprints so why not do that?

Finally, I am told that the system “will allow us to have greater benefits for the catering dept. and ultimately our students”. I can see that an electronic point of sale (EPoS) system is useful to the school as it can record what has been sold (and when) but again, this is unrelated to the fingerprint issue and I am frankly surprised that they didn’t already have such a system.

My children reported to me that the assemblies following this exchange did not explain about the alternatives or go into any of my suggested points.

I have to admit, at this point I gave up on this particular argument as the school just did not seem to be addressing the issues and (as ever) there were plenty of other things to do. It would be interesting to know what information was provided to the 2014 intake though. Can anyone tell me?

BBC Radio on the Raspberry Pi (v2)

This is an update to my recent post on this topic.

This page shows how to create a simple “radio” command to play and stop different BBC radio stations on a Raspberry Pi. Once set up, you can just type e.g. “radio BBC4″ to get your favourite station playing. This is useful for various reasons, for instance if you have a room with just an amplifier and speakers in then, with a Raspberry Pi, you can listen to the radio (and with other software your music collection). You can also listen to BBC 6 Music which you cannot get on FM.

The scripts described on this page are available to download from github as part of a larger project which I’ll be blogging on soon.

Getting BBC radio streaming from a Raspberry Pi is something I’ve been wanting to do since I started playing around with them. I’ve seen various impressive solutions involving re-purposing old portable radios but I never got the hang of what needed doing in software.

This tutorial is based on a Raspberry Pi with the latest version of Raspbian installed (2014-01-07), updated using rpi-update. The OS also has some other packages installed to enable it to be a UPnP renderer. This is all documented in a previous post.

Updating the Stream Addresses

Part of the complication with BBC radio stations is that the URLs required to stream from change periodically. First of all therefore we need a script to find the URLs and cache them. The “playlist” URLs do not change (very often), but their contents does. Lets look at one using the “curl” command:

$ curl http://open.live.bbc.co.uk/mediaselector/5/select/mediaset/http-icy-aac-lc-a/vpid/bbc_radio_one/supplier/ll_icy2/format/pls.pls

NumberOfEntries=1
File1=http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio1_q?s=1414757382&e=1414771782&h=9533b45c836fee7271d17f9a4bdd7eff
Title1=No Title
Length1=-1

The actual URL for streaming is the one following “File1=”. What we need to do is download all the playlists we want and extract each of these URLs. To do this, create a script called “bbc_radio_update”, e.g. using “sudo vi /usr/local/bin/bbc_radio_update”:

#!/bin/bash

# This script is based on one found in what is assumed to be the public domain.

set -e
playlist=/var/local/bbc_radio/urls
rm -f $playlist
 
declare -A radios
 
radios["BBC1"]="http://www.radiofeeds.co.uk/bbcradio1.pls"
radios["BBC1x"]="http://www.radiofeeds.co.uk/bbc1xtra.pls"
radios["BBC2"]="http://www.radiofeeds.co.uk/bbcradio2.pls"
radios["BBC3"]="http://www.radiofeeds.co.uk/bbcradio3.pls"
radios["BBC4"]="http://www.radiofeeds.co.uk/bbcradio4fm.pls"
radios["BBC4x"]="http://www.radiofeeds.co.uk/bbcradio4extra.pls"
radios["BBC5l"]="http://www.radiofeeds.co.uk/bbc5live.pls"
radios["BBC5lx"]="http://www.radiofeeds.co.uk/bbc5livesportsextra.pls"
radios["BBC6"]="http://www.radiofeeds.co.uk/bbc6music.pls"

for k in "${!radios[@]}"
do
	pls=${radios[$k]}
	curl -s $pls | grep File1 | sed "s/File1=/$k, /" >> "$playlist"
done

We then need to make the script executable (by all users), create a directory for the output URLs to be placed (that all users can write to) and run the script:

$ sudo chmod 755 /usr/local/bin/bbc_radio_update
$ sudo mkdir /var/local/bbc_radio
$ sudo chmod 777 /var/local/bbc_radio/
$ bbc_radio_update

It is normally not considered very secure to have a directory such as /var/local/bbc_radio which all users can write to but it is the simplest way of making it so that all users can run the command and it really doesn’t matter for a Raspberry Pi in the home.

To look at the output, just “cat” the file (note the contents of this file changes: yours will not be the same):

$ cat /var/local/bbc_radio/urls
BBC4, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio4_p?s=1399723465&e=1399737865&h=2cebe3f0ad99df0e6f0f4e56ca4d7736
BBC6, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_6music_p?s=1399723579&e=1399737979&h=b85295564b4afb786f533d75023ba57c
...etc

You can make this script run periodically by linking it to e.g. /etc/cron.hourly with the command “sudo ln -s /usr/local/bin/bbc_radio_update /etc/cron.hourly/” but there will still be times when the URLs are out of date. The script to run the radio will take care of this instead.

Playing the Streams

Now we have the necessary URLs we need to play them. Although I use gstreamer with gmediarender as a UPnP renderer I cannot get it to play the BBC stations so instead we use mpd and control it with mpc. Firstly we need to install them (and ignore the errors):

$ sudo apt-get install mpc mpd
...etc...
Setting up mpc (0.22-1) ...
Setting up mpd (0.16.7-2) ...
insserv: warning: script 'mathkernel' missing LSB tags and overrides
[....] Starting Music Player Daemon: mpdlisten: bind to '[::1]:6600' failed: Failed to create socket: Address family not supported by protocol (continuing anyway, because binding to '127.0.0.1:6600' succeeded)
Failed to load database: Failed to open database file "/var/lib/mpd/tag_cache": No such file or directory
. ok

At this point if we try to use mpc, mpd can’t access the ALSA output. Looking in the /var/log/mpd/mpd.log we see:

output: Failed to open "My ALSA Device" [alsa]: Failed to open ALSA device "hw:0,0": Device or resource busy

Edit /etc/mpd.conf and comment out some lines:

audio_output {
        type            "alsa"
        name            "My ALSA Device"
#       device          "hw:0,0"        # optional
#       format          "44100:16:2"    # optional
#       mixer_device    "default"       # optional
#       mixer_control   "PCM"           # optional
#       mixer_index     "0"             # optional
}

The key change may be the “mixer_device” line (not sure). Then restart the mpd service:

$ sudo /etc/init.d/mpd restart

Making it Easy With a “radio” Command

Finally, we need a helpful script to make controlling mpd easy. Save this script as “/usr/local/bin/radio”:

#!/bin/sh
 
# This code is copyright Stephen C Phillips (http://scphillips.com).
# It is licensed using GPL v3.
 
URLS=/var/local/bbc_radio/urls

# get the radio URLs if they are not there
[ ! -f $URLS ] && bbc_radio_update

case "$1" in
    "stop")
        mpc -q clear
    ;;
    "status")
        # Run the "mpc" command, remove any line starting "volume"
        # and replace any line starting with "http" with "Unknown station"
        mpc | grep -vE "^volume:" | sed "s/^http:.*/Unknown station/"
    ;;
    "stations")
        # Take the URLS file, split each line at the comma
        # and just print the first field, then sort them
        cat $URLS | cut -d',' -f1 | sort
    ;;
    "reset")
        # Stop whaever is playing and then update the URL list
        echo "Fetching station URLs..."
        mpc -q clear
        bbc_radio_update
    ;;
    *)
        # Search the URLS file for this script's argument ($1) followed
        # by anything and then a comma. Redirect it to /dev/null so the result
        # isn't shown in the console.
        grep -i "$1.*, " $URLS > /dev/null
        # Check if we found a URL in the URLs file matching the argument.
        if [ $? -eq 0 ]; then
            mpc -q clear
            # Again, search for the argument, take just the first line that matches
            # (using "head"), split the line at the comma and take the second field.
            # Add this to the playlist with "mpc add".
            mpc -q add `grep -i "$1.*, " $URLS | head -1 | cut -d',' -f2`
            mpc -q play
            sleep 2
            # Check if we just found an error (often caused by URLs being out of date).
            # Message could be "ERROR: problems decoding" or "ERROR: Failed to decode"
            mpc | grep "ERROR: .* decod" > /dev/null
            if [ $? -eq 0 ]; then
                # If mpc reported an error then fetch the URLS and try again.
                echo "Fetching station URLs..."
                mpc -q clear
                bbc_radio_update
                mpc -q add `grep -i "$1.*, " $URLS | head -1 | cut -d',' -f2`
                mpc -q play
            fi
            # Get the status, remove the volume line, replace any URL with
            # "unknown station" and this time let it appear on the console.
            mpc | grep -vE "^volume:" | sed "s/^http:.*/Unknown station/"
        else
            echo "No such station"
        fi
    ;;
esac

This script needs some explanation. It has got more complicated that the first version I posted a week ago.

The script supports the commands “stop”, “status”, “stations”, “reset” and any other command is assumed to be the name of a radio station. The first argument to a script is put into the “$1″ variable automatically, so the “case” statement on line 10 causes different bits of script to be run depending on what command you type.

Before we get to the main script, line 9 checks to see if the URLs file exists and, if not, runs the bbc_radio_update script to fetch them.

To work out what is going on with the other commands, you can just try typing most of the commands at the command line. For instance, for the “status” command, try typing “mpc” when nothing is playing. It just prints:

$ mpc
volume: n/a   repeat: off   random: off   single: off   consume: off

The additional pipe to ‘grep -vE “^volume:’ on line 18 means that everything except lines starting “volume:” is printed: we are not interested in those lines so we filter them out. The “sed” command replaces any line starting with “http:” with “Unknown station” as sometimes when a radio station is playing it will print the station name and sometimes it doesn’t know it and just prints the stream URL (which is not very useful so we replace it). If you were playing BBC Radio 4 then the “mpc” and the “radio status” command would give:

$ mpc
bbc_radio_fourfm
[playing] #1/1   0:05/0:00 (0%)
volume: n/a   repeat: off   random: off   single: off   consume: off
pi@raspberrypi ~ $ radio status
bbc_radio_fourfm
[playing] #1/1   0:08/0:00 (0%)

The “radio reset” command clears the mpc queue (so stops it playing) and runs the bbc_radio_update command (this is why all users need to be able to run it). It shouldn’t normally be necessary to do this by hand though as the complicated command for playing a radio station takes care of it most of the time.

So, if you type “radio bbc4″ then we get to line 35 in the script where there is a “grep” command. That searches the URLs file (ignoring case) for e.g. “BBC4, ” and sends the output to /dev/null (so we don’t see it). It is actually searching for the script’s argument (e.g. “BBC4″) followed by anything (the “.*”) followed by a comma and space. This means that the script will work if you just type “radio 4″. The next line asks if “$?” is equal to zero: “$?” is the return code of the last command (the grep) and if it is zero then it means that grep found something and so the station exists in the URLs file. In this case (on line 38) we clear the mpc playlist (and suppress the status message with “-q”) then (on line 42) we get the correct URL out of the file and add it to the playlist. To do this we use grep again and pipe the output first into “head -1″ to get only the first matching line, then into the “cut” command which splits a line, setting the delimiter to a comma and choosing the second field. We play the station on line 43 (again suppressing output).

That pretty much what I had in the previous version. Sometimes this failed silently if the radio station URLs were out of date. Also, if it did work then it just told you the URL you are playing. If instead you wait a moment and then do “mpc” then you get the name of the radio station. This is why there is now a “sleep 2″ statement on line 44. It is followed by ‘mpc | grep “ERROR: problems decoding”‘: if the URL is out of date (or there are other issues) then you get back from mpc the text ‘ERROR: problems decoding “http://bbcmedia.ic.llnwd.net/…etc”‘. We use “grep” to see if we have this error, testing the grep output on line 48. If we have an error then we want to do the “radio reset” command sequence and try playing the station again. Lines 48-53 achieve this. Finally we do the status command again.

Make it executable and try it out:

$ chmod 755 /usr/local/bin/radio
$ radio bbc4
BBC Radio 4
[playing] #1/1   0:01/0:00 (0%)
$ radio status
BBC Radio 4
[playing] #1/1   0:14/0:00 (0%)
$ radio stop

If your URLs file is out of date then you will see something like:

$ radio bbc4
Fetching station URLs...
BBC Radio 4
[playing] #1/1   0:01/0:00 (0%)

That’s all for now!

Updates:

  • 2014-05-19: made some improvements to the radio script
  • 2014-05-21: radio script bug fix
  • 2014-05-24: made radio script automatically fetch URLs the first time and added in “Unknown station” feature
  • 2014-06-29: added lots of comment to radio script, made the grep better, added links to github
  • 2014-10-31: updated the BBC playlist URLs (thanks to Daniel J Wilcox)
  • 2014-12-30: small improvement to the out-of-date URLs check
  • 2015-02-20: updated radio playlist URLs (thanks to Hamish and Fred)

BBC Radio on the Raspberry Pi

2014-05-18: There is now a second, updated post on getting BBC radio on a Raspberry Pi.

This page shows how to create a simple “radio” command to play and stop different BBC radio stations on a Raspberry Pi. Once set up, you can just type “radio BBC4″ to get your favourite station playing. This is useful for various reasons, for instance if you have a room with just an amplifier and speakers in then, with a Raspberry Pi, you can listen to the radio (and with other software your music collection). You can also listen to BBC 6 Music which you cannot get on FM.

Getting BBC radio streaming from a Raspberry Pi is something I’ve been wanting to do since I started playing around with them. I’ve seen various impressive solutions involving re-purposing old portable radios but I never got the hang of what needed doing in software.

This tutorial is based on a Raspberry Pi with the latest version of Raspbian installed (2014-01-07), updated using rpi-update. The OS also has some other packages installed to enable it to be a UPnP renderer. This is all documented in a previous post.

Regularly Updating the Stream Addresses

Part of the complication with BBC radio stations is that the URLs required to stream from change periodically. First of all therefore we need a script to find the URLs and cache them. The “playlist” URLs do not change, but their contents does. Lets look at one using the “curl” command:

$ curl http://www.bbc.co.uk/radio/listen/live/r1_aaclca.pls

NumberOfEntries=2
File1=http://bbcmedia.ic.llnwd.net/stream/bbcmedia_intl_lc_radio1_p?s=1399793891&e=1399808291&h=6c19055ef19b6b3acafc59f381af6fc6
Title1=No Title
Length1=-1
File2=http://bbcmedia.ic.llnwd.net/stream/bbcmedia_intl_lc_radio1_q?s=1399793891&e=1399808291&h=5ada50d070397aa72d01532281aad710
Title2=No Title
Length2=-1

The actual URL for streaming is the one following “File1=”. What we need to do is download all the playlists we want and extract each of these URLs. To do this, create a script called “bbc_radio_update”, e.g. using “sudo vi /usr/local/bin/bbc_radio_update”:

#!/bin/bash
set -e
URLS=/var/local/bbc_radio/urls
rm -f $URLS

declare -A radios
radios["BBC1"]="http://www.bbc.co.uk/radio/listen/live/r1_aaclca.pls"
radios["BBC1x"]="http://www.bbc.co.uk/radio/listen/live/r1x_aaclca.pls"
radios["BBC2"]="http://www.bbc.co.uk/radio/listen/live/r2_aaclca.pls"
radios["BBC3"]="http://www.bbc.co.uk/radio/listen/live/r3_aaclca.pls"
radios["BBC4"]="http://www.bbc.co.uk/radio/listen/live/r4_aaclca.pls"
radios["BBC4x"]="http://www.bbc.co.uk/radio/listen/live/r4x_aaclca.pls"
radios["BBC5l"]="http://www.bbc.co.uk/radio/listen/live/r5l_aaclca.pls"
radios["BBC5lsx"]="http://www.bbc.co.uk/radio/listen/live/r5lsp_aaclca.pls"
radios["BBC6"]="http://www.bbc.co.uk/radio/listen/live/r6_aaclca.pls"
radios["BBCan"]="http://www.bbc.co.uk/radio/listen/live/ran_aaclca.pls"

for k in "${!radios[@]}"
do
    pls=${radios[$k]}
    curl -s $pls | grep File1 | sed "s/File1=/$k, /" >> "$URLS"
done

We then need to make the script executable (by root), create a directory for the output URLs to be placed and run the script:

$ sudo chmod 744 /usr/local/bin/bbc_radio_update
$ sudo mkdir /var/local/bbc_radio
$ sudo bbc_radio_update

To look at the output, just “cat” the file (note the contents of this file changes: your will not be the same):

$ cat /var/local/bbc_radio/urls
BBC4, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio4_p?s=1399723465&e=1399737865&h=2cebe3f0ad99df0e6f0f4e56ca4d7736
BBC6, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_6music_p?s=1399723579&e=1399737979&h=b85295564b4afb786f533d75023ba57c
BBC1, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio1_p?s=1399723596&e=1399737996&h=7a36c56eac5cc55114da6d8f5dfa8558
BBC3, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio3_p?s=1399723367&e=1399737767&h=5857c11d8507a3bef9ed74ae6d59921a
BBC2, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio2_p?s=1399723512&e=1399737912&h=dad579513911b0b087cb474b7a448c6f
BBC4x, http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio4extra_p?s=1399723639&e=1399738039&h=60b10f86626cb7150269aa08e2b27910

To make this script run every day (at 6.25am by default), put a symbolic link to it from the cron.daily folder:

$ sudo ln -s /usr/local/bin/bbc_radio_update /etc/cron.daily/

Playing the Streams

Now we have the necessary URLs we need to play them. Although I use gstreamer with gmediarender as a UPnP renderer I cannot get it to play the BBC stations so instead we use mpd and control it with mpc. Firstly we need to install them (and ignore the errors):

$ sudo apt-get install mpc mpd
...etc...
Setting up mpc (0.22-1) ...
Setting up mpd (0.16.7-2) ...
insserv: warning: script 'mathkernel' missing LSB tags and overrides
[....] Starting Music Player Daemon: mpdlisten: bind to '[::1]:6600' failed: Failed to create socket: Address family not supported by protocol (continuing anyway, because binding to '127.0.0.1:6600' succeeded)
Failed to load database: Failed to open database file "/var/lib/mpd/tag_cache": No such file or directory
. ok

At this point if we try to use mpc, mpd can’t access the ALSA output. Looking in the /var/log/mpd/mpd.log we see:

output: Failed to open "My ALSA Device" [alsa]: Failed to open ALSA device "hw:0,0": Device or resource busy

Edit /etc/mpd.conf and comment out some lines:

audio_output {
        type            "alsa"
        name            "My ALSA Device"
#       device          "hw:0,0"        # optional
#       format          "44100:16:2"    # optional
#       mixer_device    "default"       # optional
#       mixer_control   "PCM"           # optional
#       mixer_index     "0"             # optional
}

The key change may be the “mixer_device” line (not sure). Then restart the mpd service:

$ sudo /etc/init.d/mpd restart

Finally, we need a helpful script to make controlling mpd easy. Save this script as “/usr/local/bin/radio”:

#!/bin/sh

URLS=/var/local/bbc_radio/urls

case "$1" in
    "stop")
        mpc stop
    ;;
    "status")
        mpc status
    ;;
    "stations")
        cat $URLS | cut -d',' -f1 | sort
    ;;
    *)
        grep -i "$1, " $URLS > /dev/null
        if [ $? -eq 0 ]; then
            mpc -q clear
            mpc -q add `grep -i "$1, " $URLS | cut -d',' -f2`
            mpc play
        else
            echo "No such station"
        fi
    ;;
esac

In the script, “$1″ is the first argument given to the radio command. So, for instance, with “radio BBC4″ $1 will be “BBC4″. The case statement deals with “stop”, “status” and “stations” and then for anything else we get to the “grep” in line 16. That searches the URLs file for e.g. “BBC4, ” and sends the output to /dev/null (to get rid of it). The next line asks if “$?” is equal to zero: “$?” is the return code of the last command (the grep) and if it is zero then it means that grep found something and so the station exists in the URLs file. In this case we clear the mpc playlist (and suppress the status message with “-q”), add the radio station’s URL to the queue and play it. To get the correct URL out of the file (for the mpc add command) we use grep again and pipe the output into the “cut” command which splits a line, setting the delimiter to a comma and choosing the second field.

Make it executable and try it out:

$ chmod 755 /usr/local/bin/radio
$ radio BBC4

http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio4_p?s=1399790284&e=1399804684&h=32d5597bc6388bb9da0414c501c900ab

[playing] #1/1   0:00/0:00 (0%)
volume: n/a   repeat: off   random: off   single: off   consume: off
$ radio status
BBC Radio 4
[playing] #1/1  41:02/0:00 (0%)
volume: n/a   repeat: off   random: off   single: off   consume: off
$ radio BBC1

http://bbcmedia.ic.llnwd.net/stream/bbcmedia_lc1_radio1_p?s=1399790041&e=1399804441&h=6895cc667ccad6176fcfd7618960c7b8

[playing] #1/1   0:00/0:00 (0%)
volume: n/a   repeat: off   random: off   single: off   consume: off
$ radio stop

Options are “status”, “stop”, “stations” and the station names as found in the URL file (“BBC1″, “BBC2″, “BBC3″, “BBC4″, “BBC4x”, “BBC5l”, “BBC5lsx”, “BBC6″, “BBCan”). Obviously, the output could be tidied up but it’s good enough for now. If you use this on the same Raspberry Pi as you use for playing music via UPnP then you will find that the sound from both systems is mixed and comes out at the same time.

There’s lots of scope for using this command in other projects to enable easier control of the radio: watch this space!

Updates:

  1. 2014-05-13: changed “grep” to “grep -i”
  2. 2014-05-13: bugfix and “stations” option, thanks to Greg Fenton
  3. 2014-05-14: added more BBC stations, thanks to Brian J Cook
  4. 2015-05-18: added link to updated post

Playing music on a Raspberry Pi using UPnP and DLNA (v3)

What we are aiming for

A music system with a Raspberry Pi plugged in to an amplifier playing music that you choose with your mobile phone. The music can come from MP3s on your phone, from files on your server, files “in the cloud” or from internet radio stations. If there’s more than one Android phone in your household you can have them all synchronised, showing the same playlist and controlling the same music. If you have multiple Raspberry Pis you can put one in each room and choose which one to play the music with. This is all achieved with free software and open standards. I’ve just written some instructions to show how to do it.

I have blogged on this topic two times previously (here and here). Each time it gets simpler as the Raspberry Pi eco-system evolves. I needed a new Raspberry Pi UPnP renderer to be attached to an amplifier and speakers.

My previous post explains lots more about how the system as a whole works, how to test it and how you control the music. This post merely updates the instructions for installing gmediarender and includes some info on using a USB sound card.

Setting up a Raspberry Pi to be a UPnP Media Renderer

Installing the Base Operating System

As usual I started from the latest Raspbian distribution (2014-01-07) which needs a 4GB SDCard card apparently. Once the Raspberry Pi is up and running and on the network, log in (user: “pi”, password “raspberry”), run the first-time configuration command and then update to the very latest firmware. These following commands also show the before and after versions in case that is useful for people:

$ uname -a
Linux raspberrypi 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l GNU/Linux
$ sudo raspi-config
$ sudo apt-get install rpi-update
$ sudo shutdown -r now

Once it has rebooted, log in again check the new status and upgrade all the installed packages:

$ uname -a
Linux raspberrypi 3.12.19+ #681 PREEMPT Fri May 9 16:36:10 BST 2014 armv6l GNU/Linux
$ /opt/vc/bin/vcgencmd version
May  9 2014 16:52:52
Copyright (c) 2012 Broadcom
version c8ed097f7c23249e702bea3ed6b5720136ba00e4 (tainted) (release)
$ sudo apt-get update
$ sudo apt-get upgrade

Installing gstreamer 1.0

Henner Zeller has made the gmrender-resurrect UPnP renderer and it is known to work best with gstreamer 1.0. Unfortunately Raspbian currently still comes with gstreamer 0.10. Fortunately, Christi Scarborough has compiled the necessary gstreamer 1.0 packages for the Raspberry Pi and provided them as debian packages. She has documented it all and very honestly included the warning that using these packages means you have to trust her not to have slipped in some malware or something. Some of the instructions on this page are just a duplication of hers. Christi also provides a ready-made build of gmediarender but as I wasn’t sure which version that was, I wanted to use Henner’s directly.

First we have to install Christi’s GPG key to show that we trust her packages:

$ sudo wget -O - http://www.chiark.greenend.org.uk/~christi/debian/christi@coraline.org.gpg.key | sudo apt-key add -

Then, using “vi”, “nano” or whatever, create a new file to hold the address of Christi’s Debian repository, call it /etc/apt/sources.list.d/upnprender.list and add just one line “deb http://www.chiark.greenend.org.uk/~christi/debian/ wheezy main”.

We then need to update the local cache of available packages to include Christi’s packages and then follow Henner’s instructions for installing gmrender-resurrect:

$ sudo apt-get update
$ sudo apt-get install libupnp-dev libgstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-alsa

Installing gmediarender

Continuing with Henner’s instructions:

$ cd
$ git clone https://github.com/hzeller/gmrender-resurrect.git
$ cd gmrender-resurrect
$ sudo apt-get install autoconf
$ ./autogen.sh
$ ./configure
$ sudo make install

This puts gmediarender into /usr/local/bin and a couple of icons into /usr/local/share/gmediarender

Henner has provided an init script to start and stop the gmediarender software, so we’ll copy this into the appropriate place:

$ sudo cp scripts/init.d/gmediarenderer /etc/init.d

Edit the init script if you need to (e.g. set the name of the renderer). Then use the “update-rc.d” command to put in the necessary symbolic links to the init script that start or stop the service at different runlevels (the following “ls” command shows these for interest):

$ sudo update-rc.d gmediarenderer defaults
$ ls -l /etc/rc*.d/*gmed*
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc0.d/K01gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc1.d/K01gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc2.d/S04gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc3.d/S04gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc4.d/S04gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc5.d/S04gmediarenderer -> ../init.d/gmediarenderer
lrwxrwxrwx 1 root root 24 May 10 11:03 /etc/rc6.d/K01gmediarenderer -> ../init.d/gmediarenderer

Rebooting now should give us a system with gmediarender running, which we can test with “ps”:

$ sudo shutdown -r now
(wait a bit and log in again)
$ ps aux|grep gmed
pi        2078 15.5  9.1 198996 40828 ?        Ssl  11:05   0:05 /usr/local/bin/gmediarender -f Garden -d -u bd1dcf3e746aa69812943cb1d00f7ebc --gstout-audiosink=alsasink --gstout-audiodevice=sysdefault --gstout-initial-volume-db=-10
pi        2123  0.0  0.1   3548   800 pts/0    S+   11:06   0:00 grep --color=auto gmed

Using a USB Audio Card

Basically it all works at this point, though using the built-in audio output of the Raspberry Pi never gives a great quality sound. There are various really interesting high quality DAC cards now available for the RPi which connect directly to the circuit board but for now I went for the simple option of getting a dirt-cheap USB audio card (for 99p you can’t really go wrong…). It does sound much better than the built-in output and is automatically picked up by the latest Raspbian version.

Plug in your USB audio card (which can cause the RPi to reboot…) and then
set it up by following an Adafruit tutorial.

I have a CM109 chipset so all I had to do was edit /etc/modprobe.d/alsa-base.conf and change “options snd-usb-audio index=-2″ to “options snd-usb-audio index=0″.

Reboot again and decent sound comes out of the USB sound card!

Morse Code Transcriber

I have been working for a while on a tool to “listen to” and transcribe Morse code. I’ve been wanting to do it for ages and considered a variety of ways, but with more browsers supporting the Web Audio API the time seemed right to give it a go.

Ultimately, the tool will be integrated into my Morse code trainer to listen to students practising the segments of Morse code in that tool and let them know if they are doing it well enough. For now though, the Morse code transcriber is just an alpha-release stand-alone tool which only works properly in Chrome (I should be able to make it work in Firefox as well).

I recently came across a website that is transmitting Morse code as part of some marketing campaign I guess. I think the transcriber would be very useful to people who want to know what it is saying. It actually seems to work quite well! You need to use the transcriber with the target frequency set to 1000 Hz and the volume on your computer right up. To get it to work reliably you really have to fiddle with the settings. I’ll sort out an auto-calibrating version as soon as I can.

I’ll post some technical details of how it all works later, but I just wanted to let people have a go with it for now.

Here’s a screenshot of it transcribing the sound from the site I mentioned above:

transcriber

Getting a Python script to run in the background (as a service) on boot

For some of my projects I write a simple service in Python and need it to start running in the background when the Raspberry Pi boots. Different Linux distributions use different ways of starting and stopping services (some now use Upstart, some systemd). I am using the “Wheezy” Debian distribution on my Raspberry Pi, and in this case the proper way to do this is using an “init script”. These are stored in the /etc/init.d folder. In there you can find scripts that for instance, start the networking system or a print server. Debian Wheezy uses the old Sys V init system which means that these scripts are run according to symbolic links in the /etc/rc.x directories. The Debian documentation explains this.

Anyway, the following init script makes getting a Python script (or e.g. a Perl script) to run when the Raspberry Pi boots fairly painless. Services are supposed to run as “daemons” which is quite complicated in Python and involves forking the process twice and other nasty bits. Instead we can make use of the handy start-stop-daemon command to run our script in the background and basically deals with everything we need.

Changing the init script

Lines 14 and 15 define where to find the Python script. In this case I have said that there is a folder /usr/local/bin/myservice and that the script is called myservice.py inside there. This is so that any additional Python files or other bits that your Python script needs can also be tidily put into that one place (not really how you’re supposed to do it, but is easy).

Line 16 defines what we call the service. You should call this script by the same name.

Line 23 sets what user to run the script as. Using root is generally not a good idea but might be necessary if you need to access the GPIO pins (which I do). You might want to change this to the “pi” user for instance.

Line 28 loads a some useful functions from a standard file. We later use the logging functions for instance. We then define functions do_start and do_stop that will be used to start and stop the script.

start-stop-daemon needs to be able to identify the process belonging to a service so that (1) it can see it is there and does not start it again, and (2) it can find it and kill it when requested. In the case of a Python script then process name is “python” so this is not a very useful identifier as there may well be other Python processes running and things would get confusing. Instead we get start-stop-daemon to store the PID (the or process ID) using the --pidfile $PIDFILE --make-pidfile arguments. When told to start the process it looks for the file $PIDFILE which is defined in line 26 to be /var/run/myservice.pid (which on a Raspberry Pi is actually found at /run/myservice.pid thanks to a symbolic link.

Other than that, we use the --background flag of start-stop-daemon to run our script in the background, “–chuid” to set the user that the script runs as (with “–user” to look for scripts run by that user when we are trying to determine if it is already running) and “–startas” to define what we want to run. The options to start-stop-daemon end with the double-hyphen and then we add on $DAEMON_OPTS in case there are any parameters to pass to the daemon itself.

When stopping the daemon the --retry 10 means that first of all a TERM signal is sent to the process and then 10 seconds later it will check if the process is still there and if it is send a KILL signal (which definitely does the job).

Using the init script

To actually use this script, put your Python script where you want and make sure it is executable (e.g. chmod 755 myservice.py) and also starts with the line that tells the computer to use the Python interpreter (e.g. #!/usr/bin/env python). Edit the init script accordingly. Copy the init script into /etc/init.d using e.g. sudo cp myservice.sh /etc/init.d. Make sure the script is executable (chmod again) and make sure that it has UNIX line-endings. At this point you should be able to start your Python script using the command sudo /etc/init.d/myservice.sh start, check its status with the /etc/init.d/myservice.sh status argument and stop it with sudo /etc/init.d/myservice.sh stop.

To make the Raspberry Pi use your init script at the right time, one more step is required: running the command sudo update-rc.d myservice.sh defaults. This command adds in symbolic links to the /etc/rc.x directories so that the init script is run at the default times. you can see these links if you do ls -l /etc/rc?.d/*myservice.sh

An example service

If you run a Python script in this way then you don’t get to see any output on the terminal so you need to do proper logging (rather than just print statements). The example Python service here shows how to parse command-line arguments and do simple logging to a file.

By default it logs to a file in /tmp and at midnight will save the day’s log file and start a new one, keeping 3 at most. To change where it logs to you need to use the “–log” or “-l” command line argument, so when running this from the init script you need to set the “$DAEMON_OPTS” variable. For instance, if you run the service as root then you could set $DAEMON_OPTS="/var/log/myservice.log" (the normal user cannot write files in there).

The example service above is heavily commented to explain what is going on, but one bit which is a little unusual is line 36-51 which I added to help people debug their services. Those lines set up a class called MyLogger which is initialised with the standard logger object just constructed along with a log level. The class only defines a write method which is all that is needed to emulate a normal stream of the standard output (or “stdout”) or standard error (or “stderr”) type. Normally when you do e.g. print "hello" in Python it actually does sys.stdout.write("hello") but line 49 changes this so that the stdout stream is replaced by an instance of MyLogger logging at the INFO level. Therefore, later in the example when the statement print "This is a print" is executed, that string actually goes into the log file. In the same way the standard error stream is replaced which means that when the program crashes because of the deliberate division by zero, the error and the traceback all appear in the log file at the ERROR level.

Please note that for both of these scripts to work you must make sure that the files have UNIX line-endings (just a LF) not DOS line-endings (CRLF). If you copy and paste from the web page into a Windows text editor and then transfer to a Linux machine (such as a Raspberry Pi) then they may end up with DOS line endings and will not work. If the shell script has DOS line-endings then if you run it using ./myservice.sh you will see -bash: ./myservice.sh: /bin/sh^M: bad interpreter: No such file or directory. If the Python script has DOS line-endings and you run it using ./myservice.py you will see : No such file or directory. You can fix this problem using the dos2unix command: just do e.g. dos2unix myservice.py (and sudo apt-get install dos2unix if you don’t have the command). To avoid the problem in the first place you could copy the “raw” link from above and do e.g. wget https://gist.github.com/scp93ch/cdb15468b84a8b3eb0aa/raw/myservice.py on the Raspberry Pi to download it directly.

Updates

  • 2013-09-30: corrected mistake in use of –make-pidfile (thanks to Max Sistemich).
  • 2014-06-29: added DAEMON_OPTS, more explanations and an example service with logging and arguments.
  • 2014-07-07: added redirection of stdout and stderr.
  • 2014-08-01: added instructions on sorting out the line-endings.

Playing music on a Raspberry Pi using UPnP and DLNA (revisited)

Edit, 2014-05-11: Please note there are (yet again) updated instructions for installing gmediarender. The rest of this tutorial is still valid though.

What we are aiming for

A music system with a Raspberry Pi plugged in to an amplifier playing music that you choose with your mobile phone. The music can come from MP3s on your phone, from files on your server or from internet radio stations. If there’s more than one Android phone in your household you can have them all synchronised, showing the same playlist and controlling the same music. If you have multiple Raspberry Pis you can put one in each room and choose which one to play the music with. This is all achieved with free software and open standards. I’ve just written some instructions to show how to do it.

With any luck your initial outlay will only be about £45. The end result will be similar to the systems from Sonos and Squeezebox costing loadsamoney. Further additional devices to play music through another Hi-Fi or TV would also be £45.

My earlier post about getting Raspberry Pis to play music using your phone or phones as the controller has been very popular. Since then though the software has developed with new releases of the Debian distribution, gmrender-resurrect and the Raspberry Pi firmware. Taking all these new releases the process is now very much simpler and the sound does not have annoying pops.

What you need

  • A Raspberry Pi
    • I got mine with a case from ModMyPi for £35
  • A wireless USB dongle (or wired ethernet connection near your Hi-Fi)
  • A micro-USB power supply (most phones use these so you might have one already)
  • An SD Card (2GB minimum) – perhaps an old one from a camera?
  • An SD Card reader/writer – often built in to a computer
  • Your music available on a linux server (e.g. CDs ripped to MP3 files) – this could be replaced with e.g. a NAS device or a Windows machine or just MP3s on your phone
  • An amplifier with a 3.5mm auxiliary input and speakers (perhaps a TV sound bar?)
  • An audio cable to connect the Pi to the amplifier
  • At least one Android device
  • Some sort of router / wireless network to connect the media server to the Raspberry Pi
  • You might need a USB keyboard and a TV/monitor to do the initial set-up (hopefully you have these lying around)

Setting up a Raspberry Pi to be a UPnP Media Renderer

The one essential thing is to get gmrender-resurrect (also known as GMediaRenderer, gmrenderer, gmrender, …) installed and working. Once that is done you will be able to play MP3s from your phone through the Raspberry Pi. GMediaRenderer is what is known as a UPnP Media Renderer: that is a destination for media files using the UPnP protocol. There is a lot of software that supports UPnP and many modern “Smart TVs” can also play your music in this way. This is also related to DLNA but I have to confess I don’t understand the interplay between these standards.

Installing the Operating System

I started from fresh, downloading the latest “Wheezy” Debian Linux distribution from the Raspberry Pi downloads page. At the time of writing this was 2013-05-25-wheezy-raspbian.zip.torrent. Following instructions on the Raspberry Pi site, I copied it onto an SD Card and turned the RPi on.

Get the Raspebrry Pi on the network

This is one of the more involved parts, which I wrote about last time. It depends on your network basically so have a look at my instructions and use Google to find some more.

First steps and updates

We now have to get everything updated to the latest versions. Note, that these instructions will get the latest software and latest firmware so you might end up with something more recent than me and therefore slightly different behaviour. To see what operating system you have:

$ uname -a
Linux raspberry2 3.6.11+ #474 PREEMPT Thu Jun 13 17:14:42 BST 2013 armv6l GNU/Linux

Do the initial configuration:

$ sudo raspi-config

You need to enable ssh access, set your password, set the hostname and reboot.

When I looked at this last time, even with my final solution of using Pulse audio there were annoying pops when music started and stopped. This issue was investigated by the Raspberry Pi developers and has now been fixed in the latest firmware.

To update the firmware to the latest drivers etc, we can use the rpi-update tool:

$ sudo apt-get install rpi-update
$ sudo rpi-update
 *** Running ldconfig
 *** Storing current firmware revision
 *** Syncing changes to disk
 *** If no errors appeared, your firmware was successfully setup
 *** A reboot is needed to activate the new firmware
$ sudo shutdown -r now

Once the RPi has rebooted you can see what version you have:

$ /opt/vc/bin/vcgencmd version
Jun 17 2013 20:49:11
Copyright (c) 2012 Broadcom
version d380dde43fe729f043befb5cf775f99e54586cde (clean) (release)

Now you need to update all the software. This can take a little while but it’s all automatic:

$ sudo apt-get update
$ sudo apt-get upgrade

Installing GMediaRenderer

Please note there are (yet again) updated instructions for installing gmediarender.

Log in to your RPi and make sure you are in your home directory. Get a copy of the gmrender-resurrect project from GitHub and enter the new directory that appears:

$ cd
$ git clone https://github.com/hzeller/gmrender-resurrect.git
$ cd gmrender-resurrect

Henner Zeller has done a great job in fixing bugs and adding features to the old gmrender project and now has a Raspberry Pi himself so has made sure the instructions included with gmrender-resurrect work very nicely. All you have to do now is follow the instructions in the INSTALL.md file (type “more INSTALL.md” to see them, or go see them online at GitHub):

$ sudo apt-get install autoconf automake libtool
$ sudo apt-get install libupnp-dev libgstreamer0.10-dev \
                gstreamer0.10-plugins-base gstreamer0.10-plugins-good \
                gstreamer0.10-plugins-bad gstreamer0.10-plugins-ugly \
                gstreamer0.10-ffmpeg \
                gstreamer0.10-pulseaudio gstreamer0.10-alsa
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
$ sudo cp scripts/init.d/gmediarenderer /etc/init.d

If you followed up to that point you will have just copied the “init script” that starts and stops the gmrender service into the /etc/init.d directory where all these scripts live. Henner’s script may need editing for your situation but it is all commented to tell you what to do. I use two Raspberry Pis in two rooms so I just changed the UPNP_DEVICE_NAME to “kitchen” and one to “bedroom”. The init script automatically generates a unique identifier (UUID) for each Raspberry Pi that you use it on so if you use more than one then they won’t clash on your network. If you need to edit the file, use “vi” or “nano” to edit the file /etc/init.d/gmediarenderer

To actually get the operating system to run the init script at the right times we need one more command and then we might as well reboot to test it:

$ sudo update-rc.d gmediarenderer defaults
$ sudo shutdown -r now

Test that GMediaRenderer is running by listing all the processes with “ps” and searching the output for gmediarender with “grep”:

$ ps aux|grep gmedia
 2279 ?        Ssl   69:52 /usr/local/bin/gmediarender -f Bedroom -d -u bd1dcf3e746aa69812943cb1d00f7ebc --gstout-audiosink=alsasink --gstout-audiodevice=sysdefault --gstout-initial-volume-db=-10

Your output won’t be exactly the same as that but as long as you get something then it’s running.

Setting the default volume

Probably the easiest way to turn the volume up is to run the alsamixer command and press the up arrow cursor until the screen looks like this:
alsamixer on a Raspberry Pi
Press the Escape key to exit. You then need to store that configuration so that it is the default when the machine is turned on:

$ sudo alsactl store

At this point you should move the Raspberry Pi so that you can plug it in to your amplifier. These instructions assume you are just going to use the 3.5mm stereo audio jack. The sound quality from that is not the best in the world but for many people it is good enough. If you want better then you can buy a USB sound card very cheaply but then the sound configuration will be different and I can’t help you on that (yet).

Checking that ALSA is fine

To summarise this part, I didn’t have to change anything even though it looked like it wasn’t working. I’m not sure what’s going on but I’ve included some diagnostic output here in case it is useful. You can probably skip on to the next section!

Check what the “sysdefault” audio sink is that GMediaRenderer is going to use:

$ aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
default:CARD=ALSA
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device
sysdefault:CARD=ALSA
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device

More information from “amixer”. This looks suspicious as it says that the playback is “Mono” when we’d expect “Stereo”:

$ amixer
Simple mixer control 'PCM',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined penum
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -1862 [79%] [-18.62dB] [on]

Confirm that the correct driver is loaded (bcm2835):

$ lsmod|grep snd
Module                  Size  Used by
snd_bcm2835            16304  1
snd_pcm                77560  2 snd_bcm2835
snd_seq                53329  0
snd_timer              19998  2 snd_pcm,snd_seq
snd_seq_device          6438  1 snd_seq
snd                    58447  6 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
snd_page_alloc          5145  1 snd_pcm

Run a speaker test:

$ speaker-test

I found this kept testing what it thought was “Front Left” and sending noise out of both speakers. There are files on the RPi that are supposed to play out of only one speaker which should be useful for testing:

$ aplay ./share/sounds/alsa/Front_Right.wav
$ aplay ./share/sounds/alsa/Front_Left.wav

I found that both of these came out of both speakers. Another speaker test showed that stereo was possible:

$ speaker-test -c 2

That resulted in sound coming from each speaker in turn! Finally I tried playing two MP3s (left and right) from a useful page and they also worked fine:

$ sudo apt-get install mpg123
$ cd /tmp
$ wget http://www.richardfarrar.com/audio/right.mp3
$ wget http://www.richardfarrar.com/audio/left.mp3
$ mpg123 right.mp3
$ mpg123 left.mp3

Once I found that MP3s were in stereo I finished the rest of the installation and found that music from GMediaRenderer was also in stereo. There’s definitely something wrong but as it doesn’t affect what we want to do I’m leaving it well alone for now.

Using your phone as a controller

To control the music with your phone I use BubbleUPnP for Android. Go to Google Play and install BubbleUPnP. It is a UPnP renderer and control point and also works with the OpenHome protocol (more on that later). In other words, it will play music (and videos and pictures) from the a media server if you have one and can also tell the Pi to play music. The free app has some limitations (not too restrictive) and you can pay £3 to have it unrestricted which is well worth it whether you need to or not if you ask me. Other UPnP controllers exist and are no doubt also available for iPhone but I haven’t tried them.

If you run BubbleUPnP and go to the “Devices” screen then you should see your Raspberry Pi listed under the “Renderers” section along with the “Local Renderer” which is the phone itself. Select the Raspberry Pi. In the “Libraries” section you should see “Local Media Server” which is the files (e.g. music) on your phone and you may see other music libraries as well if you have a media server in the house. To play music from your phone through the Raspberry Pi select the Local Media Server, choose some music from the “Library” screen and get it to play.

If someone else in the house also has BubbleUPnP installed on their phone and configured correctly then you can also choose music from their phone to play on the Raspberry Pi.

Playing music from a media server

I have all my music stored in FLAC and MP3 format on a small Linux computer that is on all the time: this is my “media server”. You may have a laptop or computer with music on which is on sometimes, or a NAS drive with music, or you might put music on a USB disc and plug that into the Raspberry Pi. All these options can be considered to be media servers.

To “serve” the music there must be a computer with software on that understands the UPnP protocol so that the phone can find it and select music from it. When you do this, the phone just tells the Raspberry Pi to fetch the music directly from the media server so it doesn’t all stream via the phone, which is good because it doesn’t suck the battery.

I’ll try to cover some of the options here. There is a big list of UPnP servers on Wikipedia.

Serving music from a Windows computer

I haven’t done this, but there are various options that are known to work:

Configure the software and it should appear in the “Libraries” list on the BubbleUPnP “Devices” screen.

Serving music using MiniDLNA on a separate Linux computer

This is what I have: a computer running Linux with the music stored on it. Again, there are many options but I have chosen to use the
MiniDLNA software. You can most likely find MiniDLNA in your package manager. For me I did:

$ sudo apt-get install minidlna

Point minidlna at your music files by editing the /etc/minidlna.conf file, e.g.:

# set this to the directory you want scanned.
media_dir=A,/mnt/usbdisc1/music/albums

# set this if you want to customize the name that shows up on your clients
friendly_name=My DLNA Server

# set this if you would like to specify the directory where you want MiniDLNA to store its database and album art cache
db_dir=/var/cache/minidlna

I also had to “sudo chown minidlna.minidlna /var/cache/minidlna” to get it to work, but YMMV.

The log for minidlna is in /var/log/minidlna.log so to see the latest messages do “tail -f /var/log/minidlna.log” and use Ctrl-C when you’ve had enough. Sometimes you need to force minidlna to rescan your media files, in which case use “sudo service minidlna force-reload”.

Serving music using MiniDLNA on the Raspberry Pi

If you have no other computer that is on all the time then you can put your music on a USB hard drive and plug that into the Raspberry Pi. The Raspberry Pi should find the USB drive automatically and mount it, making it available for use (type “mount” to see what’s available). You will still need to have the UPnP server so follow the instructions above to install MiniDLNA. You will have to set the “media_dir” config option to point to your USB drive. (Sorry this is a bit brief, I haven’t tried it.)

Serving music using a NAS

If you have a NAS on your network, or you have a Windows computer that is sharing a drive with your music on then you can mount the network drive on your Raspberry Pi and get the music as if it is on a local disc (i.e. follow the previous section). To get the network drive mounted, follow these helpful instructions.

Using multiple synchronised controllers

If you’ve stuck with it so far then you have one or more Raspberry Pis playing music from one or more media servers, all controlled from your phone. If you have more than one Android device in your house then there is one remaining problem that needs to be solved. Each BubbleUPnP controller on a phone can see the music files on the server and can tell the Raspberry Pi to play music but they will be fighting each other with no shared playlist or shared “playing now” indication.

To achieve the situation shown in the video at the start we need to use the OpenHome protocol on top of all the UPnP devices so that all your controllers (phones, tablets) share the same playlist, volume and “playing now” view. To do this we need BubbleUPnP server. Again, there are many places this last piece of software can be installed. There are instructions provided at on the BubbleUPnP server page but I’ll explain a little more. To me it makes most sense to have BubbleUPnP server running on the same machine as the MiniDLNA service.

BubbleUPnPServer on a separate Linux computer

My server runs Ubuntu so all I had to do was:

$ sudo apt-get install openjdk-7-jre-headless
$ sudo add-apt-repository ppa:bubbleguuum/bubbleupnpserver
$ sudo apt-get update
$ sudo apt-get install bubbleupnpserver
$ sudo start bubbleupnp

BubbleUPnPServer on the Raspberry Pi

Firstly install Java:

$ sudo apt-get install openjdk-7-jre-headless

Then download BubbleUPnPServer, make a place to install it and unzip the package. We’ll also install ffmpeg so that it can transcode files if necessary:

$ cd /tmp
$ wget http://www.bubblesoftapps.com/bubbleupnpserver/BubbleUPnPServer-distrib.zip
$ cd /usr/local/bin
$ sudo mkdir BubbleUPnPServer
$ cd BubbleUPnPServer
$ sudo unzip /tmp/BubbleUPnPServer-distrib.zip
$ sudo apt-get install ffmpeg

The easiest way to get the BubbleUPnPServer running is to use the provided launch.sh script, firstly making it executable:

$ sudo chmod +x launch.sh
$ sudo ./launch.sh > /dev/null 2>&1 &

That second command is running the launch script and sending all its output to /dev/null (which means it doesn’t appear on your terminal). The final “&” means it is run in the background so you get your terminal prompt back. Check it’s running like this:

$ ps aux |grep Bubb
13089 pts/0    Sl     1:18 java -Xss256k -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -jar BubbleUPnPServer.jar
13212 pts/0    S+     0:00 grep --color=auto Bubb

The log file for the service can be found in /usr/local/bin/BubbleUPnPServer/BubbleUPnPServer.log.0 so if you are still in the same directory, typing “tail -f BubbleUPnPServer.log.0″ will show you the latest messages (use Ctrl-C to stop viewing).

The problem with this approach is that the BubbleUPnPServer process will not start automatically if you reboot your Raspberry Pi so we need an init script for it. Chris Mobberley has written one just for this purpose, but you’ll need to change the DAEMON_PATH definition from “/var/www/bubbleupnp” to “/usr/local/bin/BubbleUPnPServer” to be consistent with my previous instructions. I’ve also just posted a more generic guide to init scripts that could be adapted to this case.

Setting up BubbleUPnPServer

Wherever you have installed BubbleUPnPServer you should now have it running. You need to go to the server’s admin web page to configure it. The admin page can be found on port 58050 so in your web browser go to http://hostname:58050 where “hostname” is the name of the computer running BubbleUPnPServer. If you’ve just installed it on a Raspberry Pi then you might not have a hostname that the web browser recognises. In which case use the Pi’s IP address, finding this as so:

$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether b8:27:eb:94:63:30 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.59/24 brd 192.168.1.255 scope global eth0

So in this case the RPi’s address is 192.168.1.59 (under “eth0″ as it is plugged in with an ethernet cable) and I need to go to http://192.168.1.59:58050 in my web browser. If you had it on a wireless network then the IP would be under the “wlan0″ category.

Once you have the admin page then follow the instructions from the BubbleUPnPServer website. You should see you Raspberry Pi listed in the Media Renderers tab. Tick the box to “Create an OpenHome renderer”.

Using the BubbleUPnPServer

Once you have created the OpenHome renderer then you can use it from your phones. Go back to BubbleUPnP on your phone and go to the “Devices” screen. You should now see you Raspberry Pi there twice, once as before and now also there with “(OpenHome)” after it. If you use this renderer from your phones then all instructions go via the BubbleUPnPServer and all your phones can share the same playlist etc.

BubbleUPnP for Android using OpenHome

BubbleUPnP for Android using OpenHome

Final thoughts

Finally, there is a lot more to the BubbleUPnP client and server that I couldn’t cover here and which isn’t to do with the Raspberry Pi. The client for instance can play music from internet radio stations and from Google Music. You can also set up the server so that you can access it from outside your house and by some clever configuration make it so that if you go to a friend’s house who has a DLNA renderer (lots of TVs do these days) then you can use your phone to tell your friend’s TV to play music from your server!

This solution does not create synchronised music coming from several RPi’s at once. I understand this can be done with Squeezelite (more info here).

If you have Apple devices then you might want to use Airplay, in which case look at shairport.

There is a new protocol called MagicPlay which is an open version of AirPlay. Again, I’ve not tried it but it looks very promising.

Lots of people are doing the same sort of thing. There’s a particularly good post from Mark.

I’m happy to answer questions below, but do read the massive comment thread on the previous post and the Raspberry Pi forum. I’ve tried to address some ALSA issues in another post (though please note that that was with a different Wheezy and firmware version).


2013-07-22, edit: fixed typo in alsactl command.
2013-07-23, edit: included more info on the init script for BubbleUPnP server.
2013-08-11, edit: added BubbleUPnP for Android screenshot showing OpenHome.
2014-02-20, edit: updated BubbleUPnP server version to 0.8.
2014-08-31, edit: updated BubbleUPnP server version to 0.8.3.