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:
* http://w8bh.net/pi/PiMatrix1.pdf - Assembly
* http://w8bh.net/pi/PiMatrix2.pdf - Test Routines
* http://w8bh.net/pi/PiMatrix3.pdf - Toolkit
* http://w8bh.net/pi/PiMatrix4.pdf - 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:
from LEDMatrix import LEDMatrix from time import sleep delay=0.06 if __name__ == '__main__': lm = LEDMatrix() lm.ORIENTATION = 0 // 0 = power jack on left, 90 = power jack down, // 180 power jack on right, 270 power jack on top lm.enableLEDS() try: while True: lm.setPattern(0b00011000,0b00011000) sleep(delay) lm.setPattern(0b00111100,0b00111100) sleep(delay) lm.setPattern(0b01100110,0b01100110) sleep(delay) lm.setPattern(0b11100111,0b11100111) sleep(delay) lm.setPattern(0b11000011,0b11000011) sleep(delay) except (KeyboardInterrupt, SystemExit): lm.turnOffLEDS() #lm.disableLEDS() finally: # ensure that the leds turn off lm.disableLEDS()
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 led_vu_meter.py 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.
# # "VU" meter display based on code from Stackoverflow # * http://stackoverflow.com/questions/21762412/mpd-fifo-python-audioop-arduino-and-voltmeter-faking-a-vu-meter # using the PiMatrix LED # * http://mypishop.com/Pi%20MATRIX.html # * http://w8bh.net/pi/PiMatrix1.pdf # * https://bitbucket.org/rvalbuena/pimatrix import os import audioop import time import errno import math from collections import deque from LEDMatrix import LEDMatrix if __name__ == '__main__': our_pid = os.getpid() print our_pid #Open the FIFO that MPD has created for us #This represents the sample (44100:16:2) that MPD is currently "playing" fifo = os.open('/var/tmp/mpd.fifo', os.O_RDONLY) # note: /var/tmp is mapped to RAM in /etc/fstab lm = LEDMatrix() # the "fastSet" routines ignore orientation # but I'm using the Pi hdmi-connector side down and apparently that's "this side up" lm.ORIENTATION=0 lm.enableLEDS() try: # create our buffer for the display values # there are probably faster data structures # but this seems OK for now vol_queue = deque() # initialize it with zeros for p in range(0,7): vol_queue.append(0) while 1: try: rawStream = os.read(fifo, 4096) except OSError as err: if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK: rawStream = None else: raise if rawStream: # I'm really only interested in the stereo peak #leftChannel = audioop.tomono(rawStream, 2, 1, 0) #rightChannel = audioop.tomono(rawStream, 2, 0, 1) stereoPeak = audioop.max(rawStream, 2) #leftPeak = audioop.max(leftChannel, 2) #rightPeak = audioop.max(rightChannel, 2) #leftDB = 20 * math.log10(leftPeak) -74 #rightDB = 20 * math.log10(rightPeak) -74 # 'magic numbers' that will produce 0..8 for the led matrix # note that reducing mpd volume via software mixer (mpc volume XX) # will also reduce these values, so better to use the speaker vol knobs peak = (stereoPeak/4000)-1 #peak = (stereoPeak/3800)-1 vol_queue.append(0 if (peak < 0) else peak) # print vol_queue for m in range(0,8): # this produces a decent sine wave like pattern # fastSetLED ignores orientation # lm.fastSetLED(vol_queue[m],m) lm.setLED(vol_queue[m],m) # removing this pause actually produces a better looking # (more filled in) wave and does not seem to kill performance # (I think there are "sleeps" in some of the LED Matrix routin$ # time.sleep(0.01) # remove from the head of the deque vol_queue.popleft() # clear the display so that when we reach the end of the fifo # (mpd stops playing/reaches the end of the playlist) # we don't have anything left on the display lm.turnOffLEDS() except (KeyboardInterrupt, SystemExit): lm.turnOffLEDS() #lm.disableLEDS() finally: # ensure that the leds turn off lm.disableLEDS()
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.