6 Music Radio
I had a few days off in December and decided to see if I could improve my mornings by being able to listen to Shaun Keaveny on BBC 6music in high fidelity (although I am not sure if it will make his morning programme better - but anyway, keep up the work).
Our old DAB radio sounds drab, not just because of the small speakers but also because of the poor bitrate 128kbps stream encoded in ancient MP2 (yes two). Also it looks very plastic.
What I wanted was to rebuild a couple of nice looking vintage radios (for stereo - one each side of the bed) to play the 6music 320kbps internet stream. Oh - and to have a clock and a simple control in both units. In short, a vanity project!
This is a description of how I put it all together in case anyone wants to do something similar.
Two old Roberts Radio cases (from the 70's) were used (one for each side of the bed) - the design needed to provide a speaker, display and control to each case.
A Raspberry PI was the obvious choice - providing a powerful Linux platform. Audio was provided by a Pi-DigiAMP+ - this also provided power to the Raspberry PI. Small OLED displays were connected via the I2C bus (only 4 wires including power), and a rotary controller and push switch was connected via 3 GPIO lines (again 4 wired including earth).
I considered trying to run the I2C and GPIO lines the 4 meters to the second unit but was uncertain it they would work that far, and I don't think I had the lines available (the DigiAMP+ only exposed 3). Anyway, I discovered the Arduino Uno board. This can link to the Raspberry PI via USB (also providing power) and exposes I2C and GPIO lines. Happy days!
For the speaker I just got a couple of JVC car speakers - compact 2-way, probably not so sophisticated but hey, much better than anything available in the 70s!
The most important part of an audio system must be the speakers or drivers - but budget and the size of the radio cases limit scope; I would have loved to have made a couple of 50 litre concrete enclosures holding the best drivers money could buy. Instead I had a couple of 10cm car drivers (and I was a bit worried these were too big).
The radio cases are not the same size - this really was not an accident (!), the smaller one is for the smaller bedside cabinet, of course). However for audio balance I need two identical enclosures.
Now basically there are two types of enclosures: sealed and vented (i.e. with a reflex port) and there are plenty of simulators and formula available on the Internet to calculate the correct size of the enclosure and the length of any port for tuning. However it was all pointless as either my drivers were too big or the radio cases too small - so in the end I made two identical sealed enclosure as big as I could fit, and filled them to overflowing with some washed stuffing from an old cushion (I won't mention cats, instead will leave details to the reader's imagination). I figured it would be all good if played softly - probably rather punchy but a digital equaliser should hide the worse faults. In fact, it turned out to have quite a mellow sound - lots of detail but more of a GSM rather than a Shiraz - kind of old fashioned sounding - but the streaming is noticeably better than DAB.
Worth just talking about the source - BBC 6music. The phrase ‘HD Sound’ describes audio transmission chains maintaining quality and fidelity. Radio 3 HD Sound uses the AAC-LC codec at 320kbs and has no audio signal processing applied - and carries the HD Sound label.
Although 6music is available in 320kbs AAC-LC (thank you, BBC!), the BBC states there are additional "signal processing" in the chain which means the service cannot carry the HD Sound label. Indeed there are reports that the frequency response of the audio is chopped off around 17kHz.
Now as I am over 20 (indeed over 40, err... 50), so I probably can't hear that frequency, and my speakers aren't that good - but if anyone from 6music reads this, shouldn't the Lauren Laverne "live in sessions" be streamed as HD? Perhaps some Radio 3 engineers can help ... :-)
Making the radio's was a 3 stage process - the first pulling apart the Roberts radios - and throwing away the old 70's innards. Cardboard cones and lots of hand soldering - but still a bit sad to throw away :-(
The second was getting the bits all connected up and working. Soldering is not my strong point - my advice (one bodger to another) is use a glue gun to strengthen and insulate solder joints.
And finally building (well stuffing) them into the cases to make them look nice! The hardest part to get right was to work out how to mount the displays - in the end I got some thin perspex and spray painted into a frame to hide any mess.
It was so nice to do software rather than glueing and soldering! The source code link is below, under "More Details" - remember this is hacked together to get it to all work quickly - don't look for any software architecture, be kind please.
The Raspberry PI (at least when running Raspbian) is essentially a Debian Linux machine with all the facilities you would expect - 1Gb of RAM, plenty. Python is the preferred programming language, presumably for educational reasons, although I would have thought a structured language would be better - just my age? In anycase, I just hacked what I wanted using C/C++ as I would for any Linux machine, and as I would have to use C for the Arduino this made sense to me.
The key capabilities coded were:
- Reading and playing the 6music stream. The VLC Media Player was used. It provides a library that you link to and can open and play streams, set the volume and programme an equaliser. Perfect.
- Reading the controller (see following)
- Driving the display (see following)
- Communicating to the Arduino. This really just involves opening a USB serial socket and sending / receiving some homebrew commands.
Update: Slowly improving and restructuring the software (see github) as I might as well get it in a state that others could use. New features being worked on: multiple radio channels, Spotify, scrolling displays, Web UI, Mobile app, bluetooth, and WiFi broadcasting. Maybe even an alarm clock!
The Arduino is an "open-source electronics platform based on easy-to-use hardware and software. It's intended for anyone making interactive projects". It is programmed in C++ and its special IDE runs on the Raspberry PI GUI perfectly.
The code is very simple and implemented in a loop. You can use interrupts, but why bother as it is not going to be doing anything else! (Update: I changed it to interrupts because the screen refresh is slow enough to make the volume control a bit unresponsive).
The key capabilities coded were:
- Reading the controller (see following) and sending events to the Raspberry PI.
- Driving the display (see following), displaying information sent from the Raspberry PI.
I used one of the many 0.96 128x64 OLED available on the Internet. Actually I am not 100% happy with the choice because it is very bright (not dimmable as sometimes advertised) and blue. Really a retro device should have a red display. Maybe next time I will find a full colour display. The one I went for in the end is from Amazon and has a yellow stripe of pixels that was more friendly for nighttime.
I wanted a library that was identical on the Raspberry PI and Arduino. I went for Adafruit's simple library, which Charles-Henri Hallard ported to the RPI (I had to re-enable the capability to rotate the display - included in my source link).
With the library, writing the time to display is trivial - I have 4 modes: big and blue for the day, small and yellow for dawn and dusk, a minimalist hour hand for the night, and blank for those who hate any light at all at nighttime. I spent some time thinking about how to decorate the hour hand (line) so you know which way it is pointing - but suddenly realised there was no need, after all in the middle of the night you will know it's pointing to 3 (am) not 9 (pm). So a minimalist yellow line is all you need, you don't even need a circle for a clock face.
The rotary controls look like but aren't potentiometers - instead they move round and round and the system has to count the clicks clockwise or anticlockwise to determine how far it has moved. They have two channels that click on-off out of sync. What that means is that you essentially wait for one channel to change state - and then look at the state of the other channel to determine if the control has moved clockwise or anticlockwise. In addition the button can be pressed in - which closes a 3rd channel. Each of these are connected to a GPIO line (and earth/ground). The GPIO is programmed as an input - job done.
The physical world is not so simple. I always think that a switch can be on or off, and can cleanly transition, but in reality the physical lever engages, bounces and separates, re-engages and so on until it settles down. I guess there are engineers spending their lives designing the best possible switches with minimal bounce. In analog electronics, capacitors are used to smooth the effect of the bounce, that is beyond me so I have to use software instead.
The essential approach is after any state change you wait a bit to see if any more "noise" is coming down the line. If, after waiting a bit, nothing happens we know the switch has settled down and is really engaged (or disengaged). On the other hand, if we get another change we know it was just noise and so wait a bit more. I found waiting 1 or 2 milliseconds worked well.
The Arduino code is structured as a loop (on the basis this is simple, does not rely on any complex OS facilities, and it does not matter that it wastes CPU cycles as it is not doing anything else anyway!). So all we need to do is record the time the last event happened. We loop round resetting the time as events happen until we realise that a millisecond or 2 has gone past with nothing happening - now we can process the event (e.g. sending the control event to the Raspberry PI via the USB port).
In the Raspberry PI we can't use a loop. After all it will be a bit busy doing other things (like decoding 6music) which we don't want to disturb with a loop hogging all the CPU. Instead we use interrupts that trigger our interrupt handlers when a GPIO line changes state. But how do we wait 2 milliseconds? Simple really - we need to use the multithreaded and timer capabilities. Basically we have a thread handling the rotary controller. The interrupt handlers signal the thread. And the thread waits for the signals but with a 2 millisecond timeout so it also wakes up when the rotary controller has settled down. The thread writes decoded events to a private socket that the main program thread can in turn read (it has to also read events from the Ardunio slave socket - so the same for it really).
Pull Up Resisters
As I said in my software world, I think that a switch/GPIO line can be on or off. I wire a switch to link the GPIO line to Ground if the switch closes. What is wrong with that?
Well it seems that if a GPIO is not explicitly wired to GND (low) or 5v (HIGH) then it randomly changes value - I guess quite reasonable. What you need to do is wire a "pull-up" resistor to 5v so that when the switch is open the GPIO goes to the HIGH state (thanks to the resistor). You also have pull-down resistors if you are working the other way round. The bad news is soldering in all the resistors is messy and tedious. The good news is that real engineers obviously agreed with me and GPIO chips (used in the Raspberry PI and Ardunio) have programmable resistors built in. So all you have to do is, when you configure the GPIO line to be an input in your setup code, remember to configure the resistor as well.