Tuesday, May 19, 2009

Arduino Mood light controller

UPDATE: New source code and videos here.

Background
I love black and white, especially white. But I also love color, however I tend to change what type of color I like ALOT. So when saw the concept of coloring a surface with light I knew I had to try it at home.

The hardware I decided to use my new found love the Arduino micro controller to control some sort of light. At first I thought about using IKEAS DIODER but as MikMo pointed out the RGB LED strip from Deal Extreme is quite a much better deal. A little over $16 and free shipping from Hong Kong.


What I wanted to achive


  1. Select a specific color
  2. Pulsate the specified color
  3. Random color swash i.e. go smoothly from color to color.

Controlling the colors
I decided to use a ULN2003 with the Arduino to control the DX RGB LED strip. It can handle enough amps to drive the LED strip and it's also alot neater than multiple transistors.




Arduino control of LED strip via ULN2003
RGB LED strip 400 mA @ 12 V


R1 = 10K Potentiometer (Color selector) R2 = 100 Ohm (Pin protector)
R3 = 10K Ohm (Pulldown resistor) S1 = Switch (Mode selector)





One potentiometer to rule them all
When I first started out mixing RGB colors I used three potentiometers, one for each color. This ofcourse gives you a high resolution but also three different knobs. So inspired by the Philips LivingColors, I decided to streamline my color control using only one potentiometer connected to an analog in pin on the Arduino.



As you can see in the picture below the value 0 on the analog pin is full red and then changes through the color circle back to red as the value reaches 1023.



Note: I used Adobe Illustrator to create the image above, to help me think, but when using gradient fill in Illustrator some colors take up more space than others. At least it looks like that if you space them evenly. I don't know if this also is the case with my color mixer, but I haven't really bothered to figure that out.


At first I used alot of steps before I realized I could remove some of them. Orange for example is produced when you have more red than green on your way to yellow (full red and full green). But purple requires both full red and full blue. Hence some I could remove some steps and had to keep some of them.




Videos



Button presses in the video
Filmed in broad daylight, hence the colors don't really come out too well.
1. On - Select a color with the color selector.
2. Pulse - Pulsate the selected color.
3. Random/Cycle - Cycle randomly through the palette.
4. Off.



Pulse Mode
Filmed in a dark room with the RGB-strip hidden behind the laptop screen.





Random/Cycle Mode
Filmed in a dark room with the RGB-strip hidden behind the laptop screen.


Source code

UPDATE: New source code and videos here.


The finished code with four modes, also available as a download here.
Formatted for Blogger with: formatmysourcecode.blogspot.com
/*
RGB LED controller
4 modes: off, color select, color pulse and random cycle/pulse
By Markus Ulfberg 2009-05-19

Thanks to: Ladyada, Tom Igoe and
everyone at the Arduino forum for excellent
tutorials and everyday help.

*/

// set the ledPins
int ledRed = 10;
int ledGreen = 9;
int ledBlue = 11;

// color selector pin
int potPin = 1;

// lightMode selector
int switchPin = 2;

// light mode variable
// initial value 0 = off
int lightMode = 0;

// LED Power variables
byte redPwr = 0;
byte greenPwr = 0;
byte bluePwr = 0;

// Variables for lightMode 2
// variables for keeping pulse color
byte redPulse;
byte greenPulse;
byte bluePulse;

// Set pulse to down initially
byte pulse = 0;

// floating variables needed to be able to pulse a fixed color
float redFloat;
float greenFloat;
float blueFloat;

// the amount R,G & B should step up/down to display an fixed color
float redKoff;
float greenKoff;
float blueKoff;

// Variables for lightMode 3
// set the initial random colors
byte redNew = random(255);
byte greenNew = random(255);
byte blueNew = random(255);

// misc interface variables
// potVal keeps the value of the potentiometer
int potVal;
// value from the button (debounce)
int switchVal;
int switchVal2;
// buttonState registers if the button has changed
int buttonState;

void setup()
{
pinMode(ledRed, OUTPUT);
pinMode(ledGreen, OUTPUT);
pinMode(ledBlue, OUTPUT);

pinMode(potPin, INPUT);

pinMode(switchPin, INPUT);
buttonState = digitalRead(switchPin);

// serial for debugging purposes only
Serial.begin(9600);
}

void loop()
{
switchVal = digitalRead(switchPin);      // read input value and store it in val
delay(10);                         // 10 milliseconds is a good amount of time
switchVal2 = digitalRead(switchPin);     // read the input again to check for bounces
if (switchVal == switchVal2) {                 // make sure we got 2 consistant readings!
if (switchVal != buttonState) {          // the button state has changed!
if (switchVal == LOW) {                // check if the button is pressed
if (lightMode == 0) {          // light is off
lightMode = 1;             // light is on and responds to pot
} else {
if (lightMode == 1) {
lightMode = 2;           // light pulsates in the latest color from pot
} else {
if (lightMode == 2) {
lightMode = 3;           // light changes randomly
} else {
lightMode = 0;             // light is off 
}
}
}
}
}
buttonState = switchVal;                 // save the new state in our variable
}
if (lightMode == 0) {      // turn light off
analogWrite(ledRed, 0);
analogWrite(ledGreen, 0);
analogWrite(ledBlue, 0);
}
if (lightMode == 1) {        // set fixed color

// read the potentiometer position
potVal = analogRead(potPin);


// RED > ORANGE > YELLOW
if (potVal > 0 && potVal < 170) {
redPwr = 255;
bluePwr = 0;
analogWrite(ledRed, redPwr);
greenPwr = map(potVal, 0, 170, 0, 255);
analogWrite(ledGreen, greenPwr);
analogWrite(ledBlue, bluePwr);
}

// YELLOW > LIME?? > GREEN
if (potVal > 170 && potVal < 341) {
greenPwr = 255;
bluePwr = 0;
analogWrite(ledGreen, greenPwr);
redPwr = map(potVal, 341, 170, 0, 255);
analogWrite(ledRed, redPwr);
analogWrite(ledBlue, bluePwr);
}

// GREEN > TURQOUISE
if (potVal > 341 && potVal < 511) {
greenPwr = 255;
redPwr = 0;
analogWrite(ledGreen, greenPwr);
bluePwr = map(potVal, 341, 511, 0, 255);
analogWrite(ledBlue, bluePwr);
analogWrite(ledRed, redPwr);
}

// TURQOUISE > BLUE
if (potVal > 511 && potVal < 682) {
bluePwr = 255;
redPwr = 0;
analogWrite(ledBlue, bluePwr);
greenPwr = map(potVal, 682, 511, 0, 255);
analogWrite(ledGreen, greenPwr);
analogWrite(ledRed, redPwr);
}

// BLUE > PURPLE
if (potVal > 682 && potVal < 852) {
bluePwr = 255;
greenPwr = 0;
analogWrite(ledBlue, bluePwr);
redPwr = map(potVal, 682, 852, 0, 255);
analogWrite(ledRed, redPwr);
analogWrite(ledGreen, greenPwr);
}

// PURPLE > RED
if (potVal > 852 && potVal < 1023) {
redPwr = 255;
greenPwr = 0;
analogWrite(ledRed, redPwr);
bluePwr = map(potVal, 1023, 852, 0, 255);
analogWrite(ledBlue, bluePwr);
analogWrite(ledGreen, greenPwr);
}
redFloat = float(redPwr);
greenFloat = float(greenPwr);
blueFloat = float(bluePwr);

redKoff = redFloat / 255;
greenKoff = greenFloat / 255;
blueKoff = blueFloat / 255;

redPulse = redPwr;
greenPulse = greenPwr;
bluePulse = bluePwr;

/*
// Debug
Serial.print("redFLoat: ");
Serial.print(redFloat, DEC);
Serial.print(" redPwr: ");
Serial.print(redPwr, DEC);
Serial.print(" greenFloat: ");
Serial.print(greenFloat, DEC);
Serial.print(" greenPwr: ");
Serial.print(greenPwr, DEC);
Serial.print(" blueFloat: ");
Serial.print(blueFloat, DEC);
Serial.print(" bluePwr: ");
Serial.println(bluePwr, DEC);
// End debug
*/

}

if (lightMode == 2) {     // pulse fixed color

// display the colors
analogWrite(ledRed, redFloat);
analogWrite(ledGreen, greenFloat);
analogWrite(ledBlue, blueFloat);

// add delay here for speed control
delay(5);

// pulse down
if (pulse == 0) {
if (redFloat > 10) {
redFloat = redFloat - redKoff;
}
if (greenFloat > 10) {
greenFloat = greenFloat - greenKoff;
}
if (blueFloat > 10) {
blueFloat = blueFloat - blueKoff;
}

// If all xFloat match 10 get pulse up
if (byte(redFloat) <= 10) {
if (byte(greenFloat) <= 10) {
if (byte(blueFloat) <= 10) {
pulse = 1;
}
}
}
}
// Pulse up
if (pulse == 1) {
if (redFloat < redPulse) {
redFloat = redFloat + redKoff;
}
if (greenFloat < greenPulse) {
greenFloat = greenFloat + greenKoff;
}
if (blueFloat < bluePulse) {
blueFloat = blueFloat + blueKoff;
}
// If all Pwr match Pulse get pulse down

if (byte(redFloat) == redPulse) {
if (byte(greenFloat) == greenPulse) {
if (byte(blueFloat) == bluePulse) {
pulse = 0;
}
}
}
}
/*
// Debug
Serial.print("redFloat: ");
Serial.print(redFloat, DEC);
Serial.print(" redPulse: ");
Serial.print(redPulse, DEC);
Serial.print(" greenFloat: ");
Serial.print(greenFloat, DEC);
Serial.print(" greenPulse: ");
Serial.print(greenPulse, DEC);
Serial.print(" blueFloat: ");
Serial.print(blueFloat, DEC);
Serial.print(" bluePulse: ");
Serial.print(bluePulse, DEC);
Serial.print(" pulse: ");
Serial.println(pulse, DEC);
// End debug
*/
}

if (lightMode == 3) {  // randomsize colorNew and step colorPwr to it
if (redPwr > redNew) {
redPwr--;
}
if (redPwr < redNew) {
redPwr++;
}
if (greenPwr > greenNew) {
greenPwr--;
}
if (greenPwr < greenNew) {
greenPwr++;
}
if (bluePwr > blueNew) {
bluePwr--;
}
if (bluePwr < blueNew) {
bluePwr++;
}

// If all Pwr match New get new colors

if (redPwr == redNew) {
if (greenPwr == greenNew) {
if (bluePwr == blueNew) {
redNew = random(254);
greenNew = random(254);
blueNew = random(254);
}
}
}

// display the colors
analogWrite(ledRed, redPwr);
analogWrite(ledGreen, greenPwr);
analogWrite(ledBlue, bluePwr);
delay(20);
}
}