GE Color Effects Lights logic in Python

I’ve been playing with these holiday lights recently. They’re very neat: each bulb has RGB LEDs embedded in it and is individually addressable (albeit with only 4 bits of resolution per color channel). Some wonderfully talented folks have reverse-engineered the protocol that the light controller uses; others have written Arduino code making it possible to snip the light’s control line, connect it to an Arduino, and begin programming your own animations. Still others are doing various neat and unusual things with the result. Personally, I just want to have some interesting light displays for New Year’s.

These other folks’ work makes this possible, but not as convenient as it could be: developing new animation routines on the Arduino is a pain in the butt. To do so you write your code in the Arduino’s irritating development environment, then flash it to the chip and hope that everything worked right. There’s a small risk of damaging the lights, and a large chance of things going wrong.

So! I wrote some bridge code that moves the light control logic into Python, which can run and be manipulated on a regular ol’ computer. The state of the lights is then sent to the Arduino many times per second (though not as many as I’d like — still, only about 1FPS slower than the string’s theoretical max) and the Arduino dutifully updates the state of the lights. This should make development of animations easier, and make it simpler to trigger or modulate them in response to network events or other things that the computer can detect.

If you have any use for this code, you can find it here. For me, there are two challenges remaining: creating some interesting animations, and connecting a second string to the Arduino.

This latter issue is a bigger problem than you might think: the lights’ address space is only 6 bits — too small to simply connect the two strings together and retain the ability to control individual lights (bulbs can share addresses, but I’d rather not do that). So the two strings need to be treated as individual entities. That’s easy enough on the Arduino side. But the Arduino is going to live at one end of this double-string, not in the middle. That means that the signal to the far string will have to be transmitted along the length of the near string. This is too much distance for a fast serial connection to traverse without being spread out into illegibility.

The solution, I’m told, is to shift the serial signal to RS-422, a higher-speed and more ethernet-like standard, then back to serial at the light string (it will travel along a twisted pair of conductors that I’ll run along the already-too-heavy first string). I have the chips to do this, and it all *looks* pretty simple. Fingers crossed…

well, that was much harder than it needed to be

I just spent some time banging my head against this problem; let me save people wandering in from Google similar trouble.

So: you’re trying to use pySerial to speak to your Arduino. It’s not working. Or sometimes it is, but only when you have the Arduino Serial Monitor window open! This makes no sense.

Here’s the deal: the Arduino’s fancy “you can flash new programs onto me without pressing my reset button!” functionality works by resetting the damn thing whenever a new serial connection is made. With the serial monitor open, the connection’s already open, so no reset happens and your script might actually work. When the monitor is closed, the Arduino resets when Python connects to it — and the Arduino might still be busy booting up when your script begins shoving bits across the link.

So! You can disable this auto-reset behavior (you’ll have to ask the Arduino people about that), but it seems simplest to just make your python script long-running and wait patiently for a beat or two after opening the serial link. Here’s a dead-simple example:

1
2
3
4
5
6
7
8
9
10
import serial, sys
from time import sleep

SERIAL_PORT = '/dev/tty.usbmodem621'
SERIAL_RATE = 9600      

ser = serial.Serial(SERIAL_PORT, SERIAL_RATE)
sleep(2)
val = int(sys.argv[1])
ser.write(chr(val))

Here’s the Arduino sketch. It just blinks the LED on pin 13 however many times you sent as a byte (I’m working on sending binary-ish data over the serial link; hence the use of raw byte values):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
byte inByte = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop()
{
  if (Serial.available() > 0) {
    inByte = Serial.read();
   
    for(int i=0;i<inByte;i++) {
      digitalWrite(13, HIGH);
      delay(500);
      digitalWrite(13, LOW);
      delay(500);
    }
  }
}

There! That was irritating.

it’s a divide all right, but it’s not all that digital

This is more or less why I don’t spend a ton of time at work worrying about the digital divide.  It’s tempting to tell yourself that the reason politically marginalized populations don’t avail themselves of their representatives’ attention is that they lack access to the necessary technologies.  If only you could write your senator through an SMS interface instead of needing an expensive, Javascript-capable smartphone!

But of course this is nonsense.  To a first approximation every American can afford to use the telephone and the post office. And in urban and suburban areas, at least, libraries make computers widely available.  What marginalized people lack is time, attention and education.  Maybe they don’t have a cellular data plan either, but first things first.  Besides, to the extent that technology access is a real problem, falling electronics prices are likely to do much more to address the issue than even the most conscientiously designed interfaces (on this score, the news on smartphone penetration is very encouraging).

Still, just because projects that concentrate on technological deficits are focusing on a small and relatively unimportant part of a dauntingly large problem doesn’t mean that such efforts aren’t laudable.  There’s always room for making a difference at the margin.  And there’s something to be said for principle.  Designing our work to be inclusive and truly democratic is important — sometimes more important than maintaining a depressingly realistic appraisal of our chances for coding our way to egalitarianism.

(I should probably add that I think creating accessible interfaces for politically engaged populations that have difficulty using technology — the disabled, seniors, etc — is a very different endeavor from the broader/gloomier digital divide issue)