Intro: BeagleBone Black Wifi Radio
This is a project I made two years ago, because I wanted to listen to online radio stations. The BeagleBone Black(BBB) doesn't have a built-in audio output and before the wireless variant of the BBB was available I had to use a USB dongle for Wifi. Because of this I designed an add-on board(cape in the beaglebone world) to handle audio output from the BBB.
All of the files for this design are available on github here:
I recommend you download it to follow along with this instructable. The webinar above covers more of the design process so it's worth a watch.
My goal in this instructable is to highlight some of traps I ran into as I was working on this project, so that you can avoid them on your own projects. Sometimes the simplest things are the ones that take the longest to solve, my hope is that my experiences can give you the courage to try a project that goes beyond your comfort zone. In doing so I learned a lot and that's what I'll be sharing with you. Let's get started!
Step 1: BBB Wifi Radio: Hardware Design Using EAGLE
I've been using EAGLE for almost 10 years. I feel very comfortable with it and thanks to Autodesk's acquisition it's capabilities have greatly increased( Full disclosure: I'm a product support specialist for EAGLE at Autodesk). There are many features I wish I would have had when I originally designed this project (Obstacle Avoidance, how did I ever route without you?).
See the schematic(Figure 1) above.
The star of the show here is the PCM5102 Audio DAC chip. It converts the I2S stream coming out of the Beaglebone Black and converts it to a line level audio signal on the output. That signal can then be fed to a set of powered computer speakers for playback. The chip itself is pretty straightforward to use you feed it a serial stream and out comes the audio.
IC5 is worthy of some explanation for some reason the BBB outputs an inverted BCK signal which messes with the chip. IC5 is an XOR gate converted as an inverter to correct the polarity of the BCK signal. This signal is coming in at approximately 25Mhz making it the fastest signal on this board. It's important to make sure the XOR gate has a low propagation delay otherwise the circuit will not work. If you stick to the part numbers specified, you will be OK. If you need to make a component substitution make sure to check the specs of your XOR gate.
The only thing difficult about using this chip is the requirement for 3 supply rails. For best performance, I used separate ground signals to try to isolate the Charge Pump and Digital return currents from the Analog return currents in order to minimize noise on the output. This creates a special design requirement on the board. See the picture above.
As you can see there are three different return planes on the bottom layer of the board(See Figures 2 and 3), each one is for a different supply. Because they are all GND, at some point they have connect. Best practices dictate that these returns should meet as close to the board power supply as possible. In the picture you can see it labeled as star ground. The goal of this arrangement is to keep noisy currents from interacting with and corrupting the more sensitive audio signal. EAGLE will consider these differently named returns connecting to be an overlap, since I did this on purpose, I just approve the error.
You'll also notice that effort has been put into keeping the traces as short as possible, this minimizes parasitic impedance and helps preserve signal integrity in your circuit. This is a good rule of thumb, keep your traces as short as possible to avoid trouble.
I was very happy when I saw that the board worked the very first time I plugged it in. I thought at that point that the worst was over and that I had finished the hard part. Boy, was I wrong...
Step 2: BBB Wifi Radio: Programming Linux
Linux is an extremely powerful tool. There is so much built-in functionality that you can leverage for your projects it's amazing. However, with great power comes a great learning curve. I use Linux desktops at work so I wasn't starting from zero, but there was so much to learn here.
First, the backbone of this project consists of MPD and MPC the interface I made for this project is basically a python GUI wrapper around these two tools. MPD stands for Music Player Daemon, it's a Linux service that can play music files and create playlists, it requires a client program to control it. The simplest client program available is MPC (Music Player Client) and it's a command line tool that sends command like play, stop, etc. to MPD. Configuring this tool wasn't too difficult and I found a good thread on how to set it up which helped immensely.
As you can see in the first step, this project interfaces a display to the BBB. The instant I connected this display to the BBB my troubles really began. When I was testing the Audio cape on it's own everything worked perfectly. As soon as I added this display my cape would no longer work. After much testing, I discovered the issue it was linked to something called a Device Tree in Linux. This was the most difficult part of the project, but it was also where I learned the most.
The idea behind a Device Tree is very simple. It's a file that describes the hardware peripherals to the Linux Kernel on the BBB at boot time. It also defines what peripherals are enabled by default. So far so good, now what happens if a peripheral you want to use isn't enabled by default? This is where another file called a Device Tree Overlay comes into play. The best example I can think of to explain this is of a projector and transparency sheets. The first sheet is the core Device Tree. When you place one transparency sheet over another whatever is on the new sheet will cover whatever is below it, however any clear space on the top sheet will let the bottom sheet show through. When you enable an Overlay it will affect the settings specifically mentioned in it but leave everything else in the default device tree alone. This is a gross oversimplification, but it's enough to under the issue.
The BBB ecosystem has a provision where add-on boards(called capes) can identify themselves and tell the BBB which overlay to use to enable the peripherals it needs to interact with the BBB. The BBB ships with several default overlays and users have the power to create their own. My board didn't use that provision, but the display cape did and the overlay it was invoking was disabling the audio output of the Beaglebone even though the display didn't use the audio output at all! After much trial and error and some help from the helpful users on the Beaglebone forums I was able to create an overlay that re-enabled the audio peripherals while still allowing the display to work.
The last problem I had to solve was getting the BBB to boot directly to my application. I didn't want to go through the LXDE desktop to start the radio. I wanted this to be a standalone project so this was important to get right. On Linux there is a few different ways to define startup behavior, init.d, systemd, sometimes cron can be used, etc. I tried a few different options, but I finally was able to solve the problem creating an autostart file inside one of LXDE's configuration directories. So in this case I used the desktop environment to define the startup behavior and it works very well.
All of the files described above along with a description for them can be found in the software folder of my repository. The last aspect of my project to describe is the Python GUI I made to make it really easy to use.
Step 3: BBB Wifi Radio: Programming Python
Python, like Linux is insanely powerful. It also has a large and thriving community of developers supporting it so it is very easy to find help and documentation is for the most part, available. Using Python and one of it's many built in modules I was able to build this GUI with only 150 lines of code. Tkinter is the built-in GUI library for Python, it's very stable and easy to use which is why I chose it for this project. It has a reputation for ugly non-native looking interfaces however recent versions of the library allow it to create native looking interfaces very easily. So if you used it years ago I definitely recommend taking another look.
The interface is simple at the top you will see information about the station and the current song being played. The station list is on the bottom left and on the right you have a volume bar with stop and play buttons. It's all touch-based as you can see in the video included below.
It's surprisingly functional for being as simple as it is. There's obviously a lot of additional functionality that could be implemented. At one point I wanted to turn this into an alarm clock but haven't had time to add that functionality. What's critically missing is an easy way to add/remove stations and to adjust wifi settings so if I ever decided to pick up the project again that's probably where I would focus my efforts.
The code for the GUI interface is in the trunk folder of the software directory of my repository. You'll find some additional python files in that folder. Mpcint.py is especially noteworthy since it contains all of the functions that communicate with MPC. It's a simple example that shows how you can use Python to call other programs and processes on your system. The other files just take care of parsing the station files to populate the available stations in the GUI.
Step 4: BBB Wifi Radio: Conclusion
This was a fun project, that at first intimidated me. I've always defined myself as a hardware guy (I still do) but I wanted to see if I could improve my knowledge of Linux and programming. I would say that goal was met with this project. It was so satisfying to see my PCB work on the first try and the first time it all came together was a moment of jubilee especially after all of the fighting with device tree overlays. Here are a few tips I'd like to share that I hope will help you in your future projects.
1. When tackling something new, it's important to do as much research as you can. Looking at the readme file of my repo you can see that I got a lot of information from various sources and all of it helped me to get the project done. In that research I was also able to find inspiration for other projects so it's a win win.
2. Take your time and double check. There's nothing more annoying then having to respin a board because you got a component footprint backwards or you crossed your tx and rx lines. Some people take this as a given, but I find it wasteful. Be smart, don't just fire off your design for prototypes as soon as you finish. Take a few moments to review everything and make sure you didn't miss something obvious. This will avoid a lot of frustration when your boards arrive.
3. Use forums and ask for help. Major props to the Beaglebone community, they were very attentive and thanks to their help I was able to get the overlay sorted.
4. "Continous improvement, is better than delayed perfection"- Mark Twain. Truer words were never spoken, I tend to fall victim to feature creep often and this saying helps me avoid it. At times during this project I wouldn't make progress because I was trying to get everything perfect. It's good to be thorough, but at some point to make progress you have to limit your scope if you want to get anything done. That's what happened with the alarm clock feature I mentioned in the previous step.
I hope you guys enjoyed this little trip down memory lane with me. This is my first instructable so any comments and suggestions are welcome.