I have 4 powerheads facing each other on the two sides, back left, back right, front left, front right. I set up a custom variable for the sync mode that lets me switch if they alternate side2side, front2back, diagonally (to do gyre flow), or all on sync.
The big number to the right of the pumps on top is the current pump threshold set. Mine will normally be 30 since I have Tunze powerheads.
The black speck is a piece of dirt that somehow got underneath the sticker on the front.
I have 6 LED channels, violet, royal blue, neutral white, cool blue, deep red, cyan. The current percents are shown on the display.
Update 9/4/2014: Added code to turn on alk doser when I shove a number into a custom variable in ppm carbonate as read from hanna checker. I aim for 150ppm or 3.0meq/l alkalinity, so this will run the doser a calculated amount to reach that number and then clear out the custom variable and shut it off. I would like to do the negative one which would override the doser port to off for some amount of normal dosing time to lower it, but haven't done that math yet.
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>
////// Place global variable code below here
unsigned long offtime = 0UL;
unsigned long alktime = 0UL;
void DrawCustomMain()
{
// want to add dosing status, and 6 PWM channels of lights
byte pumpmode = InternalMemory.DCPumpMode_read();
byte threshold = InternalMemory.DCPumpThreshold_read();
byte syncmode = ReefAngel.CustomVar[0];
if (pumpmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5," Constant ");
else if (pumpmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5," Lagoon ");
else if (pumpmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5,"Reef Crest ");
else if (pumpmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5,"Short Pulse");
else if (pumpmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5,"Long Pulse ");
else if (pumpmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5,"Nutr Trans ");
else if (pumpmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5,"Tidal Swell");
else if (pumpmode == 12) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5," Else ");
else if (pumpmode == 13) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5," Sine ");
else if (pumpmode == 14) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,10,5," Gyre ");
if (syncmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,10,15,"Side2Side ");
else if (syncmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,10,15," AllSync ");
else if (syncmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,10,15,"Front2Back");
else if (syncmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,10,15," Gyre ");
else ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,10,15," Unknown ");
// draw threshold number
char text[3];
ConvertNumToString(text,threshold,1);
ReefAngel.LCD.Clear(DefaultBGColor, 100,8, 126, 24);
ReefAngel.LCD.DrawHugeText(COLOR_RED, DefaultBGColor, 100,8,text,Num12x16);
DrawPumpBlock(15,25);
DrawLEDBlock(9,45);
DrawCustomMonitor(15,97);
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 107, TempRelay);
ReefAngel.LCD.DrawDate(6,122);
}
void DrawLEDBlock(byte x, byte y) {
ReefAngel.LCD.Clear(COLOR_PURPLE, x, y, x+19, y+9);
ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_PURPLE,x+1,y+1,ReefAngel.PWM.Get16ChannelValue(2));
ReefAngel.LCD.Clear(COLOR_NAVY, x+19, y, x+38, y+9);
ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_NAVY,x+20,y+1,ReefAngel.PWM.Get16ChannelValue(3));
ReefAngel.LCD.Clear(COLOR_ANTIQUEWHITE, x+38, y, x+57, y+9);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_ANTIQUEWHITE,x+39,y+1,ReefAngel.PWM.Get16ChannelValue(4));
ReefAngel.LCD.Clear(COLOR_BLUE, x+57, y, x+76, y+9);
ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_BLUE,x+58,y+1,ReefAngel.PWM.Get16ChannelValue(5));
ReefAngel.LCD.Clear(COLOR_CRIMSON, x+76, y, x+95, y+9);
ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_CRIMSON,x+77,y+1,ReefAngel.PWM.Get16ChannelValue(6));
ReefAngel.LCD.Clear(COLOR_DARKTURQUOISE, x+95, y, x+117, y+9);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_DARKTURQUOISE,x+96,y+1,ReefAngel.PWM.Get16ChannelValue(7));
}
void DrawPumpBlock(byte x, byte y) {
//left pumps
ReefAngel.LCD.DrawText(DPColor,DefaultBGColor,x,y,"BL:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetActinicValue(), DPColor, x+18, y,1);
ReefAngel.LCD.DrawText(APColor,DefaultBGColor,x,y+10,"FL:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.Get16ChannelValue(0), APColor, x+18, y+10,1);
// right pumps
ReefAngel.LCD.DrawText(DPColor,DefaultBGColor,x+60,y,"BR:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+78, y,1);
ReefAngel.LCD.DrawText(APColor,DefaultBGColor,x+60,y+10,"FR:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.Get16ChannelValue(1), APColor, x+78, y+10,1);
}
void DrawCustomMonitor(byte x, byte y) {
// left column
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x,y,"T1:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], T1TempColor, x+18, y,10);
// right column
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60,y,"PH:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, PHColor, x+78, y,100);
}
void DrawCustomGraph()
{
ReefAngel.LCD.DrawGraph(5,55);
}
////// 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 = Port6Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = 0;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port3Bit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port1Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Add support for 16 channel PWM board
ReefAngel.Add16ChPWM();
// Set the Overheat temperature setting
InternalMemory.OverheatTemp_write( 830 );
// Feeeding and Water Change mode speed
ReefAngel.DCPump.FeedingSpeed=0;
ReefAngel.DCPump.WaterChangeSpeed=0;
// Ports that are always on
ReefAngel.Relay.On( Port5 );
ReefAngel.Relay.On( Port6 );
////// Place additional initialization code below here
//Custom Port Labels
ReefAngel.CustomLabels[0]="Heater";
ReefAngel.CustomLabels[1]="Skimmer";
ReefAngel.CustomLabels[2]="Unused";
ReefAngel.CustomLabels[3]="Unused";
ReefAngel.CustomLabels[4]="Return";
ReefAngel.CustomLabels[5]="PowerHeads";
ReefAngel.CustomLabels[6]="Alkdoser";
ReefAngel.CustomLabels[7]="Caldoser";
// so portal always shows custom variables
ReefAngel.CustomVar[7]=255;
////// Place additional initialization code above here
}
/*
Want to use one float valve port for skimmate locker
Relay Ports:
1 = Heater
2 = Skimmer
3 = Unused
4 = Unused
5 = Reeflo Return
6 = Powerheads, 2xTunze 2xJebao
7 = Alkdoser
8 = CalDoser
DC Pumps:
Actinic: Tunze Left
Daylight: Tunze Right
CustomDimmer: // Want lights with sinusoidal ramp up and down with center at 1pm starting and ending at 7am/7pm
// want Jebaos to operate in conjunction with Tunzes some of the time and opposite some of the time, so sometimes it will be
// pushing water at each other, sometimes it will be turning a gyre back and forth.
0 = Jebao Left
1 = Jebao Right
2 = Violet
3 = Royal Blue
4 = White
5 = Cool Blue
6 = Deep Red
7 = Cyan
8 = PWM fans
*/
void loop()
{
//ReefAngel.PWM.SIXTEENChExpansionSetPercent(0); // initialize stuff to 0 at the beginning of the loop
ReefAngel.StandardHeater( Port1 );
ReefAngel.Relay.DelayedOn( Port2 ); // skimmer delay
if (ReefAngel.Params.PH < 850) {
ReefAngel.DosingPumpRepeat1( Port7 ); // alk doser
adjustAlk();
} else {
ReefAngel.Relay.Off(Port7); // turn it off if the pH is too high
}
ReefAngel.DosingPumpRepeat2( Port8 ); // cal doser
ReefAngel.DCPump.UseMemory = true;
////// Place your custom code below here
// pumps
setPowerheadModes(ReefAngel.CustomVar[0]);
if (ReefAngel.CustomVar[1]==0) { // then we should use these settings. If it's 0 we should do what the portal says. Set customvar[1] to 0 to not change like this
// and use a constant profile as set by the static internal memory instead
if ((hour() >= 13) && (hour() <= 16)) { // reef crest from 1 to 5
InternalMemory.DCPumpMode_write(2);
InternalMemory.DCPumpSpeed_write(50);
InternalMemory.DCPumpDuration_write(120);
} else if ((hour() >= 17) && (hour() <= 19)) { // nutrient transport from 5 to 8
InternalMemory.DCPumpMode_write(5);
InternalMemory.DCPumpSpeed_write(70);
InternalMemory.DCPumpDuration_write(120);
} else if ((hour() >= 8) && (hour() <= 12)) { // gyre from 8 to 1
InternalMemory.DCPumpMode_write(14);
InternalMemory.DCPumpSpeed_write(70);
InternalMemory.DCPumpDuration_write(4);
} else {
InternalMemory.DCPumpMode_write(0); // Constant the rest of the time
InternalMemory.DCPumpSpeed_write(30);
InternalMemory.DCPumpDuration_write(120);
}
} else { // we have fixed it to some number and set the custom variable to make it stick first.
}
// lights
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(2,0,InternalMemory.PWMSlopeEnd0_read()); // Violet channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(3,0,InternalMemory.PWMSlopeEnd1_read()); // Royal Blue channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(4,0,InternalMemory.PWMSlopeEnd2_read()); // White channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(5,0,InternalMemory.PWMSlopeEnd3_read(),-30,-30); // Cool Blue channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(6,0,InternalMemory.PWMSlopeEnd4_read(),-60,-60); // Deep Red channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(7,0,InternalMemory.PWMSlopeEnd5_read(),-60,-60); // Cyan channel, startpwm, endpwm, offsets
ReefAngel.PWM.SIXTEENChannelPWMSigmoid(8,0,75); // Fan channel, silent up to 75 or so.
// Turn off Skimmer if Locker is full
if(ReefAngel.HighATO.IsActive())
{
ReefAngel.Relay.On(Port2);
}
else
{
ReefAngel.Relay.Off(Port2);
}
// Turn off things if the power goes out
if(ReefAngel.LowATO.IsActive())
{ // then power is on
if (offtime > 0) { // then power was off
ReefAngel.Relay.On(Port2);
ReefAngel.Relay.On(Port5);
offtime = 0UL;
}
}
else
{ // then power is off
// turn off lights, skimmer, return pump after two minutes, to conserve power for powerheads
if (offtime == 0) offtime = now();
if ((now() - offtime) > 120) { // it has been off for two minutes
ReefAngel.Relay.Off(Port2); // turn off skimmer
ReefAngel.Relay.Off(Port5); // turn off return pump
}
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "AlanM" );
ReefAngel.ShowInterface();
}
void setPowerheadModes(byte syncModeIn)
{
switch (syncModeIn) {
case 0: // side to side anti-sync
ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze Left
ReefAngel.DCPump.DaylightChannel = Sync; // Tunze Right
ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = AntiSync; // Jebao Left
ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = Sync; // Jebao Right
break;
case 1: // all on sync
ReefAngel.DCPump.ActinicChannel = Sync; // Tunze Left
ReefAngel.DCPump.DaylightChannel = Sync; // Tunze Right
ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = Sync; // Jebao Left
ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = Sync; // Jebao Right
break;
case 2: // front/back sync
ReefAngel.DCPump.ActinicChannel = Sync; // Tunze Left
ReefAngel.DCPump.DaylightChannel = Sync; // Tunze Right
ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = AntiSync; // Jebao Left
ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = AntiSync; // Jebao Right
break;
default: // gyre flow, diagonal sync
ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze Left
ReefAngel.DCPump.DaylightChannel = Sync; // Tunze Right
ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = Sync; // Jebao Left
ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = AntiSync; // Jebao Right
break;
}
}
void adjustAlk() {
// check to see if extra alk was requested
// CustomVar is the value read on the checker
if (ReefAngel.CustomVar[2] > 0) {
int alkneeded = 150 - ReefAngel.CustomVar[2];
float onTime = 0;
if (alkneeded > 0) {
onTime = 3.64*(float)alkneeded; // onTime is minutes to run, for 110 gallons volume, 1ppm is 4.4ml and 1.21ml/min, so 3.64 minutes per ppm of alk.
} else {
ReefAngel.CustomVar[2] = 0; // else it must be either negative or at 150, so reset and do nothing.
return;
}
if (alktime == 0) { // it must have just gotten noticed
alktime = now()+(onTime*60UL); // should get rounded to integer seconds here
ReefAngel.Relay.On(Port7); // set it to on
} else if (now() > alktime) {
//ReefAngel.Relay.Override(Port7,2); // set it back to auto, clear the override
ReefAngel.Relay.Off(Port7); // turn it off
ReefAngel.CustomVar[2] = 0; // clear the custom variable
alktime = 0; // set alktime back to 0
} else { // must be less than alktime, so keep it on
ReefAngel.Relay.On(Port7);
}
} else {
return;
}
}