Looking for a first project to try out on a Raspberry Pi, I though what better than a Spectrum Analyzer? (Sometimes this display is erroneously referred to as a graphic equalizer--that let's you change the sound, not display it)

I was able to get it doing 2048pt FFTs and decoding mp3s in real time, and while that shouldn't be hard on a Pi considering plenty of 8bit uCs have been made to do real time FFTs, everything is done in Python, which makes it convenient (for me) to eventually add control via a web browser, sms, and other things. There's not much room to spare, though, it chokes when I move the mouse.

Most of the code to do this is already available, my goal here is just to roughly document the steps to get this working, from the perspective of a first time Pi user. I also made some tweaks to the FFT analysis chunk to speed things up a bit.

Hardware: (thanks for the toys Adafruit!)
  • RasPi -- I think mine is running wheezy, Raspbian, ver 3.10.19
  • 15ft (1m) RGB LED strip, $125 (~160 leds) controllable via SPI, built in PWM control, you just send updates--very nice. I'm just using one strip wrapped around to form 5 columns, and writing to different segments of the strip. This way I just have three wires connected to the Pi: ground, SPI Clock and SPI Data.
  • 10A 5V power supply to drive the LEDs, $25, you could probably power the Pi with this, too.
  • Those are the essentials, but you'll probably want a bunch of other stuff:
    • USB WiFi adapter $7.61(RTL8188CUS chipset drivers are built in to wheezy OS!). I used these instructions to get it working.
    • SD card (I got an 8GB one)
    • USB Power speakers $10
    • Power USB Hub to plug in keyboard, mouse, wifi,
    • Some cell phone charger to power the Pi via it's USB power connector
    • ethernet cable to connect to internet thru a laptop pre-wifi
    • USB keyboard, mouse, HDMI monitor
    • wire, some female-to-female jumper wires
  • I mostly just used this awesome Pi-based xmas lights controller code from Chris Usey,Todd Giles and Ryan Jennings. It's a full command center for orchestrating xmas lights to audio (wav, mp3, etc). Their code lets you setup playlists and turn on and off 120VAC power based on frequency bands. You can even vote on songs through SMS messages! Their code looks at frequency bands in the music, and if the sound crosses a threshold, it turns a GPIO pin on. I changed the code to display the actual frequency band level on an RGB LED strip, rather than just having an on / off threshold. Based on similarities in the code, I suspect they got their FFT processing code from this python real-time FFT demo.
  • Python control of LPD8806 RGB LED strip via SPI.
  • This isn't related to this project, but I used the Geany IDE for coding.

Step 1: Connect LED strip and setup RGB LED software

Rather than deal with separate LED strips for each column, I just looped a single 152 LED strip back and forth, securing it with zip ties to a baby gate. This wastes some LEDs as I'm not displaying anything on the turns, but you could avoid the LED waste by cutting the strip and soldering wires between the columns.

A good diagram for connecting the RGB LED strip to the RasPi can be found at adafruit's site.

Solder the 5V, ground, Clock and Data lines to the Input end of the LED strip, and connect to the Pi as shown at the link or in my pics. Be sure to connect the ground of the 5V supply to the ground of the Pi!

Grab the software and follow the instructions for getting the Pi able to output to SPI. It's important that you use the hardware SPI because any bit-banging approach won't be fast enough.

sudo raspi-config
to enable hardware SPI (follow instructions at git page).

I added the install directory to my PYTHONPATH in bashrc so I could call the functions from anywhere.
inside .bashrc:

export PYTHONPATH = $PYTHONPATH:/home/pi/RPi-LPD8806-master

test out that the strip works by running the example code:

python example.py

The xmas light code we're going to download later wants to run as root, and when you run things with a sudo in front, the environment variables, specifically, PYTHONPATH aren't transferred.

I had to edit /etc/sudoers by typing

sudo visudo

and then added at the bottom

Defaults env_keep+=PYTHONPATH

the first line is something we'll need for the xmas light package to be installed later. These make sure those environment variables stick around when you run things as sudo.

To test that you have it setup right, close the terminal and re-open, then type

sudo python
from bootstrap import *

that should turn on the first 10 LEDs.

A final step is to make some modifications to speed up writing to the strip.

Inside ledstrip.py, make sure use_py_spi = True in the def __init__ line

def __init__(self, leds, use_py_spi = True, dev="/dev/spidev0.0", driver="LPD8806"):

Now inside LPD8806.py, we're going to change the SPI speed to 16MHz

if self.use_py_spi:
import spidev
self.spi = spidev.SpiDev()
self.spi.max_speed_hz = 16000000
print 'py-spidev MHz: %d' % (self.spi.max_speed_hz / 1000000.0 )

That print statement is there just to make sure everything gets set correctly.

One final change to the LPD8806.py file is in the update() function. For whatever reason, I noticed led.update() was taking a long time, upwords of 25ms. To get a good visual effect, I wanted my entire analysis and display loop to run at 20Hz, or 50ms per loop, and burning half that time waiting for the LED strip wouldn't work. And strangely, at 16MHz, it should have taken less than a ms. 3 bytes per led, 152 LEDs, (3 * 152 * 8 bits / 16M) = .2ms! (not 25ms) When I put a scope up to the SPI port, each byte was coming out at 16MHz, but there was a 160uS pause after each call to self.spi.xfer2(). My solution was to collect the entire string of bytes into a buffer and only call self.spi.xfer2() once:

def update(self, buffer):
temp_buffer = []
if self.use_py_spi:
for x in range(self.leds):
temp_buffer = temp_buffer + [i for i in buffer[x]]
#self.spi.xfer2([i for i in buffer[x]])
self.spi.xfer2([0x00,0x00,0x00]) #zero fill the last to prevent stray colors at the end
self.spi.xfer2([0x00]) #once more with feeling - this helps :) time2 = time.time()

Writing out to 152 LEDs at 16MHz should take no time--you should see the last LED change at the same time the first one does

Some other people have struggled with this and got around it, check out the amazing POV video effect with a single vertical LED strip (sending data at 8MHz thru 20ft of cat 5!):

<p>Nice! I'm thinking about using this as home decoration, maybe behind matte glass. Could you make it respond to outside sound, with a microphone? Or maybe wiring a stereo output to it?</p>
<p>Is there a way that I can invert the output to the led's so they are inverted..like this</p><p>https://www.youtube.com/watch?v=OZyQ-g3klVU</p>
<p>very nice, thanks, it would be cool to have this with the piglow </p>
<p>Hi Scott,</p><p>Thanks for providing a guide.</p><p>Is it possible to use your code to drive WS2801 Leds?</p><p>They are individually addressable like the LPD8806 strips that you used.</p>

About This Instructable




Bio: Interested in soldering techniques, electronic music instruments, arduino.
More by CuriousInventor.com:Raspberry Pi Spectrum Analyzer with RGB LED Strip and Python Display Live Txts on Costume at Party w/ Scrolling LED Belt Buckle Control Ikea Dioder LED Strip with Arduino + 16X PWM LED Fader Board 
Add instructable to: