With several of my previous projects, I found that I often had to take some time to explain to people what they did, and why that would be interesting. I'd describe an idea that at one point I thought was rather clever, and more often than I'd like I would just get a response of "Huh, okay." The inspiration for this project was to try and make something that would be interesting and appreciable by anyone without a word of explanation, from the first moment of seeing it.
I love generative art. I am endlessly impressed by people who can write programs that produce something beautiful. I love how the best of it exploits emergent effects- simple algorithms producing something complex, sometimes startlingly organic, at once deterministic but also beyond prediction and true control. One of my favorite examples is the Fractal Flame algorithm by Scott Draves, perhaps best represented by the screensaver Electric Sheep.
I decided that I had to make an LED display that I could hang on the wall that would play fractal flames all the time.
My first thought was to write or modify some software to generate these animations on the fly. While Electric Sheep can take some minutes to generate a single 800x592 frame on a full-fledged PC, I hoped that a small single board computer like a Gumstix machine might be able to do tiny frames on the order of 32x32 in realtime. Unfortunately, this line of research didn't last very long. The open-source implementations of the fractal flame algorithm I found looked poorly suited to be stripped down and ported in this way, at least not without a rather large effort on my part. I looked into implementing it from scratch myself, but while the heart of the fractal flame algorithm isn't terribly complex, there's a surprising amount of difficulty in autonomously choosing visually appealing parameters for it to work with. I soon decided this was the wrong approach.
While investigating other options, I stumbled upon this- a collection of a couple thousand 5-second videos produced by the combined efforts of Electric Sheep's worldwide installbase in 2009. Perfect! I downloaded the entire thing and set to figuring out how to put it to use.
The trick with these videos is that in order to get seamless transitions from one to the next, you have to know what order you can play them in. Their filenames contain hints at this network of happy paths through the many files; file 00243=13758=13661=13675 is a member of flock 243, has an ID number 13758, and plays seamlessly when following 13661 and followed by 13675. That alone doesn't tell the full story, though. If you map out this graph of seamless connections between files, you find that from any given video there's often several choices of which to play next. Unfortunately, Electric Sheep produces these videos randomly, and without regard to maximizing those seamless transitions, so the graph is riddled with orphans, dead ends, unreachable regions, and inescapable loops. I wrote a program I called SheepGraph that would try to find a long, seamless path through the graph while distributing playtime evenly across as many of the videos as possible. I eventually got it to come up with a sequence playing 800 of the videos over 4,548 stops (an average of 5.7 plays each, with a total play time of 6 hours 19 minutes), with a distribution I could live with.
I had SheepGraph cut together a single file out of downsampled versions of all those videos, and wrote another little throwaway program to play that file. When I got it all working the way I wanted, I stared at its swirly colorful beauty for minutes on end. I knew then that I had a project worth pursuing.
So at this point I had the data. Next I needed hardware.
The first obvious question was how to drive a large number of LEDs. I've previously discussed using Charlieplexing to drive many LEDs directly from a microcontroller, but I think even 56 LEDs (the maximum for 8 pins) would be pushing that technique to its breaking point- and would have very serious costs in brightness and refresh rate. I wanted something more like a thousand LEDs, so that wasn't going to cut it. Another easy and popular approach is to assemble long chains of shift registers, and wire each output to an LED (or a column of LEDs). The problem with that is that it's very difficult to get those LEDs to do anything but simple on or off- that means no more than just six colors plus white for any RGB pixel. To get the kind of smooth color I wanted, I would need to be able to control the brightness of every LED.
I soon discovered TI's TLC59xx LED driver chips, and they were just about perfect for me. I settled on the TLC5947 because it had the most output channels (24), and didn't require an external oscillator. It does 12-bit PWM dimming on all 24 of its outputs, takes a simple SPI interface, and even does adjustable current regulation.
Why 12 bits, you ask? This perplexed me at first, too. After all, my computer is presently set to display 24-bit color, or just 8 bits per channel, and it looks pretty good to me. Turns out the answer is that you need the extra bits of resolution when you perform gamma correction on your image data. With twelve bits, every output of the LED driver has a range of brightness from 0-4095; 0 has the LED completely off, 4095 is steadily on, and 2048 would have it on exactly half the time and throwing out exactly half as much light. Trouble is, that's not the way our eyes see it. We perceive brightness logarithmically- the difference between brightness 5 and 6 looks like the same as between 1000 and 1017, or 4000 and 4035. Computer graphics and videos are almost always expressed in terms of perceived brightness, so we have to convert those numbers to physical brightness before sending them out to the display. This is called gamma correction.
With 24 channels, I could drive 8 RGB pixels at a time. The TLC5947 has plenty of bandwidth to be scanned over many rows, but how far you can push that was an open question. I also wanted to make certain that I understood the chip before I started making any serious time investments into using it. I needed to experiment, and I noticed that one of my Flickr contacts happened to sell just the thing I needed- a board called OctoBrite with a TLC5947 and 8 RGB LEDs very much like the ones I was thinking about. I stuck it on a breadboard along with an MSP430 microcontroller I had laying around, and spent a weekend playing with it. My final conclusions were that the TLC5947 was indeed perfect, that I could reasonably scan it across 8 rows, and ooh where can I get those LEDs?
I asked Garrett (from whom I bought the OctoBrite) for his source of LEDs, and he pointed me to a favorite seller on eBay. This guy was shipping out of Hong Kong, but he has a good feedback score, and his prices were around half of what I could find elsewhere. So, I bought 10 LEDs to evaluate (I just swapped out a few of the LEDs on my OctoBrite), and when they looked good, I bought a package of 500 more.
Each trip shipping from Hong Kong took a couple weeks, which I used to get a start on designing circuit boards. I decided to make my display out of some number of identical boards, each with 64 pixels (8 columns, 8 rows), one TLC5947, 8 MOSFETs to independently enable each row, and one microcontroller to control it all and listen for new data.
Turns out when your circuit is a grid covering every centimeter of your board, and one side is packed with LEDs, it becomes rather tricky to find room to place components. It took some creative organization of the bottom layer to fit everything in.
In the image above, you can see the eight MOSFETs on the top. Each of them switches power to the anodes of an entire column- 8 pixels, or 24 LEDs. In normal operation, only one will ever be on at a time, because all 24 rows in the board are tied together on each pin the LED driver.
The TLC5947 LED driver is dead center. It comes in a 'high temperature' package, with a metal pad on the bottom that you're supposed to solder down to better dissipate heat. Unfortunately, those are next to impossible to solder down with regular hand tools. I tried filling the thermal pad with vias in the hope that I would be able to heat them up and wick solder through them from the other side, but I picked much too small a size of the vias for that to work. As it turns out, though, the datasheet says you can skip soldering down the thermal pad if you stay below some certain level of power dissipation, and that limit is very easy to comply with as long as you keep the LEDs' supply voltage within a few volts of their voltage drop. With a supply voltage of 3.6V, the chip hardly even gets warm.
On the bottom of the layout diagram above is space for two 6-pin headers, an MSP430-F2274 microcontroller, and a set of four solder jumpers. The headers are identical, designed so that you can daisy-chain together several boards with ribbon cables. They were designed to carry power, ground, SPI in, SPI out, SPI clock, and SPI chip select. The solder jumpers are just a method for assigning a unique ID to each of several completely identical boards. Finally, you can see there's a Spi-Bi-Wire programming header for the MSP430 on the left edge of the board.
I got my boards manufactured with DorkbotPDX's monthly group order. Three boards for just $5 per square inch was a much better deal than I could find anywhere else, so I put in two orders for a total of six of my display boards. I was quite pleased with the results when they came back. The only problem I found I hadn't even noticed until several boards were already populated- the soldermask on the bottom was slightly misaligned, and could cause shorts if I wasn't careful. Once I found what to watch out for, though, it was easy to take care of.
I wound up having to rework a few details after I got the boards manufactured. First, I forgot to put a pull-up resistor on the MSP430's reset pin. Oops. Second, I decided that the SPI out and chip select pins on the bus headers were really pretty useless. I cut the trace for chip select and soldered on a wire to re-route it to the reset pin, so now the display can be reset via the bus. Finally, when I actually tried running the display, I found they had terrible stability problems because I had neglected to include decoupling capacitors. I initially added a standard 0.1μF ceramic capacitor to each one, and they helped, but some investigation with an oscilloscope proved it was not nearly enough. When you're supplying multiple amps of current via a 28-gauge ribbon cable to a daisy chain of PWM-dimmed displays, the supply voltage gets very, very noisy. I wound up putting a big 1000μF electrolytic capacitor on every board to smooth it out again.
Each board took just over an hour to populate. Most of the time was in the 384 solder joints to put the LEDs down on each board, but with some practice I got rather quick at that. Of course the ICs on the bottom are a little more difficult, but they helped me get my drag soldering technique down. It's all about learning how to make surfance tension do all the work for you.
I did one board a day fox six days, and spent any extra time on their firmware and testing. Here's what each board population looked like, compressed down to 82 seconds.
Of course, once I had all six display boards, the next step was to build a circuit to supply them with power and data. Up until this point, I had been powering the boards with a benchtop power supply my roommate had made from an old computer PSU, and feeding them with data from a spare microcontroller on a breadboard.
Each display board has a theoretical maximum current draw of 480mA- 20mA per LED times 24 LEDs at a time. So the whole display could come close to three amperes of current draw, and maybe a bit over ten watts of power. For the first time, a linear regular simply wouldn't cut it for one of my projects- I needed a good switching regulator. For stuff like that, I always look to Sparkfun first. Aside from having a fabulous selection of useful and accessible parts, they're also headquartered just five miles away from my office, so I can order a part and pick it up the same day. A quick search turned up just the sort of thing I was looking for, but I wasn't very thrilled at the price- $35 for frikkin' regulator? In the comments at the bottom of the product page, though, was a link to a similar product from Pololu, another hobby electronics site. Praise to Sparkfun for allowing that comment to remain! It was just what I needed, with a smaller footprint and less than half the price- I bought two. (I'll get to why in a moment.)
The next big decision was storage. I needed at least a gigabyte of storage, preferably in some PC-accessible form. I looked into DOSonCHIP, a solution I had heard about before, but at the time the hardware was all but unobtainable, out of stock everywhere. I looked into getting a FAT library (FatFs particularly interested me) I could put on a microcontroller, but that would have meant making my own interface to an SD card. What I finally went with was a little development board with an FTDIVinculum USB controller chip on it. I liked the idea of using a USB flash drive for storage, and I liked that one module could handle the hardware interface and the filesystem.
I assembled those parts, along with the MSP430-F2012 I'd been using to test, on a breadboard. I worked on the circuit and the software to run it on and off for a couple of months until I was confident enough in my design to transplant the whole thing to a piece of perfboard. It's nice to lay my circuits out on a proper PCB, but it's much more expensive, it's an awful lot of work because you have to be so much more sure of every single design decision, and it's much more difficult to rework later. Since this circuit was made of a relatively small number of all PTH components, I saw no good reason to not just go with perfboard. This is what my layout looked like.
The reason I got two regulators was because I needed 3.6V for the display boards, and 5V for the Vinculum board and any USB devices connected to it. The Pololu regular boards are designed for either 5V, or 3.3V if you solder in a jumper, but I wanted a little more headroom over the LEDs' voltage drop. If the displays' supply voltage sags much below 3.3V, the blue and green LEDs start to dim quite a bit, resulting in a very red-tinted image. The MSP430s I'm using have a maximum operating voltage of 3.6V, so I just went as close to that as I could by soldering in a carefully-chosen resistor rather than a jumper. And by 'carefully chosen', I mean I read the regulator's schematic and its chip's datasheet, then guess-and-checked until I got it right (but I'm a programmer, so I guess in the form of a binary search).
The display software
The software running on the displays isn't very complex. After power-on, they configure all the on-chip devices, then go into an infinite loop of drawing the display. The loop consists of the following steps:
Pick out the next line from the active framebuffer
Gamma-correct that line from 8-bit perceptual brightness values to 12-bit physical brightnesses, using a b2.2 lookup table.
Send that data to the LED driver
Switch off the previous line's MOSFET
Switch on the current line's MOSFET
Latch the driver to display the new data
Wait for a tick from the timer
The timer is configured to tick at 16KHz, so the display completely refreshes at 2KHz- more than enough to make the flicker imperceptible. The only other code in the display is the interrupt handler for incoming bytes on the SPI bus. That interrupt handler is a small state machine that accepts only properly formatted messages:
A three-byte magic header, 'Syn'
One byte matching the board's ID number (set with the solder jumpers)
192 bytes of image data
A 16-bit sum of every other byte in the message
The display keeps two framebuffers- an active one that is displayed, and a back buffer that incoming data is written to. If the message is valid and checksum at the end is correct, then the two buffers are swapped and the new data is displayed.
The master software
The software for the master board is just a little more complex. It has to connect to the Vinculum board via SPI and communicate back and forth to determine if a flash drive is plugged in, find an appropriate file in its filesystem, and then read out the file piece by piece. As that data is read in, it has to get framed into messages and sent out on the display bus. Almost all of the complexity was just in dealing with the Vinculum board. Its protocol makes sense for user-interactive use on a serial port (though I can't imagine why anybody would want to do so beyond testing), but it's awful for reliable chip-to-chip communication. It can't be modeled as a state machine, it requires pointlessly tedious parsing of data, it throws out messages asynchronously (so you always have to watch out for them), and at times there is no reliable way to distinguish between an error code and valid data. Any bit errors, unexpected messages, or ambiguous data would cause the two ends to lose sync with each other, and then it was very difficult to recover. In fact, I could never get it running stable for more than twenty minutes to a few hours at a time.
Just as I was becoming really frustrated with that, I noticed that FTDI was finally shipping their newer line of chips- Vinculum 2. After my disappointment with their previous model, I was a little hesitant to buy from them again. The Vinculum 2 chips, though, were fairly capable-looking programmable microcontrollers in addition to having their predecessor's USB and FAT features. Rather than using that shaky protocol between my microcontroller and the Vinculum chip, I could simply re-write everything to run in the Vinculum 2 and handle storage with its C API. So I bought a Vinculum 2 development board very much like the one I already had. In fact, it was even pin-compatible with the old one, which saved me from having to rework the master board too much.
My first disappointment with the Vinculum 2 was its lack of an analog-to-digital converter. I already had a potentiometer and matching knob picked out to set the display's brightness, and I needed an ADC to read it. No big deal, though- I just left the MSP430 in, and programmed it take a reading and send out the results via SPI at 10Hz. My second disappointment was with the IDE FTDI provides for programming the Vinculum 2- I found quite a few bugs, it froze frequently, and it was missing many basic features I've come to expect in even the simplest code editors. Still, nothing I couldn't work through. My third disappointment was with the libraries FTDI provided for interfacing with the chip hardware. All closed-source, just slightly buggy, and with only passable documentation- when something wasn't working right, it was a real pain to figure out why. I've been spoiled by TI's good Eclipse-based IDE, fabulous documentation, and being able to know and control every last bit of my MSP430s' state. Despite all that, though, I finally got the software for the master board all rewritten to run on the Vinculum 2, and I'm happy to report that it appears to work very well.
With the hardware and software finally coming together, of course it came time for the final part of the project- the enclosure. All along I was expecting I would have to get a custom-made frame in order to get something just the right size, but on a whim I went to check out the local hobby store to see what they had on hand. I was surprised to find a box frame that was actually close to the right size and depth, and for only $10. I took one home to see if I could make it work.
I needed a fairly heavy frosting on the glass. Some research turned up quite a few options. Apparently sandblasting is the ideal method for frosting glass, but I could only find one shop in town that it, and was told they weren't planning on getting out their sandblaster for a couple of months. Not wanting to wait for that, I went to my local hardware store and bought a jar of glass etching cream, promised them I would wear gloves and goggles, and went home to try it out. For the first application, I did exactly by the instructions on the jar, but I wasn't very happy with the results- much too light and not very consistent. So I did a second application and quadrupled the recommended etching time, but it was still much too light for me. So I stopped by the hardware store again, this time picking up a can of spray-on glass frosting. I think it would have done the trick with several coats, if not for splatter. I don't know if it was user error or just poor design, but on every attempt the can would splatter just one or two big drops onto the glass, and I couldn't figure out any way to clean them off without leaving behind visible marks. I went back to the hardware store again.. and noticed that they sell cut-to-order sheets of acrylic of several different types, including translucent white. I'd have preferred glass over plastic, but I couldn't deny that it would work very well, so I got a sheet to replace the badly-etched, badly-painted glass.
During one of my many stops at the local electronic components store for this project, I picked out all the parts for the display's control panel- a barrel jack for power (matching the AC adapter I picked out as well), a toggle power switch, an identical-looking momentary switch for input, and a potentiometer and matching knob for brightness control. Since the wood of the frame was too thick to mount them directly, I bought a strip of brass and a couple of tiny brass screws, routed out a hole big enough for everything in the frame, and screwed on the brass control panel. I think it worked out rather nicely.
The backing that came with the frame was not much better than cardboard with felt glued to the inside, and was already terribly warped. So I cut a sheet of 1/8" plywood to replace it and transplanted the retaining clips and hangars, using pop rivets to mount them back on. Twenty-four holes very carefully measured, drilled, and countersunk mounted four standoffs per display board.
The display boards were fastened down with nylon screws, so as to avoid scratching the soldermask on the boards. To mount the master board, I wound up having to remove one of the standoffs, and cut out a small slot in the side of the board to accommodate another standoff.
Today it's hanging up on the wall just outside my office at work, playing the Electric Sheep video on continuous loop. I have a little program I've whipped up that will transcode any video to be playable by it, and the extra switch is set to cycle between all the videos on the flash drive, so I can add more with little trouble. At the moment, I have the Electric Sheep video, an episode of Futurama, and The Matrix. Even at just 24x16 resolution, it usually doesn't take people very long to figure out what they're watching and what's going on.
I welcome you to reproduce or even improve upon my work. Below is all my source code, schematics, PCB layouts, and so on. If you make anything using this, I'd love to hear from you.