I wanted a fun project to freshen up my Python programming skills and I happened on this idea while putting out my Christmas lights for 2018:
Let’s make a new yard display using individually addressable LED pixels!
If all goes well, I’ll be able to replace my existing display of mini light stars with a string of these cool computer controlled LED stars. I might even be able to synchronize the display to music!
Pixels and Pixel Strips
Each pixel includes three internal LEDs (a red, a green, and a blue), along with a built-in controller. The controller can set each of the three LEDs to one of 256 brightness levels.
The 2812s operate from a 5 volt supply, and each has a data-in and data-out pin for the serial control protocol. The components are daisy chained by their ins and outs with only 3 wires needed between each pixel. I think less wiring means less chance for wiring problems.
I’m not sure if the components they use are actual 2812s, but they are compatible with the protocol. I’m using the black, 30 pixels per meter variety in this project. The strips can be cut at the pads and have a thin, double sided tape backing.
The layout was carefully planned with a few constraints from the old display. I wanted to keep a similar star size, have pixels at the tips, and avoid overlapping pixels in the middle of the star. I used a quick Scratch program to verify my layout ideas using “turtle” or “pen” graphics. I decided on a strip of 45 pixels, and cut it into five 9-pixel sections. Each section forms part of a ten pixel chord that includes the start of the next strip.
The BTF strips need a support structure to hold them in the star shape. I decided to design my own plastic strip that could be 3D printed, then assembled into the star shape. The ends of the strips snap together and have slots to accept zip-ties that securely hold the pixel strips in place.
I found a mail-order supplier on TreatStock and had my parts printed in ABS plastic. I unexpectedly specified 20% infill on the order, but that worked out OK with the strips being somewhat flexible for the woven assembly I wanted.
There are plenty of choices for LED pixel controllers. For me, the Raspberry Pi works great. I have a Raspberry Pi model 3 B, so I’m using it.
I’m running Raspbian (AKA Debian Stretch) Linux. The Pi is inexpensive, and out of the box, it offers a bunch of learning opportunities with free, open source software and development suites. Included HDMI, USB ports, WiFi, and Bluetooth make this a stand-alone computer that runs from a 5V wall-wart.
I’m using Maniacal Lab‘s awesome BiblioPixel light programming system to run the display. Their open source Python3 code library is hosted on GitHub. The package supports multiple pixel types, is portable to multiple operating systems, and comes with a lot of example animations. They support pixel layouts for one-dimensional strips, two-dimensional matrices and disks, as well as three-dimensional cubes. The animations are customizable via project files written in YAML or JSON. I was able to start with their animations very quickly using their built in strip layout. I had my own animation ideas for this star, and was able to program my own animation in Python. More on that later.
There’s one more component between the Raspberry Pi and the pixel strips. That’s Manaical Labs’ AllPixelMini.
It’s a USB device that handles the serial protocol that updates the pixel colors. This frees the Raspberry Pi of that CPU overhead and allows other real-time event driven software to co-exist on the system (audio, mouse, GUI, etc.)
In my setup, the AllPixelMini provides just the data signal (green wire) to the pixel strips (ignore the red wire above). A ground connection (white and black wires) is also required to make sure all the ground levels are common between the power supply, LEDs, and AllPixelMini. The All PixeMini gets it’s power (and ground) via the USB cable.
These 3-pin waterproof connectors work great for connecting the display to the power supply and AllPixelMini.
I plan on using the same connectors to daisy chain the stars and other display components when I scale this up.
Each 9-pixel strip is wired to the next with a 3-wire connection for 5 Volts, ground, and data.
The strips have solder pads on the back that let you keep the waterproof covering stuck to the LED side.
I also added adhesive lined heat-shrink tubing after soldering to keep the connections waterproof for outdoor use.
Each star of 45 pixels draws 1.66 Amps at 5V with all the LEDs of each pixel at full brightness, and 32mA in the quiescent “off” state. I’m using a 60 Amp, 5 volt supply driving the 5V rail of the LEDs directly (black and red wires connected to power supply above) without feeding power through the AllPixel Board. There’s plenty of power left for expansion, and the fan on the supply does not turn on with this light load.
When I first imagined the star display, I was thinking of the way firework streamers looked. You’ve probably seen animations that use particle systems to simulate fireworks. The firework explodes with glowing particles being emitted from a point. The particles stream down, flicker and fade before they burn out. This animation is similar.
The Emitter() animation is a one dimensional particle system for BiblioPixel. The animation is written in Python. My Emitter() class inherits from BiblioPixel’s Strip() animation class. You can find my GitHub repository here. Manaical Labs has also included it in their repository.
The Emitter() class has lots of parameters to control the particle effects, and the source code contains docstrings describing the parameters. Definitely check that out. The parameters can be overridden in BiblioPixel’s YAML or JSON project files. Here’s emitter_demo.yml for the demo in the video.
Each strip can have multiple emitters with programmable positions and velocities. Particles can be emitted in either or both directions. Moving emitters and particles can wrap at the end of the strip. The emitters can be invisible or have color.
Emitted particles move away from the source starting at the full brightness of a color that’s randomly selected from a palette. The brightness then varies in a random manner. The random variations are chosen from a list built at class initialization. The default settings should make the particles “sparkle” and fade. The distribution of brightness variations is adjustable.
The Python code for plotting the histogram is here.
Individual particle velocities are random with adjustable constraints. Particles have a range and won’t go beyond a specified distance in pixels. from their emission point. At a given frame step, an emitter can start multiple particles. The starts_at_once parameter controls how many can start. The starts_prob parameter controls the probability that a particle will actually start. The variance in particle velocities lets particles overtake each other as they travel down the strip. Particles can hold a brightness level below zero and then randomly come back up above zero becoming visible again. Another effect is flares. The flare_prob parameter specifies the probability that a particle can immediately return to full brightness before resuming random brightness variations.
The particles are rendered onto the strip “screen” at each animation step. A loop goes through all the strip’s pixels and sees what particles or emitters are visible from that spot. If no particles are visible, the background color is used. The aperture parameter controls the “visibility” distance. If the distance to a particle is outside the aperture distance, it does not contribute to the pixel. The colors of the visible particles are then blended based on their distance to the given pixel. Particle positions, distances, and apertures are all floating point values. Small aperture values can cause blinking when a particle or emitter becomes invisible between the pixel locations. Larger aperture values will spread a particle’s color across multiple pixels.
Check out this part of the star display demo to see all the parameters in action: