Created
February 7, 2018 15:57
-
-
Save anonymous/1aa31949462b45e7c985ca19c75708db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/////////////////////// INCLUDES /////////////////////////////////// | |
#include <IRremote.h> | |
#include <EEPROM.h> | |
// Load RTC Date/Time | |
#include <Wire.h> // must be included here so that Arduino library object file references work | |
#include <RtcDS3231.h> | |
RtcDateTime now; // time provided by RTC | |
RtcDS3231<TwoWire> Rtc(Wire); | |
// set up irReciever | |
const int irReceiverPin = 12; | |
IRrecv irrecv(irReceiverPin); | |
decode_results results; | |
/////////////////////// VARIABLES /////////////////////////////////// | |
// IMPORTANT REMINDER !!! | |
// irReciever runs on Timer 1, blocking PWM on pins 3 and 11 | |
// | |
// This will produce stange colors if you use pins 3 and 11 | |
// Pin 6 is Timer 0 which is shared with the main timer loop, | |
// which can be flickery when using PWM at times. As we use | |
// less blues then reds and greens, this is set up to use | |
// Timer 2 for Red and Green and Timer 0 for Blue. Think twice | |
// before swaping around these pins. | |
// LED pins | |
int rLED = 9; | |
int gLED = 10; | |
int bLED = 6; | |
// 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 = 1800; | |
// Brightness (0 to 100%) | |
int bright = 0; | |
// Saturation for Hue Function (0 to 255 fully) | |
int saturation = 255; | |
// Hue for Hue Function (0 to 360) | |
int hue = 0; | |
// use int rather then byte, because sometimes the code accidnetially goes negative | |
int red = 0; | |
int green = 0; | |
int blue = 0; | |
//// bedTime and riseAndShine modes | |
bool bedTime = false; | |
bool riseAndShine = false; | |
//////////////// 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 -- default is 99 which is off. | |
int specialMode = 99; | |
// 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; | |
unsigned long eepromLastWrite = 0; | |
/////////////////////// 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!) | |
// float used for brightness so to avoid truncation of percent | |
void kRGB (unsigned long kelvin = temp, float brightness = bright) { | |
int t = kelvin / 100; | |
if(t <= 66 ){ | |
red = 255; | |
green = t; | |
green = 99.4708025861 * log(green) - 161.1195681661; | |
if( t <= 19){ | |
blue = 0; | |
} | |
else { | |
blue = t-10; | |
blue = 138.5177312231 * log(blue) - 305.0447927307; | |
} | |
} | |
else if (t < 320) { | |
red = t - 60; | |
red = 329.698727446 * pow(red, -0.1332047592); | |
green = t - 60; | |
green = 288.1221695283 * pow(green, -0.0755148492 ); | |
blue = 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 | |
blue = 255; | |
red = t - 255; | |
green = t - 255; | |
} | |
// safety values for brightness | |
if (brightness > 100) { | |
bright = 100; | |
brightness = 100; | |
} | |
if (brightness < 0) { | |
bright = 0; | |
brightness = 0; | |
} | |
// dim based on brightness value | |
red = (int) round(red * (brightness/100)); | |
blue = (int) round(blue * (brightness/100)); | |
green = (int) round(green * (brightness/100)); | |
// safety values for colors | |
if (red < 0) red = 0; | |
if (green < 0) green = 0; | |
if (blue < 0) blue = 0; | |
if (red > 255) red = 255; | |
if (blue > 255) blue = 255; | |
if (green > 255) green = 255; | |
// set LED outputs | |
analogWrite(rLED, red); | |
analogWrite(gLED, green); | |
analogWrite(bLED, blue); | |
// debugging output | |
Serial.print("kelvin="); | |
Serial.print(kelvin); | |
Serial.print(", bright="); | |
Serial.print(bright); | |
Serial.print(", r="); | |
Serial.print(red); | |
Serial.print(", g="); | |
Serial.print(green); | |
Serial.print(", b="); | |
Serial.print(blue); | |
Serial.print("\n"); | |
} | |
// set color by hue, brightness, saturation | |
void hueColor() { | |
if (saturation == 0) { | |
// achromatic (grey) | |
red = green = blue = bright; | |
} | |
else | |
{ | |
unsigned int scaledHue = (hue * 6); | |
unsigned int sector = scaledHue >> 8; // sector 0 to 5 around the color wheel | |
unsigned int offsetInSector = scaledHue - (sector << 8); // position within the sector | |
unsigned int p = (bright * ( 255 - saturation )) >> 8; | |
unsigned int q = (bright * ( 255 - ((saturation * offsetInSector) >> 8) )) >> 8; | |
unsigned int t = (bright * ( 255 - ((saturation * ( 255 - offsetInSector )) >> 8) )) >> 8; | |
switch( sector ) { | |
case 0: | |
red = bright; | |
green = t; | |
blue = p; | |
break; | |
case 1: | |
red = q; | |
green = bright; | |
blue = p; | |
break; | |
case 2: | |
red = p; | |
green = bright; | |
blue = t; | |
break; | |
case 3: | |
red = p; | |
green = q; | |
blue = bright; | |
break; | |
case 4: | |
red = t; | |
green = p; | |
blue = bright; | |
break; | |
default: // case 5: | |
red = bright; | |
green = p; | |
blue = q; | |
break; | |
} | |
} | |
// set LED outputs | |
analogWrite(rLED, red); | |
analogWrite(gLED, green); | |
analogWrite(bLED, blue); | |
// debugging output | |
Serial.print("hue="); | |
Serial.print(hue); | |
Serial.print(", saturation="); | |
Serial.print(saturation); | |
Serial.print(", bright="); | |
Serial.print(bright); | |
Serial.print(", r="); | |
Serial.print(red); | |
Serial.print(", g="); | |
Serial.print(green); | |
Serial.print(", b="); | |
Serial.print(blue); | |
Serial.print("\n"); | |
} | |
// manual color settings | |
// change red, green, blue before running this function | |
void setColor () { | |
// set LED outputs | |
analogWrite(rLED, red); | |
analogWrite(gLED, green); | |
analogWrite(bLED, blue); | |
// debugging output | |
Serial.print("manual color setting"); | |
Serial.print(", r="); | |
Serial.print(red); | |
Serial.print(", g="); | |
Serial.print(green); | |
Serial.print(", b="); | |
Serial.print(blue); | |
Serial.print("\n"); | |
} | |
// 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); | |
// load initial RTC data | |
Rtc.Begin(); | |
now = Rtc.GetDateTime(); | |
// upon boot (due to power failure), load data from EEPROM | |
int eeAddress = 0; | |
EEPROM.get(eeAddress,bright); | |
eeAddress += sizeof(int); | |
EEPROM.get(eeAddress,temp); | |
kRGB(); | |
} | |
// Loop cycles through the colors | |
void loop() { | |
// every 30 seconds write the latest brightness/temperature to eeprom | |
// don't do this every loop because otherwise we would use up memory lifespan | |
// by doing this too frequently, especially during dim or brighten | |
if (millis() - eepromLastWrite >= 30000) { | |
storeTempEEPROM(); | |
eepromLastWrite = millis(); | |
// also is it time to head to bed or rise and shine? | |
now = Rtc.GetDateTime(); | |
// Is it 6:54 AM and is the Day of Week Monday = 1 through Friday = 5 ? And Not Already Sunset or Sunsrise | |
if (now.Hour() == 6 && now.Minute() == 54 && now.DayOfWeek() > 0 && now.DayOfWeek() < 6 && specialMode == 0 && bright == 0) { | |
remoteCode = 1976685926; // reverse button | |
} | |
// Is it 10:30 PM and is the Day of Week Sunday = 0 through Thursday = 4 ? And Not Already Sunset or Sunsrise | |
if (now.Hour() == 22 && now.Minute() == 30 && now.DayOfWeek() < 5 && specialMode == 0 && bright != 0) { | |
remoteCode = 3980777284; // forward button | |
} | |
// At the top of the hour, blink | |
if ( now.Minute() == 0 && specialMode == 0) { | |
specialMode = 8; | |
} | |
else if (now.Minute() == 0 && specialMode == 99) { | |
specialMode = 99; // blink once, do nothing really .. | |
} | |
else { | |
specialMode = 0; | |
} | |
} | |
// check to see if any mode requests have come in | |
// DELAY is to prevent button bounce | |
// from somebody holding the button too long | |
if (millis() - buttonMS >= 500 && irrecv.decode(&results)) { | |
Serial.println(results.value); | |
irrecv.resume(); // Receive the next value | |
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; | |
//kRGB(2700,100); | |
} | |
// turn light string on or off | |
if (remoteCode == 4105841032) { | |
/* | |
if (bright == 0 || red == 0) { | |
while (bright < 100) { | |
bright++; | |
kRGB(); | |
} | |
specialMode = 0; | |
Serial.print("on button activated"); | |
} | |
else {*/ | |
while (bright > 0) { | |
bright--; | |
kRGB(); | |
} | |
red = 0; green = 0; blue = 0; | |
setColor(); | |
Serial.print("off button activated"); | |
delay(1500); | |
//} | |
} | |
// lower or raise the brightness | |
if (remoteCode == 2209452902 ) { | |
bright -= 3; | |
// | |
kRGB(); | |
Serial.print("dim button activated"); | |
} | |
if (remoteCode == 1752382022) { | |
bright += 3; | |
kRGB(); | |
Serial.print("bright button activated"); | |
} | |
// warm or chill the color temperature | |
// above 3500 kelvin we make bigger steps | |
if (remoteCode == 1595074756) { | |
if (temp < 3000) { | |
temp += 100; | |
} | |
else if (temp < 3500) { | |
temp += 200; | |
} | |
else { | |
temp += 700; | |
} | |
kRGB(); | |
Serial.print("cool button activated"); | |
} | |
if (remoteCode == 412973352) { | |
if (temp > 3500) { | |
temp -= 700; | |
} | |
else if (temp > 3000) { | |
temp -= 200; | |
} | |
else if (temp > 100) { | |
temp -= 100; | |
} | |
kRGB(); | |
Serial.print("warm button activated"); | |
} | |
// color temperature pre-sets | |
if (remoteCode == 3778927144) { | |
temp = 500; | |
kRGB(); | |
Serial.print("one button activated"); | |
} | |
if (remoteCode == 2908251746) { | |
temp = 750; | |
kRGB(); | |
Serial.print("two button activated"); | |
} | |
if (remoteCode == 657459652) { | |
temp = 1000; | |
kRGB(); | |
Serial.print("three button activated"); | |
} | |
if (remoteCode == 4120482440) { | |
temp = 1500; | |
kRGB(); | |
Serial.print("four button activated"); | |
} | |
if (remoteCode == 1931099650) { | |
temp = 2000; | |
kRGB(); | |
Serial.print("five button activated"); | |
} | |
if (remoteCode == 742730860) { | |
temp = 2500; | |
kRGB(); | |
} | |
if (remoteCode == 1167253836) { | |
temp = 3000; | |
kRGB(); | |
Serial.print("seven button activated"); | |
} | |
if (remoteCode == 1747313982) { | |
temp = 6000; | |
kRGB(); | |
Serial.print("eight button activated"); | |
} | |
if (remoteCode == 2340753640) { | |
temp = 10000; | |
kRGB(); | |
Serial.print("nine button activated"); | |
} | |
if (remoteCode == 3119867746) { | |
// temp = 5000; | |
// bright = 100; | |
while (bright < 100) { | |
bright++; | |
kRGB(); | |
} | |
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 | |
temp = 1200; | |
bright = 50; | |
kRGB(); | |
Serial.print("back button activated"); | |
} | |
if (remoteCode == 3980777284) { | |
// special mode 2 - Sunrise | |
specialMode = 2; | |
specialModeMS = millis(); | |
specialModeMSLoop = millis(); | |
specialModeTK = 1; | |
temp = 500; | |
bright = 20; | |
kRGB(); | |
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 | |
temp = 1200; | |
bright = 50; | |
kRGB(); | |
Serial.print("play button activated"); | |
} | |
if (remoteCode == 3361986248) { | |
// special mode 4 - Sunset then nine hours later, sunrise | |
specialMode = 4; | |
specialModeMS = millis(); | |
specialModeTK = 1; | |
// start as 40% dim, warm yellow light for the sunset mode | |
temp = 1200; | |
bright = 50; | |
kRGB(); | |
Serial.print("stop button activated"); | |
} | |
if (remoteCode == 3398220998) { | |
red = 255; | |
blue = 255; | |
green = 0; | |
setColor(); | |
/* | |
* holiday season over, disabled | |
// special mode 5 - Chistmas Colors | |
specialMode = 5; | |
specialModeMS = millis(); | |
// start as black | |
red = 0; | |
green = 0; | |
blue = 0; | |
setColor(); | |
Serial.print("go back button activated"); | |
*/ | |
} | |
if (remoteCode == 3459683302) { | |
// special mode 6 - Red, Blue, White Colors blink | |
specialMode = 6; | |
specialModeMS = millis(); | |
// start as black | |
red = 0; | |
green = 0; | |
blue = 0; | |
setColor(); | |
Serial.print("dash button activated"); | |
} | |
if (remoteCode == 2331063592) { | |
// special mode 7 - rainbowGo button hit | |
specialMode = 7; | |
specialModeMS = millis(); | |
hue = 0; | |
saturation = 255; | |
// leave brightness alone, let that be adjusted manually | |
Serial.print("go 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 > 5L * 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; | |
// reduce from 50 brightness to 0 brightness | |
// then shut off the unit | |
bright -= 1; | |
kRGB(); | |
} | |
else { | |
// we are done, make sure fully off | |
bright = 0; | |
kRGB(); | |
} | |
specialModeMSLoop = millis(); // reset LOOP timer | |
} | |
} | |
if (specialMode == 2 || (specialMode == 3 && millis() - specialModeMS > 8L * 60 * 60 * 1000) || | |
(specialMode == 4 && millis() - specialModeMS > 9L * 60 * 60 * 1000)) { | |
// Task 1 for Special Mode 2 | |
// Task 2 for Special Mode 3 (eight hours later) | |
// Task 2 for Special Mode 4 (nine 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 20%, incremented 100 kelvin for 95 times | |
// to bring us up to 9,900 kelvin at 100% brightness | |
// | |
// Once we reach 10,000 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() - specialModeMSLoop > 6L * 1000) { | |
if (temp < 10000) { | |
// keep increasing color until we reach 10,000 kelvin, very blue | |
temp += 100; | |
// increase up to 100% | |
if (bright < 100) { | |
bright++; | |
} | |
else { | |
bright = 100; | |
} | |
kRGB(); | |
} | |
else { | |
/* | |
if (blue < 255) { | |
blue++; | |
} | |
setColor();*/ | |
} | |
specialModeMSLoop = millis(); | |
} | |
} | |
if (specialMode == 5) { | |
// delightful christmas color fade red to white to green | |
// seasons greetings! | |
// we just use the sluggish microprocess to limit speed here | |
if (red >= 0 && red < 255 && blue == 0 && green == 0) red++; | |
if (red == 255 && blue < 255) { blue++; green++; } | |
if (red && blue == red) { blue--; red--; } | |
if (red == 0 && green != 0) { green--; } | |
setColor(); | |
} | |
if (specialMode == 6) { | |
/* | |
// blinky christmas mode, more festive and obnoxious! | |
if (millis() - specialModeMS > 200) { | |
if (red == 0 && blue == 0 && green == 0) { red = 127; blue = 0; green = 0; } | |
else if (red == 127 && blue == 0 && green == 0) { red = 255; blue = 0; green = 0; } | |
else if (red == 255 && blue == 0 && green == 0) { red = 255; blue = 127; green = 127; } | |
else if (red == 255 && blue == 0 && green == 0) { red = 255; blue = 127; green = 127; } | |
else if (red == 255 && blue == 127 && green == 127) { red = 255; blue = 255; green = 255; } | |
else if (red == 255 && blue == 255 && green == 255) { red = 127; blue = 127; green = 255; } | |
else if (red == 127 && blue == 127 && green == 255) { red = 0; blue = 0; green = 255; } | |
else if (red == 0 && blue == 0 && green == 255) { red = 0; blue = 0; green = 127; } | |
else { red = 127; blue = 0; green = 0; } | |
setColor(); | |
specialModeMS = millis(); | |
} | |
*/ | |
// blinky patrotic mode, more festive and obnoxious! | |
if (millis() - specialModeMS > 200) { | |
if (red == 0 && blue == 0 && green == 0) { red = 127; blue = 0; green = 0; } | |
else if (red == 127 && blue == 0 && green == 0) { red = 255; blue = 0; green = 0; } | |
else if (red == 255 && blue == 0 && green == 0) { red = 255; blue = 127; green = 127; } | |
else if (red == 255 && blue == 0 && green == 0) { red = 255; blue = 127; green = 127; } | |
else if (red == 255 && blue == 127 && green == 127) { red = 255; blue = 255; green = 255; } | |
else if (red == 255 && blue == 255 && green == 255) { red = 127; blue = 255; green = 127; } | |
else if (red == 127 && blue == 255 && green == 127) { red = 0; blue = 255; green = 0; } | |
else if (red == 0 && blue == 255 && green == 0) { red = 0; blue = 127; green = 0; } | |
else { red = 127; blue = 0; green = 0; } | |
setColor(); | |
specialModeMS = millis(); | |
} | |
} | |
if (specialMode == 7) { | |
// hue changing mode | |
//if (millis() - specialModeMS > 200) { | |
if (hue < 360) { | |
hue++; | |
} | |
else { | |
hue = 0; | |
} | |
saturation = 255; | |
bright = 100; | |
hueColor(); | |
// specialModeMS = millis(); | |
//} | |
} | |
if (specialMode == 8) { | |
// At the top of the hour briefly shut off, go bright | |
// then restore to current brightness | |
uint8_t i = bright; | |
bright = 10; | |
kRGB(); | |
delay(500); | |
bright = 100; | |
kRGB(); | |
delay(500); | |
bright = i; | |
kRGB(); | |
specialMode = 99; // blink once, prevent this from loading over and over again | |
} | |
} | |
// store color temperature and brightness in eprom in case of power reset | |
// by using the update feature, it only writes when the data changes | |
void storeTempEEPROM() { | |
int eeAddress = 0; | |
EEPROM.put(eeAddress, bright); | |
eeAddress += sizeof(int); | |
EEPROM.put(eeAddress, temp); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment