// Natural Reef Aquarium Lighting V2.4
// 16/06/2013
// Developed by J. Harp (nUm - RTAW Forums, Numlock10 - Reef Central Forums)
// Formulas based off of information from NOAA website for sunrise / sunset times.
// Includes Lunar Simulation.
// Compiled in Arduino 1.5.2
//
// Testing;
// Additonal colour channels
// Unique "fullsun" values for each string
// Fix for 100% power on light start and light stop
//
// Future Development:
// Weather Simulation
//
// Please feel free to use this and modify as you see fit, if you have any comments or suggestions please let me know via messages on the forums listed above.
//
#include <math.h>
#include <Wire.h>
#define DS1307_I2C_ADDRESS 0x68
// RTC variables
byte second, rtcMins, oldMins, rtcHrs, oldHrs, dayOfWeek, dayOfMonth, month, year, psecond;
// LED variables (Change to match your needs)
byte bluePins[] = {3, 9}; // PWM pins for blues
byte whitePins[] = {10, 11}; // PWM pins for whites
byte uvPins[] = {5}; // PWM pins for UVs
byte moonPins[] = {6}; // PWM pins for moonlights
byte blueChannels = 2; // how many PWMs for blues (count from above)
byte whiteChannels = 2; // how many PWMs for whites (count from above)
byte uvChannels = 1; // how many PWMs for uv (count from above)
byte moonChannels = 1; // how many PWMs from moon (count from above)
byte BluePWMHigh[] = {255, 255}; // High value for Blue PWM each vale is for each string - if your values are noraml this is 255, if your values are inverted this is 0
byte BluePWMLow[] = {0, 0}; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255
float BlueFull[] = {25, 25}; // Value in degrees (sun angle) that each Blue string will be at max output
byte WhitePWMHigh[] = {255, 255}; // High value for White PWM - if your values are noraml this is 255, if your values are inverted this is 0
byte WhitePWMLow[] = {0, 0}; // Low value for White PWM - if your values are noraml this is 0, if your values are inverted this is 255
float WhiteFull[] = {37.5, 37.5}; // Value in degrees (sun angle) that each White string will be at max output
byte UVPWMHigh[] = {255}; // High value for UV PWM - if your values are noraml this is 255, if your values are inverted this is 0
byte UVPWMLow[] = {0}; // Low value for UV PWM - if your values are noraml this is 0, if your values are inverted this is 255
float UVFull[] = {30}; // Value in degrees (sun angle) that each UV string will be at max output
byte MoonPWMHigh[] = {255}; // High value for Moon PWM - if your values are noraml this is 255, if your values are inverted this is 0
byte MoonPWMLow[] = {0}; // Low value for Moon PWM - if your values are noraml this is 0, if your values are inverted this is 255
// Set for the location of the world you want to replicate.
float latitude = -19.770621; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia
float longitude = 149.238532; // + to E Defualt - (149.238532)
int TimeZone = 10; // + to E Defulat - (10)
// Julian Century Varaiable
float JC;
// Sunlight Variables
//float fullSun = 37.5; // sun elevation in deg that we will assume full sunlight values (Larger = more sunlight)
int delayTime = 0; // start time delay in minutes, - will push the day back, + will bring the day forward
int SunLight (byte _ledPin, byte _ledHigh, byte _ledLow, float _fullSun, byte _year, byte _month, byte _day, byte _hour, byte _min, byte _sec)
{
float a = floor((14 - _month)/12);
float y = _year + 4800 - a;
float m = _month + (12 * a) - 3;
float AH;
int result;
JC = (((_day + floor(((153.0 * m) + 2.0) / 5.0) + (365.0 * y) + floor(y / 4.0) - floor(y / 100.0) + floor(y / 400.0) - 32045.0) + ((_hour / 24.0) + (_min / 1444.0) + (_sec / 86400.0))) - 2451556.08) / 36525.0;
float GMLS = fmod(280.46646+JC*(36000.76983 + JC * 0.0003032),360);
float GMAS = 357.52911 + JC * (35999.05029 - 0.0001537 * JC);
float EEO = 0.016708634 - JC * (0.000042037 + 0.0000001267 * JC);
float SEoC = sin((GMAS * M_PI)/180)*(1.914602 - JC * (0.004817 + 0.000014 * JC)) + sin(((2 * GMAS) * M_PI) / 180) * (0.019993 - 0.000101 * JC) + sin(((3 * JC) * M_PI) / 180) * 0.000289;
float STL = GMLS + SEoC;
float STA = GMAS + SEoC;
float SRV = (1.000001018 * (1 - EEO * EEO)) / (1 + EEO * cos((STA * M_PI) / 180));
float SAL = STL - 0.00569 - 0.00478 * sin(((125.04 - 1934.136 * JC) * M_PI) / 180);
float MOE = 23 + (26 + ((21.448 - JC * (46.815 + JC * (0.00059 - JC * 0.001813)))) / 60) / 60;
float OC = MOE + 0.00256 * cos(((215.04 - 1934.136 * JC) * M_PI) / 180);
float SD = (asin(sin((OC * M_PI) / 180) * sin((SAL * M_PI) / 180))) * (180 / M_PI);
float vy = tan(((OC / 2) * M_PI) / 180) * tan(((OC / 2) * M_PI) / 180);
float EQoT = (4 * (vy * (sin(2 * ((GMLS * M_PI) / 180)) - 2 * EEO * sin((GMAS * M_PI) / 180) + 4 * EEO * vy * sin((GMAS * M_PI) / 180) * cos( 2 * ((GMLS * M_PI) / 180)) - 0.5 * vy * vy * sin(4 * ((GMLS * M_PI) / 180)) - 1.25 * EEO * EEO * sin(2 * ((GMAS * M_PI) / 180))))) * (180 / M_PI);
float HAS = acos(cos((90.833 * M_PI) / 180) / (cos((latitude * M_PI) / 180) * cos((SD * M_PI) / 180)) - tan((latitude * M_PI) / 180) * tan((SD * M_PI) / 180)) * (180 / M_PI);
float SN = (720 - 4 * longitude - EQoT + TimeZone * 60);
float SR = SN - HAS * 4;
float SS = SN + HAS * 4;
float STD = 8 * HAS;
float TST = fmod((((_hour) + (_min / 60.0) + (_sec / 3600.0)) / 24.0)*1440 + EQoT + 4 * longitude - 60 * TimeZone,1440)+delayTime;
if (TST / 4 < 0)
{
AH = ((TST / 4.0) + 180);
}
else
{
AH = ((TST / 4.0) - 180);
}
float SZA = (acos(sin((latitude * M_PI) / 180) * sin((SD * M_PI) / 180) + cos((latitude * M_PI) / 180) * cos((SD * M_PI) / 180) * cos((AH * M_PI) / 180))) * (180 / M_PI);
int SEA = 90 - SZA;
if (SEA <= 0)
{
result = _ledLow;
}
if (SEA > 0 && SEA < _fullSun)
{
result = map(SEA,0,_fullSun,_ledLow,_ledHigh);
}
if (SEA >= _fullSun)
{
result = _ledHigh;
}
analogWrite(_ledPin, result);
return result;
}
int MoonLight (float JC, byte _ledPin, byte _ledHigh, byte _ledLow)
{
int result;
float MS = fmod((2456318.69458333 - JC),29.530589);
if(MS <= 14.7518)
{
result = map(MS,0,14.7518,_ledLow,_ledHigh);
}
if( MS > 14.7518)
{
result = map(MS,14.7518,29.530589,_ledHigh,_ledLow);
}
analogWrite(_ledPin, result);
return result;
}
/***** RTC Functions *******/
/***************************/
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}
// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
void setup() {
delay(500);
Serial.begin(57600);
Wire.begin();
}
void loop() {
getDateDs1307(&second, &rtcMins, &rtcHrs, &dayOfWeek, &dayOfMonth, &month, &year);
if (psecond != second){
psecond = second;
Serial.print(rtcHrs);
Serial.print(":");
Serial.print(rtcMins);
Serial.print(":");
Serial.print(second);
Serial.print(" ");
Serial.print(dayOfMonth);
Serial.print("/");
Serial.print(month);
Serial.print("/");
Serial.println(year);
update_leds();
}
}
void update_leds ( void ){
int i;
byte value;
int MS;
Serial.println("Blue LED's");
for (i = 0; i < blueChannels; i++)
{
value = SunLight(bluePins[i],BluePWMHigh[i],BluePWMLow[i],BlueFull[i],year,month,dayOfMonth,rtcHrs,rtcMins,second);
Serial.print(map(value,BluePWMLow[i],BluePWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
Serial.println("White LED's");
for (i = 0; i < whiteChannels; i++)
{
value = SunLight(whitePins[i],WhitePWMHigh[i],WhitePWMLow[i],WhiteFull[i],year,month,dayOfMonth,rtcHrs,rtcMins,second);
Serial.print(map(value,WhitePWMLow[i],WhitePWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
Serial.println("UV LED's");
for (i = 0; i < uvChannels; i++)
{
value = SunLight(uvPins[i],UVPWMHigh[i],UVPWMLow[i],UVFull[i],year,month,dayOfMonth,rtcHrs,rtcMins,second);
Serial.print(map(value,UVPWMLow[i],UVPWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i ++)
{
MS = MoonLight(JC, moonPins[i],MoonPWMHigh[i],MoonPWMLow[i]);
Serial.println(MS);
Serial.print(map(MS,MoonPWMLow[i],MoonPWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
}