It’s the time of the year to start playing around with Christmas Light hardware and software. Lately, I’m focusing more on simpler (i.e, less light strings) displays and more on incorporating my “mini mega light tree“.
As described in previous posts (Driving GE G-35 Light String with Digilent Uno32, GE G35 ColorEffects Christmas Tree), this consists of a tree made from multiple GE G35 ColorEffects LED light strings. I adapted my light sequencing program to drive the addressable bulbs, using a Digilent ChipKit Uno32 ‘Arduino-compatible’ microcontoller as a protocol converter more or less. The PC software sends serial commands to the Uno32, and it in turn sends commands to the G35 strings via digital out pins. The software essentially treats the tree as a pixel grid which it can load bitmap to; transforms (scrolls and fades) can then be applied to the grid.
One problem that has plagued me, though, is that refreshes get bogged down when sending lots of commands to multiple strings. The effect is pretty severe when trying to do things like fade the whole tree, load an entire bitmap, or scroll a bitmap with lots of different-colored pixels around the tree (bitmaps with just a few pixel variations aren’t so bad, since I only have to update those pixels that change, not every single pixel). This year, I decided to take a stab at improving it.
Until now, I thought that my problem was shoveling a lot of data across the serial line, while also doing the work of sending the custom protocol out to various strings – possibly due to serial handling producing a lot of interrupts. Some other blog entries had misled me (or I misinterpreted them :-) to believe that controlling multiple strings at once with high refresh rates should be possible with Arduino-class hardware. Turns out this isn’t exactly true…or at least isn’t simple. There’s an interesting thread on the topic here.
Thinking maybe I just needed to reduce the serial data burden, I started down the path of offloading more of the work to the microcontroller in order to reduce the data going over the USB serial line. It turns out this is NOT the reason it get’s bogged down. Even when no info is sent via serial (like during the all-string-fades I currently have during setup() execution) the delay between the update of each string increases as I increase the number of strings. One string is very fast, with no significant delay…five is noticeably bogged down. This led me to calculate how much time it takes to send a single color/intensity to every bulb on five strings based on the µSec delays built into protocol functions in the sketch. Turns out it takes over 1/20th of a second to update an entire string! Throw in some overhead for logic and serial handling and it’s nearly a quarter of a second to update five strings! I really should have calculated this sooner (or should have read that thread mentioned above :-).
It’s clear that to get faster updates, more horsepower won’t necessarily help -I need parallel string addressing. Even with more horsepower, I’d still spend too much time blocked, waiting on delay functions. I think in that forum jwhite describes provides an implementation and provides code where he uses some kind of “ring buffer” and can theoretically update 6 strings simultaneously – I think by constantly looping through a timing cycle and writing the bits representing the ‘current’ color into six pins at once during each cycle? – effectively implementing a true raster rendering? The C code is actually over my head, so I’m kind of speculating here.
Since my C skills are probably too weak to take something like that on, it occurred to me that I could get the parallelism I want using multithreading. By driving each string from a separate thread, less time could be spent by the main thread being blocked by delay functions in the protocol implementation. As a result, I just ordered a NetDuino Plus. I’m hopeful that this will allow me to take advantage of the multhreading support in the .Net Micro framework, not to mention giving me an easy way to port a lot of the display logic to run on the microcontroller instead of the PC. It even includes ethernet support and an SD card, so I’m thinking I should be able to put all the bitmaps in the SD and move from USB tethering to a simple network-based protocol over Ethernet. Watch here for an update :-)
UPDATE: (11/27/2013) Dang, just realized the Thread.sleep() only has millisecond precision (and even then it’s not exact) – and although there are techniques for getting microsecond timing accuracy, they appear to be not very accurate, plus they just use loops which would probably not free up the cpu when sleeping. Going to have to think on this one some more…