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 <RF.h>
#include <ReefAngel.h>
#include <SunLocation.h>
#include <WaterLevel.h>
////// Place global variable code below 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 = "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;
SunLocation sl;
byte acclDay=0;
byte vacationMode=0;
//Define Custom Memory Location
#define Mem_B_RefillATO 100
#define Mem_B_ClearATO 101
#define Mem_B_Vacation 102
#define Mem_B_AcclDay 103
#define Var_HighATO 0
#define Var_Power 1
#define Var_Vacation 2
#define Var_AcclDay 3
//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;
// 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
// What was our previous modes before we restarted?
vtPrevMode=InternalMemory.RFMode_read();
vtPrevSpeed=InternalMemory.RFSpeed_read();
vtPrevDuration=InternalMemory.RFDuration_read();
// Dummy CustomVar to activate portal feature
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[Var_Power]=0;
}
ReefAngel.Relay.DelayedOn(Skimmer);
ReefAngel.Relay.DelayedOn(Reactor);
// Find out if we are on vacation
ReefAngel.CustomVar[Var_Vacation]=InternalMemory.read(Mem_B_Vacation);
vacationMode=ReefAngel.CustomVar[Var_Vacation];
// We're on vacation! Use external pump to top off the top off.
if (vacationMode==1) {
ReefAngel.WaterLevelATO(Extension,30,61,63);
} else {
ReefAngel.Relay.Off(Extension);
}
// Real ATO Clear
if (InternalMemory.read(Mem_B_ClearATO)==1) {
ReefAngel.ATOClear();
InternalMemory.write(Mem_B_ClearATO, 0);
}
// 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);
} else {
ReefAngel.Relay.Off(Extension);
InternalMemory.write(Mem_B_RefillATO, 0);
}
}
// See if we are acclimating corals and decrement the countdown each night
static boolean acclCounterReady=false;
if (now()%SECS_PER_DAY!=0) acclCounterReady=true;
acclDay=InternalMemory.read(Mem_B_AcclDay);
ReefAngel.CustomVar[Var_AcclDay]=acclDay;
if (acclDay > 0) {
if (acclCounterReady && now()%SECS_PER_DAY==0) {
acclDay--;
acclCounterReady=false;
InternalMemory.write(Mem_B_AcclDay,acclDay);
}
}
// -9 hour difference for time zone. 472/506 seconds were calculation corrections
// The acclDay will adjust the sunrise/sunset if we are adjusting for new coral
sl.SetOffset(-9,472+(acclDay*240),-9,506-(acclDay*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()<=4)) {
// 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);
}
}
// 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;
// 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);
}
// 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[Var_HighATO]=1;
bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
}
} else {
ReefAngel.CustomVar[Var_HighATO]=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[Var_Power]=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;
}
// Backup the previous modes. We don't want Night to become default...
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() {
// Toggle the refugium light if we choose this menu entry.
// Behavior is opposite for night vs day.
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");
// Had no room for this anymore :(
// 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+=12; 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();
// Display Water level
y+=10; t=x;
if (acclDay > 0) {
ConvertNumToString(buf, acclDay, 1);
strcat(buf," ");
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"Acclimation Day:"); t+=100;
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);
} else {
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+8);
}
// Relays
y+=10; t=x+7;
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
pingSerial();
y+=12;
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
pingSerial();
}
void DrawCustomGraph() {
}