Odd ATO pin output behavior on WiFi activity

Do you have a question on how to do something.
Ask in here.
Post Reply
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Odd ATO pin output behavior on WiFi activity

Post by joshlawless »

I connected a Jebao WP-25 to my lowATO pins, and wrote some code to ramp the speed from ~38% to 100% 10 seconds out of every 2 hours.

Code: Select all

    pinMode(lowATOPin,OUTPUT);
    if ( (hour() >= 8) && (hour() < 22) ) 
    {
      if ( now()%7200<10 ) analogWrite(lowATOPin,255);
      else analogWrite(lowATOPin,96);
    }
    else analogWrite(lowATOPin,96);
This seemed to work well enough, but I noticed one heck of an unexpected behavior. When I hit the refresh button in the Android app, or send any signal to the controller with the iPhone app (e.g., turning on or off a relay), it pulses the pump on that channel to full power, briefly (much briefer than the 10 seconds specified in the code).

Do I have some particularly nasty bug in this code somewhere that would explain that behavior? Is there a reason that the WiFi traffic would cause a voltage surge on the ATO pins?

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 <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 <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>
#include <Moon.h>

////// Place global variable code below here
    
#define Heater_300W           Port1
#define Heater_100W           Port2
#define Skimmer               Port3
#define Neck_Wipe             Port4
#define Air_Pump              Port5
#define Filter_Pump           Port6
#define Refuge_Light          Port7
#define Ozone_Gen             Port8

#define FlowWolf              Box1_Port1
#define Closed_Loop           Box1_Port2
#define Kalk_Stirrer          Box1_Port3
#define ATO_Pump              Box1_Port4
#define Return_Pump_L         Box1_Port5
#define Return_Pump_R         Box1_Port6
#define Heater_200W           Box1_Port7
#define Refugium_Pump         Box1_Port8

#define Neutral_White_Center  0
#define Warm_White_Center     1
#define Royal_Blue_Center     2
#define Cool_Blue_Center      3
#define Cyan_Center           4
#define Red_Center            5
#define Indigo_Center         6
#define Violet_Center         7

int MoonBlueLevel=0;
int SunBlueLevel=0;

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

void setup()
{
    ReefAngel.Init();
    ReefAngel.AddStandardMenu();
    ReefAngel.Use2014Screen();
    ReefAngel.AddSalinityExpansion();                
    ReefAngel.AddORPExpansion();                     
    ReefAngel.AddWaterLevelExpansion();
    ReefAngel.Add16ChPWM();                          

    ReefAngel.FeedingModePorts = 0;
    ReefAngel.FeedingModePortsE[0] = Port5Bit | Port6Bit;

    ReefAngel.WaterChangePorts = Port3Bit;                 // Skimmer
    ReefAngel.WaterChangePortsE[0] = Port4Bit | Port8Bit;  // ATO Pump and Refugium Circulation

    ReefAngel.LightsOnPorts = 0;
    ReefAngel.LightsOnPortsE[0] = 0;

    ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;   
    ReefAngel.OverheatShutoffPortsE[0] = Port7Bit;

    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;

    ReefAngel.DCPump.FeedingSpeed = 0;
    ReefAngel.DCPump.WaterChangeSpeed = 0;

    ReefAngel.Relay.On( Air_Pump );
    ReefAngel.Relay.On( Filter_Pump );
    ReefAngel.Relay.On( FlowWolf );
    ReefAngel.Relay.On( Closed_Loop );
    ReefAngel.Relay.On( Return_Pump_L );
    ReefAngel.Relay.On( Return_Pump_R );
    ReefAngel.Relay.On( Refugium_Pump );

}

void loop()
{
  
    if ( ReefAngel.Params.Temp[T1_PROBE] < 775 ) ReefAngel.Relay.On( Heater_300W );
    if ( ReefAngel.Params.Temp[T1_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_300W );
    if ( ReefAngel.Params.Temp[T2_PROBE] < 775 ) ReefAngel.Relay.On( Heater_100W );
    if ( ReefAngel.Params.Temp[T2_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_100W );
    if ( ReefAngel.Params.Temp[T3_PROBE] < 775 ) ReefAngel.Relay.On( Heater_200W );
    if ( ReefAngel.Params.Temp[T3_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_200W );
  
    ReefAngel.DCPump.UseMemory = false;
    ReefAngel.DCPump.Threshold = 80;   
    ReefAngel.DCPump.SetMode( Sine, 100, 240 );
    ReefAngel.DCPump.DaylightChannel = Sync; 
    ReefAngel.DCPump.ActinicChannel = AntiSync;
    
    moon_init(44,-116);   // approximate lat,lon for Boise, ID
    SunBlueLevel = PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 80, 45, 0 );
    MoonBlueLevel = PWMSmoothRampHighestRes( Moon.riseH, Moon.riseM, Moon.setH, Moon.setM, 0, (int)(MoonPhase()/19), 45, 0);


    ReefAngel.PWM.Set16ChannelRaw( Neutral_White_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 65, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Warm_White_Center,    PWMSmoothRampHighRes( 7, 25, 22, 05, 0, 65, 45, 0 ) ); // 05 minute longer 
    ReefAngel.PWM.Set16ChannelRaw( Royal_Blue_Center,    max (SunBlueLevel, MoonBlueLevel) );
    ReefAngel.PWM.Set16ChannelRaw( Cool_Blue_Center,     PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 40, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Cyan_Center,          PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 20, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Red_Center,           PWMSmoothRampHighRes( 7, 15, 22, 15, 0, 40, 45, 0 ) ); // 15 minute longer
    ReefAngel.PWM.Set16ChannelRaw( Indigo_Center,        PWMSmoothRampHighRes( 7, 20, 22, 10, 0, 30, 45, 0 ) ); // 10 minute longer
    ReefAngel.PWM.Set16ChannelRaw( Violet_Center,        PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 10, 45, 0 ) );

    ReefAngel.Relay.Set( Refuge_Light, !((hour() >= 10) && (hour() < 19)));


    pinMode(lowATOPin,OUTPUT);
    if ( (hour() >= 8) && (hour() < 22) ) 
    {
      if ( now()%7200<10 ) analogWrite(lowATOPin,255);
      else analogWrite(lowATOPin,96);
    }
    else analogWrite(lowATOPin,96);

    ReefAngel.Relay.DelayedOn( Skimmer, 1 );
    if ( ReefAngel.WaterLevel.GetLevel() > 55 ) ReefAngel.Relay.Off( Skimmer );
    if ( ReefAngel.DisplayedMenu==FEEDING_MODE ) ReefAngel.Relay.Off( Skimmer );
    if ( !ReefAngel.HighATO.IsActive() ) ReefAngel.Relay.Override( Skimmer, 0 );

    ReefAngel.DosingPumpRepeat( Neck_Wipe, 0, 240, 30 );

    ReefAngel.DosingPumpRepeat( Kalk_Stirrer, 0, 240, 30 );

    if ( (hour() >= 8) && (hour() < 22) ) 
    {
      ReefAngel.WaterLevelATO( ATO_Pump, 1200, 38, 40 ); // On at 38% and Off at 40% with 1200 second (20 minute) timeout.
    }
    if ( (hour() >= 7) && (hour() < 8) ) 
    {
      ReefAngel.WaterLevelATO( ATO_Pump, 3600, 38, 40 ); // On at 38% and Off at 40% with 3600 second (60 minute) timeout.
    }

    if ( ReefAngel.Params.ORP>450 || ReefAngel.Params.ORP==0 ) ReefAngel.Relay.Off( Ozone_Gen );
    if ( ReefAngel.Params.ORP<400 && ReefAngel.Params.ORP>0 ) ReefAngel.Relay.On( Ozone_Gen );

    ReefAngel.Portal( "joshlawless" );
    ReefAngel.DDNS( "reef" );

    ReefAngel.ShowInterface();
}

int PWMSmoothRampHighestRes(byte startHour, byte startMinute, byte endHour, byte endMinute, int startPWMint, int endPWMint, byte slopeLength, int oldValue)
{
  LightsOverride=true;
  int current_hour = hour();
  long start = NumMins(startHour, startMinute)*60L;
  long end = NumMins(endHour, endMinute)*60L;
  long slopeLengthSecs = slopeLength*60L;

  if (start > end) // Start is greater than End so it is over midnight
  {
    if (current_hour < endHour) start -= 1440L*60L; // past midnight
    if (current_hour >= startHour) end += 1440L*60L; // before midnight
  }
  long current = NumMins(current_hour, minute())*60L + second();
  if (slopeLengthSecs > ((end-start)/2) ) slopeLengthSecs = (end-start)/2; // don't allow a slope length greater than half the total period
  if (current <= start || current >= end) 
    return oldValue; // it's before the start or after the end, return the default
  else
  { // do the slope calculation
    int pwmDelta = endPWMint - startPWMint;
    float smoothPhase;
    if ((current > (start + slopeLengthSecs)) && (current < (end - slopeLengthSecs))) 
      return endPWMint; // if it's in the middle of the slope, return the high level
    else if ((current - start) < slopeLengthSecs) 
    {  // it's in the beginning slope up
      smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0) + 180.0;
      //smoothPhase = constrain(map(current, start, start+slopeLengthSecs, 180, 360), 180, 360);
    }
    else if ((end - current) < slopeLengthSecs)
    { // it's in the end slope down
      smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0);
      //smoothPhase = constrain(map(current, end-slopeLengthSecs, end, 0, 180), 0, 180);
    }
    return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));

  }
}
Post Reply