Friday, April 4, 2014

LED VU Meter using the MyPiShop 8x8 PiMatrix

One of the first addons I got for the Raspberry Pi was the 8x8 PiMatrix, which was a lot of fun to play with. Who doesn't enjoy making LEDs blink? I also wanted to use the PiMatrix for some sort of music visualization, but I'll get to that in a little bit. 

Below are the links for the PiMatrix, assembly instructions and the original example code. The expansion board under the removable LED matrix is also usable by itself for an additional 16 bits of I/O: - Assembly - Test Routines - Toolkit - Scrolling Text

I rewrote Bruce E. Hall's sample code library into a more object-oriented form, creating a LEDMatrix object and making the variables and functions in the original into properties and methods of that object.  I also changed a few methods to accept more parameters such as variable delay times, so the timing and speed of individual routines can be changed without affecting everything. 

All my code is at the Bitbucket link below:

This example makes an explosion pattern that starts in the center and moves out to the four corners of the display:

I use a similar script as a visual indication that the Pi had lost wifi connection; when my wifi check script fails to ping my router, it launch a python script that causes lots of blinking to get my attention.

So now I wanted to use the PiMatrix for some sort of music visualization, but I also wanted it to work with my existing mpd setup instead of using other players and music library management as in the other examples found online. From using the ncmpccpp client I also knew that mpd could already create a fifo output file usable for this purpose:

Fortunately, there was an example on stackoverflow for doing almost exactly what I needed. In XBMC, I prefer the simpler oscilloscope music visualization instead of the more complex ones, so I'm OK with just displaying the volume peaks instead of the fancier (and more CPU-intensive?) spectrum analyzer.

In the linked code example the mpd.fifo is read using audioop functions:

Below is the complete code for the which is also in the Bitbucket repo linked above. Basically, here's what happens:

* The peak stereo volume is read from the fifo
* That value is divided by some "magic number" I arrived at by trial and error to get a 0-to-8 to correspond to a row index, and that new value is appended to the head of a deque
* Loop through the deque and light up the individual LED corresponding to the loop index and value stored in the deque
* Pop the oldest value off the tail end of the deque
The visual effect should be of a "wave" moving left to right.

I've since read that a deque is not the fastest data structure to use for this, but it behaves exactly the way I needed it to without my having to jump through hoops to manage the list of values read from the fifo, and the performance seems fine.

Another thing to note is that in my mpd.conf I changed the location of my mpd.fifo to /var/tmp/mpd.fifo (/var/tmp/ is mapped to RAM in my fstab); I believe the example config in the "Enabling visualization" link above uses /tmp/mpd.fifo instead. So what's defined in your mpd.conf needs to match the file the script below will try to read.

Since this is on my dedicated print server mpd music player, I have the script auto-start by adding it to /etc/rc.local

I've watched the Pi's CPU usage while this script is running and it doesn't seem to be bad - usually around 25% and sometimes spiking to around 40% for very short periods. In fact I've been ssh'ed into the Pi and editing other code in nano while it's running and it's been very responsive.

One final problem to solve is having different scripts that can use the PiMatrix not interfere with each other. This led_vu_meter is always running, and as I mentioned above there's another script that gets triggered when the wifi connection goes down. Sometimes I've also accidentally started more than one instance of these scripts. The multiple processes sending different instructions to the LEDs at the same time is probably not a good thing, and at the very least leads to garbage pixels on the display. I'll probably resort to one "master" script that has logic to determine what should be displayed.