Instant Water Level ATO timeout

Expansion modules and attachments
Post Reply
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Instant Water Level ATO timeout

Post by phrusher »

Whenever WaterLevelATO is triggered (single channel) it goes straight into timeout within a fraction of a second and so it stops filling. If I clear the ATO flag it gets set yet again immediately.

Code: Select all

  ReefAngel.WaterLevelATO( Port1, 20, 75, 80 );
I haven't read about anyone else having this issue so I'm suspecting it might be a local problem? Any suggestions?
User avatar
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Instant Water Level ATO timeout

Post by cosmith71 »

Did you ever figure this out? If not, post your entire code.

--Colin
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

No I have not found the problem yet. Please have a look at the code below, it's my entire ino file.

Code: Select all

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <ReefAngel.h>
#include <PH.h>
#include <WaterLevel.h>
#include <RF.h>
#include <Math.h>
#include <SoftwareSerial.h>
////// Place global variable code below here


////// Place global variable code above here

// Port1: ATO
// Port2: Empty
// Port3: Refugium light
// Port4: Daylight
// Port5: Return pump
// Port6: Pellets pump
// Port7: Skimmer
// Port8: Heater

// Expansion 1
// Port1: Lunar
// Port2: Dimmer
// Port3: Empty
// Port4: Empty
// Port5: Mg
// Port6: Ca
// Port7: Fan
// Port8: ATO


void setup()
{
  // This must be the first line
  ReefAngel.Init();  //Initialize controller
  ReefAngel.SetTemperatureUnit(Celsius);  // set to Celsius Temperature
  
  ReefAngel.AddRANet();
  ReefAngel.Relay.RANetFallBackE[1] = Port3Bit;

//  ReefAngel.DDNS("phrusher");
//  ReefAngel.Network.WifiAuthentication("phrusher:xxxxxxxx");

  ReefAngel.Use2014Screen();
  ReefAngel.Display24h();
  ReefAngel.UseFlexiblePhCalibration();

  // Ports toggled in Feeding Mode
  ReefAngel.FeedingModePorts = Port1Bit | Port5Bit | Port7Bit | Port8Bit;  // Ports toggled in Feeding Mode
  ReefAngel.FeedingModePortsE[0] = Port3Bit | Port5Bit | Port6Bit | Port8Bit;  // Ports toggled in Feeding Mode
  
  // Ports toggled in Water Change Mode
  ReefAngel.WaterChangePorts = Port1Bit | Port5Bit | Port7Bit;  // Ports toggled in Water Change Mode
  ReefAngel.WaterChangePortsE[0] = Port3Bit | Port5Bit | Port6Bit | Port8Bit;  // Ports toggled in Water Change Mode
  
  
  // Ports toggled when Lights On / Off menu entry selected
  ReefAngel.LightsOnPorts = Port4Bit;
  
  // Ports turned off when Overheat temperature exceeded
  ReefAngel.OverheatShutoffPorts = Port4Bit | Port8Bit; 
  
  // Use T2 probe as temperature and overheat functions
  ReefAngel.TempProbe = T2_PROBE;
  ReefAngel.OverheatProbe = T2_PROBE;
  
  // Ports that are always on
  ReefAngel.Relay.On( Port5 );    // Return
  ReefAngel.Relay.On( Port8 );    // Heater

  ReefAngel.Relay.On( Box1_Port1 );
  ReefAngel.Relay.On( Box1_Port2 );
  
  InternalMemory.OverheatTemp_write( 290 );    // Set the Overheat temperature setting

  ////// Place additional initialization code below here
  

  ////// Place additional initialization code above here
}


void loop()
{
  int moonphase = MoonPhase();
  // Lights
  ReefAngel.StandardLights( Port3, 15, 0, 7, 0 );   // Refugium
  ReefAngel.StandardLights( Port4, 6, 0, 22, 00 );  // Daylight

  ReefAngel.PWM.SetChannel( 0, moonphase);  // Moonlight: Unmodified
  ReefAngel.PWM.SetChannel( 1, PWMParabola(6,0, 20,30, 2, 85, 2) );  // Daylight: Blue
  ReefAngel.PWM.SetChannel( 2, PWMParabola(7,0, 19,30, 2, 65, 0) );  // Daylight: White
  ReefAngel.PWM.SetChannel( 4, 30 );  // Refugium light 
  ReefAngel.PWM.SetChannel( 5, 100 - PWMParabola(21,0, 7,0, 94, 99, 94) );  // Moonlight 
  
  // Dosing
  ReefAngel.DosingPump( Box1_Port5, 1,  7, 00, 4 );  // 0.75 ml/sec Mg
  ReefAngel.DosingPump( Box1_Port5, 1, 22, 00, 4 );  // 0.75 ml/sec Mg
  
  ReefAngel.DosingPump( Box1_Port6, 2,  7, 01, 5 );  // 0.75 ml/sec Ca
  ReefAngel.DosingPump( Box1_Port6, 2, 22, 01, 5 );  // 0.75 ml/sec Ca
  

  // ATO
  ReefAngel.WaterLevelATO( Port1, 20, 75, 80 );
  
  if(ReefAngel.Params.Temp[T2_PROBE] > 260){
    ReefAngel.Relay.Off( Port8 );    // Heater
  } else {
    ReefAngel.Relay.On( Port8 );    // Heater
  }

  ReefAngel.Relay.DelayedOn (Port7, 2); // Delay skimmer 2 minutes after feeding
  
  ////// Place your custom code below here

  if ((hour() == 8 || hour() == 18) && minute() == 55 && second() == 0)
  {
    ReefAngel.FeedingModeStart();
  }

  if (ReefAngel.DisplayedMenu == FEEDING_MODE)
  {
    ReefAngel.PWM.SetDaylight( LongPulseMode(35, 45, 30, true ));
    ReefAngel.PWM.SetActinic(  LongPulseMode(35, 45, 30, false));
  } 
  else if(ReefAngel.DisplayedMenu == WATERCHANGE_MODE)
  {
    ReefAngel.PWM.SetActinic(0);
    ReefAngel.PWM.SetDaylight(0);
  }
  else if ((hour() == 9 || hour() == 19) && minute() <= 20)
  {
    WaveMakerSyncMillis(383, 75, 0);  // After Feeding
  } 
  else if (hour() >= 21 || hour() <= 6)
  {
    // Night mode
    ReefAngel.PWM.SetDaylight( LongPulseMode(38, 50, 30, true ));
    ReefAngel.PWM.SetActinic(  LongPulseMode(38, 50, 30, false));
  }
  else 
  {    
    if(hour() >= 8 && hour() <= 19) {
      if (minute() % 10 == 0) {
        ReefAngel.PWM.SetDaylight( LongPulseMode(40, 85, 30, true )); // LongPulseMode at 40%, 85% 30s on sync mode
        ReefAngel.PWM.SetActinic(  LongPulseMode(40, 85, 30, false)); // LongPulseMode at 40%, 85% 30s on anti-sync mode
      } else if (hour() >= 10 && hour() <= 16) {
        ReefAngel.PWM.SetDaylight( ElseMode(80, 20, true )); // ElseMode at 80% +/- 20% on sync mode
        ReefAngel.PWM.SetActinic(  ElseMode(80, 20, false)); // ElseMode at 80% +/- 20% on anti-sync mode
      } else {
        ReefAngel.PWM.SetDaylight( ReefCrestMode(70, 15, true )); // ReefCrest at 70% +/- 15% on sync mode
        ReefAngel.PWM.SetActinic(  ReefCrestMode(70, 15, false)); // ReefCrest at 70% +/- 15% on anti-sync mode
      }
    } else {
      ReefAngel.PWM.SetDaylight( LongPulseMode(40, 70, 60, true )); // LongPulseMode at 40%, 70% 60s on sync mode
      ReefAngel.PWM.SetActinic(  LongPulseMode(40, 70, 60, false)); // LongPulseMode at 40%, 70% 60s on anti-sync mode
    }
  }

  ////// Place your custom code above here

  ReefAngel.Portal( "phrusher" );
  // This should always be the last line
  ReefAngel.ShowInterface();
}


// Wavemaker

/** Calculates the tidal flow.
 *
 * Ex. hour => flow
 * 0 => 0   6 => 6  12 => 0  18 => 6
 * 1 => 1   7 => 5  13 => 1  19 => 5
 * 2 => 2   8 => 4  14 => 2  20 => 4
 * 3 => 3   9 => 3  15 => 3  21 => 3
 * 4 => 4  10 => 2  16 => 4  22 => 2
 * 5 => 5  11 => 1  17 => 5  23 => 1
 *
 * \param hour: Hour in 24h format to calculate flow from
 * \returns flow value
 */
int flow(int hour)
{	
  return (((hour/6) % 2) == 0 ? hour % 6 : 6 - hour % 6);
}


int pulsePower(int pulse, int maxPower, int minPower)
{
  return (millis()%(pulse*2))<pulse ? maxPower : minPower;
}


/** Basic tidal simulation by gradually changing the opposing flow four times during 24h.
 * 
 * By setting duration of opposing pumps it creates a directional flow in the tank that changes during one day.
 *
 * Ex. hour => pump01 : pump02  (duration in seconds)
 *  0 =>  2 : 14   6 => 14 : 2    12 =>  2 : 14   18 => 14 : 2
 *  1 =>  4 : 12   7 => 12 : 4    13 =>  4 : 12   19 => 12 : 4
 *  2 =>  6 : 10   8 => 10 : 6    14 =>  6 : 10   20 => 10 : 6
 *  3 =>  8 : 8    9 =>  8 : 8    15 =>  8 : 8    21 =>  8 : 8
 *  4 => 10 : 6   10 =>  6 : 10   16 => 10 : 6    22 =>  6 : 10
 *  5 => 12 : 4   11 =>  4 : 12   17 => 12 : 4    23 =>  4 : 12
 *
 * \param maxPower: The maximum pump power
 * \param minPower: The minimum pump power
 * \param pause: Length of the pause between each interval. Negative pause becomes overlap.
 */
void WaveMakerTidal(int maxPower, int minPower, int pause)
{
  static boolean state = true;
  static unsigned long nexttoggle=now();
  long cycle=nexttoggle-now();
  int pulse = 373;

  if (cycle<0)
  {
    int duration = flow( hour() ) << 1; // 0 - 12
    //int duration = flow( hour()); // 0 - 6

    nexttoggle = now() + (state ? duration : (12-duration)) + 2 + pause; // flow->counter flow => 2->14
    state = !state;
  }

  if (pause > 0 && cycle < pause)
  {
    ReefAngel.PWM.SetActinic(0);
    ReefAngel.PWM.SetDaylight(0);
  }
  else if (pause < 0 && cycle < pause)
  {
    ReefAngel.PWM.SetActinic(maxPower);
    ReefAngel.PWM.SetDaylight(maxPower);
  }
  else 
  {
    //int power = (millis()%(pulse*2))<pulse ? maxPower : minPower;
    int power = pulsePower(pulse, maxPower, minPower);

    if(state){
      ReefAngel.PWM.SetActinic(power);
      ReefAngel.PWM.SetDaylight(0);
    } 
    else {
      ReefAngel.PWM.SetActinic(0);
      ReefAngel.PWM.SetDaylight(power);
    }
  }
}


/** Toggles opposing pumps with a pause between each interval.
 * 
 * \param power: The pump power
 * \param duration: Duration of each interval
 * \param pause: Length of the pause between each interval. If pause is negative it will work as an overlap.
 */
void WaveMakerPause(int power, int duration, int pause)
{
  static boolean state = true;
  static unsigned long nexttoggle=now();
  long cycle=nexttoggle-now();

  if (cycle<0)
  {
    nexttoggle = now() + duration;
    state = !state;
  }

  if (pause > 0 && cycle < pause)
  {
    ReefAngel.PWM.SetActinic(0);
    ReefAngel.PWM.SetDaylight(0);
  } 
  else if(pause < 0 && cycle < pause) 
  {
    ReefAngel.PWM.SetActinic(power);
    ReefAngel.PWM.SetDaylight(power);
  } 
  else {
    if(state){
      ReefAngel.PWM.SetActinic(power);
      ReefAngel.PWM.SetDaylight(0);
    } 
    else {
      ReefAngel.PWM.SetActinic(0);
      ReefAngel.PWM.SetDaylight(power);
    }
  }
}


void WaveMakerSyncMillis(long duration, int maxPower, int minPower)
{
  if ((millis()%(duration*2))<duration)
  {
    ReefAngel.PWM.SetActinic(minPower);
    ReefAngel.PWM.SetDaylight(minPower);
  } 
  else {
    ReefAngel.PWM.SetActinic(maxPower);
    ReefAngel.PWM.SetDaylight(maxPower);
  }
}

User avatar
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Instant Water Level ATO timeout

Post by cosmith71 »

Just for fun, try removing your dosing pump code and see what happens.
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

I removed these four lines but still the same behavior. What were your thoughts regarding the dosing pumps?

Code: Select all

  // Dosing
  ReefAngel.DosingPump( Box1_Port5, 1,  7, 00, 4 );  // 0.75 ml/sec Mg
  ReefAngel.DosingPump( Box1_Port5, 1, 22, 00, 4 );  // 0.75 ml/sec Mg
  
  ReefAngel.DosingPump( Box1_Port6, 2,  7, 01, 5 );  // 0.75 ml/sec Ca
  ReefAngel.DosingPump( Box1_Port6, 2, 22, 01, 5 );  // 0.75 ml/sec Ca
User avatar
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Instant Water Level ATO timeout

Post by cosmith71 »

I was thinking there might be a timer conflict.
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

Same thought as I had :) However WL ATO doesn't use the Timer class, just a long variable named Timer which makes it a bit confusing.
User avatar
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Instant Water Level ATO timeout

Post by cosmith71 »

Huh. I have no clue. Your topoff line is identical to what the wizard would generate. Have you tried un-installing/re-installing the Reef Angel software and re-uploading?

Does the water level sensor work correctly otherwise?
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

Yeah my next step is un-/re-installing to see if it might help. The WL ATO used to work but I can't remember if it stopped after an update or if there was some change I did.

The sensor looks ok based on the level reported.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Instant Water Level ATO timeout

Post by lnevo »

You can try using an if statements to make sure and at least get your ATO working. It will be sans timeout...

if (ReefAngel.WaterLevel.GetLevel() < X) ReefAngel.Relay.On(ATO_Port);
if (Reefangel.WaterLevel.GetLevel() > Y) ReefAngel.Relay.Off(ATO_Port);

Change X and Y and the ATO Port.
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

lnevo wrote:You can try using an if statements to make sure and at least get your ATO working. It will be sans timeout...

if (ReefAngel.WaterLevel.GetLevel() < X) ReefAngel.Relay.On(ATO_Port);
if (Reefangel.WaterLevel.GetLevel() > Y) ReefAngel.Relay.Off(ATO_Port);

Change X and Y and the ATO Port.
I could do like that but it wouldn't be much fun :)
phrusher
Posts: 65
Joined: Fri May 25, 2012 12:22 am

Re: Instant Water Level ATO timeout

Post by phrusher »

I added some serial print to trace the different variables. The topping seem to work occasionally but most of the time not.

Code: Select all

WL: 73 WLATO.Timer: 262144 millis(): 282117 diff: 19973 IsTopping: 1 msTimeout: 20000
WL: 73 WLATO.Timer: 262144 millis(): 282140 diff: 19996 IsTopping: 1 msTimeout: 20000
WL: 73 WLATO.Timer: 262144 millis(): 282147 diff: 20003 IsTopping: 1 msTimeout: 20000
(millis() - WLATO.Timer) > msTimeout && WLATO.IsTopping() <-- This indicates the timeout
That was the output of a successful timeout. The interesting part is in the following run after clearing the ATO:

Code: Select all

WL: 74 WLATO.Timer: 316609 millis(): 316609 diff: 0 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316632 diff: 23 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316639 diff: 30 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316653 diff: 44 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316667 diff: 58 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316682 diff: 73 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316696 diff: 87 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316710 diff: 101 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316733 diff: 124 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316740 diff: 131 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316754 diff: 145 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 316609 millis(): 316769 diff: 160 IsTopping: 1 msTimeout: 20000
WL: 74 WLATO.Timer: 262144 millis(): 316883 diff: 54739 IsTopping: 1 msTimeout: 20000
(millis() - WLATO.Timer) > msTimeout && WLATO.IsTopping() <-- This indicates the timeout
Look at the last WLATO.Timer. Recognize the new value? Any ideas how it's set?


WaterLevelATO with the Serial.print that I used:

Code: Select all

void ReefAngelClass::WaterLevelATO(byte Channel, byte ATORelay, int ATOTimeout, byte LowLevel, byte HighLevel)
{
	// Input:  Relay port and timeout value (max number of seconds that ATO pump is allowed to run)
	// Input:  Low and High Water Level to start and stop ATO pump
	unsigned long msTimeout = ATOTimeout * 1000;
	byte waterlevel = WaterLevel.GetLevel(Channel);
	/*
	Is the low level is reached (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 (waterlevel < LowLevel && (!WLATO.IsTopping()))
	{
		Serial.println("waterlevel < LowLevel && (!WLATO.IsTopping())");
		WLATO.Timer = millis();
		WLATO.StartTopping();
		Relay.On(ATORelay);
	}

	// If the high level is reached, this is a safeguard to prevent over running of the top off pump
	if (waterlevel >= HighLevel)
	{
		WLATO.StopTopping();  // stop the low ato timer
		Relay.Off(ATORelay);
	}

	unsigned long milli = millis();
	Serial.print("WL: ");
	Serial.print(waterlevel);
	Serial.print(" WLATO.Timer: ");
	Serial.print(WLATO.Timer);
	Serial.print(" millis(): ");
	Serial.print(milli);
	Serial.print(" diff: ");
	Serial.print(milli - WLATO.Timer);
	Serial.print(" IsTopping: ");
	Serial.print(WLATO.IsTopping());
	Serial.print(" msTimeout: ");
	Serial.print(msTimeout);
	Serial.println();

	/*
	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 continuously running.
	*/
	if ((millis() - WLATO.Timer) > msTimeout && WLATO.IsTopping())
	{
		Serial.println("(millis() - WLATO.Timer) > msTimeout && WLATO.IsTopping()");
		Relay.Off(ATORelay);
		LED.On();
		bitSet(AlertFlags, ATOTimeOutFlag);
#ifdef ENABLE_EXCEED_FLAGS
		if (InternalMemory.read(ATO_Exceed_Flag)==0)
			InternalMemory.write(ATO_Exceed_Flag, 1);
#endif  // ENABLE_EXCEED_FLAGS
#ifdef ENABLE_ATO_LOGGING
		// bump the counter if a timeout occurs
		AtoEventCount++;
		if ( AtoEventCount >= MAX_ATO_LOG_EVENTS ) { AtoEventCount = 0; }
#endif  // ENABLE_ATO_LOGGING
	}
}
Post Reply