Controlling an individually addressable LED strip using Go
LEDSerial
I have been into LED lights since as long as I can remember. Since I made my first blinking LED using at ATmega, and more recently when I made a pulsating LED status indicator. But I have always wanted to hack around with individually addressable led strips. This break, I got my hands on a 2 meter strip from Pololu consisting of 60 WS2812B LEDs.
What I wanted
I wanted an LED strip to put on my desk that would display a projection of the colors on my screen. This way the strip acts as an extension of the screen as synchronized ambient lighting.
Demo
Architecture overview
The code is split up into three parts. SerialSlave
, the arduino software, ledcomm
the golang libary for communicating with
the arduino software, and ledscreen
the software that gets the screen colors.
SerialSlave
The simplest part of the system is the arduino software, it keeps an internal representation of the LED strip’s colors and supports three commands which are remotely called through a raw serial buffer.
f
- 1 byte command to flush the internal representation through to the led strip
c
- 1 byte clear the internal representation of colors and flush it
s|r|g|b|i
- 5 byte command to update a single led in the internal representation. r,g,b,i are 8bit values which
represent the red, green, and blue components of the color, and the led index in strip.
In uses the PololuLedStrip library for communicating with the LED strip.
The arduino software makes no guarantees about timing or integrity, it plainly reads and executes.
ledcomm
Because the arduino has no access to the computer’s screen, we need software running on the host computer. Ledcomm is the software layer that sits between the arduino and any clients that wish to alter the led strip. It provides an interface for opening a connection with the arduino and controlling that connection. It supports writing both RGB and HSV color values to arbitrary LEDs.
Due to the asynchronous and time-sensitive nature of the led strip protocol and the serial transfer time, functions that communicate with the arduino include the minimum sleep amounts.
ledscreen
Once we have a way to communicate with the arduino (and the led strip), we need software to read the pixels on the computer screen and project them to the screen.
Thanks to xgb, a golang port of XCB and the work of vova616 I was able to create a forked library which uses a single X connection to continously read the pixels of the screen.
Once we have access to the pixels, we have to compress the screen’s 1920x1080 pixels into a 60x1 LED strip.
I experimented with various partitioning and weighted average algorithms to get the relevant colors of the screen. But most were too complex to be run at a reasonable frame-rate. At first I partitioned the screen into 60 32x1080 rectangles, average the rgb components and sent each color to the LED strip. Although it worked in theory, the results left a lot to be desired. Averaging the entire screen was not only expensive, but it tended to produce “dirty” or “murky” colors.
I then decided to only average horizontal crossections of the screen, producing a pattern much like this: . This was based on the premise that the most important colors are located near the vertical center of the screen (where our eyes spend the most time).
After futher testing, simplifying the algorithm to take a single horizontal crossection of the screen seems to produce a much cleaner effect.
Code
All the code is available at https://github.com/pato/goLED