// Natural Reef Aquarium Lighting V2.5
// 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;
// Moon Correction (was inverted)
// Will not calculate string values if Channel count is 0 to save on processor time
//
// 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[] = {6}; // PWM pins for blues
byte whitePins[] = {10, 11}; // PWM pins for whites
byte uvPins[] = {5, 9}; // PWM pins for UVs
byte moonPins[] = {3}; // PWM pins for moonlights
byte blueChannels = 1; // how many PWMs for blues (count from above)
byte whiteChannels = 2; // how many PWMs for whites (count from above)
byte uvChannels = 2; // how many PWMs for uv (count from above)
byte moonChannels = 1; // how many PWMs from moon (count from above)
byte BluePWMHigh[] = {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}; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255
float BlueFull[] = {25}; // Value in degrees (sun angle) that each Blue string will be at max output (Larger = more sunlight)
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 (Larger = more sunlight)
byte UVPWMHigh[] = {255, 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, 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, 30}; // Value in degrees (sun angle) that each UV string will be at max output (Larger = more sunlight)
byte MoonPWMHigh[] = {50}; // 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 = -17.730211; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia
float longitude = 177.127218; // + to E Defualt - (149.238532)
int TimeZone = 12; // + to E Defulat - (10)
// Julian Century Varaiable
float JC;
// Sunlight Variables
int delayTime = -150; // 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,_ledHigh,_ledLow);
}
if( MS > 14.7518)
{
result = map(MS,14.7518,29.530589,_ledLow,_ledHigh);
}
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;
if(blueChannels > 0){
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();
}
if(whiteChannels > 0){
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();
}
if(uvChannels > 0){
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();
}
if(moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i ++)
{
MS = MoonLight(JC, moonPins[i],MoonPWMHigh[i],MoonPWMLow[i]);
Serial.print(map(MS,MoonPWMLow[i],MoonPWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
}
}
// Natural Reef Aquarium Lighting V2.5.1
// 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;
// Moon Correction (was inverted)
// Will not calculate string values if Channel count is 0 to save on processor time
//
// 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[] = {6}; // PWM pins for blues
byte whitePins[] = {10, 11}; // PWM pins for whites
byte uvPins[] = {5, 9}; // PWM pins for UVs
byte moonPins[] = {3}; // PWM pins for moonlights
byte blueChannels = 1; // how many PWMs for blues (count from above)
byte whiteChannels = 2; // how many PWMs for whites (count from above)
byte uvChannels = 2; // how many PWMs for uv (count from above)
byte moonChannels = 1; // how many PWMs from moon (count from above)
byte BluePWMHigh[] = {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}; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255
float BlueFull[] = {25}; // Value in degrees (sun angle) that each Blue string will be at max output (Larger = more sunlight)
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 (Larger = more sunlight)
byte UVPWMHigh[] = {255, 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, 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, 30}; // Value in degrees (sun angle) that each UV string will be at max output (Larger = more sunlight)
byte MoonPWMHigh[] = {50}; // 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 = -17.730211; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia
float longitude = 177.127218; // + to E Defualt - (149.238532)
int TimeZone = 12; // + to E Defulat - (10)
// Julian Century Varaiable
float JC;
// Sunlight Variables
int delayTime = -150; // 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,_ledHigh,_ledLow);
}
if( MS > 14.7518)
{
result = map(MS,14.7518,29.530589,_ledLow,_ledHigh);
}
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;
if(blueChannels > 0){
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();
}
if(whiteChannels > 0){
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();
}
if(uvChannels > 0){
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();
}
if(moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i ++)
{
value = MoonLight(JC, moonPins[i],MoonPWMHigh[i],MoonPWMLow[i]);
Serial.print(map(value,MoonPWMLow[i],MoonPWMHigh[i],0,100));
Serial.print("% ");
}
Serial.println();
}
}
PHP:// Natural Reef Aquarium Lighting V2.5.1 // 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; // Moon Correction (was inverted) // Will not calculate string values if Channel count is 0 to save on processor time // // 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[] = {6}; // PWM pins for blues byte whitePins[] = {10, 11}; // PWM pins for whites byte uvPins[] = {5, 9}; // PWM pins for UVs byte moonPins[] = {3}; // PWM pins for moonlights byte blueChannels = 1; // how many PWMs for blues (count from above) byte whiteChannels = 2; // how many PWMs for whites (count from above) byte uvChannels = 2; // how many PWMs for uv (count from above) byte moonChannels = 1; // how many PWMs from moon (count from above) byte BluePWMHigh[] = {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}; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255 float BlueFull[] = {25}; // Value in degrees (sun angle) that each Blue string will be at max output (Larger = more sunlight) 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 (Larger = more sunlight) byte UVPWMHigh[] = {255, 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, 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, 30}; // Value in degrees (sun angle) that each UV string will be at max output (Larger = more sunlight) byte MoonPWMHigh[] = {50}; // 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 = -17.730211; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia float longitude = 177.127218; // + to E Defualt - (149.238532) int TimeZone = 12; // + to E Defulat - (10) // Julian Century Varaiable float JC; // Sunlight Variables int delayTime = -150; // 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,_ledHigh,_ledLow); } if( MS > 14.7518) { result = map(MS,14.7518,29.530589,_ledLow,_ledHigh); } 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; if(blueChannels > 0){ 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(); } if(whiteChannels > 0){ 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(); } if(uvChannels > 0){ 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(); } if(moonChannels > 0){ Serial.println("Moon Value"); for (i = 0; i < moonChannels; i ++) { value = MoonLight(JC, moonPins[i],MoonPWMHigh[i],MoonPWMLow[i]); Serial.print(map(value,MoonPWMLow[i],MoonPWMHigh[i],0,100)); Serial.print("% "); } Serial.println(); } }
Try the above and let me know if that works
// Natural Reef Aquarium Lighting V2.5.2
// 11/09/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;
// Moon Correction (was inverted)
// Moon was not changing intensity, rework of moon code to see if this fixes the problem.
//
// 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 (Larger = more sunlight)
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 (Larger = more sunlight)
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 (Larger = more sunlight)
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
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 moon = fmod((2456318.69458333 - JC), 29.530589);
if (moon <= 14.7518)
{
result = map(moon, 0, 14.7518, _ledHigh, _ledLow);
}
if (moon > 14.7518)
{
result = map(moon, 14.7518, 29.530589, _ledLow, _ledHigh);
}
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;
if (blueChannels > 0){
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();
}
if (whiteChannels > 0){
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();
}
if (uvChannels > 0){
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();
}
if (moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i++)
{
value = MoonLight(JC, moonPins[i], MoonPWMHigh[i], MoonPWMLow[i]);
Serial.print(map(value, MoonPWMLow[i], MoonPWMHigh[i], 0, 100));
Serial.print("% ");
}
Serial.println();
}
}
Just an update to see if I can get the moon cycle working correctly. If anyone is still trying this please let me know if it works.
PHP:// Natural Reef Aquarium Lighting V2.5.2 // 11/09/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; // Moon Correction (was inverted) // Moon was not changing intensity, rework of moon code to see if this fixes the problem. // // 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 (Larger = more sunlight) 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 (Larger = more sunlight) 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 (Larger = more sunlight) 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 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 moon = fmod((2456318.69458333 - JC), 29.530589); if (moon <= 14.7518) { result = map(moon, 0, 14.7518, _ledHigh, _ledLow); } if (moon > 14.7518) { result = map(moon, 14.7518, 29.530589, _ledLow, _ledHigh); } 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; if (blueChannels > 0){ 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(); } if (whiteChannels > 0){ 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(); } if (uvChannels > 0){ 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(); } if (moonChannels > 0){ Serial.println("Moon Value"); for (i = 0; i < moonChannels; i++) { value = MoonLight(JC, moonPins[i], MoonPWMHigh[i], MoonPWMLow[i]); Serial.print(map(value, MoonPWMLow[i], MoonPWMHigh[i], 0, 100)); Serial.print("% "); } Serial.println(); } }
Cheers,
// Natural Reef Aquarium Lighting V2.5.2
// 11/09/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;
// Moon Correction (was inverted)
// Moon was not changing intensity, rework of moon code to see if this fixes the problem.
//
// 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[] = { 6 }; // PWM pins for blues
byte whitePins[] = { 10, 11 }; // PWM pins for whites
byte uvPins[] = { 5, 9 }; // PWM pins for UVs
byte moonPins[] = { 3 }; // PWM pins for moonlights
byte blueChannels = 1; // how many PWMs for blues (count from above)
byte whiteChannels = 2; // how many PWMs for whites (count from above)
byte uvChannels = 2; // how many PWMs for uv (count from above)
byte moonChannels = 1; // how many PWMs from moon (count from above)
byte BluePWMHigh[] = { 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 }; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255
float BlueFull[] = { 25 }; // Value in degrees (sun angle) that each Blue string will be at max output (Larger = more sunlight)
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 (Larger = more sunlight)
byte UVPWMHigh[] = { 255, 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, 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, 30 }; // Value in degrees (sun angle) that each UV string will be at max output (Larger = more sunlight)
byte MoonPWMHigh[] = { 15 }; // 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 = -17.730211; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia
float longitude = 177.127218; // + to E Defualt - (149.238532)
int TimeZone = 12; // + to E Defulat - (10)
// Julian Century Varaiable
float JC;
// Sunlight Variables
int delayTime = -150; // 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 moon = fmod((2456318.69458333 - JC), 29.530589);
if (moon <= 14.7518)
{
result = map(moon, 0, 14.7518, _ledHigh, _ledLow);
}
if (moon > 14.7518)
{
result = map(moon, 14.7518, 29.530589, _ledLow, _ledHigh);
}
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;
if (blueChannels > 0){
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();
}
if (whiteChannels > 0){
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();
}
if (uvChannels > 0){
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();
}
if (moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i++)
{
value = MoonLight(JC, moonPins[i], MoonPWMHigh[i], MoonPWMLow[i]);
Serial.print(map(value, MoonPWMLow[i], MoonPWMHigh[i], 0, 100));
Serial.print("% ");
}
Serial.println();
}
}
// Natural Reef Aquarium Lighting V2.5.3
// 14/11/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;
// Moon Correction (was inverted)
// Will not calculate string values if Channel count is 0 to save on processor time
//
// 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 (Larger = more sunlight)
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 (Larger = more sunlight)
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 (Larger = more sunlight)
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
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;
float 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(byte _ledPin, byte _ledHigh, byte _ledLow, byte _year, byte _month, byte _day, byte _hour, byte _min, byte _sec)
{
int result;
float a = floor((14 - _month) / 12);
float y = _year + 4800 - a;
float m = _month + (12 * a) - 3;
float 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 moon = fmod((2456318.69458333 - JC), 29.530589);
if (moon <= 14.7652945)
{
result = map(moon, 0, 14.7652945, _ledHigh, _ledLow);
}
if (moon >= 14.7652946)
{
result = map(moon, 14.7652946, 29.530589, _ledLow, _ledHigh);
}
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;
if (blueChannels > 0){
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();
}
if (whiteChannels > 0){
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();
}
if (uvChannels > 0){
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();
}
if (moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i++)
{
value = MoonLight(moonPins[i], MoonPWMHigh[i], MoonPWMLow[i], year, month, dayOfMonth, rtcHrs, rtcMins, second);
Serial.print(map(value, MoonPWMLow[i], MoonPWMHigh[i], 0, 100));
Serial.print("% ");
}
Serial.println();
}
}
i am doing a small build with just two strings of led's blue violet mixed and white for the tank.
and two strings one for refugium & one ats white.
my ? can i just use the tank light's for the daytime and moonlights instead of running another channel just for the moonlight. i have ldd- drivers dim to 0
thanks
jim
Patrik,
Thanks again for the feedback and really appreciate all of the testing.
The moon light should be updating once every second, it runs in the same loop cycle as the main lights.
cheers,
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 (Larger = more sunlight)
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
// 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 };
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 (Larger = more sunlight)
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
// 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 };
// Natural Reef Aquarium Lighting V2.5.3
// 14/11/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;
// Moon Correction (was inverted)
// Will not calculate string values if Channel count is 0 to save on processor time
//
// 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[] = { 6 }; // PWM pins for blues
byte whitePins[] = { 10, 11 }; // PWM pins for whites
byte uvPins[] = { 5, 9 }; // PWM pins for UVs
byte moonPins[] = { 3 }; // PWM pins for moonlights
byte blueChannels = 1; // how many PWMs for blues (count from above)
byte whiteChannels = 2; // how many PWMs for whites (count from above)
byte uvChannels = 2; // how many PWMs for uv (count from above)
byte moonChannels = 1; // how many PWMs from moon (count from above)
byte BluePWMHigh[] = { 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 }; // Low value for Blue PWM - if your values are noraml this is 0, if your values are inverted this is 255
float BlueFull[] = { 25 }; // Value in degrees (sun angle) that each Blue string will be at max output (Larger = more sunlight)
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 (Larger = more sunlight)
byte UVPWMHigh[] = { 255, 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, 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, 30 }; // Value in degrees (sun angle) that each UV string will be at max output (Larger = more sunlight)
byte MoonPWMHigh[] = {10}; // 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 = -17.730211; // + to N Defualt - (-19.770621) Heart Reef, Great Barrier Reef, QLD, Australia
float longitude = 177.127218; // + to E Defualt - (149.238532)
int TimeZone = 12; // + to E Defulat - (10)
// Julian Century Varaiable
// float JC;
// Sunlight Variables
int delayTime = -150; // 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;
float 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(byte _ledPin, byte _ledHigh, byte _ledLow, byte _year, byte _month, byte _day, byte _hour, byte _min, byte _sec)
{
int result;
float a = floor((14 - _month) / 12);
float y = _year + 4800 - a;
float m = _month + (12 * a) - 3;
float 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 moon = fmod((2456318.69458333 - JC), 29.530589);
if (moon <= 14.7652945)
{
result = map(moon, 0, 14.7652945, _ledHigh, _ledLow);
}
if (moon >= 14.7652946)
{
result = map(moon, 14.7652946, 29.530589, _ledLow, _ledHigh);
}
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;
if (blueChannels > 0){
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();
}
if (whiteChannels > 0){
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();
}
if (uvChannels > 0){
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();
}
if (moonChannels > 0){
Serial.println("Moon Value");
for (i = 0; i < moonChannels; i++)
{
value = MoonLight(moonPins[i], MoonPWMHigh[i], MoonPWMLow[i], year, month, dayOfMonth, rtcHrs, rtcMins, second);
Serial.print(map(value, MoonPWMLow[i], MoonPWMHigh[i], 0, 100));
Serial.print("% ");
}
Serial.println();
}
}