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();

    }
  }

}

Leave a Reply

Your email address will not be published. Required fields are marked *