Dosing pump and "in the internal memory"
Dosing pump and "in the internal memory"
I'm (finally) getting going with my Reef Angel. I'm mostly playing around and learning right now.
Step 1 was to get my kalk dosing pump running. Using the wizard I got this all worked out with the "in the code" memory setting. No problem.
So now I wanted to try "in the internal memory". What happens to my variables here? It now calls DosingPumpRepeat1 instead of DosingPumpRepeat, but I see no place in the code (not the wizard) to change the values now.
My goal here (besides just learning) is to be able to change the dosing pump variables from the head unit rather than with the computer hooked up.
Step 1 was to get my kalk dosing pump running. Using the wizard I got this all worked out with the "in the code" memory setting. No problem.
So now I wanted to try "in the internal memory". What happens to my variables here? It now calls DosingPumpRepeat1 instead of DosingPumpRepeat, but I see no place in the code (not the wizard) to change the values now.
My goal here (besides just learning) is to be able to change the dosing pump variables from the head unit rather than with the computer hooked up.
Re: Dosing pump and "in the internal memory"
If you are using internal memory, you have to change those values using the Portal or smart phone apps.
Roberto.
Re: Dosing pump and "in the internal memory"
So I can't set up menus to control it with the joystick?
Re: Dosing pump and "in the internal memory"
You can if you code your own menu system.
There is nothing in the libraries created for that.
It's much easier to do that through one of the apps that we never focused on creating a menu system for that.
You can also use the Client if you don't have the wifi attachment.
There is nothing in the libraries created for that.
It's much easier to do that through one of the apps that we never focused on creating a menu system for that.
You can also use the Client if you don't have the wifi attachment.
Roberto.
Re: Dosing pump and "in the internal memory"
I have the wifi (but haven't played with it yet), but would really like everything to also be controllable with the joystick too unless it is an absolute nightmare.
I was peeking around the code and found ReefAngelClass::SetupOption, which I think edits a parameter when called from a menu event. I'm not sure how to set up a custom menu, I'll have to read more first. I found DosingPumpRepeat1 uses InternalMemory.DP1RepeatInterval_read() and InternalMemory.DP1Timer_read(), so I assume those are the ones I edit with SetupOption? And DP2 versions too (but no DP3 so I'd have to figure that out if I wanted a third pump setup this way). Am I generally on the right track here?
I was peeking around the code and found ReefAngelClass::SetupOption, which I think edits a parameter when called from a menu event. I'm not sure how to set up a custom menu, I'll have to read more first. I found DosingPumpRepeat1 uses InternalMemory.DP1RepeatInterval_read() and InternalMemory.DP1Timer_read(), so I assume those are the ones I edit with SetupOption? And DP2 versions too (but no DP3 so I'd have to figure that out if I wanted a third pump setup this way). Am I generally on the right track here?
Re: Dosing pump and "in the internal memory"
Correct...
There is someone else trying to get 3 pumps into internal memory too.
Maybe you guys can collaborate together too
He's using custom memories because of the 3rd pump, but you could use the ones you mentioned for DP1 and DP2 and then use custom for DP3... Up to you. Whatever makes it easier for you.
I did something similar to what you mentioned on another project...
Here is the menu entry I used, except it was temperature, but it is a good start for you to change it to DP stuff:
There is someone else trying to get 3 pumps into internal memory too.
Maybe you guys can collaborate together too

He's using custom memories because of the 3rd pump, but you could use the ones you mentioned for DP1 and DP2 and then use custom for DP3... Up to you. Whatever makes it easier for you.
I did something similar to what you mentioned on another project...
Here is the menu entry I used, except it was temperature, but it is a good start for you to change it to DP stuff:
Code: Select all
void MenuEntry2()
{
boolean bDone=false;
int u=InternalMemory.HeaterTempOn_read();
do
{
wdt_reset();
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 10,"Heater Temperature");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 100,"Up/Down to Change");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 110,"Press to Confirm");
char t[10];
ConvertNumToString(t, u, 10);
strcat(t," ");
ReefAngel.LCD.DrawText(COLOR_CRIMSON, COLOR_WHITE, 57, 55,t);
ReefAngel.LCD.DrawText(COLOR_CRIMSON,COLOR_WHITE, 65, 45, "^");
ReefAngel.LCD.DrawText(COLOR_CRIMSON,COLOR_WHITE, 65, 65, "`");
if (ReefAngel.Joystick.IsUp())
{
u++;
}
if (ReefAngel.Joystick.IsDown())
{
u--;
}
if (u>999) u=0;
if (u<0) u=999;
if (ReefAngel.Joystick.IsButtonPressed())
{
InternalMemory.HeaterTempOn_write(u);
heatertemp=u;
bDone=true;
}
}
while(!bDone);
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE; }
Roberto.
Re: Dosing pump and "in the internal memory"
Thanks. Looks like the next step for me is to figure out how to replace the menu.
Re: Dosing pump and "in the internal memory"
Thanks, that looks very helpful. I got a little distracted by my tunzes, but this is next on the list.
Re: Dosing pump and "in the internal memory"
So I got the sample custom menu put in. No problem. But now I can't get it out. I put the menu code in a #ifdef to disable it. Then I deleted the 2 lines in ReefAngelFeatures but when I go to upload it changes them back automatically and then tells me it can't find my menu functions. Any ideas? I'd like to be able to go back and forth to the standard menu and the custom one until I get it all worked out.
Re: Dosing pump and "in the internal memory"
It seems like it is just autodetecting the number of menu entries in my INO and changing the ReefAngelFeatures.h to match, even if they're inside disabled #ifdef's. Easy enough to work around that.
Re: Dosing pump and "in the internal memory"
Correct.
It auto-detects and will apply even if it is inside comments or #ifdef.
It auto-detects and will apply even if it is inside comments or #ifdef.
Roberto.
Re: Dosing pump and "in the internal memory"
So I got a custom int in InternalMemory set up, and created a custom menu that can edit it. Woohoo. The next step is to write something to pick which variable I'm editing. Thanks for all the help. I'm statrting to get a good feel for things.
Re: Dosing pump and "in the internal memory"
I have a few questions:
What is PROGMEM when declaring variables? When should I be using it?
How much internal memory do I have, and what addresses are safe to use?
what is wdt_reset? Do I need this in my menu input loops?
Do I need to call pingSerial in my menu input loops?
Is there anything else I should be calling in my input loops?
What is PROGMEM when declaring variables? When should I be using it?
How much internal memory do I have, and what addresses are safe to use?
what is wdt_reset? Do I need this in my menu input loops?
Do I need to call pingSerial in my menu input loops?
Is there anything else I should be calling in my input loops?
Re: Dosing pump and "in the internal memory"
http://arduino.cc/en/Reference/PROGMEM
Depends on which RA you have.
You have 1K on standard RA and 4K on RA+
watchdog is required to be executed within 1 second or it reboots the controller.
http://www.nongnu.org/avr-libc/user-man ... chdog.html
pingSerial takes care of wifi serial data handling.
Depends on which RA you have.
You have 1K on standard RA and 4K on RA+
watchdog is required to be executed within 1 second or it reboots the controller.
http://www.nongnu.org/avr-libc/user-man ... chdog.html
pingSerial takes care of wifi serial data handling.
Roberto.
Re: Dosing pump and "in the internal memory"
I have the RA+.
So then there is much more memory space with PROGMEM, but the stuff needs to stay constant. Perfect for what I need.
I learned about the watchdog the hard way.
I'm still confused about pingSerial. I am inside a loop waiting for joystick input, where I had to add the wdt_reset. I don't have the wifi installed yet, but when I do (very soon) should I be calling pingSerial there too?
And on the internal memory (InternalMemory.read), where should I start my numbering?
So then there is much more memory space with PROGMEM, but the stuff needs to stay constant. Perfect for what I need.
I learned about the watchdog the hard way.

I'm still confused about pingSerial. I am inside a loop waiting for joystick input, where I had to add the wdt_reset. I don't have the wifi installed yet, but when I do (very soon) should I be calling pingSerial there too?
And on the internal memory (InternalMemory.read), where should I start my numbering?
Re: Dosing pump and "in the internal memory"
Yes, you will need pingSerial, or you may not get the wifi commands to work while you are inside the menu.
You should be good to use memory location 100 to 200 or above 1024.
You should be good to use memory location 100 to 200 or above 1024.
Roberto.
Re: Dosing pump and "in the internal memory"
Thanks for all your help getting started.
I got the custom menu added, lots of constant arrays into the PROGMEM, added 3 more dosing pumps that dose by a daily amount and a pump rate, both those variables into internal memory for all 3 pumps, and a configurable variable editor into the menu.
Whew.
I got the custom menu added, lots of constant arrays into the PROGMEM, added 3 more dosing pumps that dose by a daily amount and a pump rate, both those variables into internal memory for all 3 pumps, and a configurable variable editor into the menu.
Whew.

- DrewPalmer04
- Posts: 818
- Joined: Tue May 29, 2012 2:12 pm
- Location: Christopher, IL
Re: Dosing pump and "in the internal memory"
Your code?
Re: Dosing pump and "in the internal memory"
I wouldn't consider this final code. I'd like to clean it up and test it a little more, but it seems to be working fine. If you have wifi, search for ping_Serial and uncomment it out. I don't have my wifi set up yet. That's next on the list.
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 <ReefAngel.h>
#include <avr/pgmspace.h>
////// Place global variable code below here
// Create the menu entries
// menu text <= 20 chars
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "Overheat Clear";
prog_char menu5_label[] PROGMEM = "PH Calibration";
prog_char menu6_label[] PROGMEM = "Date / Time";
prog_char menu7_label[] PROGMEM = "Version";
prog_char menu8_label[] PROGMEM = "Variables";
prog_char menu9_label[] PROGMEM = "Reserved";
// Group the menu entries together
PROGMEM const char *menu_items[] = {
menu1_label, menu2_label, menu3_label,
menu4_label, menu5_label, menu6_label,
menu7_label, menu8_label, menu9_label
};
#define DOSER3_PORT Port1
#define DOSER4_PORT Port2
#define DOSER5_PORT Port3
#define IM_DOSER3AMOUNT 120 // int16
#define IM_DOSER3RATE 122 // int16
#define IM_DOSER4AMOUNT 124 // int16
#define IM_DOSER4RATE 126 // int16
#define IM_DOSER5AMOUNT 128 // int16
#define IM_DOSER5RATE 130 // int16
// Number of variables to edit, The PROGMEM arrays below must match
#define IMEDIT_COUNT 6
// comment this out to turn off the ok/cancel button for variable editing
#define IMEDIT_OKCANCEL
// all IMEDIT strings must be <= 20 chars
prog_char imedit_name_0[] PROGMEM = "Doser 3 Amount";
prog_char imedit_name_1[] PROGMEM = "Doser 3 Rate";
prog_char imedit_name_2[] PROGMEM = "Doser 4 Amount";
prog_char imedit_name_3[] PROGMEM = "Doser 4 Rate";
prog_char imedit_name_4[] PROGMEM = "Doser 5 Amount";
prog_char imedit_name_5[] PROGMEM = "Doser 5 Rate";
PROGMEM const char *imedit_name[] = {
imedit_name_0, imedit_name_1, imedit_name_2, imedit_name_3, imedit_name_4, imedit_name_5
};
prog_char imedit_aux1_0[] PROGMEM = "mL/day";
prog_char imedit_aux1_1[] PROGMEM = "mL/min";
PROGMEM const char *imedit_aux1[] = {
imedit_aux1_0, imedit_aux1_1, imedit_aux1_0, imedit_aux1_1, imedit_aux1_0, imedit_aux1_1 // reuse the doser unit strings for 3 dosers
};
PROGMEM prog_int16_t imedit_mempos[] = {
IM_DOSER3AMOUNT, IM_DOSER3RATE, IM_DOSER4AMOUNT, IM_DOSER4RATE, IM_DOSER5AMOUNT, IM_DOSER5RATE
};
PROGMEM prog_char imedit_isbyte[] = {
0, 0, 0, 0, 0, 0
};
PROGMEM prog_int16_t imedit_min[] = {
0, 0, 0, 0, 0, 0
};
PROGMEM prog_int16_t imedit_max[] = {
32767, 32767, 32767, 32767, 32767, 32767
};
PROGMEM prog_int16_t imedit_format[] = {
1, 10, 1, 10, 1, 10
};
void RequiredForInputLoops() {
wdt_reset(); // watch dog timer reset, just leave this here
//**todo** enable ping_Serial() when wifi hooked up
//ping_Serial(); // handle wifi events
}
boolean IMEDIT_get_isbyte(int pos) {
if (pgm_read_byte(&(imedit_isbyte[pos]))!=0) return true;
return false;
}
int IMEDIT_get_format(int pos) {
return pgm_read_word(&(imedit_format[pos]));
}
int IMEDIT_get_mempos(int pos) {
return pgm_read_word(&(imedit_mempos[pos]));
}
int IMEDIT_get_min(int pos) {
return pgm_read_word(&(imedit_min[pos]));
}
int IMEDIT_get_max(int pos) {
return pgm_read_word(&(imedit_max[pos]));
}
void IMEdit_get_name(char *dest, int pos) {
int ptr;
ptr = pgm_read_word(&(imedit_name[pos]));
strcpy_P(dest, (char*)ptr);
}
void IMEdit_get_aux1(char *dest, int pos) {
int ptr;
ptr = pgm_read_word(&(imedit_aux1[pos]));
strcpy_P(dest, (char*)ptr);
}
void IMEdit_EditorByIMPos(int pos) {
char name[21];
char aux1[21];
IMEdit_get_name(name,pos);
IMEdit_get_aux1(aux1,pos);
IMEdit_Editor(name,
aux1,"","",
IMEDIT_get_min(pos),
IMEDIT_get_max(pos),
IMEDIT_get_mempos(pos),
IMEDIT_get_isbyte(pos),
IMEDIT_get_format(pos));
}
void IMEdit_Picker() {
int pos=0;
boolean done=false;
boolean redraw=true;
int i,j,y;
char str[21];
while (!done) {
RequiredForInputLoops();
// Draw the list with the item at pos at the center of the screen
// 11 items on screen
if (redraw) {
redraw=false;
y=10;
for (i=pos-5; i<=pos+5; i++) {
if ((i<0)||(i>IMEDIT_COUNT)) {
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, " ");
} else if (i==IMEDIT_COUNT) {
if (i==pos)
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 10, y, "Exit ");
else
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, "Exit ");
} else {
IMEdit_get_name(str,i);
int len=strlen(str);
for (j=0; j<20-len; j++) {
strcat(str," ");
}
if (i==pos)
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 10, y, str);
else
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, str);
}
y+=10;
}
}
if (ReefAngel.Joystick.IsUp()) {
if (pos > 0) {
pos--;
redraw=true;
} else if (pos==0) {
pos=IMEDIT_COUNT;
redraw=true;
}
}
if (ReefAngel.Joystick.IsLeft()) {
pos-=5;
if (pos<0) pos=0;
redraw=true;
}
if (ReefAngel.Joystick.IsDown()) {
if (pos < IMEDIT_COUNT) {
pos++;
redraw=true;
} else if (pos==IMEDIT_COUNT) {
pos=0;
redraw=true;
}
}
if (ReefAngel.Joystick.IsRight()) {
pos+=5;
if (pos > IMEDIT_COUNT) pos=IMEDIT_COUNT;
redraw=true;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
if (pos==IMEDIT_COUNT) {
// Exit selected
done=true;
} else {
ReefAngel.LCD.Clear(COLOR_WHITE,1,1,132,132);
IMEdit_EditorByIMPos(pos);
ReefAngel.LCD.Clear(COLOR_WHITE,1,1,132,132);
redraw=true;
}
}
}
}
void IMEdit_Editor(char *Name, char *auxline1, char *auxline2, char *auxline3, int minval, int maxval, int memorypos, boolean is_byte, int format) {
int val;
char str[10];
int x;
boolean redraw=true;
boolean done=false;
// read the variable from memory
if (!is_byte) {
val=InternalMemory.read_int(memorypos);
} else {
val=InternalMemory.read(memorypos);
}
// constrain it based on min/max
if (val<minval) val=minval;
if (val>maxval) val=maxval;
// set up the stepping
int step=1;
int maxstep=10000;
while (maxstep > maxval) {
maxstep /= 10;
}
#define IMEDIT_VAR_Y 70
#define IMEDIT_STEP_Y 105
// draw the header and footer
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 6, 10, " ");
x=66-3*strlen(Name); // center text
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, x, 10, Name);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 20, auxline1);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 30, auxline2);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 40, auxline3);
//x=66-3*strlen("Press when done"); // center text
//x=66-3*15=21
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 21, 120,"Press when done");
// input loop
while (!done) {
RequiredForInputLoops();
if (redraw) {
redraw=false;
// draw variable
ConvertNumToString(str, val, format);
x=66-3*strlen(str); // center text
ReefAngel.LCD.Clear(COLOR_BLUE,33,IMEDIT_VAR_Y-16,98,IMEDIT_VAR_Y+22);
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 39, IMEDIT_VAR_Y-8, " ^ ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 39, IMEDIT_VAR_Y+8, " ` ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, x, IMEDIT_VAR_Y, str);
// draw step
ConvertNumToString(str, step, format);
x=strlen(str);
x=66-3*x;
ReefAngel.LCD.Clear(COLOR_WHITE,40,IMEDIT_STEP_Y-1,92,IMEDIT_STEP_Y+10);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 57, IMEDIT_STEP_Y-8, "+/-");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, x, IMEDIT_STEP_Y, str);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 33, IMEDIT_STEP_Y, "<");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 93, IMEDIT_STEP_Y, ">");
}
if (ReefAngel.Joystick.IsUp()) {
// increase variable
if (maxval - step < val)
val=maxval;
else
val+=step;
redraw=true;
}
if (ReefAngel.Joystick.IsDown()) {
// decrease variable
if (minval + step > val)
val=minval;
else
val-=step;
redraw=true;
}
if (ReefAngel.Joystick.IsRight()) {
// increase step
if (step==maxstep) step=1;
else step*=10;
redraw=true;
}
if (ReefAngel.Joystick.IsLeft()) {
// decrease step
if (step==1) step=maxstep;
else step/=10;
redraw=true;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
#ifdef IMEDIT_OKCANCEL
// switch to ok/cancel mode
while (ReefAngel.Joystick.IsButtonPressed()); // don't start until joystick is lifted
boolean isok=true;
ReefAngel.LCD.Clear(COLOR_WHITE,1,IMEDIT_STEP_Y-9,132,132);
while (!done) {
RequiredForInputLoops();
if (isok) {
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 12, 110," Ok ");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 72, 110," Cancel ");
} else {
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 12, 110," Ok ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 72, 110," Cancel ");
}
if (ReefAngel.Joystick.IsRight()||ReefAngel.Joystick.IsLeft()||ReefAngel.Joystick.IsUp()||ReefAngel.Joystick.IsDown()) {
isok=!isok;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
done=true; // this ends 2 while loops, taking us outside the outer loop
}
} // end while
#else
isok=true;
done=true;
#endif
if (isok) {
// just set the variable, no ok/cancel
if (is_byte)
InternalMemory.write(memorypos,byte(val));
else
InternalMemory.write_int(memorypos,val);
}
} //end if button pressed
} // end while
}
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry4()
{
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry5()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry6()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.DisplayVersion();
}
void MenuEntry8()
{
IMEdit_Picker();
ReefAngel.DisplayedMenu=ALT_SCREEN_MODE;
}
void MenuEntry9()
{
// Not yet implemented
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
//ReefAngel.AddStandardMenu(); // Add Standard Menu
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = 0;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = 0;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = 0;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Set the Overheat temperature setting
InternalMemory.OverheatTemp_write( 840 );
// Ports that are always on
////// Place additional initialization code below here
////// Place additional initialization code above here
}
void RunDosers345() {
double dose; // applied every 15 minutes, 96 times per day
double rate;
int tmp;
tmp=InternalMemory.read_int(IM_DOSER3AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER3RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER3_PORT,0,15,tmp);
}
tmp=InternalMemory.read_int(IM_DOSER4AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER4RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER4_PORT,5,15,tmp);
}
tmp=InternalMemory.read_int(IM_DOSER5AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER5RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER5_PORT,10,15,tmp);
}
}
void loop()
{
RunDosers345();
////// Place your custom code above here
// This should always be the last line
ReefAngel.ShowInterface();
}