Archive for May 14th, 2009

Hack of the day: LED HSV wheel

Thursday, May 14th, 2009

I recently worked on an art project called Perceptron, an interactive dance floor (it’s cool, check it out).  The kind folks at Langton Labs hosted our build process for a while, and it was there that I met the lamp of my dreams.

It was a sphere made out of pipette covers, which are a nice milky, translucent plastic, and diffuse light wonderfully.  In the center was an array of RGB LEDs, which would cycle through colors.  There were these gorgeous blues and reds in there that you just don’t see very often: they’re probably beyond what LCD technologies can produce, so it’s a redder red and a bluer blue than you’re used to seeing.

Tonight, while killing time waiting for something, I swung by my neighborhood Radio Shack and picked up an RGB LED.  When I got home, I plugged it into my arduino and got to hacking.  I now have a one-LED version of that lamp, which makes me really happy.  It’s a gorgeous thing to behold, and involves a fun little bit of code and math.  So, of course, I’m going to share it with you.

Color-wheel arduino video

To get the color transitions to be smooth, you can’t just ramp through levels of red, green, and blue: it winds up being a bunch of peaks and valleys.  Instead, you really want to go around the HSV color wheel, changing the hue as you go.  It generates a really smooth transition, which is most pleasant.  Unfortunately, due to how crappily I’m driving the LED, it’s not as smooth as it could be: the arduino has only so much resolution in how finely it can control the level of these LEDs.  This makes for a few little jerky transitions in the blue to red return.

Here’s the code, in full:

// This assumes you're using a RadioShack #276-0028 RGB LED
// 2011-05-12: Note!  The anode is the really long pin.
// You'll need to bend it to get it into Digital8.
//
//          ||  ||  ||  ||
//          ||  ||  ||  ||
//          ||  ||  ||  ||
//          G   B   ||  ||
//                  ||  R
//                   A
//         D11 D10 D08 D09 

int common_anode = 8;
int red_pin = 9;
int blue_pin = 10;
int green_pin = 11;

int red_min = 150;
int red_max = 255;

int blue_min = 185;
int blue_max = 255;

int green_min = 195;
int green_max = 255;

void hsv_to_rgb(float h, float s, float v, unsigned char *rc, unsigned char *gc, unsigned char *bc) {
int h_i = ((int)(h/60)) % 6;

float f = (h/60) - (int)(h/60);

float r,g,b;

float p = v * (1.0 - s);
float q = v * (1.0 - f*s);
float t = (1.0 - (1.0 - f)*s);

switch(h_i) {
case 0:  r = v; g = t; b = p; break;
case 1:  r = q; g = v; b = p; break;
case 2:  r = p; g = v; b = t; break;
case 3:  r = p; g = q; b = v; break;
case 4:  r = t; g = p; b = v; break;
case 5:  r = v; g = p; b = q; break;
}

*rc = red_max - (char)((red_max - red_min)*r);
*gc = green_max - (char)((green_max - green_min)*g);
*bc = blue_max - (char)((blue_max - blue_min)*b);
}

void setup() {
pinMode(common_anode, OUTPUT);
digitalWrite(common_anode, HIGH);

pinMode(red_pin, OUTPUT);
digitalWrite(red_pin, HIGH);

pinMode(green_pin, OUTPUT);
digitalWrite(green_pin, HIGH);

pinMode(blue_pin, OUTPUT);
digitalWrite(blue_pin, HIGH);

Serial.begin(9600);
}

float h = 0.0;
float s = 1.0;
float v = 0.8;

void loop() {

unsigned char r,g,b;

h += 1;
if (h > 360.0)  h -= 360.0;

hsv_to_rgb(h,s,v, &r,&g,&b);
analogWrite(red_pin, r);
analogWrite(green_pin, g);
analogWrite(blue_pin, b);

delay(100);
}