I have this code which puts the RA in to feeding mode at 7:30am. It works on the 60 but is not working on the 120.
Code: Select all
if ( ((hour() == 7)) && (minute() == 30) &&(second() == 0) ) ReefAngel.FeedingModeStart();
Code: Select all
if ( ((hour() == 7)) && (minute() == 30) &&(second() == 0) ) ReefAngel.FeedingModeStart();
absolutely, and thank you. Mind you, I've only witnessed it not going in to feed mode this morning. I will keep an eye on it.rimai wrote:Same code?
Can you post the entire code you uploaded in the 120?
Code: Select all
//// Port 1 Heater
//// Port 2 ATO pump
//// Port 3 Feeder
//// Port 4 Protein Skimmer
//// Port 5 Return Pump
//// Port 6 Wavemakers
//// Port 7 UV Sterilizer
//// Port 8 Cabinet Light
//// BOX 1 PORT 1 Calcium Doser
//// BOX 1 PORT 2 Alkalinity Doser
//// BOX 1 PORT 8 Sump Fan
//// BOX 1 PORT 8 Fans
//// Probe T1 SUMP
//// Probe T2 Tank
//// Probe T3 Canopy
#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 <DCPump.h>
////// Place global variable code below here
#define Heater Port1
#define Topoff Port2
#define Feeder Port3
#define Skimmer Port4
#define Return Port5
#define Wave Port6
#define UVlight Port7
#define Fugelight Port8
#define CanopyFan Box1_Port8
#define CalcDose Box1_Port1
#define AlkDose Box1_Port2
#define SumpFan Box1_Port7
#define FugelightBit 1<<7
#define UVlightBit 1<<6
#define WaveBit 1<<5
#define ReturnBit 1<<4
#define SkimmerBit 1<<3
#define FeederBit 1<<2
#define TopoffBit 1<<1
#define HeaterBit 1<<0
static byte wpMode;
static byte wpWavStr;
static byte wpWavOff;
////// Place global variable code above here
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.Use2014Screen(); // Let's use 2014 Screen
ReefAngel.AddMultiChannelWaterLevelExpansion(); // Multi-Channel Water Level Expansion Module
ReefAngel.Network.WifiAuthentication("Smotz:-------------");
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = SkimmerBit | ReturnBit | TopoffBit | UVlightBit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = HeaterBit | SkimmerBit | ReturnBit | TopoffBit | WaveBit | UVlightBit | FeederBit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = FugelightBit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = HeaterBit | TopoffBit | SkimmerBit | ReturnBit | WaveBit | UVlightBit | FeederBit;
// Use T2 (Tank) probe as temperature and overheat functions
ReefAngel.TempProbe = T2_PROBE;
ReefAngel.OverheatProbe = T2_PROBE;
// Ports that are always off
ReefAngel.Relay.Off( Feeder );
ReefAngel.Relay.Off( Topoff );
// Ports that are always on
ReefAngel.Relay.On( Box1_Port3 );
ReefAngel.Relay.On( Box1_Port4 );
ReefAngel.Relay.On( Box1_Port5 );
ReefAngel.Relay.On( Box1_Port6 );
ReefAngel.Relay.On( Box1_Port7 );
// DCPUMP
ReefAngel.DCPump.FeedingSpeed=35;
ReefAngel.DCPump.WaterChangeSpeed=0;
ReefAngel.DCPump.ActinicChannel=Sync;
ReefAngel.DCPump.DaylightChannel=AntiSync;
////// Place additional initialization code below here
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( Wave );
ReefAngel.Relay.Off( Feeder );
ReefAngel.MoonLights( Fugelight );
ReefAngel.StandardHeater( Heater );
ReefAngel.StandardFan( SumpFan );
////// Place your custom code below here
// Main Top Off
ReefAngel.WaterLevelATO(1,Topoff,15,85,95);
// just to be safe if the water level gets to 140 or greater, turn off the topoff.
// if (ReefAngel.WaterLevel.GetLevel(1)<=85) ReefAngel.Relay.On(Topoff); // Obsolete code
// if (ReefAngel.WaterLevel.GetLevel(1)>=95) ReefAngel.Relay.Off(Topoff); // Obsolete code
if ( ReefAngel.WaterLevel.GetLevel(1) >= 140 ) ReefAngel.Relay.Override (Topoff,0);
// End Main Top Off
//Dosing
ReefAngel.DosingPumpRepeat1( CalcDose );
ReefAngel.DosingPumpRepeat2( AlkDose );
//// 30,20 = 17.5 ml day
//// ReefAngel.DosingPumpRepeat(CalcDose,0,30,20);
//// ReefAngel.DosingPumpRepeat(AlkDose,10,30,20);
// Turn on Canopy Fans when the temp of T3 (Canopy) gets above 85.0 degrees
if ( ReefAngel.Params.Temp[T3_PROBE] >= 850 ) ReefAngel.Relay.On( CanopyFan );
// Turn off Canopy Fans when the temp of T2 (Canopy) gets below 80.0 degrees
if ( ReefAngel.Params.Temp[T3_PROBE] < 800 ) ReefAngel.Relay.Off( CanopyFan );
// Make Salinity and PH calibration changes on the fly without reboot
// ReefAngel.SalMax=InternalMemory.SalMax_read();
// ReefAngel.PHMin=InternalMemory.PHMin_read();
// ReefAngel.PHMax=InternalMemory.PHMax_read();
ReefAngel.Relay.DelayedOn(Skimmer,5);
// Buzzer if temp is too high or low, or waterlevel 1 is too high or too low.
if (ReefAngel.DisplayedMenu!=WATERCHANGE_MODE && ReefAngel.DisplayedMenu!=FEEDING_MODE) // Only check if not Feeding Mode or Water Change Mode
{
if ( ReefAngel.Params.Temp[T2_PROBE] < 700 || ReefAngel.Params.Temp[T2_PROBE] > 850 || ReefAngel.WaterLevel.GetLevel(1) > 140 || ReefAngel.WaterLevel.GetLevel(1) < 70)
{
pinMode(lowATOPin,OUTPUT);
digitalWrite(lowATOPin,HIGH);
}
else
{
pinMode(lowATOPin,OUTPUT);
digitalWrite(lowATOPin,LOW);
}}
// Turn the fugelight on if the return is off
if (ReefAngel.Relay.Status ( Return ) == 0) ReefAngel.Relay.On( Fugelight );
// EMERGENCY SUMP WATER LEVEL BELOW 25% AND WATERCHANGE MODE IS NOT ON AND WAIT FOR CONTROLLER TO BE ON FOR 5 SECONDS
// if ( ReefAngel.WaterLevel.GetLevel(1) <= 25 && ReefAngel.DisplayedMenu!=WATERCHANGE_MODE && millis() > 5000)
// {
// ReefAngel.Relay.Override(Return,0);
// ReefAngel.Relay.Override(Skimmer,0);
// ReefAngel.Relay.Override(UVlight,0);
// ReefAngel.Relay.Override(Feeder,0);
// ReefAngel.Relay.Override(CalcDose,0);
// ReefAngel.Relay.Override(AlkDose,0);
// ReefAngel.Relay.Override(Fugelight,1);
// }
// END EMERGENCY WATER LEVEL ROUTINE
// Feeding Mode
// if the hour is 7a and minute is 30 and seconds is 0 start the feeding mode
if ( ((hour() == 7)) && (minute() == 30) &&(second() == 0) ) ReefAngel.FeedingModeStart();
if ( ((hour() == 7)) && (minute() == 31) &&(second() == 0) ) ReefAngel.Relay.On(Feeder); //TURN FEEDER RELAY ON at 7:31:00
if ( ((hour() == 7)) && (minute() == 31) &&(second() == 5) ) ReefAngel.Relay.Off(Feeder); //TURN FEEDER RELAY OFF at 7:31:02
if ( ((hour() == 7)) && (minute() == 33) &&(second() == 0) ) ReefAngel.Relay.On(Feeder); //TURN FEEDER RELAY ON at 7:33:00
if ( ((hour() == 7)) && (minute() == 33) &&(second() == 5) ) ReefAngel.Relay.Off(Feeder); //TURN FEEDER RELAY OFF at 7:33:02
// end feeding mode
// Custom Wave Routine
if (InternalMemory.DCPumpMode_read()==11)
{
ReefAngel.DCPump.UseMemory=false;
// set the wpMode based on what time it is
if ( (hour() >= 2) && (hour() < 5) ) wpMode=1; // from 2am - 5am
if ( (hour() >= 5) && (hour() < 8) ) wpMode=2; // from 5am - 8am
if ( (hour() >= 8) && (hour() < 11) ) wpMode=4; // from 8am - 11am Nutrient Transport
if ( (hour() >= 11) && (hour() < 14) ) wpMode=2; // from 11a - 2pm
if ( (hour() >= 14) && (hour() < 17) ) wpMode=3; // from 2pm - 5pm
if ( (hour() >= 17) && (hour() < 20) ) wpMode=2; // from 5pm - 8pm
if ( (hour() >= 20) && (hour() < 23) ) wpMode=3; // from 8pm - 11p
if (hour() >= 23) wpMode=2; // from 11pm - midnight
if (hour() < 2) wpMode=1; // from midnight - 2am
switch (wpMode)
{
case 1: wpWavStr=35; wpWavOff=10; break; //Night Mode
case 2: wpWavStr=37; wpWavOff=4; break;
case 3: wpWavStr=42; wpWavOff=7; break;
case 4: wpWavStr=50; wpWavOff=10; break; //Nutrient Transport
default: wpWavStr=35; wpWavOff=2;
}
// Night Mode
if (wpMode==1) ReefAngel.DCPump.SetMode (Gyre, wpWavStr, wpWavOff);
//If 8am - 11am, Nutrient Transport
if (wpMode==4) ReefAngel.DCPump.SetMode (NutrientTransport, wpWavStr,wpWavOff);
//Wave for everything else
if (wpMode!=4 && wpMode!=1) ReefAngel.DCPump.SetMode (Else, wpWavStr, wpWavOff);
}
else ReefAngel.DCPump.UseMemory = true; // If it's not custom mode on the portal, then do what mode the portal says.
// End Custom Wave Routine
// Only turn on UV Sterilizer between 11pm and 3am
if ( (hour() >= 3) && (hour() < 23) ) // from 3a - 11p
ReefAngel.Relay.Off(UVlight);
else ReefAngel.Relay.On(UVlight);
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "Smotz" );
ReefAngel.ShowInterface();
}
void DrawCustomMain()
{
// Parameters
#if defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 5, ReefAngel.Params,
ReefAngel.PWM.GetDaylightValue(), ReefAngel.PWM.GetActinicValue() );
#else // defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 5, ReefAngel.Params );
#endif // defined DisplayLEDPWM && ! defined RemoveAllLights
pingSerial();
// Salinity
// ReefAngel.LCD.DrawText( COLOR_DARKKHAKI,DefaultBGColor,20,80, "SAL:" );
// ReefAngel.LCD.DrawText( COLOR_DARKKHAKI,DefaultBGColor,44,80, ReefAngel.Params.Salinity );
// pingSerial();
// Main Relay Box
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox( 12, 95, TempRelay );
pingSerial();
// Relay Expansion
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox( 12, 105, TempRelay );
pingSerial();
// Date and Time
ReefAngel.LCD.DrawDate( 6, 122 );
pingSerial();
//draw the mode on the screen
char buf[16];
char buf2[16];
// char buf3[32];
sprintf(buf,"ELSE %d%% +/- %d%%",wpWavStr,wpWavOff);
sprintf(buf2,"Wave Mode = %d%",wpMode);
// sprintf(buf3,"Return Power = %d%",InternalMemory.LEDPWMActinic_read());
// ReefAngel.LCD.DrawText(0,255,10,40,buf3);
ReefAngel.LCD.DrawText(0,255,20,60,buf);
ReefAngel.LCD.DrawText(0,255,20,50,buf2);
}
void DrawCustomGraph()
{
}
Code: Select all
if ( ((hour() == 7)) && (minute() == 30) &&(second() == 0) )
Code: Select all
unsigned long feedStartTS = 0;
// Grab timestamp if we don't have one, and it's the right time
// This should only run once at 7:30 or slightly after
if ( (feedStartTS == 0) && (hour() == 7) && (minute() >= 30) ) {
feedStartTS = millis();
}
if (feedStartTS) {
deltaTS = millis() - feedStartTS;
if ( deltaTS >= 600000 ) { // 10 minutes has elapsed
// Do something
// Set timestamp to 0 so we stop doing feeder stuff since this is the last stage
feedStartTS = 0;
} else if ( deltaTS >= 300000 ) { // 5 minutes has elapsed
// Do something
} else if ( deltaTS >= 120000 ) { // 2 minutes has elapsed
// Do something
} else if ( deltaTS >= 60000 ) { // 1 minute has elapsed
// Do something
} else {
// Start feeding mode
}
}
That would work too, but it looks like he is trying to turn things on for amounts of time that are less than a minute. Which means he will need to use something smaller than a minute.lnevo wrote:Or easier just take off the second() test altogether.
I should have been more specific it isn't for feed mode that he needs the sub-minute resolution it is for his auto-feeder. He is turning it on for only five seconds, and then turning it off. I agree that getting rid of the seconds() for the feed mode would work but he will still need it for the auto feeder part.lnevo wrote:Starting feeding mode wouldnt be a problem to continue running for the minute. Think about it, there's as much potential of it going multiple times in that second already.
Mode... Not feeder.Smotz wrote:No feeding mode this morning.
Odd because it worked for years on my other (PWM) RA. As a matter of fact, it still does. I think this new RA may be 'sluggish' - is that even possible ?re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
lnevo wrote:Look at the function I have for my Swabbie and autofeeder. It follows what re76's example is doing too
Have you added more extensions and functionality since then?Smotz wrote:Odd because it worked for years on my other (PWM) RA. As a matter of fact, it still does. I think this new RA may be 'sluggish' - is that even possible ?re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
It 'should' never dump the feeder continually. The relay is a "button push" - the feeder rotates once upon pressing it.re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
re76 wrote:Have you added more extensions and functionality since then?Smotz wrote:Odd because it worked for years on my other (PWM) RA. As a matter of fact, it still does. I think this new RA may be 'sluggish' - is that even possible ?re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
That is good to know. I am not a Reef Angel expert by any means, and in most non-reef angel cases I've encountered setting an output high would leave it on until you set it low again. My background is in embedded software for medical devices though where everything is super strict so I have a tendency to be extra careful...Smotz wrote:It 'should' never dump the feeder continually. The relay is a "button push" - the feeder rotates once upon pressing it.re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
Well, your help is very much appreciated. I think you nailed it when you said it is 'missing the second'. I suspect this RA may be 'sluggish' or something.re76 wrote:That is good to know. I am not a Reef Angel expert by any means, and in most non-reef angel cases I've encountered setting an output high would leave it on until you set it low again. My background is in embedded software for medical devices though where everything is super strict so I have a tendency to be extra careful...Smotz wrote:It 'should' never dump the feeder continually. The relay is a "button push" - the feeder rotates once upon pressing it.re76 wrote:Give this a shot, but keep in mind that if it was in fact the problem, you might now have the same exact problem with the feeder too since you are also using seconds in those lines! This is the point I was trying to make. It would be really bad if your feeder turned on but you happened to miss the specific second where you are trying to turn it off, and then it never turns off!
Make sure you keep an eye on this! At a minimum I would change the line where you turn the feeder off to be a greater than or equal check on seconds, that way you know you will be guaranteed to turn the feeder off. I would rather miss turning the feeder on than miss turning it off and pouring a ton of food into the tank.
re76 wrote:To clarify a bit. The Reef Angel is based off of the Arduino platform. The Arduino tries to makes things simple for the end user by having only two important functions that are needed: a setup() function and loop() function.
The loop function gets called by the arduino as much as possible. If you have a really basic loop function then it might be able to call it 1000 times a second. It can't call it again though until the loop function is finished from the last time it was called. So as your loop() gets more and more code in it it will be getting called less times every second.
Not to mention for every new extension you add to the RA there is more library code to support it. As an end user you don't see this a lot unless you go looking for it. This is why I asked if you had added more extensions or features. As you add complexity to your code it is going to run a bit slower. This isn't necessarily a bad thing, it just is. It is a standard understood trade off for all embedded systems whether they are using Arduino or not.
There is a concept in software development called defensive programming. It is the idea that you make your code robust enough to handle unforeseen conditions. This is why I am a big advocate of using >= or <= instead of == wherever possible. You sometimes have to work your logic a bit differently but it usually isn't a lot more work, and it ensures that if for some crazy reason you don't catch something on the first loop through you will get it on the second time around, and will never just miss it totally like you can with ==.
Hope this makes sense and isn't just causing more confusion!
Code: Select all
[code]//FEEDING MODE
// Begin Feeding Mode
if ( ((hour() == 7)) && (minute() == 30) || ((hour() == 18)) && (minute() == 00)) {
ReefAngel.FeedingModeStart(); // FEEDING MODE @ 7:30 AM or 6:00 PM
NoteTime = millis();
unsigned long DeltaTS = millis() - NoteTime;
if ( DeltaTS >= 60000 ) ReefAngel.Relay.On(Feeder); // turn on after 1 minute
if ( DeltaTS >= 70000 ) ReefAngel.Relay.On(Feeder); // turn off shortly after
if ( DeltaTS >= 120000 ) ReefAngel.Relay.On(Feeder); // turn on after 2 minutes
if ( DeltaTS >= 130000 ) ReefAngel.Relay.On(Feeder); // turn off shortly after
}
Code: Select all
if (ReefAngel.WaterLevel.GetLevel(1) <= 25 && ReefAngel.DisplayedMenu!=WATERCHANGE_MODE)
{
NoteTime = millis();
unsigned long DeltaTS = millis() - NoteTime;
if ( DeltaTS >= 15000 )
{
ReefAngel.Relay.Override(Return,0);
ReefAngel.Relay.Override(Skimmer,0);
ReefAngel.Relay.Override(UVlight,0);
ReefAngel.Relay.Override(Feeder,0);
ReefAngel.Relay.Override(CalcDose,0);
ReefAngel.Relay.Override(AlkDose,0);
ReefAngel.Relay.Override(Fugelight,1);
pinMode(lowATOPin,OUTPUT); //Buzzer
digitalWrite(lowATOPin,HIGH); //Buzzer
}}
// END EMERGENCY WATER LEVEL ROUTINE
You are correct that time_t is an unsigned long, and it will work in this scenario. It is intended however to work with the time.h library which stores the seconds since epoch (seconds since 1970), which is a lot of seconds. Hence why it is an unsigned long. The call to millis() returns the millisecond counter (also very big).lnevo wrote:It doesn't need to be static if you set it every time to millis() but it may need to be global only if you are using it in other functions outside of loop. If you are setting it once and want it retained like a flag that you want to set if something happens then you'll want to use static.
Also time_t is an unsigned long so no difference here.
Code: Select all
//FEEDING MODE
// Begin Feeding Mode
if (((hour() == 7)) && (minute() == 30) || (hour() == 18) && (minute() == 00)) {
ReefAngel.FeedingModeStart(); // FEEDING MODE @ 7:30 AM or 6:00 PM
NoteTime = millis();
unsigned long DeltaTS = millis() - NoteTime;
if ( DeltaTS >= 130000 ) ReefAngel.Relay.Off(Feeder); // turn off 2 minutes 10 secs
else
if ( DeltaTS >= 120000 ) ReefAngel.Relay.On(Feeder); // turn on after 2 minutes
else
if ( DeltaTS >= 70000 ) ReefAngel.Relay.Off(Feeder); // turn off 1 min 10 secs
else
if ( DeltaTS >= 60000 ) ReefAngel.Relay.On(Feeder); // turn on after 1 minute
}