I basically want to adapt Inevo's Vortech control, especially the custom modes and influence of the tide, and I thought the calculating the evaporation rate would be really cool as well. I also want to get the sunrise/set and moonrise/set code to work, since I hope to upgrade to controllable LEDs in the future.
I get it to compile, and it is running ok, but it doesn't seem to work as I expected.
1. I seem to have an extra Relay on my dashboard now, and I don't know why.
2. When in custom mode the vtmode stays at 1. Looking at the code I expected it to change every 6 hours.
3. Evaporation stays at 0.
4. The only way I can get the sunrise offset to wok is by hard coding the value; it doesn't seem to work from the memory.
There's probably a lot wrong with the code, so any help would be greatly appreciated!
Here's my 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 <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>
#include <SunLocation.h>
#include <Tide.h>
#include <Moon.h>
// Won't compile without this...
// ReefAngel.DCPump.UseMemory=true;
// Custom menus
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Refugium Light";
prog_char menu2_label[] PROGMEM = "Feeding Mode";
prog_char menu3_label[] PROGMEM = "Water Change";
prog_char menu4_label[] PROGMEM = "Vortech Mode";
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
};
// Define Custom Memory Locations
#define Mem_B_MoonOffset 100
#define Mem_I_Latitude 108
#define Mem_I_Longitude 110
#define Mem_B_AcclRiseOffset 112
#define Mem_B_AcclSetOffset 113
#define Mem_B_AcclDay 114
#define Mem_B_TideMin 117
#define Mem_B_TideMax 118
#define Mem_B_PumpOffset 119
#define Mem_B_FeedingRF 120
#define Mem_B_NightRF 121
#define Mem_B_NightSpeed 122
#define Mem_B_NightDuration 123
#define Mem_B_NTMSpeed 124
#define Mem_B_NTMDuration 125
#define Mem_B_NTMDelay 126
#define Mem_B_NTMTime 127
#define Mem_B_LogATO 140
#define Mem_B_LogPrevATO 141
#define Mem_B_TideMode 143
#define Mem_B_LightMode 160
#define Mem_B_LightOffset 161
#define Mem_I_RiseOffset 162
#define Mem_I_SetOffset 164
#define Mem_B_AcclActinicOffset 166
#define Mem_B_AcclDaylightOffset 167
#define Mem_B_RandomMode 168
#define Mem_B_GyreOffset 169
#define Mem_B_MoonMode 170
#define Mem_B_LightsOffPerc 171
#define Mem_B_FeedingSpeed 172
#define Mem_B_WCSpeed 173
#define Mem_B_EnableStorm 178
#define Mem_B_ForceRandomTide 179
#define Mem_B_PrintDebug 198
#define Mem_B_ResetMemory 199
void init_memory() {
// Initialize Custom Memory Locations
InternalMemory.write(Mem_B_MoonOffset,30);
InternalMemory.write_int(Mem_I_Latitude,21);
InternalMemory.write_int(Mem_I_Longitude,-73);
InternalMemory.write(Mem_B_AcclRiseOffset,4);
InternalMemory.write(Mem_B_AcclSetOffset,2);
InternalMemory.write(Mem_B_AcclDay,0);
InternalMemory.write(Mem_B_TideMin,10);
InternalMemory.write(Mem_B_TideMax,20);
InternalMemory.write(Mem_B_PumpOffset,80);
InternalMemory.write(Mem_B_FeedingRF,true);
InternalMemory.write(Mem_B_NightRF,false);
InternalMemory.write(Mem_B_NightSpeed,35);
InternalMemory.write(Mem_B_NightDuration,16);
InternalMemory.write(Mem_B_NTMSpeed,60);
InternalMemory.write(Mem_B_NTMDuration,5);
InternalMemory.write(Mem_B_NTMDelay,15);
InternalMemory.write(Mem_B_NTMTime,150);
InternalMemory.write(Mem_B_TideMode,0);
InternalMemory.write(Mem_B_LightOffset,20);
InternalMemory.write(Mem_B_LightMode,1);
InternalMemory.write_int(Mem_I_RiseOffset,5);
InternalMemory.write_int(Mem_I_SetOffset,5);
InternalMemory.write(Mem_B_AcclActinicOffset,100);
InternalMemory.write(Mem_B_AcclDaylightOffset,100);
InternalMemory.write(Mem_B_RandomMode,true);
InternalMemory.write(Mem_B_GyreOffset,10);
InternalMemory.write(Mem_B_MoonMode,1);
InternalMemory.write(Mem_B_LightsOffPerc,1);
InternalMemory.write(Mem_B_FeedingSpeed,3);
InternalMemory.write(Mem_B_WCSpeed,25);
InternalMemory.write(Mem_B_EnableStorm,true);
InternalMemory.write(Mem_B_LogATO,0);
InternalMemory.write(Mem_B_LogPrevATO,0);
InternalMemory.write(Mem_B_ResetMemory,false);
}
////// Place global variable code below here
#define NUMBERS_8x16
#define Var_Tide 0
#define Var_LogATO 1
#define Var_TideMode 2
#define Level 75
#define MinPWM 55
#define OperatingPWM 75
// Custom classes
SunLocation sun;
Tide tide;
// Vortech Variables
byte vtMode, vtSpeed, vtDuration;
// Roberto Flow Variables
long nummillis=5000;
byte PWMValue=0;
unsigned long lastmillis=millis();
boolean override=false;
#define Refugium 4
// Needs to be global for DrawCustomGraph()
int ScreenID=1;
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items)); // Initialize Menu
ReefAngel.AddSalinityExpansion(); // Salinity Expansion Module
ReefAngel.AddMultiChannelWaterLevelExpansion(); // Multi-Channel Water Level Expanion Module
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port5Bit | Port7Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port5Bit | Port7Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port6Bit;
// Use T1 probe as temperature and T2 as overheat
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T2_PROBE;
// Feeeding and Water Change mode speed
ReefAngel.DCPump.FeedingSpeed=0;
ReefAngel.DCPump.WaterChangeSpeed=0;
// Ports that are always on
ReefAngel.Relay.On( Port2 );
ReefAngel.Relay.On( Port5 );
ReefAngel.Relay.On( Port7 );
ReefAngel.Relay.On( Port8 );
////// Place additional initialization code below here
InternalMemory.WaterLevelMax_write(1800);
ReefAngel.RF.UseMemory=false;
randomSeed(now()/SECS_PER_DAY);
Serial.println("Booting.");
if (InternalMemory.read(Mem_B_ResetMemory))
init_memory();
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.SingleATOLow( Port1 );
ReefAngel.DayLights( Port4 );
ReefAngel.StandardHeater( Port6 );
ReefAngel.DCPump.UseMemory = true;
//ReefAngel.DCPump.DaylightChannel = None;
//ReefAngel.DCPump.ActinicChannel = Sync;
////// Place your custom code below here
// Lighting and Flow
SetSun(); // Setup Sun rise/set lighting
// AcclimateLED(); // Apply acclimation dimming
SetMoon(); // Setup Moon rise/set lighting
// FillInMoon(); // Fill in 5% to 0% gap in main LEDs
// LEDPresets();
// CheckCloud(); // Check for cloud and lightning.
// UpdateLED();
SetTide(); // Set High/Low tide properties
SetRF(); // Set Vortech modes
CheckATO(); // Calculate ATO reservoir evaporation
RefugiumLight(); // Turn off refugium lights
//Kalk stirrer on for 10 min per hour
if (minute()< 10)
{
ReefAngel.Relay.On( Port3 );
}
else
{
ReefAngel.Relay.Off( Port3 );
}
// RobertoFlow
PWMValue=OperatingPWM;
if (ReefAngel.WaterLevel.GetLevel(2)==0)
{
PWMValue=77;
}
else
{
if (ReefAngel.WaterLevel.GetLevel(2)<Level-2)
{
override=true;
lastmillis=millis();
PWMValue+=(Level-ReefAngel.WaterLevel.GetLevel(2));
}
if (ReefAngel.WaterLevel.GetLevel(2)>Level+2)
{
override=true;
lastmillis=millis();
PWMValue-=(ReefAngel.WaterLevel.GetLevel(2)-Level);
}
if (millis()-lastmillis>nummillis && override)
{
override=false;
}
if (!override) PWMValue=OperatingPWM;
if (ReefAngel.WaterLevel.GetLevel(2)>Level+10) PWMValue=MinPWM;
PWMValue=constrain(PWMValue,MinPWM,100);
}
ReefAngel.PWM.SetActinic(PWMValue);
// This should always be the last line
ReefAngel.Portal( "gpsinger","8228" );
ReefAngel.ShowInterface();
}
void SetSun() {
// Start acclimation routine
int acclRiseOffset=InternalMemory.read(Mem_B_AcclRiseOffset)*60;
int acclSetOffset=InternalMemory.read(Mem_B_AcclSetOffset)*60;
byte acclDay=InternalMemory.read(Mem_B_AcclDay);
// See if we are acclimating corals and decrease the countdown each day
static boolean acclCounterReady=false;
if (now()%SECS_PER_DAY!=0) acclCounterReady=true;
if (now()%SECS_PER_DAY==0 && acclCounterReady && acclDay>0) {
acclDay--;
acclCounterReady=false;
InternalMemory.write(Mem_B_AcclDay,acclDay);
}
// End acclimation
// Add some customizable offsets
sun.Init(InternalMemory.read_int(Mem_I_Latitude), InternalMemory.read_int(Mem_I_Longitude));
int riseOffset=(-5);
int setOffset=(-5);
sun.SetOffset(riseOffset,(acclDay*acclRiseOffset),setOffset,(-acclDay*acclSetOffset)); // Bahamas
sun.CheckAndUpdate(); // Calculate today's Sunrise / Sunset
byte lightOffset=InternalMemory.read(Mem_B_LightOffset); // left right separation
byte actinicOffset=InternalMemory.ActinicOffset_read();
// Make sure light resets to zero at night.
for(int i=0;i<4;i++) { ReefAngel.PWM.SetChannel(i,0); }
switch(InternalMemory.read(Mem_B_LightMode)) {
case 0: {
// Daylights
ReefAngel.PWM.Channel0PWMSlope(lightOffset,0);
ReefAngel.PWM.Channel2PWMSlope(0,lightOffset);
// Actinics
ReefAngel.PWM.Channel1PWMSlope(actinicOffset+lightOffset,actinicOffset);
ReefAngel.PWM.Channel3PWMSlope(actinicOffset,actinicOffset+lightOffset);
break;
}
case 1: {
// Daylights
ReefAngel.PWM.Channel0PWMParabola(lightOffset,0);
ReefAngel.PWM.Channel2PWMParabola(0,lightOffset);
// Actinics
ReefAngel.PWM.Channel1PWMParabola(actinicOffset+lightOffset,actinicOffset);
ReefAngel.PWM.Channel3PWMParabola(actinicOffset,actinicOffset+lightOffset);
break;
}
case 2: {
// Daylights
ReefAngel.PWM.Channel0PWMSmoothRamp(lightOffset,0);
ReefAngel.PWM.Channel2PWMSmoothRamp(0,lightOffset);
// Actinics
ReefAngel.PWM.Channel1PWMSmoothRamp(actinicOffset+lightOffset,actinicOffset);
ReefAngel.PWM.Channel3PWMSmoothRamp(actinicOffset,actinicOffset+lightOffset);
break;
}
case 3: {
// Daylights
ReefAngel.PWM.Channel0PWMSigmoid(lightOffset,0);
ReefAngel.PWM.Channel2PWMSigmoid(0,lightOffset);
// Actinics
ReefAngel.PWM.Channel1PWMSigmoid(actinicOffset+lightOffset,actinicOffset);
ReefAngel.PWM.Channel3PWMSigmoid(actinicOffset,actinicOffset+lightOffset);
break;
}
}
}
void SetMoon() {
byte offset=InternalMemory.read(Mem_B_MoonOffset);
byte startD=InternalMemory.read(Mem_B_PWMSlopeStartD);
byte endD=InternalMemory.read(Mem_B_PWMSlopeEndD);
byte timeD=InternalMemory.read(Mem_B_PWMSlopeDurationD);
byte startA=InternalMemory.read(Mem_B_PWMSlopeStartA);
byte endA=InternalMemory.read(Mem_B_PWMSlopeEndA);
byte timeA=InternalMemory.read(Mem_B_PWMSlopeDurationA);
time_t onTime=ScheduleTime(Moon.riseH, Moon.riseM,0);
time_t offTime=ScheduleTime(Moon.setH, Moon.setM,0);
time_t offsetOnTime=ScheduleTime(Moon.riseH, Moon.riseM,0)-(offset*60);
time_t offsetOffTime=ScheduleTime(Moon.setH, Moon.setM,0)-(offset*60);
byte actRiseH=(offsetOnTime%SECS_PER_DAY)/SECS_PER_HOUR;
byte actRiseM=((offsetOnTime%SECS_PER_DAY)%SECS_PER_HOUR)/60;
byte actSetH=(offsetOffTime%SECS_PER_DAY)/SECS_PER_HOUR;
byte actSetM=((offsetOffTime%SECS_PER_DAY)%SECS_PER_HOUR)/60;
static byte mp=MoonPhase();
if (mp!=MoonPhase()) {
InternalMemory.write(Mem_B_PWMSlopeEndD,mp);
InternalMemory.write(Mem_B_PWMSlopeEndA,mp);
mp=MoonPhase();
}
moon_init(InternalMemory.read_int(Mem_I_Latitude), InternalMemory.read_int(Mem_I_Longitude));
// Make sure light resets to zero at night.
ReefAngel.PWM.SetDaylight(0);
ReefAngel.PWM.SetActinic(0);
switch(InternalMemory.read(Mem_B_MoonMode)) {
case 0: {
// Daylights
ReefAngel.PWM.SetDaylightRaw(PWMSlopeHighRes(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM,startA,endA,timeA,0));
ReefAngel.PWM.SetActinicRaw(PWMSlopeHighRes(actRiseH,actRiseM,actSetH,actSetM,startD,endD,timeD,0));
break;
}
case 1: {
ReefAngel.PWM.SetDaylightRaw(PWMParabolaHighRes(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM, startA,endA,0));
ReefAngel.PWM.SetActinicRaw(PWMParabolaHighRes(actRiseH,actRiseM,actSetH,actSetM, startD,endD,0));
break;
}
case 2: {
ReefAngel.PWM.SetDaylightRaw(PWMSmoothRampHighRes(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM,startA,endA,timeA,0));
ReefAngel.PWM.SetActinicRaw(PWMSmoothRampHighRes(actRiseH,actRiseM,actSetH,actSetM,startD,endD,timeD,0));
break;
}
case 3: {
ReefAngel.PWM.SetDaylightRaw(PWMSigmoidHighRes(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM,startA,endA,0));
ReefAngel.PWM.SetActinicRaw(PWMSigmoidHighRes(actRiseH,actRiseM,actSetH,actSetM,startD,endD,0));
break;
}
}
}
void SetTide() {
byte nightSpeed=InternalMemory.read(Mem_B_NightSpeed);
byte tideMin=InternalMemory.read(Mem_B_TideMin);
byte tideMax=InternalMemory.read(Mem_B_TideMax);
// Set tide offsets
tide.SetOffset(tideMin, tideMax);
// Set tide speed. Slope in/out of Night Mode
tide.SetSpeed(PWMSlope(sun.GetRiseHour()-1,sun.GetRiseMinute(),
sun.GetSetHour(),sun.GetSetMinute(),nightSpeed+tideMin,vtSpeed,120,nightSpeed+tideMin));
// Show tide info on portal
ReefAngel.CustomVar[Var_Tide]=tide.CalcTide();
}
void SetRF() {
int ntmDelay=InternalMemory.read(Mem_B_NTMDelay)*60;
int ntmTime=InternalMemory.read(Mem_B_NTMTime)*60;
boolean nightRF=InternalMemory.read(Mem_B_NightRF);
boolean feedingRF=InternalMemory.read(Mem_B_FeedingRF);
static time_t t;
ReefAngel.RF.FeedingSpeed=InternalMemory.read(Mem_B_FeedingSpeed);
ReefAngel.RF.WaterChangeSpeed=InternalMemory.read(Mem_B_WCSpeed);
vtMode=InternalMemory.RFMode_read();
vtSpeed=InternalMemory.RFSpeed_read();
vtDuration=InternalMemory.RFDuration_read();
if ((now()-t > ntmDelay && now()-t < ntmTime+ntmDelay) && feedingRF) {
// Post feeding mode
vtMode=Smart_NTM;
vtSpeed=InternalMemory.read(Mem_B_NTMSpeed);
vtDuration=InternalMemory.read(Mem_B_NTMDuration);
} else if (!sun.IsDaytime() && nightRF) {
vtMode=Night;
vtSpeed=InternalMemory.read(Mem_B_NightSpeed);
vtDuration=InternalMemory.read(Mem_B_NightDuration);
} else {
if (vtMode!=Night && ReefAngel.RF.Mode==Night)
ReefAngel.RF.SetMode(Night_Stop,0,0);
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) {
t=now(); // Run post feeding mode when this counter stops
} else if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
// Not needed anymore.
// ReefAngel.RF.SetMode(Constant,25,0);
} else {
if ((vtMode==Smart_NTM) || (vtMode==ShortPulse)) vtDuration=InternalMemory.read(Mem_B_NTMDuration);
(vtMode==Custom) ? RFCustom() : ReefAngel.RF.SetMode(vtMode,vtSpeed,vtDuration);
}
}
void RFCustom() {
static boolean changeMode;
byte rcSpeed, rcSpeedAS;
// Define new modes
const int BHazard=15;
const int RA_ReefCrest=16;
const int RA_Lagoon=17;
const int RA_TidalSwell=18;
const int RA_Smart_NTM=19;
const int RA_ShortPulse=20;
const int RA_LongPulse=21;
byte tideSpeed=tide.CalcTide();
byte tideMin=InternalMemory.read(Mem_B_TideMin);
byte tideMax=InternalMemory.read(Mem_B_TideMax);
byte tideMode=InternalMemory.read(Mem_B_TideMode);
float pumpOffset=(float) InternalMemory.read(Mem_B_PumpOffset)/100;
byte RandomModes[]={ ReefCrest, TidalSwell, Smart_NTM, Lagoon, ShortPulse, LongPulse, BHazard, Else, Sine, Constant };
// if (now()%SECS_PER_DAY!=0 && InternalMemory.read(Mem_B_RandomMode)) changeMode=true;
// if (now()%SECS_PER_DAY==0 && changeMode) {
if (now()%(6*SECS_PER_HOUR)!=10 && InternalMemory.read(Mem_B_RandomMode)) changeMode=true;
if (now()%(6*SECS_PER_HOUR)==10 && changeMode) {
tideMode=random(100)%sizeof(RandomModes);
InternalMemory.write(Mem_B_TideMode,tideMode);
changeMode=false;
}
// Choose another random mode if triggered
if (InternalMemory.read(Mem_B_ForceRandomTide)) {
tideMode=random(100)%sizeof(RandomModes);
InternalMemory.write(Mem_B_TideMode,tideMode);
InternalMemory.write(Mem_B_ForceRandomTide,false);
}
ReefAngel.CustomVar[Var_TideMode]=tideMode+1;
switch (RandomModes[tideMode]) {
case ReefCrest: {
ReefAngel.RF.SetMode(ReefCrest,tideSpeed,vtDuration);
return;
break;
}
case Lagoon: {
ReefAngel.RF.SetMode(Lagoon,tideSpeed,vtDuration);
return;
break;
}
case TidalSwell: {
ReefAngel.RF.SetMode(TidalSwell,tideSpeed,vtDuration);
return;
break;
}
case Smart_NTM: {
ReefAngel.RF.SetMode(Smart_NTM,tideSpeed,vtDuration);
return;
break;
}
case ShortPulse: {
ReefAngel.RF.SetMode(ShortPulse,tideSpeed,vtDuration);
return;
break;
}
case LongPulse: {
ReefAngel.RF.SetMode(LongPulse,tideSpeed,vtDuration);
return;
break;
}
case RA_ReefCrest: {
rcSpeed=ReefCrestMode(tideSpeed,vtDuration*2,true);
rcSpeedAS=ReefCrestMode(tideSpeed,vtDuration*2,false);
break;
}
case RA_Lagoon: {
rcSpeed=ReefCrestMode(tideSpeed,vtDuration,true);
rcSpeedAS=ReefCrestMode(tideSpeed,vtDuration,false);
break;
}
case RA_TidalSwell: {
rcSpeed=TidalSwellMode(tideSpeed,true);
rcSpeedAS=TidalSwellMode(tideSpeed,false);
break;
}
case RA_Smart_NTM: {
rcSpeed=NutrientTransportMode(0,tideSpeed,vtDuration*50,true);
rcSpeedAS=NutrientTransportMode(0,tideSpeed,vtDuration*50,false);
break;
}
case RA_ShortPulse: {
rcSpeed=ShortPulseMode(0,tideSpeed,vtDuration*50,true);
rcSpeedAS=ShortPulseMode(0,tideSpeed,vtDuration*50,false);
break;
}
case RA_LongPulse: {
rcSpeed=LongPulseMode(0,tideSpeed,vtDuration,true);
rcSpeedAS=LongPulseMode(0,tideSpeed,vtDuration,false);
break;
}
case Else: {
rcSpeed=ElseMode(tideSpeed,vtDuration*2,true);
rcSpeedAS=ElseMode(tideSpeed,vtDuration*2,false);
break;
}
case BHazard: {
rcSpeed=millis()%1200>800?tideSpeed:0;
rcSpeedAS=millis()%1200<400?0:tideSpeed;
break;
}
case Sine: {
rcSpeed=SineMode(tideSpeed-tideMin,tideSpeed+tideMin,vtDuration*100,true);
rcSpeedAS=SineMode(tideSpeed-tideMin,tideSpeed+tideMin,vtDuration*100,false);
break;
}
default: {
rcSpeed=tideSpeed;
rcSpeedAS=tideSpeed;
pumpOffset=(float) InternalMemory.read(Mem_B_GyreOffset)/100;
}
}
ReefAngel.RF.SetMode(Custom,rcSpeedAS*pumpOffset,tide.isOutgoing());
ReefAngel.RF.SetMode(Custom,rcSpeed,tide.isIncoming());
}
void NextRFMode() {
vtMode++;
if (vtMode > 12) {
vtMode=0;
vtSpeed=50; // Constant
} else if (vtMode == 1) {
vtSpeed=40; // Lagoon
} else if (vtMode == 2) {
vtSpeed=45; // Reef Crest
} else if (vtMode == 3) {
vtSpeed=55; vtDuration=10; // Short Pulse
} else if (vtMode == 4) {
vtSpeed=55; vtDuration=20; // Long Pulse
} else if (vtMode == 5) {
vtSpeed=InternalMemory.read(Mem_B_NTMSpeed);
vtDuration=InternalMemory.read(Mem_B_NTMDuration); // Smart_NTM
} else if (vtMode == 6) {
vtSpeed=50; vtDuration=10; // Smart_TSM
} else if (vtMode == 7) {
vtSpeed=InternalMemory.read(Mem_B_NightSpeed);
vtDuration=InternalMemory.read(Mem_B_NightDuration);
vtMode=9; // Night
} else if (vtMode == 10) {
vtSpeed=65; vtDuration=5; // Storm
} else if (vtMode == 11) {
vtSpeed=45; vtDuration=10; // Custom
}
if (vtMode!=InternalMemory.RFMode_read())
InternalMemory.RFMode_write(vtMode);
if (vtSpeed!=InternalMemory.RFSpeed_read())
InternalMemory.RFSpeed_write(vtSpeed);
if (vtDuration!=InternalMemory.RFDuration_read())
InternalMemory.RFDuration_write(vtDuration);
}
// Calculate evaportation rate
void CheckATO() {
byte numHours, index, level, delta;
float duration;
const int logHours=12;
static float rate, logLevel[logHours];
static boolean rateSaved, rateInit, refillActive;
numHours=(now()%(SECS_PER_HOUR*logHours))/SECS_PER_HOUR;
level=ReefAngel.WaterLevel.GetLevel(1);
// Initialize the array based on last rate
// Wait a little to make sure WL reading properly. Or we're refilling.
if (now()-LastStart>1 && !rateInit) {
rateInit=true;
index=numHours;
rate=InternalMemory.read(Mem_B_LogATO);
for (byte i=0;i<logHours;i++) {
logLevel[index]=level+(i*(rate/24.0));
//Serial.print(i);
//Serial.print(": ");
//Serial.print(index);
//Serial.print(": ");
//Serial.println(logLevel[index]);
index>0?index--:index=11;
}
}
if(minute()==59 && rateSaved==false) {
InternalMemory.write(Mem_B_LogATO, rate);
logLevel[numHours]=level;
rateSaved=true;
} else {
rateSaved=false;
}
ReefAngel.CustomVar[Var_LogATO]=rate;
// if(now()%30==0) Serial.println(rate);
}
void RefugiumLight() {
int runtime=2*SECS_PER_HOUR;
static boolean trigger=false;
static time_t t;
// Refugium Light Turned on
if (ReefAngel.Relay.isMaskOn(Refugium) && trigger==false) {
t=now();
trigger=true;
}
if ((now()-t > runtime) && trigger==true) {
ReefAngel.Relay.Auto(Refugium);
trigger=false;
}
}
// Menu Code
void MenuEntry1() {
// Toggle refugium light between on/auto.
ReefAngel.Relay.Override(Refugium, ReefAngel.Relay.Status(Refugium)+1);
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry2() {
ReefAngel.FeedingModeStart();
}
void MenuEntry3() {
ReefAngel.WaterChangeModeStart();
}
void MenuEntry4() {
NextRFMode();
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();
}
void MenuEntry8() {
ReefAngel.SetupCalibrateWaterLevel();
}
void MenuEntry9() {
ReefAngel.SetupDateTime();
}
// Custom Main Screen
void DrawCustomMain() {
const int NumScreens=3;
static boolean drawGraph=true;
// Main Header
// ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, 2,"Lee's Reef");
ReefAngel.LCD.DrawDate(5,2);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 128, 11);
// Param Header
DrawParams(5,14);
switch (ScreenID) {
case 0:
{
if (drawGraph) { ReefAngel.LCD.DrawGraph(5,40); drawGraph=false; }
break;
}
case 1: { DrawStatus(5,40); break; }
case 2: { DrawSunMoon(5,40); break; }
}
// Draw Relay
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
// Date+Time
// ReefAngel.LCD.DrawDate(5,122);
if (ReefAngel.Joystick.IsLeft()) {
ReefAngel.ClearScreen(DefaultBGColor);
ScreenID--; drawGraph=true;
}
if (ReefAngel.Joystick.IsRight()) {
ReefAngel.ClearScreen(DefaultBGColor);
ScreenID++; drawGraph=true;
}
if (ScreenID<0) ScreenID=NumScreens-1;
if (ScreenID>=NumScreens) ScreenID=0;
}
void DrawCustomGraph() {
if (ScreenID==0)
ReefAngel.LCD.DrawGraph(5, 40);
}
void DrawParams(int x, int y) {
char buf[16];
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);
}
void DrawStatus(int x, int y) {
int t=x;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,15,y,"High",Font8x16);
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,85,y,"Low",Font8x16);
if (ReefAngel.HighATO.IsActive()) {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_RED);
}
if (ReefAngel.LowATO.IsActive()) {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_RED);
}
y+=12;
// Display Water level
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); x+=65;
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.WaterLevel.GetLevel(1),DPColor,x,y,1);
x+=5*(intlength(ReefAngel.WaterLevel.GetLevel(1))+1);
ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x, y, "%");
y+=12; x=t;
// Vortech Mode
ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); x+=20;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,t+(128-t),y+8);
if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,x,y,"Constant");
else if (vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,x,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,x,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,x,y,"Night");
else if (vtMode == 10) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,0,x,y,"Storm");
else if (vtMode == 11) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,x,y,"Custom");
y+=10; x=t;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); x+=60;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,128,y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,vtSpeed); x+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,"/"); x+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,vtDuration);
y+=10; x=t;
}
void DrawSunMoon(int x, int y) {
char buf[16];
int t=x;
y+=2;
/// Display Sunrise / Sunset
sprintf(buf, "%02d:%02d", sun.GetRiseHour(), sun.GetRiseMinute());
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Rise:"); x+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
sprintf(buf, "%02d:%02d", sun.GetSetHour(), sun.GetSetMinute()); x+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Set:"); x+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
y+=15; x=t;
/// Display Moonrise / Moonset
sprintf(buf, "%02d:%02d", Moon.riseH, Moon.riseM);
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MR:"); x+=21;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
sprintf(buf, "%02d:%02d", Moon.setH, Moon.setM); x+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MS:"); x+=21;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf); x+=36;
if (Moon.isUp) ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,"@");
else ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,"_");
y+=10; x=t;
// MoonPhase
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,128,y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
y+=10; x=t;
}