Lee's Feature Complete PDE
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
I am going to add to the dosing log routine the tracking of my water change pump. All the routines are there already and ready to go, just have to add the variables and have it clear when water change mode starts. This will also tell me exactly how much water is in my mixing bin
i want to calibrate the water level gauge at some point and then i can use that to calibrate the ro/di one. I have a week to get that done 
-
InPandaWeTrust
- Posts: 11
- Joined: Tue Oct 08, 2013 3:12 pm
Re: Lee's Feature Complete PDE
Hi Lee, you've really written some awesome code here! I've been threading through it and adjusting it for my needs for 2 weeks solid
Thanks for sharing!
What ReefAngel Class are you using? I'm getting a lot of compiling errors, things like "has no member named 'CustomVar'" so I assume you have edited the ReefAngel class that comes with the installer?
On the first page, you mention 3 lines that need to be put in the update/features txt. I can't seem to find the file you mention, could you please point me in the right direction?
What ReefAngel Class are you using? I'm getting a lot of compiling errors, things like "has no member named 'CustomVar'" so I assume you have edited the ReefAngel class that comes with the installer?
On the first page, you mention 3 lines that need to be put in the update/features txt. I can't seem to find the file you mention, could you please point me in the right direction?
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
The ReefAngel class I'm using is from the wifialert patch that i have in my git repo.
http://github.com/lnevo/Libraries
The CustomVar array is part of the stock libraries...can you post the specific error?
http://github.com/lnevo/Libraries
The CustomVar array is part of the stock libraries...can you post the specific error?
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
The feature.txt file should be in Documents\Arduino\update folder
If its not there Roberto please chime in
Those *should* have been in current libraries but i think they were "misplaced". I'm hoping they will be in the next update.
So what elements have you used? Post a thread
love to see what others are doing with it!
If its not there Roberto please chime in
So what elements have you used? Post a thread
-
InPandaWeTrust
- Posts: 11
- Joined: Tue Oct 08, 2013 3:12 pm
Re: Lee's Feature Complete PDE
I did have a look in both of my folders (Documents + Program Files) but found nothing like that. Have downloaded the zip from github now, hopefully this fixes my errorslnevo wrote:The feature.txt file should be in Documents\Arduino\update folder
If its not there Roberto please chime inThose *should* have been in current libraries but i think they were "misplaced". I'm hoping they will be in the next update.
So what elements have you used? Post a threadlove to see what others are doing with it!
I'm probably using about 70% of your code, the portions to do with certain things like water changes + auto feeding I've taken out. While automation is great, there are certain things I would just like to have control of myself
Your tide class is amazing BTW. I'm hoping to use reef crest during the day and make a tidal surge(albeit lower flow rates
In the section of your code where you set up all the flow modes, is there an option for randomly picking a mode for the day or morning/afternoon? How would i modify your code to make it use tidal swell mode as the night mode? Sorry for derailing your thread
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
If you look in SetRF() you'll see where I'm setting it to night mode. Just change it to the mode you want and make sure the night mode memory location is set to true.
If its not in Documents\Arduino i have no clue where it installs to on Windows. Did you install the ReefAngel installer or did you manually install the libraries into the Arduino installation?
If its not in Documents\Arduino i have no clue where it installs to on Windows. Did you install the ReefAngel installer or did you manually install the libraries into the Arduino installation?
-
InPandaWeTrust
- Posts: 11
- Joined: Tue Oct 08, 2013 3:12 pm
Re: Lee's Feature Complete PDE
Thinking back, I initially used the reef angel installer but had to reinstall my PC so i just saved my libraries and the code I was working on then copied it over. This might be the root of my probelmlnevo wrote:If you look in SetRF() you'll see where I'm setting it to night mode. Just change it to the mode you want and make sure the night mode memory location is set to true.
If its not in Documents\Arduino i have no clue where it installs to on Windows. Did you install the ReefAngel installer or did you manually install the libraries into the Arduino installation?
One thing I wondered while I was going through your code, what does RF stand for? I've got jebao wp25s so I've been editing all the vortech stuff to make it work for my pumps. Have to DIY my own cables for them which is going to be fun
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
I have the vortechs which use an RF signal generated via the RF module.
-
InPandaWeTrust
- Posts: 11
- Joined: Tue Oct 08, 2013 3:12 pm
Re: Lee's Feature Complete PDE
I have tried compiling your code(without any modifications) using only your libraries from github and I still get the error when compiling about CustomVar not being part of the ReefAngelClass. It mentions others as well, but maybe its part of the same problem. Its all with the reefangel class. I've tried through the reefangel installer and normal arduino. The lines it highlights are in a few of the functions, sunrise, dosing pumps etc. Any ideas on how I can fix that? I should probably start a new thread for this :/
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Yeah my code doesn't compile so easily I've discovered but that doesn't explain why its complaining about CustomVar..
Can you create a wizard based code or use one of the examples and add a line in loop:
ReefAngel.CustomVar[0]=1;
And see what happens?
Can you create a wizard based code or use one of the examples and add a line in loop:
ReefAngel.CustomVar[0]=1;
And see what happens?
-
InPandaWeTrust
- Posts: 11
- Joined: Tue Oct 08, 2013 3:12 pm
Re: Lee's Feature Complete PDE
With this in the loop :
I get the 'classReefAngelClass' has no member named 'CustomVar'. I tried the same code on the pre-loaded RA example and then there was no such error. What does CustomVar do? Maybe I can edit it out or change it
Code: Select all
void loop()
{
ReefAngel.CustomVar[0]=1;
ReefAngel.PWM.SetDaylight(MoonPhase());
ReefAngel.PWM.SetActinic(MoonPhase());
ReefAngel.ShowInterface();
}
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Pfft, I have a bug with my water change code and the ATO is getting overridden causing the timeout to trigger cause the pump never goes on... gonna have to find some time to troubleshoot this. 
I don't see anywhere it's not working and I even went back a version and still not working.. WTF!
I don't see anywhere it's not working and I even went back a version and still not working.. WTF!
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Found my bug! Now I get to fix it. I setup the ATO Refill / WC Pump as a DosingPump so I could take advantage of my rate calculation and use it to determine the water change volume
The problem is that with the RunDosingPumps() will turn it off... I could always move RefillATO and AutoWaterChange to run after the RunDosingPumps... or fix it in the code... let's see....
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Ok, Not *thrilled* with my fix... but it works. I created a new numPumps variable inside RunDosing that = numDPumps - 1 so that it wouldn't iterate over the last pump defined. I'll need to find a better way to do this.. maybe this will stimulate me to write the DosingPump class because then I can have an option belonging to that object to run or not run (i.e. log and/or calibrate only...) This has been needed for a while to make this mechanism easier for people to use.
Anyway, latest code is posted as usual. Hopefully next WC will not be as painful...
Anyway, latest code is posted as usual. Hopefully next WC will not be as painful...
-
bencollinz
- Posts: 140
- Joined: Wed Mar 13, 2013 5:36 pm
Re: Lee's Feature Complete PDE
so, I've been trying to copy/implement your random vortech modes/times code into mine. and I'm just not getting it. is there any way you can dumb it down for me please?
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
I'm not really using the vortech modes in the random routine, but it could be easily modified to do so. I'll take a look at the code later and see what I can break out for you.
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Here is PaulTurner911's code modified for the Vortech RF module. I have only tested to make sure this compiles.
What this will do is go into go into Smart_NTM for one hour AFTER feeding mode ends ***. It will go into night mode between 10:30pm and 8:30am and then finally if the mode is set to Custom in the portal, it will select a random mode from the modes[] array.
If you want more modes added just add it to the array. If you want a mode to happen more frequently... add it multiple times in the array.
Enjoy
The original code made for DCPump is here http://forum.reefangel.com/viewtopic.php?p=32261#p32261
*** Paul's original request was to have Smart_NTM happen DURING feeding mode which I don't think the majority want. You wan the post feeding mode if statement to look like this if you want it after.
What this will do is go into go into Smart_NTM for one hour AFTER feeding mode ends ***. It will go into night mode between 10:30pm and 8:30am and then finally if the mode is set to Custom in the portal, it will select a random mode from the modes[] array.
If you want more modes added just add it to the array. If you want a mode to happen more frequently... add it multiple times in the array.
Enjoy
Code: Select all
// Add random mode if we set to Mode to Custom in portal
static int rmode;
static boolean changeMode=true;
// These are the modes we can cycle through. You can add more and even repeat...
byte modes[] = { ReefCrest, TidalSwell, ShortPulse, NutrientTransport };
if (now()%SECS_PER_DAY==0 || changeMode==true) { // Change at midnight or if controller rebooted
rmode=random(100)%sizeof(modes); // Change the mode once per day to pick from our array
changeMode=false;
}
// Set timer when in feeding mode
static unsigned long feeding;
if (ReefAngel.DisplayedMenu==FEEDING_MODE) feeding=now();
if (now()-feeding>0 && now()-feeding<3600) {
// Continue NTM for the 60 minutes
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.Mode=NutrientTransport;
} else if (now()%SECS_PER_DAY<30600 || now()%SECS_PER_DAY>=81000) { // 8:30am / 10:30pm
// Night mode (go to 30%)
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.Mode=Constant;
ReefAngel.RF.Speed=30;
} else if (InternalMemory.RFMode_read()==11) {
// Custom Mode and nothing else going on
ReefAngel.RF.UseMemory=false;
ReefAngel.RF.Mode=modes[rmode]; // Put the mode to the random mode :)
ReefAngel.RF.Speed=InternalMemory.RFSpeed_read(); // Set speed from portal
} else {
ReefAngel.RF.UseMemory=true; // Will reset all values from memory
}
*** Paul's original request was to have Smart_NTM happen DURING feeding mode which I don't think the majority want. You wan the post feeding mode if statement to look like this if you want it after.
Code: Select all
if (now()-feeding>0 && now()-feeding<3600) {
-
bencollinz
- Posts: 140
- Joined: Wed Mar 13, 2013 5:36 pm
Re: Lee's Feature Complete PDE
ok. thank you very much. I really do appreciate you continually helping me! I will look this over.
-
aranax
- Posts: 120
- Joined: Thu Jun 02, 2011 11:54 pm
Re: Lee's Feature Complete PDE
Hey Lee et al,
Have you seen this issue before? I used your PDE an altered it slightly to fit my needs (changed port assignment, commented out things I don't use, etc.). The issue i have is my controller freezes on what looks to be a corrupted chart-like screen.
Symptom
- RA randomly freezes
--- Current state of ports are frozen
--- Unit does not respond to menu initiation
--- Requires hard restart to get the controller back online
I tried to resolve it by enabling the watchdog timer that I used with my older RA but that didn't work.
Can anyone please help? Thanks.
Frozen:

After Reboot/Normal

Jeremiah
Have you seen this issue before? I used your PDE an altered it slightly to fit my needs (changed port assignment, commented out things I don't use, etc.). The issue i have is my controller freezes on what looks to be a corrupted chart-like screen.
Symptom
- RA randomly freezes
--- Current state of ports are frozen
--- Unit does not respond to menu initiation
--- Requires hard restart to get the controller back online
I tried to resolve it by enabling the watchdog timer that I used with my older RA but that didn't work.
Can anyone please help? Thanks.
Frozen:

After Reboot/Normal

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 <RA_Colors.h>
#include <RA_CustomColors.h>
#include <RF.h>
#include <ReefAngel.h>
#include <SunLocation.h>
#include <WaterLevel.h>
#include <Tide.h>
#include <Moon.h>
#include <WiFiAlert.h>
// Custom menus
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Refugium Light";
prog_char menu2_label[] PROGMEM = "Feeding Mode";
prog_char menu3_label[] PROGMEM = "Water Change";
prog_char menu4_label[] PROGMEM = "Vortech Mode";
prog_char menu5_label[] PROGMEM = "ATO Clear";
prog_char menu6_label[] PROGMEM = "Overheat Clear";
prog_char menu7_label[] PROGMEM = "PH Calibration";
prog_char menu8_label[] PROGMEM = "WLS Calibration";
prog_char menu9_label[] PROGMEM = "Date / Time";
// 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 Custom Memory Locations
#define Mem_B_MoonSlope 100
#define Mem_B_Vacation 101
#define Mem_B_AutoFeed 102
#define Mem_B_AutoFeedPress 103
#define Mem_B_AutoFeedRepeat 104
#define Mem_B_AutoFeedOffset 105
#define Mem_I_WCFillTime 106
#define Mem_I_Latitude 108
#define Mem_I_Longitude 110
#define Mem_B_AcclRiseOffset 112
#define Mem_B_AcclSetOffset 113
#define Mem_B_AcclDay 114
#define Mem_B_SwabbieRepeat 115
#define Mem_B_SwabbieTime 116
#define Mem_B_TideMin 117
#define Mem_B_TideMax 118
#define Mem_B_PumpOffset 119
#define Mem_B_FeedingRF 120
#define Mem_B_NightRF 121
#define Mem_B_NightSpeed 122
#define Mem_B_NightDuration 123
#define Mem_B_NTMSpeed 124
#define Mem_B_NTMDuration 125
#define Mem_B_NTMDelay 126
#define Mem_B_NTMTime 127
#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_B_LogATO 140
#define Mem_B_LogPrevATO 141
#define Mem_B_RateAlarm 142
#define Mem_B_TideMode 143
#define Mem_B_MaintGAC 144
#define Mem_B_MaintGFO 145
#define Mem_B_MaintCal 146
#define Mem_B_MaintAlk 147
#define Mem_B_MaintWC 148
#define Mem_B_MaintATO 149
#define Mem_B_MaintFeeding 150
#define Mem_B_MaintSkimmer 151
#define Mem_B_MaintSocks 152
#define Mem_B_MaintWCVol 153
#define Mem_I_CalDP3Vol 154
#define Mem_I_CalDP3Time 156
#define Mem_I_DP3Volume 158
#define Mem_B_PrintDebug 198
#define Mem_B_ResetMemory 199
void init_memory() {
// Initialize Custom Memory Locations
InternalMemory.write(Mem_B_MoonSlope,60);
InternalMemory.write(Mem_B_Vacation,false);
InternalMemory.write(Mem_B_AutoFeed,true);
InternalMemory.write(Mem_B_AutoFeedPress,2);
InternalMemory.write(Mem_B_AutoFeedRepeat,4);
InternalMemory.write(Mem_B_AutoFeedOffset,3);
InternalMemory.write_int(Mem_I_WCFillTime,240);
InternalMemory.write_int(Mem_I_Latitude,21);
InternalMemory.write_int(Mem_I_Longitude,-73);
InternalMemory.write(Mem_B_AcclRiseOffset,4);
InternalMemory.write(Mem_B_AcclSetOffset,2);
InternalMemory.write(Mem_B_AcclDay,0);
InternalMemory.write(Mem_B_SwabbieRepeat,6);
InternalMemory.write(Mem_B_SwabbieTime,1);
InternalMemory.write(Mem_B_TideMin,10);
InternalMemory.write(Mem_B_TideMax,30);
InternalMemory.write(Mem_B_PumpOffset,70);
InternalMemory.write(Mem_B_FeedingRF,true);
InternalMemory.write(Mem_B_NightRF,false);
InternalMemory.write(Mem_B_NightSpeed,30);
InternalMemory.write(Mem_B_NightDuration,10);
InternalMemory.write(Mem_B_NTMSpeed,65);
InternalMemory.write(Mem_B_NTMDuration,5);
InternalMemory.write(Mem_B_NTMDelay,15);
InternalMemory.write(Mem_B_NTMTime,150);
InternalMemory.write_int(Mem_I_CalDP1Vol,11);
InternalMemory.write_int(Mem_I_CalDP1Time,600);
InternalMemory.write_int(Mem_I_DP1Volume,30);
InternalMemory.write_int(Mem_I_CalDP2Vol,11);
InternalMemory.write_int(Mem_I_CalDP2Time,600);
InternalMemory.write_int(Mem_I_DP2Volume,30);
InternalMemory.write_int(Mem_I_CalDP3Vol,3);
InternalMemory.write_int(Mem_I_CalDP3Time,270);
InternalMemory.write_int(Mem_I_DP3Volume,0);
InternalMemory.write(Mem_B_RateAlarm,15);
InternalMemory.write(Mem_B_TideMode,0);
InternalMemory.write(Mem_B_LogATO,0);
InternalMemory.write(Mem_B_LogPrevATO,0);
// InternalMemory.write(Mem_B_MaintGAC,0);
// InternalMemory.write(Mem_B_MaintGFO,0);
// InternalMemory.write(Mem_B_MaintCal,0);
// InternalMemory.write(Mem_B_MaintAlk,0);
InternalMemory.write(Mem_B_MaintWC,0);
InternalMemory.write(Mem_B_MaintATO,0);
InternalMemory.write(Mem_B_MaintFeeding,0);
InternalMemory.write(Mem_B_MaintSkimmer,0);
InternalMemory.write(Mem_B_MaintSocks,0);
InternalMemory.write(Mem_B_ResetMemory,false);
}
#define NUMBERS_8x16
// Define Portal Variables
#define Var_Feedings 0
#define Var_DPump1 1
#define Var_DPump2 2
#define Var_AcclDay 3
#define Var_Tide 4
#define Var_TideMode 5
#define Var_LogATO 6
#define Var_WCVol 7
// Define Relay Ports by Name
#define RightLED 1
#define ATOSump 2
#define MidLED 3
#define Heater 4
#define Skimmer 5
#define SumpCirc 6
#define Return 7
#define WaveRight 8
//#define WhiteLEDs 3
//#define BlueLEDs 4
//#define Extension 5
//#define Refugium 7
//#define Reactor 8
#define LeftLED Box1_Port1
#define Vortech1 Box1_Port2
#define Reflights Box1_Port3
#define Dosing Box1_Port4
#define TBD2 Box1_Port5
#define WaveLeft Box1_Port6
#define Feeder Box1_Port7
#define ATOres Box1_Port8
//#define Swabbie Box1_Port1
//#define Feeder Box1_Port2
//#define Vortech2 Box1_Port4
//#define VortechUPS Box1_Port5
//#define Unused Box1_Port6
//#define DPump1 Box1_Port7
//#define DPump2 Box1_Port8
#define VO_RefillATO Box2_Port1
#define VO_EnableATO Box2_Port2
#define VO_StartFill Box2_Port3
#define VO_Vacation Box2_Port4
#define VO_AutoFeed Box2_Port5
#define VO_Unused Box2_Port6
#define VO_Calibrate Box2_Port7
#define VO_LockPorts Box2_Port8
////// Place global variable code below here
boolean dosing=false;
byte vtechmode;
//boolean bFeeding=false;
// Custom classes
SunLocation sun;
Tide tide;
// Vortech Variables
byte vtMode, vtSpeed, vtDuration;
// Needs to be global for DrawCustomGraph()
int ScreenID=1;
////// Place global variable code above here
// Setup on controller startup/reset
void setup()
{
// This must be the first line
wdt_enable(WDTO_1S);
ReefAngel.Init(); //Initialize controller
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items)); // Initialize Menu
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port7Bit | Port5Bit ;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port7Bit | Port5Bit ;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Box1_Port3;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port4Bit ;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Ports that default on
ReefAngel.Relay.On(Return);
ReefAngel.Relay.On(ATOSump);
ReefAngel.Relay.On(Vortech1);
ReefAngel.Relay.On(SumpCirc);
//ReefAngel.Relay.On(VortechUPS);
//ReefAngel.Relay.On(VO_LockPorts);
// Ports that default off
//ReefAngel.Relay.Off(Extension);
//ReefAngel.Relay.Off(Swabbie);
//ReefAngel.Relay.Off(Feeder);
//ReefAngel.Relay.Off(Unused);
////// Place additional initialization code below here
ReefAngel.RF.UseMemory=false;
randomSeed(now()/SECS_PER_DAY);
if (InternalMemory.read(Mem_B_ResetMemory))
init_memory();
////// Place additional initialization code above here
}
void loop()
{
//ReefAngel.MoonLights(Refugium);
//ReefAngel.ActinicLights(BlueLEDs);
//ReefAngel.StandardLights(WhiteLEDs);
ReefAngel.StandardHeater( (Heater),751,780 );
//ReefAngel.StandardLights( (Skimmer),0,0,20,0 );
ReefAngel.StandardLights( (WaveRight),6,0,11,30 );
ReefAngel.DosingPumpRepeat( (Dosing),0,60,10 );
ReefAngel.StandardLights( (WaveLeft),11,30,17,30 );
ReefAngel.StandardHeater(Heater);
////// Place your custom code below here
CheckPower(); // Monitor for power outages
// CheckSwitches(); // Monitor for float switches
SetSun(); // Setup Sun rise/set lighting
SetMoon(); // Setup Moon rise/set lighting
SetTide(); // Set High/Low tide properties
SetRF(); // Set Vortech modes
CheckATO(); // Calculate ATO reservoir evaporation
// RefillATO(); // Automatic ATO reservoir refill
// Vacation(); // Automation while on Vacation
// LogFeedings(); // Track a relay
// RunFeeder(); // Automatic Fish feeder
// RunSwabbie(); // Skimmer neck cleaner
// RunDosingPumps(); // Dose by volume or time
// LogDosingPumps(); // Keep track of dosing
// CalibrateDPumps(); // Calibrate Dosing pumps
// AutoWaterChange(); // Automated water change
Reminders(); // Increment maintenance counters
// LockPorts(); // Clear overrides for critical ports
DelayedOnModes(Skimmer); // DelayedOn after mode change only
//Dosing
if (ReefAngel.Params.PH<800 && !dosing)
{
ReefAngel.Timer[1].Start();
ReefAngel.Timer[2].Start();
ReefAngel.Relay.On((Dosing));
dosing=true;
}
if (ReefAngel.Timer[1].IsTriggered())
{
ReefAngel.Relay.Off((Dosing));
}
if (ReefAngel.Timer[2].IsTriggered())
{
dosing=false;
}
if (ReefAngel.Params.PH>855 && dosing)
{
ReefAngel.Relay.Off((Dosing));
}
//ATO Res.
ReefAngel.WaterLevelATO( Box1_Port8,600,10,80 );
//Skimmer failsafe
if (ReefAngel.Relay.Status((Return)) != 1)
{
ReefAngel.Relay.Off((Skimmer));
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "aranax" , "");
ReefAngel.ShowInterface();
}
void CheckPower() {
static boolean powerOutage=false;
static WiFiAlert powerAlert;
// Power Outage - turn off everything
if (!ReefAngel.Relay.IsRelayPresent(EXP1_RELAY)) // Expansion Relay NOT present
{
powerOutage=true;
ReefAngel.Relay.Off(Skimmer);
//ReefAngel.Relay.Off(WhiteLEDs);
//ReefAngel.Relay.Off(BlueLEDs);
//ReefAngel.Relay.Off(Extension);
ReefAngel.Relay.Off(Heater);
//ReefAngel.Relay.Off(Refugium);
//ReefAngel.Relay.Off(Reactor);
ReefAngel.Relay.Off(Vortech1);
//ReefAngel.Relay.Off(Vortech2);
//ReefAngel.Relay.Off(VortechUPS);
powerAlert.Send("Power+outage!");
}
// Power Restored - Turn things back on
if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
{
LastStart=now();
powerOutage=false;
//ReefAngel.Relay.On(Reactor);
ReefAngel.Relay.On(Vortech1);
//ReefAngel.Relay.On(Vortech2);
//ReefAngel.Relay.On(VortechUPS);
powerAlert.Send("Power+restored.",true);
}
}
void CheckSwitches() {
static boolean returnOverride=true;
static WiFiAlert switchAlert, skimmerAlert, atoAlert;
skimmerAlert.SetDelay(3600);
atoAlert.SetDelay(3600);
if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE)
returnOverride=true;
// Turn off return if sump overflowing or out of water in return
ReefAngel.ReverseATOHigh(); // swap switch behavior so we can reuse as ATO
if (!ReefAngel.HighATO.IsActive()) { // switch on by default
if (!returnOverride) {
switchAlert.Send("Sump+level+alarm!+Return+pump+disabled.");
ReefAngel.Relay.Override(Return,0);
returnOverride=true;
}
} else {
returnOverride=false;
}
// Turn off Skimmer if waste collector is full.
if (!ReefAngel.LowATO.IsActive()) { // switch is on by default
skimmerAlert.Send("Skimmate+container+full!+Skimmer+disabled.");
ReefAngel.Relay.Override(Skimmer,0);
if (InternalMemory.read(Mem_B_MaintSkimmer) > 0) // Reset last Skimmer counter
InternalMemory.write(Mem_B_MaintSkimmer,0);
}
// Turn off Skimmer if Return pump has been shutoff.
if (!ReefAngel.Relay.Status(Return)) {
ReefAngel.Relay.Override(Skimmer,0);
}
// Alert if ATO timeput flag
if (bitRead(ReefAngel.AlertFlags,ATOTimeOutFlag))
atoAlert.Send("ATO+timeout!+ATO+disabled.");
}
void SetSun() {
// Start acclimation routine
int acclRiseOffset=InternalMemory.read(Mem_B_AcclRiseOffset)*60;
int acclSetOffset=InternalMemory.read(Mem_B_AcclSetOffset)*60;
byte acclDay=InternalMemory.read(Mem_B_AcclDay);
ReefAngel.CustomVar[Var_AcclDay]=acclDay;
// See if we are acclimating corals and decrease the countdown each day
static boolean acclCounterReady=false;
if (now()%SECS_PER_DAY!=0) acclCounterReady=true;
if (now()%SECS_PER_DAY==0 && acclCounterReady && acclDay>0) {
acclDay--;
acclCounterReady=false;
InternalMemory.write(Mem_B_AcclDay,acclDay);
}
// End acclimation
sun.Init(InternalMemory.read_int(Mem_I_Latitude), InternalMemory.read_int(Mem_I_Longitude));
sun.SetOffset(-1,(acclDay*acclRiseOffset),-1,(-acclDay*acclSetOffset)); // Bahamas
sun.CheckAndUpdate(); // Calculate today's Sunrise / Sunset
}
void SetMoon() {
// rfi
byte offset=InternalMemory.read(Mem_B_MoonSlope);
byte startD=InternalMemory.read(Mem_B_PWMSlopeStartD);
byte endD=InternalMemory.read(Mem_B_PWMSlopeEndD);
byte timeD=InternalMemory.read(Mem_B_PWMSlopeDurationD);
byte startA=InternalMemory.read(Mem_B_PWMSlopeStartA);
byte endA=InternalMemory.read(Mem_B_PWMSlopeEndA);
byte timeA=InternalMemory.read(Mem_B_PWMSlopeDurationA);
static byte mp=MoonPhase();
if (mp!=MoonPhase()) {
InternalMemory.write(Mem_B_PWMSlopeEndD,mp);
InternalMemory.write(Mem_B_PWMSlopeEndA,mp);
mp=MoonPhase();
}
moon_init(InternalMemory.read_int(Mem_I_Latitude), InternalMemory.read_int(Mem_I_Longitude));
ReefAngel.PWM.SetDaylight(PWMSlope(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM, startD,endD,timeD,0));
ReefAngel.PWM.SetActinic(PWMSlope(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM, startA,endA,timeA,0));
}
void SetTide() {
// rfi
byte nightSpeed=InternalMemory.read(Mem_B_NightSpeed);
byte tideMin=InternalMemory.read(Mem_B_TideMin);
byte tideMax=InternalMemory.read(Mem_B_TideMax);
// Set tide offsets
tide.SetOffset(tideMin, tideMax);
// Set tide speed. Slope in/out of Night Mode
tide.SetSpeed(PWMSlope(sun.GetRiseHour()-1,sun.GetRiseMinute(),
sun.GetSetHour(),sun.GetSetMinute(),nightSpeed+tideMin,vtSpeed,120,nightSpeed+tideMin));
// Show tide info on portal
ReefAngel.CustomVar[Var_Tide]=tide.CalcTide();
}
void SetRF() {
int ntmDelay=InternalMemory.read(Mem_B_NTMDelay)*60;
int ntmTime=InternalMemory.read(Mem_B_NTMTime)*60;
boolean nightRF=InternalMemory.read(Mem_B_NightRF);
boolean feedingRF=InternalMemory.read(Mem_B_FeedingRF);
static time_t t;
vtMode=InternalMemory.RFMode_read();
vtSpeed=InternalMemory.RFSpeed_read();
vtDuration=InternalMemory.RFDuration_read();
if ((now()-t > ntmDelay && now()-t < ntmTime+ntmDelay) && feedingRF) {
// Post feeding mode
vtMode=Smart_NTM;
vtSpeed=InternalMemory.read(Mem_B_NTMSpeed);
vtDuration=InternalMemory.read(Mem_B_NTMDuration);
} else if (!sun.IsDaytime() && nightRF) {
vtMode=Night;
vtSpeed=InternalMemory.read(Mem_B_NightSpeed);
vtDuration=InternalMemory.read(Mem_B_NightDuration);
} else {
if (vtMode!=Night && ReefAngel.RF.Mode==Night)
ReefAngel.RF.SetMode(Night_Stop,0,0);
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) {
t=now(); // Run post feeding mode when this counter stops
} else if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
ReefAngel.RF.SetMode(Constant,25,0);
} else {
if ((vtMode==Smart_NTM) || (vtMode==ShortPulse)) vtDuration=InternalMemory.read(Mem_B_NTMDuration);
(vtMode==Custom) ? RFCustom() : ReefAngel.RF.SetMode(vtMode,vtSpeed,vtDuration);
}
}
void RFCustom() {
static boolean changeMode;
byte rcSpeed, rcSpeedAS;
// Define new modes
const int BHazard=15;
const int Else=16;
const int Sine=17;
byte tideSpeed=tide.CalcTide();
byte tideMin=InternalMemory.read(Mem_B_TideMin);
byte tideMax=InternalMemory.read(Mem_B_TideMax);
byte tideMode=InternalMemory.read(Mem_B_TideMode);
float pumpOffset=(float) InternalMemory.read(Mem_B_PumpOffset)/100;
byte RandomModes[]={ ReefCrest, TidalSwell, Smart_NTM, Lagoon, ShortPulse, LongPulse, BHazard, Else, Sine };
if (now()%SECS_PER_DAY!=0) changeMode=true;
if ((now()%SECS_PER_DAY==0 && changeMode)) {
tideMode=random(100)%sizeof(RandomModes);
InternalMemory.write(Mem_B_TideMode,tideMode);
changeMode=false;
}
ReefAngel.CustomVar[Var_TideMode]=tideMode+1;
switch (RandomModes[tideMode]) {
case ReefCrest: {
rcSpeed=ReefCrestMode(tideSpeed,vtDuration*2,true);
rcSpeedAS=ReefCrestMode(tideSpeed,vtDuration*2,false);
break;
}
case Lagoon: {
rcSpeed=ReefCrestMode(tideSpeed,vtDuration,true);
rcSpeedAS=ReefCrestMode(tideSpeed,vtDuration,false);
break;
}
case TidalSwell: {
rcSpeed=TidalSwellMode(tideSpeed,true);
rcSpeedAS=TidalSwellMode(tideSpeed,false);
break;
}
case Smart_NTM: {
rcSpeed=NutrientTransportMode(0,tideSpeed,vtDuration*50,true);
rcSpeedAS=NutrientTransportMode(0,tideSpeed,vtDuration*50,false);
break;
}
case ShortPulse: {
rcSpeed=ShortPulseMode(0,tideSpeed,vtDuration*50,true);
rcSpeedAS=ShortPulseMode(0,tideSpeed,vtDuration*50,false);
break;
}
case LongPulse: {
rcSpeed=LongPulseMode(0,tideSpeed,vtDuration,true);
rcSpeedAS=LongPulseMode(0,tideSpeed,vtDuration,false);
break;
}
case BHazard: {
rcSpeed=millis()%1200>800?tideSpeed:0;
rcSpeedAS=millis()%1200<400?0:tideSpeed;
break;
}
case Else: {
rcSpeed=ElseMode(tideSpeed,vtDuration*2,true);
rcSpeedAS=ElseMode(tideSpeed,vtDuration*2,false);
break;
}
case Sine: {
rcSpeed=SineMode(tideSpeed-tideMin,tideSpeed+tideMin,vtDuration*100,true);
rcSpeedAS=SineMode(tideSpeed-tideMin,tideSpeed+tideMin,vtDuration*100,false);
break;
}
default: {
rcSpeed=tideSpeed;
rcSpeedAS=tideSpeed;
}
}
ReefAngel.RF.SetMode(Custom,rcSpeedAS*pumpOffset,tide.isOutgoing());
ReefAngel.RF.SetMode(Custom,rcSpeed,tide.isIncoming());
}
// Monitor ATO reservoir
void CheckATO() {
static time_t t;
static WiFiAlert atoLogAlert;
static boolean atoSaved;
byte prevLevelOld, prevLevel, currLevel;
byte numHours, rate, rateAlarm;
char msg[32];
atoLogAlert.SetDelay(SECS_PER_HOUR*6);
currLevel=ReefAngel.WaterLevel.GetLevel();
prevLevel=InternalMemory.read(Mem_B_LogATO);
prevLevelOld=InternalMemory.read(Mem_B_LogPrevATO);
numHours=(now()%(SECS_PER_HOUR*6))/SECS_PER_HOUR;
rate=(prevLevelOld-currLevel)*(24/(6+numHours));
rateAlarm=InternalMemory.read(Mem_B_RateAlarm);
ReefAngel.CustomVar[Var_LogATO]=rate;
if ((rate<rateAlarm && rateAlarm!=0) && !ReefAngel.Relay.Status(VO_Vacation)) {
if (!ReefAngel.Relay.Status(VO_RefillATO)) {
sprintf(msg,"ATO+rate+(%d)+is+too+low!",rate);
atoLogAlert.Send(msg);
}
}
if (now()%(SECS_PER_HOUR*6)==0) { // Save level and alert every 6 hours
if (!atoSaved) {
atoSaved=true;
InternalMemory.write(Mem_B_LogATO,currLevel);
InternalMemory.write(Mem_B_LogPrevATO,prevLevel);
}
} else {
atoSaved=false;
}
}
// ATO Refill mode. Top off ATO reservoir until it's at 100%
/* void RefillATO() {
static boolean refillActive=false;
static WiFiAlert refillAlert;
byte level, prevRate;
if (ReefAngel.Relay.Status(VO_EnableATO) || ReefAngel.Relay.Status(VO_Vacation)) {
ReefAngel.Relay.Override(VO_RefillATO,2);
}
level=ReefAngel.WaterLevel.GetLevel();
if (ReefAngel.Relay.Status(VO_RefillATO)) {
if (level<100) {
ReefAngel.Relay.On(ATOres);
refillActive=true;
} else {
ReefAngel.Relay.Override(VO_RefillATO,2);
refillAlert.Send("ATO+Reservoir+is+full.");
if (InternalMemory.read(Mem_B_MaintATO) > 0) // Reset last ATO counter
InternalMemory.write(Mem_B_MaintATO,0);
}
} else {
if (refillActive) {
prevRate=InternalMemory.read(Mem_B_LogPrevATO)-InternalMemory.read(Mem_B_LogATO);
InternalMemory.write(Mem_B_LogPrevATO, level+prevRate);
InternalMemory.write(Mem_B_LogATO, level+1);
ReefAngel.Relay.Off(ATORes);
refillActive=false;
}
}
}
void Vacation() {
static boolean onVacation=InternalMemory.read(Mem_B_Vacation);
ReefAngel.Relay.Set(VO_Vacation, onVacation); // Sets RelayData only
if (ReefAngel.Relay.Status(VO_Vacation)) { // Looks at actual status
onVacation=ReefAngel.Relay.Status(VO_Vacation);
InternalMemory.write(Mem_B_Vacation, onVacation);
ReefAngel.Relay.Override(VO_Vacation,2); // Back to auto mode
}
if (onVacation) {
ReefAngel.WaterLevelATO(ATORes);
if (now()%SECS_PER_DAY==19*SECS_PER_HOUR) {
ReefAngel.FeedingModeStart();
}
}
}
*/
void LogFeedings() {
static byte feedings;
static boolean feedStatus;
if (ReefAngel.Relay.Status(Feeder)) {
feedStatus=true;
} else {
if (feedStatus) {
feedStatus=false;
feedings++;
}
}
if (ReefAngel.DisplayedMenu==FEEDING_MODE) {
if (InternalMemory.read(Mem_B_MaintFeeding) > 0) // Reset last time in Feeding Mode
InternalMemory.write(Mem_B_MaintFeeding,0);
}
ReefAngel.CustomVar[Var_Feedings]=feedings;
if (now()%SECS_PER_DAY==SECS_PER_DAY-1) feedings=0; // Clear feedings at end of day
}
void RunFeeder() {
int press=InternalMemory.read(Mem_B_AutoFeedPress);
int repeat=InternalMemory.read(Mem_B_AutoFeedRepeat)*SECS_PER_HOUR;
int offset=InternalMemory.read(Mem_B_AutoFeedOffset)*SECS_PER_HOUR;
boolean autoFeed=InternalMemory.read(Mem_B_AutoFeed);
static time_t t;
ReefAngel.Relay.Set(VO_AutoFeed, autoFeed);
if (ReefAngel.Relay.Status(VO_AutoFeed)!=autoFeed) {
autoFeed=ReefAngel.Relay.Status(VO_AutoFeed);
InternalMemory.write(Mem_B_AutoFeed, autoFeed);
ReefAngel.Relay.Override(VO_AutoFeed,2);
}
if (autoFeed) {
if (sun.IsDaytime() && (now()+offset)%repeat==0) {
t=now();
}
}
if (ReefAngel.Relay.isMaskOn(Feeder)) {
ReefAngel.Relay.Override(Feeder,2);
t=now()-now()%20; // One feeding has already happened.
}
// Press the button once every 20 seconds
if (now()-t < press*20) {
ReefAngel.Relay.Set(Feeder,now()%20==0);
} else {
ReefAngel.Relay.Off(Feeder);
}
}
/* void RunSwabbie() {
int repeat=InternalMemory.read(Mem_B_SwabbieRepeat)*60;
int runtime=InternalMemory.read(Mem_B_SwabbieTime)*60;
static time_t t;
// Manual mode
if (ReefAngel.Relay.isMaskOn(Swabbie)) {
ReefAngel.Relay.Override(Swabbie,2);
t=now();
}
if (now()-t < runtime) {
ReefAngel.Relay.On(Swabbie);
} else {
ReefAngel.DosingPumpRepeat(Swabbie,0,repeat,runtime);
}
}
// Definitions for dosing pumps
#define numDPumps 3
byte pump[numDPumps]={ DPump1, DPump2, Extension}; // Pump 3 is just for logging/calibration routine
byte varReport[numDPumps]={ Var_DPump1, Var_DPump2, Var_WCVol};
byte 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-1; // -1 = Remove "Extension" from the equation.
static byte doseTime[numPumps]={
InternalMemory.read(memDPTime[0]),
InternalMemory.read(memDPTime[1])
};
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;
}
// Make sure we're not calibrating
if (!ReefAngel.Relay.Status(VO_Calibrate))
ReefAngel.DosingPumpRepeat(pump[i], i*5, dpRepeat, doseTime[i]);
}
}
void LogDosingPumps() {
static time_t pumpTimer[numDPumps];
static boolean pumpStatus[numDPumps];
float rate;
for (int i=0;i< numDPumps;i++) {
if (ReefAngel.Relay.Status(pump[i])) {
if (!pumpStatus[i]) {
pumpTimer[i]=now()-pumpTimer[i]; // Pump was off, timer is now a time
pumpStatus[i]=true;
}
} else {
if (pumpStatus[i]) {
pumpTimer[i]=now()-pumpTimer[i]; // Pump was on, timer is now a timer
pumpStatus[i]=false;
rate=(float)InternalMemory.read_int(memCalVol[i])/InternalMemory.read_int(memCalTime[i]);
ReefAngel.CustomVar[varReport[i]]=pumpTimer[i]*rate;
}
}
if (now()%SECS_PER_DAY==SECS_PER_DAY-1 && i!=2) { // Don't overwrite WC log
pumpTimer[i]=0; // Clear timer at end of day
ReefAngel.CustomVar[varReport[i]]=0; // Clear portal variable
}
}
// Reset logging for WC fill pump at start of WC
static boolean wcStart;
if (ReefAngel.DisplayedMenu!=WATERCHANGE_MODE) wcStart=false;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE && !wcStart) {
ReefAngel.CustomVar[varReport[2]]=0;
pumpTimer[2]=0;
wcStart=true;
}
}
void CalibrateDPumps() {
static time_t pumpTimer[numDPumps];
static boolean pumpStatus[numDPumps];
static boolean running=false;
if (ReefAngel.Relay.Status(VO_Calibrate)) {
running=true;
for (int i=0;i < numDPumps;i++) {
if (ReefAngel.Relay.Status(pump[i])) {
if (!pumpStatus[i]) {
pumpTimer[i]=now()-pumpTimer[i]; // Pump was off, timer is now a time
pumpStatus[i]=true;
}
} else {
if (pumpStatus[i]) {
pumpTimer[i]=now()-pumpTimer[i]; // Pump was on, timer is now a timer
pumpStatus[i]=false;
}
}
}
} else {
if (running) {
running=false;
for (int i=0;i < numDPumps;i++) {
if (pumpTimer[i]>0 && !ReefAngel.Relay.Status(VO_LockPorts)) {
InternalMemory.write_int(memCalTime[i], pumpTimer[i]);
}
ReefAngel.Relay.Override(pump[i],2); // Go back to auto mode
pumpStatus[i]=false;
pumpTimer[i]=0;
}
}
}
}
void AutoWaterChange() {
int runtime=InternalMemory.read_int(Mem_I_WCFillTime);
static WiFiAlert wcAlert;
static boolean started;
static time_t t;
if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
ReefAngel.Relay.On(Refugium);
if (ReefAngel.Relay.Status(VO_EnableATO)) {
ReefAngel.SingleATOHigh(Extension); // Refill fresh SW as needed
if (InternalMemory.read(Mem_B_MaintWC) > 0) // Reset last WC counter
InternalMemory.write(Mem_B_MaintWC,0);
if (ReefAngel.Relay.Status(VO_StartFill) && !started) {
ReefAngel.Relay.Override(Reactor,1); // Start draining
started=true;
t=now();
}
if ( (now()-t > runtime && started) || bitRead(ReefAngel.AlertFlags,ATOTimeOutFlag) ) {
ReefAngel.Relay.Override(Reactor,0); // Stop draining
ReefAngel.Relay.Override(VO_StartFill,2); // Reset Start switch
if (started) wcAlert.Send("Reactor+disabled.", true);
started=false;
}
} else {
// Done with ATO
ReefAngel.Relay.Off(Extension);
// Backup WC Vol
if (InternalMemory.read(Mem_B_MaintWCVol) != ReefAngel.CustomVar[Var_WCVol])
InternalMemory.write(Mem_B_MaintWCVol, ReefAngel.CustomVar[Var_WCVol]);
// Clear stale timeout
ReefAngel.ATOClear();
}
} else {
ReefAngel.Relay.Override(VO_EnableATO,2);
ReefAngel.Relay.Override(VO_StartFill,2);
}
}
// Disable masks for things that should not be turned on by mistake
void LockPorts() {
ReefAngel.AddPortOverrides();
if (ReefAngel.Relay.Status(VO_LockPorts)) {
ReefAngel.OverridePortsE[0] = Port7Bit | Port8Bit;
ReefAngel.OverridePortsE[1] = Port4Bit | Port5Bit | Port7Bit;
} else {
ReefAngel.OverridePortsE[0] = 0;
ReefAngel.OverridePortsE[1] = 0;
}
}
*/
void Reminders() {
static boolean counterReady;
byte counter[]= { Mem_B_MaintGAC, Mem_B_MaintGFO, Mem_B_MaintCal, Mem_B_MaintAlk,
Mem_B_MaintWC, Mem_B_MaintATO, Mem_B_MaintFeeding, Mem_B_MaintSkimmer, Mem_B_MaintSocks };
if (now()%SECS_PER_DAY!=0) counterReady=true;
if (now()%SECS_PER_DAY==0 && counterReady) {
for (int i=0;i<sizeof(counter);i++) {
InternalMemory.write(counter[i],InternalMemory.read(counter[i])+1);
}
counterReady=false;
}
}
void NextRFMode() {
vtMode++;
if (vtMode > 12) {
vtMode=0;
vtSpeed=50; // Constant
} else if (vtMode == 1) {
vtSpeed=40; // Lagoon
} else if (vtMode == 2) {
vtSpeed=45; // Reef Crest
} else if (vtMode == 3) {
vtSpeed=55; vtDuration=10; // Short Pulse
} else if (vtMode == 4) {
vtSpeed=55; vtDuration=20; // Long Pulse
} else if (vtMode == 5) {
vtSpeed=InternalMemory.read(Mem_B_NTMSpeed);
vtDuration=InternalMemory.read(Mem_B_NTMDuration); // Smart_NTM
} else if (vtMode == 6) {
vtSpeed=50; vtDuration=10; // Smart_TSM
} else if (vtMode == 7) {
vtSpeed=InternalMemory.read(Mem_B_NightSpeed);
vtDuration=InternalMemory.read(Mem_B_NightDuration);
vtMode=9; // Night
} else if (vtMode == 10) {
vtSpeed=65; vtDuration=5; // Storm
} else if (vtMode == 11) {
vtSpeed=45; vtDuration=10; // Custom
}
if (vtMode!=InternalMemory.RFMode_read())
InternalMemory.RFMode_write(vtMode);
if (vtSpeed!=InternalMemory.RFSpeed_read())
InternalMemory.RFSpeed_write(vtSpeed);
if (vtDuration!=InternalMemory.RFDuration_read())
InternalMemory.RFDuration_write(vtDuration);
}
// Menu Code
void MenuEntry1() {
// Toggle refugium light between on/auto.
// ReefAngel.Relay.Override(Refugium, ReefAngel.Relay.Status(Refugium)+1);
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry2() {
ReefAngel.FeedingModeStart();
}
void MenuEntry3() {
ReefAngel.WaterChangeModeStart();
}
void MenuEntry4() {
NextRFMode();
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5() {
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6() {
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry7() {
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8() {
ReefAngel.SetupCalibrateWaterLevel();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry9() {
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
// Custom Main Screen
void DrawCustomMain() {
const int NumScreens=3;
static boolean drawGraph=true;
// Main Header
// ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, 2,"Lee's Reef");
ReefAngel.LCD.DrawDate(5,2);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 128, 11);
// Param Header
DrawParams(5,14);
switch (ScreenID) {
case 0:
{
if (drawGraph) { ReefAngel.LCD.DrawGraph(5,40); drawGraph=false; }
break;
}
case 1: { DrawStatus(5,40); break; }
case 2: { DrawSunMoon(5,40); break; }
}
// Draw Relays
DrawRelays(12,94);
// Date+Time
// ReefAngel.LCD.DrawDate(5,122);
if (ReefAngel.Joystick.IsLeft()) {
ReefAngel.ClearScreen(DefaultBGColor);
ScreenID--; drawGraph=true;
}
if (ReefAngel.Joystick.IsRight()) {
ReefAngel.ClearScreen(DefaultBGColor);
ScreenID++; drawGraph=true;
}
if (ScreenID<0) ScreenID=NumScreens-1;
if (ScreenID>=NumScreens) ScreenID=0;
}
void DrawCustomGraph() {
if (ScreenID==0)
ReefAngel.LCD.DrawGraph(5, 40);
}
void DrawParams(int x, int y) {
char buf[16];
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
// Temp and PH
y+=2;
ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
y+=6;
ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
ConvertNumToString(buf, ReefAngel.Params.PH, 100);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
y+=5;
ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
}
void DrawStatus(int x, int y) {
int t=x;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,15,y,"High",Font8x16);
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,85,y,"Low",Font8x16);
if (ReefAngel.HighATO.IsActive()) {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_RED);
}
if (ReefAngel.LowATO.IsActive()) {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_RED);
}
y+=12;
// Display Water level
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); x+=65;
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.WaterLevel.GetLevel(),DPColor,x,y,1);
x+=5*(intlength(ReefAngel.WaterLevel.GetLevel())+1);
ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x, y, "%");
y+=12; x=t;
// Vortech Mode
ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); x+=20;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,t+(128-t),y+8);
if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,x,y,"Constant");
else if (vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x,y,"Lagoon");
else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,x,y,"Reef Crest");
else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,x,y,"Short Pulse");
else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,x,y,"Long Pulse");
else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x,y,"Smart NTM");
else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x,y,"Tidal Swell");
else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,x,y,"Night");
else if (vtMode == 10) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,0,x,y,"Storm");
else if (vtMode == 11) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,x,y,"Custom");
y+=10; x=t;
ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); x+=60;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,128,y+8);
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,vtSpeed); x+=15;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,"/"); x+=10;
ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,x,y,vtDuration);
y+=10; x=t;
// Display Acclimation timer
if (ReefAngel.CustomVar[Var_AcclDay] > 0) {
ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"Acclimation Day:"); x+=100;
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.CustomVar[Var_AcclDay],DefaultFGColor,x,y,1);
} else {
ReefAngel.LCD.Clear(DefaultBGColor,x,y,128,y+8);
}
}
void DrawSunMoon(int x, int y) {
char buf[16];
int t=x;
y+=2;
/// Display Sunrise / Sunset
sprintf(buf, "%02d:%02d", sun.GetRiseHour(), sun.GetRiseMinute());
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Rise:"); x+=31;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
sprintf(buf, "%02d:%02d", sun.GetSetHour(), sun.GetSetMinute()); x+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Set:"); x+=25;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
y+=15; x=t;
/// Display Moonrise / Moonset
sprintf(buf, "%02d:%02d", Moon.riseH, Moon.riseM);
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MR:"); x+=21;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
sprintf(buf, "%02d:%02d", Moon.setH, Moon.setM); x+=36;
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MS:"); x+=21;
ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf); x+=36;
if (Moon.isUp) ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,"@");
else ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,"_");
y+=10; x=t;
// MoonPhase
ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,128,y+8);
ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
y+=10; x=t;
// MoonLight %
ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); x+=68;
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(),DPColor,x,y,1);
x+=5*(intlength(ReefAngel.PWM.GetDaylightValue())+1);
ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x, y, "%");
}
void DrawRelays(int x, int y) {
// Draw Relays
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(x, y, TempRelay);
y+=12;
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(x, y, TempRelay);
y+=12;
TempRelay = ReefAngel.Relay.RelayDataE[1];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[1];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[1];
ReefAngel.LCD.DrawOutletBox(x, y, TempRelay);
}
byte ElseMode( byte MidPoint, byte Offset, boolean WaveSync )
{
// Static's only initialize the first time they are called
static unsigned long LastChange=millis(); // Set the inital time that the last change occurred
static int Delay = random( 500, 3000); // Set the initial delay
static int NewSpeed = MidPoint; // Set the initial speed
static int AntiSpeed = MidPoint; // Set the initial anti sync speed
if ((millis()-LastChange) > Delay) // Check if the delay has elapsed
{
Delay=random(500,5000); // If so, come up with a new delay
int ChangeUp = random(Offset); // Amount to go up or down
if (random(100)<50) // 50/50 chance of speed going up or going down
{
NewSpeed = MidPoint - ChangeUp;
AntiSpeed = MidPoint + ChangeUp;
}
else
{
NewSpeed = MidPoint + ChangeUp;
AntiSpeed = MidPoint - ChangeUp;
}
LastChange=millis(); // Reset the time of the last change
}
if (WaveSync)
{
return NewSpeed;
}
else
{
return AntiSpeed;
}
}
void DelayedOnModes(byte relay) {
static unsigned long startTime=now();
if (startTime==LastStart && ReefAngel.HighATO.IsActive()) {
ReefAngel.Relay.On(relay);
} else {
ReefAngel.Relay.DelayedOn(relay);
}
}
Last edited by aranax on Mon Jan 13, 2014 9:54 pm, edited 1 time in total.
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Lee's Feature Complete PDE
I would revert back to a wizard code and see if you still have issues.
If you want to troubleshoot then comment out the functions called in loop() one by one till you find the culprit.
But i don't think anything could cause an RA lockup..
If you want to troubleshoot then comment out the functions called in loop() one by one till you find the culprit.
But i don't think anything could cause an RA lockup..
-
Visser
- Posts: 9
- Joined: Tue Jan 14, 2014 4:46 am
- Location: Brits, South africa
Re: Lee's Feature Complete PDE
Hi lee. Your code is really very cool.
I'm trying to figure out how to code the "high" & "low" float indicators from your main screen into mine, but struggling with the code.
As in your picture below:

Can you perhaps indicate the code lines with any other supporting code required to do this?
Thanks
I'm trying to figure out how to code the "high" & "low" float indicators from your main screen into mine, but struggling with the code.
As in your picture below:
Can you perhaps indicate the code lines with any other supporting code required to do this?
Thanks
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Lee's Feature Complete PDE
It's in the DrawStatus() function. You can add this code to the end of your INO and then call it in your DrawCustomMain with the coordinates you want it drawn at...
DrawStatus(10,20);
I removed the rest of the function to just give you the switch status.
DrawStatus(10,20);
Code: Select all
void DrawStatus(int x, int y) {
int t=x;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,15,y,"High",Font8x16);
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,85,y,"Low",Font8x16);
if (ReefAngel.HighATO.IsActive()) {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_RED);
}
if (ReefAngel.LowATO.IsActive()) {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_GREEN);
} else {
ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_RED);
}
}
-
Visser
- Posts: 9
- Joined: Tue Jan 14, 2014 4:46 am
- Location: Brits, South africa
Re: Lee's Feature Complete PDE
Thanks Lee! The code worked fantastic!!!
Sump + skimmer deactivated, Normal temps

Sump & skimmer active, Normal temps

Sump + skimmer deactivated, Low temps

Sump + skimmer deactivated, Overhead temps

Works like a charm. Thanks so much guys!!!
Here is the code if anyone wants to try.
Sump + skimmer deactivated, Normal temps

Sump & skimmer active, Normal temps

Sump + skimmer deactivated, Low temps

Sump + skimmer deactivated, Overhead temps

Works like a charm. Thanks so much guys!!!
Here is the code if anyone wants to try.
Code: Select all
////// Place global variable code below here
int avgph[10];
unsigned long totalavgph=0;
byte avgindex=0;
void DrawCustomMain()
{
byte x;
byte y = 2;
char text[7];
// *********** CHANGE TEMP READOUT COLOR DEPENDENT ON FAN AND HEATER STATUS ***********
int TempColor; // Color for drawing temperature
boolean FanOn = ReefAngel.Relay.Status(Port7); // Get the status of the fan relay
boolean HeatOn = ReefAngel.Relay.Status(Port6); // Get the status of the heater relay
if (HeatOn)
{
TempColor = COLOR_NAVY; // Blue text, too cold, heater is on
}
if (FanOn)
{
TempColor = COLOR_RED; // Red text, too warm, fan is on
}
if (!HeatOn && !FanOn)
{
TempColor = COLOR_GREEN; // Green text, no fan or heater on
}
// ***********************************************************************************
ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 30, 2, "CJ's PICO REEF"); // Put a banner at the top
ReefAngel.LCD.DrawDate(6, 119); // Put the date and time at the bottom
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 132, 11); // Draw a black line under the banner
x = 6;
y += MENU_START_ROW*1.0; // MENU_START_ROW is 10, according to globals.h, so y=2+10+1=13
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+1, "Pico Temp PH");
x = 6;
y = MENU_START_ROW*0.8;
ConvertNumToString(text, ReefAngel.Params.PH, 100); // Get pH reading and convert
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+73, y+12, text, Font8x16); // Put pH on the screen
ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10); // Get T1 temp and convert
x = 6;
y += MENU_START_ROW*1.3;
ReefAngel.LCD.DrawHugeNumbers(COLOR_WHITE, TempColor, x, y, text); // Draw the temperature, white numbers on a colored background
x += 6;
y += MENU_START_ROW*1.2;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x-6, y+5, "Room Temp");
ConvertNumToString(text, ReefAngel.Params.Temp[T2_PROBE], 10); // Get T2 temp and convert
y += MENU_START_ROW*1.2;
x = 6;
ReefAngel.LCD.DrawHugeNumbers(COLOR_WHITE, TempColor, x, y, text); // Draw the temperature, white numbers on a colored background
x += 6;
y += MENU_START_ROW*2.2;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"Sump Level",Font8x8); // Draw the Float switch status
if (ReefAngel.HighATO.IsActive()) {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
} else {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
x += 6;
y += MENU_START_ROW*1.6;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"Skimmer Cup",Font8x8);
if (ReefAngel.LowATO.IsActive()) {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
} else {
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
byte TempRelay = ReefAngel.Relay.RelayData; // Code for drawing the relay box
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 100, TempRelay);
}
void DrawCustomGraph()
{
}
////// Place global variable code above here
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Haven't had any updates in a while...mainly because i have nothing new to add yet.
Today I re-calibrated my pumps. It was the coolest method. Since I'm storing the calibration values on the RA and I keep track of the last time i filled my dosing containers...here is what I did...
In the past 31 days I used 1250ml of part a and 1200ml of part b. I calculated how much time the doser ran over the 31 days. 79 seconds * 24 doses * 31 days... Then i was able to calculate the actual flow rate. 1.276 ml/min on pump a and 1.224 on pump b. I reduced the fraction to my normal calibration range which came to 470 and 490 seconds respectively to fill 10ml of fluid.
This obviously reset my dosing time since my target volume didn't change. I then modified the volume to get back to the time i was at originally. Based on the new rate to get the same dosing time on pump a i had to change 35 ml to 40 ml. So now pump a went from 79 seconds to 78 seconds and 79 on pump b went to 81 seconds. That should get me an extra 30-50 ml a month to make up for the difference. Pretty accurate calculations I would say.
I am thinking of making this more accurate by calculating the dosing time in milliseconds. This would allow you to dose really low values per day or increase to a lot more doses than 24 per day. Anyone interested in that kind of accuracy?
I really need to objectify this code so it can be added to the libraries. Maybe thats when I'll add the millisecond accuracy.
All this came from a thread on MR about how inaccurate the bubble magus dosing pumps can be.
Today I re-calibrated my pumps. It was the coolest method. Since I'm storing the calibration values on the RA and I keep track of the last time i filled my dosing containers...here is what I did...
In the past 31 days I used 1250ml of part a and 1200ml of part b. I calculated how much time the doser ran over the 31 days. 79 seconds * 24 doses * 31 days... Then i was able to calculate the actual flow rate. 1.276 ml/min on pump a and 1.224 on pump b. I reduced the fraction to my normal calibration range which came to 470 and 490 seconds respectively to fill 10ml of fluid.
This obviously reset my dosing time since my target volume didn't change. I then modified the volume to get back to the time i was at originally. Based on the new rate to get the same dosing time on pump a i had to change 35 ml to 40 ml. So now pump a went from 79 seconds to 78 seconds and 79 on pump b went to 81 seconds. That should get me an extra 30-50 ml a month to make up for the difference. Pretty accurate calculations I would say.
I am thinking of making this more accurate by calculating the dosing time in milliseconds. This would allow you to dose really low values per day or increase to a lot more doses than 24 per day. Anyone interested in that kind of accuracy?
I really need to objectify this code so it can be added to the libraries. Maybe thats when I'll add the millisecond accuracy.
All this came from a thread on MR about how inaccurate the bubble magus dosing pumps can be.
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
So just to see just how accurate my dosing has been...my alk has dropped a bit. For the past month it was hovering around 120ppm. Wanted to bring it closer to ~135. My dosers were currently set to 40ml/day. So I raised it to 45ml to see how it would change. 5 days later, tested my alk and it was at 130. Just to check on how my tank was responding, I verified on the reef chemistry calculator at http://reef.diesyst.com/chemcalc/chemcalc.html and put in my starting alk (121ppm) and my current alk (130) and it came back with 25.2ml. That matches perfectly with how many ml I am adding (5 days x 5ml).
It is very cool to see how accurately the dosers are dialed in. The next day I reset the dosing back to 40ml and I'll continue my normal monitoring.
I will definitely be turning the dosing routines I've created into a self contained class soon. Time is so tight, i don't really get time to sit down and code, but I'd really like to treat the AWC, RO Solenoid and automated salt mixing that I'll be doing as "Dosing Pumps". This way i can track exactly how much water I'm changing, how much water I'm evaporating, etc. there is something wrong with the current logging of my water change volume, but not sure what yet it is...so really the only solution for me is to improve the code
It is very cool to see how accurately the dosers are dialed in. The next day I reset the dosing back to 40ml and I'll continue my normal monitoring.
I will definitely be turning the dosing routines I've created into a self contained class soon. Time is so tight, i don't really get time to sit down and code, but I'd really like to treat the AWC, RO Solenoid and automated salt mixing that I'll be doing as "Dosing Pumps". This way i can track exactly how much water I'm changing, how much water I'm evaporating, etc. there is something wrong with the current logging of my water change volume, but not sure what yet it is...so really the only solution for me is to improve the code
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
The result of well calibrated dosing pumps 


-
james211
Re: Lee's Feature Complete PDE
So I'm very interested in the calibration aspect of this code specifically. I'm trying to install Inevo's code but I keep getting a boat load of errors on verification. Most of them are some where along this line but there is a lot of them. I'm probably doing something really stupid. Any help would be great.
Thank you!
I also need some help setting up the ethernet shield, any suggestions?
Code: Select all
InitialInternalMemory:197: error: 'SunLocation' does not name a typeI also need some help setting up the ethernet shield, any suggestions?
- lnevo
- Posts: 5422
- Joined: Fri Jul 20, 2012 9:42 am
Re: Lee's Feature Complete PDE
Cant help you with the ethernet shield but there are packages you need to install that are posted on the second post of this thread if your trying to use my code as-is.
-
89delta
- Posts: 157
- Joined: Mon Oct 15, 2012 7:21 pm
- Location: Leesburg, GA
Re: Lee's Feature Complete PDE
Great read Lee. Am very interested in using your dosing calibration code. Could you explain more about it and how we'd be able to use it as I dont see a menu item for calibrating them.