So I don't have much time to go into details here. But after a bit of trouble shooting on the above code. I got things working better over last weekend.
The Refugium and Phyto lights turn on/off correctly. And the Moonlights are running on their correct time schedules with the new nested code.
Here's the latest version that I have loaded up to the head unit and running for the last few days.
Code: Select all
//*************************************************************************************************************
//Start of PWM Expansion Code Header
//This is just how we are going to reference the PWM expansion ports within the code.
//You can change the labels if you would like, just as long as they are changed all throughout the code too.
//Standard PowerRelay PWM Daylight = L-DT White/Color
//Standard PowerRelay PWM Actinic = L-DT Blue/UV
#define LEDPWM0 0 // R-DT = White/Color
#define LEDPWM1 1 // R-DT = Blue/UV
#define LEDPWM2 2 // L-DT = MoonLight
#define LEDPWM3 3 // R-DT = Moonlight
#define LEDPWM4 4 // - EMPTY -
#define LEDPWM5 5 // - EMPTY -
//Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={0,0,0,0,0,0};
//End of PWM Expansion Code Header
//*************************************************************************************************************
#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 <ReefAngel.h>
// - Creates a variable to control a "force cloud function" inside a menu.
boolean ForceCloud=false;
//added for custom menu set up
#include <avr/pgmspace.h>
// Create the menu entries
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "Overheat Clear";
prog_char menu5_label[] PROGMEM = "PH Calibration";
prog_char menu6_label[] PROGMEM = "Date / Time";
prog_char menu7_label[] PROGMEM = "Version";
prog_char menu8_label[] PROGMEM = "Storm Effect";
// 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};
// Creating the custom menu fucntions. menu2_label corresponds to MenuEntry2 funtion
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry4()
{
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry5()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry6()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.DisplayVersion();
}
// Trying to add a menu to the RA Head Unit so I can trigger a cloud effect on demand.
void MenuEntry8()
{
ForceCloud=true;
ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
////// Place global variable code below here //////
byte LDT_DaylightPWMValue=0;
byte LDT_ActinicPWMValue=0;
byte LDT_MoonLightANALOGValue=0;
byte RDT_DaylightPWMValue=0;
byte RDT_ActinicPWMValue=0;
byte RDT_MoonLightANALOGValue=0;
////// Place global variable code above here //////
void setup()
{
//This must be the first line.
ReefAngel.Init(); //Initialize controller
//This must be the first line.
// Initialize the custom menu
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
ReefAngel.AddStandardMenu(); // Add Standard Menu
ReefAngel.Use2014Screen(); // Let's use 2014 Screen
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = 0;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port5Bit | Port6Bit | Port7Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
//Ports that are always on
ReefAngel.Relay.On( Port3 );
ReefAngel.Relay.On( Port4 );
ReefAngel.Relay.On( Port5 );
ReefAngel.Relay.On( Port6 );
ReefAngel.Relay.On( Port7 );
ReefAngel.Relay.On( Port8 );
// Port1 = Heater
// Port2 = Heater
// Port3 = Refugium Light
// Port4 = Phyto Light
// Port5 = Return Pump
// Port6 = Protein Skimmer
// Port7 = UV Sterilizer
// Port8 = Refugium Pump
////// Place additional initialization code below here //////
// ***PLEASE HELP HERE********************************************************************************************************************************************
//Sets values of ALL lighting channels to 0% on Start Up correctly, I think.
ReefAngel.PWM.SetDaylight(0);
ReefAngel.PWM.SetActinic(0);
ReefAngel.PWM.SetChannel(LEDPWM0,0);
ReefAngel.PWM.SetChannel(LEDPWM1,0);
ReefAngel.PWM.SetChannel(LEDPWM2,0);
ReefAngel.PWM.SetChannel(LEDPWM3,0);
ReefAngel.PWM.SetChannel(LEDPWM4,0);
ReefAngel.PWM.SetChannel(LEDPWM5,0);
// I believe using this got rid of my bright flash to 100% when I uploaded new code. Specifically setting the Standard Daylight/Actinic
//****************************************************************************************************************************************************************
// Adds the date and time menu to controller so I can change for Daylight Savings Time. **Takes up memory space**
// - Can be removed after change or adding wifi. Time can be changed in portal or through phone app. eventually
//ReefAngel.AddDateTimeMenu();
////// Place additional initialization code above here //////
}
void loop()
{
ReefAngel.StandardHeater(Port1);
ReefAngel.StandardHeater(Port2);
////// Place your custom code below here //////
//-----------------------------------------------------------------------------------------------------------------------------------------------------//
//Set up for Refugium and Phytoplankton light cycle
//USE THIS TO TOGGLE PORTS ON/OFF - ReefAngel.StandardLight(relay,onhour,onminute,offhour,offminute)
//Turns Refugium Light plugged into port3 on at 9:30 pm and off at 9:30 am.
ReefAngel.StandardLights(Port3,21,30,9,30);
//Turns Phyto Light plugged into port4 on at 9:30 pm and off at 9:30 am.
ReefAngel.StandardLights(Port4,21,30,9,30);
//if ((NumMins(hour(now()),minute(now())) > NumMins(21,30)) && (NumMins(hour(now()),minute(now())) < NumMins(9,30)))
// {
// ReefAngel.Relay.On( Port3 );
// ReefAngel.Relay.On( Port4 );
// }
// else
// {
// ReefAngel.Relay.Off( Port3 );
// ReefAngel.Relay.Off( Port4 );
// }
//-----------------------------------------------------------------------------------------------------------------------------------------------------//
//*********************************************************************************************************************************************************
//// Calculate your regular sunrise/sunset PWM values ////
//*********************************************************************************************************************************************************
//_____________________________________________________________________________________________________________
// - Turns RDT - B/UV channel to 0% power between 8:30 p.m. and 11:00 a.m. to conserve energy
// - Dimming Expansion - LEDPWM1 - RDT Blue/UV -
// - Parabola dimming 11%-85%-11%
//_____________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(11,00)) && (NumMins(hour(now()),minute(now())) < NumMins(20,30)))
{
RDT_ActinicPWMValue=PWMParabola(11,00,20,20,11,85,RDT_ActinicPWMValue);
}
else
{
RDT_ActinicPWMValue=0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//_____________________________________________________________________________________________________________
// - Turns LDT - B/UV channel to 0% power between 9:00 p.m. and 11:15 p.m. to conserve energy
// - RA Standard Power Unit - LDT Blue/UV -
// - Parabola dimming 11%-85%-11%
//_____________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(11,15)) && (NumMins(hour(now()),minute(now())) < NumMins(21,00)))
{
LDT_ActinicPWMValue=PWMParabola(11,15,20,50,11,85,LDT_ActinicPWMValue);
}
else
{
LDT_ActinicPWMValue=0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//_____________________________________________________________________________________________________________
// - Turns RDT - W/C channel to 0% power between 8:30 p.m. and 11:25 a.m. to conserve energy
// - Dimming Expansion - RDT White/Color -
// - Parabola dimming 11%-60%-11%
//_____________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(11,25)) && (NumMins(hour(now()),minute(now())) < NumMins(20,30)))
{
RDT_DaylightPWMValue=PWMParabola(11,25,20,40,11,60,RDT_DaylightPWMValue);
}
else
{
RDT_DaylightPWMValue=0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//_____________________________________________________________________________________________________________
// - Turns LDT - W/C channel to 0% power between 9:00 p.m. and 11:40 a.m. to conserve energy
// - RA Power Unit - LDT White/Color -
// - Parabola dimming 11%-60%-11%
//_____________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(11,40)) && (NumMins(hour(now()),minute(now())) < NumMins(21,00)))
{
LDT_DaylightPWMValue=PWMParabola(11,40,21,10,11,60,LDT_DaylightPWMValue);
}
else
{
LDT_DaylightPWMValue=0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//// Set MoonLights Cycles ////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LDT_MoonLightANALOGValue=PWMParabola(11,40,21,00,10,30,PWMSlope(21,20,23,15,10,MoonPhase()/5+10,30,LDT_MoonLightANALOGValue=0));
RDT_MoonLightANALOGValue=PWMParabola(11,25,20,30,10,30,PWMSlope(21,15,23,15,10,MoonPhase()/5+10,30,RDT_MoonLightANALOGValue=0));
//_________________________________________________________________________________________________________________________________________________
// - Turns RDT Moonlights to 0% power between 11:15 p.m. and 9:15 p.m. to conserve energy.
// - Slope dimming 10% up to 1/5 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 30 minutes.
// - Dimming Expansion - LEDPWM3 - RDT Moonlight - on @ 9:15 p.m. - off @ 11:15 p.m.
//_________________________________________________________________________________________________________________________________________________
//if ((NumMins(hour(now()),minute(now())) > NumMins(23,15)) && (NumMins(hour(now()),minute(now())) < NumMins(11,45)))
// {
// RDT_MoonLightANALOGValue=0;
// //ReefAngel.PWM.SetChannel(LEDPWM3,0);
// }
// else if ((NumMins(hour(now()),minute(now())) > NumMins(11,40)) && (NumMins(hour(now()),minute(now())) < NumMins(20,30)))
// {
// RDT_MoonLightANALOGValue=PWMParabola(11,40,20,30,10,30,RDT_MoonLightValue));
//ReefAngel.PWM.SetChannel(LEDPWM3,PWMParabola(11,40,20,30,10,30,LEDPWM3));
// }
// else
// {
// RDT_MoonLightANALOGValue=PWMSlope(21,15,23,15,10,MoonPhase()/5+10,30,RDT_MoonLightValue));
//ReefAngel.PWM.SetChannel(LEDPWM3,PWMSlope(21,15,23,15,10,MoonPhase()/5+10,30,LEDPWM3));
// }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//_________________________________________________________________________________________________________________________________________________
// - Turns LDT Moonlights to 0% power between 11:30 p.m. and 9:30 p.m. to conserve energy
// - Slope dimming - 10% up to 1/5 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 30 minutes
// - Dimming Expansion - LEDPWM2 - LDT Moonlight - on @ 9:30 p.m. - off @ 11:30 a.m.
//_________________________________________________________________________________________________________________________________________________
//if ((NumMins(hour(now()),minute(now())) > NumMins(23,30)) && (NumMins(hour(now()),minute(now())) < NumMins(21,35)))
// {
// LDT_MoonLightANALOGValue=0;
//ReefAngel.PWM.SetChannel(LEDPWM2,0);
// }
// else if ((NumMins(hour(now()),minute(now())) > NumMins(11,55)) && (NumMins(hour(now()),minute(now())) < NumMins(21,00)))
// {
// LDT_MoonLightANALOGValue=PWMParabola(11,40,20,30,10,30,LDT_MoonLightValue));
//ReefAngel.PWM.SetChannel(LEDPWM2,PWMParabola(11,55,21,00,10,30,LEDPWM2));
// }
// else
// {
// LDT_MoonLightANALOGValue=PWMSlope(21,15,23,15,10,MoonPhase()/5+10,30,LDT_MoonLightValue));
//ReefAngel.PWM.SetChannel(LEDPWM2,PWMSlope(21,30,23,30,10,MoonPhase()/5+10,30,LEDPWM2));
// }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
CheckCloud();
ReefAngel.PWM.SetDaylight(LDT_DaylightPWMValue);
ReefAngel.PWM.SetActinic(LDT_ActinicPWMValue);
ReefAngel.PWM.SetChannel(LEDPWM2,LDT_MoonLightANALOGValue);
ReefAngel.PWM.SetChannel(LEDPWM0,RDT_DaylightPWMValue);
ReefAngel.PWM.SetChannel(LEDPWM1,RDT_ActinicPWMValue);
ReefAngel.PWM.SetChannel(LEDPWM3,RDT_MoonLightANALOGValue);
//_________________________________________________________________________________________________
////// Place your custom code above here //////
//Connects Reef Angel Head Unit to the Portal through WiFi Unit.
ReefAngel.Portal("troubadour11");
// This should always be the last line
ReefAngel.ShowInterface();
}
//************************************************************************************************************************************************************************************************************************
// - My lighting is set up as:
// - LDT (Left Display Tank) fixture on the "Standard" 2 (PWM signal) slots on my power relay box.
// - RDT (Right Display Tank) fixture on channels 0=daylight and 1=actinic (PWM signal) of the Dimming Expansion.
// - Moonlights = channels 2 and 3 of the PWM expansion. These drivers are Analog and the jumper pins have been set for 0-10v Analog in Dimming Expansion.
// - I'd like to calculate the cloud effect for the LDT lights using the "Weather Sim for Standard PWM" code which I think
// I got copied, set up, and working correctly for the 1 fixture on the Standard Ports.
// - Then find a way to feed those values into the RDT lights with a "Random Sample" of + or - a small time offset for the cloud effect on the RDT
// - This way I could randomly generate a weather sim, and randomly have the cloud move Right-to-Left or Left-to-Right each time a cloud occurs
// - Lightning effect could either be off set with a similar way with +/- value OR left sync'd to LDT lightning (would need to test what looks good).
// - Or possibly more or less lightning per fixture during cloud.
//*************************************************************************************************************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
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
// I should create a random variable here to randomize the days it has clouds?
#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 50
// Minimum number of minutes for cloud duration. Don't use min duration of less than 6.
#define Min_Cloud_Duration 6
// Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 12
// Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 5
// Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 15
// Only start the cloud effect after this setting
// In this example, start could happen after 11:30am
#define Start_Cloud_After NumMins(13,00)
// Always end the cloud effect before this setting
// In this example, end must be before 6:45pm
#define End_Cloud_Before NumMins(19,45)
// Percentage chance of lightning happening 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 50
// 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 results 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.
//____________________________________________________________________________________________________________________________________________________________
//***Question***
// Why do you have to double the amount of time from 250 to 500 in your example? - This is just a question to understand the programming better, I don't see why it needs to be doubled.
// I see it in the code dividing by (numclouds*2), etc. What's the purpose of multiplying it up by 2? I'm just trying to learn on this question if you have time to explain. Thanks :-)
//____________________________________________________________________________________________________________________________________________________________
//#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.
// Change the values above to customize your cloud/storm effect
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Do not change anything below here
static byte cloudchance = 255;
static byte cloudduration=0;
static int cloudstart=0;
static byte numclouds=0;
//made individual lightningchance for each fixture
static byte LDTlightningchance=0;
//static byte RDTlightningchance=0;
static byte cloudindex=0;
//made separate lightning status for each fixture
static byte LDTlightningstatus=0;
//static byte RDTlightningstatus=0;
static int LastNumMins=0;
//********************************************************************************************************************************************************
// - I created this value to use as +/- offset value to make clouds pass left-to-right or vice-versa.
//static int RDTcloudstartOffset=0;
// - I added this to use as a randomly chosen value between (-)x to y.
// - This will be added to "cloudstart" in order to determine the start time of the RDT cloud effect.
//static byte RDTcloudstart=0;
// - I added this to create a random sample between 80% and 100% dimming power for "random" lightning intensity effect on each fixture.
static byte LDT_LightningFlashPower=0;
static byte RDT_LightningFlashPower=0;
// - I added these to create a variable to randomize when and how long each lightning strike happens for each cloud.
//LDTlightningRandomStart=0;
//LDTlightningLength=0;
//RDTlightningRandomStart=0;
//RDTlightningLength=0;
//********************************************************************************************************************************************************
// 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
{
//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 between these values to offset the RDT cloud by this many minutes
//RDTcloudstartOffset=random(-1,1);
// adds the random offset value to "cloudstart" and sets the cloudstart time for RDT light fixture
//RDTcloudstart=cloudstart+RDTcloudstartOffset;
//********************************************************************************************************************************************************
// 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
LDTlightningchance=random(100);
//RDTlightningchance=random(100);
// if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning on the next cloud.
if (LDTlightningchance>Lightning_Change_per_Cloud) LDTlightningchance=0;
//if (RDTlightningchance>Lightning_Change_per_Cloud) RDTlightningchance=0;
}
}
// Now that we have all the parameters for the cloud, let's create the effect down below.
// Force Cloud from Menu Function jumps to here to start cloud effect immediately for a show!
if (ForceCloud)
{
ForceCloud=false;
cloudchance=1;
cloudduration=8;
LDTlightningchance=1;
//RDTlightningchance=1;
cloudstart=NumMins(hour(),minute())+2;
//RDTcloudstart=NumMins(hour(),minute())+1;
//RDTcloudstartOffset=random(-1,1);
//RDTcloudstart=cloudstart+RDTcloudstartOffset;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// - Controls Cloud and Lightning Effect timings for both fixtures
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
if (cloudchance)
{
//is it time for cloud yet over Left Display Tank?
if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
{
// - Dims LDT to create cloud and back up to Daylight Parabola. - maybe change to 11 in order to make minimum cloud power 11% so the lights don't click off.
RDT_DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,RDT_DaylightPWMValue,10,180);
LDT_DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_DaylightPWMValue,10,180);
RDT_ActinicPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,RDT_DaylightPWMValue,20,180);
LDT_ActinicPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_DaylightPWMValue,20,180);
RDT_MoonLightANALOGValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,RDT_MoonLightANALOGValue,11,180);
LDT_MoonLightANALOGValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_MoonLightANALOGValue,11,180);
// To adjust possible length of time of lightning strike, Change the "second()< 5 " ... where 5 is the # of seconds of lightning during cloud.
// and the "minute())=(cloudstart_(cloudduration/2)))" sets start time... dividing by 2 is exactly half way through cloud to trigger 5 seconds lightning.
// if there is lightning and current time is 1/2 way through cloud, but less then 1/2 way through cloud + 5 seconds
// if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/LDTlightningRandomStart))) && second()<LDTlightningLength)
if (LDTlightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5)
{
if (random(100)<20) LDTlightningstatus=1;
else LDTlightningstatus=0;
if (LDTlightningstatus)
{
// - This will correctly flash lightning up to 100% power every lightning strike.
//LDT_DaylightPWMValue=100;
//LDT_ActinicPWMValue=100;
//_______________________________________________________________________________________________________
// - Trying to make a random sample between, say 80% & 100% for the brightness of the lightning?
// - If there will be lightning, pick a random number between 80 and 100 for the lightning power.
LDT_LightningFlashPower=random(80,100);
RDT_LightningFlashPower=random(80,100);
RDT_DaylightPWMValue=RDT_LightningFlashPower;
RDT_ActinicPWMValue=RDT_LightningFlashPower;
RDT_MoonLightANALOGValue=RDT_LightningFlashPower;
LDT_DaylightPWMValue=LDT_LightningFlashPower;
LDT_ActinicPWMValue=LDT_LightningFlashPower;
LDT_MoonLightANALOGValue=LDT_LightningFlashPower;
//_______________________________________________________________________________________________________
}
else
{
// - Changed these values from 0 to 11 so at end of lightning cycle, the go to min. 11% power
RDT_DaylightPWMValue=11;
RDT_ActinicPWMValue=20;
RDT_MoonLightANALOGValue=11;
LDT_DaylightPWMValue=11;
LDT_ActinicPWMValue=20;
LDT_MoonLightANALOGValue=11;
//------------------------------------------------------------------
}
delay(1);
}
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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);
//************************************************************************************************************************************************************************************************
//// - I think this resets the random offset and cloudstart time for the RDT Fixture to match the new/next "cloudstart" time if there will be another cloud today ////
//************************************************************************************************************************************************************************************************
// pick a random number between these values to offset the RDT cloud by this many minutes
//RDTcloudstartOffset=random(-1,1);
// adds the random offset value to "cloudstart" and sets the cloudstart time for RDT light fixture
//RDTcloudstart=cloudstart+RDTcloudstartOffset;
//***********************************************************************************************************************************************************************
//Pick a random number between 0 and 99
LDTlightningchance=random(100);
//RDTlightningchance=random(100);
// if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
if (LDTlightningchance>Lightning_Change_per_Cloud) LDTlightningchance=0;
//if (RDTlightningchance>Lightning_Change_per_Cloud) RDTlightningchance=0;
}
}
if (LastNumMins!=NumMins(hour(),minute()))
{
LastNumMins=NumMins(hour(),minute());
ReefAngel.LCD.Clear(255,0,120,132,132);
ReefAngel.LCD.DrawText(0,255,5,120,"C");
ReefAngel.LCD.DrawText(0,255,11,120,"00:00");
ReefAngel.LCD.DrawText(0,255,45,120,"L");
ReefAngel.LCD.DrawText(0,255,51,120,"00:00");
if (cloudchance && (NumMins(hour(),minute())<cloudstart))
{
int x=0;
if ((cloudstart/60)>=10) x=11; else x=17;
ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart/60));
if ((cloudstart%60)>=10) x=29; else x=35;
ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart%60));
}
ReefAngel.LCD.DrawText(0,255,90,120,cloudduration);
if (LDTlightningchance)
{
int x=0;
if (((cloudstart+(cloudduration/2))/60)>=10) x=51; else x=57;
ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/60));
if (((cloudstart+(cloudduration/2))%60)>=10) x=69; else x=75;
ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60));
}
}
}
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 PWMStart;
//End Cloud & Lighting
}
Next up... getting my PH probe to read something more accurate then a constant value of 1. I've failed twice at getting it to calibrate correctly.