Suddenly my AWC code stopped working... Why?

Share you PDE file with our community

Posts: 80
Joined: Thu Mar 08, 2012 5:11 pm
Location: Oakland, California
PostPosted: Thu Apr 02, 2015 12:20 am
Hi everyone,

I'm not a programmer by any means, but I was pretty happy with my code about 2 weeks ago. It did just about everything I wanted it to, including automatic water changes (AWCs). I was so comfortable with it, that I left town for 10 days with my tank totally in the hands of my RA. I returned to find most things OK, but some corals looking a bit sad. I checked everything out, and my parameters were fine, except my pH read 1.08. Since all my rock and corals weren't dissolving in a tank full of acid, I figured there was something that had gone wrong with my pH meter. While I was fiddling with that, an AWC initiated. But something went wrong. The first pump in my AWC sequence turned on for about 0.5 seconds, and then shut off. That was the end of my AWC. I spent a lot of time trying to make automatic checks that would email me if an AWC failed, but they didn't catch this. I'm not sure how long the tank was without an AWC, although as I said, params were fine. My concern is this: everything was working perfectly for a couple weeks before I left town. Now they aren't even after rebooting and reuploading the code. What gives? Any ideas? Below is my code. Thanks in advance for your help!

AWC code:
Code: Select all
//This function will send me an email alert if an AWC fails.
void AWCAlert()
{
  time_t t = now();
  static WiFiAlert AWCReport;
  char msg[80];
  char h[3];
  char m[3];
  char s[3];
  char d[3];
  char mo[3];
  char y[5];
 
  dtostrf((float) hour(t), 2, 0, h);
  dtostrf((float) minute(t), 2, 0, m);
  dtostrf((float) second(t), 2, 0, s);
  dtostrf((float) day(t), 2, 0, d);
  dtostrf((float) month(t), 2, 0, mo);
  dtostrf((float) year(t), 4, 0, y);
  sprintf(msg, "The AWC at %s:%s:%s on %s/%s/%s, has failed.", h, m, s, mo, d, y);
  AWCReport.Send(msg);
}
//This function will check to make sure the water level in the sump is at the high float before initiating an AWC.
int AWCATOCheck()
{
  if (!ReefAngel.Relay.Status(returnPump))  //If the return pump is not on, don't run an AWC and return 0 (false).
  {
    return 0;
  }
 
  if (!ReefAngel.HighATO.IsActive())
  {
    AWCTimer = now();
   
    ReefAngel.Relay.On(ATOPump);  //Fill the return section to the high float with RODI.
   
    return 2;
  }
  else
    return 1;
}
void loop()
{
  //Start an AWC once per day at 12 noon if an AWC hasn't happened since midnight and there hasn't been an ATO timeout.
  AWCt = now();
  if (!AWCFail && (hour(AWCt) == AWCHour) && (minute(AWCt) == AWCMinute) && !AWCToday && !AWC && !ReefAngel.isATOTimeOut())
  {
    AWC = true;  //Sets the AWC control variable to true.
   
    static WiFiAlert tempAlert;
    char tempMsg[] = "An AWC occured.";
    tempAlert.Send(tempMsg);
  }
   
  //Reset the automatic water change variable and AWC and dosing counts every day at midnight.  Also add the day's AWC count to the total AWC count.
  if (hour(AWCt) == 0 && minute(AWCt) == 0 && AWCToday)
  {
    AWCToday = false;
    ReefAngel.CustomVar[Var_AWCCountTotal] += ReefAngel.CustomVar[Var_AWCCountToday];
    ReefAngel.CustomVar[Var_AWCCountToday] = 0;
    ReefAngel.CustomVar[Var_CDosesToday] = 0;
    ReefAngel.CustomVar[Var_NO3DosesToday] = 0;
  }
   
  //Start the chain of AWC functions.
  if (AWC && !AWCToday)
    if (!ReefAngel.IO.GetChannel(0))
    {
      AWCToday = true;
      ReefAngel.CustomVar[Var_AWCCountToday] += 1;
      wp25Timer = now();
      wp25On = false;
    }
    else
    {
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCOverflowFail] = 1;
      AWC = false;
      wp25On = true;
      AWCAlert();
    }
  if (((now() - wp25Timer) >= 300) && AWCToday && AWC)
  {
    AWC = false;
    AWCCheck = AWCATOCheck();
  }
  if (AWCCheck == 2)
  {
    if (ReefAngel.HighATO.IsActive())
    {
      ReefAngel.Relay.Off(ATOPump);
     
      AWCCheck = 1;
    }
    else if ((now() - AWCTimer) >= ATOTimeOut)
    {
      ReefAngel.Relay.Off(ATOPump);
     
      bitSet(ReefAngel.AlertFlags,ATOTimeOutFlag);  //Trigger ATO timeout flag.
     
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCATOFail] = 1;
      AWCAlert();
      wp25On = true;
      AWCCheck = 0;
    }
  }
  else if (AWCCheck == 1)
  {
    ATOOn = false;
   
    AWCTimer = now();
   
    ReefAngel.Relay.On(AWCDrainPump);

    if (ReefAngel.LowATO.IsActive())
    {
      ReefAngel.Relay.Off(AWCDrainPump);
     
      AWCCheck = 0;
     
      AWCDrainCheck = 1;
    }
    else if ((now() - AWCTimer) >= AWCDrainSecs)
    {
      ReefAngel.Relay.Off(AWCDrainPump);
     
      AWCCheck = 0;
      AWCDrainCheck = 0;
      ATOOn = true;
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCDrainFail] = 1;
      AWCAlert();
      wp25On = true;
    }
  }
  if (AWCDrainCheck == 1)
  {
    AWCTimer = now();
   
    ReefAngel.Relay.On(AWCFillPump);
   
    if (ReefAngel.HighATO.IsActive())
    {
      ReefAngel.Relay.Off(AWCFillPump);
     
      ATOOn = true;
      wp25On = true;
      AWCDrainCheck = 0;
    }
    else if ((now() - AWCTimer) >= AWCFillSecs)
    {
      ReefAngel.Relay.Off(AWCFillPump);
     
      ATOOn = true;
      AWCDrainCheck = 0;
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCFillFail] = 1;
      AWCAlert();
      wp25On = true;
    }
  }
 
  //Drain AWC overflow reservoir when water is in it.
  if (ReefAngel.IO.GetChannel(0) && !ReefAngel.Relay.Status(AWCOverflow) && !AWCFail)
  {
    ReefAngel.Relay.On(AWCOverflow);
   
    AWCOverflowTimer = now();
  }
  else if (!ReefAngel.IO.GetChannel(0) && ReefAngel.Relay.Status(AWCOverflow))
    ReefAngel.Relay.Off(AWCOverflow);
  else if (((now() - AWCOverflowTimer) >= AWCOverflowSecs) && ReefAngel.Relay.Status(AWCOverflow))
  {
    ReefAngel.Relay.Off(AWCOverflow);
    AWCFail = true;
    ReefAngel.CustomVar[Var_AWCOverflowFail] = 1;
    AWCAlert();
  }


My entire ino:
Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DCPump.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <ReefAngel.h>
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <WiFiAlert.h>
#include <avr/pgmspace.h>

//Ports assignment:
//Port1  - Daylight fixture and QT light - need 2 outlets
#define daylightFixture Port1
//Port2  - Actinic fixture
#define actinicFixture Port2
//Port3  - Refugium fixture
#define fugeFixture Port3
//Port4  - DT heaters - "http://www.reefangel.com/Support.Relay-function-StandardHeater.ashx" - need 2 outlets
#define heater Port4
//Port5  - Skimmer Recirculation Pump
#define skimmerPump Port5
//Port6  - Calcium Reactor Recirculation Pump
#define CaRXPump Port6
//Port7  - Manifold Pump
#define manifoldPump Port7
//Port8  - Return Pump, wavebox, both WP-25s, Refugium Flow, QT flow, and QT filter - need 8 outlets
#define returnPump Port8

//Port9 (Box1_Port1)  - Eventually will be used for automatic dry food feeder control - "http://wamas.org/forums/blog/13/entry-46-diy-automatic-feeder-for-aquacontroller/"
#define doseC Box1_Port1
//Port10 (Box1_Port2) - AWC overflow reservoir drain pump.
#define AWCOverflow Box1_Port2
//Port11 (Box1_Port3) - Standard ATO - "http://forum.reefangel.com/viewtopic.php?f=7&t=240"
#define ATOPump Box1_Port3
//Port12 (Box1_Port4) - CO2 Solenoid
#define doseNO3 Box1_Port4
//Port13 (Box1_Port5) - Temporary control of QT lights.
#define QTFixture Box1_Port5
//Port14 (Box1_Port6) - QT heater
#define QTheater Box1_Port6
//Port15 (Box1_Port7) - AWC fill pump and QT overflow reservoir drain pump - need 2 outlets
#define AWCFillPump Box1_Port7
//Port16 (Box1_Port8) - AWC drain pump
#define AWCDrainPump Box1_Port8

//Define portal variables.
#define Var_AWCCountTotal     0
#define Var_AWCCountToday     1
#define Var_AWCDrainFail      2
#define Var_AWCFillFail       3
#define Var_AWCOverflowFail   4
#define Var_AWCATOFail        5
#define Var_CDosesToday    6
#define Var_NO3DosesToday  7

//Change these values as needed. They are the threshold values for the low and high temperatures, respectively.
int lowTempValue = 718;
int highTempValue = 725;
 
//These variables help keep count of the number of dosing events that happen each day.
boolean carbonDosing = false;
boolean NO3Dosing = false;
 
//defnie dimming channels to control the left wp-25 and actinic dimming channel to control the right wp-25
#define leftWp25 SetDaylight
#define rightWp25 SetActinic

////// Place global variable code below here
//The following variables control when the lights first come on during a 24 hour period starting at midnight.
//After that, lights stay on for 6 hours, turn off for 6 hours, come on for 6 hours, and turn off for 6 hours.
int lightsOnHour = 6;
int lightsOnMin = 0;
boolean mainLightsOn = false;
boolean fugeLightsOn = false;

//The following variables will toggle the state of the relays controling the main and fuge lights, respectively.
boolean toggleMainLights = false;
boolean toggleFugeLights = false;

//The following variable controls acclimation mode, in which the return section is topped off, then the ATO switches
//to use salt water instead of RODI to replace water lost to acclimation of new arrivals.
boolean acclimationMode = false;

//The following variables control the ReefAngel.StandardATO() function to work with either RODI or saltwater and to have a timeout of either 300 or 60 seconds.
byte fillPump = ATOPump;
int ATOTimeOut = 600;

//The following variables control when automatic water changes occur.
int AWCHour = 22;
int AWCMinute = 0;

//These variables will be used to keep track of the progress of an AWC.
int AWCCheck = 0;
boolean AWCDrainCheck = false;
boolean AWCFail = false;

//This variable will trigger an AWC().
boolean AWC = false;

//This variable will be set to true if a water change has been done since the last midnight and will be set to false at midnight.
boolean AWCToday = false; 

//This vairable will store the current time for comparisons on when to initialize an AWC.
time_t AWCt;

//This variable will be a backup timer for AWC.
static unsigned long AWCTimer = 0;

//This variable will be a timer for calcium reactor pH checks
static unsigned long pHTimer = 0;

//This variable will be a timer for starting the skimmer pump 5 minutes after the return pump is started in order to preven the skimmer cup from overflowing.
static unsigned long returnTimer = 0;

//This variable will be a timer for making sure the AWC overflow pump doesn't run dry in case the overflow reservoir float switch fails.
static unsigned long AWCOverflowTimer = 0;

//These vairiables will turn on and off the WP-25s for AWC and feeding.
boolean wp25On = false;
static unsigned long wp25Timer = 0;

//These variables will control how long the AWC drain, fill, and overflow drain pumps should be on (in seconds) in case of float switch failure.
int AWCDrainSecs = 180;
int AWCFillSecs = 180;
int AWCOverflowSecs = 3600;

//This variable will control whether ATO is working.
boolean ATOOn = true;

//This function will send me an email alert if an AWC fails.
void AWCAlert()
{
  time_t t = now();
  static WiFiAlert AWCReport;
  char msg[80];
  char h[3];
  char m[3];
  char s[3];
  char d[3];
  char mo[3];
  char y[5];
 
  dtostrf((float) hour(t), 2, 0, h);
  dtostrf((float) minute(t), 2, 0, m);
  dtostrf((float) second(t), 2, 0, s);
  dtostrf((float) day(t), 2, 0, d);
  dtostrf((float) month(t), 2, 0, mo);
  dtostrf((float) year(t), 4, 0, y);
  sprintf(msg, "The AWC at %s:%s:%s on %s/%s/%s, has failed.", h, m, s, mo, d, y);
  AWCReport.Send(msg);
}

//These variables will control automatic feeding.
boolean canFeed = false;  //Controls when feeding is allowed.
const int feedsPerDay = 4;  //Controls number of feedings per 24 hours; must be an even number.
const int feedDelay = 30;  //Delay in minutes after lights come on and before lights go out when feeding is allowed.
int feedsToday = 0;  //Keeps track of the number of feedings today.
static unsigned long feedTimer = 0;  //Compared with feedTime to determine when to feed.
const int feedingPause = 900;  //Controls how long the circulation pumps will be shut off during feeding.
static unsigned long feedingPauseTimer = 0;  //Times how long the circulation pumps have been off during feeding.
boolean feeding = false;  //True while feeding.
time_t feedt;  //Used to store the current time for evaluations.

//This function will control automatic feeding.
/*void feed()
{
  time_t t = now();
 
  if (hour(t) == 0 && minute(t) == 0)
    feedsToday = 0;
   
  if (canFeed && feedsToday < feedsPerDay)
  {
    switch (now() - feedTimer)
    {
      case (feedDelay * 60):
        feedsToday += 1;
        ReefAngel.FeedingModeStart();
        while (true)
        {
          if ((now() - feedTimer) == (120 + (feedDelay * 60)))
            ReefAngel.Relay.On(feeder);
          if ((now() - feedTimer) == (121 + (feedDelay * 60)))
          {
            ReefAngel.Relay.Off(feeder);
            break;
          }
        }
        break;
      case (21600 - (feedDelay * 60)):
        feedsToday += 1;
        ReefAngel.FeedingModeStart();
        while (true)
        {
          if ((now() - feedTimer) == (21720 - (feedDelay * 60)))
            ReefAngel.Relay.On(feeder);
          if ((now() - feedTimer) == (21721 - (feedDelay * 60)))
          {
            ReefAngel.Relay.Off(feeder);
            break;
          }
        }
        break;
    }
  }
}*/

[u][b]//This function will check to make sure the water level in the sump is at the high float before initiating an AWC.
int AWCATOCheck()
{
  if (!ReefAngel.Relay.Status(returnPump))  //If the return pump is not on, don't run an AWC and return 0 (false).
  {
    return 0;
  }
 
  if (!ReefAngel.HighATO.IsActive())
  {
    AWCTimer = now();
   
    ReefAngel.Relay.On(ATOPump);  //Fill the return section to the high float with RODI.
   
    return 2;
  }
  else
    return 1;
}[/b][/u]

//This function will be called once a minute and will check the pH in the calcium reactor and turn off or on the solenoid accordingly
/*void pHControl()
{
  if (!ReefAngel.Relay.Status(solenoid) && ReefAngel.Params.PH > 670)
  {
    ReefAngel.Relay.On(solenoid);
  }
 
  if (ReefAngel.Relay.Status(solenoid) && ReefAngel.Params.PH < 660)
  {
    ReefAngel.Relay.Off(solenoid);
  }
}*/

//Custom Menu Code
prog_char menu0_label[] PROGMEM = "Manual Feed";
prog_char menu1_label[] PROGMEM = "Start AWC";
prog_char menu2_label[] PROGMEM = "Acclimation Mode";
prog_char menu3_label[] PROGMEM = "Toggle DT Lights";
prog_char menu4_label[] PROGMEM = "Toggle Fuge Lights";
prog_char menu5_label[] PROGMEM = "Clear Alerts & Vars";
prog_char menu6_label[] PROGMEM = "pH Calibration";
prog_char menu7_label[] PROGMEM = "Toggle All Pumps";
PROGMEM const char *menu_items[] = {
menu0_label, menu1_label, menu2_label, menu3_label, menu4_label,
menu5_label, menu6_label, menu7_label
};

void MenuEntry1()
{
  feedsToday += 1;
  wp25On = false;
  feedingPauseTimer = now();
  feeding = true;
}
void MenuEntry2()
{
  if (!ReefAngel.IO.GetChannel(0))
  {
    AWCToday = false;
    AWC = true;
  }
}
void MenuEntry3()
{
  if (acclimationMode == false)
  {
    if (!ReefAngel.HighATO.IsActive())
    {
      AWCTimer = now(); 
   
      ReefAngel.Relay.On(ATOPump);  //Fill the return section to the high float with RODI.
   
      while (true)
      {
        if (ReefAngel.HighATO.IsActive())
        {
          ReefAngel.Relay.Off(ATOPump);  //Turn off the RODI pump once the high float is triggered.
     
          break;
        }
        else if ((now() - AWCTimer) >= ATOTimeOut)
        {
          ReefAngel.Relay.Off(ATOPump);  //Turn off the RODI pump.
       
          bitSet(ReefAngel.AlertFlags,ATOTimeOutFlag);  //Trigger ATO timeout flag.
         
          break;
        }
      }
    }
   
    if (!ReefAngel.isATOTimeOut())
    {
      acclimationMode = true;
      fillPump = AWCFillPump;
      ATOTimeOut = AWCFillSecs;
    }
  }
  else
  {
    acclimationMode = false;
    fillPump = ATOPump;
    ATOTimeOut = 600;
  }
}
void MenuEntry4()
{
  if (toggleMainLights)
    toggleMainLights = false;
  else
    toggleMainLights = true;
   
  if (mainLightsOn)
  {
    mainLightsOn = false;
    ReefAngel.Relay.Off(daylightFixture);
    ReefAngel.Relay.Off(actinicFixture);
  }
  else
  {
    mainLightsOn = true;
    ReefAngel.Relay.On(daylightFixture);
    ReefAngel.Relay.On(actinicFixture);
  }
}
void MenuEntry5()
{
  if (toggleFugeLights)
    toggleFugeLights = false;
  else
    toggleFugeLights = true;
   
  if (fugeLightsOn)
  {
    fugeLightsOn = false;
    ReefAngel.Relay.Off(fugeFixture);
  }
  else
  {
    fugeLightsOn = true;
    ReefAngel.Relay.On(fugeFixture);
  }
}
void MenuEntry6()
{
  //Clear portal variables.
  ReefAngel.CustomVar[Var_AWCCountTotal] = 0;
  ReefAngel.CustomVar[Var_AWCCountToday] = 0;
  ReefAngel.CustomVar[Var_AWCDrainFail] = 0;
  ReefAngel.CustomVar[Var_AWCFillFail] = 0;
  ReefAngel.CustomVar[Var_AWCOverflowFail] = 0;
  ReefAngel.CustomVar[Var_AWCATOFail] = 0;
 
  //Clear AWC control variables.
  AWC = false;
  AWCToday = false;
  AWCFail = false;
  AWCCheck = 0;
  AWCDrainCheck = 0;
 
  //Clear ATO timeout.
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry7()
{
  ReefAngel.StartSetupCalibrateChoicePH();
}
void MenuEntry8()
{
  ReefAngel.WaterChangeModeStart();
}

void DrawCustomMain()
{
  // the graph is drawn/updated when we exit the main menu & when the parameters are saved
  ReefAngel.LCD.DrawDate(6, 120);

  pingSerial();

  byte mainTempColor;
  //To make changes for each temp sensor, change the value of tempColor to be the appropriate color
  if ( ReefAngel.Params.Temp[T1_PROBE] < lowTempValue ) { mainTempColor = COLOR_BLUE; }
  else if ( ReefAngel.Params.Temp[T1_PROBE] > highTempValue ) { mainTempColor = COLOR_RED; }
  else { mainTempColor = COLOR_SEAGREEN; }
  //Display the T1 Main System temperature starting at 1, 64
  ReefAngel.LCD.DrawText(mainTempColor, DefaultBGColor, 1, 64, "MainTemp:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], mainTempColor, 56, 64, 10);
 
  byte QTTempColor;
  //To make changes for each temp sensor, change the value of tempColor to be the appropriate color
  if ( ReefAngel.Params.Temp[T3_PROBE] < lowTempValue ) { QTTempColor = COLOR_BLUE; }
  else if ( ReefAngel.Params.Temp[T3_PROBE] > highTempValue ) { QTTempColor = COLOR_RED; }
  else { QTTempColor = COLOR_SEAGREEN; }
  //Display the T3 QT temperature starting at 1, 74
  ReefAngel.LCD.DrawText(QTTempColor, DefaultBGColor, 1, 74, "QTTemp:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T3_PROBE], QTTempColor, 46, 74, 10);
 
  //Display the T2 Room temperature starting at 1, 84
  ReefAngel.LCD.DrawText(COLOR_BLACK, DefaultBGColor, 1, 84, "RoomTemp:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T2_PROBE], COLOR_BLACK, 56, 84, 10);

  //Change these values as needed. They are the threshold values for the low and high pH, respectively.
  int lowpHValue = 660;
  int highpHValue = 670;
  byte pHColor;
  //Depending upon the temperature, change the value of pHColor to be the appropriate color
  if ( ReefAngel.Params.PH < lowpHValue ) { pHColor = COLOR_YELLOW; }
  else if ( ReefAngel.Params.PH > highpHValue ) { pHColor = COLOR_MAGENTA; }
  else { pHColor = COLOR_SEAGREEN; }
  //Display the pH starting at 88, 74
  ReefAngel.LCD.DrawText(pHColor, DefaultBGColor, 88, 74, "pH:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, pHColor, 105, 74, 100);

  pingSerial();

  //Display relay box 1 ports with status at 12,94
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 94, TempRelay);

  //Display relay box 2 ports with status at 12,106
  TempRelay = ReefAngel.Relay.RelayDataE[0];
  TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
  TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
  ReefAngel.LCD.DrawOutletBox(12, 106, TempRelay);
}

void DrawCustomGraph()
{
  //Print "Gabe's Grad Reef" at the top of the screen
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 18, 2, "Gabe's Grad Reef");
  ReefAngel.LCD.DrawGraph(5,10);
}
////// Place global variable code above here


void setup()
{
  ReefAngel.Init();
  ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
  ReefAngel.WaterChangePorts = Port4Bit | Port5Bit | Port6Bit | Port7Bit | Port8Bit;  // Turn off ports 4-8 when water change mode is activated
  ReefAngel.WaterChangePortsE[0] = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit;  // Turn off box1_ports 1-6 when water change mode is activated
  ReefAngel.UseFlexiblePhCalibration();
  //ReefAngel.Relay.On(CaRXPump);  //Turn on Port 6
  //ReefAngel.Relay.On(manifoldPump);  //Turn on Port 7
  ReefAngel.Relay.On(returnPump);  //Turn on Port 8
  returnTimer = now();  //Start the return timer
  wp25On = true;
  //Initialize portal variables.
  ReefAngel.CustomVar[Var_AWCCountTotal] = 0;
  ReefAngel.CustomVar[Var_AWCCountToday] = 0;
  ReefAngel.CustomVar[Var_AWCDrainFail] = 0;
  ReefAngel.CustomVar[Var_AWCFillFail] = 0;
  ReefAngel.CustomVar[Var_AWCOverflowFail] = 0;
  ReefAngel.CustomVar[Var_AWCATOFail] = 0;
  ReefAngel.CustomVar[Var_CDosesToday] = 0;
  ReefAngel.CustomVar[Var_NO3DosesToday] = 0;
}

void loop()
{
  if (ATOOn == true)  //If ATO is allowed, run the standard reef angel ATO function
  {
    ReefAngel.StandardATO(fillPump, ATOTimeOut); // Controls ATO with either RODI (normal operation) or new saltwater (acclimation mode only)
  }
 
  [u][b]//Start an AWC once per day at 12 noon if an AWC hasn't happened since midnight and there hasn't been an ATO timeout.
  AWCt = now();
  if (!AWCFail && (hour(AWCt) == AWCHour) && (minute(AWCt) == AWCMinute) && !AWCToday && !AWC && !ReefAngel.isATOTimeOut())
  {
    AWC = true;  //Sets the AWC control variable to true.
   
    static WiFiAlert tempAlert;
    char tempMsg[] = "An AWC occured.";
    tempAlert.Send(tempMsg);
  }
   
  //Reset the automatic water change variable and AWC and dosing counts every day at midnight.  Also add the day's AWC count to the total AWC count.
  if (hour(AWCt) == 0 && minute(AWCt) == 0 && AWCToday)
  {
    AWCToday = false;
    ReefAngel.CustomVar[Var_AWCCountTotal] += ReefAngel.CustomVar[Var_AWCCountToday];
    ReefAngel.CustomVar[Var_AWCCountToday] = 0;
    ReefAngel.CustomVar[Var_CDosesToday] = 0;
    ReefAngel.CustomVar[Var_NO3DosesToday] = 0;
  }
   
  //Start the chain of AWC functions.
  if (AWC && !AWCToday)
    if (!ReefAngel.IO.GetChannel(0))
    {
      AWCToday = true;
      ReefAngel.CustomVar[Var_AWCCountToday] += 1;
      wp25Timer = now();
      wp25On = false;
    }
    else
    {
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCOverflowFail] = 1;
      AWC = false;
      wp25On = true;
      AWCAlert();
    }
  if (((now() - wp25Timer) >= 300) && AWCToday && AWC)
  {
    AWC = false;
    AWCCheck = AWCATOCheck();
  }
  if (AWCCheck == 2)
  {
    if (ReefAngel.HighATO.IsActive())
    {
      ReefAngel.Relay.Off(ATOPump);
     
      AWCCheck = 1;
    }
    else if ((now() - AWCTimer) >= ATOTimeOut)
    {
      ReefAngel.Relay.Off(ATOPump);
     
      bitSet(ReefAngel.AlertFlags,ATOTimeOutFlag);  //Trigger ATO timeout flag.
     
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCATOFail] = 1;
      AWCAlert();
      wp25On = true;
      AWCCheck = 0;
    }
  }
  else if (AWCCheck == 1)
  {
    ATOOn = false;
   
    AWCTimer = now();
   
    ReefAngel.Relay.On(AWCDrainPump);

    if (ReefAngel.LowATO.IsActive())
    {
      ReefAngel.Relay.Off(AWCDrainPump);
     
      AWCCheck = 0;
     
      AWCDrainCheck = 1;
    }
    else if ((now() - AWCTimer) >= AWCDrainSecs)
    {
      ReefAngel.Relay.Off(AWCDrainPump);
     
      AWCCheck = 0;
      AWCDrainCheck = 0;
      ATOOn = true;
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCDrainFail] = 1;
      AWCAlert();
      wp25On = true;
    }
  }
  if (AWCDrainCheck == 1)
  {
    AWCTimer = now();
   
    ReefAngel.Relay.On(AWCFillPump);
   
    if (ReefAngel.HighATO.IsActive())
    {
      ReefAngel.Relay.Off(AWCFillPump);
     
      ATOOn = true;
      wp25On = true;
      AWCDrainCheck = 0;
    }
    else if ((now() - AWCTimer) >= AWCFillSecs)
    {
      ReefAngel.Relay.Off(AWCFillPump);
     
      ATOOn = true;
      AWCDrainCheck = 0;
      AWCFail = true;
      ReefAngel.CustomVar[Var_AWCFillFail] = 1;
      AWCAlert();
      wp25On = true;
    }
  }
 
  //Drain AWC overflow reservoir when water is in it.
  if (ReefAngel.IO.GetChannel(0) && !ReefAngel.Relay.Status(AWCOverflow) && !AWCFail)
  {
    ReefAngel.Relay.On(AWCOverflow);
   
    AWCOverflowTimer = now();
  }
  else if (!ReefAngel.IO.GetChannel(0) && ReefAngel.Relay.Status(AWCOverflow))
    ReefAngel.Relay.Off(AWCOverflow);
  else if (((now() - AWCOverflowTimer) >= AWCOverflowSecs) && ReefAngel.Relay.Status(AWCOverflow))
  {
    ReefAngel.Relay.Off(AWCOverflow);
    AWCFail = true;
    ReefAngel.CustomVar[Var_AWCOverflowFail] = 1;
    AWCAlert();
  }[/b][/u]
   
  //Light control
  if (hour() < 12 && !toggleMainLights && !toggleFugeLights)
  {
    ReefAngel.StandardLights(daylightFixture, 6, 0, 12, 0);
    ReefAngel.StandardLights(actinicFixture, 6, 0, 12, 0);
    ReefAngel.StandardLights(QTFixture, 6, 0, 12, 0);
    ReefAngel.StandardLights(fugeFixture, 0, 0, 6, 0);
  }
  else if (hour() >= 12 && !toggleMainLights && !toggleFugeLights)
  {
    ReefAngel.StandardLights(daylightFixture, 18, 0, 24, 0);
    ReefAngel.StandardLights(actinicFixture, 18, 0, 24, 0);
    ReefAngel.StandardLights(QTFixture, 18, 0, 24, 0);
    ReefAngel.StandardLights(fugeFixture, 12, 0, 18, 0);
  }
 
  //Call the automatic feeding control function and monitor the light schedule to inform the feeding schedule.
  //feed();
 
  if (ReefAngel.Relay.Status(daylightFixture) || ReefAngel.Relay.Status(actinicFixture))
    canFeed == true;
  else
    canFeed == false;
 
  feedt = now(); 
  if (hour(feedt) == (lightsOnHour || lightsOnHour + 12) && minute(feedt) == 0)
    feedTimer = now();
   
  if (feeding && ((now() - feedingPauseTimer) >= feedingPause))
  {
    wp25On = true;
    feeding = false;
  }
 
  //Heater control
  ReefAngel.StandardHeater(heater, lowTempValue, highTempValue); // Heater on at 71.8.0F and off at 72.5.0F
  if ((ReefAngel.Params.Temp[T3_PROBE] < lowTempValue) && !ReefAngel.Relay.Status(QTheater))  //QT heater on at 71.8.0F and off at 72.5.0F
    ReefAngel.Relay.On(QTheater);
  else if ((ReefAngel.Params.Temp[T3_PROBE] >= highTempValue) && ReefAngel.Relay.Status(QTheater))
    ReefAngel.Relay.Off(QTheater);
 
  /*if ((now() - pHTimer) >= 60) //Every 60 seconds, check the pH in the calcium reactor and turn on or off the solenoid
  {
    pHControl();
   
    pHTimer = now();
  }*/
 
  if (returnTimer < 1 && ReefAngel.Relay.Status(returnPump))
    returnTimer = now();
  if (returnTimer >= 300)
  {
    ReefAngel.StandardLights(skimmerPump, 0, 0, 6, 0);
    ReefAngel.StandardLights(manifoldPump, 0, 0, 6, 0);
  }
  else
  {
    ReefAngel.Relay.Off(skimmerPump);
    ReefAngel.Relay.Off(manifoldPump);
  }
  if (!ReefAngel.Relay.Status(returnPump))
    returnTimer = 0;
 
  if (wp25On)
  {
    if (ReefAngel.Relay.Status(daylightFixture) || ReefAngel.Relay.Status(actinicFixture))
    {
      ReefAngel.PWM.SetDaylight(GyreMode(20,65,40,true)); //Ramp from 20% to 65% with 20 min duration in sync mode
      ReefAngel.PWM.SetActinic(GyreMode(20,65,40,false)); //Ramp from 20% to 65% with 20 min duration in Antisync mode
    }
    else
    {
      ReefAngel.PWM.SetDaylight(GyreMode(20,40,40,true)); //Ramp from 20% to 40% with 20 min duration in sync mode
      ReefAngel.PWM.SetActinic(GyreMode(20,40,40,false)); //Ramp from 20% to 40% with 20 min duration in Antisync mode
    }
  }
  else
  {
    ReefAngel.PWM.SetDaylight(0); //Turn WP-25s off by dimming to 0.
    ReefAngel.PWM.SetActinic(0);
  }
 
  ReefAngel.DosingPumpRepeat(doseC, 0, 480, 1);  //Dose carbon for 1 second three times per day for a total of 3 seconds x 0.66 mL/second = 1 mL per day.
  //Keep track of the number of carbon doses today.
  if (ReefAngel.Relay.Status(doseC) && !carbonDosing)
  {
    carbonDosing = true;
    ReefAngel.CustomVar[Var_CDosesToday] += 1;
  }
  else if (!ReefAngel.Relay.Status(doseC) && carbonDosing)
    carbonDosing = false;
   
  ReefAngel.DosingPumpRepeat(doseNO3, 0, 240, 50);  //Dose nitrate for 50 seconds six times per day for a total of 5 minutes x 1 mL/minutes = 5 mL per day.
  //Keep track of the number of nitrate doses today.
  if (ReefAngel.Relay.Status(doseNO3) && !NO3Dosing)
  {
    NO3Dosing = true;
    ReefAngel.CustomVar[Var_NO3DosesToday] += 1;
  }
  else if (!ReefAngel.Relay.Status(doseNO3) && NO3Dosing)
    NO3Dosing = false;
   
  ReefAngel.Portal("gaberosenfield", "none of your beeswax!");
  ReefAngel.ShowInterface();
}
Gabe's Grad Reef
Image

Posts: 11845
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Thu Apr 02, 2015 9:08 am
Did you check if the floats are working correctly?
Roberto.
User avatar
Posts: 1421
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City
PostPosted: Fri Apr 03, 2015 3:17 am
What he said. A leaking float switch could be the cause of both your pH and AWC problems.

Posts: 80
Joined: Thu Mar 08, 2012 5:11 pm
Location: Oakland, California
PostPosted: Mon Apr 06, 2015 8:24 am
As usual, Roberto was right. My low float switch was the problem. Everything is good now! I wonder why that happens... I guess this is why the pressure sensors and laser level systems are considered more reliable. Sorry it took me so long to respond. Thanks again (as usual) to everyone for the help!

Best,
Gabe
Gabe's Grad Reef
Image

Return to My PDE/INO file

Who is online

Users browsing this forum: No registered users and 1 guest