LED Driver Code
This is the draft code I am using to drive the RGB LED string I am eventually going to build into a lighting fixature, that will give me soothing red lights in the evening and then wake me up with brighting red and orange lights, fading into blue.
/////////////////////// INCLUDES ///////////////////////////////////
#include <IRremote.h>
// set up irReciever
const int irReceiverPin = 3;
IRrecv irrecv(irReceiverPin);
decode_results results;
/////////////////////// VARIABLES ///////////////////////////////////
// LED pins
int rLED = 9;
int gLED = 10;
int bLED = 6;
// Current LED color (0... 255 brightness)
int rC = 0;
int gC = 0;
int bC = 0;
// last remote code (needs to be an unsigned long
unsigned long remoteCode = 0;
///////////////// TEMPERATURE and BRIGHTNESS OF COLOR
// Color Temperature, In Kelvin
// unsigned long needed for very cold, very blue light temperatures,
// e.g. above 32,700 kelvin (we can do that even if those colors are mostly theortical!)
unsigned long temp = 3000;
// Brightness
int bright = 100;
//////////////// TIMERS FOR THE LOOP
// Time of Last Loop
unsigned long prevMS = 0;
// Time of Last Button Press
unsigned long buttonMS = 0;
////////////// SPECIAL MODES (e.g. time delay dimming for sleep or rise)
// special mode version
int specialMode = 0;
// time elapsed in special mode
unsigned long specialModeMS = 0;
// time elapsed in special mode LOOP
unsigned long specialModeMSLoop = 0;
// what task number are we on in specialMode
int specialModeTK = 1;
/////////////////////// FUNCTIONS ///////////////////////////////////
// Kelvin to RGB Code is roughly based on this javascript that I re-wrote in C#
// http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
// unsigned long needed for very cold, very blue light temperatures,
// e.g. above 32,700 kelvin (we can do that even if those colors are mostly theortical!)
void kRGB (unsigned long kelvin, int *r, int *g, int *b) {
Serial.println(kelvin);
int t = kelvin / 100;
if(t <= 66 ){
*r = 255;
*g = t;
*g = 99.4708025861 * log(*g) - 161.1195681661;
if( t <= 19){
*b = 0;
}
else {
*b = t-10;
*b = 138.5177312231 * log(*b) - 305.0447927307;
}
}
else if (t < 320) {
*r = t - 60;
*r = 329.698727446 * pow(*r, -0.1332047592);
*g = t - 60;
*g = 288.1221695283 * pow(*g, -0.0755148492 );
*b = 255;
}
else {
// this is mostly made up, just testing
// as original code doesn't look at imaginary colors
// beyond about 32,000 kelvin
// this will create negative numbers at times
// but setColor will just zero out the channels
*b = 255;
*r = t - 255;
*g = t - 255;
}
}
// Dim Each Channel Equally
void dimColor(float percent, int *r, int *g, int *b) {
if (percent > 100) percent = 100;
float p = (percent/100);
float rD = *r;
float gD = *g;
float bD = *b;
*r = round(rD * p);
*g = round(gD * p);
*b = round(bD * p);
}
// Simple Function to Set Colors
void setColor(int r, int g, int b) {
// occassionally we will get fed
// values outside of the 0-255 acceptable
// range so we fix here
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
analogWrite(rLED, r);
analogWrite(gLED, g);
analogWrite(bLED, b);
// set the internal color channels
rC = r;
bC = g;
gC = b;
// debugging output
Serial.print("r=");
Serial.print(r);
Serial.print(", g=");
Serial.print(g);
Serial.print(", b=");
Serial.print(b);
Serial.print("\n");
}
// set color by temperature
void setColorByTemp(unsigned long tempColor) {
temp = tempColor; // set the internal temp color
kRGB(temp, &rC, &gC, &bC);
dimColor(bright, &rC, &gC, &bC);
setColor(rC, gC, bC);
}
// set brightness
void setColorByBrightness(int brightness) {
bright = brightness; // set the internal brightness
kRGB(temp, &rC, &gC, &bC);
dimColor(bright, &rC, &gC, &bC);
setColor(rC, gC, bC);
}
// Setup Function
void setup() {
// for debugging
Serial.begin(9600);
// load IR pin
irrecv.enableIRIn();
// load pin
pinMode(rLED, OUTPUT);
pinMode(gLED, OUTPUT);
pinMode(bLED, OUTPUT);
// start LEDs
kRGB(temp, &rC, &gC, &bC);
dimColor(bright, &rC, &gC, &bC);
setColor(rC, gC, bC);
}
// Loop cycles through the colors
void loop() {
// check to see if any mode requests have come in
if (irrecv.decode(&results)) {
Serial.println(results.value);
irrecv.resume(); // Receive the next value
// this is to prevent button bounce
// from somebody holding the button too long
if (millis() - buttonMS >= 400) {
remoteCode = results.value;
buttonMS = millis();
}
}
// every half a second see if user
// has requested any changes
if (millis() - prevMS >= 500) {
// if any remote codes are hit, interrupt any special modes
// reset to default modes before taking next option
if (remoteCode && specialMode) {
specialMode = 0;
specialModeMS = 0;
specialModeTK = 1;
temp = 3000;
bright = 100;
kRGB(temp, &rC, &gC, &bC);
dimColor(bright, &rC, &gC, &bC);
setColor(rC, gC, bC);
}
// turn light string on or off
if (remoteCode == 4105841032) {
if (!rC && !bC && !gC) {
kRGB(temp, &rC, &gC, &bC);
dimColor(bright, &rC, &gC, &bC);
setColor(rC, gC, bC);
Serial.print("on button activated");
}
else {
setColor(0,0,0);
Serial.print("off button activated");
}
}
// lower or raise the brightness
if (remoteCode == 2209452902 ) {
if (bright > 10) {
bright -= 10;
}
setColorByBrightness(bright);
Serial.print("dim button activated");
}
if (remoteCode == 1752382022) {
if (bright < 90) {
bright += 10;
}
setColorByBrightness(bright);
Serial.print("bright button activated");
}
// warm or chill the color temperature
// above 3500 kelvin we make bigger steps
if (remoteCode == 1595074756) {
if (temp < 3500) {
temp += 200;
}
else {
temp += 700;
}
setColorByTemp(temp);
Serial.print("cool button activated");
}
if (remoteCode == 412973352) {
if (temp > 3500) {
temp -= 700;
}
else if (temp > 100) {
temp -= 200;
}
setColorByTemp(temp);
Serial.print("warm button activated");
}
// color temperature pre-sets
if (remoteCode == 3778927144) {
setColorByTemp(500);
Serial.print("one button activated");
}
if (remoteCode == 2908251746) {
setColorByTemp(1000);
Serial.print("two button activated");
}
if (remoteCode == 657459652) {
setColorByTemp(1500);
Serial.print("three button activated");
}
if (remoteCode == 4120482440) {
setColorByTemp(2000);
Serial.print("four button activated");
}
if (remoteCode == 1931099650) {
setColorByTemp(2500);
Serial.print("five button activated");
}
if (remoteCode == 742730860) {
setColorByTemp(3000);
Serial.print("six button activated");
}
if (remoteCode == 1167253836) {
setColorByTemp(3500);
Serial.print("seven button activated");
}
if (remoteCode == 1747313982) {
setColorByTemp(6000);
Serial.print("eight button activated");
}
if (remoteCode == 2340753640) {
setColorByTemp(10000);
Serial.print("nine button activated");
}
if (remoteCode == 3119867746) {
bright = 100;
temp = 5000; // rough ball park
setColor(255,255,255);
Serial.print("zero button activated");
}
////////// special modes ////////////
if (remoteCode == 1976685926) {
// special mode 1 - Sunset
specialMode = 1;
specialModeMS = millis();;
specialModeTK = 1;
// start as 50% dim, warm yellow light
setColorByTemp(1200);
setColorByBrightness(50);
Serial.print("back button activated");
}
if (remoteCode == 3980777284) {
// special mode 2 - Sunrise
specialMode = 2;
specialModeMS = millis();
specialModeMSLoop = millis();
specialModeTK = 1;
setColorByTemp(500);
setColorByBrightness(5);
Serial.print("forward button activated");
}
if (remoteCode == 1784778242) {
// special mode 3 - Sunset then eight hours later, sunrise
specialMode = 3;
specialModeMS = millis();;
specialModeTK = 1;
// start as 50% dim, warm yellow light for the sunset mode
setColorByTemp(1200);
setColorByBrightness(50);
Serial.print("play button activated");
}
if (remoteCode == 1784778242) {
// special mode 3 - Sunset then seven hours later, sunrise
specialMode = 4;
specialModeMS = millis();
specialModeTK = 1;
// start as 50% dim, warm yellow light for the sunset mode
setColorByTemp(1200);
setColorByBrightness(50);
Serial.print("stop button activated");
}
// reset remote code to zero to avoid repeating
// the action and rest the loop timer to zero
remoteCode = 0;
prevMS = millis();
}
/////////// this section is for special timer modes ////////////////////////
if (specialMode == 1 || (specialMode == 3 && millis() - specialModeMS < 10L * 60 * 1000) ||
(specialMode == 4 && millis() - specialModeMS < 10L * 60 * 1000) ) {
// Task 1 for Special Mode 1
// Task 1 for Special Mode 3, 4 (first ten minutes)
//
// Special Mode 1 - Sunset Mode
// Special Mode 2 - Sunset Mode; See Below for Sunrise
//
// oranges and reds, lights fade out to black in 5 minutes
// 50 cycles * 10 seconds = 5 minutes
// initial color set above in "play button activated section"
if (millis() - specialModeMSLoop > 10L * 1000) {
if (bright > 0) {
// fifty cycles
// reduce color temperature by 10 degrees kelvin
// from 1000k to 500k to make a nice warm temperature
temp -= 10;
setColorByTemp(temp);
// reduce from 50 brightness to 0 brightness
// then shut off the unit
bright -= 1;
setColorByBrightness(bright);
}
else {
// we are done. turn off special mode, reset colors
specialMode = 0;
setColor(0,0,0);
temp = 3000;
bright = 100;
specialModeTK++; // task 1 is done
}
specialModeMSLoop = millis(); // reset LOOP timer
}
}
if (specialMode == 2 || (specialMode == 3 && millis() - specialModeMS > 8L * 60 * 60 * 100) ||
(specialMode == 4 && millis() - specialModeMS > 7L * 60 * 60 * 100)) {
// Task 1 for Special Mode 2
// Task 2 for Special Mode 3 (eight hours later)
// Task 2 for Special Mode 4 (seven hours later)
//
// Special Mode 2 - Sunrise Mode
// Special Mode 3 and Special Mode 4, Task 2 - Sunrise in the Morning Mode
//
// 500 kelvin light driven at 5%, incremented 100 kelvin for 95 times
// to bring us up to 9,500 kelvin at 100% brightness
//
// Once we reach 9,500 kelvin, we pull up the blue channel (bC) at a rate of
// 10/256th or 4% each ten seconds until we reach 255 (full brightness).
//
// Then to reach full brightness, which is less blue and more light
// we pull up the red (rC) and green chanels at a rate of
// 10/256th or 4% each ten seconds until we reach 255 (full brightness all channels).
//
// 95 cycles * 6 seconds = 570 seconds or 9 1/2 minutes to
// full brightness
// initial color set above in "play button activated section"
if (millis() - specialModeMS > 6L * 1000) {
if (bright < 100) {
// fifty cycles
// reduce color temperature by 100 degrees kelvin
// from 500 K to 10,0000 to a nice daylight temperature
temp += 100;
setColorByTemp(temp);
// reduce from 50 brightness to 0 brightness
// then shut off the unit
bright++;
setColorByBrightness(bright);
}
else if (bright > 100 && rC != 255 && bC != 255 & gC != 255){
// if the blue channel isn't at 255 increment it by 4% or 10/256th
if (bC < 255) {
bC+= 10; // if we overrun, that's fine, we check and fix that in setColorw()
}
else {
// do the same equally with red and green channelsl
rC+=10;
gC+=10;
}
setColor(rC,gC,bC);
}
else {
// we are done. turn off special mode, reset colors
specialMode = 0;
setColor(255,255,255); // end at maximum brightness
temp = 3000;
bright = 100;
specialModeTK++; // task 2 is done
}
specialModeMS = millis();
}
}
}