Got my controller,
Got my controller,
Woohoo! Got my controller hooked up to the Mac (no issues communicating!!) got the wifi module configured thru serial (Wifi Utility didn't do anything...) Connected to controller and tested through web. Port forwarding configured and working! iOS app working... Just have to configure portal now...but first, have to load some code!!!
The git commenting Doxygen step will now take a short intermission...maybe back to it later tonight or this weekend. Let the fun begin!!!
Thanks Roberto, package came nicely packed and everything looks great!!
The git commenting Doxygen step will now take a short intermission...maybe back to it later tonight or this weekend. Let the fun begin!!!
Thanks Roberto, package came nicely packed and everything looks great!!
Re: Got my controller,
So here's what I got after my first night of coding... let me know what you think.
Next up is to customize the menu and main screens to show the MoonPhase and Vortech Mode. Anyone have any input on changing the vortech mode through a custom menu? I saw the thread detailing this, but wasn't sure how that code ever ended up working...
Also need to see about working with the Sunrise/Sunset stuff I wanted to work on. But at least the controller is currently usable and ready to be connected up. Will probably code a few more features before making the swap.
Feedback is more than welcome....
Next up is to customize the menu and main screens to show the MoonPhase and Vortech Mode. Anyone have any input on changing the vortech mode through a custom menu? I saw the thread detailing this, but wasn't sure how that code ever ended up working...
Also need to see about working with the Sunrise/Sunset stuff I wanted to work on. But at least the controller is currently usable and ready to be connected up. Will probably code a few more features before making the swap.
Feedback is more than welcome....
Code: Select all
#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
byte vtechmode;
boolean bFeeding=false;
boolean isNight=false;
//*********************************************************************************************************************************
//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 setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// 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( 869 );
// Ports that are always on
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( UPS );
////// Place additional initialization code below here
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.DelayedOn( Vortech,5 );
ReefAngel.Relay.DelayedOn( Skimmer,5 );
ReefAngel.StandardLights( WhiteLEDs,12,0,20,0 );
ReefAngel.StandardLights( BlueLEDs,11,0,23,0 );
ReefAngel.StandardHeater( Heater,788,792 );
////// Place your custom code below here
if (( hour() <= 11 ) || ( hour() >= 23 ))
{
isNight=true;
} else {
isNight=false;
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
{
bFeeding=false;
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Smart_NTM,155,5);
ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
ReefAngel.Timer[4].Start();
vtechmode = 5;
}
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
{
if (isNight)
{
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Night,15,0);
vtechmode = 9;
} else {
ReefAngel.RF.UseMemory=true;
vtechmode = InternalMemory.RFMode_read();
}
}
if (isNight)
{
ReefAngel.PWM.SetDaylight( MoonPhase() );
ReefAngel.PWM.SetActinic( MoonPhase() );
ReefAngel.Relay.On( Refugium );
} else {
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
Re: Got my controller,
Nice job!!!
The easiest way of changing modes using the custom menu is by cycling modes everytime you choose that menu, then go to memory mode as the last choice.
The easiest way of changing modes using the custom menu is by cycling modes everytime you choose that menu, then go to memory mode as the last choice.
Roberto.
Re: Got my controller,
If I did that, how do I prevent my loop() from reverting it back to memory mode? Also how do I set the default memory mode?
Re: Got my controller,
To prevent it from going back to memory mode, just use this:
It will simply ignore whatever is in the memory setting.
To change the memory setting, there are several ways:
Java status app
Android app
Portal
Built-in webserver
Code: Select all
ReefAngel.RF.UseMemory=false;
To change the memory setting, there are several ways:
Java status app
Android app
Portal
Built-in webserver
Roberto.
Re: Got my controller,
Nevermind, re-reading my code, I only go back to memory mode if the timer is trigged. But can I set the default memory mode in my setup()? and change later through the apps?
Re: Got my controller,
Yes.
Code: Select all
InternalMemory.RFMode_write(3);
InternalMemory.RFSpeed_write(50);
InternalMemory.RFDuration_write(10);
Roberto.
Re: Got my controller,
Ok, here is my current code...
I still haven't sync'd up my Vortech yet, but now that I have the laptop able to upload code, I can work on that. Also should have pH calibration fluid in the LFS by Wednesday... I still have to setup the Vortech control menu so I can play with it from the controller. (Do the mode buttons still work on the unit itself after it's sync'd?) The moonlights are hooked up and look sweet! They are really low, even on 100% which is perfect.. I just want a touch of light when everything is off so it's not so dark... Right now we're at 20% and it gives a nice glimmer. Too bad I can't get a good photo.
Anyway, does anyone have a good code to add the Vortech controls? I want to preserve most of the Simple_Menu and have to start playing around with that.
I've also added the Sunrise / Sunset, but my string writing seems a bit messed up. I think the calculations are good, but without seeing the results, it's hard to tell. It seemed about an hour off from the Utah given as the example, so I really need some time to troubleshoot. I've got the base code stripped of all weather and dimming functions, so it's pretty basic. If someone can take a look and give me another set of eyes would be great!
Anyway, here it is...sorry to be soo long winded..
I still haven't sync'd up my Vortech yet, but now that I have the laptop able to upload code, I can work on that. Also should have pH calibration fluid in the LFS by Wednesday... I still have to setup the Vortech control menu so I can play with it from the controller. (Do the mode buttons still work on the unit itself after it's sync'd?) The moonlights are hooked up and look sweet! They are really low, even on 100% which is perfect.. I just want a touch of light when everything is off so it's not so dark... Right now we're at 20% and it gives a nice glimmer. Too bad I can't get a good photo.
Anyway, does anyone have a good code to add the Vortech controls? I want to preserve most of the Simple_Menu and have to start playing around with that.
I've also added the Sunrise / Sunset, but my string writing seems a bit messed up. I think the calculations are good, but without seeing the results, it's hard to tell. It seemed about an hour off from the Utah given as the example, so I really need some time to troubleshoot. I've got the base code stripped of all weather and dimming functions, so it's pretty basic. If someone can take a look and give me another set of eyes would be great!
Anyway, here it is...sorry to be soo long winded..
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
//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 elapsedTime;//used multiple places as elapsed since midnight
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
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dow=0;//day of week
//END HEADER FOR MHOCKIN Weather package //
#define NUMBERS_8x16
byte vtechmode;
boolean bFeeding=false;
boolean isNight=false;
double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;
//*********************************************************************************************************************************
//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 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;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+16);
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();
y=y+20;
/*
/// Display Sunrise / Sunset (to be calculated later...)
char riseTime[5];
char setTime[5];
char temp[]=" ";
itoa(SunriseHour,temp,10);
strcat(riseTime, temp);
strcat(riseTime, ":");
itoa(SunriseMin,temp,10);
strcat(riseTime, temp);
itoa(SunsetHour,temp,10);
strcat(setTime, temp);
strcat(setTime, ":");
itoa(SunsetMin,temp,10);
strcat(setTime, temp);
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Rise:");
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+32,y,riseTime);
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+70,y,"Set:");
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+98,y,setTime);
*/
// MoonLight %
y=y+10;
t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1; t *= 5;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLight:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+63, y, 1);
ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+63+t, y, "%");
pingSerial();
// 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.DrawText(COLOR_CORNFLOWERBLUE,255,x+32,y,ThisPhaseLabel[ThisPhase]);
pingSerial();
// Vortech Mode
y=y+10;
ReefAngel.LCD.DrawText(0,255,x,y,"RF:");
ReefAngel.LCD.Clear(DefaultBGColor,x+20,y,x+(128-x),y+8);
if (vtechmode == 0) ReefAngel.LCD.DrawText(COLOR_LIMEGREEN,255,x+20,y,"Constant");
else if(vtechmode == 1) ReefAngel.LCD.DrawText(COLOR_GOLD,255,x+20,y,"Lagoon");
else if (vtechmode == 2) ReefAngel.LCD.DrawText(COLOR_GOLD,255,x+20,y,"Reef Crest");
else if (vtechmode == 3) ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,x+20,y,"Short Pulse");
else if (vtechmode == 4) ReefAngel.LCD.DrawText(COLOR_PINK,255,x+20,y,"Long Pulse");
else if (vtechmode == 5) ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+20,y,"Nutrient");
else if (vtechmode == 6) ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+2,y,"Tidal Swell");
else if (vtechmode == 9) ReefAngel.LCD.DrawText(COLOR_WHITE,0,x+20,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()
{
// ReefAngel.LCD. DrawGraph(5, 15);
}
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// 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
////// Place additional initialization code above here
}
void loop()
{
// Set Sunrise and Sunset from MHOCKIN Weather package functions.
elapsedTime=(now()-newDay); //Elapsed time is seconds from midnight of today- local processor time.
if (dow!=day()){ //used to see that were in a new day and need to recalculate sunrise and sunset
CalSun();
dow=day();
}
ReefAngel.Relay.DelayedOn( Skimmer,5 );
ReefAngel.StandardLights( WhiteLEDs,12,0,20,0 );
ReefAngel.StandardLights( BlueLEDs,11,0,23,0 );
ReefAngel.StandardHeater( Heater,788,792 );
////// Place your custom code below here
if (( hour() <= 11 ) || ( hour() >= 23 )) isNight=true; else isNight=false;
if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
{
bFeeding=false;
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Smart_NTM,155,5);
ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
ReefAngel.Timer[4].Start();
vtechmode = 5;
}
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
{
if (isNight)
{
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Night,15,0);
vtechmode = 9;
} else {
ReefAngel.RF.UseMemory=true;
vtechmode = InternalMemory.RFMode_read();
}
}
if (isNight)
{
ReefAngel.PWM.SetDaylight( MoonPhase() );
ReefAngel.PWM.SetActinic( MoonPhase() );
ReefAngel.Relay.On( Refugium );
} else {
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
}
////// 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);
//**********************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;
/*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);*/
SunriseHour=hour(rise);
SunriseMin=minute(rise);
SunsetHour=hour(set);
SunsetMin=minute(set);
}//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);
}
Re: Got my controller,
Ok roberto, I've made some progress. I have the custom menu cycling through my vortech modes. The only issue I have at the moment is the logic I'm doing for my scheduling...
I wanted to be able to override night mode to go into feed mode, so I've got that working, but I'd also like to be able to change the pumps if I'm in night mode as well. I've put a wrapper around the menu code so that it wouldn't change the screen if I'm set to not use memory.
Can you just look at my scheduling code and let me know if there's a better way? I should probably just simplify it since I probably shouldn't change my vortech or feed after lights out... but I want FULL control
Thanks,
Lee
Code is already posted in the thread below, so don't want to duplicate.
http://forum.reefangel.com/viewtopic.php?p=12941#p12941
I wanted to be able to override night mode to go into feed mode, so I've got that working, but I'd also like to be able to change the pumps if I'm in night mode as well. I've put a wrapper around the menu code so that it wouldn't change the screen if I'm set to not use memory.
Can you just look at my scheduling code and let me know if there's a better way? I should probably just simplify it since I probably shouldn't change my vortech or feed after lights out... but I want FULL control
Thanks,
Lee
Code is already posted in the thread below, so don't want to duplicate.
http://forum.reefangel.com/viewtopic.php?p=12941#p12941
Re: Got my controller,
Looks awesome!!
I think the only thing you may have problems is to come out of night mode.
Let me know if this is the case.
I think the only thing you may have problems is to come out of night mode.
Let me know if this is the case.
Roberto.
Got my controller,
I probably need to redo my scheduling anyway since i think the only thing synced to the GBR right now is my refugium light, vortech and moonlights
Got my controller,
Never mind, I think I got it...
What I need to do is change my vortech menu function to use SetMode instead of changing the memory value when it's not in memory mode. Then I can start the timer just like feed mode, so that I can set it to what I want and go to bed and it will revert after x time...I think that should be all I need...
And I guess then I can call StandardLights in the section of loop that decides if it's a new day so it only runs once each day when the sunrise/sunset variables change...Does that sound right?
I think at this point I've got all the feature planned except for setting up my float switches....need to figure out a bracket for my ato reservoir and DT... Thinking about aqua-hub moldable plastic strips (except for the $10 shipping for a $10 product ), but for the reservoir a magnetic might be better since I've got it in a closed container...so we'll see.
I'm having a lot of fun with this. Soo happy I got it over the apex...I know I would have felt constrained in the long run. Thanks for building a great product and for some great code!
What I need to do is change my vortech menu function to use SetMode instead of changing the memory value when it's not in memory mode. Then I can start the timer just like feed mode, so that I can set it to what I want and go to bed and it will revert after x time...I think that should be all I need...
And I guess then I can call StandardLights in the section of loop that decides if it's a new day so it only runs once each day when the sunrise/sunset variables change...Does that sound right?
I think at this point I've got all the feature planned except for setting up my float switches....need to figure out a bracket for my ato reservoir and DT... Thinking about aqua-hub moldable plastic strips (except for the $10 shipping for a $10 product ), but for the reservoir a magnetic might be better since I've got it in a closed container...so we'll see.
I'm having a lot of fun with this. Soo happy I got it over the apex...I know I would have felt constrained in the long run. Thanks for building a great product and for some great code!
Re: Got my controller,
You're right! Today was the first night I had the vortech setup and thankfully I just checked. I put it into feed mode which should trigger the function to put it back into UseMemory mode. I know I also have an issue if the controller resets during night mode as it stays in whatever active mode in memory. So I really need to restructure that part of the scheduling...rimai wrote:Looks awesome!!
I think the only thing you may have problems is to come out of night mode.
Let me know if this is the case.
Thanks, definitely in the right direction, just spinning right now trying to figure out the scheduling part without having access to my IDE at workrimai wrote:Cool!!
Yeah, I think you are in the right track.
Re: Got my controller,
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.
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...
Lee
Edit: Posted some bug fixes... Everything is working
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);
}
Lee
Edit: Posted some bug fixes... Everything is working
Re: Got my controller,
Some bugfixes, slight modifications, some cleanup, and so forth...
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=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
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=-(9*3600); // -9 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(1200); // Timer for 20min
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;
CalSun();
}
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+=18;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
itoa(SunsetHour,text,10);
if (SunsetHour<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 (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.DrawText(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,783,798 );
////// 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,80,5);
ReefAngel.Timer[4].SetInterval(900); // Timer for 15min
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(){
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
//Serial.println("CalSun Run Now");
latitude=ddToSeconds(latitude);
longitude=ddToSeconds(longitude);
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;
/*
Serial.print("rise and set= ");
Serial.println(rise);
Serial.println(set);
Serial.print("newDay as seconds since 2000 to todays midnight= ");
Serial.println(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+userOffset);
SunriseMin=minute(rise+userOffset);
SunsetHour=hour(set+userOffset);
SunsetMin=minute(set+userOffset);
}//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);
}
Re: Got my controller,
Latest version... bugs fixed in Sunrise/Sunset. Still have some issues with Vortech controlling in feeding mode, etc. It doesn't change the DISPLAY_MODE when I go into feeding mode from my iphone app... and so the vortech never changes mode...but my skimmer goes off... not sure what's going on yet... Also need to try and use ONLY memory through the various modes because that's the only way I can easily change it from other places...
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) and 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 CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
//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)
//variables for SWFLTEK functions
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dom=0;//day of month
//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
int userRiseOffset=-(9*3600)+472;
int userSetOffset=-(9*3600)+506;
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************
//#define Constant 0
//#define Random1 1 // Lagoonal
//#define Random2 2 // Reef Crest
//#define ShortWave 3
//#define LongWave 4
//#define Smart_NTM 5 // Nutrient Transport Mode
//#define Smart_TSM 6 // Tidal Swell Mode
//#define Feeding_Start 7
//#define Feeding_Stop 8
//#define Night 9
//#define Slave_Start 97
//#define Slave_Stop 98
//#define None 99
//Vortech Mode Definitions
byte vtechmode=Smart_TSM;
byte vtechDefault=Smart_TSM;
byte vtechDuration=10;
byte vtechSpeed=40;
byte vtechNTMSpeed=65;
byte vtechNightSpeed=10;
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define UPS 5
#define Heater 6
#define Refugium 7
#define Vortech 8
boolean bFeeding=false;
boolean isNight=false;
////// Place global variable code above here
// Custom Menu Code
#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
};
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,40,10);
ReefAngel.Timer[1].SetInterval(1200); // Timer for 20min
ReefAngel.Timer[1].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();
}
// Custom Main Screen
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+=14;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
itoa(SunsetHour,text,10);
if (SunsetHour<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 (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;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
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()
{
}
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port2Bit;
// 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
// Convert lat/long to proper format
latitude=ddToSeconds(latitude);
longitude=ddToSeconds(longitude);
// 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, 15 );
ReefAngel.StandardHeater( Heater,783,788 );
////// Place your custom code below here
// Set Sunrise and Sunset from MHOCKIN Weather package functions.
if (dom!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
{
CalSun();
dom=day();
}
// Send the date/time to the portal...
ReefAngel.CustomVar[0]=SunriseHour;
ReefAngel.CustomVar[1]=SunriseMin;
ReefAngel.CustomVar[2]=SunsetHour;
ReefAngel.CustomVar[3]=SunsetMin;
ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
// Let's make the time one number so we can figure out it's night or day...
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,vtechNightSpeed,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,vtechNightSpeed,0);
}
}
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
{
bFeeding=false;
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Smart_NTM,vtechNTMSpeed,5);
ReefAngel.Timer[1].SetInterval(45*60); // Timer for 45 minutes
ReefAngel.Timer[1].Start();
vtechmode = Smart_NTM;
}
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[1].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();
}
// Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
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
long hours, minutes, seconds;//store current elapsed local hours as total seconds from midnight
long newDay;
//Serial.println("CalSun Run Now");
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
seconds=second(t);
newDay=now();
newDay-=(hours+minutes+seconds); //Convert current local unix epoch time to local unix epoch time of midnight
newDay-=SECS_YR_2000; // Convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today (+946684800)
//set value to send to SunRise as midnight GMT in seconds from Y2K
rise=newDay;
set=newDay;
// Calculate rise time and set time using Epherma Library functions (see end of code)
SunRise(&rise);
SunSet(&set);
// Bring us back to real time.
rise+=SECS_YR_2000;
set+=SECS_YR_2000;
newDay+=SECS_YR_2000;
// Set the invididual variables and add userOffsets.
SunriseHour=hour(rise+userRiseOffset);
SunriseMin=minute(rise+userRiseOffset);
SunsetHour=hour(set+userSetOffset);
SunsetMin=minute(set+userSetOffset);
}
//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
// The code below this is 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);
}
Re: Got my controller,
OK. Finally after an almost all nighter of attempting to fix some bugs. I've converted all the operations in the RF mode to use InternalMemory. This means that I can change from portal, iPhone (if it didn't crash when I try to save the memory..) or from the Menu. I have full control from the menu with custom durations and speeds for each mode. I'm also displaying the speed/duration on the main screen as well. All the Sunrise/Sunset code is working good. No more issues there. Only thing long term left is to get more temp probes and hook up the float switches
The only outstanding issues are that when in night mode, it does not kick out to feeding mode. Also, sometimes when going into feeding mode, the LCD does not clear completely even though i added a flag to return and clearscreen in my CustomMain. Would love some thoughts on these two issues. In the meantime... here's the current code.
The only outstanding issues are that when in night mode, it does not kick out to feeding mode. Also, sometimes when going into feeding mode, the LCD does not clear completely even though i added a flag to return and clearscreen in my CustomMain. Would love some thoughts on these two issues. In the meantime... 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) and 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 CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
// Custom Menu Code
#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
};
//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)
//variables for SWFLTEK functions
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dom=0;//day of month
//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
int userRiseOffset=-(9*3600)+472;
int userSetOffset=-(9*3600)+506;
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************
// Vortech Defaults
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;
byte vtPrevMode;
byte vtPrevSpeed;
byte vtPrevDuration;
boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define UPS 5
#define Heater 6
#define Refugium 7
#define Vortech 8
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port2Bit;
// 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
// Convert lat/long to proper format
latitude=ddToSeconds(latitude);
longitude=ddToSeconds(longitude);
// Reset Vortech to preferred defaults
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.DelayedOn( Skimmer, 20 );
ReefAngel.StandardHeater( Heater,778,782 );
////// Place your custom code below here
// Set Sunrise and Sunset from MHOCKIN Weather package functions.
if (dom!=day()) // Used to see that were in a new day and need to recalculate sunrise and sunset
{
CalSun();
dom=day();
// Send the date/time to the portal
ReefAngel.CustomVar[0]=SunriseHour;
ReefAngel.CustomVar[1]=SunriseMin;
ReefAngel.CustomVar[2]=SunsetHour;
ReefAngel.CustomVar[3]=SunsetMin;
}
ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
// Let's make the time one number so we can figure out it's night or day...
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 (vtOverride==false && isFeeding==false) {
setRFmode(Night,vtNightSpeed,vtNightDuration);
}
} else {
// It's Daytime!
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
if (vtOverride==false && vtMode==Night) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
{
isFeeding=false;
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
if (vtOverride && ReefAngel.Timer[1].IsTriggered())
{
vtOverride=false;
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
} else {
setRFmode(); // Update if set externally
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {
// Check if mode has changed
if (mode!=vtMode) {
vtPrevMode=vtMode;
vtMode=mode;
if (mode!=InternalMemory.RFMode_read()) {
InternalMemory.RFMode_write(mode);
}
// Temp fix for coming out of night mode
if (vtPrevMode==Night) {
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0);
ReefAngel.RF.UseMemory=true;
}
if (isNight || vtMode==Smart_NTM) {
setRFtimer(30);
}
}
// Check if speed has changed
if (speed!=vtSpeed) {
vtPrevSpeed=vtSpeed;
vtSpeed=speed;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
// Check if duration has changed
if (duration!=vtDuration) {
vtPrevDuration=vtDuration;
vtDuration=duration;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
}
void setRFmode() {
setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
ReefAngel.Timer[1].SetInterval(minutes*60);
ReefAngel.Timer[1].Start();
vtOverride=true;
}
// Menu Code
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
byte mode, speed,duration;
mode=vtMode;
mode++;
if (mode > 9 ) {
mode=0;
speed=50; duration=0; // Constant
} else if (mode == 1) {
speed=40; duration=0; // Lagoon
} else if (mode == 2) {
speed=45; duration=0; // Reef Crest
} else if (mode == 3) {
speed=50; duration=10; // Short Pulse
} else if (mode == 4) {
speed=50; duration=20; // Long Pulse
} else if (mode == 6) {
speed=45; duration=10; // Smart_TSM
} else if (mode == 5) {
speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
} else if (mode == 7) {
speed=vtNightSpeed; duration=vtNightDuration; // Night
mode=9;
}
setRFmode(mode,speed,duration);
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();
}
// Custom Main Screen
void DrawCustomMain()
{
char text[7];
byte x = 5;
byte y = 2;
byte t;
if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }
// 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+=20;
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");
// Temp and PH
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+=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+=14;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
itoa(SunsetHour,text,10);
if (SunsetHour<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 (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+=10;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
pingSerial();
// MoonLight %
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+=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 (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
y+=10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
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()
{
}
// Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
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
long hours, minutes, seconds;//store current elapsed local hours as total seconds from midnight
long newDay;
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
seconds=second(t);
newDay=now();
newDay-=(hours+minutes+seconds); //Convert current local unix epoch time to local unix epoch time of midnight
newDay-=SECS_YR_2000; // Convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today (+946684800)
//set value to send to SunRise as midnight GMT in seconds from Y2K
rise=newDay;
set=newDay;
// Calculate rise time and set time using Epherma Library functions (see end of code)
SunRise(&rise);
SunSet(&set);
// Bring us back to real time.
rise+=SECS_YR_2000;
set+=SECS_YR_2000;
newDay+=SECS_YR_2000;
// Set the invididual variables and add userOffsets.
SunriseHour=hour(rise+userRiseOffset);
SunriseMin=minute(rise+userRiseOffset);
SunsetHour=hour(set+userSetOffset);
SunsetMin=minute(set+userSetOffset);
}
//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
// The code below this is 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);
}
Re: Got my controller,
Ok. I created a ticket.
https://github.com/reefangel/Libraries/issues/46
https://github.com/reefangel/Libraries/issues/46
Roberto.
Re: Got my controller,
Ok, have some bug fixes and some cleanup. My Vortech's were going in and out of night mode all night because of how I was setting the timer. I hope this resolves it. I also implemented the SunLocation class that binder put together from the code I scraped together. Deails on that class are located on this post (with fixes) http://forum.reefangel.com/viewtopic.php?p=14508#p14508
I have one issue still.. I'm trying to add a menu option to toggle the refugium light.. not working as I'd hope. If there is a simple way to do it, I'd be grateful
Thanks and Enjoy
I have one issue still.. I'm trying to add a menu option to toggle the refugium light.. not working as I'd hope. If there is a simple way to do it, I'd be grateful
Thanks and Enjoy
Code: Select all
#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>
#include <SunLocation.h>
////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
// Custom Menu Code
#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 = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO 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
};
// Vortech Defaults
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;
byte vtPrevMode;
byte vtPrevSpeed;
byte vtPrevDuration;
boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;
SunLocation sl;
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define UPS 5
#define Heater 6
#define Refugium 7
#define Vortech 8
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
// 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
// Setup SunLocation class
sl.Init(-18.285833, 147.699722);
sl.SetOffset(-9,472,-9,506);
// Save previous used vortech mode.
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.DelayedOn( Skimmer, 20 );
ReefAngel.StandardHeater( Heater,778,782 );
////// Place your custom code below here
sl.CheckAndUpdate();
SunriseHour=sl.GetRiseHour(); SunriseMin=sl.GetRiseMinute();
SunsetHour=sl.GetSetHour(); SunsetMin=sl.GetSetMinute();
// Send the SunLocation info to the portal
ReefAngel.CustomVar[0]=SunriseHour; ReefAngel.CustomVar[1]=SunriseMin;
ReefAngel.CustomVar[2]=SunsetHour; ReefAngel.CustomVar[3]=SunsetMin;
// Turn the lights on
ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
// Let's make the time one number so we can figure out it's night or day...
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 (vtOverride==false && isFeeding==false) {
setRFmode(Night,vtNightSpeed,vtNightDuration);
}
} else {
// It's Daytime!
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
if (vtOverride==false && vtMode==Night) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
{
isFeeding=false;
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
if (vtOverride && ReefAngel.Timer[1].IsTriggered())
{
vtOverride=false;
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
} else {
setRFmode(); // Update if set externally
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {
// Check if mode has changed
if (mode!=vtMode) {
vtPrevMode=vtMode;
vtMode=mode;
if (mode!=InternalMemory.RFMode_read()) {
InternalMemory.RFMode_write(mode);
}
// Temp fix for coming out of night mode
if (vtPrevMode==Night) {
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0);
ReefAngel.RF.UseMemory=true;
}
if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
setRFtimer(30);
}
}
// Check if speed has changed
if (speed!=vtSpeed) {
vtPrevSpeed=vtSpeed;
vtSpeed=speed;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
// Check if duration has changed
if (duration!=vtDuration) {
vtPrevDuration=vtDuration;
vtDuration=duration;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
}
void setRFmode() {
setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
ReefAngel.Timer[1].SetInterval(minutes*60);
ReefAngel.Timer[1].Start();
vtOverride=true;
}
// Menu Code
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
byte mode, speed,duration;
mode=vtMode;
mode++;
if (mode > 9 ) {
mode=0;
speed=50; duration=0; // Constant
} else if (mode == 1) {
speed=40; duration=0; // Lagoon
} else if (mode == 2) {
speed=45; duration=0; // Reef Crest
} else if (mode == 3) {
speed=50; duration=10; // Short Pulse
} else if (mode == 4) {
speed=50; duration=20; // Long Pulse
} else if (mode == 6) {
speed=45; duration=10; // Smart_TSM
} else if (mode == 5) {
speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
} else if (mode == 7) {
speed=vtNightSpeed; duration=vtNightDuration; // Night
mode=9;
}
setRFmode(mode,speed,duration);
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry4()
{
ReefAngel.Relay.Toggle ( Refugium );
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
ReefAngel.DisplayVersion();
}
// Custom Main Screen
void DrawCustomMain()
{
char buf[16];
byte x = 5;
byte y = 2;
byte t;
if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }
// 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+=18;
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");
// Temp and PH
y+=8;
ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
ConvertNumToString(buf, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, buf, Num8x16);
pingSerial();
/// Display Sunrise / Sunset (to be calculated later...)
y+=20; t=x;
sprintf(buf, "%02d:%02d", SunriseHour, SunriseMin);
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
sprintf(buf, "%02d:%02d", SunsetHour, SunsetMin); t+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
// MoonPhase
y+=10;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
pingSerial();
// MoonLight %
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+=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 (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
y+=10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
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()
{
}
Re: Got my controller,
Can you help me replicate this issue?lnevo wrote:Also, sometimes when going into feeding mode, the LCD does not clear completely even though i added a flag to return and clearscreen in my CustomMain.
I was unable to do so...
I'd like to fix in the libraries, but I can't get it to do that.
Roberto.
Re: Got my controller,
I'll take a few more peaks the next few feedings and see if I'm still experiencing it. If you have any troubleshooting you'd like me to try... You could also run my code (but now you'll need the SunLocation class ) as a test...if I'm just crazy...
Re: Got my controller,
Modified my code to use heater temp settings from memory so I can modify without changing code. I also added a function to the SunLocation class so I can have my lighting times controlled by memory. I'll be posting that separately. I also added my float switches and set the Low to turn off my pump / skimmer when the water level in the return chamber gets too low (drain problem, ato problem?). The high I am using to send me an alert if something happens and the sump gets too full (return pump issue, return line obstruction...) Next up waiting for my water level sensor and I'll be able to monitor my ATO reservoir.
Edit: Needed to add a flag instead of just checking the mode, otherwise it would never turn return pumps back on after water change / feeding mode... The flag is used if triggered, mode change, or controller restart and is reset once the HighATO.IsActive function comes up false. This way it's only disabled once and not when we trigger it on purpose.
Here's the code.
Edit: Needed to add a flag instead of just checking the mode, otherwise it would never turn return pumps back on after water change / feeding mode... The flag is used if triggered, mode change, or controller restart and is reset once the HighATO.IsActive function comes up false. This way it's only disabled once and not when we trigger it on purpose.
Here's the code.
Code: Select all
#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>
#include <SunLocation.h>
////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
// Custom Menu Code
#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 = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO 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
};
// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
byte vtNTMSpeed=55;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;
boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;
boolean activatedHighATO=true;
SunLocation sl;
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define UPS 5
#define Heater 6
#define Refugium 7
#define Vortech 8
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
// 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
// Setup SunLocation class
sl.Init(-18.285833, 147.699722); // GBR
sl.SetOffset(-9,472,-9,506); // 9 hour difference
// Save previous used vortech mode.
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.DelayedOn( Skimmer, 5 );
ReefAngel.StandardHeater( Heater );
////// Place your custom code below here
sl.CheckAndUpdate();
// Turn on the lights
ReefAngel.StandardLights( WhiteLEDs );
ReefAngel.ActinicLights( BlueLEDs );
// Let's make the time one number so we can figure out it's night or day...
int currTime=NumMins(hour(),minute());
int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute());
int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
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 (vtOverride==false && isFeeding==false) {
setRFmode(Night,vtNightSpeed,vtNightDuration);
}
// Trigger night mode again to extend built-in timer
if ( ((hour() == 6) && (minute() == 00) && (second() == 00) ) ) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
} else {
// It's Daytime!
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
if (vtOverride==false && vtMode==Night) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
ReefAngel.Timer[1].ForceTrigger();
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) activatedHighATO=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
{
isFeeding=false;
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
else if (vtOverride && ReefAngel.Timer[1].IsTriggered())
{
vtOverride=false;
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
else
{
setRFmode(); // Update if set externally
}
if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
if (ReefAngel.LowATO.IsActive()) {
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
if (ReefAngel.HighATO.IsActive()) {
if (!activatedHighATO) {
activatedHighATO=true;
ReefAngel.CustomVar[0]=2;
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
} else {
ReefAngel.CustomVar[0]=1;
activatedHighATO=false;
}
// Turn off Skimmer if Return pump is shutoff.
if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {
// Check if mode has changed
if (mode!=vtMode) {
vtPrevMode=vtMode;
vtMode=mode;
if (mode!=InternalMemory.RFMode_read()) {
InternalMemory.RFMode_write(mode);
}
// Temp fix for coming out of night mode
if (vtPrevMode==Night) {
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0);
ReefAngel.RF.UseMemory=true;
}
if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
setRFtimer(30);
}
}
// Check if speed has changed
if (speed!=vtSpeed) {
vtPrevSpeed=vtSpeed;
vtSpeed=speed;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
// Check if duration has changed
if (duration!=vtDuration) {
vtPrevDuration=vtDuration;
vtDuration=duration;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
}
void setRFmode() {
setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
ReefAngel.Timer[1].SetInterval(minutes*60);
ReefAngel.Timer[1].Start();
vtOverride=true;
}
// Menu Code
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
byte mode,speed,duration,prev_mode,prev_speed,prev_dur;
mode=vtMode;
mode++;
if (mode > 9 ) {
mode=0;
speed=50; duration=0; // Constant
} else if (mode == 1) {
speed=40; duration=0; // Lagoon
} else if (mode == 2) {
speed=45; duration=0; // Reef Crest
} else if (mode == 3) {
speed=50; duration=10; // Short Pulse
} else if (mode == 4) {
speed=50; duration=20; // Long Pulse
} else if (mode == 6) {
speed=45; duration=10; // Smart_TSM
} else if (mode == 5) {
speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
} else if (mode == 7) {
speed=vtNightSpeed; duration=vtNightDuration; // Night
mode=9;
}
prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
setRFmode(mode,speed,duration);
if (!isNight) {
vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry4()
{
if (isNight) {
if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
}
} else {
if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
}
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
ReefAngel.DisplayVersion();
}
// Custom Main Screen
void DrawCustomMain()
{
char buf[16];
byte x = 5;
byte y = 2;
byte t;
if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }
// 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+=18;
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");
// Temp and PH
y+=8;
ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
ConvertNumToString(buf, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, buf, Num8x16);
pingSerial();
/// Display Sunrise / Sunset (to be calculated later...)
y+=20; t=x;
sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
// MoonPhase
y+=10;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
pingSerial();
// MoonLight %
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+=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 (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
y+=10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
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()
{
}
Re: Got my controller,
Added my relay expansion yesterday. Lot's of new additions to the code. The following features have been added:
Vacation Mode - Use WL sensor and turn the extension cord I have plugged in, into an ATO port
ATO Refill - Use WL sensor to fill my ATO reservoir to 100%
Power Outage detection - Turn off all unnecessary items if we lose power.
Complete Darkness - Added 2 hours without moonlghts to my schedule
Smart_NTM Delay - Added a delay after feeding mode before Smart_NTM is activated
Dosing Pumps - Added these, but I need to get better pumps
Coral Acclimation Schedule - Need to test this, but should be working good.
Vacation Mode - Use WL sensor and turn the extension cord I have plugged in, into an ATO port
ATO Refill - Use WL sensor to fill my ATO reservoir to 100%
Power Outage detection - Turn off all unnecessary items if we lose power.
Complete Darkness - Added 2 hours without moonlghts to my schedule
Smart_NTM Delay - Added a delay after feeding mode before Smart_NTM is activated
Dosing Pumps - Added these, but I need to get better pumps
Coral Acclimation Schedule - Need to test this, but should be working good.
Code: Select all
#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>
#include <SunLocation.h>
#include <WaterLevel.h>
////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
// Custom Menu Code
#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 = "Refugium Light";
prog_char menu5_label[] PROGMEM = "Clear Overrides";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "WLS Calibration";
prog_char menu8_label[] PROGMEM = "Date / Time";
prog_char menu9_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, menu9_label
};
// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=20;
byte vtNightDuration=10;
boolean isNight=false;
boolean isFeeding=false;
boolean feedDelay=false;
boolean vtOverride=false;
boolean floatHigh=true;
boolean powerOutage=true;
int riseOffset=472;
int setOffset=506;
byte acclTime=0;
SunLocation sl;
//Define Custom Memory Location
#define Mem_B_RefillATO 100
#define Mem_B_Vacation 101
#define Mem_B_AcclTime 102
//Define Relay Ports
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define Extension 5
#define Heater 6
#define Refugium 7
#define Reactor 8
#define Unused1 Box1_Port1
#define Unused2 Box1_Port2
#define Vortech1 Box1_Port3
#define Vortech2 Box1_Port4
#define VortechUPS Box1_Port5
#define Unused3 Box1_Port6
#define DPump1 Box1_Port7
#define DPump2 Box1_Port8
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
// 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( Vortech1 );
ReefAngel.Relay.On( Vortech2 );
ReefAngel.Relay.On( VortechUPS );
ReefAngel.Relay.Off( Extension );
ReefAngel.Relay.Off( Unused1 );
ReefAngel.Relay.Off( Unused2 );
ReefAngel.Relay.Off( Unused3 );
////// Place additional initialization code below here
// Setup SunLocation class
sl.Init(-18.285833, 147.699722); // GBR
// Save previous used vortech mode.
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
ReefAngel.CustomVar[7]=255;
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.StandardHeater( Heater );
ReefAngel.DosingPumpRepeat1( DPump1 );
ReefAngel.DosingPumpRepeat2( DPump2 );
ReefAngel.StandardLights( WhiteLEDs );
ReefAngel.ActinicLights( BlueLEDs );
if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
{
powerOutage=false;
LastStart=now();
ReefAngel.CustomVar[1]=0;
}
ReefAngel.Relay.DelayedOn( Skimmer, 5 );
ReefAngel.Relay.DelayedOn( Reactor, 10 );
if (InternalMemory.read( Mem_B_Vacation )==1) {
ReefAngel.WaterLevelATO( Extension,30,61,63 );
ReefAngel.CustomVar[3]=1;
} else {
ReefAngel.CustomVar[3]=0;
ReefAngel.Relay.Off( Extension );
}
if (InternalMemory.read( Mem_B_RefillATO )==1) {
if (ReefAngel.WaterLevel.GetLevel()<=100) {
ReefAngel.Relay.On( Extension );
ReefAngel.CustomVar[2]=1;
} else {
ReefAngel.Relay.Off( Extension );
InternalMemory.write( Mem_B_RefillATO, 0);
ReefAngel.CustomVar[2]=0;
}
}
acclTime=InternalMemory.read( Mem_B_AcclTime );
if (acclTime > 0) {
ReefAngel.CustomVar[4]=acclTime;
if ( (hour()==0) && (minute()==0) && (second()==0)) {
acclTime--;
InternalMemory.write( Mem_B_AcclTime, acclTime );
}
}
////// Place your custom code below here
sl.SetOffset(-9,riseOffset+(acclTime*240),-9,setOffset+(acclTime*120)); // 9 hour difference
sl.CheckAndUpdate();
// Let's make the time one number so we can figure out it's night or day...
int currTime=NumMins(hour(),minute());
int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute());
int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;
if (isNight)
{
if ((hour()>=3) && (hour()<=5)) {
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
} else {
ReefAngel.PWM.SetDaylight( MoonPhase() );
ReefAngel.PWM.SetActinic( MoonPhase() );
}
if (vtOverride==false && isFeeding==false) {
setRFmode(Night,vtNightSpeed,vtNightDuration);
}
ReefAngel.Relay.On( Refugium );
} else {
// It's Daytime!
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
if (vtOverride==false && vtMode==Night) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
ReefAngel.Timer[1].ForceTrigger();
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) floatHigh=true;
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
{
isFeeding=false;
feedDelay=true;
setRFtimer(30); // Stay in FeedMode for a while longer.
}
else if (vtOverride && ReefAngel.Timer[1].IsTriggered())
{
vtOverride=false;
if(feedDelay) {
feedDelay=false;
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
} else {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
}
else
{
setRFmode(); // Update if set externally
}
if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
if (ReefAngel.LowATO.IsActive()) {
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
if (ReefAngel.HighATO.IsActive()) {
if (!floatHigh) {
floatHigh=true;
ReefAngel.CustomVar[0]=1;
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
} else {
ReefAngel.CustomVar[0]=0;
floatHigh=false;
}
// Turn off Skimmer if Return pump is shutoff.
if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
// Power Outage - Only Return Pump
if (!ReefAngel.Relay.IsRelayPresent(EXP1_RELAY)) // Exp1 Relay NOT present
{
powerOutage=true;
ReefAngel.Relay.Off (Skimmer);
ReefAngel.Relay.Off (WhiteLEDs);
ReefAngel.Relay.Off (BlueLEDs);
ReefAngel.Relay.Off (Extension);
ReefAngel.Relay.Off (Heater);
ReefAngel.Relay.Off (Refugium);
ReefAngel.Relay.Off (Reactor);
ReefAngel.CustomVar[1]=1;
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {
// Check if mode has changed
if (mode!=vtMode) {
vtPrevMode=vtMode;
vtMode=mode;
if (mode!=InternalMemory.RFMode_read()) {
InternalMemory.RFMode_write(mode);
}
// Temp fix for coming out of night mode
if (vtPrevMode==Night) {
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0);
ReefAngel.RF.UseMemory=true;
}
if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
setRFtimer(60);
}
}
// Check if speed has changed
if (speed!=vtSpeed) {
vtPrevSpeed=vtSpeed;
vtSpeed=speed;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
// Check if duration has changed
if (duration!=vtDuration) {
vtPrevDuration=vtDuration;
vtDuration=duration;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
}
void setRFmode() {
setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
ReefAngel.Timer[1].SetInterval(minutes*60);
ReefAngel.Timer[1].Start();
vtOverride=true;
}
// Menu Code
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
byte mode,speed,duration,prev_mode,prev_speed,prev_dur;
mode=vtMode;
mode++;
prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
if (mode > 9 ) {
mode=0;
speed=50; duration=0; // Constant
} else if (mode == 1) {
speed=40; duration=0; // Lagoon
} else if (mode == 2) {
speed=45; duration=0; // Reef Crest
} else if (mode == 3) {
speed=55; duration=10; // Short Pulse
} else if (mode == 4) {
speed=55; duration=20; // Long Pulse
} else if (mode == 6) {
speed=50; duration=10; // Smart_TSM
} else if (mode == 5) {
speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
} else if (mode == 7) {
speed=vtNightSpeed; duration=vtNightDuration; // Night
mode=9;
}
prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
setRFmode(mode,speed,duration);
if (!isNight) {
vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry4()
{
if (isNight) {
if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
}
} else {
if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
}
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.SetupCalibrateWaterLevel();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry9()
{
ReefAngel.DisplayVersion();
}
// Custom Main Screen
void DrawCustomMain()
{
char buf[16];
byte x = 5;
byte y = 2;
byte t;
// if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }
// Main Header
// ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
// Date+Time
ReefAngel.LCD.DrawDate(x+1, y);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
// Param Header
y+=12;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
// Temp and PH
y+=2;
ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
y+=6;
ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
ConvertNumToString(buf, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
y+=5;
ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
pingSerial();
/// Display Sunrise / Sunset (to be calculated later...)
y+=15; t=x;
sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
pingSerial();
// MoonPhase
y+=10;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
pingSerial();
// MoonLight %
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();
// Display Water level
y+=10; t=x;
ConvertNumToString(buf, ReefAngel.WaterLevel.GetLevel(), 1);
strcat(buf," ");
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); t+=60;
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);
// Vortech Mode
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 (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
y+=10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
pingSerial();
y+=15;
// Relays
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
pingSerial();
y+=12;
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
pingSerial();
}
void DrawCustomGraph()
{
}
Re: Got my controller,
Bugs worked out with Coral Acclimation counter and a lot of comments / code cleanup
Code: Select all
#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 <RA_Colors.h>
#include <RA_CustomColors.h>
//#include <Salinity.h>
//#include <IO.h>
//#include <ORP.h>
//#include <AI.h>
#include <RF.h>
#include <ReefAngel.h>
#include <SunLocation.h>
#include <WaterLevel.h>
////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 8
#define NUMBERS_8x16
// Custom Menu Code
#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 = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO Clear";
prog_char menu6_label[] PROGMEM = "Overheat Clear";
prog_char menu7_label[] PROGMEM = "PH Calibration";
prog_char menu8_label[] PROGMEM = "WLS Calibration";
prog_char menu9_label[] PROGMEM = "Date / Time";
// 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, menu9_label
};
// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;
// Default Mode
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
// NTM mode
byte vtNTMSpeed=65;
byte vtNTMDuration=5;
// Night Mode
byte vtNightSpeed=20;
byte vtNightDuration=10;
boolean isNight=false;
boolean isFeeding=false;
boolean feedDelay=false;
boolean vtOverride=false;
boolean floatHigh=true;
boolean powerOutage=true;
byte acclTime=0;
SunLocation sl;
//Define Custom Memory Location
#define Mem_B_RefillATO 100
#define Mem_B_Vacation 101
#define Mem_B_AcclTime 102
#define Mem_B_ClearATO 103
//Define Relay Ports by Name
#define Return 1
#define Skimmer 2
#define WhiteLEDs 3
#define BlueLEDs 4
#define Extension 5
#define Heater 6
#define Refugium 7
#define Reactor 8
#define Unused1 Box1_Port1
#define Unused2 Box1_Port2
#define Vortech1 Box1_Port3
#define Vortech2 Box1_Port4
#define VortechUPS Box1_Port5
#define Unused3 Box1_Port6
#define DPump1 Box1_Port7
#define DPump2 Box1_Port8
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize Menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port8Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit | Port8Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port3Bit | Port4Bit ;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port3Bit | Port4Bit | 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 default on
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( Vortech1 );
ReefAngel.Relay.On( Vortech2 );
ReefAngel.Relay.On( VortechUPS );
// Ports that default off
ReefAngel.Relay.Off( Extension );
ReefAngel.Relay.Off( Unused1 );
ReefAngel.Relay.Off( Unused2 );
ReefAngel.Relay.Off( Unused3 );
////// Place additional initialization code below here
// Setup SunLocation class
sl.Init(-18.285833, 147.699722); // Great Barrier Reef
// What was our previous modes before we restarted?
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
ReefAngel.CustomVar[7]=255;
////// Place additional initialization code above here
}
void loop()
{
// Default port modes. Use Memory settings for external control
ReefAngel.StandardHeater( Heater );
ReefAngel.DosingPumpRepeat1( DPump1 );
ReefAngel.DosingPumpRepeat2( DPump2 );
ReefAngel.StandardLights( WhiteLEDs );
ReefAngel.ActinicLights( BlueLEDs );
////// Place your custom code below here
// See if power is back on so DelayedOn ports will reset.
if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
{
powerOutage=false;
LastStart=now();
ReefAngel.CustomVar[1]=0;
}
ReefAngel.Relay.DelayedOn( Skimmer );
ReefAngel.Relay.DelayedOn( Reactor );
// We're on vacation! Use external pump to top off the top off.
if (InternalMemory.read( Mem_B_Vacation )==1) {
ReefAngel.WaterLevelATO( Extension,30,61,63 );
ReefAngel.CustomVar[3]=1;
} else {
ReefAngel.CustomVar[3]=0;
ReefAngel.Relay.Off( Extension );
}
// Real ATO Clear
if (InternalMemory.read( Mem_B_ClearATO )==1) {
ReefAngel.ATOClear();
}
// ATO Refill mode. Top off ATO reservoir until it's at 100%
if (InternalMemory.read( Mem_B_RefillATO )==1) {
if (ReefAngel.WaterLevel.GetLevel()<=100) {
ReefAngel.Relay.On( Extension );
ReefAngel.CustomVar[2]=1;
} else {
ReefAngel.Relay.Off( Extension );
InternalMemory.write( Mem_B_RefillATO, 0);
ReefAngel.CustomVar[2]=0;
}
}
// See if we are acclimating corals and decrement the countdown each night
static boolean acclCounterReady=false;
if ( acclCounterReady && now()%86400==0) {
acclTime--;
acclCounterReady=false;
InternalMemory.write( Mem_B_AcclTime, acclTime );
}
if ( now()%86400!=0 ) acclCounterReady=true;
// -9 hour difference for time zone. 472/506 seconds were calculation corrections
// The acclTime will adjust the sunrise/sunset if we are adjusting for new coral
sl.SetOffset(-9,472+(acclTime*240),-9,506-(acclTime*120));
// Calculate the new Sunrise / Sunset based on our GPS coordinates
sl.CheckAndUpdate();
// Let's make the time one number so we can figure out it's night or day...
int currTime=NumMins(hour(),minute());
int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute());
int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;
if (isNight)
{
if ((hour()>=3) && (hour()<=5)) {
// Some complete darkness
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
} else {
// Set moonlights to the MoonPhase
ReefAngel.PWM.SetDaylight( MoonPhase() );
ReefAngel.PWM.SetActinic( MoonPhase() );
}
// Vortech's to night mode.
if (vtOverride==false && isFeeding==false) {
setRFmode(Night,vtNightSpeed,vtNightDuration);
}
// Turn on refugium light opposite normal lights
ReefAngel.Relay.On( Refugium );
} else { // It's Daytime!
// Regular lights are controlled by Memory variables set in SunLocation class
ReefAngel.PWM.SetDaylight( 0 );
ReefAngel.PWM.SetActinic( 0 );
ReefAngel.Relay.Off( Refugium );
// Go back to our regularly scheduled program.
if (vtOverride==false && vtMode==Night) {
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
// Force the trigger to make sure we break feeding/night modes.
ReefAngel.Timer[1].ForceTrigger();
}
}
// Enable Feeding Mode flag
if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
// Turn on Refugium light during water change mode
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
// Enable ATOHigh flag on purpose during feed/water change mode so we don't get alerts.
if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) floatHigh=true;
/*** RF Modes ***/
// Here's what we do if we're just out of feeding mode...
if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding ) {
isFeeding=false;
feedDelay=true; // This will let us know we want some extra time before Smart_NTM
setRFtimer(30); // Start Smart_NTM in 30 minutes...
} else if (vtOverride && ReefAngel.Timer[1].IsTriggered()) { // Our RF timer is over.
vtOverride=false; // Stop overriding the default RF mode
// First let's deal with that extra 30 minutes
if(feedDelay) {
feedDelay=false; // Reset the feedDelay flag
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration); // Smart_NTM time!
} else {
// Otherwise go to Previous settings
setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
}
} else {
setRFmode(); // Update the mode if we change it remotely
}
// A little extra Smart_NTM never hurt anyone
if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
}
/*** End of RF modes ***/
// Turn off return pump if we run out of water!
if (ReefAngel.LowATO.IsActive()) {
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
}
// Turn off return if we are somehow overflowing the sump
if (ReefAngel.HighATO.IsActive()) {
if (!floatHigh) {
floatHigh=true;
ReefAngel.CustomVar[0]=1;
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
}
} else {
ReefAngel.CustomVar[0]=0;
floatHigh=false;
}
// Turn off Skimmer if Return pump is shutoff.
if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
}
// Power Outage - Only Return Pump should be active
if (!ReefAngel.Relay.IsRelayPresent(EXP1_RELAY)) // Expansion Relay NOT present
{
powerOutage=true;
ReefAngel.Relay.Off (Skimmer);
ReefAngel.Relay.Off (WhiteLEDs);
ReefAngel.Relay.Off (BlueLEDs);
ReefAngel.Relay.Off (Extension);
ReefAngel.Relay.Off (Heater);
ReefAngel.Relay.Off (Refugium);
ReefAngel.Relay.Off (Reactor);
ReefAngel.CustomVar[1]=1;
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "lnevo" );
ReefAngel.ShowInterface();
}
// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {
// Check if mode has changed
if (mode!=vtMode) {
vtPrevMode=vtMode;
vtMode=mode;
if (mode!=InternalMemory.RFMode_read()) {
InternalMemory.RFMode_write(mode);
}
// Fix for coming out of night mode
if (vtPrevMode==Night) {
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.SetMode(Feeding_Stop,0,0);
ReefAngel.RF.UseMemory=true;
}
// If it's at night or we are setting NTM, do this only temporarily
if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
setRFtimer(60);
}
}
// Check if speed has changed
if (speed!=vtSpeed) {
vtPrevSpeed=vtSpeed;
vtSpeed=speed;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
// Check if duration has changed
if (duration!=vtDuration) {
vtPrevDuration=vtDuration;
vtDuration=duration;
if (speed!=InternalMemory.RFSpeed_read()) {
InternalMemory.RFSpeed_write(speed);
}
}
}
void setRFmode() {
setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
ReefAngel.Timer[1].SetInterval(minutes*60);
ReefAngel.Timer[1].Start();
vtOverride=true;
}
// Menu Code
void MenuEntry1() {
ReefAngel.FeedingModeStart();
}
void MenuEntry2() {
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3() {
byte mode,speed,duration;
byte prev_mode,prev_speed,prev_dur;
mode=vtMode;
mode++;
prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
if (mode > 9 ) {
mode=0;
speed=50; duration=0; // Constant
} else if (mode == 1) {
speed=40; duration=0; // Lagoon
} else if (mode == 2) {
speed=45; duration=0; // Reef Crest
} else if (mode == 3) {
speed=55; duration=10; // Short Pulse
} else if (mode == 4) {
speed=55; duration=20; // Long Pulse
} else if (mode == 6) {
speed=50; duration=10; // Smart_TSM
} else if (mode == 5) {
speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
} else if (mode == 7) {
speed=vtNightSpeed; duration=vtNightDuration; // Night
mode=9;
}
prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
setRFmode(mode,speed,duration);
// If it's night time, don't overwrite the default daytime mode when using the menu
if (!isNight) {
vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry4() {
if (isNight) { // Turn off the Refugium light
if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
}
} else { // Turn on the Refugium light
if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
} else {
bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
}
}
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5() {
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6() {
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry7() {
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8() {
ReefAngel.SetupCalibrateWaterLevel();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry9() {
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
// Custom Main Screen
void DrawCustomMain() {
char buf[16];
byte x = 5;
byte y = 2;
byte t;
// Main Header
// ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
// Date+Time
ReefAngel.LCD.DrawDate(x+1, y);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
// Param Header
y+=12;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
// Temp and PH
y+=2;
ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
y+=6;
ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
ConvertNumToString(buf, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
y+=5;
ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
pingSerial();
/// Display Sunrise / Sunset (to be calculated later...)
y+=15; t=x;
sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
pingSerial();
// MoonPhase
y+=10;
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
pingSerial();
// MoonLight %
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();
// Display Water level
y+=10; t=x;
ConvertNumToString(buf, ReefAngel.WaterLevel.GetLevel(), 1);
strcat(buf," ");
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); t+=60;
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);
// Vortech Mode
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 (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
y+=10; t=x;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
pingSerial();
y+=15;
// Relays
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
pingSerial();
y+=12;
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
pingSerial();
}
void DrawCustomGraph() {
}
Got my controller,
Ok, figured out what happened. I overwrote the code that reads the timer from memory and updates my CustomVar when I pasted roberto's timer change. So acclTime reverted to 0 and memory was written as 255. Portal showed 0 since I was no longer updating that variable. I will post my fixes and final code tonight.