Das Blinkenlichten – wearable lighting
Update: The source code (firmware), schematics and protocol of Das Blinkenlichten are now published here. Now you can build your own! For those without chip programmers (i.e. most everybody), I will be making preprogrammed chips available for sale soon.
Heh, yeah, a little late writing this one. I was meaning to write this up…like, months ago, right after the “public beta”, but didn’t get around to it.
So some friends and I had tickets to the Boston leg of the VNV Nation Judgment tour, April 18. Of course no electronica concert is complete without things that blink and blue LEDs, handheld spinny balls, etc. So my friend JR and I hacked together a wearable demo of Das Blinkenlichten, triggered by a microphone and envelope detector, which she wanted to wear at the show.
At probably midnight the night before, assembly was finished. I only had a handful of working nodes (or ones I thought were working…); she made wrist and neck bands out of some flexible clear rubbery sheeting we found in the lab, mounted LED nodes into each piece, and ran a long wire for each terminating in a 3-pin header. Two on each wristband and 3 on the neck; they looked good. The controller was stuffed into an acrylic folding case that once housed some free electronics samples, but was just the right depth (with some coaxing) to accommodate a 9V battery.
Next question…how are we getting a package of nearly naked electronics…with a microphone…through security at a concert where they’re confiscating recording devices…in Boston. It doesn’t even have somebody on it giving the finger.
Sure enough, at the doors security pulls me aside and wants to know what this electronics package is. One asks (jokingly), “That’s not a bomb, is it?” …the other, maybe not getting the joke, replies straight faced that it’s too small to be a bomb. I was eventually let through with my hoax device bag intact, though the mini-screwdriver set (for adjusting the gain pots) was confiscated as Sharp Objects. (Had to track down a security guy and got them back upon leaving.)
The goal of the project was to develop an extremely low-cost, “intelligent” reactive lighting system using tri-color (RGB) LEDs. Specifications were:
- “Smarts” – Lights should be reactive and able to interact in arbitrarily complex ways. The simplest approach is to have one master processor coordinating many, individually addressible LEDs.
- Cheap to implement – a complete system would be one controller but many lighting nodes, so these end nodes should be as simple and low-cost as possible.
- Compact and simple to assemble – Minimal size (controller and nodes), a minimum of wires. They should be able to act as a drop-in replacement for decorative mood lights, Xmas tree light strings, or be wearable :-)
These goals were met by implementing the LED lighting nodes using one RGB LED each, connected to a PIC10F200 microcontroller (yes folks, that’s 50 cents per MIPS, or about 20 Mark I’s, even if you still don’t get a hardware multiplier) running a custom 1-wire (ahem, not including power & ground) serial interface.
I laid out boards and had a simple demo together a couple years ago, then it kind of got shuffled onto my indefinite back burner. Not until recently did I get around to actually doing something with it.
This design involved some tradeoffs of course. To keep the end nodes both tiny and cheap, they are controlled by a PIC10F200 in a miniscule 6-pin SOT23 package (about a grain of rice). With power and ground, this only leaves 4 pins for general I/O: 3 for the 3 LED chips (red, green, blue) and one for receiving commands. On PIC10 one of these I/O pins is input-only, so of course this one was used for the communications. If this pin were bidirectional, the controller could query the bus and get a list of nodes (addresses) attached to it. Instead, this trade-off means the controller needs to know the addresses of all attached nodes in advance (currently, these are hand-entered by the user and stored in flash memory). This is not really too bad, since even if the controller could query them, it would have no way to determine where each was physically located (handy if you want some particular effects, like colors playfully chasing each other around).
The “1-wire” bus, as mentioned earlier, is actually 3 wires: data, power & ground. It uses a custom communications protocol I wrote for this project. I could reduce the wire count further to 2 wires by using a protocol along the lines of Dallas/Maxim’s 1-wire. This works by combining the power and data lines into one, passing the data in the form of very brief power interruptions (with a large enough capacitor at the device end that it isn’t affected by these power interruptions). However, this would add size and cost by requiring a large enough capacitor at each node to power 3 LED colors and the PIC10 simultaneously. More importantly, the data rate would be much slower, reducing the ability to do complex effects… so 3 wires it is.
The (only) lighting effect I had implemented by then (between all the soldering and beat detector stuff) was a fader that selected randomly a color (R/G/B combination) for each node on a beat detection, fading it in and then out again over the course of about 500ms. After the concert, Mark (yes, VNV’s Mark) said that watching the blinkenlights out in the crowd was helping him keep on beat, but I think he was just being polite :-) Due to the full fade-in before fade-out, the light pulses peak about 1/4 second after the beat. This was a (maybe questionable) design decision made because the sudden engagement of all the lights at once (no fade-in) seemed a bit jarring while I had it on my bench playing with lighting effects.
Analog beat detector
Beat detection can be made an arbitrarily complex beast, depending on how accurate you want to be and over what diversity of frequencies you will register a beat (e.g. do snare drums and cymbal clashes count?). qDot of Slashdong has a great introductory primer on audio filtering and beat detection (as it applies to OhMiBod and other vibrating toys ;), including the very sucky fact that when the amplitude of said audio changes, so should your detection thresholds (unless you don’t really care about detecting anything). For this application, we’re expecting some eyeball-rattling bass, yo, so we’ll concentrate beat detection efforts there. Of course since it won’t be known just how loud the venue will be (or even from one song to the next, etc.), some form of automatic level control is a must.
Yes, we have an 8MHz microcontroller with onboard A/D that could sample the audio through to some crazy DSP filters, take Fourier transforms and run a detection algorithm, but that sounds suspiciously like a bunch of work, and I’m lazy. (Yeah yeah, it also says I’m a DSP expert on my resume (I programmed one in college once*)). For under a buck extra we can save my wee brain from math-induced failure modes and do it the retro way, using opamps and RC filters.
Here is (essentially) the beat detection circuit I built, utilizing a single LM324 quad opamp and some discretes. Stage 1 is a basic carbon mic preamp. The mic is essentially a resistor that changes with sound pressure, so biasing it with another resistor to Vcc (+5v) creates a voltage divider that turns out the tiny sound waveform (with some DC offset) at its + terminal, which is then re-centered about 2.5V and gained hugely. This is followed by a very simple, 1-pole lowpass filter (just the bass, ma’am) feeding into a unity gain buffer. The buffered output feeds two envelope detectors: essentially just a diode rectifying the waveform into a storage cap. The size (or speed) of the envelope is determined by the size of the cap and its accompanying bleed resistor. Here is where the auto threshold is done. The top envelope detector is “fast”, fast attack and fast decay. The bottom one is “slow”; not only is the decay time much longer (seconds), the attack is also dulled by a resistor in series. The two envelope detectors feed the 3rd stage, where the opamp is being used to fake a comparator (with hysteresis), with the “fast” envelope as the peak and the “slow” as the threshold. The output will swing HIGH when the instantaneous peak exceeds the moving threshold by about 100mV; it will swing LOW again when the peak drops below again by the same amount. (By making the attack part of the detection criteria, we help avoid false detections from extraneous noise, sustained bass notes with a bit of reverb, and other non-rapid, unbeatlike changes.) The result is a fairly reliable, low-to-high interrupt at every beat.
Node Firmware / 1-Wire Protocol
This is easy-peasy. All right, maybe not that trivial because we’re counting clocks, but at least there’s no math or much statefulness involved. Each node runs a software (bitbanged) PWM loop for each of the three color outputs, which consists of perpetually rotating a register (into carry) and using the carry flag to determine if the output is on or off for this iteration. The more ‘1’s in the register, the greater the LED’s effective duty cycle. We can store 0-8 ‘1’s in each color register, for a total of 9 intensity levels per color. Between PWM loop iterations, it polls the 1-wire for a START condition (‘1’, which will last longer than a single PWM loop to guarantee everyone catches it), and gets ready to read the incoming command if present. Therefore, sending commands briefly stalls the PWM for all devices. One stall is too short for the human eye to see, but we probably don’t want to completely saturate the bus with them. The actual bits are a simple baudrate system, where a 1 is signified by a high pulse followed by a “short” (same length as the high one) low pulse, and a 0 is signified by a high pulse followed by a “long” (double the length of the high) low one. Or something to that effect; I haven’t looked at the code or actual waveforms in a while.
Each node has a unique 8-bit address, with address 0 reserved as the broadcast (general call, for you I2C freaks) address. 01 ~ 0F (or more if you like) should additionally be reserved for group addresses, to be described. The command consists of one address byte followed by one payload byte consisting of the command type (0 = set color, 1 = extended cmd), and for a “set color”, colors affected (3 bits), and intensity (4 bits). So to set all the colors (R,G,B) on a node to the same value only one command is needed; to set them all differently takes three. If “extended cmd”, all the above is disregarded, and the rest of the bits are decoded on a cmd-specific basis. Currently the only extended cmd is “set group address”, in which the remaining bits define another address this node should respond to (in addition to 0 and its own). Ideally, a block of addresses should be set aside for this purpose. This allows multiple nodes to be “grouped” to act in unison by giving them the same group address, allowing more efficient use of the bus. Of course, “0 set group 0” essentially clears all groups (since everyone listens on 0 anyway), so there doesn’t need to be an explicit “clear group address” command.
The chain initialization and random (“randumb”?) number generator are not much changed from the original prototype (given away to then-girlfriend for use as a night light), which simply iterated through all known addresses in series and slowly faded each node from its current color combination to a new target combination (gotten from the random number table). This is a 256-byte table where each byte consists of an integer from 0 to 8, the possible intensity values. To fetch a value from the table, the ROM byte currently indexed by the table pointer (TBLPTRU:TBLPTRH:TBLPTRL in PIC18-ese) is returned, and only the low byte of the pointer is incremented (the address will then always roll over to the first table entry again upon reaching the end). Since each of the 3 LED colors is controlled independently, 3 table reads are used to set a new color combination for each node. A better, actual ‘random’ approach would be to have a continuously running 8-bit timer/counter, and at each ‘event’ (bass thump) add the current counter value to the table pointer. With a fast timer and the natural noise/jitter/imprecision of beat detection, the results would be fairly unpredictable. However, since the table size is not divisible by the number of colors per LED (3) or the number of LED nodes present (or combination of the two), any exact pattern repeat would be a very long time in coming and is unlikely to be noticed.
The effects system is (IMO) well-written and maintainable for an assembly program, making it straightforward to add and layer new visual effects (the only one written so far is the simple fader shown). In my Copious Free Time of course, so that might not happen soon. Knowing me though, I’ll probably scrap the whole chip & codebase first and move to a small DSP (PIC30?) and rewrite everything in C.
* except for a brief foray into active vibration cancelling on optical seekers, which…don’t get me started. (If it only takes out other missiles, and only exoatmospherically, shouldn’t it be called a peacehead?)