More Fun With the AllPixel and TinyTiM

As a follow-on to our previous article on the TiM and TinyTiM displays from WyoLum, I wanted to demonstrate some of the flexibility of the AllPixel in terms of how well it can pair with already existing products. The TinyTim is an as-yet-unreleased product from WyoLum, but it will be dropping soon. It’s really a slick board, consisting of 64 WS2812 LEDs arranged in an 8×8 grid. There are plenty of connections on the back of the PCB to easily wire together multiple displays, in either parallel or serial data transfer configurations. For this build, just one was used, and it was turned into a stand-alone USB LED Matrix.

TinyTim_RasPiAPlus

The innards of the device are, of course, a TinyTiM and an AllPixel. The enclosure is a cheap shadow box-style frame I picked up from a local crafts store for a few bucks. Behind the glass front is a sheet of regular printer paper. This worked very well as a diffuser, even on the rather low max brightness setting used in the code.  Taped to the sheet of paper is the TinyTiM/AllPixel “assembly.” Sorry I don’t have a picture of that, but I used clear packing tape on the four corners of the TinyTiM to attach it to the paper. There’s some cardboard and a paperboard backing that hold the paper/assembly pressed against the glass.

TinyTim_AllPixelMounted

One of the optional features of the AllPixel is the ability to add a protection diode, which enables the connected LEDs to receive power directly from the USB port. Obviously, you are limited to a few hundred milliamps of current. This translates to a small number of LEDs running at full brightness or a slightly larger number running at lower brightness. I’ve opted for the later on this build, with the max brightness fixed in code to 1/4 power (64 out of 255).

I first used an external power supply to test the animations so I could get a precise reading of the current drawn. It peaked at about 300mA for a second or two. Everything else was well below that. The AllPixel itself only draws a few milliamps when running, so I was well within the limits for a standard 500mA USB port. Once I was happy with the current draw, I removed the external supply, installed the protection diode to enable USB power, and closed the box up.

TinyTim_Rear

TinyTim_Front

So basically what your left with is a slick-looking 8×8 USB LED matrix. Using the BiblioPixel Python library, which directly supports the AllPixel, coding up animations is not to complex, even for a non-Software Engineer such as myself. Since it’s Python-based, it can run on a wide variety of platforms. I happened to pick up a Raspberry Pi A+ not too long ago, and I figured I’d give it a whirl driving the display.

TinyTim_Final
Of course, it works a treat. No muss, no fuss, easy to set up and take down for other applications.

Many thanks to the folks at WyoLum for letting us test drive the TinyTiM. I’ll definitely be picking up a few more once it goes on sale to the public.

If you’re interested in the AllPixel, our Kickstarter has ended, but to be notified as soon as it is available for purchase, follow us on Twitter and sign up for our newsletter.

Thanks for reading!

-Dan

I’m Making a Note Here… HUGE SUCCESS!!!

KermitFlail

Thank you SO MUCH to all of our amazing backers! We couldn’t be happier with how well the AllPixel campaign went!

Now… the hard work begins. We’ve already started working with Seeed Studio to begin the manufacturing process and hope to have the rewards out to everyone even before our April timeline.

For those who might have missed the deadline or think they may want more, fear not. We plan on having more AllPixel and PowerTap kits available for sale through Seeed and distributors shortly after the backer rewards go out.

This is only the beginning. Stay tuned!

Happy Making!

Binary Epoch Christmas Tree

So I have 15 meters of LPD8806 programmable LED lights on my fake Christmas tree. But I feel I can up the Nerd quotient even more.

Inspired by the Binary Epoch Clock kit, I added an animation to the tree that turns it into a giant unreadable binary clock. The lights are controlled by a RasPi running the BiblioPixel library, with the AllPixel making the hardware interfacing a snap.

Unix epoch time (number of seconds since 12:00am Jan 1 1970) is represented in binary notation. The strand of lights shows 32 places. Green represents a ‘one’, red, a ‘zero’. The top of the tree is the Least Significant Bit, the bottom the Most Significant Bit. There is an option to reverse the order of the bits (so that I could make the first bit be at the bottom of the tree instead). Once I clean up the code a bit, I’ll incorporate this into the BiblioPixel source code as a strip animation example.

In closing, my Christmas tree is an unreadable binary clock. Your argument is invalid.

-Dan

 

 

FFT Audio Animation with BiblioPixel and the AllPixel

We showed off this fun little piece of code in the AllPixel Kickstarter introduction video, but never really got into the details of how that audio animation really worked.

The animation in the introduction video and the video above uses a Fast Fourier Transform to analyze the audio coming into the computer’s mic/line-in and generate an intensity map for various frequency ranges in the audio signal. FFT is some really hardcore math, so instead of trying to explain the details of how it, and the python code, works see this post (and it’s links) on the blog of Scott Harden. His code was modified to provide the correct number of data points for a given matrix size and to work with the BaseMatrixAnimation class in BiblioPixel.

import numpy
import struct
import pyaudio
import threading
import struct
from collections import deque

from bibliopixel import LEDMatrix
from bibliopixel.animation import BaseMatrixAnim
import bibliopixel.colors as colors


class Recorder:
    """Simple, cross-platform class to record from the microphone."""
    
    def __init__(self):
        """minimal garb is executed when class is loaded."""
        self.RATE=48100
        self.BUFFERSIZE=2**12 #4069 is a good buffer size
        self.secToRecord=.1
        self.threadsDieNow=False
        self.newAudio=False
        self.maxVals = deque(maxlen=500)
        
    def setup(self):
        """initialize sound card."""
        #TODO - windows detection vs. alsa or something for linux
        #TODO - try/except for sound card selection/initiation

        self.buffersToRecord=int(self.RATE*self.secToRecord/self.BUFFERSIZE)
        if self.buffersToRecord==0: self.buffersToRecord=1
        self.samplesToRecord=int(self.BUFFERSIZE*self.buffersToRecord)
        self.chunksToRecord=int(self.samplesToRecord/self.BUFFERSIZE)
        self.secPerPoint=1.0/self.RATE
        
        self.p = pyaudio.PyAudio()
        self.inStream = self.p.open(format=pyaudio.paInt16,channels=1,rate=self.RATE,input=True, output=False,frames_per_buffer=self.BUFFERSIZE)

        self.xsBuffer=numpy.arange(self.BUFFERSIZE)*self.secPerPoint
        self.xs=numpy.arange(self.chunksToRecord*self.BUFFERSIZE)*self.secPerPoint
        self.audio=numpy.empty((self.chunksToRecord*self.BUFFERSIZE),dtype=numpy.int16)               
    
    def close(self):
        """cleanly back out and release sound card."""
        self.p.close(self.inStream)
    
    ### RECORDING AUDIO ###  
    
    def getAudio(self):
        """get a single buffer size worth of audio."""
        audioString=self.inStream.read(self.BUFFERSIZE)
        return numpy.fromstring(audioString,dtype=numpy.int16)
        
    def record(self,forever=True):
        """record secToRecord seconds of audio."""
        while True:
            if self.threadsDieNow: break
            for i in range(self.chunksToRecord):
                self.audio[i*self.BUFFERSIZE:(i+1)*self.BUFFERSIZE]=self.getAudio()
            self.newAudio=True 
            if forever==False: break
    
    def continuousStart(self):
        """CALL THIS to start running forever."""
        self.t = threading.Thread(target=self.record)
        self.t.start()
        
    def continuousEnd(self):
        """shut down continuous recording."""
        self.threadsDieNow=True

    ### MATH ###
        
    def fft(self,xMax, yMax):
        data=self.audio.flatten()

        left,right=numpy.split(numpy.abs(numpy.fft.fft(data)),2)
        ys=numpy.add(left,right[::-1])

        #FFT max values can vary widely depending on the hardware/audio setup.
        #Take the average of the last few values which will keep everything 
        #in a "normal" range (visually speaking). Also makes it volume independent.
        self.maxVals.append(numpy.amax(ys))

        ys = ys[:xMax]
        m = max(100000, numpy.average(self.maxVals))
        ys = numpy.rint(numpy.interp(ys,[0,m],[0,yMax-1]))
        return ys
 
class EQ(BaseMatrixAnim):

    def __init__(self, led):
        super(EQ, self).__init__(led)
        self.rec = Recorder()
        self.rec.setup()
        self.rec.continuousStart()
        self.colors = [colors.hue_helper(y, self.height, 0) for y in range(self.height)]

    def endRecord(self):
        self.rec.continuousEnd()

    def step(self, amt = 1):
        self._led.all_off()
        eq_data = self.rec.fft(self.width, self.height + 1)
        for x in range(self.width):
            for y in range(self.height):
                if y < int(eq_data[x]):
                    self._led.set(x, self.height - y - 1, self.colors[y])
        # x = 0
        # for y in eq_data:
        #     self._led.drawLine(x, self._led.height - 1, x, self._led.height - int(y), colors.hue_helper(int(y), self._led.height, 0))
        #     x += 1
        self._step = amt
            
#Load driver for your hardware, visualizer just for example
# from bibliopixel.drivers.visualizer import DriverVisualizer
# driver = DriverVisualizer(width = 24, height = 24, stayTop = True)

from bibliopixel.drivers.serial_driver import *
import bibliopixel.gamma as gamma
w = 16
h = 16
print "Pixel Count: {}".format(w*h)
driver = DriverSerial(LEDTYPE.NEOPIXEL, w*h, c_order=ChannelOrder.GRB, gamma = gamma.NEOPIXEL)

#load the LEDMatrix class
from bibliopixel.led import *
#change rotation and vert_flip as needed by your display
led = LEDMatrix(driver, rotation = MatrixRotation.ROTATE_0, width = w, height = h, vert_flip = False, serpentine=True)
led.setMasterBrightness(255)
import bibliopixel.log as log
#log.setLogLevel(log.DEBUG)

try:
    anim = EQ(led)
    anim.run(fps=30)
except KeyboardInterrupt:
    anim.endRecord()
    led.all_off()
    led.update()

To do all of this, you will need to install NumPy (which does the FFT) and PyAudio which captures the audio. If running on Windows, it is highly recommended to grab these pre-compiled binaries for both packages. Otherwise the installation can be quite a pain.

Then, for capturing the audio, things can get a little weird. On *nix type systems (including OS X) it is very possible that the code above will capture the system output audio with no fuss. But my mileage varied wildly depending on OS version, audio configuration, and hardware. On Windows, however, the only option is to take an audio cable and loop it from the headphone jack back into the microphone jack, which should also work on Linux and Mac. You could also use an external audio source like an MP3 player and plug it into the microphone jack. In either case, it will likely be needed to play with the output and input level settings. Start at the lowest on everything and slowly increase until the animation starts reacting. What level exactly also depends wildly on the OS, hardware, etc.

Change the driver = DriverSerial line to use whatever LED type and orientation you happen to be using as well as the 'w' and 'h' variables to set the width and height.

If you want to try this out now, don't forget that you can use the BiblioPixel Visualizer and see it for yourself.

Happy making!

Sharing Among the Community

We already knew that the open source and open hardware community was full of awesome people, but launching the AllPixel Kickstarter has just confirmed that even more. There has been an outpouring of support and advice that will certainly be helpful as we move through the final manufacturing steps.

What has been really cool is that some other open hardware makers like us have been super excited to see how their hardware works with the AllPixel. Two in particular are RGB-123 and WyoLum who were kind enough to send us some of their awesome LED displays, for which we’ve traded some pre-production AllPixel boards.

Ryan from RGB-123 sent us one of his awesome 16×16 WS2812 displays – or in this case, two of his 16×8 displays that haven’t been depaneled. It’s a standard single data input setup with a serpentine pixel layout and a really nice size and pixel density. What’s really cool about these displays is how they are designed for maximum power. Just look at those power connectors! 12 gauge wrapped in silicone insulation.

This panel will draw over 15A at full power, made possible by using huge 5V and Ground planes instead of single power traces. And it is bright! At 256 total pixels it doesn’t even break half of what the AllPixel is capable of.

The fine people at WyoLum sent us a prototype version of their TiM and their yet to be announced TinyTiM… yup, that’s right. We’re breaking the news here… TinyTiM is coming soon!

TiM is a 16×8 matrix of WS2812 LEDs with a little twist. Instead of the usual serpentine layout of the LEDs the TiM can be used in parallel mode where you control each of the 8 rows individually. This can allow slightly faster update speed with the WS2812 if you were using a bunch of these displays together. This is not supported on the AllPixel, but that’s OK, because you can also configure the board to run in that usual serial mode where there’s a single data input and output.

WyoLum TiM

The new TinyTiM is the same great setup as the TiM but in an 8×8 configuration and with updated connection options including chainable IDC connectors.

We’ve used a ton of different LED displays and strips, but it’s always great to see the AllPixel working with another. If you want great and super easy to use matrices for use with your AllPixel, you can’t go wrong with any of these. It’s a lot easier than building your own! Thanks so much to both RGB-123 and WyoLum!