Controlling a relay (not relay box)
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Controlling a relay (not relay box)
I seem to be posting a bit lately. (I keep getting more ideas :p)
I have a quick question and hopefully it will be a quick answer.
I have a ozone unit that is manually controlled (will not turn on auto magically when plugged into live power, you have to press an on switch)
Now I'm looking at controlling the on switch using a relay, but I'm unsure as to how I will be able to control/power the relay. Am I able to use PWM at all? Or is there something else I could do?
I have a quick question and hopefully it will be a quick answer.
I have a ozone unit that is manually controlled (will not turn on auto magically when plugged into live power, you have to press an on switch)
Now I'm looking at controlling the on switch using a relay, but I'm unsure as to how I will be able to control/power the relay. Am I able to use PWM at all? Or is there something else I could do?
Re: Controlling a relay (not relay box)
This is similar to the mod i did on my eheim auto-feeder. You need to bypass the switch that has the manual on. You can solder the pins on the switch to a relay (5v or 12v) then connect the relay to a small wall wart transformer (usb charger is 5v usually...) then you just need to turn the relay the wall wart is plugged into for a few seconds to simulate a button press. The main power no longer needs to be on the RA unless you want to override and ensure it cant be turned on..
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
Ahhhhh! The good old wall wart. I think I might do that. I only need it to trigger momentarily (about 1 second or maybe less)
Could I also use a pin on the same relay (double pole I guess) that can tie in with my IO module to ensure it closes/turns off after it's been triggered?)
I saw the auto feeder in your code (I'm going through your code and studying it by the way, I really love how nice and clean and neat it is! I'm using some if the neatness ideas in mine. I hope that's ok? :p
Could I also use a pin on the same relay (double pole I guess) that can tie in with my IO module to ensure it closes/turns off after it's been triggered?)
I saw the auto feeder in your code (I'm going through your code and studying it by the way, I really love how nice and clean and neat it is! I'm using some if the neatness ideas in mine. I hope that's ok? :p
Re: Controlling a relay (not relay box)
Please do! Thats why I post it and keep it updated...
You could...if you have the io module you could just connect it directly to that and do the on/off through the i/on module. No relay required
Take a look at the autofeeder thread...i believe the code is there for using the io module to do it...
You could...if you have the io module you could just connect it directly to that and do the on/off through the i/on module. No relay required

Take a look at the autofeeder thread...i believe the code is there for using the io module to do it...
Re: Controlling a relay (not relay box)
Eheim-autofeeder
http://forum.reefangel.com/viewtopic.php?t=1415
The code is in there later in the thread..i don't know if thats the "simplest" approach though...there may be an easier way to output to the io channel...
http://forum.reefangel.com/viewtopic.php?t=1415
The code is in there later in the thread..i don't know if thats the "simplest" approach though...there may be an easier way to output to the io channel...
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
Oh! You can do that? Is there a max voltage/amperage you can out through it? Does it kinda become a relay itself?
Re: Controlling a relay (not relay box)
I think it's pretty much open and close, but I don't have an IO module so I don't know.
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
Ok, I'm stuck. lol.
Until I can figure out the IO module method of doing this, I'm using a wall wart as you (lee) suggested to trigger a relay on, and then off.
Now, I've tried using the autofeeder code, but I don't know what it is about it, but I just can't get it to work the way I need it too.
In my code, I have a method that sits in loop that checks the orp so that it can decided whether to trigger the ozone on, or off dependent on the values. The way I want to turn the ozone on and off (the ozonator runs all the time) is to call a method that will trigger a relay which a wall wart is plugged into which will then *Press* the switch for 1-2 seconds then release.
Excuse my code from being a mess, I'm throwing stuff together and it's a little unorganized.
Until I can figure out the IO module method of doing this, I'm using a wall wart as you (lee) suggested to trigger a relay on, and then off.
Now, I've tried using the autofeeder code, but I don't know what it is about it, but I just can't get it to work the way I need it too.
In my code, I have a method that sits in loop that checks the orp so that it can decided whether to trigger the ozone on, or off dependent on the values. The way I want to turn the ozone on and off (the ozonator runs all the time) is to call a method that will trigger a relay which a wall wart is plugged into which will then *Press* the switch for 1-2 seconds then release.
Excuse my code from being a mess, I'm throwing stuff together and it's a little unorganized.
Code: Select all
#include <Salinity.h>
#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 <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <ReefAngel.h>
#include <WiFiAlert.h>
// Custom Menu
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Lights On";
prog_char menu2_label[] PROGMEM = "Feeding Mode";
prog_char menu3_label[] PROGMEM = "Water Change";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Sal Calibration";
prog_char menu8_label[] PROGMEM = "ORP 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 Relay Ports by Name
#define Return 1
#define WaveMakerLeft 2
#define WaveMakerRight 3
#define LED_WhiteBlue 4
#define LED_Supplemental 5
#define LED_Moonlights 6
#define SkimmerPump 7
#define SkimmerVenturi 8
#define OzoneTrigger Box1_Port1
#define Heater2 Box1_Port2
#define Chiller Box1_Port3
#define ChillerPump Box1_Port4
#define SumpLight Box1_Port5
#define Ozone Box1_Port6
#define ATO Box1_Port7
#define Unused Box1_Port8
//Define Custom Memory Locations
#define CMem_B_Delay_Ozone 100
#define CMem_B_Delay_Skimmer 101
#define CMem_B_Delay_Chiller 102
#define CMem_I_OrpMin 103
#define CMem_I_OrpMax 104
#define CMem_B_LightsEnabled 105
#define CMem_B_OzoneRelayDelay 106
#define CMem_B_ResetMemory 199
void init_memory()
{
InternalMemory.write(CMem_B_Delay_Ozone,10);
InternalMemory.write(CMem_B_Delay_Skimmer,1);
InternalMemory.write(CMem_B_Delay_Chiller,1);
InternalMemory.write_int(CMem_I_OrpMin,400);
InternalMemory.write_int(CMem_I_OrpMax,420);
InternalMemory.write(CMem_B_LightsEnabled,false);
InternalMemory.write(CMem_B_OzoneRelayDelay,2);
InternalMemory.write(CMem_B_ResetMemory,true);
}
static boolean bOrpOn;
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.SetTemperatureUnit( Celsius ); // set to Celsius Temperature
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port3Bit | Port7Bit;
ReefAngel.FeedingModePortsE[0] = Port4Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port5Bit | Port6Bit | Port7Bit | Port8Bit;
ReefAngel.WaterChangePortsE[0] = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port6Bit | Port7Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port4Bit | Port5Bit;
ReefAngel.LightsOnPortsE[0] = Port5Bit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port4Bit | Port5Bit | Port6Bit;
ReefAngel.OverheatShutoffPortsE[0] = Port1Bit | Port2Bit | Port5Bit | Port6Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Ports that are always on
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( WaveMakerLeft );
ReefAngel.Relay.On( WaveMakerRight );
ReefAngel.Relay.On( SkimmerPump );
ReefAngel.Relay.On( Chiller );
ReefAngel.Relay.On( ChillerPump );
//ReefAngel.Relay.On( Heater1 );
ReefAngel.Relay.On( Heater2 );
ReefAngel.Relay.On( Chiller );
////// Place additional initialization code below here
bOrpOn = false;
//RA has been restarted. Set lights enabled memory to off
////// Place additional initialization code above here
//ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items)); // Initialize Menu
if (InternalMemory.read(CMem_B_ResetMemory))
{
init_memory();
InternalMemory.write(CMem_B_ResetMemory,false);
}
}
void loop()
{
//Initialise Relays
ReefAngel.MoonLights( LED_Moonlights );
ReefAngel.MoonLights( SumpLight );
ReefAngel.DayLights( LED_WhiteBlue );
ReefAngel.DayLights( LED_Supplemental );
//ReefAngel.StandardHeater( Heater1 );
//ReefAngel.StandardHeater( Heater2 )
//ReefAngel.StandardFan( ChillerPump );
ReefAngel.SingleATOLow( ATO );
ReefAngel.Relay.DelayedOn( Ozone, InternalMemory.read(CMem_B_Delay_Ozone));
ReefAngel.Relay.DelayedOn( SkimmerVenturi, InternalMemory.read(CMem_B_Delay_Skimmer));
// ReefAngel.Relay.DelayedOn( Chiller, InternalMemory.read(CMem_B_Delay_Chiller));
//Check Temp and Control Ports
//if(InternalMemory.read(temp
//SetPWM as normal if Lights Mode Off
if(!InternalMemory.read(CMem_B_LightsEnabled))
{
ReefAngel.PWM.Channel0PWMParabola();
ReefAngel.PWM.Channel1PWMParabola();
ReefAngel.PWM.Channel2PWMParabola();
ReefAngel.PWM.Channel3PWMParabola();
ReefAngel.PWM.SetChannel( 4, MoonPhase() );
ReefAngel.PWM.SetChannel( 5, MoonPhase() );
}
else
{
ReefAngel.PWM.SetChannel(0,80);
ReefAngel.PWM.SetChannel(1,100);
ReefAngel.PWM.SetChannel(2,100);
ReefAngel.PWM.SetChannel(3,100);
}
////// Place your custom code below here
OrpControl();
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "egadgetjnr" );
ReefAngel.ShowInterface();
}
void DrawCustomMain()
{
int x,y;
char text[10];
// Dimming Expansion
x = 15;
y = 2;
for ( int a=0;a<6;a++ )
{
if ( a>2 ) x = 75;
if ( a==3 ) y = 2;
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x,y,"Ch :" );
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x+12,y,a );
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x+24,y,ReefAngel.PWM.GetChannelValue(a) );
y += 10;
}
pingSerial();
// I/O Expansion
byte bkcolor;
x = 14;
y = 32;
for ( int a=0;a<6;a++ )
{
ReefAngel.LCD.DrawCircleOutline( x+(a*20),y,4,COLOR_MEDIUMORCHID );
if ( ReefAngel.IO.GetChannel(a) ) bkcolor=COLOR_WHITE;
else bkcolor=COLOR_GRAY;
ReefAngel.LCD.FillCircle( x+(a*20),y,2,bkcolor );
}
pingSerial();
// Parameters
#if defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 39, ReefAngel.Params,
ReefAngel.PWM.GetDaylightValue(), ReefAngel.PWM.GetActinicValue() );
#else // defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 39, ReefAngel.Params );
#endif // defined DisplayLEDPWM && ! defined RemoveAllLights
pingSerial();
// Salinity
ReefAngel.LCD.DrawText( COLOR_DARKKHAKI,DefaultBGColor,16,69, "SAL:" );
ReefAngel.LCD.DrawSingleMonitor( ReefAngel.Params.Salinity,COLOR_DARKKHAKI,40,69, 10 );
pingSerial();
// ORP
ReefAngel.LCD.DrawText( COLOR_PALEVIOLETRED,DefaultBGColor,82,69, "ORP:" );
ReefAngel.LCD.DrawText( COLOR_PALEVIOLETRED,DefaultBGColor,106,69, ReefAngel.Params.ORP );
pingSerial();
// pH Expansion
ReefAngel.LCD.DrawText( COLOR_MEDIUMSEAGREEN,DefaultBGColor,15,80, "PHE:" );
ReefAngel.LCD.DrawSingleMonitor( ReefAngel.Params.PHExp,COLOR_MEDIUMSEAGREEN,39,80, 100 );
pingSerial();
// Main Relay Box
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox( 12, 90, TempRelay );
pingSerial();
// Relay Expansion
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox( 12, 103, TempRelay );
pingSerial();
// Date and Time
ReefAngel.LCD.DrawDate( 6, 122 );
pingSerial();
}
void DrawCustomGraph()
{
}
void OrpControl()
{
int iOrpMax = InternalMemory.read(CMem_I_OrpMax);
int iOrpMin = InternalMemory.read(CMem_I_OrpMin);
int iCurrentOrp = ReefAngel.Params.ORP;
byte bOzoneDelay = InternalMemory.read(CMem_B_Delay_Ozone);
static WiFiAlert waOrpAlert;
if(bOrpOn && iCurrentOrp >= iOrpMax)
{
//OzoneRelayTrigger();
//Send alert Orp is Off
waOrpAlert.Send("Ozone+is+off!+Max+Ozone");
}
else if(!bOrpOn && iCurrentOrp <= iOrpMin )
{
//Trigger ORP Relay
//OzoneRelayTrigger();
waOrpAlert.Send("Ozone+is+on.");
}
}
void OzoneRelayTrigger()
{
Serial.println("OzoneRelayTrigger");
static TimerClass afTimer;
static time_t buttonPress;
byte orpDelay=InternalMemory.read(CMem_B_OzoneRelayDelay);
if(InternalMemory.read(CMem_B_OzoneRelayDelay)) {
InternalMemory.write(CMem_B_OzoneRelayDelay,false);
afTimer.SetInterval(1);
afTimer.Start();
}
if(afTimer.IsTriggered())
{
buttonPress=now();
if (bOrpOn)
{
bOrpOn = false;
}
else
{
bOrpOn = true;
}
}
// Turn on relay for 2 seconds.
if (now()-buttonPress<2)
{
ReefAngel.Relay.On(OzoneTrigger);
Serial.println("OzoneTriggerON");
}
else
{
ReefAngel.Relay.Off(OzoneTrigger);
Serial.println("OzoneTriggerOFF");
}
}
void Overheat()
{
//Enter code for overheat
//Send wifi alert
}
//Menu Code
/*prog_char menu1_label[] PROGMEM = "Lights On";
prog_char menu2_label[] PROGMEM = "Feeding Mode";
prog_char menu3_label[] PROGMEM = "Water Change";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Sal Calibration";
prog_char menu8_label[] PROGMEM = "ORP Calibration";
prog_char menu9_label[] PROGMEM = "Date / Time";*/
void MenuEntry1()
{
//Turn All Lights On
if(!InternalMemory.read(CMem_B_LightsEnabled))
{
//Turn All Lights Relays On (except moons)
ReefAngel.Relay.Override(LED_WhiteBlue,1);
ReefAngel.Relay.Override(LED_Supplemental,1);
ReefAngel.Relay.Override(SumpLight,1);
InternalMemory.write(CMem_B_LightsEnabled,true);
//Set PWM to max
//Might not need to, done in loop
}
else
{
//Set Lights to default
ReefAngel.StandardLights(LED_WhiteBlue);
ReefAngel.StandardLights(LED_Supplemental);
ReefAngel.MoonLights(SumpLight);
//Set PWM to normal
InternalMemory.write(CMem_B_LightsEnabled,false);
}
}
void MenuEntry2()
{
//Feeding Mode
ReefAngel.FeedingModeStart();
}
void MenuEntry3()
{
//Water Change
ReefAngel.WaterChangeModeStart();
}
void MenuEntry4()
{
//ATO Clear
ReefAngel.ATOClear();
}
void MenuEntry5()
{
//Overheat Clear
ReefAngel.OverheatClear();
}
void MenuEntry6()
{
//PH Calibration
ReefAngel.SetupCalibratePHExp();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
//Sal Calibration
//ReefAngel.SetupCalibrateSalinity();
//ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
//ORP Calibration
}
void MenuEntry9()
{
//Date/Time
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
Re: Controlling a relay (not relay box)
This piece doesn't make sense:
In the first pass, InternalMemory.read(CMem_B_OzoneRelayDelay) will read 2, assuming you initialized memory.
So the if will result in true and it will assing 0 to that memory location in the next line: InternalMemory.write(CMem_B_OzoneRelayDelay,false);
Then, the if will never be able to return true ever again until you initialize memory again.
I'm not so sure what you were trying to do and why you were saving false (which is the same as zero) to that memory location is the if statement is true.
Code: Select all
if(InternalMemory.read(CMem_B_OzoneRelayDelay)) {
InternalMemory.write(CMem_B_OzoneRelayDelay,false);
afTimer.SetInterval(1);
afTimer.Start();
}
So the if will result in true and it will assing 0 to that memory location in the next line: InternalMemory.write(CMem_B_OzoneRelayDelay,false);
Then, the if will never be able to return true ever again until you initialize memory again.
I'm not so sure what you were trying to do and why you were saving false (which is the same as zero) to that memory location is the if statement is true.
Roberto.
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
That actually makes sense.
I took that bit of code from the Autofeeder part (it was an early post Lee put up).
I tried to adapt it and I guess I broke it. lol.
My first adaptation of it, i think the Loop() was calling it and it just kept setting now() to the time plus 2 seconds all the time.
So, does that setinterval act like a relay.delay where if its in the loop, it will be a count down? also, with the setinterval is that in minutes or seconds.
I took that bit of code from the Autofeeder part (it was an early post Lee put up).
I tried to adapt it and I guess I broke it. lol.
My first adaptation of it, i think the Loop() was calling it and it just kept setting now() to the time plus 2 seconds all the time.
So, does that setinterval act like a relay.delay where if its in the loop, it will be a count down? also, with the setinterval is that in minutes or seconds.
Re: Controlling a relay (not relay box)
I don't use the timer class anymore..think its easier with a simple variable to hold the time.
I'll check my current code for you tomorrow and help you pull this off..
Before we jump through hoops you've confirmed this works manually right?
I'll check my current code for you tomorrow and help you pull this off..
Before we jump through hoops you've confirmed this works manually right?
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
That would be awesome Lee
If by manually you mean using the wall wart to trigger the separate relay to act as a switch, then yes. Lol. All the hardware is working perfectly when the relay is triggered for at least 1 second (short bursts don't work).
If by manually you mean using the wall wart to trigger the separate relay to act as a switch, then yes. Lol. All the hardware is working perfectly when the relay is triggered for at least 1 second (short bursts don't work).

-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
I'm stuck. Lol. I tried to get it to work but I think I'm going around in circles. 

Re: Controlling a relay (not relay box)
Sorry i'll post something later this morning for you to try...
Re: Controlling a relay (not relay box)
I'm assuming the button pressed again turns the ozone off. I'm also assuming that if you are over orpmax you want ozone off and under orpmin you want it on... Also assume kind of like programming a remote control macro that relay off on the ozone followed by relay on for one second on the button press will turn it on... Correct me if I'm off on anything...this is a bit trickier than I expected but no worries...
Re: Controlling a relay (not relay box)
Ok so this was based on the assumptions above. Your OrpControl function will determine if bOrpOn is true or false and then call OzoneRelayTrigger(). If bOrpOn is false it will power off the Ozone port (don't want an out of sync condition...safer to turn off the ozone relay completely...) if bOrpOn is true and the ozone relay is off (so it should only happen once...) it will turn on the ozone port and assign now() to buttonPress. After that it will wait bOzoneDelay seconds (make sure to set this back to non-zero based on your earlier code that was changing it...) then turn on the wall wart on for two seconds. When orp gets over the max, bOrpOn will be false and the trigger function will turn off the ozone again. The trigger relay should be off by default except during the narrow window between the orp delay and orp delay + 2.
Code: Select all
void OrpControl()
{
int iOrpMax = InternalMemory.read(CMem_I_OrpMax);
int iOrpMin = InternalMemory.read(CMem_I_OrpMin);
int iCurrentOrp = ReefAngel.Params.ORP;
byte bOzoneDelay = InternalMemory.read(CMem_B_Delay_Ozone);
static WiFiAlert waOrpAlert;
if(bOrpOn && iCurrentOrp >= iOrpMax)
{
//Send alert Orp is Off
waOrpAlert.Send("Ozone+is+off!+Max+Ozone");
bOrpOn=false;
}
else if(!bOrpOn && iCurrentOrp <= iOrpMin )
{
bOrpOn=true;
waOrpAlert.Send("Ozone+is+on.");
}
// Trigger ORP Relay based on bOrpOn global variable
OzoneRelayTrigger();
}
void OzoneRelayTrigger()
{
// Serial.println("OzoneRelayTrigger");
static time_t buttonPress;
// wait some seconds for ozone to turn on before hitting button
byte orpDelay=InternalMemory.read(CMem_B_OzoneRelayDelay);
// turn on Ozone then trigger button press if ozone is needed
if (bOrpOn && !ReefAngel.Relay.Status(Ozone) {
ReefAngel.Relay.On(Ozone);
buttonPress=now();
}
// turn off ozone if not needed
if (!bOrpOn) ReefAngel.Relay.Off(Ozone);
// Turn on relay for 2 seconds but wait for orpDelay
if ((now()-buttonPress>orpDelay) && (now()-buttonPress<orpDelay+2))
{
ReefAngel.Relay.On(OzoneTrigger);
Serial.println("OzoneTriggerON");
}
else
{
ReefAngel.Relay.Off(OzoneTrigger);
Serial.println("OzoneTriggerOFF");
}
}
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
Your assumptions were spot on! Thanks heaps lee, I finally had a chance to upload my program and it works perfectly!
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)

What seems to be happening is, the ozone relay trigger (Box1_Port1) is working perfectly as it should be, on at startup of the RA for about 3 seconds or so, then off, but the actual ozoneator is only turning on for a split second during the period, then turning off and staying off.
I've gone through the code nearly 100 times now, and I just can't pick whats causing it to be on for only a second and I've tried many different things to try and get it to stay on.
If need be, I don't care if we just get rid of the "delay" of the ozoneator coming on to make things easy. As long as the ozoneator comes on then the relay Trigger is on for about 3-4 seconds this will allow the initial startup of the Ozoneator then the it catching the Relay Trigger.
I'm hoping that makes sense, I think I might be losing it. lol

Lee or anyone for that matter, are you able to take a look and tell me where I'm going wrong please? Your wisdom is greatly appreciated!
I honestly think I've done something and might have screwed it up, but I can't find what

Code: Select all
#include <Salinity.h>
#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 <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <ReefAngel.h>
#include <WiFiAlert.h>
// Custom Menu
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Lights On";
prog_char menu2_label[] PROGMEM = "Feeding Mode";
prog_char menu3_label[] PROGMEM = "Water Change";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Sal Calibration";
prog_char menu8_label[] PROGMEM = "ORP 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 Relay Ports by Name
#define Return 1
#define WaveMakerLeft 2
#define WaveMakerRight 3
#define LED_WhiteBlue 4
#define LED_Supplemental 5
#define LED_Moonlights 6
#define SkimmerPump 7
#define SkimmerVenturi 8
#define OzoneTrigger Box1_Port1
#define Heater2 Box1_Port2
#define Chiller Box1_Port3
#define ChillerPump Box1_Port4
#define SumpLight Box1_Port5
#define Ozone Box1_Port6
#define ATO Box1_Port7
#define Unused Box1_Port8
//Define Custom Memory Locations
#define CMem_B_Delay_Ozone 100
#define CMem_B_Delay_Skimmer 101
#define CMem_B_Delay_Chiller 102
#define CMem_I_OrpMin 103
#define CMem_I_OrpMax 104
#define CMem_B_LightsEnabled 105
#define CMem_B_OzoneRelayDelay 106
#define CMem_B_ResetMemory 199
void init_memory()
{
InternalMemory.write(CMem_B_Delay_Ozone,10);
InternalMemory.write(CMem_B_Delay_Skimmer,1);
InternalMemory.write(CMem_B_Delay_Chiller,1);
InternalMemory.write_int(CMem_I_OrpMin,400);
InternalMemory.write_int(CMem_I_OrpMax,420);
InternalMemory.write(CMem_B_LightsEnabled,false);
InternalMemory.write(CMem_B_OzoneRelayDelay,3);
InternalMemory.write(CMem_B_ResetMemory,true);
}
static boolean bOrpOn;
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.SetTemperatureUnit( Celsius ); // set to Celsius Temperature
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port3Bit | Port7Bit;
ReefAngel.FeedingModePortsE[0] = Port4Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port5Bit | Port6Bit | Port7Bit | Port8Bit;
ReefAngel.WaterChangePortsE[0] = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port6Bit | Port7Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port4Bit | Port5Bit;
ReefAngel.LightsOnPortsE[0] = Port5Bit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port4Bit | Port5Bit | Port6Bit;
ReefAngel.OverheatShutoffPortsE[0] = Port1Bit | Port2Bit | Port5Bit | Port6Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Ports that are always on
ReefAngel.Relay.On( Return );
ReefAngel.Relay.On( WaveMakerLeft );
ReefAngel.Relay.On( WaveMakerRight );
ReefAngel.Relay.On( SkimmerPump );
ReefAngel.Relay.On( Chiller );
ReefAngel.Relay.On( ChillerPump );
ReefAngel.Relay.On( Heater2 );
////// Place additional initialization code below here
bOrpOn = false;
//RA has been restarted. Set lights enabled memory to off
////// Place additional initialization code above here
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items)); // Initialize Menu
if (InternalMemory.read(CMem_B_ResetMemory))
{
init_memory();
InternalMemory.write(CMem_B_ResetMemory,false);
}
}
void loop()
{
//Initialise Relays
ReefAngel.MoonLights( LED_Moonlights );
ReefAngel.MoonLights( SumpLight );
ReefAngel.DayLights( LED_WhiteBlue );
ReefAngel.DayLights( LED_Supplemental );
ReefAngel.SingleATOLow( ATO );
//ReefAngel.Relay.DelayedOn( Ozone, InternalMemory.read(CMem_B_Delay_Ozone));
ReefAngel.Relay.DelayedOn( SkimmerVenturi, InternalMemory.read(CMem_B_Delay_Skimmer));
//ReefAngel.Relay.DelayedOn( Chiller, InternalMemory.read(CMem_B_Delay_Chiller));
////// Place your custom code below here
OrpControl(); //Checks ORP on orp probe, and enables or disables Ozoneator depending.
TempControl(); //INACTIVE: Checks Temp and enables and disables heaters and chiller depending.
LightModeCheck(); //Checks the lights enabled varible then sets lights on or off depending.
//Overheat(); //INACTIVE: Checks overheat status, and acts occordingly.
//HourlyMaintenance(); //INACTIVE: Performs hourly Maintenance
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "egadgetjnr" );
ReefAngel.ShowInterface();
}
void LightModeCheck()
{
//Check to see if lights mode is enabled, if so, set relays
if(!InternalMemory.read(CMem_B_LightsEnabled))
{
//Set light relays to normal
ReefAngel.MoonLights( SumpLight );
ReefAngel.DayLights( LED_WhiteBlue );
ReefAngel.DayLights( LED_Supplemental );
//Set PWM to normal
ReefAngel.PWM.Channel0PWMParabola();
ReefAngel.PWM.Channel1PWMParabola();
ReefAngel.PWM.Channel2PWMParabola();
ReefAngel.PWM.Channel3PWMParabola();
ReefAngel.PWM.SetChannel( 4, MoonPhase() );
ReefAngel.PWM.SetChannel( 5, MoonPhase() );
}
else
{
//Turn light relays on
ReefAngel.Relay.On( SumpLight );
ReefAngel.Relay.On( LED_WhiteBlue );
ReefAngel.Relay.On( LED_Supplemental );
//Set PWM to normals
ReefAngel.PWM.SetChannel(0,80);
ReefAngel.PWM.SetChannel(1,100);
ReefAngel.PWM.SetChannel(2,100);
ReefAngel.PWM.SetChannel(3,100);
}
}
void DrawCustomMain()
{
int x,y;
char text[10];
// Dimming Expansion
x = 15;
y = 2;
for ( int a=0;a<6;a++ )
{
if ( a>2 ) x = 75;
if ( a==3 ) y = 2;
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x,y,"Ch :" );
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x+12,y,a );
ReefAngel.LCD.DrawText( COLOR_DARKGOLDENROD,DefaultBGColor,x+24,y,ReefAngel.PWM.GetChannelValue(a) );
y += 10;
}
pingSerial();
// I/O Expansion
byte bkcolor;
x = 14;
y = 32;
for ( int a=0;a<6;a++ )
{
ReefAngel.LCD.DrawCircleOutline( x+(a*20),y,4,COLOR_MEDIUMORCHID );
if ( ReefAngel.IO.GetChannel(a) ) bkcolor=COLOR_WHITE;
else bkcolor=COLOR_GRAY;
ReefAngel.LCD.FillCircle( x+(a*20),y,2,bkcolor );
}
pingSerial();
// Parameters
#if defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 39, ReefAngel.Params,
ReefAngel.PWM.GetDaylightValue(), ReefAngel.PWM.GetActinicValue() );
#else // defined DisplayLEDPWM && ! defined RemoveAllLights
ReefAngel.LCD.DrawMonitor( 15, 39, ReefAngel.Params );
#endif // defined DisplayLEDPWM && ! defined RemoveAllLights
pingSerial();
// Salinity
ReefAngel.LCD.DrawText( COLOR_DARKKHAKI,DefaultBGColor,16,69, "SAL:" );
ReefAngel.LCD.DrawSingleMonitor( ReefAngel.Params.Salinity,COLOR_DARKKHAKI,40,69, 10 );
pingSerial();
// ORP
ReefAngel.LCD.DrawText( COLOR_PALEVIOLETRED,DefaultBGColor,82,69, "ORP:" );
ReefAngel.LCD.DrawText( COLOR_PALEVIOLETRED,DefaultBGColor,106,69, ReefAngel.Params.ORP );
pingSerial();
// pH Expansion
ReefAngel.LCD.DrawText( COLOR_MEDIUMSEAGREEN,DefaultBGColor,15,80, "PHE:" );
ReefAngel.LCD.DrawSingleMonitor( ReefAngel.Params.PHExp,COLOR_MEDIUMSEAGREEN,39,80, 100 );
pingSerial();
// Main Relay Box
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox( 12, 90, TempRelay );
pingSerial();
// Relay Expansion
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox( 12, 103, TempRelay );
pingSerial();
// Date and Time
ReefAngel.LCD.DrawDate( 6, 122 );
pingSerial();
}
void DrawCustomGraph()
{
}
void HourlyMaintenance()
{
//Check to see if NOW is an hour.
if (now()%SECS_PER_HOUR == 0)
{
//Perform Cleanup/Checks for out of sync
}
}
void OrpControl()
{
int iOrpMax = InternalMemory.read(CMem_I_OrpMax);
int iOrpMin = InternalMemory.read(CMem_I_OrpMin);
int iCurrentOrp = ReefAngel.Params.ORP;
byte bOzoneDelay = InternalMemory.read(CMem_B_Delay_Ozone);
static WiFiAlert waOrpAlert;
if(bOrpOn && iCurrentOrp >= iOrpMax)
{
//Send alert Orp is Off
waOrpAlert.Send("Ozone+is+off!+Max+Ozone");
bOrpOn=false;
}
else if(!bOrpOn && iCurrentOrp <= iOrpMin )
{
bOrpOn=true;
waOrpAlert.Send("Ozone+is+on.");
}
// Trigger ORP Relay based on bOrpOn global variable
OzoneRelayTrigger();
}
void OzoneRelayTrigger()
{
// Serial.println("OzoneRelayTrigger");
static time_t buttonPress;
// wait some seconds for ozone to turn on before hitting button
byte orpDelay=InternalMemory.read(CMem_B_OzoneRelayDelay);
// turn on Ozone then trigger button press if ozone is needed
if (bOrpOn && !ReefAngel.Relay.Status(Ozone)) {
ReefAngel.Relay.On(Ozone);
buttonPress=now();
}
// turn off ozone if not needed
if (!bOrpOn) ReefAngel.Relay.Off(Ozone);
// Turn on relay for 2 seconds but wait for orpDelay
if ((now()-buttonPress>orpDelay) && (now()-buttonPress<orpDelay+2))
{
ReefAngel.Relay.On(OzoneTrigger);
Serial.println("OzoneTriggerON");
}
else
{
ReefAngel.Relay.Off(OzoneTrigger);
Serial.println("OzoneTriggerOFF");
}
}
void TempControl()
{
if ((ReefAngel.Params.Temp[T1_PROBE] >= InternalMemory.read_int(Mem_I_ChillerTempOn)) && (ReefAngel.Params.Temp[T1_PROBE] <= InternalMemory.read_int(Mem_I_ChillerTempOff)))
{
//Turn on Chiller Ports
}
else
{
//Turn off chiller ports
}
}
void Overheat()
{
//Enter code for overheat
//Send wifi alert
}
void LightsEnable()
{
//Turn All Lights On
if(!InternalMemory.read(CMem_B_LightsEnabled))
{
//Set Lights Enabled Item to true so lights are on
InternalMemory.write(CMem_B_LightsEnabled,true);
}
else
{
//Set Lights Disabled Item to true so lights are normal
InternalMemory.write(CMem_B_LightsEnabled,false);
}
}
//Menu Entry Block
void MenuEntry1()
{
//Turn All Lights On
//NOTE: Merged code from here to LightsEnable method
LightsEnable();
}
void MenuEntry2()
{
//Feeding Mode
ReefAngel.FeedingModeStart();
}
void MenuEntry3()
{
//Water Change
LightsEnable();
ReefAngel.WaterChangeModeStart();
}
void MenuEntry4()
{
//ATO Clear
ReefAngel.ATOClear();
}
void MenuEntry5()
{
//Overheat Clear
ReefAngel.OverheatClear();
}
void MenuEntry6()
{
//PH Calibration
ReefAngel.SetupCalibratePHExp();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
//Sal Calibration
ReefAngel.SetupCalibrateSalinity();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
//ORP Calibration
ReefAngel.SetupCalibrateORP();
}
void MenuEntry9()
{
//Date/Time
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
Re: Controlling a relay (not relay box)
The ozone on and off is controlled by the bOrpOn variable. This is based on the orpmin and orpmax that you set. It's controlled independently from the relay trigger and delay... I would start with confirming your memory variables are set properly and then also add some serial.println calls showing the value of bOrpOn so you can see if that is what's expected or not. The code looks ok, doesn't look like you changed anything functional. But I would start with the debug to see what's happening with bOrpOn...
-
- Posts: 54
- Joined: Wed Jul 18, 2012 1:32 pm
Re: Controlling a relay (not relay box)
I might give that a go. Just play around a bit more to get it to tell me what's going on.
I keep getting the odd random email a couple of times a day telling me ORP is on the sometimes off when it hasn't change to be high enough to turn of. It's weird
I keep getting the odd random email a couple of times a day telling me ORP is on the sometimes off when it hasn't change to be high enough to turn of. It's weird
Re: Controlling a relay (not relay box)
Sensors jitter. Thats actually a problem in your code a bit...need to measure that its been in low orp or high orp long enough to be real...it could be messing with the actual usage enough to be a problem.