As it is now, if I try to manually turn fluorescents on outside of their normal 2pm-6pm time frame, they flash on, then turn right back off, so I think the auto is overriding my manual on attempts as well.
Also while you are looking, my storms always happen at 3:30pm, even though I have the random time portion in my code.
Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>
#include <SunLocation.h>
#include <Tide.h>
#include <Moon.h>
#include <WiFiAlert.h>
////// Place global variable code below here
SunLocation sl;
int avgph[10];
unsigned long totalavgph=0;
byte avgindex=0;
void DrawCustomMain()
{
byte x;
byte y = 2;
char text[7];
// *********** CHANGE TEMP READOUT COLOR DEPENDENT ON FAN AND HEATER STATUS ***********
int TempColor; // Color for drawing temperature
boolean HeatOn = ReefAngel.Relay.Status(Port2); // Get the status of the heater relay
if (HeatOn)
{
TempColor = COLOR_NAVY; // Blue text, too cold, heater is on
}
if (!HeatOn)
{
TempColor = COLOR_GREEN; // Green text, no fan or heater on
}
// ***********************************************************************************
int pHColor; // Color for drawing pH
boolean LowpH = (ReefAngel.Params.PH < 780) ; // Check for Low pH Value
boolean HighpH = (ReefAngel.Params.PH > 850); // Check for High pH Value
if (LowpH)
{
pHColor = COLOR_NAVY; // Blue text, Low pH value
}
if (HighpH)
{
pHColor = COLOR_RED; // Red text, High pH value
}
if (!LowpH && !HighpH)
{
pHColor = COLOR_GREEN; // Green text, pH acceptable
}
// ***********************************************************************************
ReefAngel.LCD.DrawLargeText(COLOR_DARKSLATEBLUE,DefaultBGColor, 6, 3, " Thunder Reef",Font8x8); // Put a banner at the top
ReefAngel.LCD.DrawDate(6, 119); // Put the date and time at the bottom
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 12, 132, 12); // Draw a black line under the banner
x = 6;
y += MENU_START_ROW*1.4; // MENU_START_ROW is 10, according to globals.h, so y=2+10+1=13
ReefAngel.LCD.DrawLargeText(COLOR_BLUE, COLOR_WHITE, x, y+1, " Temp pH");
ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10); // Get T1 temp and convert
x = 2;
y += MENU_START_ROW*1.6;
ReefAngel.LCD.DrawHugeNumbers(COLOR_BLACK, TempColor, x, y, text); // Draw the temperature, white numbers on a colored background
ConvertNumToString(text, ReefAngel.Params.PH, 100); // Get pH reading and convert
x = 2;
y = MENU_START_ROW*2.6;
ReefAngel.LCD.DrawHugeNumbers(COLOR_YELLOW, pHColor, x+65, y+6, text); // Put pH on the screen
x += 6;
y += MENU_START_ROW*3.3;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"Sump Level",Font8x8); // Draw the Sump Float switch status
if (ReefAngel.LowATO.IsActive())
{ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
}
else
{
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
x += 6;
y += MENU_START_ROW*2.0;
ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,25,y,"Skimmer Cup",Font8x8); // Draw the Skimmer Cup Float switch status
if (ReefAngel.HighATO.IsActive()) {ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_GREEN);
}
else
{
ReefAngel.LCD.FillCircle(15,y+3,7,COLOR_RED);
}
byte TempRelay = ReefAngel.Relay.RelayData; // Code for drawing the relay box
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 100, TempRelay);
}
void DrawCustomGraph()
{
}
int ActinicPWMValue0=1; // For cloud code, channel 0, left blue
int ActinicPWMValue2=1; // For cloud code, chennel 2, center blue
int ActinicPWMValue4=1; // For cloud code, chennel 4, right blue
int DaylightPWMValue1=1; // For cloud code, channel 1, left white
int DaylightPWMValue3=1; // For cloud code, chennel 3, center white
int DaylightPWMValue5=1; // For cloud code, chennel 5, right white
////// 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.Timer[FEEDING_TIMER].SetInterval(1200); // Feeding timer at 20 minutes (in seconds)
ReefAngel.FeedingModePorts = Port1Bit | Port6Bit | Port7Bit | Port8Bit; // Ports toggled in Feeding Mode
ReefAngel.WaterChangePorts = Port1Bit | Port6Bit | Port7Bit | Port8Bit; // Ports toggled in Water Change Mode
ReefAngel.LightsOnPorts = Port2Bit | Port3Bit | Port4Bit | Port5Bit; // Ports toggled when Lights On / Off menu entry selected
ReefAngel.OverheatShutoffPorts = Port2Bit | Port3Bit | Port4Bit; // Ports turned off when Overheat temperature exceeded
ReefAngel.TempProbe = T1_PROBE; // Use T1 probe as temperature and overheat functions
ReefAngel.OverheatProbe = T1_PROBE;
InternalMemory.OverheatTemp_write( 820 ); // Set the Overheat temperature setting
CustomOverheatClear(T1_PROBE);
// Ports that are always on
ReefAngel.Relay.On( Port1 ); // Return
// Ports that are always off
ReefAngel.Relay.Off( Port7 );
// Delayed start for skimmer to allow sump level to return to normal after water change
ReefAngel.Relay.DelayedOn( Port5,5 );
////// Place additional initialization code below here
sl.Init(28.5700, -81.6800); // Lat/long for Clermont, FL
sl.SetOffset(-2,0,-2,0); // rise_hour, rise_seconds, set_hour, set_seconds (set 2 hrs later for better viewing time (-4 offset = actual time))
randomSeed(now()%SECS_PER_DAY);
//Custom Variable [0] = Month/Season
ReefAngel.CustomLabels[0]="Return";
ReefAngel.CustomLabels[1]="Heaters";
ReefAngel.CustomLabels[2]="Cooling Fan";
ReefAngel.CustomLabels[3]="ATO/Swabbie";
ReefAngel.CustomLabels[4]="Skimmer";
ReefAngel.CustomLabels[5]="Kalk Stirrer";
ReefAngel.CustomLabels[6]="Storm Mode";
ReefAngel.CustomLabels[7]="Fuge Light";
ReefAngel.CustomLabels[8]="Left Whites";
ReefAngel.CustomLabels[9]="Left Blues";
ReefAngel.CustomLabels[10]="Center Whites";
ReefAngel.CustomLabels[11]="Center Blues";
ReefAngel.CustomLabels[12]="Right Whites";
ReefAngel.CustomLabels[13]="Right Blues";
ReefAngel.CustomLabels[14]="ATI Coral+";
ReefAngel.CustomLabels[15]="Moonlights";
////// Place additional initialization code above here
}
void loop()
{
// seasonal temperatures
SeasonalTemps();
// Default lights program
ActinicPWMValue0=PWMSlopeHighRes(10,00,21,30,1,72,180,41); // Default for blues
ActinicPWMValue2=PWMSlopeHighRes(10,15,21,45,1,72,180,41);
ActinicPWMValue4=PWMSlopeHighRes(10,30,22,00,1,72,180,41);
DaylightPWMValue1=PWMSlopeHighRes(12,45,18,45,1,22,105,41); // Default for whites
DaylightPWMValue3=PWMSlopeHighRes(13,00,19,00,1,22,105,41);
DaylightPWMValue5=PWMSlopeHighRes(13,15,19,15,1,22,105,41);
CheckCloud(); // Check for cloud and slow lightning.
ReefAngel.PWM.SetChannelRaw(0,ActinicPWMValue0); // Write values for either default or cloud/slow
ReefAngel.PWM.SetChannelRaw(2,ActinicPWMValue2); // lightning if it's time.
ReefAngel.PWM.SetChannelRaw(4,ActinicPWMValue4); // lightning if it's time.
ReefAngel.PWM.SetChannelRaw(1,DaylightPWMValue1); // Write values for either default or cloud/slow
ReefAngel.PWM.SetChannelRaw(3,DaylightPWMValue3); // lightning if it's time.
ReefAngel.PWM.SetChannelRaw(5,DaylightPWMValue5); // lightning if it's time.
// Turn Actinic outlets on if any % is >= 2
(ReefAngel.PWM.GetChannelValue(0)>=2) ? ReefAngel.Relay.On( Box1_Port2 ) : ReefAngel.Relay.Off( Box1_Port2 );
(ReefAngel.PWM.GetChannelValue(2)>=2) ? ReefAngel.Relay.On( Box1_Port4 ) : ReefAngel.Relay.Off( Box1_Port4 );
(ReefAngel.PWM.GetChannelValue(4)>=2) ? ReefAngel.Relay.On( Box1_Port6 ) : ReefAngel.Relay.Off( Box1_Port6 );
//Turn Daylight outlet on if any % is >=2
(ReefAngel.PWM.GetChannelValue(1)>=2) ? ReefAngel.Relay.On( Box1_Port1 ) : ReefAngel.Relay.Off( Box1_Port1 );
(ReefAngel.PWM.GetChannelValue(3)>=2) ? ReefAngel.Relay.On( Box1_Port3 ) : ReefAngel.Relay.Off( Box1_Port3 );
(ReefAngel.PWM.GetChannelValue(5)>=2) ? ReefAngel.Relay.On( Box1_Port5 ) : ReefAngel.Relay.Off( Box1_Port5 );
// ATI Coral+ Flourescents
if (hour()>=14 && hour()<18)
{
ReefAngel.Relay.On( Box1_Port7 );
}
else
{
ReefAngel.Relay.Off( Box1_Port7 );
}
// Refugium Light: sPar38-Fuge
if (hour()<18 || hour() >=20)
{
ReefAngel.Relay.On( Port8 );
}
else
{
ReefAngel.Relay.Off( Port8 );
}
// Moonlight Strip
if (hour()>=8 && hour()<23)
{
ReefAngel.Relay.On( Box1_Port8 );
}
else
{
ReefAngel.Relay.Off( Box1_Port8 );
}
//Enclosure cooling fan control with T2 probe
if (ReefAngel.Params.Temp[T2_PROBE]>=820) ReefAngel.Relay.On( Port3 );
if (ReefAngel.Params.Temp[T2_PROBE]<800) ReefAngel.Relay.Off( Port3 );
////// Place your custom code below here
// pp-15 powerheads schedule
if (hour()>=8 && hour()<10)
{
ReefAngel.PWM.SetDaylight( ReefCrestMode(40,5,true) ); // reefcrest at 40% +/- 5% on sync mode (Morning Mode)
ReefAngel.PWM.SetActinic( ReefCrestMode(40,5,false) ); // reefcrest at 40% +/- 5% on Anti-sync mode (Morning mode)
}
else if (hour()>=10 && hour()<13)
{
//byte random_speed=random(35,50);
//byte random_duration=random(3,7);
ReefAngel.PWM.SetDaylight( GyreMode(25,70,15,true) ); // Ramping 25%-70% for 15 min on sync mode (Gyre)
ReefAngel.PWM.SetActinic( GyreMode(25,70,15,false) ); // Ramping 25%-70% for 15 min on anti-sync mode (Gyre)
}
else if (hour()>=13 && hour()<15)
{
byte random_speed=random(40,60);
byte random_duration=random(3,5);
ReefAngel.PWM.SetDaylight( LongPulseMode(25,random_speed,random_duration,true) ); // Long Pulse 25% ramping up to 40%-60% for 3-5 seconds on sync mode (Medium swells)
ReefAngel.PWM.SetActinic( LongPulseMode(25,random_speed,random_duration,false) ); // Long Pulse 25% ramping up to 40%-60% for 3-5 seconds on Anti-sync mode (Medium swells)
}
else if (hour()>=15 && hour()<17)
{
byte random_speed=random(40,50);
ReefAngel.PWM.SetDaylight( ShortPulseMode(1,random_speed,408,true) ); // Short pulse at 45%-60% with 408ms pulse on sync mode (surface wave 1/2"-3/4")
ReefAngel.PWM.SetActinic( ShortPulseMode(1,random_speed,408,false) ); // Short pulse at 45%-60%% with 408ms pulse on Anti-sync mode (surface wave 1/2"-3/4")
}
else if (hour()>=17 && hour()<19)
{
byte random_speed=random(40,50);
byte random_duration=random(2,5);
ReefAngel.PWM.SetDaylight( LongPulseMode(1,random_speed,random_duration,true) ); // Long Pulse 1% ramping up to 40%-50% for 2-5 seconds on sync mode (Feeding hour)
ReefAngel.PWM.SetActinic( LongPulseMode(1,random_speed,random_duration,false) ); // Long Pulse 1% ramping up to 40%-50% for 2-5 seconds on Anti-sync mode (Feeding hour)
}
else if (hour()>=19 && hour()<20)
{
byte random_min=random(35,45);
byte random_speed=random(50,65);
byte random_duration=random(2,6);
ReefAngel.PWM.SetDaylight( LongPulseMode(random_min,random_speed,random_duration,true) ); // Long Pulse 35%-45% ramping up to 50%-65% for 2-5 seconds on sync mode (nutrient transport)
ReefAngel.PWM.SetActinic( LongPulseMode(random_min,random_speed,random_duration,false) ); // Long Pulse 35%-45% ramping up to 50%-65% for 2-5 seconds on Anti-sync mode (nutrient transport)
}
else if (hour()>=20 && hour()<21)
{
ReefAngel.PWM.SetDaylight( ReefCrestMode(40,10,true) ); // reefcrest at 40% +/- 10% on sync mode (Evening Mode)
ReefAngel.PWM.SetActinic( ReefCrestMode(40,10,false) ); // reefcrest at 40% +/- 10% on Anti-sync mode (Evening mode)
}
else
{
ReefAngel.PWM.SetDaylight( ReefCrestMode(35,10,true) ); // reefcrest at 35% +/- 10% on sync mode (night Mode)
ReefAngel.PWM.SetActinic( ReefCrestMode(35,10,false) ); // reefcrest at 35% +/- 10% on Anti-sync mode (night mode)
}
if( ReefAngel.DisplayedMenu==FEEDING_MODE )
{
ReefAngel.PWM.SetActinic(20);
ReefAngel.PWM.SetDaylight(20);
}
if( ReefAngel.DisplayedMenu==WATERCHANGE_MODE )
{
ReefAngel.PWM.SetActinic(1);
ReefAngel.PWM.SetDaylight(1);
}
// enter feeding mode at 6:15pm
//if ( (hour()==18 && minute()==15 && second()==0))
// {
//ReefAngel.FeedingModeStart(); // turn on feeding mode
// }
if(ReefAngel.HighATO.IsActive()) // Float switch in Skimmer Locker
{
ReefAngel.Relay.DelayedOn( Port5,5 );
}
else
{
ReefAngel.Relay.Off(Port5); // Turn off Skimmer when locker full.
}
// ATO, Port 8 is ATO using Clear RoDi
ReefAngel.SingleATO(true,Port4,400,0); // Sump switch. If ATO/RoDi runs for 400 seconds, then shut off and send alert.
{
sl.CheckAndUpdate(); // handle updating sunrise and sunset values
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "DmnYnkee" );
ReefAngel.ShowInterface();
}
void SeasonalTemps ()
{
static int heatArray[][2] = { {786,790}, // default in case of error in month=0 (June)
{786,790},//January (winter) // 77.6
{786,790},//February (winter) // 77.8
{786,790},//March (early spring) // 78.0
{786,790},//April (spring) // 78.2
{786,790},//May (spring) // 78.4
{786,790},//June (early summer) // 78.8
{786,790},//July (summer) // 79.2
{786,790},//August (summer) // 79.6
{786,790},//September (early fall) // 79.2
{786,790},//October (fall) // 78.8
{786,790},//November (fall) // 78.4
{786,790} };//December (early winter) // 78.0
ReefAngel.StandardHeater( Port2,heatArray[month()][0],heatArray[month()][1]);
}//end seasonalTemps
// ------------------------------------ Auto overheat clear
void CustomOverheatClear(byte probe)
{
if((bitRead(ReefAngel.AlertFlags, OverheatFlag)) && (ReefAngel.Params.Temp[probe] <= InternalMemory.OverheatTemp_read()-30))
ReefAngel.OverheatClear();
}
// ------------------------------------------------------------ Weather section
// Do not change anything below here
static byte cloudchance=255;
static byte cloudduration=0;
static int cloudstart=0;
static byte numclouds=0;
static byte lightningchance=0;
static byte cloudindex=0;
static byte lightningstatus=0;
static byte lightningMode=0;
static boolean chooseLightning=true;
void CheckCloud()
{
// ------------------------------------------------------------
// Change the values below to customize your cloud/storm effect
// Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
// For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1
// Percentage chance of a cloud happening today
// For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 100
// Minimum number of minutes for cloud duration. Don't use min duration of less than 6
#define Min_Cloud_Duration 8
// Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 15
// Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 1
// Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 2
// Only start the cloud effect after this setting
// In this example, start cloud after 1:30pm
#define Start_Cloud_After NumMins(13,30)
// Always end the cloud effect before this setting
// In this example, end cloud before 6:30pm
#define End_Cloud_Before NumMins(18,30)
// Percentage chance of a lightning happen for every cloud
// For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 100
// Note: Make sure to choose correct values that will work within your PWMSLope settings.
// For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
// Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes
// of effects or unforseen result could happen.
// Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
// In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the
// 250 minutes (or 500 minutes) can fit in that 510 minutes window.
// It's a tight fit, but it did.
//#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process.
// Add Random Lightning modes
#define Calm 0 // No lightning
#define Slow 1 // 5 seconds of slow lightning in the middle of a cloud for ELN style (slow response) drivers
#define Fast 2 // 5 seconds of fast lightning in the middle of a cloud for LDD style (fast response) drivers
#define Mega 3 // Lightning throughout the cloud, higher chance as it gets darker
#define Mega2 4 // Like Mega, but with more lightning
// Set which modes you want to use
// Example: { Calm, Fast, Mega, Mega2 } to randomize all four modes.
// { Mega2 } for just Mega2. { Mega, Mega, Fast} for Mega and Fast, with twice the chance of Mega.
byte LightningModes[] = { Mega }; // <---- set Storm modes here
// Change the values above to customize your cloud/storm effect
static time_t DelayCounter=millis(); // Variable for lightning timing.
static int DelayTime=random(1000); // Variable for lightning timimg.
// Every day at midnight, we check for chance of cloud happening today
if (hour()==0 && minute()==0 && second()==0) cloudchance=255;
#ifdef forcecloudcalculation
if (cloudchance==255)
#else
if (hour()==0 && minute()==0 && second()==1 && cloudchance==255)
#endif
{
randomSeed(millis()); // Seed the random number generator
//Pick a random number between 0 and 99
cloudchance=random(100);
// if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
// Check if today is day for clouds.
if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0;
// If we have cloud today
if (cloudchance)
{
// pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
// pick the time that the first cloud will start
// the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day.
cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
// pick a random number for the cloud duration of first cloud.
cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
//Pick a random number between 0 and 99
lightningchance=random(100);
// if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
}
}
// Now that we have all the parameters for the cloud, let's create the effect
if (cloudchance)
{
if (ReefAngel.Relay.isMaskOn(Port7)) // Change this to whatever port you want to use as a trigger to start a storm.
{
cloudstart = NumMins(hour(), minute());
ReefAngel.Relay.Auto(Port7); // Here, too.
}
//is it time for cloud yet?
//if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
//{
//ReefAngel.Relay.Off(Box1_Port7); // Turn off Flourescents
//}
//else
//{
//ReefAngel.Relay.Auto(Box1_Port7); // Flourescents back to auto.
//}
if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
{
// Increase Blue channel first, for better effect and to compensate for drop in Whites
ActinicPWMValue0=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ActinicPWMValue0,ActinicPWMValue0+DaylightPWMValue1*1.1,180);
ActinicPWMValue2=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ActinicPWMValue2,ActinicPWMValue2+DaylightPWMValue3*1.1,180);
ActinicPWMValue4=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ActinicPWMValue4,ActinicPWMValue4+DaylightPWMValue5*1.1,180);
// Daylight dimming from cloud
DaylightPWMValue1=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue1/40.95,2,180)*40.95;
DaylightPWMValue3=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue3/40.95,2,180)*40.95;
DaylightPWMValue5=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue5/40.95,2,180)*40.95;
if (chooseLightning)
{
lightningMode=LightningModes[random(100)%sizeof(LightningModes)];
chooseLightning=false;
}
switch (lightningMode)
{
case Calm:
break;
case Mega:
// Lightning chance from beginning of cloud through the end. Chance increases with darkness of cloud.
if (lightningchance && random(ReversePWMSlope(cloudstart,cloudstart+cloudduration,100,0,180))<1 && (millis()-DelayCounter)>DelayTime)
{
// Send the trigger
int r=random(34);
if (r<20) {
Strike1(); // All 3 lights
} else if (r<22) {
Strike2(); // Left only
} else if (r<24) {
Strike3(); // Center only
} else if (r<28) {
Strike4(); // Right only
} else if (r<30) {
Strike5(); // Left & Center
} else if (r<34)
Strike6(); // Center & Right
DelayCounter=millis(); // If we just had a round of flashes, then lets put in a longer delay
DelayTime=random(1800); // of up to a second for dramatic effect before we do another round.
}
break;
case Mega2:
// Higher lightning chance from beginning of cloud through the end. Chance increases with darkness of cloud.
if (lightningchance && random(ReversePWMSlope(cloudstart,cloudstart+cloudduration,100,0,180))<2)
{
Strike();
}
break;
case Fast:
// 5 seconds of lightning in the middle of the cloud
if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5 && (millis()-DelayCounter)>DelayTime)
{
Strike();
DelayCounter=millis(); // If we just had a round of flashes, then lets put in a longer delay
DelayTime=random(1000); // of up to a second for dramatic effect before we do another round.
}
break;
case Slow:
// Slow lightning for 5 seconds in the middle of the cloud. Suitable for slower ELN style drivers
if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5)
{
SlowStrike();
}
break;
default:
break;
}
}
else
{
chooseLightning=true; // Reset the flag to choose a new lightning type
}
if (NumMins(hour(),minute())>(cloudstart+cloudduration))
{
cloudindex++;
if (cloudindex < numclouds)
{
cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
// pick a random number for the cloud duration of first cloud.
cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
//Pick a random number between 0 and 99
lightningchance=random(100);
// if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
}
}
}
// Cloud ON option - Clouds every minute
if (ReefAngel.Relay.isMaskOff(Port7) && now()%60<10)
{
SlowStrike();
}
}
void SlowStrike()
{
int r = random(80);
if (r<20) lightningstatus=1;
else lightningstatus=0;
if (lightningstatus)
{
// Let's separate left, center, right, or All.
if (r<10 ) { // All 3
DaylightPWMValue1=4095;
DaylightPWMValue3=4095;
DaylightPWMValue5=4095;
} else if (r<12) { // Left only
DaylightPWMValue1=4095;
DaylightPWMValue3=100;
DaylightPWMValue5=100;
} else if (r<14) { // Center only
DaylightPWMValue1=100;
DaylightPWMValue3=4095;
DaylightPWMValue5=100;
} else if (r<16) { // Right only
DaylightPWMValue1=100;
DaylightPWMValue3=100;
DaylightPWMValue5=4095;
} else if (r<18) { // Left & Center only
DaylightPWMValue1=4095;
DaylightPWMValue3=4095;
DaylightPWMValue5=100;
} else { // Center & Right only
DaylightPWMValue1=100;
DaylightPWMValue3=4095;
DaylightPWMValue5=4095;
}
}
else
{
DaylightPWMValue1=100;
DaylightPWMValue3=100;
DaylightPWMValue5=100;
}
delay(1);
}
void DrawClouds(int x, int y)
{
// Write the times of the next cloud, next lightning, and cloud duration to the screen and into some customvars for the Portal.
ReefAngel.LCD.DrawText(0,255,x,y,"C"); x+=6;
ReefAngel.LCD.DrawText(0,255,x,y,"00:00"); x+=34;
ReefAngel.LCD.DrawText(0,255,x,y,"L"); x+=6;
ReefAngel.LCD.DrawText(0,255,x,y,"00:00"); x=5;
if (cloudchance && (NumMins(hour(),minute())<cloudstart))
{
int x=0;
if ((cloudstart/60)>=10) x=11;
else x=17;
ReefAngel.LCD.DrawText(0,255,x,y,(cloudstart/60));
//ReefAngel.CustomVar[3]=cloudstart/60; // Write the hour of the next cloud to custom variable for Portal reporting
if ((cloudstart%60)>=10) x=29;
else x=35;
ReefAngel.LCD.DrawText(0,255,x,y,(cloudstart%60));
//ReefAngel.CustomVar[4]=cloudstart%60; // Write the minute of the next cloud to custom variable for Portal reporting
}
ReefAngel.LCD.DrawText(0,255,x+85,y,cloudduration);
ReefAngel.CustomVar[7]=(cloudduration); // Put the duration of the next cloud in a custom var for the portal
if (lightningchance)
{
int x=0;
if (((cloudstart+(cloudduration/3))/60)>=10) x=51;
else x=57;
ReefAngel.LCD.DrawText(0,255,x,y,((cloudstart+(cloudduration/3))/60));
ReefAngel.CustomVar[5]=(cloudstart+(cloudduration/2))/60; // Write the hour of the next lightning to a custom variable for the Portal
if (((cloudstart+(cloudduration/3))%60)>=10) x=69;
else x=75;
ReefAngel.LCD.DrawText(0,255,x,y,((cloudstart+(cloudduration/3))%60)); // Write the minute of the next lightning to a custom variable for the Portal
ReefAngel.CustomVar[6]=(cloudstart+(cloudduration/2))%60;
}
}
void Strike1() // All 3 lights
{
int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*1)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 1.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*3)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 3.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*5)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 5.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(1); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*1));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(3); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*3));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(5); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*5));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike2() // Left only
{
int a=random(1,4); // Pick a number of consecutive flashes from 1 to 3.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*1)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 1.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(1); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*1));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike3() // Center only
{
int a=random(1,4); // Pick a number of consecutive flashes from 1 to 3.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*3)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 3.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(3); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*3));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike4() // Right only
{
int a=random(1,4); // Pick a number of consecutive flashes from 1 to 3.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*5)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 5.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(5); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*5));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike5() // Left & Center
{
int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*1)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 1.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*3)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 3.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(1); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*1));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(3); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*3));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike6() // Center & Right
{
int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*3)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 3.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*5)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 5.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(3); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*3));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(5); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*5));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
void Strike()
{
int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.
for (int i=0; i<a; i++)
{
// Flash on
int newdata=4095;
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*1)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 1.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*3)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 3.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
Wire.beginTransmission(0x40); // Address of the dimming expansion module
Wire.write(0x8+(4*5)); // 0x8 is channel 0, 0x12 is channel 1, etc. This is channel 5.
Wire.write(newdata&0xff); // Send the data 8 bits at a time. This sends the LSB
Wire.write(newdata>>8); // This sends the MSB
Wire.endTransmission();
int randy=random(20,80); // Random number for a delay
if (randy>71) randy=((randy-70)/2)*100; // Small chance of a longer delay
delay(randy); // Wait from 20 to 69 ms, or 100-400 ms
// Flash off. Return to baseline.
newdata=ReefAngel.PWM.GetChannelValueRaw(1); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*1));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(3); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*3));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
newdata=ReefAngel.PWM.GetChannelValueRaw(5); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*5));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
delay(random(30,50)); // Wait from 30 to 49 ms
wdt_reset(); // Reset watchdog timer to avoid re-boots
}
}
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
long n=elapsedSecsToday(now());
cstart*=60;
cend*=60;
if (n<cstart) return PWMStart;
if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
if (n>cend) return (int) PWMStart;
}
int ReversePWMSlope(long cstart,long cend,int PWMStart,int PWMEnd, byte clength)
{
long n=elapsedSecsToday(now());
cstart*=60;
cend*=60;
if (n<cstart) return PWMStart;
if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
if (n>cend) return (int) PWMStart;
}