Tag Archives: RGB LED strip

RGB LED Strip Controllers: IR Codes

Back around Christmas time, my son Samuel and I designed and built an alarm clock with the unusual capability of waking him up with a pulsing bright light. To the RGB LED Strip Light Alarm Clock we added an IR receiver module and code to allow independent control of the LED strip using a readily available 24-key IR transmitter. In addition we also coded four special light effects.

Along with the 24-key IR remote that we used in the clock design, I have recently purchased a 44-key remote from eBay and have now mapped the transmitted IR code values for each key button. This makes it really easy to add IR control using a similar 24- or 44-key remote to any future project.

24keyIRRemote

24-key IR Remote Transmitter

24keyIRRemote Codes

24-key IR Remote Codes

44-key IR Remote Transmitter

44-key IR Remote Transmitter

44-keyIRRemoteCodes

44-key IR Remote Codes

Continue reading

IR Remote Special Functions

After building the RGB LED Strip Alarm Clock, the software development continued with the addition of IR Remote functionality with operation of the preset colours and intensity controls. The four special functions – FLASH, STROBE, FADE and SMOOTH – have now been added to the software load. These four functions provide interesting dynamic light effects by directly controlling the LED strip. In a similar manner to other IR Remote functions, an alarm event overrides this control of the LED strip to execute the alarm light effect.

The new four IR Remote effects are shown here…

  1. The SMOOTH effect very slowly moves around the HSV colour wheel, starting from the hue and saturation of a selected colour preset.
  1. The FLASH effect quickly raises and lowers the intensity of the light at a hue selected from any of the preset colours.
  1. The FADE effect raises and lowers the intensity of the light while also rotating the hue – starting from a selected preset colour – around the colour wheel.

4. The STROBE effect pulses high intensity white light.

The Arduino code to perform these additional functions can be seen here…

[codesyntax lang=”php” title=”RGB Strip Alarm Clock – IR Remote Special Functions” blockstate=”collapsed”]

//****************************************************************************************
//                             LIGHT STRIP WAKE UP ALARM CLOCK 
//                      Samuel Rochefort and Adrian Jones, Janueary 2015
//****************************************************************************************
 
// Build 1
//   r1 141218 - initial build
//   r2 141219 - addition of date and alarm time and its setting
//   r3 141226 - triangle pulsing
//   r4 141227 - switches for mode control, timeout.
//   r6 141230 - addition of date setting, hue & saturation setting and immediate start of alarm effect
//   r7 141231 - addition of effects time variables to EEPROM and modes
//   r8 150103 - addition of IR receiver and control of strip colour (based on  Ken Shirriff's work http://arcfn.com )
//   r9 150109 - addition of IR functions FLASH, STROBE, FADE and SMOOTH
//               FLASH:  fast fade of preset colour up and down
//               STROBE: quickly flashes high intensity white
//               FADE:   slowly fades colour up and down and cycles through hue
//               SMOOTH: very slowly fades preset colour up and down
//*****************************************************************************************
#define build 1
#define revision 9
//*****************************************************************************************

// ARDUINO PIN USAGE
// LED Strip: D9 - red, D10 - green, D6 - blue
// LCD: D3-en,D4-rs,D5-d4,D12-d5,D7-d6,D8-d7
// RTC: A4-SDA, A5-SCL
// Switches: A0-Mode(1), A1-Inc(2), A2-Dec(3), A3-Alarm(4)
// IR receiver: D2-IR Input
// NOTE: Conflict with D11... do not use! 


#include <avr/pgmspace.h>


// IR remote
#include <IRremote.h>
#define RECV_PIN 2                // IR receiver pin
IRrecv irrecv(RECV_PIN);
unsigned long codeValue;          // The code value if not raw
unsigned int rawCodes[RAWBUF];    // The durations if raw
decode_results results;
String mess="";

// RTC
#include <Wire.h>                 // Interface to use SDA/SCL of RTC
#include <RTClib.h>               // RTC-Library
RTC_DS1307 RTC;                   // RTC module (SDA - A4, SCL - A5)
DateTime now;                     // current time
int ch,cm,cs,lastsec;             // time variables
int cdy,cmn,cyr,cdw;              // date variables

// LED strip drivers
#define rled       9              // red LED strip
#define gled      10              // green LED strip
#define bled       6              // blue LED strip
int r, g, b;
float intensity;


#include <LiquidCrystal.h>
// initialize LCD library (i.e. lcd(rs, en, d4, d5, d6, d7))
LiquidCrystal lcd(3, 4, 5, 12, 7, 8);
byte bell[8] = {0x1F,0x11,0x00,0x0E,0x11,0x1F,0x11,0x00};   // bell character
byte nbll[8] = {0x00,0x0E,0x00,0x0E,0x11,0x1F,0x11,0x00};   // inverse bell character
byte irC[8] = {0x00,0x10,0x00,0x17,0x15,0x14,0x14,0x00};   // IR ON

// mode switches
#define modSw      A2              // mode switch (1)
#define incSw      A3              // increment switch (2) 
#define decSw      A0              // decrement switch (3) 
#define almSw      A1              // alarm switch (4)

#define mode_Normal        0      // normal mode
#define mode_AlarmDisplay  1      // display alarm
#define mode_DateDisplay   2      // display date 
#define mode_AlarmHourSet  3      // set alarm hours
#define mode_AlarmMinSet   4      // set alarm mins
#define mode_TimeHourSet   5      // set time hours
#define mode_TimeMinSet    6      // set time mins
#define mode_DateDaySet    7      // set date day 
#define mode_DateMnthSet   8      // set date month
#define mode_DateYearSet   9      // set date year
#define mode_HueSet       10      // hue set
#define mode_SatSet       11      // saturation set
#define mode_PulseTime    12      // pulse time
#define mode_doAlarm      13      // start effect immediately

#define num_modes         14      // number of modes (mode 7, 8, 9 not used)
int mode = mode_Normal;
boolean modFlag=false;
boolean incFlag=false;
boolean decFlag=false;
boolean alsFlag=false;
int inc = 0;              

// timeout
long interval = 10000;            // resets mode after this duration
long previousMillis = 0;          // old count
unsigned long currentMillis;      // current counter

// alarm and eeprom storage
#include <EEPROM.h>               // for alarm and mode data storage
boolean alarm=true;               // alarm on/off
int ah,am;

// light effect for alarm
boolean alFlag=false;
#define effectTime  300           // time (secs) for light to stay lit
int effectDelay=40;               // time (ms) for each step in effect (1-100)
int effectHue=140;                // hue for effect
int effectSat=80;                 // saturation value for effect (*100)
int tCount;                       // effect timer (secs)
int pulse;                        // pulse effect counter

// IR
int codeType = -1;                // The type of received code
int codeLen;                      // The length of the received code
int irI=50,irS=100,irH=240;       // values of intensity, saturation and hue for IR receiver
boolean useIR=false;              // ON/OFF toggle
boolean vueIR=false;              // see IR settings
#define none     0                // IR effect
#define flash    1                // IR effect
#define strobe   2                // IR effect
#define fade     3                // IR effect
#define smooth   4                // IR effect
String irText[] = {"","FLASH ","STR0BE","FADE  ","SMOOTH"};

int IRmode=none;                  
boolean IReffect=false;           // IR effect enabled
int irCount=0;
#define irFlashCount   300        // IR flash effect limit
#define irStCount      100        // IR strobe effect count
#define irFadeCount   1000        // IR fade effect time
#define irSmoothCount 1000        // IR smooth effect time
 
int *set[] = {&ah, &am, &effectHue, &effectSat, &effectDelay, &irI, &irS, &irH};          // values to set to EEPROM

boolean doSerial=true;            // enable/disable serial monitor

//*****************************************************************************************
//                                      Initial Setup
//*****************************************************************************************
void setup() {

  if(doSerial) {
    Serial.begin(57600); 
    Serial.println(F("RGB LED STRIP WAKE UP ALARM CLOCK")); 
    Serial.println(F("Samuel Rochefort and Adrian Jones, Dec. 2014"));
    Serial.print(F("Build ")); 
    Serial.print(build); 
    Serial.print(F(".")); 
    Serial.println(revision);
    Serial.print(F("Free RAM: "));  
    Serial.print(freeRam()); 
    Serial.println(F("B\r\n"));
  }

  Wire.begin();
  RTC.begin();
  if (! RTC.isrunning()) {   
    RTC.adjust(DateTime(__DATE__, __TIME__));
    if(doSerial) Serial.println(F("> RTC reset"));
  } else {
    if(doSerial) Serial.println(F("> RTC running"));
  }

  startSwitches();                        // set up switches
  restoreSettings();                      // alarm time, effect parameters
  startLCD();                             // satrt LCD
  startLEDStrip();                        // set up LED drivers
  resetTimeout();                         // reset mode timeout
  irrecv.enableIRIn();                    // Start the IR receiver
} 


//*****************************************************************************************
//                                      MAIN LOOP
//*****************************************************************************************
void loop() {
  checkIRReceiver();                      // check IR receiver
  doIRFunction();                         // do IR fucntion if selected
  getTime();                              // get latest time 
  checkSwitches();                        // what are you doing?
  doAdjust();                             // adjust alarm or time
  if(cs != lastsec) {                     // normal mode
    checkTimeout();                       // check timeouts if not in normal mode
    checkAlarm();                         // alarm set?
    checkBd();                            // 
    printTime();                          // write information to LCD and serial port 
    lastsec=cs;                           // reset seconds
    tCount++;                             // increment effects counter
  }
  if(alFlag) doLightEffect();             // if in alarm mode, do light effect
} 


//*****************************************************************************************
//                                      SUBROUTINES
//*****************************************************************************************

// startLEDStrip: configure pins for LED strip drivers.
void startLEDStrip() {
    pinMode(rled, OUTPUT);
    pinMode(gled, OUTPUT);
    pinMode(bled, OUTPUT);
    for(intensity = 0.0; intensity < 1.0; intensity += 0.01) {
      doColourLED(0, 0.0, min(intensity, 1.0));
      delay(1);  
   }   
    for(intensity = 1.0; intensity > 0.0; intensity -= 0.01) {
      doColourLED(0, 0.0, max(intensity, 0.0));
      delay(1);  
   }   
}


//*****************************************************************************************
// startLSwitches: configure pins for mode switches.
void startSwitches() {
    pinMode(modSw, INPUT_PULLUP);
    pinMode(incSw, INPUT_PULLUP);
    pinMode(decSw, INPUT_PULLUP);
    pinMode(almSw, INPUT_PULLUP); 
}

//*****************************************************************************************
// startLCD: start 2x16 LCD display
void startLCD() {
    lcd.begin(16, 2);                    // set up the LCD  for 16 columns and 2 rows
    lcd.createChar(0, bell);             // bell character
    lcd.createChar(1, nbll);             // inverse bell character
    lcd.createChar(2, irC);              // IR transmitter character
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("LED ALARM CLOCK");        // clear and write first line
    lcd.write(byte(2));                  // IR character
    
    lcd.setCursor(0, 1);
    lcd.print(F("B ")); 
    lcd.print(build); 
    lcd.print(F(".")); 
    lcd.print(revision);
    lcd.print(F(" RAM "));  
    lcd.print(freeRam()); 
    lcd.print(F("B "));
    delay(1000);
}

//*****************************************************************************************
// doAdjust: adjust alarm or time
void doAdjust() {
  switch(mode) {
    case mode_AlarmHourSet: 
      ah = ah + inc; 
      if(ah >24) ah = 1; 
      if(ah < 1) ah = 24;
      inc=0;  saveSettings(); break;
 
    case mode_AlarmMinSet: 
      am = am + inc; 
      if(am >59) am = 0;  
      if(am < 0) am = 59;
      inc=0;  saveSettings(); break;
 
    case mode_TimeHourSet: 
      ch = ch + inc; 
      if(ch >24) ch = 1; 
      if(ch < 1) ch = 24;
      if(inc != 0) RTC.adjust(DateTime(now.year(), now.month(), now.day(), ch, now.minute(), 0 ));
      inc=0; break;
 
    case mode_TimeMinSet: 
      cm = cm + inc; 
      if(cm >59) cm = 0;  
      if(cm < 0) cm = 59;
      if(inc != 0) RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), cm, 0 ));
      inc=0; break;

    case mode_DateDaySet: 
      cdy = cdy + inc; 
      if(cdy >31) cdy = 1;  
      if(cdy < 1) cdy = 31;
      if(inc != 0) RTC.adjust(DateTime(now.year(), now.month(), cdy, now.hour(), now.minute(), 0 ));
      inc=0; break;

    case mode_DateMnthSet: 
      cmn = cmn + inc; 
      if(cmn >12) cmn = 1;  
      if(cmn < 1) cmn = 12;
      if(inc != 0) RTC.adjust(DateTime(now.year(), cmn, now.day(), now.hour(), now.minute(), 0 ));
      inc=0; break;

    case mode_DateYearSet: 
      cyr = cyr + inc;
      if(inc != 0) RTC.adjust(DateTime( (cyr+2000), now.month(), now.day(), now.hour(), now.minute(), 0 ));
      inc=0; break;

    case mode_HueSet: 
      effectHue = (effectHue + (5*inc));
      if(effectHue > 360) effectHue=0;
      if(effectHue < 0)   effectHue=360;
      inc=0; saveSettings(); break;  

    case mode_SatSet: 
      effectSat = (effectSat + inc);
      if(effectSat < 0) effectSat=0;
      if(effectSat > 100) effectSat=100;
      inc=0; saveSettings(); break;  
      
    case mode_PulseTime:
      effectDelay = effectDelay + (5*inc);
      if(effectDelay < 1)   effectDelay=1;
      if(effectDelay > 100) effectDelay=100;
      inc=0; saveSettings(); break;  
  
    case mode_doAlarm: 
      if(inc ==  1) {alarm=true; alFlag=true;} 
      if(inc == -1) {tCount+=1000;}
      inc=0; break;
 }
}


//*****************************************************************************************
// printTime(): print time on serial port and lcd
void printTime() {
  if(doSerial) {
    Serial.print(F("Mode ")); Serial.print(mode);
    Serial.print(F("\tTime: ")); 
    Serial.print(ch); Serial.print(F(":")); 
    Serial.print(cm); Serial.print(F(":")); 
    Serial.print(cs);

    Serial.print(F("\tDate: ")); 
    Serial.print(cyr); Serial.print(F("/")); 
    Serial.print(cmn); Serial.print(F("/")); 
    Serial.print(cdy);

    Serial.print(F("\tButton: "));Serial.print(inc); 
    
    if(alarm)  { Serial.print(F("\tAlarm: "));  Serial.print(ah); Serial.print(F(":")); Serial.print(am); }      
    if(alFlag) { Serial.print(F("\tEffect: ")); Serial.print(intensity); }
    
    if(useIR)  { 
                 Serial.print(F("\tIR: ")); Serial.print(mess); Serial.print(" ");
                 Serial.print(irH); Serial.print(","); 
                 Serial.print(irS); Serial.print(",");
                 Serial.print(irI);
    }    
    Serial.println("");
  }
  lcd.setCursor(0, 1);
  lcd.noBlink();
  if(mode == mode_Normal) {
    if(useIR && vueIR) {
      if(IReffect) {
        lcd.print(F("Effect: ")); lcd.print(irText[IRmode]); lcd.print(F(" ")); 
        lcd.setCursor(15, 1);
        if(alarm) { if(alFlag && (cs%2)) lcd.write(byte(1)); else lcd.write(byte(0)); } 
      } else { 
        lcd.print(F("HSV:"));
        if(irH<100) lcd.print(F(" "));if(irH<10) lcd.print(F(" ")); lcd.print(irH); lcd.print(F(","));
        if(irS<100) lcd.print(F(" "));if(irS<10) lcd.print(F(" ")); lcd.print(irS); lcd.print(F(","));
        if(irI<100) lcd.print(F(" "));if(irI<10) lcd.print(F(" ")); lcd.print(irI); lcd.print(F(" "));
        lcd.setCursor(15, 1);
        if(alarm) { if(alFlag && (cs%2)) lcd.write(byte(1)); else lcd.write(byte(0)); } 
      }
    } else {
      lcd.print(F("Time: "));
      if(ch<10) lcd.print(F("0")); lcd.print(ch); lcd.print(F(":"));
      if(cm<10) lcd.print(F("0")); lcd.print(cm); lcd.print(F(":"));
      if(cs<10) lcd.print(F("0")); lcd.print(cs); 
      if(useIR) lcd.write(byte(2)); else lcd.print(F(" "));
      if(alarm) { if(alFlag && (cs%2)) lcd.write(byte(1)); else lcd.write(byte(0)); } else { lcd.print(F(" ")); }
    } 
  }

  if(mode == mode_DateDisplay) { 
    lcd.print(F("Date: "));
    if(cyr<10) lcd.print(F("0"));lcd.print(cyr);lcd.print(F("/"));
    if(cmn<10) lcd.print(F("0"));lcd.print(cmn);lcd.print(F("/"));
    if(cdy<10) lcd.print(F("0"));lcd.print(cdy);lcd.print(F("    "));
  }

  if(mode == mode_AlarmDisplay) { 
    lcd.print(F("Alarm: "));
    if(ah<10) lcd.print(F("0"));lcd.print(ah);lcd.print(F(":"));
    if(am<10) lcd.print(F("0"));lcd.print(am);lcd.print(F("   "));
  }
  
  if(mode == mode_AlarmHourSet || mode == mode_AlarmMinSet) { 
    lcd.print(F("Alarm Set: "));
    if(ah<10) lcd.print(F("0"));lcd.print(ah);lcd.print(F(":"));
    if(am<10) lcd.print(F("0"));lcd.print(am);lcd.print(F("  "));
    if(mode == mode_AlarmHourSet) lcd.setCursor(12,1); else  lcd.setCursor(15,1);
    lcd.blink();
  }

  if(mode == mode_TimeHourSet || mode == mode_TimeMinSet) { 
    lcd.print(F("Time Set: "));
    if(ch<10) lcd.print(F("0"));lcd.print(ch);lcd.print(F(":"));
    if(cm<10) lcd.print(F("0"));lcd.print(cm);lcd.print(F("  "));
    if(mode == mode_TimeHourSet) lcd.setCursor(11,1); else lcd.setCursor(14,1);
    lcd.blink();
  }

  if(mode == mode_DateDaySet || mode == mode_DateMnthSet || mode == mode_DateYearSet) { 
    lcd.print(F("Dateset:"));
    if(cyr<10) lcd.print(F("0"));lcd.print(cyr);lcd.print(F("/"));
    if(cmn<10) lcd.print(F("0"));lcd.print(cmn);lcd.print(F("/"));
    if(cdy<10) lcd.print(F("0"));lcd.print(cdy);lcd.print(F(" "));
    if(mode == mode_DateYearSet) lcd.setCursor(9,1); 
    if(mode == mode_DateMnthSet) lcd.setCursor(12,1);
    if(mode == mode_DateDaySet)  lcd.setCursor(15,1);
    lcd.blink();
  }
  
  if(mode == mode_HueSet || mode == mode_SatSet) { 
    lcd.print(F("Hue: "));
    if(effectHue<100) lcd.print(F(" "));
    if(effectHue< 10) lcd.print(F(" "));
    lcd.print(effectHue);lcd.print(F(" "));
    lcd.setCursor(9,1);
    lcd.print(F("Sat:"));
    if(effectSat<100) lcd.print(F(" "));
    if(effectSat< 10) lcd.print(F(" "));
    lcd.print(effectSat);lcd.print(F(" "));
    if(mode == mode_HueSet) lcd.setCursor(7,1); else lcd.setCursor(15,1);
    lcd.blink();
  }
  
  if(mode == mode_PulseTime) { 
    lcd.print(F("Effect Delay:"));
    if(effectDelay<100) lcd.print(F(" "));
    if(effectDelay< 10) lcd.print(F(" "));
    lcd.print(effectDelay);lcd.print(F(" "));
    lcd.setCursor(15,1);
    lcd.blink();
  }

  if(mode == mode_doAlarm) { 
    lcd.print(F("Start Effect ?  "));
    lcd.setCursor(13,1);
    lcd.blink();
  }
  
}

//*****************************************************************************************
// checkAlarm(): check to see is alarm is triggered
void checkAlarm() {
  if(!alarm) return;                    // if alarm is not enabled, return
  if(alFlag) return;                    // if already in alarm sequence, return
  if(ah != ch) return;                  // if hours match and...
  if(am != cm) return;                  // minites match and
  if(cs != 0) return;                   // seconds are 0 then...
  alFlag = true;                        // set flag and reset time counter and
  useIR=false;                          // ... turn off IR remote functions
  IReffect=false;
  IRmode=none;
  tCount=0;
}


//*****************************************************************************************
// checkAlarm(): check to see is alarm is triggered
void checkSwitches() {

// Alarm switch
  if(alsFlag && digitalRead(almSw) == HIGH) {
    alsFlag=false;
    resetTimeout();
    if(mode == mode_Normal) {
      alarm = !alarm;
    } else {
      mode--;
      if (mode < mode_Normal) mode = (num_modes-1);
      inc=0;
    }
  }
  if(digitalRead(almSw) == LOW) alsFlag=true;           
  
// Mode switch  
  if(modFlag && digitalRead(modSw) == HIGH) {
    modFlag=false;
    resetTimeout();
    mode++;
    if (mode >= num_modes) mode = mode_Normal;
    inc=0;
  }
  if(digitalRead(modSw) == LOW) modFlag=true;
  
// Increment switch
  if(incFlag && digitalRead(incSw) == HIGH) {
    incFlag=false; resetTimeout(); inc = 1;
  }
  if(digitalRead(incSw) == LOW) incFlag=true;

// Decrement switch
  if(decFlag && digitalRead(decSw) == HIGH) {
    decFlag=false; resetTimeout(); inc = -1;
  }
  if(digitalRead(decSw) == LOW) decFlag=true;  
}

//*****************************************************************************************
// getTime(): get current time
void getTime() { 
  now = RTC.now();  
  ch=now.hour(); cm=now.minute(); cs=now.second();
  cdy=now.day(); cmn=now.month(); cyr=now.year()-2000; cdw=now.dayOfWeek();
}

//*****************************************************************************************
// checkTimeout: check to see if timeout has occured. If so, reset mode
void checkTimeout() {
  currentMillis = millis();  
  if(currentMillis - previousMillis > interval) {      // if timeout duration expired then
     resetTimeout();                                   // reset timeout and ...
     mode = mode_Normal;                               // reset mode
     vueIR=false;
   }
}

//*****************************************************************************************
// resetTimeout(): ensure timeout is reset
void resetTimeout()    { currentMillis = millis(); previousMillis = currentMillis; }


//*****************************************************************************************
// doLightEffect: ramp up light levels to max and hold for effect time
void doLightEffect() {
  if(alarm) {  
    intensity = (pulse<100)? (float) pulse/100.0: 1.0-( (float) pulse-100.0)/100.0;
    pulse = (++pulse > 200)? 0: pulse;
    doColourLED(effectHue, (float) (effectSat/100.0), intensity);                   
    delay(effectDelay);
    if(tCount < effectTime) return;                    // if not finished then return
    if(pulse != 0) return;                             // wait for end of current cycle
    tCount=0;                                          // otherwise reset counter
    alFlag=false;                                      // reset alarm flag
    doColourLED(0, 0.0, 0.0);                          // shut down LED strip
  } else {
    tCount=0;                                          // otherwise reset counter
    alFlag=false;                                      // reset alarm flag
    doColourLED(0, 0.0, 0.0);                          // shut down LED strip   
  }
}

//*****************************************************************************************
// doColourLED() HSI to RGB converter
// takes Hue [0-360], Saturation [0.0-1.0], Intensity [0.0-1.0] and converts to PWM rgb [0-255].
void doColourLED(float H, float S, float I) {
  H = fmod(H,360);                 // cycle H around to 0-360 degrees
  H = 3.14159*H/(float)180;        // Convert to radians.
  S = S>0?(S<1?S:1):0;             // clamp S and I to interval [0,1]
  I = I>0?(I<1?I:1):0;
    
  // Math! Thanks in part to Kyle Miller.
  if(H < 2.09439) {
    r = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    g = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    b = 255*I/3*(1-S);
  } else if(H < 4.188787) {
    H = H - 2.09439;
    g = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    b = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    r = 255*I/3*(1-S);
  } else {
    H = H - 4.188787;
    b = 255*I/3*(1+S*cos(H)/cos(1.047196667-H));
    r = 255*I/3*(1+S*(1-cos(H)/cos(1.047196667-H)));
    g = 255*I/3*(1-S);
  }
  analogWrite(rled, r);      // PWM red
  analogWrite(gled, g);      // PWM green
  analogWrite(bled, b);      // PWM blue
}


//*****************************************************************************************
// checkIRReceiver: check to see if IR code received and if so, take appropriate action
void checkIRReceiver() {
  if (irrecv.decode(&results)) {
    matchCode(&results);
    irrecv.resume(); // resume receiver  
  }
}

//*****************************************************************************************
// doIRfunction: continue IR special function if set
void doIRFunction() {
  if(!IReffect) return;
  switch(IRmode) {
    case(flash):  if(++irCount < (2*irFlashCount)) { intensity = (irCount <= irFlashCount)? (float) irCount/irFlashCount: 1.0 - (float) (irCount-irFlashCount)/irFlashCount; doColourLED(irH, irS/100.0, intensity); } else { irCount=0; } break;
    case(strobe): if(++irCount > irStCount) { doColourLED(0,0.0,1.0); delay(50); irCount=0; } else { doColourLED(0,0.0,0.0); } break;
    case(fade):   if(++irCount < (2*irFadeCount)) { intensity = (irCount <= irFadeCount)? (float) irCount/irFadeCount: 1.0 - (float) (irCount-irFadeCount)/irFadeCount; doColourLED(irH, irS/100.0, intensity); } else { irCount=0; irH = (++irH)%360;} break;
    case(smooth): doColourLED(irH, 1.0, (float) irI/100.0); if(++irCount > irSmoothCount) { irCount=0; irH = (++irH)%360;  } break;
  }
}

//*****************************************************************************************
// matchCode: match IR code with appropriate action
boolean matchCode(decode_results *results) {
  codeType = results->decode_type;
  codeValue = results->value;
  codeLen = results->bits;

  if(codeType != NEC) return false;
  
  switch (codeValue) {
    case 0xF700FF: mess="Up";    irI=min(irI++, 100); break; // Brightness up
    case 0xF7807F: mess="Down";  irI=max(irI--, 0); break; // Brightness down
    case 0xF740BF: mess="OFF";   useIR=false; break;            // OFF
    case 0xF7C03F: mess="ON";    useIR=true;  break;            // ON
    case 0xF720DF: mess="Red";   irH=0; irS=100; break;         // Red (H=0)
    case 0xF7A05F: mess="Green"; irH=120; irS=100; break;       // Green (H=120)
    case 0xF7609F: mess="Blue";  irH=240; irS=100; break;       // Blue (H=)
    case 0xF7E01F: mess="White"; irH=0; irS=0; break;           // White (H=0,S=0)
    case 0xF710EF: mess="Sienna"; irH=15; irS=100; break;       // #F87431
    case 0xF7906F: mess="Lawn Green"; irH=145; irS=80; break;   // #87F717
    case 0xF750AF: mess="Royal Blue"; irH=260; irS=90; break;   // #2B60DE
    case 0xF7D02F: mess="FLASH"; IRmode=flash; doIREffectSetUp(); break;      // FLASH
    case 0xF730CF: mess="Coral"; irH=30; irS=100; break;        // #C34A2C
    case 0xF7B04F: mess="Turquoise"; irH=170; irS=90; break;    // #4EE2EC 	
    case 0xF7708F: mess="Purple"; irH=290; irS=100; break;      // #461B7E	
    case 0xF7F00F: mess="STROBE"; IRmode=strobe; doIREffectSetUp(); break;    // STROBE
    case 0xF708F7: mess="Salmon"; irH=45; irS=90; break;        // #F9966B
    case 0xF78877: mess="Aquamarine"; irH=190; irS=90; break;   // #348781
    case 0xF748B7: mess="Hot Pink"; irH=315; irS=80; break;     // #7D2252  
    case 0xF7C837: mess="FADE"; IRmode=fade; doIREffectSetUp(); break;        // FADE
    case 0xF728D7: mess="Yellow"; irH=60; irS=100; break;       // #FFFF00	
    case 0xF7A857: mess="Steel Blue"; irH=210; irS=100; break;  // #2B547E	
    case 0xF76897: mess="Violet"; irH=335; irS=90; break;       // #8D38C9	
    case 0xF7E817: mess="SMOOTH"; IRmode=smooth; doIREffectSetUp(); break;    // SMOOTH
  }
  
  if(useIR) {
    if(IRmode == none) {
      saveSettings();
      doColourLED(irH, (float) irS/100.0, (float) irI/100.0);
      resetTimeout();
    }
    vueIR = true;
  } else {
    if(!alFlag) doColourLED(0,0.0,0.0);
    IReffect=false;
    IRmode=none;
  }
}


//*****************************************************************************************
// doIREffect: special IR effect action
void doIREffectSetUp() {
  IReffect=true; 
  resetTimeout();
  irCount=0;
}

//*****************************************************************************************
//                              OPERATION ROUTINES
//*****************************************************************************************
// FREERAM: Returns the number of bytes currently free in RAM  
int freeRam(void) {
  extern int  __bss_end, *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  } 
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
}


// EEPROM save, restore and set/save defaults
void saveSettings()    { for(int addr = 0; addr < sizeof(set)/2; addr++) { EEPROM.write(addr, *set[addr]); } }    // save
void restoreSettings() { for(int addr = 0; addr < sizeof(set)/2; addr++) { *set[addr] = EEPROM.read(addr); } }   // recover EEPROM value
void checkBd() {if (!(cdy==5 && cmn==4)) return; lcd.setCursor(0, 0); lcd.print("!HAPPY BIRTHDAY!"); return; }

[/codesyntax]

Elegant and eminently hackable.

Enjoy!

IR Remote for RGB Strip Alarm Clock

My son and my Christmas vacation project was the RGB LED STRIP ALARM CLOCK, designed to wake him up with a warm pulsing pool of light. In addition to the normal alarm clock functions, he also wanted to use the IR Remote Controller sold with the RGB LED Strip as a separate control. However, subsequent experiments showed that a mechanical switch (a single-pole double-throw for each colour channel) would be required to select between the alarm and the IR controller. Although this approach would work, we found it messy, with lots of additional wiring. Surely, the more elegant solution would be to integrate an IR receiver and the functions of the IR controller directly into the alarm clock.  Et Voila!

Here is the completed version of the design with the additional IR remote functionality…

RGB LED Strip Alarm Clock with IR Functionality

RGB LED Strip Alarm Clock with IR Functionality

Continue reading

RGB LED Strip Light Alarm Clock

My son, home for the holidays, wanted to build an alarm clock that would wake him gently in the morning with lots of light. He lives in a basement flat out West and does not receive much natural lighting early in the morning. He plans to install 5-10 metres of inexpensive RGB LED light strip (see previous discussion) around the periphery of his raised bed. When the alarm activates, a cycle of slowly pulsing light emanates from below the bed. He is holidaying at the right place!

A software and hardware design made for hacking!

Here’s a video of the (nearly) finished clock:

Continue reading

RGB LED Strips

On another topic, I recently purchased a 5 metre strip of RGB LEDs.

RGB LED Strip (5m)

RGB LED Strip

These are quite different from the addressable LED strips used in my clocks. These strips consist of multiple tri-colour LEDs (in this case, 60 per metre) wired so that all like-colour LEDs are connected together – often 3 LEDs of the same colour, connected in series. The flexible substrate has four connections: +12V, Red, Green and Blue, that can be cut every three LEDs.  Pulling these colour connections (RGB) to ground illuminates all of the LEDs of that colour in the strip. Driving each LED colour with a Pulse Width Modulated (PWM) signal allows you to precisely control the intensity of each colour, so that the entire strip can display myriad colour combinations. Using an Arduino, for instance, due to the current required to illuminate the LEDs (300 per colour for a 5 metre strip @10mA/LED = 3A!) you will need to drive each colour signal from a PWM output pin connected to a power NPN Darlington or an N-type power FET. Driving the LEDs hard will also mean you need a hefty 12V poser supply and heatsinks for the power devices.

Continue reading