///////////////////////////////////////////////////////////////////
// Current Satellite LED+ Controller V3.6 //
// Indychus...Dahammer...mistergreen @ plantedtank.net //
// This code is public domain. Pass it on. //
// Confirmed on Arduino UNO 1.0.5 //
// Req. Time, TimeAlarms, RTClib, IRremote //
///////////////////////////////////////////////////////////////////
//
// This version uses Ken Shirriff's IRremote library to Rx/Tx the IR codes
// http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
//
// This code does NOT use PIN 13 on the Uno, as do previous versions
// Instead PIN 3, which is a PWM pin, is used. So you'll need to connect
// your LED to PIN 3 instead of PIN 13 for it to work.
// Install LCD per instructions at http://learn.adafruit.com/character-lcds/overview
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <IRremote.h>
#include <LiquidCrystal.h>
RTC_DS1307 RTC;
IRsend irsend;
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
/*********** BEGIN USER DEFINED VARIABLES ***********/
// DEBUG_IR adds the option to test the IR commands via the Arduino software's serial monitor
// You can send any value from 1 to 32 and it send the corresponding IR code
// The codes follow the remote controller, left to right, top to bottom
// e.g 1 = Orange, 2 = Blue, 21 = Moon1, etc
#define DEBUG_IR
int postDelay = 100; // Delay after codes are sent
int randAnalogPin = 0; // This needs to be set to an unused Analog pin, Used by ThunderStorm()
// Current Satellite+ IR Codes (NEC Protocol)
unsigned long codeHeader = 0x20DF; // Always the same
// Remote buttons listed left to right, top to bottom
unsigned int codeOrange = 0x3AC5;
unsigned int codeBlue = 0xBA45;
unsigned int codeRose = 0x827D;
unsigned int codePowerOnOff = 0x02FD;
unsigned int codeWhite = 0x1AE5;
unsigned int codeFullSpec = 0x9A65;
unsigned int codePurple = 0xA25D;
unsigned int codePlay = 0x22DD;
unsigned int codeRedUp = 0x2AD5;
unsigned int codeGreenUp = 0xAA55;
unsigned int codeBlueUp = 0x926D;
unsigned int codeWhiteUp = 0x12ED;
unsigned int codeRedDown = 0x0AF5;
unsigned int codeGreenDown = 0x8A75;
unsigned int codeBlueDown = 0xB24D;
unsigned int codeWhiteDown = 0x32CD;
unsigned int codeM1Custom = 0x38C7;
unsigned int codeM2Custom = 0xB847;
unsigned int codeM3Custom = 0x7887;
unsigned int codeM4Custom = 0xF807;
unsigned int codeMoon1 = 0x18E7;
unsigned int codeMoon2 = 0x9867;
unsigned int codeMoon3 = 0x58A7;
unsigned int codeDawnDusk = 0xD827;
unsigned int codeCloud1 = 0x28D7;
unsigned int codeCloud2 = 0xA857;
unsigned int codeCloud3 = 0x6897;
unsigned int codeCloud4 = 0xE817;
unsigned int codeStorm1 = 0x08F7;
unsigned int codeStorm2 = 0x8877;
unsigned int codeStorm3 = 0x48B7;
unsigned int codeStorm4 = 0xC837;
void SetAlarms()
{
// Set up your desired alarms here
// The default value of dtNBR_ALARMS is 6 in Alarms.h.
// This code sets 12 alarms by default, so you'll need to change dtNBR_ALARMS to 12 or more
Alarm.alarmRepeat(7,00,0, DawnDusk);
Alarm.alarmRepeat(9,00,0, Cloud2); // (HR,MIN,SEC,FUNCTION)
Alarm.alarmRepeat(11,00,0, FullSpec);
Alarm.alarmRepeat(15,00,0, Cloud2);
Alarm.alarmRepeat(19,00,0, DawnDusk);
Alarm.alarmRepeat(21,00,0, Moon2);
// Comment these out if you don't want the chance of a random storm each day
Alarm.alarmRepeat(12,00,00, ThunderStorm);
ThunderStorm(); // Sets up intial storm so we don't have wait until alarm time
}
/************* END USER DEFINED VARIABLES *************/
void setup()
{
Wire.begin();
RTC.begin();
lcd.begin(20, 4);
Serial.begin(9600);
//Serial.println(freeRam());
if (! RTC.isrunning()) {
Serial.println("RTC Error");
RTC.adjust(DateTime(__DATE__, __TIME__));} //Adjust to compile time
setSyncProvider(syncProvider); //reference our syncProvider function instead of RTC_DS1307::get()
Alarm.timerRepeat(900, digitalClockDisplay); // Display the time every 15 minutes
digitalClockDisplay();
SetAlarms();
Serial.print("SRAM : "); //un-comment these line to check available SRAM
Serial.println(freeRam());}
void loop()
{
#ifdef DEBUG_IR
if (Serial.available() > 0) {
delay(5); //Wait for transmission to finish
CurrentCMDs(SerialReadInt());
}
#endif
Alarm.delay(100);
// Service alarms & wait (msec)
lcdClockDisplay();
}
time_t syncProvider()
{
//this does the same thing as RTC_DS1307::get()
return RTC.now().unixtime();
}
void ThunderStorm ()
{
// Schedules a storm between 1 & 9 in the evening
// It sets Storm2, followed by Cloud2 or DawnDusk or Moon2, depending on when the storm is over
randomSeed(analogRead(randAnalogPin)); // Generate random seed on unused pin
byte RH = random(23); // Randomizer for thunderstorm
byte RM = random(59);
byte RS = random(59);
byte TSDurationH = random(2);
byte TSDurationM = random(59);
if (RH <= 12)
{
Serial.println("No storm today");
lcd.setCursor(0,1);
lcd.print("No storm today");
return;
}
if (RH > 12) // If random value is after 1 pm, allow storm
{
Alarm.alarmOnce(RH,RM,RS,Storm2);
Serial.print("Next Storm: ");
Serial.print(RH);
printDigits(RM);
printDigits(RS);
Serial.print(" ");
Serial.print("Duration = ");
Serial.print(TSDurationH);
printDigits(TSDurationM);
Serial.println();
lcd.setCursor(0,1);
lcd.print("Next Storm: ");
lcdHRdigits(RH);
lcdDigits(RM);}
if ((RH + TSDurationH) < 19) // Return to Cloud2 if storm ends between 1-7pm
{Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Cloud2);}
else if ((RH + TSDurationH) < 21) // Return to DawnDusk if storm ends between 7-9pm
{Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,DawnDusk);}
else // Return to Night2 if storm ends after 9pm
{Alarm.alarmOnce((RH + TSDurationH),(RM + TSDurationM),RS,Moon2);}
}
void digitalClockDisplay() // Digital clock
{
Serial.print("Time = ");
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.println(); }
void lcdClockDisplay()
{lcd.setCursor(0,0);
lcdHRdigits(hour());
lcdDigits(minute());}
void printDigits(int digits) // Add :
{
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
void lcdDigits(int digits) // Add :
{
lcd.print(":");
if(digits < 10)
lcd.print('0');
lcd.print(digits);
}
void lcdHRdigits(int HRdigits) // Preface hour with 0
{
if(HRdigits < 10)
lcd.print('0');
lcd.print(HRdigits);
}
#ifdef DEBUG_IR
int SerialReadInt()
{
int i, numAva;
char inBytes[3]; // Array to hold the bytes
char * inBytesPtr = &inBytes[0]; // Pointer to the first element of the array
numAva = Serial.available(); // Read number of input bytes
if (numAva > 2)
numAva = 2; // Only allow 2 characters to prevent overflow
for (i=0; i<numAva; i++) // Load input bytes into array
inBytes[i] = Serial.read();
inBytes[i] = '\0'; // Put NULL character at the end
return atoi(inBytesPtr); // Call atoi function and return result
}
void CurrentCMDs (int cmd)
{switch (cmd)
{
case 1:
Orange();
break;
case 2:
Blue();
break;
case 3:
Rose();
break;
case 4:
PowerOnOff();
break;
case 5:
White();
break;
case 6:
FullSpec();
break;
case 7:
Purple();
break;
case 8:
Play();
break;
case 9:
RedUp();
break;
case 10:
GreenUp();
break;
case 11:
BlueUp();
break;
case 12:
WhiteUp();
break;
case 13:
RedDown();
break;
case 14:
GreenDown();
break;
case 15:
BlueDown();
break;
case 16:
WhiteDown();
break;
case 17:
M1Custom();
break;
case 18:
M2Custom();
break;
case 19:
M3Custom();
break;
case 20:
M4Custom();
break;
case 21:
Moon1();
break;
case 22:
Moon2();
break;
case 23:
Moon3();
break;
case 24:
DawnDusk();
break;
case 25:
Cloud1();
break;
case 26:
Cloud2();
break;
case 27:
Cloud3();
break;
case 28:
Cloud4();
break;
case 29:
Storm1();
break;
case 30:
Storm2();
break;
case 31:
Storm3();
break;
case 32:
Storm4();
break;
default:
Serial.println("Invalid Choice");}}
#endif
void SendCode (unsigned int code, byte numTimes, const char *sMessage)
{unsigned long irCode = (codeHeader << 16) + code; // Header is 2 bytes, shift all the way to left & add code to it
for( int i = 0; i < numTimes; i++)
{irsend.sendNEC(irCode,32); // Send code
Alarm.delay(postDelay);}
Serial.println(sMessage); // Print message
lcd.setCursor(6,0);
lcd.print(sMessage);
for(byte i = sizeof(sMessage); i <= 14; i++)
lcd.print(" ");
digitalClockDisplay();}
void Orange()
{SendCode(codeOrange, 2, "Orange");}
void Blue()
{SendCode(codeBlue, 2, "Blue");}
void Rose()
{SendCode(codeRose, 2, "Rose");}
void PowerOnOff()
{SendCode(codePowerOnOff, 1, "Power On/Off");}
void White()
{SendCode(codeWhite, 2, "White");}
void FullSpec()
{SendCode(codeFullSpec, 2, "Full Spectrum");}
void Purple()
{SendCode(codePurple, 2, "Purple");}
void Play()
{SendCode(codePlay, 1, "Play/Pause:");}
void RedUp()
{SendCode(codeRedUp, 1, "Red Up");}
void GreenUp()
{SendCode(codeGreenUp, 1, "Green Up");}
void BlueUp()
{SendCode(codeBlueUp, 1, "Blue");}
void WhiteUp()
{SendCode(codeWhiteUp, 1, "White Up");}
void RedDown()
{SendCode(codeRedDown, 1, "Red Down");}
void GreenDown()
{SendCode(codeGreenDown, 1, "Green Down");}
void BlueDown()
{SendCode(codeBlueDown, 1, "Blue Down");}
void WhiteDown()
{SendCode(codeWhite, 1, "White Down");}
void M1Custom()
{SendCode(codeM1Custom, 2, "Custom Mix 1");}
void M2Custom()
{SendCode(codeM2Custom, 2, "Custom Mix 2");}
void M3Custom()
{SendCode(codeM3Custom, 2, "Custom Mix 3");}
void M4Custom()
{SendCode(codeM4Custom, 2, "Custom Mix 4");}
void Moon1()
{SendCode(codeMoon1, 2, "Moonlight 1");}
void Moon2()
{SendCode(codeMoon2, 2, "Moonlight 2");}
void Moon3()
{SendCode(codeMoon3, 2, "Moonlight 3");}
void DawnDusk()
{SendCode(codeDawnDusk, 2, "Dawn/Dusk");}
void Cloud1()
{SendCode(codeCloud1, 2, "Cloud Cover 1");}
void Cloud2()
{SendCode(codeCloud2, 2, "Cloud Cover 2");}
void Cloud3()
{SendCode(codeCloud3, 2, "Cloud Cover 3");}
void Cloud4()
{SendCode(codeCloud4, 2, "Cloud Cover 4");}
void Storm1()
{SendCode(codeStorm1, 2, "Thunderstorm 1");}
void Storm2()
{SendCode(codeStorm2, 2, "Thunderstorm 2");}
void Storm3()
{SendCode(codeStorm3, 2, "Thunderstorm 3");}
void Storm4()
{SendCode(codeStorm4, 2, "Thunderstorm 4");}
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}