/* * Copyright 2010 Reef Angel / Roberto Imai * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Updated by: Curt Binder * Updates Released under Apache License, Version 2.0 */ #include #include #include #include "ReefAngel.h" byte ButtonPress = 0; #if defined DisplayLEDPWM && ! defined RemoveAllLights || defined DCPUMPCONTROL boolean LightsOverride=true; #endif // defined DisplayLEDPWM && ! defined RemoveAllLights #ifdef RA_STANDARD #include #elif defined RA_PLUS #include #elif defined RA_STAR #include #elif defined RA_TOUCH || defined RA_TOUCHDISPLAY #include #elif defined RA_EVOLUTION #include #endif // RA_STANDARD /* Constants declare in ReefAngel.h */ const byte ReefAngelClass::PH_MAXIMUM_RANGE[2]={4, 10}; const byte ReefAngelClass::PH_DEFAULT_RANGE[2]={7, 10}; const char ReefAngelClass::PH_SETUP_MENU_LABEL[2][19]={"Calibrate pH", "Calibrate pH(Exp.)"}; const char ReefAngelClass::PH_SETUP_MENU_STEP[2][13]={"First value", "Second value"}; void ReefAngelClass::Init() { Serial.begin(57600); Serial.setTimeout(100); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } #ifdef RA_STANDARD #include #elif defined RA_PLUS #include #elif defined RA_STAR #include #elif defined RA_TOUCH || defined RA_TOUCHDISPLAY #include #elif defined RA_EVOLUTION #include #endif // RA_STANDARD pinMode(lowATOPin,INPUT); pinMode(highATOPin,INPUT); digitalWrite(lowATOPin,HIGH); //pull up resistor on lowATOPin digitalWrite(highATOPin,HIGH); //pull up resistor on highATOPin #if defined DisplayLEDPWM pinMode(actinicPWMPin,OUTPUT); pinMode(daylightPWMPin,OUTPUT); digitalWrite(actinicPWMPin,LOW); //pull down resistor on actinicPWMPin digitalWrite(daylightPWMPin,LOW); //pull down resistor on daylightPWMPin #endif // DisplayLEDPWM digitalWrite(0,HIGH); //pull up resistor on RX digitalWrite(1,HIGH); //pull up resistor on TX #ifdef __AVR_ATmega2560__ digitalWrite(14,HIGH); //pull up resistor on TX3 digitalWrite(15,HIGH); //pull up resistor on RX3 digitalWrite(16,HIGH); //pull up resistor on TX2 digitalWrite(17,HIGH); //pull up resistor on RX2 digitalWrite(18,HIGH); //pull up resistor on TX1 digitalWrite(19,HIGH); //pull up resistor on RX1 #endif // __AVR_ATmega2560__ TempSensor.Init(); RAStart=now(); LastFeedingMode=now(); LastWaterChangeMode=now(); LastStart = RAStart; // Set the time normal mode is started BusLocked=false; // Bus is not locked OldTempRelay=255; OldDaylight=255; OldActinic=255; ChangeMode=0; AlertFlags = 0; StatusFlags = 0; Splash=true; Relay.AllOff(); CEM=0; OverheatProbe = T2_PROBE; TempProbe = T1_PROBE; #ifdef ENABLE_ATO_LOGGING AtoEventCount = 0; #endif // ENABLE_ATO_LOGGING #ifdef ENABLE_EXCEED_FLAGS InternalMemory.write(Overheat_Exceed_Flag, 0); InternalMemory.write(ATO_Exceed_Flag, 0); InternalMemory.write(ATO_Single_Exceed_Flag, 0); #endif // ENABLE_EXCEED_FLAGS PHMin = InternalMemory.PHMin_read(); PHMax = InternalMemory.PHMax_read(); #ifdef ORPEXPANSION ORPMin = InternalMemory.ORPMin_read(); ORPMax = InternalMemory.ORPMax_read(); #endif // ORPEXPANSION #ifdef SALINITYEXPANSION SalMax = InternalMemory.SalMax_read(); #endif // SALINITYEXPANSION #ifdef PHEXPANSION PHExpMin = InternalMemory.PHExpMin_read(); PHExpMax = InternalMemory.PHExpMax_read(); #endif // PHEXPANSION taddr = InternalMemory.T1Pointer_read(); Params.Salinity=0; Params.ORP=0; Params.PHExp=0; if ((taddr>120) || (taddr<0)) { InternalMemory.T1Pointer_write(0); taddr = 0; } Timer[FEEDING_TIMER].SetInterval(InternalMemory.FeedingTimer_read()); // Default Feeding timer if ( InternalMemory.LCDTimer_read() < 60 ) InternalMemory.LCDTimer_write(60); // if it's less than 60, force it to 60 Timer[LCD_TIMER].SetInterval(InternalMemory.LCDTimer_read()); // LCD Sleep Mode timer Timer[LCD_TIMER].Start(); // start timer Timer[PORTAL_TIMER].SetInterval(300); // Portal Timer[PORTAL_TIMER].Start(); // start timer Timer[STORE_PARAMS_TIMER].SetInterval(720); // Store Params Timer[STORE_PARAMS_TIMER].ForceTrigger(); // Set the default ports to be turned on & off during the 2 modes FeedingModePorts = 0; WaterChangePorts = 0; // Set the ports that get shutoff when the overheat value is reached OverheatShutoffPorts = 0; // DelayedOn ports, do not manually modify this variable, let the DelayedOn function modify it DelayedOnPorts = 0; // Set the ports that get turned on when you select the Lights On LightsOnPorts = 0; #ifdef OVERRIDE_PORTS // Override all relay masks for the following ports OverridePorts = 0; #endif // OVERRIDE_PORTS #ifdef RelayExp // Expansion Module ports to toggle, defaults to not toggle any ports for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { FeedingModePortsE[i] = 0; WaterChangePortsE[i] = 0; OverheatShutoffPortsE[i] = 0; DelayedOnPortsE[i] = 0; LightsOnPortsE[i] = 0; #ifdef OVERRIDE_PORTS // Override all relay masks for the following ports OverridePortsE[i] = 0; #endif // OVERRIDE_PORTS } #endif // RelayExp #if defined wifi || defined I2CMASTER || defined ETH_WIZ5100 EM = PWMEbit + RFEbit + AIbit + Salbit + ORPbit + IObit + PHbit + WLbit; EM1 = HUMbit + DCPumpbit + Leakbit + PARbit + SCPWMbit; #ifdef RelayExp for (byte a=0;aSplashDuration) && Splash) { Splash=false; if (TS.IsCalibrationNeeded()) { CalibrateTouchScreen(); } TouchLCD.FullClear(BKCOLOR); } #endif // RA_TOUCH #if not defined RA_TOUCHDISPLAY #ifdef RFEXPANSION byte RFRecv=0; RFRecv=RF.RFCheck(); if (RFRecv==1) { ClearScreen(DefaultBGColor); FeedingModeStart(); } if (RFRecv==2) { Timer[FEEDING_TIMER].ForceTrigger(); } if (DisplayedMenu!=FEEDING_MODE && RF.UseMemory) RF.SetMode(InternalMemory.RFMode_read(),InternalMemory.RFSpeed_read(),InternalMemory.RFDuration_read()); if (LightRelayOn) { for (byte a=0; aAI.StreamDelay) { AI.Send(); AI.AImillis=millis(); } #endif // AI_LED #if defined PWMEXPANSION && defined DisplayLEDPWM #if defined(__SAM3X8E__) VariableControl.ExpansionWrite(); #else // __SAM3X8E__ PWM.ExpansionWrite(); #endif // __SAM3X8E__ #endif // PWMEXPANSION #ifdef SIXTEENCHPWMEXPANSION && defined DisplayLEDPWM #if defined(__SAM3X8E__) VariableControl.SIXTEENChExpansionWrite(); #else // __SAM3X8E__ PWM.SIXTEENChExpansionWrite(); #endif // __SAM3X8E__ #endif // SIXTEENCHPWMEXPANSION #ifdef IOEXPANSION if (bitRead(ReefAngel.CEM,CloudIOBit)==0) IO.GetChannel(); #endif // IOEXPANSION #endif // RA_TOUCHDISPLAY #ifdef OVERRIDE_PORTS // Reset relay masks for ports we want always in their programmed states. ReefAngel.Relay.RelayMaskOn &= ~OverridePorts; ReefAngel.Relay.RelayMaskOff |= OverridePorts; #ifdef RelayExp byte i; for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOnE[i] &= ~OverridePortsE[i]; Relay.RelayMaskOffE[i] |= OverridePortsE[i]; } #endif // RelayExp #endif // OVERRRIDE_PORTS #ifdef RA_PLUS if (relaytest) { Relay.RelayData=0; Relay.RelayMaskOff=255; Relay.RelayMaskOn=1<<((millis()%3200)/400); } #endif // RA_PLUS Relay.Write(); #ifdef ETH_WIZ5100 Network.Update(); #endif // ETH_WIZ5100 #ifdef RANET // Send RANet data if (millis()-RANetlastmillis>RANetDelay) { RANetlastmillis=millis(); RANetCRC=0; RANetData[0]=RANetSeq; RANetData[1]=RANET_SIZE; for (int a=0;a>8; // MSB #endif #else RANetData[18+a]=0; RANetData[18+a+1]=0; #endif // PWMEXPANSION } for (int a=0;a>8; // MSB #endif #else RANetData[30+a]=0; RANetData[30+a+1]=0; #endif // SIXTEENCHPWMEXPANSION } // char buf[3]; RANetData[62]=TriggerValue; // Trigger byte TriggerValue=0; // Reset to 0 for (int a=0;a DEGREE_C_HIGH_TEMP ) { fConvert = true; x = CONVERT_TO_C(x); InternalMemory.HeaterTempOn_write(x); } x = InternalMemory.HeaterTempOff_read(); if ( x > DEGREE_C_HIGH_TEMP ) { fConvert = true; x = CONVERT_TO_C(x); InternalMemory.HeaterTempOff_write(x); } x = InternalMemory.ChillerTempOn_read(); if ( x > DEGREE_C_HIGH_TEMP ) { fConvert = true; x = CONVERT_TO_C(x); InternalMemory.ChillerTempOn_write(x); } x = InternalMemory.ChillerTempOff_read(); if ( x > DEGREE_C_HIGH_TEMP ) { fConvert = true; x = CONVERT_TO_C(x); InternalMemory.ChillerTempOff_write(x); } x = InternalMemory.OverheatTemp_read(); if ( (x > DEGREE_C_OVERHEAT_HIGH_TEMP) || fConvert ) { x = CONVERT_TO_C(x); InternalMemory.OverheatTemp_write(x); } } else { // F // if the values are smaller than lowest temp, then we know we have C stored bool fConvert = false; x = InternalMemory.HeaterTempOn_read(); if ( x < DEGREE_F_LOW_TEMP ) { fConvert = true; x = CONVERT_TO_F(x); InternalMemory.HeaterTempOn_write(x); } x = InternalMemory.HeaterTempOff_read(); if ( x < DEGREE_F_LOW_TEMP ) { fConvert = true; x = CONVERT_TO_F(x); InternalMemory.HeaterTempOff_write(x); } x = InternalMemory.ChillerTempOn_read(); if ( x < DEGREE_F_LOW_TEMP ) { fConvert = true; x = CONVERT_TO_F(x); InternalMemory.ChillerTempOn_write(x); } x = InternalMemory.ChillerTempOff_read(); if ( x < DEGREE_F_LOW_TEMP ) { fConvert = true; x = CONVERT_TO_F(x); InternalMemory.ChillerTempOff_write(x); } x = InternalMemory.OverheatTemp_read(); if ( (x < DEGREE_F_OVERHEAT_LOW_TEMP) || fConvert ) { x = CONVERT_TO_F(x); InternalMemory.OverheatTemp_write(x); } } } #if defined SALINITYEXPANSION void ReefAngelClass::ApplySalinityCompensation() { // Salinity Compensation was contributed by ahmedess // http://forum.reefangel.com/viewtopic.php?p=7386#p7386 // Credits to dazza1304 // http://forum.reefangel.com/viewtopic.php?f=3&t=2670 if (Salinity.TemperatureCompensation!=-1 && Params.Temp[TempProbe]>0) { double SalCompensation; double SalConstant=Salinity.TemperatureCompensation; if (Salinity.TemperatureCompensation==0) { if (TempSensor.unit) SalConstant=0.0024; else SalConstant=0.001333; } SalCompensation=Params.Salinity/(1+((Params.Temp[TempProbe]-InternalMemory.SalTempComp_read())*SalConstant)); Params.Salinity=round(SalCompensation); } } #endif // SALINITYEXPANSION #ifdef BUSCHECK boolean ReefAngelClass::isBusLock() { return bitRead(AlertFlags, BusLockFlag); } #endif //BUSCHECK #ifdef LEAKDETECTOREXPANSION boolean ReefAngelClass::IsLeakDetected() { boolean detect=false; if (bitRead(ReefAngel.CEM,CloudLeakBit)==0) { int iLeak=0; Wire.requestFrom(I2CLeak, 2); if (Wire.available()) { iLeak = Wire.read(); iLeak = iLeak<<8; iLeak += Wire.read(); } detect=iLeak>2000; #ifdef EMBEDDED_LEAK detect|=analogRead(LeakPin)<400; LeakValue=detect; #endif // EMBEDDED_LEAK #ifdef RA_TOUCHDISPLAY detect=LeakStatus; #endif // RA_TOUCHDISPLAY } else { detect=LeakValue; } return detect; } void ReefAngelClass::LeakCheck() { // if leak is detected if ( !IsLeakDetected() ) Leakmillis=millis(); if (millis()-Leakmillis>3000) // Only flag leak if we have a leak for 3 seconds { LED.On(); bitSet(AlertFlags,LeakFlag); // invert the ports that are activated Relay.RelayMaskOff &= ~LeakShutoffPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOffE[i] &= ~LeakShutoffPortsE[i]; } #endif // RelayExp } } void ReefAngelClass::LeakClear() { LED.Off(); bitClear(AlertFlags,LeakFlag); Relay.RelayMaskOff |= LeakShutoffPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOffE[i] |= LeakShutoffPortsE[i]; } #endif // RelayExp Relay.Write(); #if defined RA_TOUCH || defined RA_TOUCHDISPLAY || defined RA_STAR if (DisplayedMenu==TOUCH_MENU) SetDisplayedMenu(DEFAULT_MENU); #endif // RA_TOUCH #ifdef RA_TOUCHDISPLAY SendMaster(MESSAGE_COMMAND,COMMAND_CLEAR_LEAK,0); #endif // RA_TOUCHDISPLAY } boolean ReefAngelClass::isLeak() { return bitRead(AlertFlags, LeakFlag); } #endif // LEAKDETECTOREXPANSION void ReefAngelClass::StandardLights(byte LightsRelay, byte OnHour, byte OnMinute, byte OffHour, byte OffMinute) { int NumMinsToday=NumMins(hour(),minute()); if (NumMins(OffHour,OffMinute) > NumMins(OnHour,OnMinute)) { if (NumMinsToday >= NumMins(OnHour,OnMinute)) Relay.On(LightsRelay); else Relay.Off(LightsRelay); if (NumMinsToday >= NumMins(OffHour,OffMinute)) Relay.Off(LightsRelay); } else { if (NumMinsToday >= NumMins(OffHour,OffMinute)) Relay.Off(LightsRelay); else Relay.On(LightsRelay); if (NumMinsToday >= NumMins(OnHour,OnMinute)) Relay.On(LightsRelay); } } void ReefAngelClass::MHLights(byte LightsRelay, byte OnHour, byte OnMinute, byte OffHour, byte OffMinute, byte MHDelay) { unsigned int MHTimer = MHDelay; MHTimer *= SECS_PER_MIN; if ( now()-RAStart > MHTimer ) StandardLights(LightsRelay, OnHour, OnMinute, OffHour, OffMinute); } void ReefAngelClass::StandardHeater(byte Probe, byte HeaterRelay, int LowTemp, int HighTemp) { if (LowTemp < 100) LowTemp *= 10; // Correct temp settings that aren't in the correct range if (HighTemp < 100) HighTemp *= 10; // Correct temp settings that aren't in the correct range if (Params.Temp[Probe] == 0) return; // Don't turn the heater on if the temp is reading 0 if (Params.Temp[Probe] <= LowTemp && Params.Temp[Probe] > 0) Relay.On(HeaterRelay); // If sensor 1 temperature <= LowTemp - turn on heater if (Params.Temp[Probe] >= HighTemp) Relay.Off(HeaterRelay); // If sensor 1 temperature >= HighTemp - turn off heater } void ReefAngelClass::StandardHeater(byte HeaterRelay, int LowTemp, int HighTemp) { StandardHeater(TempProbe, HeaterRelay, LowTemp, HighTemp); } void ReefAngelClass::StandardHeater2(byte HeaterRelay, int LowTemp, int HighTemp) { StandardHeater(T2_PROBE, HeaterRelay, LowTemp, HighTemp); } void ReefAngelClass::StandardHeater3(byte HeaterRelay, int LowTemp, int HighTemp) { StandardHeater(T3_PROBE, HeaterRelay, LowTemp, HighTemp); } void ReefAngelClass::StandardFan(byte Probe, byte FanRelay, int LowTemp, int HighTemp) { if (LowTemp < 100) LowTemp *= 10; // Correct temp settings that aren't in the correct range if (HighTemp < 100) HighTemp *= 10; // Correct temp settings that aren't in the correct range if (Params.Temp[Probe] == 0) return; // Don't turn the fan/chiller on if the temp is reading 0 if (Params.Temp[Probe] >= HighTemp) Relay.On(FanRelay); // If sensor 1 temperature >= HighTemp - turn on fan if (Params.Temp[Probe] <= LowTemp) Relay.Off(FanRelay); // If sensor 1 temperature <= LowTemp - turn off fan } void ReefAngelClass::StandardFan(byte FanRelay, int LowTemp, int HighTemp) { StandardFan(T1_PROBE, FanRelay, LowTemp, HighTemp); } void ReefAngelClass::StandardFan2(byte FanRelay, int LowTemp, int HighTemp) { StandardFan(T2_PROBE, FanRelay, LowTemp, HighTemp); } void ReefAngelClass::StandardFan3(byte FanRelay, int LowTemp, int HighTemp) { StandardFan(T3_PROBE, FanRelay, LowTemp, HighTemp); } void ReefAngelClass::CO2Control(byte CO2Relay, int LowPH, int HighPH) { CO2Control(CO2Relay,LowPH,HighPH,false); } void ReefAngelClass::CO2Control(byte CO2Relay, int LowPH, int HighPH, bool useExp) { int ph=Params.PH; if (useExp) ph=Params.PHExp; if (ph <= LowPH) Relay.Off(CO2Relay); // If PH <= LowPH - turn on CO2 if (ph >= HighPH) Relay.On(CO2Relay); // If sensor 1 PH >= HighPH - turn off CO2 } void ReefAngelClass::PHControl(byte PHControlRelay, int LowPH, int HighPH) { PHControl(PHControlRelay,LowPH,HighPH,false); } void ReefAngelClass::PHControl(byte PHControlRelay, int LowPH, int HighPH, bool useExp) { int ph=Params.PH; if (useExp) ph=Params.PHExp; if (ph <= LowPH) Relay.On(PHControlRelay); // If PH <= LowPH - turn on PHControlRelay if (ph >= HighPH) Relay.Off(PHControlRelay); // If sensor 1 PH >= HighPH - turn off PHControlRelay } void ReefAngelClass::StandardATO(byte ATORelay, int ATOTimeout) { // Input: Relay port and timeout value (max number of seconds that ATO pump is allowed to run) unsigned long TempTimeout = ATOTimeout; TempTimeout *= 1000; /* Is the low switch active (meaning we need to top off) and are we not currently topping off Then we set the timer to be now and start the topping pump */ if ( LowATO.IsActive() && ( !LowATO.IsTopping()) ) { LowATO.Timer = millis(); LowATO.StartTopping(); Relay.On(ATORelay); } // If the high switch is activated, this is a safeguard to prevent over running of the top off pump if ( HighATO.IsActive() ) { LowATO.StopTopping(); // stop the low ato timer Relay.Off(ATORelay); } /* If the current time minus the start time of the ATO pump is greater than the specified timeout value AND the ATO pump is currently running: We turn on the status LED and shut off the ATO pump This prevents the ATO pump from contniously running. */ if ( (millis()-LowATO.Timer > TempTimeout) && LowATO.IsTopping() ) { LED.On(); bitSet(AlertFlags,ATOTimeOutFlag); #ifdef ENABLE_EXCEED_FLAGS if (InternalMemory.read(ATO_Exceed_Flag)==0) InternalMemory.write(ATO_Exceed_Flag, 1); #endif // ENABLE_EXCEED_FLAGS Relay.Off(ATORelay); #ifdef ENABLE_ATO_LOGGING // bump the counter if a timeout occurs AtoEventCount++; if ( AtoEventCount >= MAX_ATO_LOG_EVENTS ) { AtoEventCount = 0; } #endif // ENABLE_ATO_LOGGING } } #if defined WATERLEVELEXPANSION || defined MULTIWATERLEVELEXPANSION #ifdef MULTIWATERLEVELEXPANSION void ReefAngelClass::WaterLevelATO(byte Channel, byte ATORelay, int ATOTimeout, byte LowLevel, byte HighLevel) { #else void ReefAngelClass::WaterLevelATO(byte ATORelay, int ATOTimeout, byte LowLevel, byte HighLevel) { byte Channel = 0; #endif // MULTIWATERLEVELEXPANSION // Input: Relay port and timeout value (max number of seconds that ATO pump is allowed to run) // Input: Low and High Water Level to start and stop ATO pump unsigned long TempTimeout = ATOTimeout; TempTimeout *= 1000; /* Is the low level is reached (meaning we need to top off) and are we not currently topping off Then we set the timer to be now and start the topping pump */ if ( WaterLevel.GetLevel(Channel) < LowLevel && ( !WLATO.IsTopping()) ) { WLATO.Timer = millis(); WLATO.StartTopping(); Relay.On(ATORelay); } // If the high level is reached, this is a safeguard to prevent over running of the top off pump if ( WaterLevel.GetLevel(Channel) > HighLevel ) { WLATO.StopTopping(); // stop the low ato timer Relay.Off(ATORelay); } /* If the current time minus the start time of the ATO pump is greater than the specified timeout value AND the ATO pump is currently running: We turn on the status LED and shut off the ATO pump This prevents the ATO pump from contniously running. */ if ( (millis()-WLATO.Timer > TempTimeout) && WLATO.IsTopping() ) { LED.On(); bitSet(AlertFlags,ATOTimeOutFlag); #ifdef ENABLE_EXCEED_FLAGS if (InternalMemory.read(ATO_Exceed_Flag)==0) InternalMemory.write(ATO_Exceed_Flag, 1); #endif // ENABLE_EXCEED_FLAGS Relay.Off(ATORelay); #ifdef ENABLE_ATO_LOGGING // bump the counter if a timeout occurs AtoEventCount++; if ( AtoEventCount >= MAX_ATO_LOG_EVENTS ) { AtoEventCount = 0; } #endif // ENABLE_ATO_LOGGING } } #endif // WATERLEVELEXPANSION || MULTIWATERLEVELEXPANSION void ReefAngelClass::SingleATO(bool bLow, byte ATORelay, int intTimeout, byte byteHrInterval) { /* If the switch is active, the float is opposite of the 2 wires, Check if we are not currently topping, if we are not check if we can run If we have an hour interval, check if we can run If we can run, activate the pump because we need water Otherwise the switch is not active, we need to see if we are currently topping If we are topping, then we need to stop the pump because we are topped off */ bool bCanRun = true; static int iLastTop = -1; if ( byteHrInterval ) { int iSafeTop = NumMins(hour(), minute()) - iLastTop; if ( iSafeTop < 0 ) { iSafeTop += 1440; } if ( (iSafeTop < (byteHrInterval * 60)) && (iLastTop >= 0) ) { bCanRun = false; } } RA_ATOClass *ato; if ( bLow ) { ato = &LowATO; } else { ato = &HighATO; } unsigned long t = intTimeout; t *= 1000; if ( ato->IsActive() ) { if ( (! ato->IsTopping()) && bCanRun ) { ato->Timer = millis(); ato->StartTopping(); Relay.On(ATORelay); } } else { // not active if ( ato->IsTopping() ) { iLastTop = NumMins(hour(), minute()); ato->StopTopping(); Relay.Off(ATORelay); } } if ( ((millis() - ato->Timer) > t) && ato->IsTopping() ) { LED.On(); bitSet(AlertFlags,ATOTimeOutFlag); #ifdef ENABLE_EXCEED_FLAGS if (InternalMemory.read(ATO_Single_Exceed_Flag)==0) InternalMemory.write(ATO_Single_Exceed_Flag, 1); #endif // ENABLE_EXCEED_FLAGS Relay.Off(ATORelay); #ifdef ENABLE_ATO_LOGGING // bump the counter if a timeout occurs AtoEventCount++; if ( AtoEventCount >= MAX_ATO_LOG_EVENTS ) { AtoEventCount = 0; } #endif // ENABLE_ATO_LOGGING } } boolean ReefAngelClass::isATOTimeOut() { return bitRead(AlertFlags, ATOTimeOutFlag); } #ifdef KALKDOSER void ReefAngelClass::KalkDoser(byte KalkRelay, int LowPH, int TimeoutSeconds, byte MinuteInterval) { bool canRun = true; static int lastRun = -1; if (MinuteInterval) { int nextSafeRun = NumMins(hour(), minute()) - lastRun; if ( nextSafeRun < 0 ) { nextSafeRun += 24; } if( nextSafeRun < MinuteInterval && lastRun >= 0 ) { canRun = false; } } // if pH is low if(Params.PH <= LowPH && !KWDoser.IsTopping() && canRun) { KWDoser.Timer = millis(); KWDoser.StartTopping(); Relay.On(KalkRelay); } if(Params.PH > LowPH && KWDoser.IsTopping() == true) { lastRun = NumMins(hour(), minute()); KWDoser.StopTopping(); Relay.Off(KalkRelay); } unsigned long t = TimeoutSeconds * 1000; if ((millis() - KWDoser.Timer) > t && KWDoser.IsTopping()) { lastRun = NumMins(hour(), minute()); KWDoser.StopTopping(); Relay.Off(KalkRelay); } } #endif // KALKDOSER void ReefAngelClass::DosingPump(byte DPRelay, byte DPTimer, byte OnHour, byte OnMinute, int RunTime) { /* This function configures and sets up the dosing pump and turns it on at the appropriate time Once the timer has expired for the dosing pump, it shuts it off DPRelay - relay dosing pump is plugged into (0-8) Timer - timer to control dosing pump OnHour & OnMinute - time to turn on the dosing pump (in 24hr based time) RunTime - duration to run the pump */ // Let's see if it's supposed to start running the timer now if ( (NumMins(hour(), minute()) == NumMins(OnHour, OnMinute)) && (second() == 0) ) { Relay.On(DPRelay); //LED.On(); Timer[DPTimer].SetInterval(RunTime); Timer[DPTimer].Start(); } // is the timer expired? if ( Timer[DPTimer].IsTriggered() ) { // timer expired, so let's shut off the pump Relay.Off(DPRelay); //LED.Off(); } } void ReefAngelClass::DosingPumpRepeat(byte DPRelay, int OffsetMinute, int RepeatMinute, int RunTime) { // Old code has been replaced with dedvalson (Don) - 01/06/2012 /* This function runs the specified relay for the RunTime seconds every RepeatMinute minutes. So you can run the relay for 10 seconds every 60 minutes (1 hour) This function bases the RepeatMinute off of Midnight (00:00) of the current day. It uses midnight to compute when the pump will run. DPRelay - Relay that contains the dosing pump Timer - number of the timer in the timer array to use RepeatMinute - number of minutes to wait before running the pump again RunTime - duration (in seconds) to run the pump */ // if the current minutes since midnight are divisible by the repeat interval and the current seconds // are zero (top of the minute), then we can run the pump /* time_t t = now(); uint8_t h = hour(t); if ( (h == 0) && (minute(t) == 0) ) { // if we are at midnight, change hours to 24 so we can get the correct minutes for computation h = 24; } int current_min = NumMins(h, minute(t)); int r = current_min % RepeatMinute; if ( (r == 0) && (second(t) == 0) ) { Relay.On(DPRelay); Timer[DPTimer].SetInterval(RunTime); Timer[DPTimer].Start(); } // Should change the timer to be a Dosing Pump Timer // is the timer expired? if ( Timer[DPTimer].IsTriggered() ) { Relay.Off(DPRelay); } */ signed long t=(elapsedSecsToday(now())-((long)OffsetMinute*60)); Relay.Set(DPRelay,(t%((long)RepeatMinute*60))=0); } void ReefAngelClass::Wavemaker(byte WMRelay, int WMTimer) { // Old code has been replaced with dedvalson (Don) - 01/06/2012 Relay.Set(WMRelay,(now()%(WMTimer*2))WMRTimer) { WMRTimer=now()+random(MinWMTimer, MaxWMTimer); Relay.Toggle(WMRelay); } } void ReefAngelClass::WavemakerRandom1(byte WMRelay, int MinWMTimer, int MaxWMTimer) { static time_t WMRTimer1=now()+MinWMTimer; if (now()>WMRTimer1) { WMRTimer1=now()+random(MinWMTimer, MaxWMTimer); Relay.Toggle(WMRelay); } } void ReefAngelClass::WavemakerRandom2(byte WMRelay, int MinWMTimer, int MaxWMTimer) { static time_t WMRTimer2=now()+MinWMTimer; if (now()>WMRTimer2) { WMRTimer2=now()+random(MinWMTimer, MaxWMTimer); Relay.Toggle(WMRelay); } } void ReefAngelClass::WavemakerToggle(byte WMRelay1, byte WMRelay2, int WMTimer) { if ( (now()%(WMTimer*2))3000) // Only flag overheat if we have overheat for 3 seconds { LED.On(); bitSet(AlertFlags,OverheatFlag); #ifdef ENABLE_EXCEED_FLAGS InternalMemory.write(Overheat_Exceed_Flag, 1); #endif // ENABLE_EXCEED_FLAGS // invert the ports that are activated Relay.RelayMaskOff &= ~OverheatShutoffPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOffE[i] &= ~OverheatShutoffPortsE[i]; } #endif // RelayExp } } void ReefAngelClass::OverheatClear() { LED.Off(); bitClear(AlertFlags,OverheatFlag); #ifdef ENABLE_EXCEED_FLAGS InternalMemory.write(Overheat_Exceed_Flag, 0); #endif // ENABLE_EXCEED_FLAGS Relay.RelayMaskOff |= OverheatShutoffPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOffE[i] |= OverheatShutoffPortsE[i]; } #endif // RelayExp Relay.Write(); #if defined RA_TOUCH || defined RA_TOUCHDISPLAY || defined RA_STAR if (DisplayedMenu==TOUCH_MENU) SetDisplayedMenu(DEFAULT_MENU); #endif // RA_TOUCH #ifdef RA_TOUCHDISPLAY SendMaster(MESSAGE_COMMAND,COMMAND_CLEAR_OVERHEAT,0); #endif // RA_TOUCHDISPLAY } boolean ReefAngelClass::isOverheat() { return bitRead(AlertFlags, OverheatFlag); } void ReefAngelClass::LightsOn() { // turn on ports Relay.RelayMaskOn |= LightsOnPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOnE[i] |= LightsOnPortsE[i]; } #endif // RelayExp Relay.Write(); bitSet(StatusFlags,LightsOnFlag); #if defined RA_TOUCH || defined RA_TOUCHDISPLAY || defined RA_STAR menu_button_functions1[2] = &ReefAngelClass::LightsOff; if (DisplayedMenu==TOUCH_MENU) SetDisplayedMenu(DEFAULT_MENU); #endif // RA_TOUCH #ifdef RA_TOUCHDISPLAY SendMaster(MESSAGE_COMMAND,COMMAND_LIGHTS_ON,0); #endif // RA_TOUCHDISPLAY } void ReefAngelClass::LightsOff() { // reset ports Relay.RelayMaskOn &= ~LightsOnPorts; #ifdef RelayExp for ( byte i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { Relay.RelayMaskOnE[i] &= ~LightsOnPortsE[i]; } #endif // RelayExp #if defined DisplayLEDPWM && !defined REEFANGEL_MINI // TODO should possibly store the PWM value to be reset instead of turning off completely // sets PWM to 0% #if defined(__SAM3X8E__) VariableControl.SetActinic(0); VariableControl.SetDaylight(0); #else PWM.SetActinic(0); PWM.SetDaylight(0); #endif #endif // defined DisplayLEDPWM && !defined REEFANGEL_MINI Relay.Write(); bitClear(StatusFlags,LightsOnFlag); #if defined RA_TOUCH || defined RA_TOUCHDISPLAY || defined RA_STAR menu_button_functions1[2] = &ReefAngelClass::LightsOn; if (DisplayedMenu==TOUCH_MENU) SetDisplayedMenu(DEFAULT_MENU); #endif // RA_TOUCH #ifdef RA_TOUCHDISPLAY SendMaster(MESSAGE_COMMAND,COMMAND_LIGHTS_OFF,0); #endif // RA_TOUCHDISPLAY } void ReefAngelClass::ExitMenu() { // Handles the cleanup to return to the main screen if (bitRead(StatusFlags,FeedingFlag)) LastFeedingMode=now(); bitClear(StatusFlags,FeedingFlag); if (bitRead(StatusFlags,WaterChangeFlag)) LastWaterChangeMode=now(); bitClear(StatusFlags,WaterChangeFlag); WDTReset(); ClearScreen(DefaultBGColor); Timer[LCD_TIMER].Start(); SetDisplayedMenu(DEFAULT_MENU); #ifdef RA_STAR DisplayedScreen=MAIN_SCREEN; #endif CheckDrawGraph(); redrawmenu=true; OldTempRelay=Relay.RelayData-1; OldParams.PH=Params.PH-1; } void ReefAngelClass::SetDisplayedMenu(byte value) { DisplayedMenu=value; #ifdef RA_TOUCHDISPLAY SendMaster(MESSAGE_MENU,value,value); // Change Menu #endif // RA_TOUCHDISPLAY } #if defined wifi || defined ETH_WIZ5100 void ReefAngelClass::Portal(char *username) { Network.Portal(username); } void ReefAngelClass::Portal(char *username, char *key) { Network.Portal(username,key); } void ReefAngelClass::DDNS(char *subdomain) { Network.DDNS(subdomain); } #endif // wifi || ETH_WIZ5100 #ifdef RA_TOUCHDISPLAY void receiveEvent(int howMany) { byte d[4]; byte crc=0; wdt_reset(); if (ReefAngel.Sleeping) { while(Wire.available()) Wire.read(); } else { if (howMany==4) { crc=0; for(int a=0;a<4;a++) { d[a]=Wire.read(); // Serial.print(d[a]); // Serial.print("\t"); crc+=d[a]; } // Serial.println(); crc-=d[3]; if (crc==d[3] && d[0]=='$') { switch (d[1]) { case 0: if (ReefAngel.DisplayedMenu!=d[2]) { if (ReefAngel.DisplayedMenu!=TOUCH_MENU) { // deduct 100 from the value to indicate we are coming from an interrupt. ReefAngel.DisplayedMenu=d[2]-100; } } break; case 1: ReefAngel.Board=d[2]; break; case 2: ReefAngel.AlertFlags=d[2]; break; case 3: ReefAngel.StatusFlags=d[2]; break; case 4: ReefAngel.Params.Temp[T1_PROBE]=((ReefAngel.Params.Temp[T1_PROBE]/256)<<8)+d[2]; break; case 5: ReefAngel.Params.Temp[T1_PROBE]=(d[2]<<8) + (ReefAngel.Params.Temp[T1_PROBE]%256); break; case 6: ReefAngel.Params.Temp[T2_PROBE]=((ReefAngel.Params.Temp[T2_PROBE]/256)<<8)+d[2]; break; case 7: ReefAngel.Params.Temp[T2_PROBE]=(d[2]<<8) + (ReefAngel.Params.Temp[T2_PROBE]%256); break; case 8: ReefAngel.Params.Temp[T3_PROBE]=((ReefAngel.Params.Temp[T3_PROBE]/256)<<8)+d[2]; break; case 9: ReefAngel.Params.Temp[T3_PROBE]=(d[2]<<8) + (ReefAngel.Params.Temp[T3_PROBE]%256); break; case 10: ReefAngel.Params.PH=((ReefAngel.Params.PH/256)<<8)+d[2]; break; case 11: ReefAngel.Params.PH=(d[2]<<8) + (ReefAngel.Params.PH%256); break; case 12: ReefAngel.LowATO.SetActive(bitRead(d[2],0)); ReefAngel.HighATO.SetActive(bitRead(d[2],1)); ReefAngel.AlarmInput.SetActive(bitRead(d[2],2)); ReefAngel.LeakStatus=bitRead(d[2],3); break; case 13: ReefAngel.PWM.SetDaylight(d[2]); break; case 14: ReefAngel.PWM.SetActinic(d[2]); break; case 15: ReefAngel.PWM.SetDaylight2(d[2]); break; case 16: ReefAngel.PWM.SetActinic2(d[2]); break; case 17: ReefAngel.Relay.RelayData=d[2]; break; case 18: ReefAngel.Relay.RelayMaskOn=d[2]; break; case 19: ReefAngel.Relay.RelayMaskOff=d[2]; break; case 20: case 21: case 22: case 23: case 24: case 25: ReefAngel.PWM.SetChannel(d[1]-20,d[2]); break; case 26: ReefAngel.RF.Mode=d[2]; break; case 27: ReefAngel.RF.Speed=d[2]; break; case 28: ReefAngel.RF.Duration=d[2]; break; case 29: case 30: case 31: case 32: case 33: case 34: ReefAngel.RF.RadionChannels[d[1]-29]=d[2]; break; case 35: case 36: case 37: ReefAngel.AI.SetChannel(d[1]-35,d[2]); break; case 38: ReefAngel.IO.IOPorts=d[2]; break; case 39: ReefAngel.DCPump.Mode=d[2]; break; case 40: ReefAngel.DCPump.Speed=d[2]; break; case 41: ReefAngel.DCPump.Duration=d[2]; break; case 42: ReefAngel.Relay.RelayDataE[0]=d[2]; break; case 43: ReefAngel.Relay.RelayMaskOnE[0]=d[2]; break; case 44: ReefAngel.Relay.RelayMaskOffE[0]=d[2]; break; case 45: ReefAngel.Relay.RelayDataE[1]=d[2]; break; case 46: ReefAngel.Relay.RelayMaskOnE[1]=d[2]; break; case 47: ReefAngel.Relay.RelayMaskOffE[1]=d[2]; break; case 48: ReefAngel.Relay.RelayDataE[2]=d[2]; break; case 49: ReefAngel.Relay.RelayMaskOnE[2]=d[2]; break; case 50: ReefAngel.Relay.RelayMaskOffE[2]=d[2]; break; case 51: ReefAngel.Relay.RelayDataE[3]=d[2]; break; case 52: ReefAngel.Relay.RelayMaskOnE[3]=d[2]; break; case 53: ReefAngel.Relay.RelayMaskOffE[3]=d[2]; break; case 54: ReefAngel.Relay.RelayDataE[4]=d[2]; break; case 55: ReefAngel.Relay.RelayMaskOnE[4]=d[2]; break; case 56: ReefAngel.Relay.RelayMaskOffE[4]=d[2]; break; case 57: ReefAngel.Relay.RelayDataE[5]=d[2]; break; case 58: ReefAngel.Relay.RelayMaskOnE[5]=d[2]; break; case 59: ReefAngel.Relay.RelayMaskOffE[5]=d[2]; break; case 60: ReefAngel.Relay.RelayDataE[6]=d[2]; break; case 61: ReefAngel.Relay.RelayMaskOnE[6]=d[2]; break; case 62: ReefAngel.Relay.RelayMaskOffE[6]=d[2]; break; case 63: ReefAngel.Relay.RelayDataE[7]=d[2]; break; case 64: ReefAngel.Relay.RelayMaskOnE[7]=d[2]; break; case 65: ReefAngel.Relay.RelayMaskOffE[7]=d[2]; break; case 66: ReefAngel.Params.Salinity=((ReefAngel.Params.Salinity/256)<<8)+d[2]; break; case 67: ReefAngel.Params.Salinity=(d[2]<<8) + (ReefAngel.Params.Salinity%256); break; case 68: ReefAngel.Params.ORP=((ReefAngel.Params.ORP/256)<<8)+d[2]; break; case 69: ReefAngel.Params.ORP=(d[2]<<8) + (ReefAngel.Params.ORP%256); break; case 70: ReefAngel.Params.PHExp=((ReefAngel.Params.PHExp/256)<<8)+d[2]; break; case 71: ReefAngel.Params.PHExp=(d[2]<<8) + (ReefAngel.Params.PHExp%256); break; case 72: ReefAngel.Humidity.SetLevel(((ReefAngel.Humidity.GetLevel()/256)<<8)+d[2]); break; case 73: ReefAngel.Humidity.SetLevel((d[2]<<8) + (ReefAngel.Humidity.GetLevel()%256)); break; case 74: ReefAngel.WaterLevel.SetLevel(0,((ReefAngel.WaterLevel.GetLevel()/256)<<8)+d[2]); break; case 75: ReefAngel.WaterLevel.SetLevel(0,(d[2]<<8) + (ReefAngel.WaterLevel.GetLevel()%256)); break; case 76: ReefAngel.WaterLevel.SetLevel(1,((ReefAngel.WaterLevel.GetLevel(1)/256)<<8)+d[2]); break; case 77: ReefAngel.WaterLevel.SetLevel(1,(d[2]<<8) + (ReefAngel.WaterLevel.GetLevel(1)%256)); break; case 78: ReefAngel.WaterLevel.SetLevel(2,((ReefAngel.WaterLevel.GetLevel(2)/256)<<8)+d[2]); break; case 79: ReefAngel.WaterLevel.SetLevel(2,(d[2]<<8) + (ReefAngel.WaterLevel.GetLevel(2)%256)); break; case 80: ReefAngel.WaterLevel.SetLevel(3,((ReefAngel.WaterLevel.GetLevel(3)/256)<<8)+d[2]); break; case 81: ReefAngel.WaterLevel.SetLevel(3,(d[2]<<8) + (ReefAngel.WaterLevel.GetLevel(3)%256)); break; case 82: ReefAngel.WaterLevel.SetLevel(4,((ReefAngel.WaterLevel.GetLevel(4)/256)<<8)+d[2]); break; case 83: ReefAngel.WaterLevel.SetLevel(4,(d[2]<<8) + (ReefAngel.WaterLevel.GetLevel(4)%256)); break; case 84: ReefAngel.EM=d[2]; break; case 85: ReefAngel.EM1=d[2]; break; case 86: ReefAngel.REM=d[2]; break; case 87: ReefAngel.CustomVar[0]=d[2]; break; case 88: ReefAngel.CustomVar[1]=d[2]; break; case 89: ReefAngel.CustomVar[2]=d[2]; break; case 90: ReefAngel.CustomVar[3]=d[2]; break; case 91: ReefAngel.CustomVar[4]=d[2]; break; case 92: ReefAngel.CustomVar[5]=d[2]; break; case 93: ReefAngel.CustomVar[6]=d[2]; break; case 94: ReefAngel.CustomVar[7]=d[2]; break; // Don't go over 99. The max array is set to 100 } } } else if (howMany==3) { for(int a=0;a<3;a++) d[a]=Wire.read(); switch (d[0]) { case 0: if (abs(ReefAngel.Timer[FEEDING_TIMER].Trigger-(now()+d[1]+(d[2]<<8)))>2) ReefAngel.Timer[FEEDING_TIMER].Trigger=now()+d[1]+(d[2]<<8); break; } } else { for (int a=0;a1000) { lastmasterupdate=millis(); byte atostatus=0; if (ReefAngel.LowATO.IsActive()) bitSet(atostatus,0); else bitClear(atostatus,0); if (ReefAngel.HighATO.IsActive()) bitSet(atostatus,1); else bitClear(atostatus,1); #ifdef RA_STAR if (ReefAngel.AlarmInput.IsActive()) bitSet(atostatus,2); else bitClear(atostatus,2); #endif // RA_STAR #if defined RA_STAR || defined LEAKDETECTOREXPANSION if (ReefAngel.IsLeakDetected()) bitSet(atostatus,3); else bitClear(atostatus,3); #endif // defined RA_STAR || defined LEAKDETECTOREXPANSION MasterWrite(DisplayedMenu,0); MasterWrite(Board,1); MasterWrite(AlertFlags,2); MasterWrite(StatusFlags,3); MasterWrite(Params.Temp[T1_PROBE],4); MasterWrite(Params.Temp[T2_PROBE],6); MasterWrite(Params.Temp[T3_PROBE],8); MasterWrite(Params.PH,10); MasterWrite(atostatus,12); MasterWrite(PWM.GetDaylightValue(),13); MasterWrite(PWM.GetActinicValue(),14); MasterWrite(PWM.GetDaylight2Value(),15); MasterWrite(PWM.GetActinic2Value(),16); MasterWrite(Relay.RelayData,17); MasterWrite(Relay.RelayMaskOn,18); MasterWrite(Relay.RelayMaskOff,19); for (int a=0;a255) { if (value!=(olddata[index]<<8)+olddata[index+1]) { olddata[index+1]=value>>8; crc=0; data[0]='$'; data[1]=index+1; data[2]=olddata[index+1]; for (int a=0;a<3;a++) crc+=data[a]; Wire.beginTransmission(I2CRA_TouchDisplay); Wire.write(data,3); Wire.write(crc); Wire.endTransmission(); delay(10); wdt_reset(); } } } void receiveEventMaster(int howMany) { byte d[9]; wdt_reset(); if (howMany==3) { for(int a=0;a<3;a++) d[a]=Wire.read(); switch (d[0]) { case MESSAGE_BUTTON: // Simulate button press { if (d[1]==1 && d[2]==1) ButtonPress++; break; } case MESSAGE_RELAY_OVERRIDE: // Override relay ports { ReefAngel.Relay.Override(d[1],d[2]); break; } case MESSAGE_CHANNEL_OVERRIDE: // Override Channels { if (d[1]<=OVERRIDE_CHANNEL5) ReefAngel.PWM.Override(d[1],d[2]); if (d[1]>=OVERRIDE_AI_WHITE && d[1]<=OVERRIDE_AI_ROYALBLUE) ReefAngel.AI.Override(d[1]-OVERRIDE_AI_WHITE,d[2]); if (d[1]>=OVERRIDE_RF_WHITE && d[1]<=OVERRIDE_RF_INTENSITY) ReefAngel.RF.Override(d[1]-OVERRIDE_RF_WHITE,d[2]); if (d[1]>=OVERRIDE_DAYLIGHT2 && d[1]<=OVERRIDE_ACTINIC2) ReefAngel.PWM.Override(d[1],d[2]); break; } case MESSAGE_MENU: // Change menu screen { if (d[1]==d[2]) { switch (d[1]) { case FEEDING_MODE: case WATERCHANGE_MODE: ReefAngel.ChangeMode=d[1]; break; case TOUCH_MENU: case DATE_TIME_MENU: case PH_CALIBRATE_MENU: case SAL_CALIBRATE_MENU: case ORP_CALIBRATE_MENU: case PHE_CALIBRATE_MENU: case WL_CALIBRATE_MENU: case DEFAULT_MENU: ReefAngel.DisplayedMenu=d[1]; break; } } break; } case MESSAGE_COMMAND: // I2C Commands { ReefAngel.I2CCommand=d[1]; break; } case MESSAGE_RESEND_ALL: // Resend all data { for (int a=0;a8) { sprintf(buffer,"MR%02d:%02x%02x%02x%02x%02x%02x%02x%02x",mindex/8,InternalMemory.read(VarsStart+mindex+0),InternalMemory.read(VarsStart+mindex+1),InternalMemory.read(VarsStart+mindex+2),InternalMemory.read(VarsStart+mindex+3),InternalMemory.read(VarsStart+mindex+4),InternalMemory.read(VarsStart+mindex+5),InternalMemory.read(VarsStart+mindex+6),InternalMemory.read(VarsStart+mindex+7)); ReefAngel.Network.CloudPublish(buffer); Serial.println(buffer); mindex+=8; } } } } #endif // RA_STAR void ReefAngelClass::CheckOverride(int option) { if (option<10) return; byte o_relay=option/10; byte o_type=option%10; if (o_type==0) // Turn port off { if ( o_relay < 9 ) { bitClear(ReefAngel.Relay.RelayMaskOn,o_relay-1); bitClear(ReefAngel.Relay.RelayMaskOff,o_relay-1); } #ifdef RelayExp if ( (o_relay > 10) && (o_relay < 89) ) { byte EID = byte(o_relay/10); bitClear(ReefAngel.Relay.RelayMaskOnE[EID-1],(o_relay%10)-1); bitClear(ReefAngel.Relay.RelayMaskOffE[EID-1],(o_relay%10)-1); } #endif // RelayExp } else if (o_type==1) // Turn port on { if ( o_relay < 9 ) { bitSet(ReefAngel.Relay.RelayMaskOn,o_relay-1); bitSet(ReefAngel.Relay.RelayMaskOff,o_relay-1); } #ifdef RelayExp if ( (o_relay > 10) && (o_relay < 89) ) { byte EID = byte(o_relay/10); bitSet(ReefAngel.Relay.RelayMaskOnE[EID-1],(o_relay%10)-1); bitSet(ReefAngel.Relay.RelayMaskOffE[EID-1],(o_relay%10)-1); } #endif // RelayExp } else if (o_type==2) // Set port back to Auto { if ( o_relay < 9 ) { bitClear(ReefAngel.Relay.RelayMaskOn,o_relay-1); bitSet(ReefAngel.Relay.RelayMaskOff,o_relay-1); } #ifdef RelayExp if ( (o_relay > 10) && (o_relay < 89) ) { byte EID = byte(o_relay/10); bitClear(ReefAngel.Relay.RelayMaskOnE[EID-1],(o_relay%10)-1); bitSet(ReefAngel.Relay.RelayMaskOffE[EID-1],(o_relay%10)-1); } #endif // RelayExp } #ifdef OVERRIDE_PORTS // Reset relay masks for ports we want always in their programmed states. ReefAngel.Relay.RelayMaskOn &= ~ReefAngel.OverridePorts; ReefAngel.Relay.RelayMaskOff |= ReefAngel.OverridePorts; #ifdef RelayExp byte i; for ( i = 0; i < MAX_RELAY_EXPANSION_MODULES; i++ ) { ReefAngel.Relay.RelayMaskOnE[i] &= ~ReefAngel.OverridePortsE[i]; ReefAngel.Relay.RelayMaskOffE[i] |= ReefAngel.OverridePortsE[i]; } #endif // RelayExp #endif // OVERRIDE_PORTS ReefAngel.Relay.Write(); // Force update of the Portal after relay change } void ReefAngelClass::DimmingOverride(int weboption, int weboption2 ) { // Override channel // weboption2 is channel // weboption is override value // if channel is from an expansion module that is not enabled, the command will be accepted, but it will do nothing. #ifdef DisplayLEDPWM #if defined(__SAM3X8E__) if (weboption2==0) ReefAngel.VariableControl.SetDaylightOverride(weboption); else if (weboption2==1) ReefAngel.VariableControl.SetActinicOverride(weboption); #else if (weboption2==0) ReefAngel.PWM.SetDaylightOverride(weboption); else if (weboption2==1) ReefAngel.PWM.SetActinicOverride(weboption); #endif #ifdef PWMEXPANSION #if defined(__SAM3X8E__) if (weboption2>=OVERRIDE_CHANNEL0 && weboption2<=OVERRIDE_CHANNEL5) ReefAngel.VariableControl.SetChannelOverride(weboption2-OVERRIDE_CHANNEL0,weboption); #else if (weboption2>=OVERRIDE_CHANNEL0 && weboption2<=OVERRIDE_CHANNEL5) ReefAngel.PWM.SetChannelOverride(weboption2-OVERRIDE_CHANNEL0,weboption); #endif #endif // PWMEXPANSION #ifdef AI_LED if (weboption2>=OVERRIDE_AI_WHITE && weboption2<=OVERRIDE_AI_ROYALBLUE) ReefAngel.AI.SetChannelOverride(weboption2-OVERRIDE_AI_WHITE,weboption); #endif // AI_LED #ifdef RFEXPANSION if (weboption2>=OVERRIDE_RF_WHITE && weboption2<=OVERRIDE_RF_INTENSITY) ReefAngel.RF.SetChannelOverride(weboption2-OVERRIDE_RF_WHITE,weboption); #endif // RFEXPANSION #if defined RA_STAR || defined RA_EVOLUTION #if defined(__SAM3X8E__) if (weboption2==OVERRIDE_DAYLIGHT2) ReefAngel.VariableControl.SetDaylight2Override(weboption); else if (weboption2==OVERRIDE_ACTINIC2) ReefAngel.VariableControl.SetActinic2Override(weboption); #else if (weboption2==OVERRIDE_DAYLIGHT2) ReefAngel.PWM.SetDaylight2Override(weboption); else if (weboption2==OVERRIDE_ACTINIC2) ReefAngel.PWM.SetActinic2Override(weboption); #endif #endif // RA_STAR #ifdef SIXTEENCHPWMEXPANSION #if defined(__SAM3X8E__) if (weboption2>=OVERRIDE_16CH_CHANNEL0 && weboption2<=OVERRIDE_16CH_CHANNEL15) ReefAngel.VariableControl.Set16ChannelOverride(weboption2-OVERRIDE_16CH_CHANNEL0,weboption); #else if (weboption2>=OVERRIDE_16CH_CHANNEL0 && weboption2<=OVERRIDE_16CH_CHANNEL15) ReefAngel.PWM.Set16ChannelOverride(weboption2-OVERRIDE_16CH_CHANNEL0,weboption); #endif #endif // SIXTEENCHPWMEXPANSION #endif // DisplayLEDPWM } ReefAngelClass ReefAngel = ReefAngelClass() ;