What's not to like about RGB LEDs? With their bright, mesmerizing glow, often capable of displaying millions of colors, they're a great to way to catch the attention of the viewer. Now, what if you had a 5 meter long RGB LED strip, loaded with 160 RGB LEDs to play with? Oh, the possibilities... It so happens that Adafruit, in their infinite wisdom, carries a very nice RGB LED strip, powered by a LPD8806 driver and encased in a waterproof sleeve.
What about turning it into a mini video wall for instance? Think 'Times Square', just smaller :)
Building a 16x10 video display from an RGB LED strip
Having a 160 LEDs, the strip can easily be converted into a 16x10 display: every 16 pixels, the strip overlaps 2 segments and binds them together with solder. It's easy enough to separate the 10 segments with a soldering iron and some patience. Limor covers the casing removal procedure and desoldering method in details here http://www.ladyada.net/products/digitalrgbledstrip/index.html#how_to_separate_longer_strips_advanced so, we'll not re-iterate it.
The method used to create the display was hot-glueing each 16 pixel segment to a hollow plastic board purchased from a local craft store like this:
Each segment was then reconnected to the next one using solid core wire, making sure that the SPI bus lines were properly matched.
The serpentine configuration of the display presents a challenge from a 'memory layout' management point of view but this aspect will be discussed in details later. For the time being, let's just say that managing the strip as a display is a ...hmmm... twisted proposition.
The controller for the RGB LED strip is built around a Netduino mini, coupled with a XBee transceiver so that graphics, text and commands to be sent to the display remotely for fun applications such as Twitter integration, displaying computer performance counters, sensor data, ads, etc.
The LED display and the Netduino Mini, mounted on the back of the display, are both powered using a recycled PC power supply. In terms of power usage the display, while running the demo shown in the YouTube video, consumes between ~10 to ~28 Watts, which is not bad at all. If you don't have a PC power supply handy, which in this instance can deliver ~250 Watts, you will need to find a power suply that can deliver at least 10 Amps @ 5 volts to handle a display at full brightness.
Based on the specifications of the RGB LED strip, we have:
- 20 mA / LED
- 60 mA / RGB LED (3 LEDs * 20 mA)
- 60 mA * 160 RGB LEDs = 9600 mA total for the display with all RGB LEDs at full brightness, which amounts to 9.6 Amps
To determine the required Wattage, apply this formula: W = V x A, which gives us 5v * 9.6 Amps = 48 Watts
As always, it's wise to consider a 20% to 30% buffer above the maximum power requirements to ensure that the system remains as stable as possible.
Finally, because the LEDs are so bright, it was necessary to diffuse the light: using 1" stand-offs, a foam-core board with its center cut out in the shape of a rectangle was placed above the LED segments. A thick plastic sheet was then hotglued to the back of the foam core board before mounting the asembly on the stand-offs. In the end, the setup formed a large, sturdy frame, perfect for testing the Netduino driver code.
The Netduino Driver
The Netduino driver, AdaFruitLPD8806.cs, is part of the Netduino Helpers library located here (http://netduinohelpers.codeplex.com) with the demo sample showcasing the driver being located under \Samples\AdaFruitLPD8806Test. The LED strip is addressable over SPI and in this build, the display is capable of accepting SPI data clocked @ 16 MHz reliably. Above that speed, bits tended to get randomly lost along the way, which is not to say that higher speeds cannot be achieved with proper care. Because multiple strips can be chained together, it is necessary to take propagation delays along the strips into consideration, which is not something that the driver does currently since this scenario could not be tested. To maximize efficiency of SPI communication with the display, the driver manages an internal pixel buffer and only refreshes the display a full frame at a time. Each pixel takes 3 bytes, one byte per red, green and blue component of a color. Note that the strip expects a color to be defined as a GRB tuple instead of a RGB tuple. Each RGB color component can range from 0 to 127, allowing for 2048383 possible colors per RGB LED. The reason for this is that the LPD8806 LED controller uses bit 8 (MSB) as a parity bit for SPI communication with the LPD8806 controllers on the strip and therefore, cannot be used to express color information.
Supported driver functions
The driver is designed to abstract the fact that every other line of the display must be addressed in reverse due to the 'serpentine' configuration of the display segments (see the Copy functions if you really need to know how it works). While more complex from a software point of view, this configuration ensures the shortest possible connections between display segments, minimizing potential SPI issues due to EMI, RF interferences, inductance and resistance as wire-length increases.
- AdaFruitLPD8806(int width, int height, Cpu.Pin chipSelect, SPI.SPI_module spiModule = SPI.SPI_module.SPI1, uint speedKHz = 10000): defines the dimensions of the LED strip expressed in terms of width and height. In the case of a single strip with 160 RGB LEDs, this means 16 columns x 10 lines. If multiple strips are chained together, the overall dimension of the display needs to be provided accordingly.
- void Reset(): Sets the background color to black across the strip
- void SetColor(byte red, byte green, byte blue): Sets the color of the entire strip
- void Refresh(int delayMS = 0): Sends the internal pixel buffer to the strip and optionally waits for a specifies amount of time in milliseconds before returning
- UInt32 RgbToColor(byte red, byte green, byte blue): Generates a 32 bit value from RGB values. RGB values must be between 0 and 127 (2,048,383 colors 'only')
- void SetPixel(int pixelIndex, byte red, byte green, byte blue): Sets a pixel at given index with an RGB value.
- void SetPixel(int pixelIndex, UInt32 color): Sets a pixel at given index with a color value. The color parameter needs to come from RgbToColor()
- void SetPixel(int x, int y, UInt32 color): Set a pixel at a given coordinate with a color value. The color parameter needs to come from RgbToColor()
- void SetBackgroundColor(byte red, byte green, byte blue): Sets the default background color. Internally used by Reset, SetColor, Scroll and Shift.
- void Shift(ScrollDirection direction, ScrollingType scrollingType): Shift the entire strip as a single line, either left or right, circularly or not, one pixel at a time
- void Scroll(ScrollDirection direction, ScrollingType scrollingType, int pixelCount): Scroll the entire frame by x pixels either left or right, circularly or not, one pixel at a time
- void Gradient(int startRed, int startGreen, int startBlue, int endRed, int endGreen, int endBlue, int pixelIndexStart, int pixelIndexEnd): Generates a gradient between two colors and between two pixels indices
- void FadeIn(byte bitmap, int sourceBufferOffset = 0, byte Speed = 1): Fade a bitmap into view using the bitmap currently loaded in the LED strip buffer as the point of reference
- void Copy(byte bitmap, int sourceBufferOffset = 0): Copies a source bitmap to the LED strip buffer. Source and destinations must be the same size.
- void Copy(byte bitmap, int x, int y, int width, int height, int bitmapWidth, int bitmapHeight): Copies a source bitmap at the given x,y coordinates into the LED strip buffer. Does not yet handle the case where the source bitmap is smaller than the target frame size.
- void DrawRectangle(int x, int y, int width, int height, UInt32 color): Draws a rectangle of a given color at the given coordinates
- byte BuildMarquee(string text, CharSet charSet, byte redBackground, byte greenBackground, byte blueBackground, byte redText, byte greenText, byte blueText): Takes a string of text, a character set, background and text colors and returns a bitmap intended to be scrolled across the display. Casing (upper) matters with the default CharSet used in the demo. The longer the text string, the more memory will be used to create the bitmap. The code expects 8x8 character sets.
- void Dispose(): Releases all resources used by the driver
About the Copy() functions: they expect bitmaps to be encoded in GRB format, with the 8th MSB bit set to 1, as natively understood by the strip to minimize runtime computations. To this end, 24 bit bitmaps in .BMP format can be easily batch-converted using the 'VideoBmpStrip' tool located under the \Tools folder in the netduino helpers repository.
Turning an Adafruit LPD8806 RGB LED strip into an actual display was a fun software challenge due to the whacky memory layout of the strips. Fortunately, the LPD8806 Netduino driver makes it really easy for a maker to build a display as a weekend project now. The applications for such an RGB display are quite varied, ranging from Twitter feed viewers, stock tickers, graphic ad streamers and can even handle simple video animation playback. It's a compelling, cost effective alternative to the typical commercial LED text-scrolling or static signs that are so pervasive these days. So, go ahead and mesmerize your audience with your own mini-video wall: no matter what your message says, it will get the viewer's attention.