Dosing by Volume (not time)

Post Reply
icecool2
Posts: 62
Joined: Tue Jul 19, 2011 3:12 pm

Dosing by Volume (not time)

Post by icecool2 »

I'm adding a BRS two-part dosing system to my tank this weekend. I just got the relay expansion module so I can use my spare relay box for the extra outlets. I'd like to be able to adjust the volume I dose, not set the dosing time and repeat interval. I pulled some code from lenovo's PDE that I think handles the conversion to dosing by volume. What isn't clear to me is how this enables me to set the volume I want to dose.

Here is my PDE with that code added:

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>

////// Place global variable code below here

#define Mem_I_CalDP1Vol       128
#define Mem_I_CalDP1Time      130
#define Mem_I_DP1Volume       132
#define Mem_I_CalDP2Vol       134
#define Mem_I_CalDP2Time      136
#define Mem_I_DP2Volume       138
#define Var_DPump1       1
#define Var_DPump2       2
#define Var_DPump3       3
#define DPump3             Box1_Port6 //calibration
#define DPump1             Box1_Port7 //alk
#define DPump2             Box1_Port8 //cal

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

 void init_memory() {

  InternalMemory.write_int(Mem_I_CalDP1Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP1Time,470);  
  InternalMemory.write_int(Mem_I_DP1Volume,42);  
  InternalMemory.write_int(Mem_I_CalDP2Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP2Time,490);    
  InternalMemory.write_int(Mem_I_DP2Volume,42);    
  InternalMemory.write_int(Mem_I_CalDP3Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP3Time,480);    
  InternalMemory.write_int(Mem_I_DP3Volume,45);    
}

void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.Use2014Screen();  // Let's use 2014 Screen 
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port3Bit | Port6Bit | Port8Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port3Bit | Port4Bit | Port6Bit | Port8Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port5Bit;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port3Bit | Port4Bit | Port8Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;


    // Ports that are always on
    ReefAngel.Relay.On( Port1 );
    ReefAngel.Relay.On( Port7 );

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

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

void loop()
{
    ReefAngel.SingleATOHigh( Port2 );
    ReefAngel.Relay.DelayedOn( Port3 );
    ReefAngel.StandardHeater( Port4 );
    ReefAngel.DayLights( Port5 );
    ReefAngel.DayLights( Port6 );
    ReefAngel.StandardHeater( Port8 );
    ReefAngel.PWM.DaylightPWMSlope();
    ReefAngel.PWM.ActinicPWMSlope();
    ////// Place your custom code below here
    

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

    // This should always be the last line
    ReefAngel.Portal( "icecool2" );
    ReefAngel.ShowInterface();
}
icecool2
Posts: 62
Joined: Tue Jul 19, 2011 3:12 pm

Re: Dosing by Volume (not time)

Post by icecool2 »

My ultimate goal is to be able to adjust the volume dosed for both alk and calc from the iOS app using internal memory.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Dosing by Volume (not time)

Post by lnevo »

Mem_I_CalDP1Vol = Calibration Volume = 10 ml
Mem_I_CalDP1Time = Calibration Time = 470 seconds
Mem_I_DP1Volume = How much volume to dose in 24 hours.

It then calculates how much volume per dose based on the repeat interval set. The function will calculate the time it takes and update the DP1Timer so you can see what it calculates and save in the default Internal Memory location.

The function will also run that DosingPump on the schedule selected.

init_memory doesn't always work right for me. I would suggest verifying directly with the controller what the variables are set to.

You need to copy my RunDosingPumps function and add a call to it in loop() as well. You'll want the defines I have before the function, otherwise it should be pretty standalone aside from those and the memory locations.

Code: Select all

// Definitions for dosing pumps
#define numDPumps 3 
byte pump[numDPumps]={ DPump1, DPump2, DPump3}; // Pump 3 is just for logging/calibration routine
byte varReport[numDPumps]={ Var_DPump1, Var_DPump2, Var_DPump3};
int memDPTime[numDPumps]={ Mem_B_DP1Timer, Mem_B_DP2Timer, Mem_B_DP3Timer};
int memDPVolume[numDPumps]={ Mem_I_DP1Volume, Mem_I_DP2Volume, Mem_I_DP3Volume };
int memDPRepeat[numDPumps]={ Mem_I_DP1RepeatInterval, Mem_I_DP2RepeatInterval, Mem_I_DP3RepeatInterval };
int memCalTime[numDPumps]={ Mem_I_CalDP1Time, Mem_I_CalDP2Time, Mem_I_CalDP3Time };
int memCalVol[numDPumps]={ Mem_I_CalDP1Vol, Mem_I_CalDP2Vol, Mem_I_CalDP3Vol };
icecool2
Posts: 62
Joined: Tue Jul 19, 2011 3:12 pm

Re: Dosing by Volume (not time)

Post by icecool2 »

Thanks! I appreciate you sharing that code.

I've added everything (I think) and my PDE compiles. So that's a good start :)

Questions:
1. My dosers put out 1.1mL/min. It would then hold that I should set CalDP1Vol = 11 and CalDP1Time = 600 right?
2. Can I adjust DP1Volume from the iOS app?
3. How do I verify the init_memory values on the device itself?

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>

////// Place global variable code below here
#define Var_DPump1       1
#define Var_DPump2       2
#define Var_DPump3       3
#define Mem_I_CalDP1Vol       128
#define Mem_I_CalDP1Time      130
#define Mem_I_DP1Volume       132
#define Mem_I_CalDP2Vol       134
#define Mem_I_CalDP2Time      136
#define Mem_I_DP2Volume       138
#define Mem_I_CalDP3Vol       154
#define Mem_I_CalDP3Time      156
#define Mem_I_DP3Volume       158
#define Mem_B_ResetMemory     199


#define DPump3             Box1_Port6 //calibration
#define DPump1             Box1_Port7 //alk
#define DPump2             Box1_Port8 //cal

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

 void init_memory() {

  InternalMemory.write_int(Mem_I_CalDP1Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP1Time,470);  
  InternalMemory.write_int(Mem_I_DP1Volume,42);  
  InternalMemory.write_int(Mem_I_CalDP2Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP2Time,490);    
  InternalMemory.write_int(Mem_I_DP2Volume,42);    
  InternalMemory.write_int(Mem_I_CalDP3Vol,10);  
  InternalMemory.write_int(Mem_I_CalDP3Time,480);    
  InternalMemory.write_int(Mem_I_DP3Volume,45);    
}

void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.Use2014Screen();  // Let's use 2014 Screen 
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port3Bit | Port6Bit | Port8Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port3Bit | Port4Bit | Port6Bit | Port8Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port5Bit;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port3Bit | Port4Bit | Port8Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;


    // Ports that are always on
    ReefAngel.Relay.On( Port1 );
    ReefAngel.Relay.On( Port7 );

    ////// Place additional initialization code below here
     if (InternalMemory.read(Mem_B_ResetMemory)) 
    init_memory();

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

void loop()
{
    ReefAngel.SingleATOHigh( Port2 );
    ReefAngel.Relay.DelayedOn( Port3 );
    ReefAngel.StandardHeater( Port4 );
    ReefAngel.DayLights( Port5 );
    ReefAngel.DayLights( Port6 );
    ReefAngel.StandardHeater( Port8 );
    ReefAngel.PWM.DaylightPWMSlope();
    ReefAngel.PWM.ActinicPWMSlope();
    ////// Place your custom code below here
    RunDosingPumps();

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

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

// Definitions for dosing pumps
#define numDPumps 3 
byte pump[numDPumps]={ DPump1, DPump2, DPump3}; // Pump 3 is just for logging/calibration routine
byte varReport[numDPumps]={ Var_DPump1, Var_DPump2, Var_DPump3};
int memDPTime[numDPumps]={ Mem_B_DP1Timer, Mem_B_DP2Timer, Mem_B_DP3Timer};
int memDPVolume[numDPumps]={ Mem_I_DP1Volume, Mem_I_DP2Volume, Mem_I_DP3Volume };
int memDPRepeat[numDPumps]={ Mem_I_DP1RepeatInterval, Mem_I_DP2RepeatInterval, Mem_I_DP3RepeatInterval };
int memCalTime[numDPumps]={ Mem_I_CalDP1Time, Mem_I_CalDP2Time, Mem_I_CalDP3Time };
int memCalVol[numDPumps]={ Mem_I_CalDP1Vol, Mem_I_CalDP2Vol, Mem_I_CalDP3Vol };

void RunDosingPumps() {
  float rate;
  byte dpTime;
  int dpRepeat, calcTime, totalVolume;
  const int numPumps = numDPumps;

  static byte doseTime[numPumps]={ 
    InternalMemory.read(memDPTime[0]), 
    InternalMemory.read(memDPTime[1]), 
    InternalMemory.read(memDPTime[2]) 
  };
  
  for (int i=0;i < numPumps; i++) {
    dpTime=InternalMemory.read(memDPTime[i]);
    dpRepeat=InternalMemory.read_int(memDPRepeat[i]);
    rate=(float)InternalMemory.read_int(memCalVol[i])/InternalMemory.read_int(memCalTime[i]);
    calcTime=(InternalMemory.read_int(memDPVolume[i])/rate)/(1440/dpRepeat);
    totalVolume=rate*(dpTime*(1440/dpRepeat)); 
    
 
    if (dpTime!=doseTime[i]) { // Memory has changed.
        InternalMemory.write_int(memDPVolume[i], totalVolume); // Update volume
        doseTime[i]=dpTime;
    } else if (dpTime!=calcTime) { // Calculated time has changed.
        InternalMemory.write(memDPTime[i], calcTime); // Update time
        doseTime[i]=calcTime;
    }

    // If any of these are on
    if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
      // Do nothing
    } else {
      ReefAngel.DosingPumpRepeat(pump[i], i*5, dpRepeat, doseTime[i]);
    }
  }
}

Post Reply