PiBox: XM radio app (PiXM) and wifi scanning in PNC


Back in October I started work on a new app for PiBox: an xm radio player.  The hardware is an XMPCR, a device sold in the early 2000's that connected over USB to a PC.  The device didn't last long.  I think there was some concern that people would steal the signals and rebroadcast.  Something silly like that.  But they only cost around $45 each back then.  I got two, one each for my wife and I.  As usual I didn't have PC software for Linux so I wrote a GTK+ wrapper around a Perl script called OpenXM that let me manage the radio.  That project was called XimbaRadio.  I wrote an article about it for Linux Journal.

Flash forward to Halloween 2016 and the Maker Faires in Colorado Springs.  I got an itch to add some kind of audio playback device to go with the video player for PiBox Media Center.  I have lots of audio ripped at home but it turns out writing a database handler for the ID3 tags was harder than doing the video lookups on TheMovieDB or the TheTVDB.  So I've punted on playing my music for now.  But the xmpcr's were still around in a box in my office.  So I dug them out and started digging in.

PIXM

pixm

PiXM is an XM Radio app for PiBox. It manages an XMPCR connected over USB to the Raspberry Pi.

The first thing I did was pull out the old Perl scripts.  There is little support for scripting in PiBox because scripting languages are not my thing.  Oh, I use them plenty.  But I want PiBox Media Center to show what you can do with plain ol' C.  Scripting is for kiddies.  PiBox is for grown ups.  Anyway, the perl scripts still work after all these years and I was able, after re-signing up the devices with SiriusXM, to use them to listen to pretty much any channel with the device connected to my laptop (not PiBox).  The devices have an ordinary audio plug for output – there is no playback through the PC.  But that's not a major issue to me.  I can always route the output back into the Pi and manage it that way if I really need to.

So the devices still worked.  Now I needed to look at my old GTK+ code.  It also worked.  Badly.  Not because the libraries had changed but because it wasn't a well written app and wouldn't fit into the minimalist design of the PiBox Media Center UI.  So I decided to start over.  And the first thing I wanted to do was write my own serial protocol management to avoid calling Perl scripts.  After all, the device's serial protocol was very simplistic and there really wasn't much code to port.  There were even some example implementations.

Work on the protocol went quickly and I was able to get data into and out of the device without much trouble.  The next step was to implement a UI.  The UI needed to adhere to the same basic usage principles of the video player which uses just the arrow, tab, ESC and ENTER keys for navigation and selection.  This wasn't as hard as it seemed.  I created two lists, one for categories and one for channels.  Choosing a category showed the list of channels in that category.  Tabbing between category and channels would display all channels.  Searching used the same builtin GTK+ mechanism for trees (just type some letters).  Easy peasy.  Unlike the video player, however, there are no graphics to display on the right side of the UI.  Instead, it's just a set of four fields for channel, category artist and title.

In testing this layout I discovered some basic problems.  The first was that the serial device, /dev/ttyUSB0, is not accessible by default.  I'm using mdev in PiBox and didn't set it up properly to give non-root users access to the device.  But before I remembered that was the problem (the app is not privileged so runs as a non-root user so I need to fix the mdev setup scripts)  I added a device state icon to the menu bar.  This bar is drawn with Cairo and is where you see the tab options for Category and Channels.  The device state icon is green for available and red for unavailable.  Makes it easier to identify hardware problems.

pixm-controls

PiXM Controls: The Categories and Channels are selected using the Tab key. The device status icon is green, showing the device is available. The Mute icon is not set. Pressing M will toggle the mute state of the device's audio output.

I also noticed that the device serial protocol allows for muting the output.  Seemed like it might be nice to add that feature, so I added one additional key, M, to toggle the mute on the XMPCR.  This violates my desire to keep all PiBox Media Center ui components (other than the PiBox Network Config app) to just the arrows, Tab, ENTER and ESC key.  But since PiBox is using those nice FAVI keyboards, I figured what the heck.  Whose gonna be annoyed?

The majority of the app is plain ol' GTK+ 2 widgets.  The control bar is a drawing area with Cairo doing custom rendering.  I use Cairo for much of the PiBox UI: PiClock, the video player (aka VideoFE) and the launcher use it extensively.  Like the video player, the PiXM app does simple on-demand drawings in the drawing area.  This isn't exactly efficient or by design of GTK+.  After working on the wifi scanner (see below) I realized I should have written my own widget to better deal with async issues inside the main GTK+ loop.   PiClock has such as widget, but it needs additional work based on lessons learned on the wifi scanner.

Wifi Scanner for PiBox Network Config

pnc-scanner

The wifi scanner is implemented as part of the PiBox Network Config app. It should probably be set as optional since the app can be used to configure wired ethernet too.

One of the major lessons learned from the Maker Faire was a need to solve my problems with PiBox acting as an access point for a local network of PiBox players.  At home I had a setup of three PiBox Players playing movies from a PiBox media server.  This worked fine there.  But taking the same setup to the library (and Barnes and Noble for a separate Maker Faire event) showed that wifi performance was unacceptable. 

I've done lots of research on possible causes for poor wifi performance.  I first thought it was a bad Samba configuration since videos are accessed by players from a Samba share on the server.  That helped, but didn't solve the problem.  Then I found that images used in the video player browser were huge.  So I scaled them back and that helped a bunch with browsing the list of available videos.  But it didn't solve the issues related to video playback.

So then I dug into wifi itself.  I found that channels 1, 6 and 11 are typically the default channels in routers because they apparently don't overlap other channels as much the other channels overlap each other.  Also, signal strength is important since choosing a channel where your signal strength is better than other networks on the same channel will help with throughput.   Choosing a channel with fewer other networks using it requires knowing which networks are using which channels.  Same it true for signal strength.

Which brings me to the discovery of Kevin Yuan's Wifi Analyzer for Android.  What a cool little app.  And that's coming from a guy who doesn't use ANY apps.  It shows an animated graph of wifi networks in the local area.  So I looked into how you get the data for this kind of graph.  Turns out you can get everything you need by looking at /proc/net/wireless and using iwlist and iw.  More easy peasy.

pnc-scanner-only

The scanner shows the set of SSIDs along with their channels and signal strengths as text on the right. The graphs are filled with semi-transparent coloring so the text can be read even of the graphs extend into the text area.

With the data gathering solved, I needed to integrate a UI and a way to get the gathered data into the UI.  One problem to solve is how to choose separate colors for each network.  In the end I used the golden ratio.  Next is how to get the data, which can take several seconds to acquire, without interfering with the UI.  So I put the data gathering into a background thread that made calls to the pnc (PiBox Network Config) library.  The data was stored in that thread and was made available to the UI using mutexes. 

I first made the UI act like the control bar of PiXM but that didn't work.  The periodic updates caused lockups in the GTK+ main loop.  So I switched to writing my own widget, just as I'd done with PiClock.  This widget is far more complex and in doing the port I found bugs in PiClock.  But using a widget solved the problem of GTK+ main loop lockups. 

Drawing the graphs is done with Cairo.  If you look at the graph it looks like a bunch of parabolas.  At first I looked into how to graph these.  But the parabolas needed to be closed on the wide end in order to fill them.  That's when I realized the trick to graphing in Cairo:  using masks.  There was an example of how to create an ellipse by using transforms to fit the ellipse in a box.  The transform placed the center of the ellipse on the channel axis with the left side drawn off the left edge of the widget, making it only partially but annoyingly visible.  The fix to that was to mask off the area to the left of the axis and THEN draw the axis.  Cairo layers the drawing commands so the mask cuts out the left side of the ellipses and then the axis is drawn over the mask.  More easy peasy.  After many hours trying to find the trick.  Silly me.  It was in the documentation.  The last place one would look.

You might wonder why the graphs are drawn left to right instead of bottom to top.  Simple:  space.  I have lots of vertical space here but not much horizontal space.  So I just draw left to right.  Seems okay to me.  Comments welcome.

What's Next

I need to return to PiClock and clean up it's widget, adding custom layers to the Cairo rendering to allow for custom themes for the background, hands and overlay.  The same needs to be added to the launcher so I can do things similar to what you see with the roku front page.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.