How do I code delayed start from feed mode, i.e. for skimmer
How do I code delayed start from feed mode, i.e. for skimmer
I know I brought this up before, but I figured I could collaborate here.
I'm not sure if many of you are in this same situation, but when I go into feeding mode or water change mode, my pumps turn off and my skimmer turns off. At that point the water rises in my sump (and in my skimmer area). If everything turned back on at the same time when the mode is complete, I'd have quite a bit of wet skimming going on and my collection cup would fill up pretty quickly. I'd like to code in a 15 min delay. If this could also be added in with the RAGen, I'm sure some folks would find it helpful (here's looking at you Curt ).
I'm not sure if many of you are in this same situation, but when I go into feeding mode or water change mode, my pumps turn off and my skimmer turns off. At that point the water rises in my sump (and in my skimmer area). If everything turned back on at the same time when the mode is complete, I'd have quite a bit of wet skimming going on and my collection cup would fill up pretty quickly. I'd like to code in a 15 min delay. If this could also be added in with the RAGen, I'm sure some folks would find it helpful (here's looking at you Curt ).
Re: How do I code delayed start from feed mode, i.e. for ski
Use a timer to trigger the relay you use for the skimmer.
Attached code is an example.
This example code actually implements the delay on startup and on feeding mode.
Attached code is an example.
This example code actually implements the delay on startup and on feeding mode.
- Attachments
-
- SkimmerDelay.pde
- (1.2 KiB) Downloaded 506 times
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
I understand the code you posted Roberto, but I don't understand how to implement it with the RA Gen code.
or if you dare, here's the whole first 1/2 of the code (stopped at the setup menu screens function):
Code: Select all
case MainMenu_FeedingMode:
{
// turn off ports
#ifdef SaveRelayState
// TODO Test SaveRelayState
byte CurrentRelayState = Relay.RelayData;
#endif // SaveRelayState
Relay.RelayMaskOff = ~FeedingModePorts;
#ifdef RelayExp
byte i;
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = ~FeedingModePortsE[i];
}
#endif // RelayExp
Relay.Write();
// run feeding mode
FeedingMode();
// turn on ports
Relay.RelayMaskOff = B11111111;
#ifdef RelayExp
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = B11111111;
}
#endif // RelayExp
// restore ports
#ifdef SaveRelayState
Relay.RelayData = CurrentRelayState;
#endif // SaveRelayState
Relay.Write();
break;
}
Code: Select all
/*
* Copyright 2010 Reef Angel / Roberto Imai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ReefAngel_Globals.h>
#include <Wire.h>
#include <DS1307RTC.h>
#include "ReefAngel.h"
#include <ReefAngel_Wifi.h>
byte ButtonPress = 0;
SIGNAL(PCINT0_vect) {
if (millis()-ButtonDebounce>600)
{
ButtonDebounce=millis();
ButtonPress++;
}
}
// NOTE for nested menus
// NOTE Menu labels can be a max of 20 characters
// Associate a menu name to a numeric value
// the total number must match the max number of menus
enum Menus {
MainMenu,
SetupMenu,
#ifndef RemoveAllLights
LightsMenu,
#endif // RemoveAllLights
TempsMenu,
#if defined SetupExtras || defined ATOSetup
TimeoutsMenu
#endif // if defined SetupExtras || defined ATOSetup
};
// Main Menu
prog_char mainmenu_0_label[] PROGMEM = "Feeding";
prog_char mainmenu_1_label[] PROGMEM = "Water Change";
#ifndef RemoveAllLights
prog_char mainmenu_2_label[] PROGMEM = "Lights ->";
#endif // RemoveAllLights
prog_char mainmenu_3_label[] PROGMEM = "Temps ->";
#if defined SetupExtras || defined ATOSetup
prog_char mainmenu_4_label[] PROGMEM = "Timeouts ->";
#endif // if defined SetupExtras || defined ATOSetup
prog_char mainmenu_5_label[] PROGMEM = "Setup ->";
#ifdef VersionMenu
prog_char mainmenu_6_label[] PROGMEM = "Version";
#endif // VersionMenu
PROGMEM const char *mainmenu_items[] = {
mainmenu_0_label,
mainmenu_1_label,
#ifndef RemoveAllLights
mainmenu_2_label,
#endif // RemoveAllLights
mainmenu_3_label,
#if defined SetupExtras || defined ATOSetup
mainmenu_4_label,
#endif // if defined SetupExtras || defined ATOSetup
mainmenu_5_label,
#ifdef VersionMenu
mainmenu_6_label
#endif // VersionMenu
};
enum MainMenuItem {
MainMenu_FeedingMode,
MainMenu_WaterChangeMode,
#ifndef RemoveAllLights
MainMenu_Lights,
#endif // RemoveAllLights
MainMenu_Temps,
#if defined SetupExtras || defined ATOSetup
MainMenu_Timeouts,
#endif // if defined SetupExtras || defined ATOSetup
MainMenu_Setup,
#ifdef VersionMenu
MainMenu_Version
#endif // VersionMenu
};
// Setup MenuWavemakerSetup
#ifdef WavemakerSetup
prog_char setupmenu_0_label[] PROGMEM = "Wavemaker";
#endif // WavemakerSetup
#ifdef DosingPumpSetup
prog_char setupmenu_1_label[] PROGMEM = "Single Dose";
#endif // DosingPumpSetup
#ifdef DosingPumpIntervalSetup
prog_char setupmenu_2_label[] PROGMEM = "Multi Dose";
#endif // DosingPumpIntervalSetup
prog_char setupmenu_3_label[] PROGMEM = "Calibrate pH";
#ifdef DateTimeSetup
prog_char setupmenu_4_label[] PROGMEM = "Date / Time";
#endif // DateTimeSetup
PROGMEM const char *setupmenu_items[] = {
#ifdef WavemakerSetup
setupmenu_0_label,
#endif // WavemakerSetup
#ifdef DosingPumpSetup
setupmenu_1_label,
#endif // DosingPumpSetup
#ifdef DosingPumpIntervalSetup
setupmenu_2_label,
#endif // DosingPumpIntervalSetup
setupmenu_3_label,
#ifdef DateTimeSetup
setupmenu_4_label
#endif // DateTimeSetup
};
enum SetupMenuItem {
#ifdef WavemakerSetup
SetupMenu_Wavemaker,
#endif // WavemakerSetup
#ifdef DosingPumpSetup
SetupMenu_DosingPump,
#endif // DosingPumpSetup
#ifdef DosingPumpIntervalSetup
SetupMenu_DosingPumpInterval,
#endif // DosingPumpIntervalSetup
SetupMenu_CalibratePH,
#ifdef DateTimeSetup
SetupMenu_DateTime
#endif // DateTimeSetup
};
#ifndef RemoveAllLights
// Lights Menu
prog_char lightsmenu_0_label[] PROGMEM = "Lights On";
prog_char lightsmenu_1_label[] PROGMEM = "Lights Off";
#ifdef MetalHalideSetup
prog_char lightsmenu_2_label[] PROGMEM = "Metal Halides";
prog_char lightsmenu_3_label[] PROGMEM = "MH On Delay";
#endif // MetalHalideSetup
#ifdef StandardLightSetup
prog_char lightsmenu_4_label[] PROGMEM = "Standard Lights";
#endif // StandardLightSetup
#ifdef DisplayLEDPWM
prog_char lightsmenu_5_label[] PROGMEM = "LED PWM";
#endif // DisplayLEDPWM
PROGMEM const char *lightsmenu_items[] = {
lightsmenu_0_label, lightsmenu_1_label,
#ifdef MetalHalideSetup
lightsmenu_2_label,
lightsmenu_3_label,
#endif // MetalHalideSetup
#ifdef StandardLightSetup
lightsmenu_4_label,
#endif // StandardLightSetup
#ifdef DisplayLEDPWM
lightsmenu_5_label
#endif // DisplayLEDPWM
};
enum LightsMenuItem {
LightsMenu_On,
LightsMenu_Off,
#ifdef MetalHalideSetup
LightsMenu_MetalHalides,
LightsMenu_MetalHalideDelay,
#endif // MetalHalideSetup
#ifdef StandardLightSetup
LightsMenu_StandardLights,
#endif // StandardLightSetup
#ifdef DisplayLEDPWM
LightsMenu_LEDPWM
#endif // DisplayLEDPWM
};
#endif // RemoveAllLights
// Temps Menu
prog_char tempsmenu_0_label[] PROGMEM = "Heater";
prog_char tempsmenu_1_label[] PROGMEM = "Chiller";
#ifdef OverheatSetup
prog_char tempsmenu_2_label[] PROGMEM = "Overheat Set";
#endif // OverheatSetup
prog_char tempsmenu_3_label[] PROGMEM = "Overheat Clear";
PROGMEM const char *tempsmenu_items[] = {
tempsmenu_0_label,
tempsmenu_1_label,
#ifdef OverheatSetup
tempsmenu_2_label,
#endif // OverheatSetup
tempsmenu_3_label};
enum TempsMenuItem {
TempsMenu_Heater,
TempsMenu_Chiller,
#ifdef OverheatSetup
TempsMenu_Overheat,
#endif // OverheatSetup
TempsMenu_OverheatClr
};
// Timeouts Menu
#if defined SetupExtras || defined ATOSetup
#ifdef ATOSetup
prog_char timeoutsmenu_0_label[] PROGMEM = "ATO Set";
#ifdef SingleATOSetup
prog_char timeoutsmenu_1_label[] PROGMEM = "ATO Interval";
#endif // SingleATOSetup
prog_char timeoutsmenu_2_label[] PROGMEM = "ATO Clear";
#endif // ATOSetup
#ifdef SetupExtras
prog_char timeoutsmenu_3_label[] PROGMEM = "Feeding";
prog_char timeoutsmenu_4_label[] PROGMEM = "LCD";
#endif // SetupExtras
PROGMEM const char *timeoutsmenu_items[] = {
#ifdef ATOSetup
timeoutsmenu_0_label,
#ifdef SingleATOSetup
timeoutsmenu_1_label,
#endif // SingleATOSetup
timeoutsmenu_2_label,
#endif // ATOSetup
#ifdef SetupExtras
timeoutsmenu_3_label,
timeoutsmenu_4_label
#endif // SetupExtras
};
enum TimeoutsMenuItem {
#ifdef ATOSetup
TimeoutsMenu_ATOSet,
#ifdef SingleATOSetup
TimeoutsMenu_ATOHrInterval,
#endif // SingleATOSetup
TimeoutsMenu_ATOClear,
#endif // ATOSetup
#ifdef SetupExtras
TimeoutsMenu_Feeding,
TimeoutsMenu_LCD
#endif // SetupExtras
};
#endif // if defined SetupExtras || defined ATOSetup
ReefAngelClass::ReefAngelClass()
{
#if defined(__AVR_ATmega2560__)
PCMSK0 |= 128;
#else // __AVR_ATmega2560__
PCMSK0 |= 32;
#endif // __AVR_ATmega2560__
PCICR |= 1;
}
void ReefAngelClass::Init()
{
Wire.begin();
Serial.begin(57600);
pinMode(Piezo, OUTPUT);
digitalWrite(lowATOPin,HIGH); //pull up resistor on lowATOPin
digitalWrite(highATOPin,HIGH); //pull up resistor on highATOPin
LCD.Init();
Joystick.Init();
TempSensor.Init();
setSyncProvider(RTC.get); // the function to get the time from the RTC
setSyncInterval(SECS_PER_HOUR); // Changed to sync every hour.
RAStart=now();
LCD.BacklightOn();
Relay.AllOff();
/*
TODO Check this code, is it needed?
PHMin=EEPROM.read(PH_Min)*256 + EEPROM.read(PH_Min+1);
PHMax=EEPROM.read(PH_Max)*256 + EEPROM.read(PH_Max+1);
*/
PHMin = InternalMemory.PHMin_read();
PHMax = InternalMemory.PHMax_read();
taddr = InternalMemory.T1Pointer_read();
if ((taddr>120) || (taddr<0))
{
InternalMemory.T1Pointer_write(0);
taddr = 0;
}
#ifdef SetupExtras
Timer[0].SetInterval(InternalMemory.FeedingTimer_read()); // Default Feeding timer
Timer[3].SetInterval(InternalMemory.LCDTimer_read()); // LCD Sleep Mode timer
#else
Timer[0].SetInterval(900); // Default Feeding timer
Timer[3].SetInterval(600); // LCD Sleep Mode timer
#endif // SetupExtras
Timer[3].Start(); // start timer
Timer[5].SetInterval(720); // Store Params
Timer[5].ForceTrigger();
#ifdef DisplayLEDPWM
// Restore PWM values
PWM.SetActinic(InternalMemory.LEDPWMActinic_read());
PWM.SetDaylight(InternalMemory.LEDPWMDaylight_read());
#endif // DisplayLEDPWM
// Set the default ports to be turned on & off during the 2 modes
// To enable a port to be toggled, place a 1 in the appropriate position
// Default to have ports 4, 5, & 8 toggled
// Override in Setup function of PDE
// Port 87654321
FeedingModePorts = B10011000;
WaterChangePorts = B10011000;
// Set the ports that get shutoff when the overheat value is reached
// Default to have port 3 shutoff
// Port 87654321
OverheatShutoffPorts = B00000100;
#ifdef RelayExp
// Expansion Module ports to toggle, defaults to not toggle any ports
for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
FeedingModePortsE[i] = 0;
WaterChangePortsE[i] = 0;
OverheatShutoffPortsE[i] = 0;
#ifndef RemoveAllLights
LightsOnPortsE[i] = 0;
#endif // RemoveAllLights
}
#endif // RelayExp
#ifndef RemoveAllLights
// Set the ports that get turned on when you select the Lights On
// Default to have ports 2 & 3 turn on
// Port 87654321
LightsOnPorts = B00000110;
#endif // RemoveAllLights
// Initialize the Nested Menus
InitMenus();
}
void ReefAngelClass::Refresh()
{
pingSerial();
if (ds.read_bit()==0) return; // ds for OneWire TempSensor
now();
#ifdef DirectTempSensor
LCD.Clear(DefaultBGColor,0,0,1,1);
Params.Temp1=TempSensor.ReadTemperature(TempSensor.addrT1);
LCD.Clear(DefaultBGColor,0,0,1,1);
Params.Temp2=TempSensor.ReadTemperature(TempSensor.addrT2);
LCD.Clear(DefaultBGColor,0,0,1,1);
Params.Temp3=TempSensor.ReadTemperature(TempSensor.addrT3);
LCD.Clear(DefaultBGColor,0,0,1,1);
Params.PH=analogRead(PHPin);
Params.PH=map(Params.PH, PHMin, PHMax, 700, 1000); // apply the calibration to the sensor reading
LCD.Clear(DefaultBGColor,0,0,1,1);
TempSensor.RequestConvertion();
LCD.Clear(DefaultBGColor,0,0,1,1);
#else // DirectTempSensor
int x = TempSensor.ReadTemperature(TempSensor.addrT1);
LCD.Clear(DefaultBGColor,0,0,1,1);
int y;
y = x - Params.Temp1;
// check to make sure the temp readings aren't beyond max allowed
#ifdef DEV_MODE
Serial.print("T1: ");
Serial.print(x);
Serial.print("(");
Serial.print(y);
Serial.print(")");
#endif // DEV_MODE
if ( abs(y) < MAX_TEMP_SWING || Params.Temp1 == 0) Params.Temp1 = x;
x = TempSensor.ReadTemperature(TempSensor.addrT2);
LCD.Clear(DefaultBGColor,0,0,1,1);
y = x - Params.Temp2;
#ifdef DEV_MODE
Serial.print(", T2: ");
Serial.print(x);
Serial.print("(");
Serial.print(y);
Serial.print(")");
#endif // DEV_MODE
if ( abs(y) < MAX_TEMP_SWING || Params.Temp2 == 0) Params.Temp2 = x;
x = TempSensor.ReadTemperature(TempSensor.addrT3);
LCD.Clear(DefaultBGColor,0,0,1,1);
y = x - Params.Temp3;
#ifdef DEV_MODE
Serial.print(", T3: ");
Serial.print(x);
Serial.print("(");
Serial.print(y);
Serial.println(")");
#endif // DEV_MODE
if ( abs(y) < MAX_TEMP_SWING || Params.Temp3 == 0) Params.Temp3 = x;
Params.PH=analogRead(PHPin);
LCD.Clear(DefaultBGColor,0,0,1,1);
Params.PH=map(Params.PH, PHMin, PHMax, 700, 1000); // apply the calibration to the sensor reading
TempSensor.RequestConvertion();
#endif // DirectTempSensor
}
void ReefAngelClass::SetTemperatureUnit(byte unit)
{
// 0 (or DEGREE_F) for farenheit
// 1 (or DEGREE_C) for celcius
TempSensor.unit = unit;
}
void ReefAngelClass::StandardLights(byte LightsRelay, byte OnHour, byte OnMinute, byte OffHour, byte OffMinute)
{
if (NumMins(OffHour,OffMinute) > NumMins(OnHour,OnMinute))
{
if (NumMins(hour(),minute()) >= NumMins(OnHour,OnMinute)) Relay.On(LightsRelay); else Relay.Off(LightsRelay);
if (NumMins(hour(),minute()) >= NumMins(OffHour,OffMinute)) Relay.Off(LightsRelay);
}
else
{
if (NumMins(hour(),minute()) >= NumMins(OffHour,OffMinute)) Relay.Off(LightsRelay); else Relay.On(LightsRelay);
if (NumMins(hour(),minute()) >= NumMins(OnHour,OnMinute)) Relay.On(LightsRelay);
}
}
void ReefAngelClass::MHLights(byte LightsRelay, byte OnHour, byte OnMinute, byte OffHour, byte OffMinute, byte MHDelay)
{
unsigned int MHTimer = MHDelay;
MHTimer *= SECS_PER_MIN;
if ( now()-RAStart > MHTimer )
StandardLights(LightsRelay, OnHour, OnMinute, OffHour, OffMinute);
}
void ReefAngelClass::StandardHeater(byte HeaterRelay, int LowTemp, int HighTemp)
{
if (Params.Temp1 == 0) return; // Don't turn the heater on if the temp is reading 0
if (Params.Temp1 <= LowTemp && Params.Temp1 > 0) Relay.On(HeaterRelay); // If sensor 1 temperature <= LowTemp - turn on heater
if (Params.Temp1 >= HighTemp) Relay.Off(HeaterRelay); // If sensor 1 temperature >= HighTemp - turn off heater
}
void ReefAngelClass::StandardFan(byte FanRelay, int LowTemp, int HighTemp)
{
if (Params.Temp1 >= HighTemp) Relay.On(FanRelay); // If sensor 1 temperature >= HighTemp - turn on fan
if (Params.Temp1 <= LowTemp) Relay.Off(FanRelay); // If sensor 1 temperature <= LowTemp - turn off fan
}
void ReefAngelClass::StandardATO(byte ATORelay, int ATOTimeout)
{
// Input: Relay port and timeout value (max number of seconds that ATO pump is allowed to run)
unsigned long TempTimeout = ATOTimeout;
TempTimeout *= 1000;
/*
Is the low switch active (meaning we need to top off) and are we not currently topping off
Then we set the timer to be now and start the topping pump
*/
if ( LowATO.IsActive() && ( !LowATO.IsTopping()) )
{
LowATO.Timer = millis();
LowATO.StartTopping();
Relay.On(ATORelay);
}
// If the high switch is activated, this is a safeguard to prevent over running of the top off pump
if ( HighATO.IsActive() )
{
LowATO.StopTopping(); // stop the low ato timer
Relay.Off(ATORelay);
}
/*
If the current time minus the start time of the ATO pump is greater than the specified timeout value
AND the ATO pump is currently running:
We turn on the status LED and shut off the ATO pump
This prevents the ATO pump from contniously running.
*/
if ( (millis()-LowATO.Timer > TempTimeout) && LowATO.IsTopping() )
{
LED.On();
Relay.Off(ATORelay);
}
}
void ReefAngelClass::SingleATO(bool bLow, byte ATORelay, byte byteTimeout, byte byteHrInterval)
{
// if switch is active, stop the pump because the resevoir is full
// when the switch is not active, we need to turn on the relay to fill up resevoir
bool bCanRun = true;
static int iLastTop = 0;
if ( byteHrInterval )
{
int iSafeTop = NumMins(hour(), minute()) - iLastTop;
if ( iSafeTop < 0 )
{
iSafeTop += 1440;
}
if ( iSafeTop < (byteHrInterval * 60) )
{
bCanRun = false;
}
}
ReefAngel_ATOClass *ato;
if ( bLow )
{
ato = &LowATO;
}
else
{
ato = &HighATO;
}
unsigned long t = byteTimeout;
t *= 1000;
if ( ato->IsActive() )
{
iLastTop = NumMins(hour(), minute());
ato->StopTopping();
Relay.Off(ATORelay);
}
else if ( !ato->IsTopping() )
{
if ( bCanRun )
{
ato->Timer = millis();
ato->StartTopping();
Relay.On(ATORelay);
}
}
if ( (millis() - ato->Timer > t) && ato->IsTopping() )
{
LED.On();
Relay.Off(ATORelay);
}
}
void ReefAngelClass::DosingPump(byte DPRelay, byte DPTimer, byte OnHour, byte OnMinute, byte RunTime)
{
/*
This function configures and sets up the dosing pump and turns it on at the appropriate time
Once the timer has expired for the dosing pump, it shuts it off
DPRelay - relay dosing pump is plugged into (0-8)
Timer - timer to control dosing pump
OnHour & OnMinute - time to turn on the dosing pump (in 24hr based time)
RunTime - duration to run the pump
*/
// Let's see if it's supposed to start running the timer now
if ( (NumMins(hour(), minute()) == NumMins(OnHour, OnMinute)) && (second() == 0) )
{
Relay.On(DPRelay);
//LED.On();
Timer[DPTimer].SetInterval(RunTime);
Timer[DPTimer].Start();
}
// is the timer expired?
if ( Timer[DPTimer].IsTriggered() )
{
// timer expired, so let's shut off the pump
Relay.Off(DPRelay);
//LED.Off();
}
}
void ReefAngelClass::DosingPumpRepeat(byte DPRelay, byte DPTimer, int RepeatMinute, byte RunTime)
{
/*
This function runs the specified relay for the RunTime seconds every RepeatMinute minutes.
So you can run the relay for 10 seconds every 60 minutes (1 hour)
This function bases the RepeatMinute off of Midnight (00:00) of the current day. It uses midnight to
compute when the pump will run.
DPRelay - Relay that contains the dosing pump
Timer - number of the timer in the timer array to use
RepeatMinute - number of minutes to wait before running the pump again
RunTime - duration (in seconds) to run the pump
*/
// if the current minutes since midnight are divisible by the repeat interval and the current seconds
// are zero (top of the minute), then we can run the pump
time_t t = now();
uint8_t h = hour(t);
if ( (h == 0) && (minute(t) == 0) )
{
// if we are at midnight, change hours to 24 so we can get the correct minutes for computation
h = 24;
}
int current_min = NumMins(h, minute(t));
int r = current_min % RepeatMinute;
if ( (r == 0) && (second(t) == 0) )
{
Relay.On(DPRelay);
Timer[DPTimer].SetInterval(RunTime);
Timer[DPTimer].Start();
}
// Should change the timer to be a Dosing Pump Timer
// is the timer expired?
if ( Timer[DPTimer].IsTriggered() )
{
Relay.Off(DPRelay);
}
}
void ReefAngelClass::Wavemaker(byte WMRelay, byte WMTimer)
{
// TODO Update Timers appropriately
if ( Timer[WMTimer].IsTriggered() )
{
Timer[WMTimer].Start();
Relay.Toggle(WMRelay);
}
}
// Simplified for PDE file
void ReefAngelClass::StandardLights(byte Relay)
{
StandardLights(Relay,
InternalMemory.StdLightsOnHour_read(),
InternalMemory.StdLightsOnMinute_read(),
InternalMemory.StdLightsOffHour_read(),
InternalMemory.StdLightsOffMinute_read());
}
void ReefAngelClass::MHLights(byte Relay)
{
MHLights(Relay,
InternalMemory.MHOnHour_read(),
InternalMemory.MHOnMinute_read(),
InternalMemory.MHOffHour_read(),
InternalMemory.MHOffMinute_read(),
InternalMemory.MHDelay_read());
}
void ReefAngelClass::StandardHeater(byte Relay)
{
StandardHeater(Relay,
InternalMemory.HeaterTempOn_read(),
InternalMemory.HeaterTempOff_read());
}
void ReefAngelClass::StandardFan(byte Relay)
{
StandardFan(Relay,
InternalMemory.ChillerTempOff_read(),
InternalMemory.ChillerTempOn_read());
}
void ReefAngelClass::StandardATO(byte Relay)
{
StandardATO(Relay, InternalMemory.ATOTimeout_read());
}
void ReefAngelClass::SingleATOLow(byte Relay)
{
SingleATO(true, Relay, InternalMemory.ATOTimeout_read(), InternalMemory.ATOHourInterval_read());
}
void ReefAngelClass::SingleATOHigh(byte Relay)
{
SingleATO(false, Relay, InternalMemory.ATOHighTimeout_read(), InternalMemory.ATOHighHourInterval_read());
}
void ReefAngelClass::DosingPump1(byte Relay)
{
// TODO Update Timers appropriately
DosingPump(Relay, 1,
InternalMemory.DP1OnHour_read(),
InternalMemory.DP1OnMinute_read(),
InternalMemory.DP1Timer_read());
}
void ReefAngelClass::DosingPump2(byte Relay)
{
// TODO Update Timers appropriately
DosingPump(Relay, 2,
InternalMemory.DP2OnHour_read(),
InternalMemory.DP2OnMinute_read(),
InternalMemory.DP2Timer_read());
}
void ReefAngelClass::DosingPumpRepeat1(byte Relay)
{
// TODO Update Timers appropriately
DosingPumpRepeat(Relay, 1,
InternalMemory.DP1RepeatInterval_read(),
InternalMemory.DP1Timer_read());
}
void ReefAngelClass::DosingPumpRepeat2(byte Relay)
{
// TODO Update Timers appropriately
DosingPumpRepeat(Relay, 2,
InternalMemory.DP2RepeatInterval_read(),
InternalMemory.DP2Timer_read());
}
void ReefAngelClass::Wavemaker1(byte WMRelay)
{
// TODO Update Timers appropriately
static bool bSetup = false;
if ( ! bSetup )
{
Timer[1].SetInterval(InternalMemory.WM1Timer_read());
Timer[1].Start();
Relay.On(WMRelay);
#ifdef WavemakerSetup
WM1Port = WMRelay;
#endif // WavemakerSetup
// once setup, don't setup again
bSetup = true;
}
Wavemaker(WMRelay, 1);
}
void ReefAngelClass::Wavemaker2(byte WMRelay)
{
// TODO Update Timers appropriately
static bool bSetup = false;
if ( ! bSetup )
{
Timer[2].SetInterval(InternalMemory.WM2Timer_read());
Timer[2].Start();
Relay.On(WMRelay);
#ifdef WavemakerSetup
WM2Port = WMRelay;
#endif // Wavemakersetup
// once setup, don't setup again
bSetup = true;
}
Wavemaker(WMRelay, 2);
}
#ifdef VersionMenu
void ReefAngelClass::DisplayVersion()
{
// Display the Software Version
LCD.DrawText(ModeScreenColor,DefaultBGColor,10,10,"Reef Angel");
LCD.DrawText(ModeScreenColor,DefaultBGColor,10,20,"v"ReefAngel_Version);
#ifdef DEV_MODE
LCD.DrawText(ModeScreenColor,DefaultBGColor,10,30,"Dev Mode");
#endif // DEV_MODE
#ifdef wifi
// Display wifi related information
// Place holder information currently, need wifi module
// to be able to write functions to retrieve actual information
LCD.DrawText(ModeScreenColor,DefaultBGColor,10,40,"Wifi Enabled");
#endif // wifi
}
#endif // VersionMenu
void ReefAngelClass::ClearScreen(byte Color)
{
// clears the entire screen
LCD.Clear(Color, 0, 0, 131, 131);
}
#ifdef DisplayLEDPWM
void ReefAngelClass::MoonlightPWM(byte RelayID, bool ShowPWM)
{
int m,d,y;
int yy,mm;
long K1,K2,K3,J,V;
byte PWMvalue;
m = month();
d = day();
y = year();
yy = y - ((12-m)/10);
mm = m + 9;
if (mm>=12) mm -= 12;
K1 = 365.25 * (yy+4712);
K2 = 30.6 * mm+.5;
K3 = int(int((yy/100)+49)*.75)-38;
J = K1+K2+d+59-K3;
V = (J-2451550.1)/0.29530588853;
V -= int(V/100)*100;
V = abs(V-50);
PWMvalue = 4*abs(50-V); // 5.12=100% 4=~80%
pinMode(lowATOPin,OUTPUT);
if (RelayID && (bitRead(Relay.RelayData,RelayID-1)==0)) PWMvalue=0;
analogWrite(lowATOPin,PWMvalue);
if (ShowPWM) PWM.SetActinic((PWMvalue*100)/255);
}
#endif // DisplayLEDPWM
void ReefAngelClass::PCLogging()
{
// This function is used for logging the data to Dave's PC Client software
// It prints the strings from program memory instead of RAM
PROGMEMprint(XML_T1);
Serial.print(Params.Temp1);
PROGMEMprint(XML_T2);
Serial.print(Params.Temp2);
PROGMEMprint(XML_T3);
Serial.print(Params.Temp3);
PROGMEMprint(XML_PH);
Serial.print(Params.PH);
PROGMEMprint(XML_R);
Serial.print(Relay.RelayData,DEC);
PROGMEMprint(XML_RON);
Serial.print(Relay.RelayMaskOn,DEC);
PROGMEMprint(XML_ROFF);
Serial.print(Relay.RelayMaskOff,DEC);
PROGMEMprint(XML_RE_CLOSE);
PROGMEMprint(XML_RE_OFF);
Serial.print(">");
#ifdef RelayExp
for ( int EID = 0; EID < MAX_RELAY_EXPANSION_MODULES; EID++ )
{
// relay data
PROGMEMprint(XML_RE_OPEN);
Serial.print(EID, DEC);
Serial.print(">");
Serial.print(Relay.RelayDataE[EID],DEC);
PROGMEMprint(XML_RE_CLOSE);
Serial.print(EID, DEC);
Serial.print(">");
// relay on mask
PROGMEMprint(XML_RE_OPEN);
PROGMEMprint(XML_RE_ON);
Serial.print(EID, DEC);
Serial.print(">");
Serial.print(Relay.RelayMaskOnE[EID],DEC);
PROGMEMprint(XML_RE_CLOSE);
PROGMEMprint(XML_RE_ON);
Serial.print(EID, DEC);
Serial.print(">");
// relay off mask
PROGMEMprint(XML_RE_OPEN);
PROGMEMprint(XML_RE_OFF);
Serial.print(EID, DEC);
Serial.print(">");
Serial.print(Relay.RelayMaskOffE[EID],DEC);
PROGMEMprint(XML_RE_CLOSE);
PROGMEMprint(XML_RE_OFF);
Serial.print(EID, DEC);
Serial.print(">");
}
#endif // RelayExp
PROGMEMprint(XML_ATOLOW);
Serial.print(LowATO.IsActive());
PROGMEMprint(XML_ATOHIGH);
Serial.print(HighATO.IsActive());
PROGMEMprint(XML_END);
}
#ifdef wifi
void ReefAngelClass::LoadWebBanner(int pointer, byte qty)
{
webbannerpointer = pointer;
webbannerqty = qty;
}
void ReefAngelClass::WebBanner()
{
char buffer[22];
int ptr = webbannerpointer;
int tagptr = pgm_read_word(&(webbanner_tags[0]));
PROGMEMprint(BannerGET);
Serial.print(Params.Temp1, DEC);
PROGMEMprint(BannerT2);
Serial.print(Params.Temp2, DEC);
PROGMEMprint(BannerT3);
Serial.print(Params.Temp3, DEC);
PROGMEMprint(BannerPH);
Serial.print(Params.PH, DEC);
PROGMEMprint(BannerRelayData);
// compute the correct relay data, when we force a port on or off, it does not get reflected in the relaydata
// so we must compute it based on the mask and send the data to the banner (compute just like when sending the
// relay data on the wire)
byte TempRelay = Relay.RelayData;
TempRelay &= Relay.RelayMaskOff;
TempRelay |= Relay.RelayMaskOn;
Serial.print(TempRelay, DEC);
if ( webbannerqty == WEB_BANNER_QTY )
{
for ( int i = 0; i < WEB_BANNER_QTY; i++ )
{
strcpy_P(buffer, (char *)tagptr++);
tagptr += strlen(buffer);
Serial.print("&");
Serial.print(buffer);
Serial.print("=");
strcpy_P(buffer, (char *)ptr++);
ptr += strlen(buffer);
Serial.print(buffer);
} // for i
}
else
{
strcpy_P(buffer, (char *)tagptr++);
tagptr += strlen(buffer);
Serial.print("&");
Serial.print(buffer);
Serial.print("=");
strcpy_P(buffer, (char *)ptr++);
ptr += strlen(buffer);
Serial.print(buffer);
Serial.print("&");
P(badmsg)=BAD;
printP(badmsg);
}
Serial.println("\n\n");
}
#endif // wifi
void ReefAngelClass::InitMenus()
{
// loads all the menus
menusptr[MainMenu] = pgm_read_word(&(mainmenu_items[0]));
menuqtysptr[MainMenu] = SIZE(mainmenu_items);
menusptr[SetupMenu] = pgm_read_word(&(setupmenu_items[0]));
menuqtysptr[SetupMenu] = SIZE(setupmenu_items);
#ifndef RemoveAllLights
menusptr[LightsMenu] = pgm_read_word(&(lightsmenu_items[0]));
menuqtysptr[LightsMenu] = SIZE(lightsmenu_items);
#endif // RemoveAllLights
menusptr[TempsMenu] = pgm_read_word(&(tempsmenu_items[0]));
menuqtysptr[TempsMenu] = SIZE(tempsmenu_items);
#if defined SetupExtras || defined ATOSetup
menusptr[TimeoutsMenu] = pgm_read_word(&(timeoutsmenu_items[0]));
menuqtysptr[TimeoutsMenu] = SIZE(timeoutsmenu_items);
#endif // if defined SetupExtras || defined ATOSetup
// initialize menus
PreviousMenu = DEFAULT_MENU;
DisplayedMenu = DEFAULT_MENU; // default menu to display
SelectedMenuItem = DEFAULT_MENU_ITEM; // default item to have selected
redrawmenu = true;
showmenu = false; // initially we are showing the main graphic and no menu
}
void ReefAngelClass::ShowInterface()
{
Refresh();
// are we displaying the menu or not??
if ( showmenu )
{
DisplayMenuHeading();
DisplayMenu();
}
else
{
// not displaying the menu, so we're gonna show the default screen
if ( DisplayedMenu == DEFAULT_MENU )
{
// process screensaver timeout
if ( Timer[3].IsTriggered() )
{
// Screensaver timeout expired
LCD.BacklightOff();
}
if ( Joystick.IsButtonPressed() )
{
// turn the backlight on
LCD.BacklightOn();
// TODO check Timer[3] code
if ( Timer[3].Trigger == 0 )
{
Timer[3].Start();
return;
}
// Clears the screen to draw the menu
// Displays main menu, select first item, save existing menu
ClearScreen(DefaultBGColor);
SelectedMenuItem = DEFAULT_MENU_ITEM;
PreviousMenu = DEFAULT_MENU;
DisplayedMenu = MAIN_MENU;
showmenu = true;
redrawmenu = true;
menutimeout = now();
// get out of this function and display the menu
return;
}
if ( Joystick.IsUp() || Joystick.IsDown() || Joystick.IsRight() || Joystick.IsLeft() )
{
// Turn backlight on
LCD.BacklightOn();
Timer[3].Start();
}
pingSerial();
// display everything on the home screen except the graph
// the graph is drawn/updated when we exit the main menu & when the parameters are saved
LCD.DrawDate(6, 112);
pingSerial();
#if defined DisplayLEDPWM && ! defined RemoveAllLights
LCD.DrawMonitor(15, 60, Params, PWM.GetDaylightValue(), PWM.GetActinicValue());
#else // defined DisplayLEDPWM && ! defined RemoveAllLights
LCD.DrawMonitor(15, 60, Params);
#endif // defined DisplayLEDPWM && ! defined RemoveAllLights
pingSerial();
byte TempRelay = Relay.RelayData;
TempRelay &= Relay.RelayMaskOff;
TempRelay |= Relay.RelayMaskOn;
LCD.DrawOutletBox(12, 93, TempRelay);
pingSerial();
// Process any checks/tests/events that can happen while displaying the home screen
// This can be the timers for wavemakers or any overheat temperatures
// process timers
if ( Timer[5].IsTriggered() )
{
int CurTemp;
// Values are stored in the I2CEEPROM1
taddr++;
if ( taddr >= 120 ) taddr = 0;
Timer[5].Start();
CurTemp = map(Params.Temp1, T1LOW, T1HIGH, 0, 50); // apply the calibration to the sensor reading
CurTemp = constrain(CurTemp, 0, 50); // in case the sensor value is outside the range seen during calibration
//LCD.Clear(DefaultBGColor,0,0,1,1);
Memory.Write(taddr, CurTemp);
pingSerial();
LCD.Clear(DefaultBGColor,0,0,1,1);
CurTemp = map(Params.Temp2, T2LOW, T2HIGH, 0, 50); // apply the calibration to the sensor reading
CurTemp = constrain(CurTemp, 0, 50); // in case the sensor value is outside the range seen during calibration
LCD.Clear(DefaultBGColor,0,0,1,1);
Memory.Write(taddr+120, CurTemp);
pingSerial();
LCD.Clear(DefaultBGColor,0,0,1,1);
CurTemp = map(Params.Temp3, T3LOW, T3HIGH, 0, 50); // apply the calibration to the sensor reading
CurTemp = constrain(CurTemp, 0, 50); // in case the sensor value is outside the range seen during calibration
//LCD.Clear(DefaultBGColor,0,0,1,1);
Memory.Write(taddr+240, CurTemp);
pingSerial();
LCD.Clear(DefaultBGColor,0,0,1,1);
CurTemp = map(Params.PH, PHLOW, PHHIGH, 0, 50); // apply the calibration to the sensor reading
CurTemp = constrain(CurTemp, 0, 50); // in case the sensor value is outside the range seen during calibration
//LCD.Clear(DefaultBGColor,0,0,1,1);
Memory.Write(taddr+360, CurTemp);
pingSerial();
LCD.Clear(DefaultBGColor,0,0,1,1);
if ((taddr%10)==0) InternalMemory.T1Pointer_write(taddr);
LCD.DrawGraph(5, 5);
}
// if temp2 exceeds overheat temp
#ifdef OverheatSetup
if ( Params.Temp2 >= InternalMemory.OverheatTemp_read() )
#else // OverheatSetup
if ( Params.Temp2 >= 1500 ) // 150.0 F is the default
#endif // OverheatSetup
{
LED.On();
// invert the ports that are activated
Relay.RelayMaskOff = ~OverheatShutoffPorts;
#ifdef RelayExp
for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = ~OverheatShutoffPortsE[i];
}
#endif // RelayExp
}
// commit relay changes
Relay.Write();
}
else
{
// we are viewing another screen
if ( Joystick.IsButtonPressed() )
{
// button is pressed, so we gotta exit out, show the menu & redraw it too
redrawmenu = true;
showmenu = true;
Timer[0].ForceTrigger();
Timer[3].Start();
}
} // if DisplayedMenu == DEFAULT_MENU
} // if showmenu
}
void ReefAngelClass::DisplayMenu()
{
// redrawmenu should only get set from within this function when we move the joystick or press the button
byte qty = menuqtysptr[DisplayedMenu];
int ptr = menusptr[DisplayedMenu];
if ( Joystick.IsUp() )
{
// process UP press
if ( (--SelectedMenuItem) > qty )
{
// we're moving up and we hit the top of the list
// gotta wrap down to the bottom of the list
// qty - 1 gives us the last item in our list
//SelectedMenuItem = qty - 1;
// This allows us to add in our last item
SelectedMenuItem = qty;
}
redrawmenu = true;
menutimeout = now();
}
if ( Joystick.IsDown() )
{
// process DOWN press
// > allows for selection of last item, >= skips it
if ( (++SelectedMenuItem) > qty )
{
// we've hit the bottom of the list
// wrap around to the top of the list
SelectedMenuItem = DEFAULT_MENU_ITEM;
}
redrawmenu = true;
menutimeout = now();
}
// TODO Have ability to customize menu timeout delay
if ( (now() - menutimeout) > MENU_TIMEOUT )
{
// menu timeout returns to the main screen
// skip all the other menu checks
SelectedMenuItem = EXCEED_TIMEOUT_MENU;
DisplayedMenu = EXCEED_TIMEOUT_MENU;
ButtonPress++;
}
if ( Joystick.IsButtonPressed() )
{
// button gets pressed, so we need to handle the button press
ProcessButtonPress();
redrawmenu = true;
// Don't finish processing the rest of the menu
return;
}
// don't redraw the menu if we don't have to
if ( ! redrawmenu )
return;
byte i;
byte bcolor, fcolor;
char buffer[22];
for ( i = 0; i <= qty; i++ )
{
bcolor = DefaultBGColor;
fcolor = DefaultFGColor;
if ( i < qty )
{
strcpy_P(buffer, (char *)ptr++);
}
else
{
// the last item in the list is either Exit or Prev Menu
if ( DisplayedMenu == MainMenu )
{
strcpy(buffer, "Exit");
}
else
{
strcpy(buffer, "<- Prev Menu");
}
}
ptr += strlen(buffer);
// change the background color on the selected menu entry
if ( i == SelectedMenuItem )
{
bcolor = SelectionBGColor;
fcolor = SelectionFGColor;
}
LCD.Clear(bcolor, MENU_START_COL-3,
(i*MENU_START_ROW)+MENU_HEADING_SIZE-1,
MENU_END_COL,
(i*MENU_START_ROW)+(MENU_HEADING_SIZE+MENU_ITEM_HEIGHT-1));
LCD.DrawText(fcolor, bcolor, MENU_START_COL, (i*MENU_START_ROW)+MENU_HEADING_SIZE, buffer);
} // for i
// once drawn, no need to redraw yet
redrawmenu = false;
}
void ReefAngelClass::DisplayMenuHeading()
{
// NOTE do we redraw the menu heading or not? use same logic as with the menu
if ( ! redrawmenu )
return;
char buffer[10];
switch ( DisplayedMenu )
{
default:
// {
// //strcpy(buffer, "Menu:");
// sprintf(buffer, "Menu (%d):", MenuNum);
// }
// break;
case MainMenu:
{
strcpy(buffer, "Main:");
}
break;
case SetupMenu:
{
strcpy(buffer, "Setup:");
}
break;
#ifndef RemoveAllLights
case LightsMenu:
{
strcpy(buffer, "Lights:");
}
break;
#endif // RemoveAllLights
case TempsMenu:
{
strcpy(buffer, "Temp:");
}
break;
#if defined SetupExtras || defined ATOSetup
case TimeoutsMenu:
{
strcpy(buffer, "Timeouts:");
}
break;
#endif // if defined SetupExtras || defined ATOSetup
} // switch MenuNum
// clear the line that has the menu heading on it
LCD.Clear(DefaultBGColor, MENU_START_COL, MENU_START_ROW, MAX_X, MAX_Y);
// Display the menu heading
LCD.DrawText(DefaultFGColor, DefaultBGColor, MENU_START_COL, MENU_START_ROW, buffer);
}
void ReefAngelClass::DisplayMenuEntry(char *text)
{
ClearScreen(DefaultBGColor);
LCD.DrawText(DefaultFGColor, DefaultBGColor, MENU_START_COL, MENU_START_ROW, text);
LCD.DrawText(DefaultFGColor, DefaultBGColor, MENU_START_COL, MENU_START_ROW*4, "Press to exit...");
}
void ReefAngelClass::FeedingMode()
{
LCD.DrawText(ModeScreenColor, DefaultBGColor, 30, 10, "Feeding Mode");
Timer[0].Start(); //Start Feeding Mode timer
#ifdef DisplayImages
LCD.DrawEEPromImage(40,50, 40, 30, I2CEEPROM2, I2CEEPROM2_Feeding);
#endif // DisplayImages
int t;
bool bDone = false;
do
{
t = Timer[0].Trigger - now();
if ( (t >= 0) && ! Timer[0].IsTriggered() )
{
LCD.Clear(DefaultBGColor,60+(intlength(t)*5),100,100,108);
LCD.DrawText(DefaultFGColor,DefaultBGColor,60,100,t);
}
else
{
bDone = true;
}
if ( Joystick.IsButtonPressed() )
{
// joystick button pressed, so we stop the feeding mode
bDone = true;
}
delay(200);
} while ( ! bDone );
// we're finished, so let's clear the screen and return
ClearScreen(DefaultBGColor);
Timer[3].Start(); // start LCD shutoff timer
}
void ReefAngelClass::WaterChangeMode()
{
LCD.DrawText(ModeScreenColor, DefaultBGColor, 20, 10, "Water Change Mode");
#ifdef DisplayImages
LCD.DrawEEPromImage(51,55, 40, 30, I2CEEPROM2, I2CEEPROM2_Water_Change);
#endif // DisplayImages
do
{
// just wait for the button to be pressed
delay(200);
} while ( ! Joystick.IsButtonPressed() );
ClearScreen(DefaultBGColor);
Timer[3].Start(); // start LCD shutoff timer
}
void ReefAngelClass::ProcessButtonPress()
{
bool bResetMenuTimeout = true;
switch ( DisplayedMenu )
{
default: // DEFAULT_MENU == 255
// {
// // Default Screen
break;
// }
case MainMenu:
{
ProcessButtonPressMain();
break;
}
case SetupMenu:
{
ProcessButtonPressSetup();
break;
}
#ifndef RemoveAllLights
case LightsMenu:
{
ProcessButtonPressLights();
break;
}
#endif // RemoveAllLights
case TempsMenu:
{
ProcessButtonPressTemps();
break;
}
#if defined SetupExtras || defined ATOSetup
case TimeoutsMenu:
{
ProcessButtonPressTimeouts();
break;
}
#endif // if defined SetupExtras || defined ATOSetup
case EXCEED_TIMEOUT_MENU:
{
// we bypass all the other menus when the timeout has exceeded
// we need to mimic the default action for the main menu in addition to
// clearing out the stack
SelectedMenuItem = DEFAULT_MENU_ITEM;
DisplayedMenu = DEFAULT_MENU;
showmenu = false;
ClearScreen(DefaultBGColor);
bResetMenuTimeout = false;
// we are exiting the menu, so draw the graph
LCD.DrawGraph(5, 5); // Redraw graphic of params
break;
}
}
// if a button was pressed, we have to reset the timeout after processing the press
if ( bResetMenuTimeout )
{
menutimeout = now();
}
}
void ReefAngelClass::ProcessButtonPressMain()
{
showmenu = true;
ClearScreen(DefaultBGColor);
switch ( SelectedMenuItem )
{
case MainMenu_FeedingMode:
{
// turn off ports
#ifdef SaveRelayState
// TODO Test SaveRelayState
byte CurrentRelayState = Relay.RelayData;
#endif // SaveRelayState
Relay.RelayMaskOff = ~FeedingModePorts;
#ifdef RelayExp
byte i;
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = ~FeedingModePortsE[i];
}
#endif // RelayExp
Relay.Write();
// run feeding mode
FeedingMode();
// turn on ports
Relay.RelayMaskOff = B11111111;
#ifdef RelayExp
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = B11111111;
}
#endif // RelayExp
// restore ports
#ifdef SaveRelayState
Relay.RelayData = CurrentRelayState;
#endif // SaveRelayState
Relay.Write();
break;
}
case MainMenu_WaterChangeMode:
{
// turn off ports
#ifdef SaveRelayState
// TODO Test SaveRelayState
byte CurrentRelayState = Relay.RelayData;
#endif // SaveRelayState
Relay.RelayMaskOff = ~WaterChangePorts;
#ifdef RelayExp
byte i;
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = ~WaterChangePortsE[i];
}
#endif // RelayExp
Relay.Write();
// Display the water change mode
WaterChangeMode();
// turn on ports
Relay.RelayMaskOff = B11111111;
#ifdef RelayExp
for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = B11111111;
}
#endif // RelayExp
#ifdef SaveRelayState
Relay.RelayData = CurrentRelayState;
#endif // SaveRelayState
Relay.Write();
break;
}
#ifndef RemoveAllLights
case MainMenu_Lights:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
PreviousMenu = DisplayedMenu;
DisplayedMenu = LightsMenu;
break;
}
#endif // RemoveAllLights
case MainMenu_Temps:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
PreviousMenu = DisplayedMenu;
DisplayedMenu = TempsMenu;
break;
}
#if defined SetupExtras || defined ATOSetup
case MainMenu_Timeouts:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
PreviousMenu = DisplayedMenu;
DisplayedMenu = TimeoutsMenu;
break;
}
#endif // if defined SetupExtras || defined ATOSetup
case MainMenu_Setup:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
PreviousMenu = DisplayedMenu;
DisplayedMenu = SetupMenu;
break;
}
#ifdef VersionMenu
case MainMenu_Version:
{
DisplayVersion();
// turn off the menu so we can wait for the button press to exit
showmenu = false;
break;
}
#endif // VersionMenu
default:
{
// This will be the EXIT choice
SelectedMenuItem = DEFAULT_MENU_ITEM;
// switch to the previous menu
DisplayedMenu = DEFAULT_MENU;
// disable the menu, display main screen
showmenu = false;
// When we exit the main menu, we will redraw the graph
LCD.DrawGraph(5, 5); // Redraw graphic of params
break;
}
}
}
void ReefAngelClass::ProcessButtonPressSetup()
{
showmenu = true;
ClearScreen(DefaultBGColor);
switch ( SelectedMenuItem )
{
#ifdef WavemakerSetup
case SetupMenu_Wavemaker:
{
int v = InternalMemory.WM1Timer_read();
int y = InternalMemory.WM2Timer_read();
if ( SetupOption(v, y, 0, 21600, 5, "s", "", "Setup Wavemakers", "WM1:", "WM2:") )
{
InternalMemory.WM1Timer_write(v);
InternalMemory.WM2Timer_write(y);
// after we set the values we need to update the timers
// TODO Update Timers appropriately
Timer[1].Trigger = 0;
Timer[1].SetInterval(v);
Timer[1].Start();
Relay.On(WM1Port);
Timer[2].Trigger = 0;
Timer[2].SetInterval(y);
Timer[2].Start();
Relay.On(WM2Port);
Relay.Write();
}
break;
}
#endif // WavemakerSetup
#ifdef DosingPumpSetup
case SetupMenu_DosingPump:
{
SetupDosingPump();
break;
}
#endif // DosingPumpSetup
#ifdef DosingPumpIntervalSetup
case SetupMenu_DosingPumpInterval:
{
int v = InternalMemory.DP1RepeatInterval_read();
int y = InternalMemory.DP2RepeatInterval_read();
if ( SetupOption(v, y, 1, 1440, 4, "m", "", "Repeat Interval", "DP1:", "DP2:") )
{
InternalMemory.DP1RepeatInterval_write(v);
InternalMemory.DP2RepeatInterval_write(y);
}
v = InternalMemory.DP1Timer_read();
y = InternalMemory.DP2Timer_read();
if ( SetupOption(v, y, 1, 255, 3, "s", "", "Run Time", "DP1:", "DP2:") )
{
InternalMemory.DP1Timer_write(v);
InternalMemory.DP2Timer_write(y);
}
break;
}
#endif // DosingPumpIntervalSetup
case SetupMenu_CalibratePH:
{
SetupCalibratePH();
break;
}
#ifdef DateTimeSetup
case SetupMenu_DateTime:
{
SetupDateTime();
break;
}
#endif // DateTimeSetup
default:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
// switch to the previous menu
DisplayedMenu = PreviousMenu;
break;
}
}
}
#ifndef RemoveAllLights
void ReefAngelClass::ProcessButtonPressLights()
{
showmenu = true; // set to true when displaying setup screens
ClearScreen(DefaultBGColor);
switch ( SelectedMenuItem )
{
case LightsMenu_On:
{
// turn on ports
Relay.RelayMaskOn = LightsOnPorts;
#ifdef RelayExp
for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOnE[i] = LightsOnPortsE[i];
}
#endif // RelayExp
#ifdef DisplayLEDPWM
PWM.SetActinic(InternalMemory.LEDPWMActinic_read());
PWM.SetDaylight(InternalMemory.LEDPWMDaylight_read());
#endif // DisplayLEDPWM
Relay.Write();
DisplayMenuEntry("Lights On");
showmenu = false;
break;
}
case LightsMenu_Off:
{
// reset ports
Relay.RelayMaskOn = B00000000;
#ifdef RelayExp
for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOnE[i] = B00000000;
}
#endif // RelayExp
#ifdef DisplayLEDPWM
// sets PWM to 0%
PWM.SetActinic(0);
PWM.SetDaylight(0);
#endif // DisplayLEDPWM
Relay.Write();
DisplayMenuEntry("Restore Lights");
showmenu = false;
break;
}
#ifdef MetalHalideSetup
case LightsMenu_MetalHalides:
{
SetupLightsOptionDisplay(true);
break;
}
case LightsMenu_MetalHalideDelay:
{
int v = InternalMemory.MHDelay_read();
int y = -1;
if ( SetupOption(v, y, 0, 255, 3, "m", "", "Setup MH Delay", "", "") )
{
InternalMemory.MHDelay_write((uint8_t)v);
}
break;
}
#endif // MetalHalideSetup
#ifdef StandardLightSetup
case LightsMenu_StandardLights:
{
SetupLightsOptionDisplay(false);
break;
}
#endif // StandardLightSetup
#ifdef DisplayLEDPWM
case LightsMenu_LEDPWM:
{
int v = InternalMemory.LEDPWMActinic_read();
int y = InternalMemory.LEDPWMDaylight_read();
if ( SetupOption(v, y, 0, 100, 3, "%", "", "Setup LED", "AP:", "DP:") )
{
InternalMemory.LEDPWMActinic_write((uint8_t)v);
InternalMemory.LEDPWMDaylight_write((uint8_t)y);
// Restore PWM values
PWM.SetActinic((uint8_t)v);
PWM.SetDaylight((uint8_t)y);
}
break;
}
#endif // DisplayLEDPWM
default:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
// switch to the previous menu
DisplayedMenu = PreviousMenu;
break;
}
}
}
#endif // RemoveAllLights
void ReefAngelClass::ProcessButtonPressTemps()
{
showmenu = true;
ClearScreen(DefaultBGColor);
switch ( SelectedMenuItem )
{
case TempsMenu_Heater:
{
int v = InternalMemory.HeaterTempOn_read();
int y = InternalMemory.HeaterTempOff_read();
if ( SetupOption(v, y, 700, 900, 3, "F", ".", "Setup Heater", "On @", "Off @") )
{
InternalMemory.HeaterTempOn_write(v);
InternalMemory.HeaterTempOff_write(y);
}
break;
}
case TempsMenu_Chiller:
{
int v = InternalMemory.ChillerTempOn_read();
int y = InternalMemory.ChillerTempOff_read();
if ( SetupOption(v, y, 700, 900, 3, "F", ".", "Setup Chiller", "On @", "Off @") )
{
InternalMemory.ChillerTempOn_write(v);
InternalMemory.ChillerTempOff_write(y);
}
break;
}
#ifdef OverheatSetup
case TempsMenu_Overheat:
{
int v = InternalMemory.OverheatTemp_read();
int y = -1;
if ( SetupOption(v, y, 800, 2000, 4, "F", ".", "Setup Overheat", "", "") )
{
InternalMemory.OverheatTemp_write(v);
}
break;
}
#endif // OverheatSetup
case TempsMenu_OverheatClr:
{
LED.Off();
Relay.RelayMaskOff = B11111111;
#ifdef RelayExp
for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ )
{
Relay.RelayMaskOffE[i] = B11111111;
}
#endif // RelayExp
Relay.Write();
DisplayMenuEntry("Clear Overheat");
showmenu = false;
break;
}
default:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
// switch to the previous menu
DisplayedMenu = PreviousMenu;
break;
}
}
}
#if defined SetupExtras || defined ATOSetup
void ReefAngelClass::ProcessButtonPressTimeouts()
{
showmenu = true;
ClearScreen(DefaultBGColor);
switch ( SelectedMenuItem )
{
#ifdef ATOSetup
case TimeoutsMenu_ATOSet:
{
#ifdef SingleATOSetup
int v = InternalMemory.ATOTimeout_read();
int y = InternalMemory.ATOHighTimeout_read();
if ( SetupOption(v, y, 0, 255, 3, "s", "", "ATO Timeout", "Low:", "High:") )
{
InternalMemory.ATOTimeout_write((uint8_t)v);
InternalMemory.ATOHighTimeout_write((uint8_t)y);
}
#else // SingleATOSetup
int v = InternalMemory.ATOTimeout_read();
int y = -1;
if ( SetupOption(v, y, 0, 255, 3, "s", "", "ATO Timeout", "", "") )
{
InternalMemory.ATOTimeout_write((uint8_t)v);
}
#endif // SingleATOSetup
break;
}
#ifdef SingleATOSetup
case TimeoutsMenu_ATOHrInterval:
{
int v = InternalMemory.ATOHourInterval_read();
int y = InternalMemory.ATOHighHourInterval_read();
if ( SetupOption(v, y, 0, 24, 2, "h", "", "ATO Interval", "Low:", "High:") )
{
InternalMemory.ATOHourInterval_write((uint8_t)v);
InternalMemory.ATOHighHourInterval_write((uint8_t)y);
}
break;
}
#endif // SingleATOSetup
case TimeoutsMenu_ATOClear:
{
// Need delay for clearing & returning screen
LED.Off();
LowATO.StopTopping();
HighATO.StopTopping();
DisplayMenuEntry("Clear ATO Timeout");
showmenu = false;
break;
}
#endif // ATOSetup
#ifdef SetupExtras
case TimeoutsMenu_Feeding:
{
int v = InternalMemory.FeedingTimer_read();
int y = -1;
if ( SetupOption(v, y, 0, 3600, 4, "s", "", "Feeding Timer", "", "") )
{
InternalMemory.FeedingTimer_write(v);
// update the feeding timer value
Timer[0].SetInterval(v);
}
break;
}
case TimeoutsMenu_LCD:
{
int v = InternalMemory.LCDTimer_read();
int y = -1;
if ( SetupOption(v, y, 0, 3600, 4, "s", "", "Screen Timeout", "" "") )
{
InternalMemory.LCDTimer_write(v);
// update the timer value
Timer[3].SetInterval(v);
Timer[3].Start();
}
break;
}
#endif // SetupExtras
default:
{
SelectedMenuItem = DEFAULT_MENU_ITEM;
// switch to the previous menu
DisplayedMenu = PreviousMenu;
break;
}
}
}
#endif // if defined SetupExtras || defined ATOSetup
Re: How do I code delayed start from feed mode, i.e. for ski
I was looking at the libraries core code and it's going to be tricky...
We definitely do not want to mess with the libraries code, otherwise you may end up having to edit the libraries everytime Curt has an update.
The problem I see is that there is no indication of when feeding mode has begun or ended as far as you PDE is concerned. All we have access is functions on the PDE and if we can't find out start and end time of feeding mode, it becomes hard to trigger the delay.
The FeedingMode() function is just looping inside the core code and only comes out when it's done.
Let's wait for Curt's ideas.
We definitely do not want to mess with the libraries code, otherwise you may end up having to edit the libraries everytime Curt has an update.
The problem I see is that there is no indication of when feeding mode has begun or ended as far as you PDE is concerned. All we have access is functions on the PDE and if we can't find out start and end time of feeding mode, it becomes hard to trigger the delay.
The FeedingMode() function is just looping inside the core code and only comes out when it's done.
Let's wait for Curt's ideas.
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
Ok, at least I know it is as difficult as I thought. I wasn't sure if it was just me.
I think I could code it in my RA Gen PDE, but then I don't know if the menu would be functional anymore... essentially making it so I couldn't manually feed. I think this with the automatic feeding timer would work together though if we can't figure out how to make this code better.
BTW, for anyone else who is wondering where the code I pasted from is... it's the ReefAngel.cpp file in the libraries/ReefAngel folder
I think I could code it in my RA Gen PDE, but then I don't know if the menu would be functional anymore... essentially making it so I couldn't manually feed. I think this with the automatic feeding timer would work together though if we can't figure out how to make this code better.
BTW, for anyone else who is wondering where the code I pasted from is... it's the ReefAngel.cpp file in the libraries/ReefAngel folder
Re: How do I code delayed start from feed mode, i.e. for ski
Yeah, I left the current functionality of simply just staying inside of the FeedingMode function while in feeding mode (and same for WaterChangeMode).rimai wrote: The FeedingMode() function is just looping inside the core code and only comes out when it's done.
Let's wait for Curt's ideas.
Now that things are becoming more advanced and we are wanting more "granular" control over the flow of the controller, it appears that an alternative solution is going to need to be devised. I know the StandardGui (or ShowInterface) function is already designed to handle multiple states. I would think that adding in additional states for FeedingMode or WaterChangeMode wouldn't be difficult. If these states were added in, then the additional functionality one desires would be possible. HOWEVER, the main loop would still be processing and the other functions would still be working and operating just as before (at least in its current state). Additional checks for what mode we are in would be necessary for the other functions.
Without having an opportunity (yet) to fully think this through, it seems like getting the fine tuned control of delays on exiting feeding mode and slowly starting up each device is going to be complex and there's not a simple straight forward way to handle it. It will have to be hard coded manually on a per user basis unless we start getting really complex and maintaining what ports are on what length of delay and so forth. To be honest, this type of complexity is possible but not very feasible with the current limitations of the ReefAngel controller software size.
As I've been writing this, I've had a chance to think a little more and what I'm thinking is I can add a skeleton function (empty function) that gets called at the end (or beginning) of FeedingMode. Then that can override the standard functionality of exiting FeedingMode. Then users can create their own code inside the libraries inside that function. When an update occurs, they would just have to copy that function and paste it into the library again. Also, even better, would be for me to have a separate file for them to create their own code in and if they tell the libraries to use their custom function when exiting FeedingMode, then they wouldn't have to re-copy anything at all. Their function would be intact and all that would have to be done is make sure that nothing has changed with their function.
Now, this is just me rambling and talking out several ideas. It is going to require more thought, BUT I at least wanted to offer up my input and get some feedback on the various suggestions.
Curt
Re: How do I code delayed start from feed mode, i.e. for ski
This sounds like a great idea. I can't imagine that I am the first one to stumble on this problem (the fact that all equipment turns back on at the same time). What happens with your skimmers when you exit feeding or water change mode?? Does your sump not rise?binder wrote: As I've been writing this, I've had a chance to think a little more and what I'm thinking is I can add a skeleton function (empty function) that gets called at the end (or beginning) of FeedingMode. Then that can override the standard functionality of exiting FeedingMode. Then users can create their own code inside the libraries inside that function. When an update occurs, they would just have to copy that function and paste it into the library again. Also, even better, would be for me to have a separate file for them to create their own code in and if they tell the libraries to use their custom function when exiting FeedingMode, then they wouldn't have to re-copy anything at all. Their function would be intact and all that would have to be done is make sure that nothing has changed with their function.
Re: How do I code delayed start from feed mode, i.e. for ski
Is it possible to reference a function to be called as parameter?
This way anyone could create their own fundtion on PDE and just reference it as a parameter.
Just thinking out loud.
This way anyone could create their own fundtion on PDE and just reference it as a parameter.
Just thinking out loud.
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
I don't ever shut my sump off when I'm in feeding mode. My sump area can hold all the additional water overflow/backflow if all pumps are off. My sump contains my protein skimmer as well. So if the sump is off, I'm still skimming but it doesn't increase any additional skimmate in my skimmer. So it doesn't affect me.alexwbush wrote: This sounds like a great idea. I can't imagine that I am the first one to stumble on this problem (the fact that all equipment turns back on at the same time). What happens with your skimmers when you exit feeding or water change mode?? Does your sump not rise?
Now if I were to shut off my sump in feeding mode, I would have to stop several of my filters because they would be halfway out of the water (because my water level drops about 2 inches or so when the sump is off.
Last edited by binder on Fri Mar 25, 2011 8:13 pm, edited 1 time in total.
Re: How do I code delayed start from feed mode, i.e. for ski
Yeah, that's what I was thinking too. It "may" be possible but I'd have to explore it more too. I know with standard C++ you can have functions be passed as parameters and reference them. I do something similar to this with my single ato function. This sort of stuff was what I had in mind with the separate file containing the function.rimai wrote:Is it possible to reference a function to be called as parameter?
This way anyone could create their own fundtion on PDE and just reference it as a parameter.
Just thinking out loud.
I will say that if it's a user created function in the PDE, having a parameter being passed to the Feeding Mode function would be tricky to do since the FeedingMode function is inside the libraries and there's not a direct way to call it from the PDE file. Plus, the function would have to be called the same thing inside the PDE file and the prototype for it would have to be created and stored inside either it's own class (and do a sort of Virtual Function overriding) or have it be a global function inside the Globals file (or inside my ReefAngel.h file) this way the FeedingMode function would know what to call it.
I would think the class and Virtual Function overriding might be the "best" idea, but again it would need some testing to see what way offers the most flexibility with the least amount of confusion.
Going along with the least amount of confusion concept, it would have to be simple and straight forward for people to code it if they desired. I doubt it would be handled from within RAGen, unless I give them a "scratch pad" area to create the function and have it inserted automatically for them (again, talking out loud).
Re: How do I code delayed start from feed mode, i.e. for ski
Has there been any development on this matter. I have the same problem as the original poster. For now I do not plan on using feed mode so I really just need a delay for starting up the skimmer after power is restored to the controller.
Re: How do I code delayed start from feed mode, i.e. for ski
No development yet on my end. I've mostly just been talking things out on the possible logic for this. Investigation/testing still needs to be done and then long-term maintenance needs to be thought out a bit too. Don't want to implement something and not have it be functional or not end accomplishing what we want it to do.b_s_c1 wrote:Has there been any development on this matter.
Re: How do I code delayed start from feed mode, i.e. for ski
Hello everyone
I would very delay on a skimmer in the event of restart after blackout we tried with this, What do you say?
void RitardoSkimmer(byte SKPort, byte SKDelay)
{
if (now()-RAStart < SKDelay) ReefAngel.Relay.On(SKPort);
}
RitardoSkimmer(Port8,120);
I would very delay on a skimmer in the event of restart after blackout we tried with this, What do you say?
void RitardoSkimmer(byte SKPort, byte SKDelay)
{
if (now()-RAStart < SKDelay) ReefAngel.Relay.On(SKPort);
}
RitardoSkimmer(Port8,120);
Re: How do I code delayed start from feed mode, i.e. for ski
I'm curious to see your setup Curt. If I shut off the return pump my water level rises in my sump due to siphoning from the return. And when the water level rises the skimmer starts wet skimming or sucking the water up into the collection cup (which I now have drilled to drain to another bucket). I could drill a small hole or something, but it doesn't really bother me that much and is kind of the basis for how I know how much to drain during water changes.binder wrote:I don't ever shut my sump off when I'm in feeding mode. My sump area can hold all the additional water overflow/backflow if all pumps are off. My sump contains my protein skimmer as well. So if the sump is off, I'm still skimming but it doesn't increase any additional skimmate in my skimmer. So it doesn't affect me.alexwbush wrote: This sounds like a great idea. I can't imagine that I am the first one to stumble on this problem (the fact that all equipment turns back on at the same time). What happens with your skimmers when you exit feeding or water change mode?? Does your sump not rise?
Now if I were to shut off my sump in feeding mode, I would have to stop several of my filters because they would be halfway out of the water (because my water level drops about 2 inches or so when the sump is off.
Re: How do I code delayed start from feed mode, i.e. for ski
Ok, after a good refreshing vacation, I think I know how to solve this problem.
Drawback, you must have at least another socket controlled by the feeding mode.
Example:
Socket 8: Return
Socket 7: Skimmer
On Ragen, you only select socket 8 as being part of feeding mode.
Then you will use a custom function called DelayStart(), which I'll create tonight.
The sole purpose of this function is going to be monitoring socket 8 and turn on socket 7 after x seconds everytime socket 8 turns on.
Does it make sense?
Drawback, you must have at least another socket controlled by the feeding mode.
Example:
Socket 8: Return
Socket 7: Skimmer
On Ragen, you only select socket 8 as being part of feeding mode.
Then you will use a custom function called DelayStart(), which I'll create tonight.
The sole purpose of this function is going to be monitoring socket 8 and turn on socket 7 after x seconds everytime socket 8 turns on.
Does it make sense?
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
Sorry to raise expectations, but this idea also did not work on my tests.
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
This is not a real solution for the problem, but it can be used while Curt is working on a more permanent solution:
What this function does is actually monitor for the last time it was called. If it was last called 100 seconds ago, it probably means it was in feeding/water change mode. This triggers the delay start.
The problem you will encounter is if you go to the setup menu and spend more than 100 seconds in one of the setup screens, it will be counted towards the 100 seconds and when you exit from the setup screen, you will see the port being turned off and start the delay too.
I hope this can help some.
Attached is a code with an example on how to use it.
Code: Select all
void DelayStart(byte DelayedPort, int SecsDelay)
{
static unsigned long lastCheck;
static unsigned long lastOff;
if (lastCheck!=0 && (now()-lastCheck>100))
{
lastOff=now();
ReefAngel.Relay.Off(DelayedPort);
}
lastCheck=now();
if (lastOff!=0 && now()-lastOff>SecsDelay)
{
ReefAngel.Relay.On(DelayedPort);
}
}
The problem you will encounter is if you go to the setup menu and spend more than 100 seconds in one of the setup screens, it will be counted towards the 100 seconds and when you exit from the setup screen, you will see the port being turned off and start the delay too.
I hope this can help some.
Attached is a code with an example on how to use it.
- Attachments
-
- DelayStart.pde
- (1.07 KiB) Downloaded 463 times
Roberto.
Re: How do I code delayed start from feed mode, i.e. for ski
Here is an old shot of my sump / filtration area. I'll try to take a more recent photo next week to show you, but you should at least get the idea. The major difference is the divider on the far right has been removed and there is not a refugium anymore. It's just a larger reservoir area.alexwbush wrote:I'm curious to see your setup Curt.
And it's not as full as it gets. This shot was taken while I was still testing out the water levels in my tank before I got it running. The water comes up just below the black rim after everything is shut off. So no worries about anything skimming too much because the water level stays below where all the skimming action takes place.
curt
Re: How do I code delayed start from feed mode, i.e. for ski
That code would have to be modified to be Greater Than the delay then turn on the port, otherwise it would only be on while the delay is in effect. The delay would be in minutes (just like with the MHLights). If you didn't want a minute delay, then you would need to remove the timer variable and place the Delay variable back in the if statement. So it should be changed to this:lucaeff wrote:Hello everyone
I would very delay on a skimmer in the event of restart after blackout we tried with this, What do you say?
void RitardoSkimmer(byte SKPort, byte SKDelay)
{
if (now()-RAStart < SKDelay) ReefAngel.Relay.On(SKPort);
}
Code: Select all
void DelayedOn(byte Port, byte Delay)
{
unsigned int timer = Delay;
timer *= SECS_PER_MIN;
if ( (now() - RAStart) > timer ) ReefAngel.Relay.On(Port);
else ReefAngel.Relay.Off(Port); // this is probably overkill but ensures it is off
}
Code: Select all
void loop()
{
// ... other stuff here
DelayedOn(Port4, 2); // 2 minute delay for starting up Port4
// ... other stuff here
}
This solution, however, would not work for the entering and exiting of feeding mode and water change mode. It would only work on power outage / loss. I still have to work on the feeding mode & water change mode solutions. It will require some other changes and modifications and I have some ideas in my head, so be patient please....
curt
Re: How do I code delayed start from feed mode, i.e. for ski
I created a poll to see what delay interval would be most useful for people: seconds or minutes.
Your feedback will help determine how it gets coded. All feedback is welcomed.
Here's a link to the poll.
http://forum.reefangel.com/viewtopic.php?f=9&t=30
Your feedback will help determine how it gets coded. All feedback is welcomed.
Here's a link to the poll.
http://forum.reefangel.com/viewtopic.php?f=9&t=30
Re: How do I code delayed start from feed mode, i.e. for ski
thanks guys, I know you're working hard, I'll wait patiently
Re: How do I code delayed start from feed mode, i.e. for ski
Curt, mine rises about 4-6" as well in my skimmer compartment when the return is shut off. The change in water level will cause more skimmate and eventually start wet skimming. I'm surprised you hadn't run into this issue. I actually use mine now for water changes. I'll let it wet skim out what I want to change out, then unplug my ATO and plug in a pump to pump in new saltwater. I'd like to automate the process, but it's much easier than it used to be before anyhow.
Re: How do I code delayed start from feed mode, i.e. for ski
I just finished coding in a DelayedOn function.
I've tested it and it appears to be working the way it should but will need some further testing to be certain. It currently has a minute delay and is in my latest code on my github account (https://github.com/curtbinder/ReefAngel).
I also have a PDE file created that shows how to use the function. It is attached to this post.
Here is how the function is designed to work:
// Always on ports that have a delay
// These ports will delay turning on for the specified MINUTE_DELAY during the following modes:
// Controller Power Up
// Feeding Mode (if toggled during mode)
// Water Change Mode (if toggled during mode)
//
// This function needs to be placed inside loop() and not inside setup()
//
// Usage is DelayedOn(PORT, MINUTE DELAY)
So that means that at controller startup, any port that is specified as a DelayedOn port will delay starting up for the given minute delay. The Feeding and Water Change modes will only do the delay if the port was actually turned off during those modes.
Here is an example scenario:
If you don't turn off your skimmer during feeding mode but do during water change mode, the skimmer will only be delayed in turning on when the controller powers up and after leaving water change mode. Nothing happens during feeding mode because it was never turned off for feeding mode.
curt
I've tested it and it appears to be working the way it should but will need some further testing to be certain. It currently has a minute delay and is in my latest code on my github account (https://github.com/curtbinder/ReefAngel).
I also have a PDE file created that shows how to use the function. It is attached to this post.
Here is how the function is designed to work:
// Always on ports that have a delay
// These ports will delay turning on for the specified MINUTE_DELAY during the following modes:
// Controller Power Up
// Feeding Mode (if toggled during mode)
// Water Change Mode (if toggled during mode)
//
// This function needs to be placed inside loop() and not inside setup()
//
// Usage is DelayedOn(PORT, MINUTE DELAY)
So that means that at controller startup, any port that is specified as a DelayedOn port will delay starting up for the given minute delay. The Feeding and Water Change modes will only do the delay if the port was actually turned off during those modes.
Here is an example scenario:
If you don't turn off your skimmer during feeding mode but do during water change mode, the skimmer will only be delayed in turning on when the controller powers up and after leaving water change mode. Nothing happens during feeding mode because it was never turned off for feeding mode.
curt
- Attachments
-
- DelayedOn.pde
- DelayedOn function example
- (1.58 KiB) Downloaded 456 times
Re: How do I code delayed start from feed mode, i.e. for ski
Curt, your delay on function seems to work great... however, it doesn't seem to work for power losses just as the halide does. I have a float switch on my collection bucket so that if it's full, the skimmer turns off. If the float switch is triggered, then cleared, the skimer will turn off for one minute. But, I also want it to delay start if the power goes out just as the halide function does. Any suggestions?
my PDE
my PDE
Code: Select all
// Autogenerated file by RAGen (v1.0.4.92), (05/08/2011 17:03)
// RA_050811_1703.pde
//
// This version designed for v0.8.5 Beta 12 or later
/* The following features are enabled for this PDE File:
#define DisplayImages - ok
#define WavemakerSetup - ok
#define DateTimeSetup - ok
#define ATOSetup - ok, but probably not needed since the ato timeouts are not used
#define MetalHalideSetup - ok
#define DirectTempSensor - ok
#define RelayExp - ok
#define SingleATOSetup - ok, but probably not needed since the single ato function isn't used
#define StandardLightSetup -ok
#define CUSTOM_MAIN - ok
#define COLORS_PDE - ok
*/
//Custom colors
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <ReefAngel_Features.h>
#include <ReefAngel_Globals.h>
#include <ReefAngel_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <ReefAngel_EEPROM.h>
#include <ReefAngel_NokiaLCD.h>
#include <ReefAngel_ATO.h>
#include <ReefAngel_Joystick.h>
#include <ReefAngel_LED.h>
#include <ReefAngel_TempSensor.h>
#include <ReefAngel_Relay.h>
#include <ReefAngel_PWM.h>
#include <ReefAngel_Timer.h>
#include <ReefAngel_Memory.h>
#include <ReefAngel.h>
/*
#define Box1_Port1 11 Fuge LED
#define Box1_Port2 12 Carbon Reactor
#define Box1_Port3 13
#define Box1_Port4 14 Vortech
#define Box1_Port5 15 WC Heater
#define Box1_Port6 16 WC Pump
#define Box1_Port7 17 Actinic LED
#define Box1_Port8 18
*/
/*
//#include <avr/pgmspace.h>
prog_char id_label[] PROGMEM = "wolfpack1206";
prog_char probe1_label[] PROGMEM = "Water";
prog_char probe2_label[] PROGMEM = "Room";
prog_char probe3_label[] PROGMEM = "Not%20Used";
prog_char relay1_label[] PROGMEM = "ATO";
prog_char relay2_label[] PROGMEM = "Moonlight";
prog_char relay3_label[] PROGMEM = "Actinic";
prog_char relay4_label[] PROGMEM = "Halide";
prog_char relay5_label[] PROGMEM = "Sump%20Light";
prog_char relay6_label[] PROGMEM = "Powerhead";
prog_char relay7_label[] PROGMEM = "Heater";
prog_char relay8_label[] PROGMEM = "Return";
#ifdef RelayExp
prog_char relay11_label[] PROGMEM = "Fuge%20LED";
prog_char relay12_label[] PROGMEM = "Carbon%20Reactor";
prog_char relay13_label[] PROGMEM = "Not%20Used";
prog_char relay14_label[] PROGMEM = "Vortech";
prog_char relay15_label[] PROGMEM = "WC%20Heater";
prog_char relay16_label[] PROGMEM = "WC%20Pump";
prog_char relay17_label[] PROGMEM = "Actinic%20LED";
prog_char relay18_label[] PROGMEM = "Not%20Used";
#endif // RelayExp
PROGMEM const char *webbanner_items[] = {
id_label, probe1_label, probe2_label, probe3_label,
relay1_label, relay2_label, relay3_label, relay4_label,
relay5_label, relay6_label, relay7_label, relay8_label,
#ifdef RelayExp
relay11_label, relay12_label, relay13_label, relay14_label,
relay15_label, relay16_label, relay17_label, relay18_label,
#endif // RelayExp
};
*/
void DrawCustomMain()
{
// Change the 30 to adjust the horizontal position of the text on the screen, max 20-21 chars
ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, 2, "Alex's Cube");
ReefAngel.LCD.DrawDate(6, 119);
pingSerial();
ReefAngel.LCD.DrawMonitor(15, 63, ReefAngel.Params,
ReefAngel.PWM.GetDaylightValue(),
ReefAngel.PWM.GetActinicValue());
pingSerial();
// draw main relay
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 93, TempRelay);
#ifdef RelayExp
// draw 1st expansion relay
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(12, 105, TempRelay);
#endif // RelayExp
}
void DrawCustomGraph()
{
ReefAngel.LCD.DrawGraph(5, 10);
}
void setup()
{
ReefAngel.Init(); //Initialize controller
//ReefAngel.LoadWebBanner(pgm_read_word(&(webbanner_items[0])), SIZE(webbanner_items));
// Initialize and start the timer
//ReefAngel.Timer[4].SetInterval(120); // set interval to 180 seconds
//ReefAngel.Timer[4].Start();
ReefAngel.FeedingModePorts = B10101000;
//ReefAngel.WaterChangePorts = B10000010;
ReefAngel.WaterChangePorts = B00000001;
ReefAngel.OverheatShutoffPorts = B00000110;
// Ports that are always on
ReefAngel.Relay.On(Port6);
ReefAngel.Relay.On(Port8);
ReefAngel.Relay.On(Box1_Port1);
ReefAngel.Relay.On(Box1_Port2);
ReefAngel.Relay.On(Box1_Port4);
}
void loop()
{
ReefAngel.ShowInterface();
// Specific functions
// ReefAngel.SingleATOHigh(Port1);
if(ReefAngel.HighATO.IsActive())
{
ReefAngel.Relay.On(Port1);
ReefAngel.Relay.On(Box1_Port6);
}
if(ReefAngel.HighATO.IsActive()==false)
{
ReefAngel.Relay.Off(Port1);
ReefAngel.Relay.Off(Box1_Port6);
}
if(ReefAngel.LowATO.IsActive())
ReefAngel.DelayedOn(Port6, 1);
if(ReefAngel.LowATO.IsActive()==false)
ReefAngel.Relay.Off(Port6);
ReefAngel.StandardLights(Port2);
ReefAngel.MHLights(Port3);
//ReefAngel.Wavemaker1(Port4);
ReefAngel.StandardFan(Port5);
ReefAngel.StandardHeater(Port7);
ReefAngel.StandardLights(Box1_Port7,12,15,21,45);
}
Re: How do I code delayed start from feed mode, i.e. for ski
The code is exactly the same for both of the delays. DelayedOn uses a variable called LastStart and the Halides use a variable called RAStart. When the controller initializes, LastStart gets set with the value from RAStart.alexwbush wrote:Curt, your delay on function seems to work great... however, it doesn't seem to work for power losses just as the halide does.
From your code, it looks like your skimmer is on Port6. If it is, your problem is how you turn it on. If you want a DelayedOn, you cannot turn it ON in the setup. You have the ReefAngel.Relay.On(Port6) inside setup(), which turns it on every time the controller powers up.
You already have your conditional statement that checks whether or not to turn on the port. So just stop turning it on when the controller starts up and you should be fine.
curt
Re: How do I code delayed start from feed mode, i.e. for ski
great catch! I can't believe I overlooked that, thanks!