So I'm currently trying to write a subroutine that will control my CO2 injection system, turning CO2 on in the system based on (1) the light schedule, (2) the current pH reading, and (3) the on/off status of my filter. The last criterion is necessary because my CO2 reactor is inline with my canister filter, and I don't want to be injecting CO2 when the water is not flowing. This is where I'm having problems getting my code to work.
I've attached my current development ino. There are lots of broken, 'in progress' elements that are commented out (and will be the topic of future posts to the forum), but I'm hoping one of you geniuses out there in the forum hive-mind can help me understand what I'm doing wrong in the subroutine 'CO2control'.
Code: Select all
// Autogenerated file by RAGen (v1.2.2.171), (04/30/2012 14:56)
// RA_043012_1456.ino
//
// Modified (and mostly broken) by billybob, 2012
//
// This version designed for v0.9.0 or later
/* The following features are enabled for this File:
#define DisplayImages
#define VersionMenu
#define DisplayLEDPWM
#define wifi
#define WDT
#define SaveRelayState
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 9
#define PWMEXPANSION
#define CUSTOM_MAIN
#define ENABLE_EXCEED_FLAGS
#define FONT_8x8
*/
///// Header files
#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 <ReefAngel.h>
///// Global variables
// first, name our outlets
#define Aux1 1
#define Aux2 2
#define CO2 3
#define Filter 4
#define LightFans 5
#define Heater 6
#define CoolWhite 7
#define NeutralWhite 8
#define Aux1_b 1<<7
#define Aux2_b 1<<6
#define CO2_b 1<<5
#define Filter_b 1<<4
#define LightFans_b 1<<3
#define Heater_b 1<<2
#define CoolWhite_b 1<<1
#define NeutralWhite_b 1<<0
// now, name our pwm expansion ports
#define MoonLights 0
#define PWM1 1
#define PWM2 2
#define PWM3 3
#define LightFansIntake 4
#define LightFansExhaust 5
// define a few variables for our light hood fans
int FanSpeedIntake;
int FanSpeedExhaust;
///// Set up our menu and main screen
#include <avr/pgmspace.h>
prog_char menu0_label[] PROGMEM = "Feeding Mode";
prog_char menu1_label[] PROGMEM = "Water Change";
prog_char menu2_label[] PROGMEM = "Toggle Lights";
prog_char menu3_label[] PROGMEM = "Toggle CO2";
prog_char menu4_label[] PROGMEM = "Toggle Filter";
prog_char menu5_label[] PROGMEM = "Clear Overheat";
prog_char menu6_label[] PROGMEM = "Calibrate pH";
prog_char menu7_label[] PROGMEM = "Reset Watchdog";
prog_char menu8_label[] PROGMEM = "Version";
PROGMEM const char *menu_items[] = {
menu0_label, menu1_label, menu2_label, menu3_label, menu4_label, menu5_label, menu6_label, menu7_label, menu8_label };
void MenuEntry1() // Start the feeding mode
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2() // Do a water change
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3() // Toggle lights on or off
{
ReefAngel.DisplayVersion();
/*
// if one of the light strips is on, turn them all off
if (ReefAngel.Relay.Status(CoolWhite) || ReefAngel.Relay.Status(NeutralWhite))
{
ReefAngel.Relay.RelayMaskOff=CoolWhite_b;
ReefAngel.Relay.RelayMaskOff=NeutralWhite_b;
ReefAngel.Relay.RelayMaskOff=LightFans_b;
}
// otherwise, turn them all on
else
{
//if its daylight time, just turn them back to automatic to work with the schedule
if()
{
ReefAngel.Relay.RelayMask=CoolWhite_b;
ReefAngel.Relay.RelayMaskOn=NeutralWhite_b;
ReefAngel.Relay.RelayMaskOn=LightFans_b;
}
else //else turn it on at some predefined level, say 20%
{
ReefAngel.Relay.RelayMaskOn=CoolWhite_b;
ReefAngel.PWM
ReefAngel.Relay.RelayMaskOn=NeutralWhite_b;
ReefAngel.Relay.RelayMaskOn=LightFans_b;
}
}
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
*/
}
void MenuEntry4() // Toggle the CO2 system on or off
{
if (ReefAngel.Relay.Status(CO2)) ReefAngel.Relay.Off(CO2);
else ReefAngel.Relay.On(CO2);
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}
void MenuEntry5() // Toggle the filter on or off
{
if (ReefAngel.Relay.Status(Filter)) ReefAngel.Relay.Off(Filter);
else ReefAngel.Relay.On(Filter);
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}
void MenuEntry6() // Clear the overheat
{
ReefAngel.OverheatClear();
}
void MenuEntry7() // Calibrate pH
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8() // Reset the Watchdog
{
wdt_reset();
}
void MenuEntry9() // Version
{
ReefAngel.DisplayVersion();
}
void DrawCustomMain()
{
// the graph is drawn/updated when we exit the main menu &
// when the parameters are saved
ReefAngel.LCD.DrawDate(6, 112);
pingSerial();
#if defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor(15, 60, ReefAngel.Params,
ReefAngel.PWM.GetDaylightValue(), ReefAngel.PWM.GetActinicValue());
#else // defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor(15, 60, ReefAngel.Params);
#endif // defined DisplayLEDPWM && ! defined RemoveAllLights
pingSerial();
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 93, TempRelay);
}
void DrawCustomGraph()
{
ReefAngel.LCD.DrawGraph(5, 5);
}
///// Define a few subroutines and functions that will be called from the main program
void CO2Control(byte CO2Relay, int LowPH, int HighPH, int DeltaMinOn, int DeltaMinOff)
/* Control our CO2 input. CO2 is only turned on if the following three conditions are met:
1) Lights must be on. You can offset the CO2 on/off times relative to the light
on/off times using DeltaMinOn and DeltaMinOff: DeltaMinOn < 0 means the CO2 goes
on that many minutes before lights on, DeltaMinOn > 0 delays the CO2 on time relative
to the light schedule. Similarly for DeltaMinOff relative to lights-out time.
2) pH must be in the appropriate range. Here CO2 is turned on if the pH is above
HighPH and turned off when it goes below LowPH.
3) Filter must be running, since our CO2 reactor is inline with our canister filter
and we don't want to vaporlock the reactor by pumping CO2 in there without the
water flowing.
Code is based on examples given in the ReefAngel forum: viewtopic.php?f=9&t=455,
viewtopic.php?f=12&t=952, viewtopic.php?f=15&t=895&start=50, viewtopic.php?f=12&t=756,
and viewtopic.php?f=7&t=927, among others.
*/
{
int LightsOnMin;
int LightsOffMin;
int CurrentMin;
boolean isCO2OnTime;
// No pH reading? return.
if (ReefAngel.Params.PH == 0) return; //example in viewtopic.php?f=9&t=455
// Figure out whether the CO2 should be on, relative to the light schedule. Example
// of NumMins given in viewtopic.php?f=12&t=952, example of internal memory
// usage given in viewtopic.php?f=15&t=895&start=50
LightsOnMin = NumMins(InternalMemory.StdLightsOnHour_read(),
InternalMemory.StdLightsOnMinute_read());
LightsOffMin = NumMins(InternalMemory.StdLightsOffHour_read(),
InternalMemory.StdLightsOffMinute_read());
CurrentMin = NumMins(hour(),minute());
isCO2OnTime = ((CurrentMin - LightsOnMin >= DeltaMinOn) &&
(CurrentMin - LightsOffMin <= DeltaMinOff));
// Only turn on CO2 if it's appropriate relative to the lights, and if the filter is running
if (isCO2OnTime && ReefAngel.Relay.Status(Filter))
{
// If sensor PH <= HighPH - turn on the CO2
if (ReefAngel.Params.PH >= HighPH) ReefAngel.Relay.On(CO2Relay);
// If sensor PH < LowPH - turn off the CO2
if (ReefAngel.Params.PH < LowPH) ReefAngel.Relay.Off(CO2Relay);
}
else ReefAngel.Relay.Off(CO2Relay);
}
///// Run all this stuff once at power on
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
// Initialize the custom menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Filter_b | CO2_b | Heater_b;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Filter_b | CO2_b | Heater_b;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = CoolWhite_b | NeutralWhite_b;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = LightFans_b | CoolWhite_b | NeutralWhite_b;
// Ports that are always on
ReefAngel.Relay.On(Aux1);
ReefAngel.Relay.On(Aux2);
//ReefAngel.Relay.On(Filter);
// How do we assign the temperature probes?
ReefAngel.OverheatProbe = T1_PROBE;
ReefAngel.TempProbe = T3_PROBE;
}
///// Main Program
void loop()
{
// Specific functions that use Internal Memory values
//ReefAngel.StandardLights(CO2);
ReefAngel.StandardLights(LightFans);
ReefAngel.StandardHeater(Heater);
ReefAngel.StandardLights(CoolWhite);
ReefAngel.StandardLights(NeutralWhite);
// PWMSlope based on Internal Memory values for Standard Lights
ReefAngel.PWM.ActinicPWMSlope();
ReefAngel.PWM.DaylightPWMSlope();
//Set some default levels on the PWM module. Arguments of PWMSlope are h-on, m-on, h-off,
// m-off, start-%, end-%, slope duration, start %.
//ReefAngel.PWM.SetChannel(0,PWMSlope(8,30,21,0,15,100,60,15));
//ReefAngel.PWM.SetChannel(1,PWMSlope(8,30,21,0,15,100,60,15));
//ReefAngel.PWM.SetChannel(2,PWMSlope(8,30,21,0,15,100,60,15));
//ReefAngel.PWM.SetChannel(3,PWMSlope(8,30,21,0,15,100,60,15));
//ReefAngel.PWM.SetChannel(4,PWMSlope(8,30,21,0,15,100,60,15));
//ReefAngel.PWM.SetChannel(5,PWMSlope(8,30,21,0,15,100,60,15));
//See if we can set the pwm light fans to a speed appropriate to the temperature,
// as in http://forum.reefangel.com/viewtopic.php?f=3&t=1170
//FanSpeedExhaust=map(ReefAngel.Params.Temp[T1_PROBE],1000,1400,0,100); // Off below 80, max at 120
//FanSpeedIntake=constrain(FanSpeedIntake,0,100);
//ReefAngel.PWM.SetChannel(LightFanIntake,FanSpeedIntake)
//FanSpeedExhaust=map(ReefAngel.Params.Temp[T1_PROBE],1000,1400,0,100); // Off below 100, max at 140
//FanSpeedExhaust=constrain(FanSpeedExhaust,0,100);
//ReefAngel.PWM.SetChannel(LightFanExhaust,FanSpeedExhaust)
//Turn on our moonlight?
ReefAngel.PWM.SetChannel(MoonLights,MoonPhase());
// Do we need CO2? turn on if pH>=6.8, off if pH<=6.5, during the period 30 minutes before
// lights on to 30 minutes before lights off.
CO2Control(CO2,650,680,-30,-30);
// This should always be the last line
ReefAngel.ShowInterface();
}
However, the injection system doesn't seem to be responding to whether or not the filter is running. It turns off with the filter in, for example, the water change mode. But if I mask the filter 'off' in the RA client or iphone app, the regulator keeps happily pumping CO2 into the reactor, eventually leading to a vaporlock condition.
I think I must be misunderstanding how the Relay.Status subroutine works, or else there's a problem with the logic in my CO2Control routine that I've been too dense to see for myself. Does anything obviously wrong jump out at anyone else out there?
--many thanks...