My code: It does all the basic controls...heating/cooling pumping, turn relays, and feeding modes. What is unique is how I am trying to utilize some of these items (nothing fancy). Heating and cooling is based on a seasonal temps (I created my own to try). I set it up so I can adjust every month. The other unique thing is my control of pumps and since my lighting is a reef breeder and not controllable I thought it would be fun to see if I can get a random pump mode (daily) and an occasional storm mode (we'll see if it works).
Please look over and provide any comments/feedback to fix or make better.
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 <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>
#include <WiFiAlert.h>
//******************************************
//define ports
#define Skimmer 1
#define ReturnPump 2
#define FugeLight 3
#define WCPump 4
#define Heater 5
#define Jebao_MP10 6
#define Cooling 7
#define ATOpump 8
//******************************************
////// Place global variable code below here
int avgph[10];
unsigned long totalavgph=0;
byte avgindex=0;
void DrawCustomMain()
{
byte x;
byte y = 2;
char text[7];
// *********** CHANGE TEMP READOUT COLOR DEPENDENT ON FAN AND HEATER STATUS ***********
int TempColor; // Color for drawing temperature
boolean FanOn = ReefAngel.Relay.Status(Cooling); // Get the status of the fan relay
boolean HeatOn = ReefAngel.Relay.Status(Heater); // Get the status of the heater relay
if (HeatOn)
{
TempColor = COLOR_NAVY; // Blue text, too cold, heater is on
}
if (FanOn)
{
TempColor = COLOR_RED; // Red text, too warm, fan is on
}
if (!HeatOn && !FanOn)
{
TempColor = COLOR_GREEN; // Green text, no fan or heater on
}
// ***********************************************************************************
ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 30, 2, "Hedrick's REEF"); // Put a banner at the top
ReefAngel.LCD.DrawDate(6, 119); // Put the date and time at the bottom
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 132, 11); // Draw a black line under the banner
x = 6;
y += MENU_START_ROW*1.0; // MENU_START_ROW is 10, according to globals.h, so y=2+10+1=13
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+1, "Tank Temp PH");
x = 6;
y = MENU_START_ROW*0.8;
ConvertNumToString(text, ReefAngel.Params.PH, 100); // Get pH reading and convert
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+73, y+12, text, Font8x16); // Put pH on the screen
ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10); // Get T1 temp and convert
x = 6;
y += MENU_START_ROW*1.3;
ReefAngel.LCD.DrawHugeNumbers(COLOR_WHITE, TempColor, x, y, text); // Draw the temperature, white numbers on a colored background
x += 6;
y += MENU_START_ROW*1.2;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x-6, y+5, "Stand Temp");
ConvertNumToString(text, ReefAngel.Params.Temp[T2_PROBE], 10); // Get T2 temp and convert
y += MENU_START_ROW*1.2;
x = 6;
ReefAngel.LCD.DrawHugeNumbers(COLOR_WHITE, TempColor, x, y, text); // Draw the temperature, white numbers on a colored background
x += 6;
y += MENU_START_ROW*2.2;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"ATO",Font8x8); // Draw the Float switch status
if (ReefAngel.HighATO.IsActive()) {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
} else {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
x += 6;
y += MENU_START_ROW*1.6;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"Skimmate",Font8x8);
if (!ReefAngel.LowATO.IsActive()) {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
} else {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
byte TempRelay = ReefAngel.Relay.RelayData; // Code for drawing the relay box
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 100, TempRelay);
}
void DrawCustomGraph()
{
}
////// Place global variable code above here
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.Use2014Screen(); // Let's use 2014 Screen
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port8Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port8Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port3Bit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port1Bit | Port3Bit | Port5Bit | 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( 840 );
// Feeding and Water Change mode speed
ReefAngel.DCPump.FeedingSpeed=25;
ReefAngel.DCPump.WaterChangeSpeed=0;
// Ports that are always on/off
ReefAngel.Relay.On( ReturnPump );
ReefAngel.Relay.Off( WCPump );
ReefAngel.Relay.On( Jebao_MP10 );
////// Place additional initialization code below here
randomSeed(now()%SECS_PER_DAY);
//Custom Variable [0] = Month/Season
//Custom Variable [1] = NTM Feeding mode time remaining
//Custom Variable [2] = Storm Mode (24/17)
//Custom Variable [3] = Pump Mode
////// Place additional initialization code above here
}//End Setup
void loop()
{
ReefAngel.StandardLights( FugeLight,21,0,9,0 );
// seasonal temperatures
SeasonalTemps();
// skimmer on with delay
ReefAngel.Relay.DelayedOn(Skimmer);
ReefAngel.DCPump.UseMemory = true; //use Portal Settings
ReefAngel.DCPump.ActinicChannel=Sync; // portal settings
ReefAngel.DCPump.DaylightChannel=AntiSync; // portal setting
////// Place your custom code below here
//Set DCPump (Jebao_MP10)
JebaoMode();
// Monitor for float switch for sump level (too low) and turn off return
CheckFloatSwitches();
//check and set Jebao for Feeding mode and Turn Skimmer Off if ReturnPump off
SkimmerOffMode(Skimmer);
//ATO High Switch
ReefAngel.SingleATOHigh(ATOpump);
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "hedrickms", "xxxxxx" );
ReefAngel.ShowInterface();
} //End Loop
void CheckFloatSwitches()
{
static boolean atocheck = false;
static boolean atoalertsent = false;
static boolean switchalertsent = false;
static boolean skimmeralertsent = false;
static unsigned long atotimeout;
static unsigned long timeout;
static WiFiAlert atoAlert, switchAlert, skimmerAlert;
if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE)
return;
else
{
//if ato is on set flag and timer
if (ReefAngel.HighATO.IsActive() && ReefAngel.Relay.Status(ATOpump))
{
atocheck = true;
atotimeout = now();
}
if (bitRead(ReefAngel.AlertFlags,ATOTimeOutFlag))
{
if (!atoalertsent)
{
atoAlert.Send("ATO+pump+disabled.");
atoalertsent = true;
}
ReefAngel.Relay.Override(ATOpump,0);
// Turn off return if sump level is low
if (ReefAngel.HighATO.IsActive() && now()-timeout>3600) //if the ato float is active for 1 hours turn off return skimmer and ato
{
if (!switchalertsent)
{
switchAlert.Send("Sump+level_low+alarm!+Return+pump+Skimmer+disabled.");
ReefAngel.Relay.Override(ReturnPump,0);
switchalertsent = true;
}
}
else switchalertsent = false; //reset switchalert flag so alert can be sent if needed
}
else
{
timeout = now();
atoalertsent = false; //reset atoalert flag so alert can be sent if needed
//run ato for additional 10secs after ato triggered to avoid float bouncing activation
if (atocheck && now()-atotimeout<10)
{
ReefAngel.Relay.On(ATOpump);
}
else
{
atocheck = false;
ReefAngel.Relay.Off(ATOpump);
}
}
// turn off skimmer if skimmate overflows
if (!ReefAngel.LowATO.IsActive()) //active by default
{
if (!skimmeralertsent)
{
skimmerAlert.Send("Skimmer+Overflow+alarm!+Skimmer+disabled.");
skimmeralertsent = true;
}
ReefAngel.Relay.Override(Skimmer,0);
}
else skimmeralertsent = false; //reset skimmer flag so alert can be sent if needed
}
}// End CheckFloatSwitches
void SkimmerOffMode (byte relayport)
{
// Turn off relay if Return pump has been shut off
if (!ReefAngel.Relay.Status(ReturnPump) && ReefAngel.Relay.Status(relayport))
{
ReefAngel.Relay.Override(relayport,0);
}
} //End SkimmerOffModes
void JebaoMode()
{
static boolean feedingalert = false;
static boolean pumpstormalert = false;
static unsigned long isfeeding;
static byte ranMode = random (30);
static byte ranPumpMode = ranMode%9;
static WiFiAlert pumpAlert;
if (ReefAngel.DCPump.Mode == Custom)
{
if (ReefAngel.DisplayedMenu==FEEDING_MODE)
{
isfeeding = now();
feedingalert = false; //reset feeding flag so alert can be sent if needed
}
if (now()-isfeeding<3600) // Continue for the 60 minutes
{
if (!feedingalert)
{
pumpAlert.Send("Pump+Mode+NTM+Feeding!+Activated.");
feedingalert = true;
}
ReefAngel.PWM.SetDaylight( NutrientTransportMode(30,70,140,true));
ReefAngel.PWM.SetActinic( NutrientTransportMode(30,70,140,false));
ReefAngel.CustomVar[1]=((now()-isfeeding)/60);
}
else
{
if (ReefAngel.CustomVar[1]>0) ReefAngel.CustomVar[1]=0;//reset feed timer portal variable if was in feeding mode;
if ((now()%SECS_PER_DAY >= 75600) || (now()%SECS_PER_DAY < 28800)) // night mode 9pm -> 8am
{
if (now()%SECS_PER_DAY == 75600) pumpstormalert = false; //reset pump flag at 9pm so alert can be sent if needed
if (!pumpstormalert)
{
pumpAlert.Send("Pump+Mode+Sine+NightMode!+Activated.");
pumpstormalert = true;
}
ReefAngel.PWM.SetDaylight(SineMode(25,35,20, true));
ReefAngel.PWM.SetActinic(SineMode(25,35,20, false));
ReefAngel.CustomVar[3]=99;//night mode = 99
}// end night mode 8am
else // choose a random pump mode (ReefCrest, TidalSwell, ShortPulse, LongPulse, Else) between 0 and 8
{
if (now()%SECS_PER_DAY == 28800)//at 8am set pump mode and determine if storm
{
pumpstormalert = false; //reset pump flag so alert can be sent if needed
//set random pump mode at start of each day (midnight) and determine if storm will occur
ranMode = random(30);
ranPumpMode = ranMode%9;
}
else if (ranMode==27 || ranMode==11)//storm mode only after 8am
{
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+STORM!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
StormMode();
ReefAngel.CustomVar[2]=ranMode;
ReefAngel.CustomVar[3]=0;
}
else
{
switch (ranPumpMode)
{
case 0:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+ShortPule+case0!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetActinic( ShortPulseMode(10,70,600,true) );
ReefAngel.PWM.SetDaylight( ShortPulseMode(10,70,600,false) );
break;
case 1:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+ReefCrest+case1!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight( ReefCrestMode(50,20,true) );
ReefAngel.PWM.SetActinic( ReefCrestMode(50,20,false) );
break;
case 2:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+TideMode+case2!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetActinic( ShortPulseMode(0,75,800,true) );
ReefAngel.PWM.SetDaylight( ShortPulseMode(0,75,800,false) );
break;
case 3:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+ElseMode+case3!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight( ElseMode(55,25,true) );
ReefAngel.PWM.SetActinic( ElseMode(55,25,false) );
break;
case 4:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+TideMode+case4!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight( ReefCrestMode(55,25,true) );
ReefAngel.PWM.SetActinic( ReefCrestMode(55,25,false) );
break;
case 5:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+LongPulse+case5!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight( LongPulseMode(20,70,10,true) );
ReefAngel.PWM.SetActinic( LongPulseMode(20,70,10,false) );
break;
case 6:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+ElseMode+case6!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight( ElseMode(50,15,true) );
ReefAngel.PWM.SetActinic( ElseMode(50,15,false) );
break;
case 7:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+SineWave+case7!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetDaylight(SineMode(25,50,20, true));
ReefAngel.PWM.SetActinic(SineMode(25,50,20, false));
break;
case 8:
if (!pumpstormalert)
{
//enter storm mode for pumps (random)
pumpAlert.Send("Pump+Mode+ShortPule+case8!+Activated.");
pumpstormalert = true; //reset pump flag so alert can be sent if needed
}
ReefAngel.PWM.SetActinic( ShortPulseMode(20,65,300,true) );
ReefAngel.PWM.SetDaylight( ShortPulseMode(20,65,300,false) );
break;
}//end of random pump mode
ReefAngel.CustomVar[2]=ranMode;
ReefAngel.CustomVar[3]=ranPumpMode;
}//end else switch
}//end else pump mode
}//end else
} //end of DCPump custom
} // End Void JebaoRandomMode()
void StormMode ()
{
if ((now()%SECS_PER_DAY >= 28800) && (now()%SECS_PER_DAY < 43200)) // 8am - 12 pm
{
ReefAngel.PWM.SetDaylight( NutrientTransportMode(40,85,140,true));
ReefAngel.PWM.SetActinic( NutrientTransportMode(40,85,140,false));
}
else if ((now()%SECS_PER_DAY >= 43200) && (now()%SECS_PER_DAY < 64800)) // 12pm - 6pm
{
ReefAngel.PWM.SetDaylight( LongPulseMode(30,55,10,true) );
ReefAngel.PWM.SetActinic( LongPulseMode(30,55,10,false) );
}
else if ((now()%SECS_PER_DAY >= 64800) && (now()%SECS_PER_DAY < 72000)) // 6pm - 8pm
{
ReefAngel.PWM.SetDaylight( NutrientTransportMode(35,75,260,true));
ReefAngel.PWM.SetActinic( NutrientTransportMode(35,75,260,false));
}
else if ((now()%SECS_PER_DAY >= 72000) && (now()%SECS_PER_DAY < 75600)) // 8pm - 9pm
{
ReefAngel.PWM.SetDaylight( ElseMode(65,25,true) );
ReefAngel.PWM.SetActinic( ElseMode(65,25,false) );
}
}//end StormMode
void SeasonalTemps ()
{
ReefAngel.CustomVar[0]=month();
static int heatArray[][2] = { {792,796},// default in case of error in month=0 (June)
{770,774},//January (winter)
{774,778},//February (winter)
{775,779},//March (early spring)
{779,783},//April (spring)
{785,789},//May (spring)
{792,796},//June (early summer)
{799,803},//July (summer)
{806,810},//August (summer)
{796,801},//September (early fall)
{787,791},//October (fall)
{780,784},//November (fall)
{775,779} };//December (early winter)
static int coolArray[][2] = { {794,799},// default in case of error in month=0 (June)
{772,776},//January (winter)
{776,780},//February (winter)
{777,781},//March (early spring)
{781,785},//April (spring)
{787,791},//May (spring)
{794,798},//June (early summer)
{801,805},//July (summer)
{808,812},//August (summer)
{798,803},//September (early fall)
{789,793},//October (fall)
{782,786},//November (fall)
{777,781} };//December (early winter)
ReefAngel.StandardHeater( Heater,heatArray[month()][0],heatArray[month()][1]);
ReefAngel.StandardFan(Cooling,coolArray[month()][0],coolArray[month()][1]);
}//end seasonalTemps
byte ElseMode( byte MidPoint, byte Offset, boolean WaveSync )
{
// Static's only initialize the first time they are called
static unsigned long LastChange=millis(); // Set the inital time that the last change occurred
static int Delay = random( 500, 3000); // Set the initial delay
static int NewSpeed = MidPoint; // Set the initial speed
static int AntiSpeed = MidPoint; // Set the initial anti sync speed
if ((millis()-LastChange) > Delay) // Check if the delay has elapsed
{
Delay=random(500,5000); // If so, come up with a new delay
int ChangeUp = random(Offset); // Amount to go up or down
if (random(100)<50) // 50/50 chance of speed going up or going down
{
NewSpeed = MidPoint - ChangeUp;
AntiSpeed = MidPoint + ChangeUp;
}
else
{
NewSpeed = MidPoint + ChangeUp;
AntiSpeed = MidPoint - ChangeUp;
}
LastChange=millis(); // Reset the time of the last change
}
if (WaveSync)
{
return NewSpeed;
}
else
{
return AntiSpeed;
}
}//end Elsemode()