Ok, I think I've resolved my scheduling issues... also cleaned up a lot... still waiting to hear from rufessor on the time calculations being off... but anyway, here's the current code.
Code: Select all
//By Matthew Hockin 2012.
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great.
//If you copy from this (its all open source) please
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <ReefAngel.h>
////// Place global variable code below here
#define NUMBERS_8x16
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>
//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)
//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude, longitude;
long newDay;
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
byte dow=0;//day of week
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************
// Additional variables added for Sunrise/Sunset calculation
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
int UserOffset=5*3600; // 5 hours
byte vtechmode=Random2;
byte vtechDefault=Random2;
int vtechSpeed=50;
int vtechDuration=10;
boolean bFeeding=false;
boolean isNight=false;
double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";
// Group the menu entries together
PROGMEM const char *menu_items[] = {
menu1_label, menu2_label, menu3_label,
menu4_label, menu5_label, menu6_label,
menu7_label, menu8_label
};
//*********************************************************************************************************************************
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define Vortech 5
#define Heater 6
#define Refugium 7
#define UPS 8
//*********************************************************************************************************************************
////// Place global variable code above here
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
byte vtmode=vtechmode;
vtmode++;
if ( vtmode == 7 ) vtmode = 9;
if ( vtmode > 9 ) vtmode = 0;
vtechmode=vtmode;
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
ReefAngel.RF.SetMode(vtmode,ReefAngel.RF.Speed,ReefAngel.RF.Duration);
ReefAngel.Timer[4].SetInterval(10); // Timer for 30min
ReefAngel.Timer[4].Start();
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry4()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
ReefAngel.DisplayVersion();
}
void DrawCustomMain()
{
char text[7];
byte x = 5;
byte y = 2;
byte t;
// Main Header
ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
// Param Header
y=y+20;
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");
// Temp and PH
y=y+10;
ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
ConvertNumToString(text, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
pingSerial();
/// Display Sunrise / Sunset (to be calculated later...)
y=y+20; t=x;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
itoa(SunriseHour,text,10);
if (SunriseHour<10) {
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
} else {
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
}
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
if (SunriseMin<10) {
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
}
itoa(SunriseMin,text,10);
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=20;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
itoa(SunsetHour,text,10);
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=10;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
if (SunsetMin<10) ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
itoa(SunsetMin,text,10);
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);
// MoonPhase
y=y+10;
DayAge = MoonAge(day(), month(), year());
MoonState(DayAge);
char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x+32,y,ThisPhaseLabel[ThisPhase]);
pingSerial();
// MoonLight %
y=y+10;
t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1; t *= 5;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
pingSerial();
// Vortech Mode
y=y+10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,t,y,"Constant");
else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,t,y,"Long Pulse");
else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
pingSerial();
// Relays
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
pingSerial();
// Date+Time
ReefAngel.LCD.DrawDate(6, 122);
pingSerial();
}
void DrawCustomGraph()
{
}
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = 0;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port6Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Set the Overheat temperature setting
InternalMemory.OverheatTemp_write( 820 );
// Ports that are always on
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( Vortech );
ReefAngel.Relay.On( UPS );
////// Place additional initialization code below here
// Reset Vortech to preferred defaults
InternalMemory.RFMode_write(vtechDefault);
InternalMemory.RFSpeed_write(vtechSpeed);
InternalMemory.RFDuration_write(vtechDuration);
ReefAngel.RF.UseMemory=true;
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.DelayedOn( Skimmer,5 );
ReefAngel.StandardHeater( Heater,788,792 );
////// Place your custom code below here
// Set Sunrise and Sunset from MHOCKIN Weather package functions.
if (dow!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
{
CalSun();
dow=day();
// And let's reset the light schedule
ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3,SunsetMin );
ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
}
int currTime = hour()*100+minute();
int riseTime=SunriseHour*100+SunriseMin;
int setTime=SunsetHour*100+SunsetMin;
if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
if (isNight)
{
ReefAngel.PWM.SetDaylight( MoonPhase() );
ReefAngel.PWM.SetActinic( MoonPhase() );
ReefAngel.Relay.On( Refugium );
if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode!=Night) {
ReefAngel.RF.SetMode(Night,15,0);
}
} else {
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
// It's Daytime!
if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode==Night) {
ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
ReefAngel.RF.SetMode(vtechDefault,15,0);
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
{
bFeeding=false;
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Smart_NTM,100,5);
ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
ReefAngel.Timer[4].Start();
vtechmode = Smart_NTM;
}
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
{
ReefAngel.RF.UseMemory=true;
}
if (ReefAngel.RF.UseMemory) {
vtechmode=ReefAngel.RF.Mode;
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
int JulianDate(int d, int m, int y)
{
int mm, yy;
int k1, k2, k3;
int j;
yy = y - (int)((12 - m) / 10);
mm = m + 9;
if (mm >= 12)
{
mm = mm - 12;
}
k1 = (int)(365.25 * (yy + 4712));
k2 = (int)(30.6001 * mm + 0.5);
k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
// 'j' for dates in Julian calendar:
j = k1 + k2 + d + 59;
if (j > 2299160)
{
// For Gregorian calendar:
j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
}
return j;
}
double MoonAge(int d, int m, int y)
{
int j = JulianDate(d, m, y);
//Calculate the approximate phase of the moon
int ip = (j + 4.867) / 29.53059;
ip = ip - abs(ip);
//After several trials I've seen to add the following lines,
//which gave the result was not bad
if(ip < 0.5)
int ag = ip * 29.53059 + 29.53059 / 2;
else
int ag = ip * 29.53059 - 29.53059 / 2;
// Moon's age in days
byte ag = abs(ag) + 1;
return ag;
}
byte MoonState(byte D)
{
switch(D){
case 1:
0, 29;
ThisPhase = 0;
case 2:
1, 2, 3, 4, 5, 6;
ThisPhase = 1;
case 3:
7;
ThisPhase = 2;
case 4:
8, 9, 10, 11, 12, 13;
ThisPhase = 3;
case 5:
14;
ThisPhase = 4;
case 6:
15, 16, 17, 18, 19, 20, 21;
ThisPhase = 5;
case 7:
22;
ThisPhase = 6;
case 8:
23, 24, 25, 26, 27, 28;
ThisPhase = 7;
default:
return 0;
}
}
//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
//Serial.println("CalSun Run Now");
// latitude=dmsToSeconds(40,44,00);//United States of America- Salt Lake City, local time is -7 hours GMT
// longitude=dmsToSeconds(-111,47,00);
latitude=dmsToSeconds(18,34,4);// Great Barrier Reef, Australia
longitude=dmsToSeconds(148,33,19); // Great Barrier Reef, Australia
//**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load********************************************
if (dow==0){//if the controller has resarted we need to find midnight
long hours, minutes;//store current elapsed local hours as total seconds from midnight
time_t t=now();//store current clock time to parse
hours=hour(t);
hours=(hours*3600);//current hour number 0-23 as seconds
minutes=minute(t);
minutes=(minutes*60);//minutes of current hour as seconds
newDay=now();
newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
}
else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
newDay=now();
}
//#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
set=newDay;//
//Calculate rise time and set time using Epherma Library functions (see end of code)
SunRise(&rise);//call to Epherma function
SunSet(&set);//Call to Epherma functionunsigned long newDay;
// Convert rise/set to normal hours/minutes before we make it an offset from midnight.
// Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
SunriseHour=hour(rise+(56*60)+UserOffset);
SunriseMin=minute(rise+(56*60)+UserOffset);
SunsetHour=hour(set-(19*60)+UserOffset);
SunsetMin=minute(set-(19*60)+UserOffset);
/*Serial.print("rise and set= ");
Serial.println(rise);
Serial.println(set);
Serial.print("newDay as seconds since 2000 to todays midnight= ");
Serial.println(newDay);*/
rise=(rise-newDay);// set to elapsed seconds of day
set=(set-newDay);
/*Serial.print("rise and set as elapsed seconds of day= ");
Serial.println(rise);
Serial.println(set);*/
newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day)
/*Serial.print("newDay as seconds since since 1970 to todays midnight= ");
Serial.println(newDay);
Serial.print("elapsed is");
long elapsed=now()-newDay;
Serial.println(elapsed);*/
}//END SunCalc FUNCTION
//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.
//this code is being used freely with attribution to Micahel Rice in accord with his request
// A big thank you for these library functions. Its great!
// decimal degrees
long ddToSeconds(float dd){
return dd * 3600.0;
}
//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;
ret = labs((long)d);
ret = ret * 3600L + 60L * m + s;
ret = (d<0L) ? -ret : ret;
return ret;
}
/* ------------------------------------------------------------------------------------------------
'Equation of Time'
We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;
dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
dt %= _tropical_year;
t = dt;
t /= _tropical_year;
t *= 6.283185307179586;
t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
return t;
}
/* -----------------------------------------------------------------------------------------------
'Solar Declination'
Returns declination in radians
Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;
dt %= _tropical_year;
y = dt;
y /= _tropical_year; // fractional year
y *= 6.283185307179586;
y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
return y;
}
/* ------------------------------------------------------------------------------------------------
Return the period between sunrise and sunset, in seconds.
At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;
d = -SolarDeclination(dt); // will be positive in Northern winter
l = latitude / _sec_rad; // latitude in radians
e += 60.0 * l * tan(l + d); // latitudinal error
d = tan(l) * tan(d); //
if(d>1.0) return 86400UL;
if(d < -1.0) return 0UL;
d = acos(d);
d /= _zenith;
n = 86400UL * d;
n += e;
return n;
}
/* ------------------------------------------------------------------------------------------------
Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
all day.
*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;
daylen = daylightseconds(*dt);
if(daylen == 86400UL) return 1; // there is no 'night' today (midnight sun)
if(daylen == 0UL) return -1; // there is no 'day' today
*dt /= 86400UL;
*dt *= 86400UL;
*dt += 43200UL; // set the time stamp to 12:00:00 GMT
*dt -= daylen / 2; // sunrise at the prime meridian
if(set) *dt += daylen; // sunset at the prime meridian
*dt -= equation_of_time(*dt);
//*dt -= longitude / 15.0; // rotate to our own meridian
return 0;
}
// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
return SunRiseSet(when, 1);
}
Let me know if you think the scheduling and overrides look better now. I'll be testing this later tonight and my wife would appreciate if I wasn't online all night working on it...
Edit: Posted some bug fixes... Everything is working