linking CO2 injection to filter on/off status

Do you have a question on how to do something.
Ask in here.
Post Reply
billybob
Posts: 21
Joined: Tue Apr 17, 2012 2:47 pm
Location: Colorado

linking CO2 injection to filter on/off status

Post by billybob »

Hi Folks-- I've been integrating my reef angel into my planted freshwater aquarium setup, and trying to customize the RAGen-generated code to deal with some specifics of my situation. It turns out that I'm not much of a coder, and the whole process has been a little overwhelming. That said, there's some great examples on these forums, and I've been pleasantly surprised to find that I've been able to integrate many of the approaches posted in these lists and get more-or-less the behavior I was intending, albeit not always with a full understanding of why sections of code worked as they did.

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();
}
The code seems to work fine in terms of the on-off times and offsets, and in terms of the pH criteria (although I have not yet run the injection system down to the LowPH value, so I'm not 100% sure if that is working or not).

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...
Last edited by billybob on Sat May 05, 2012 7:35 pm, edited 2 times in total.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: linking CO2 injection to filter on/off status

Post by rimai »

Actually, I was able to check it out and indeed it is what I mentioned above.
This is in the libraries:

Code: Select all

 
 inline boolean Status(byte Port) { return bitRead(RelayData,Port-1); }
So, we'll have to come up with a function for you until it gets fixed.
Sent from my SPH-D700 using Tapatalk 2
Roberto.
billybob
Posts: 21
Joined: Tue Apr 17, 2012 2:47 pm
Location: Colorado

Re: linking CO2 injection to filter on/off status

Post by billybob »

erm, I must have missed something. which 'above'? did a post get omitted from the discussion?

at any rate, I'm still not sure what the problem is.

--many thanks...
Last edited by billybob on Sat May 05, 2012 7:35 pm, edited 1 time in total.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: linking CO2 injection to filter on/off status

Post by rimai »

Wow. I don't know where my first post went :(
I was using my phone yesterday. I guess something got screwed up and it never got posted.
Anyway, I said that your code looks awesome and I don't see anything wrong.
The problem I suspected was a bug in the libraries and I was able to confirm with this line on the libraries:

Code: Select all

inline boolean Status(byte Port) { return bitRead(RelayData,Port-1); }
This line indicates that the Status() function only return the status of the "Auto" mode and everytime you mask a relay, it does not apply the mask.
That's why your code is not turning the CO2 off when you mask the relay.
I'll have this function fixed for next release.
But in the mean time, you will need a work around.
Let me work on it and I'll post shortly
Roberto.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: linking CO2 injection to filter on/off status

Post by rimai »

I think this should work for you:

Code: Select all

boolean RelayStatus(byte Port)
{
  return bitRead((ReefAngel.Relay.RelayData & ReefAngel.Relay.RelayMaskOff) | ReefAngel.Relay.RelayMaskOn,Port-1); 
}
Just replace all ReefAngel.Relay.Status() functions in your code with RelayStatus() and let me know if it works.
I'll include it in for the next update if it works.
Roberto.
billybob
Posts: 21
Joined: Tue Apr 17, 2012 2:47 pm
Location: Colorado

Re: linking CO2 injection to filter on/off status

Post by billybob »

Works perfectly. Thanks Roberto!
Post Reply