Re: Cloud and Lightning Code for Dimming Expansion
Posted: Fri Aug 28, 2015 12:58 pm
Post all of your code.
--Colin
--Colin
Community discussion about Reef Angel Controllers and reefing related subjects
https://forum.reefangel.com/
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>
////// Place global variable code below here
int DaylightPWMValue=0; // For cloud code
////// 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
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port1Bit | Port4Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port2Bit | Port3Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Ports that are always on
ReefAngel.Relay.On( Port1 );
ReefAngel.Relay.On( Port4 );
ReefAngel.Relay.On( Port8 );
////// Place additional initialization code below here
////// Place additional initialization code above here
}
void loop()
{
ReefAngel.StandardHeater( Port2 );
if ( ReefAngel.Params.Temp[T1_PROBE] < (InternalMemory.HeaterTempOn_read() - 10)) { ReefAngel.Relay.On(Port3); };
if ( ReefAngel.Params.Temp[T1_PROBE] > (InternalMemory.HeaterTempOff_read() - 5)) { ReefAngel.Relay.Off(Port3); };
ReefAngel.SingleATOLow( Port5 );
DaylightPWMValue=PWMSlopeHighRes(10,0,20,0,0,30,60,0);
CheckCloud();
ReefAngel.PWM.SetChannelRaw(0,DaylightPWMValue);
ReefAngel.PWM.SetChannel(1,PWMSlopeHighRes(9,30,20,30,5,30,30,0)); // Blues from 0930 to 2230 on a slope
ReefAngel.StandardLights(Port7,18,0,10,0); // Fuge light from 1800 to 1000
////// Place your custom code below here
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "MDB1029" );
ReefAngel.DDNS( "Reef" ); // Your DDNS is MDB1029-Reef.myreefangel.com
ReefAngel.ShowInterface();
}
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 max duration of less than 6
#define Min_Cloud_Duration 7
// Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 7
// 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 noon
#define Start_Cloud_After NumMins(12,00)
// Always end the cloud effect before this setting
// In this example, end cloud before 9:00pm
#define End_Cloud_Before NumMins(21,00)
// 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[] = {Mega2,Mega,Mega};
// 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;
static byte lightningchance=0;
static byte cloudindex=0;
static byte lightningstatus=0;
static int LastNumMins=0;
static byte lightningMode=0;
static boolean chooseLightning=true;
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)
{
//is it time for cloud yet?
if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
{
DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue/40.95,0,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
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 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)
{
if (random(100)<20) lightningstatus=1;
else lightningstatus=0;
if (lightningstatus)
{
DaylightPWMValue=4095;
}
else
{
DaylightPWMValue=0;
}
delay(1);
}
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;
}
}
}
// Write the times of the next cloud, next lightning, and cloud duration to the screen and into some customvars for the Portal.
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));
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,120,(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,90,120,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/2))/60)>=10) x=51;
else x=57;
//ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/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/2))%60)>=10) x=69;
else x=75;
//ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60)); // Write the minute of the next lightning to a custom variable for the Portal
ReefAngel.CustomVar[6]=(cloudstart+(cloudduration/2))%60;
}
}
}
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. I'm using 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
}
}
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;
}
Code: Select all
ReefAngel.PWM.SetChannel(1,PWMSlopeHighRes(9,30,20,30,5,30,30,0)); // Blues from 0930 to 2230 on a slope
Code: Select all
ReefAngel.PWM.SetChannelRaw(1,PWMSlopeHighRes(9,30,20,30,5,30,30,0)); // Blues from 0930 to 2230 on a slope
Between the flashes it is blue but only looks like the blue is flashing and the white is not present.cosmith71 wrote:Only channel 0 should flash. Are you sure the blues are flashing?
Got it on video if you want to see it but confirmed the white dim out on channel 0 and the blue are the ones flashing on channel 1. Is there something in the bottom part of the code which still has it flash on channel 1?cosmith71 wrote:The blue should stay the same. The white should slowly dim with flashes of varying length and intensity, like the videos above.
--Colin
Code: Select all
DaylightPWMValue=PWMSlopeHighRes(10,0,20,0,0,30,60,0);
CheckCloud();
ReefAngel.PWM.SetChannelRaw(0,DaylightPWMValue);
Code: Select all
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*0)); // 0x8 is channel 0, 0x12 is channel 1, etc. I'm using channel 0.
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(0); // Use the channel number you're flashing here
Wire.beginTransmission(0x40); // Same as above
Wire.write(0x8+(4*0));
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
}
}
Code: Select all
//#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.
Code: Select all
#define ReturnPump Port1
Code: Select all
ReefAngel.Relay.On(ReturnPump);
Code: Select all
ReefAngel.Relay.On(Port1);
Code: Select all
//#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.
Code: Select all
#ifdef forcecloudcalculation
if (cloudchance==255)
#else
if (hour()==0 && minute()==0 && second()==1 && cloudchance==255)
#endif
Code: Select all
//#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process.
Code: Select all
[quote]
[color=#7E7E7E]//By Matthew Hockin 2012. [/color]
[color=#7E7E7E]//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library[/color]
[color=#7E7E7E]//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. [/color]
[color=#7E7E7E]//If you copy from this (its all open source) please [/color]
[color=#7E7E7E]//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).[/color]
#include <Time.h>
#include <[color=#CC6600]Wire[/color].h>
#include <[color=#CC6600]OneWire[/color].h>
#include <Time.h>
#include <DS1307RTC.h>
#include <avr/wdt.h>
[color=#7E7E7E]//includes for SWFLTEK functions[/color]
#include <stdlib.h>
#include <math.h>
[color=#7E7E7E]//***********************************ARRAYS YOU MUST MODIFY TO MAKE YOUR TANK SET UP WORK*****************************[/color]
[color=#7E7E7E]//YOU MUST READ EVERY WORD IN THIS SECTION IN ORDER TO APPROPRIATELY CONFIGURE THIS PROGERAM- READ EVERY LINE BELOW///[/color]
[color=#CC6600]byte[/color] ChMax[]={255,220,255,220,255,220,0,0};[color=#7E7E7E]//Incremental value (Max-flicker) above flicker you want as max intensity (!!!!!!! Light Set Point is ChMax PLUS Flicker !!!!!!) [/color]
[color=#CC6600]byte[/color] flicker[]={29,29,29,29,29,29,0,0};[color=#7E7E7E]//need to input actual values here for flicker point on all channels in PWM expansion box[/color]
[color=#CC6600]boolean[/color] Wchannel[]={0,1,0,1,0,1,0,0}; [color=#7E7E7E]//use 1 to designate white channel (i.e. off during storm and used for lightning). Array corresponds to PWM channel 0-5 in order[/color]
[color=#7E7E7E]//Array to give direction to dimming. e.g. DimOrder[]={0,0,1,1,0,0} (cloud chase effects, just group channels you want to dim together during a cloud or storm [/color]
[color=#7E7E7E]//as either a 0 or a 1, i.e. all left side channels are 0 all right are 1 or all front are 0 all back are 1 or whatever[/color]
[color=#7E7E7E]//(which is zero or 1 will change who dims first). set them all to 0 if your tank has no left/right or front/back lights.[/color]
[color=#CC6600]byte[/color] DimOrder[]={0,0,0,1,1,1,0,0};
[color=#7E7E7E]//set all channel positions that you would like to use for the lightning strike effect to 1 (0-5 are PWM channels 6,7 are Main PWM outs)- and channels with a 0 are not used in strike[/color]
[color=#CC6600]byte[/color] StrikeChannel[]={0,1,0,1,0,1,0,0};
[color=#CC6600]byte[/color] MoonCh[]={0,0,0,0,0,0,0,0};[color=#7E7E7E]//place a 1 in the array position of all lighting channels you would like to use a moon lighting (this does not preclude their use in other phases (day, storm etc)[/color]
[color=#7E7E7E]//**********************************DONE CHANGING THINGS HERE BUT YOU MUST CHANGE ChOffset array IN CalcSUN function******[/color]
[color=#7E7E7E]//defines for SWFLTEK functions[/color]
[color=#7E7E7E]// arc-seconds per radian[/color]
#define _sec_rad 206264.806247096370813
[color=#7E7E7E]// axial tilt of earth at epoch, in radians[/color]
#define _tilt 0.409092804222329
[color=#7E7E7E]// tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136[/color]
#define _tropical_year 31556925
[color=#7E7E7E]// 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)[/color]
#define _zenith 3.11250383272322
[color=#7E7E7E]//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************[/color]
[color=#7E7E7E]//Unless your planning on editing the program DO NOT CHANGE ANYTHING HERE[/color]
[color=#CC6600]long[/color] latitude, longitude;
[color=#CC6600]byte[/color] TrueIntensity[8];[color=#7E7E7E]//array used to place hold final write values for PWM intensity setting[/color]
[color=#CC6600]long[/color] elapsedTime;[color=#7E7E7E]//used multiple places as elapsed since midnight[/color]
[color=#CC6600]long[/color] newDay;
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] rise;[color=#7E7E7E]//time in seconds from the year 2000 (GMT) for sunrise[/color]
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] set;[color=#7E7E7E]//time in seconds from the year 2000 (GMT) for sunrise[/color]
[color=#CC6600]long[/color] ChRiseSet[16];[color=#7E7E7E]//times of rise and set for all 8 channels based upon offsets from calc rise and set values[/color]
[color=#CC6600]float[/color] ChSlope[16];[color=#7E7E7E]//slopes for 1/2 day calculations based upon time from offset to midday for channel 1-8[/color]
[color=#CC6600]long[/color] CloudMaster[20];[color=#7E7E7E]// Set up array to hold start and end times for clouds for the day-[/color]
[color=#CC6600]long[/color] midDay;[color=#7E7E7E]// exactly 1/2 way between rise and set, i.e. solar noon for latitudes <60 close enough for us... [/color]
[color=#CC6600]byte[/color] PWMports[] ={
3,5,6,9,10,11};
[color=#CC6600]byte[/color] ChannelValue[8];[color=#7E7E7E]// Array to store output of insolaiton which may be modified and stored in TrueIntensity which is used to write to the PWM channels[/color]
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] StrikeStart;[color=#7E7E7E]//timer to keep track of strike sequence[/color]
[color=#CC6600]int[/color] StrikeMaster[20];[color=#7E7E7E]//Array to hold random strike pattern generated by weather array is sized to MAX needed given strike patter generator (8 strikes=16 positions)[/color]
[color=#CC6600]byte[/color] StrikeNumber;[color=#7E7E7E]//place to hold total number of strikes this sequence[/color]
[color=#CC6600]boolean[/color] StrikeNow;[color=#7E7E7E]//starts lightning strike sequence in loop state change made in weather/storm loop[/color]
[color=#CC6600]byte[/color] StrikeCount;[color=#7E7E7E]//Used to properly sequence strike sequence for delay between strikes[/color]
[color=#CC6600]byte[/color] cmdnum=255;
[color=#CC6600]byte[/color] datanum=255;
[color=#CC6600]byte[/color] dow=0;[color=#7E7E7E]//day of week[/color]
[color=#CC6600]byte[/color] strikePattern, strikeTime;[color=#7E7E7E]//used in Lightning() for timing of particular instance of strike [/color]
[color=#CC6600]boolean[/color] Cloud;[color=#7E7E7E]// are we in a cloud interval on days that have clouds[/color]
[color=#CC6600]boolean[/color] CloudToday;[color=#7E7E7E]//set in CalcSun if randomization yields a day with clouds.[/color]
[color=#CC6600]boolean[/color] IsStorm;[color=#7E7E7E]// are we in a storm[/color]
[color=#CC6600]byte[/color] CloudsTotal;[color=#7E7E7E]// how many clouds today[/color]
[color=#CC6600]long[/color] lastmillis;[color=#7E7E7E]// variable to track millis to enable cloud and insolation loop restriction by time[/color]
[color=#CC6600]boolean[/color] StormAdvance;[color=#7E7E7E]//storm timer for light effect advance[/color]
[color=#CC6600]boolean[/color] InsolationAdvance;[color=#7E7E7E]//when true we recalculate light intensity during clear sky (every 3 seconds seems more than often enough)[/color]
[color=#CC6600]byte[/color] counter;[color=#7E7E7E]//used to track millis advance for insolation,cloud trigger[/color]
[color=#7E7E7E]//****************************************[/color]
[color=#7E7E7E]//END HEADER/Global Variable declaration//[/color]
[color=#7E7E7E]//****************************************[/color]
[color=#7E7E7E]//Setup[/color]
[color=#CC6600]void[/color] [color=#CC6600][b]setup[/b][/color](){
[color=#CC6600][b]Serial[/b][/color].[color=#CC6600]begin[/color](57600);
[color=#CC6600]Wire[/color].[color=#CC6600]begin[/color](8);
[color=#7E7E7E]//Wire.onReceive(receiveEvent);[/color]
[color=#7E7E7E]//Wire.onRequest(requestEvent);[/color]
[color=#CC6600]pinMode[/color](3,[color=#006699]OUTPUT[/color]);
[color=#CC6600]pinMode[/color](5,[color=#006699]OUTPUT[/color]);
[color=#CC6600]pinMode[/color](6,[color=#006699]OUTPUT[/color]);
[color=#CC6600]pinMode[/color](9,[color=#006699]OUTPUT[/color]);
[color=#CC6600]pinMode[/color](10,[color=#006699]OUTPUT[/color]);
[color=#CC6600]pinMode[/color](11,[color=#006699]OUTPUT[/color]);
wdt_enable(WDTO_1S);
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] seed=0, count=32;
[color=#CC6600]while[/color] (--count){
seed = (seed<<1) | ([color=#CC6600]analogRead[/color](5)&1);
}
[color=#CC6600]randomSeed[/color](seed);[color=#7E7E7E]//start random generator at a different point each time (not perfect but whatever its gonna be pretty damn random)[/color]
[color=#CC6600]setSyncProvider[/color](RTC.[color=#CC6600]get[/color]); [color=#7E7E7E]// the function to get the time from the RTC[/color]
[color=#CC6600]setSyncInterval[/color](SECS_PER_HOUR); [color=#7E7E7E]// Changed to sync every hour.[/color]
dow=0;[color=#7E7E7E]//set Day Of Week (dow) to a 0 value which is impossible (day()=1-7)... so we trigger calcSun on restart [/color]
StrikeNow=[color=#CC6600]false[/color];[color=#7E7E7E]//no lightning strike yet[/color]
CloudToday=[color=#CC6600]false[/color];[color=#7E7E7E]//set to no clouds so CalcSun can set correctly if should be true[/color]
Cloud=[color=#CC6600]false[/color];[color=#7E7E7E]//set cloud to false[/color]
IsStorm=[color=#CC6600]false[/color];[color=#7E7E7E]//set storm to false[/color]
lastmillis=[color=#CC6600]millis[/color]();[color=#7E7E7E]//start our millis timer now[/color]
counter=0;[color=#7E7E7E]//used in weather for triggering a storm, triggering lightning in a storm.[/color]
StrikeCount=0;[color=#7E7E7E]//Number of lightning strikes in the sequence.. set to zero until initialized in sequence[/color]
}
[color=#7E7E7E]//End Setup[/color]
[color=#7E7E7E]//*********************************************************************************************************************************[/color]
[color=#7E7E7E]//*********************************************************************************************************************************[/color]
[color=#7E7E7E]//Loop[/color]
[color=#CC6600]void[/color] [color=#CC6600][b]loop[/b][/color](){
elapsedTime=([color=#CC6600]now[/color]()-newDay);[color=#7E7E7E]//Elapsed time is seconds from midnight of today- local processor time.[/color]
wdt_reset();
[color=#CC6600]if[/color] (cmdnum!=255){
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
[color=#CC6600]if[/color] (dow!=[color=#CC6600]day[/color]()){ [color=#7E7E7E]//used to see that were in a new day and need to recalculate sunrise and sunset[/color]
CalSun();
dow=[color=#CC6600]day[/color]();
}
[color=#7E7E7E]//Use millis to enable tracking of time interval[/color]
[color=#CC6600]if[/color] (([color=#CC6600]millis[/color]()-lastmillis)>=100){
lastmillis=[color=#CC6600]millis[/color]();
counter+=1;
StormAdvance=[color=#CC6600]true[/color];
[color=#7E7E7E]//now a bunch of stuff that may or may not be true at the same time but that all needs to happen when its true[/color]
[color=#CC6600]if[/color] (counter==0){
InsolationAdvance=[color=#CC6600]true[/color];[color=#7E7E7E]//so that it runs on start up to provide light immediately [/color]
}
[color=#CC6600]if[/color] (counter%30==0){
InsolationAdvance=[color=#CC6600]true[/color];
}
[color=#CC6600]if[/color] (counter==210) counter=0;
}
[color=#CC6600]if[/color] (InsolationAdvance==[color=#CC6600]true[/color]) Insolation();[color=#7E7E7E]//calculate clear sky solar intensity as the day advances[/color]
Weather();[color=#7E7E7E]//run the weather overlay (cloud, storm)[/color]
[color=#7E7E7E]//check to see if were need to have a lightning strike[/color]
[color=#CC6600]if[/color] (StrikeNow==[color=#CC6600]true[/color]){
[color=#CC6600]if[/color] (([color=#CC6600]millis[/color]()-StrikeStart)>=StrikeMaster[(StrikeCount*2)]){[color=#7E7E7E]//check if time has passed the delay (position 0,2,4,6,8 etc in StrikeMaster)-StrikeCount is indexed up by 1 after each strike so we see positions 0,2,4,6,etc in sequence[/color]
[color=#CC6600]byte[/color] intensity;
intensity=[color=#CC6600]random[/color](180,256);[color=#7E7E7E]// this little bit should generate a randomly bright flash variation between the series of flashes in StrikeMaster[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] b=0; b<6; b++){
[color=#CC6600]if[/color] (StrikeChannel[b]==1) [color=#CC6600]analogWrite[/color](PWMports[b],intensity);[color=#7E7E7E]// set all strike channels to intensity of strike[/color]
}
[color=#CC6600]delay[/color](StrikeMaster[((StrikeCount*2)+1)]);[color=#7E7E7E]//index to +1 position in array from 0,2,4, etc to 1,3,5 etc[/color]
StrikeCount++;[color=#7E7E7E]//so that the next time we look at elapsed time were looking at the right array position[/color]
[color=#CC6600]if[/color] (StrikeCount==(StrikeNumber-1)){
StrikeNow=[color=#CC6600]false[/color];
StrikeCount=0;
}
}
}
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0;a<6;a++){[color=#7E7E7E]//using all prior mods to light intensity (Insolation-->Cloud-->Storm) lets make some light[/color]
[color=#CC6600]analogWrite[/color](PWMports[a],TrueIntensity[a]);[color=#7E7E7E]//dont change this to 8 to refelct array for channels.. we only have 6 here![/color]
}
}
[color=#7E7E7E]//End Loop[/color]
[color=#7E7E7E]//*********************************************************************************************************************************[/color]
[color=#7E7E7E]//*********************************************************************************************************************************[/color]
[color=#7E7E7E]//Standard PWM Functions Receive/Process[/color]
[color=#CC6600]void[/color] receiveEvent([color=#CC6600]int[/color] howMany) {
wdt_reset();
[color=#CC6600]if[/color] (howMany==5){
[color=#CC6600]byte[/color] cmd1, cmd2, cmd3, cmd4, cmd5;
cmd1=[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
cmd2=[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
cmd3=[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
cmd4=[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
cmd5=[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
[color=#CC6600]if[/color] (cmd1==[color=#006699]'$'[/color] && cmd2==[color=#006699]'$'[/color] && cmd3==[color=#006699]'$'[/color]){
cmdnum=cmd4;
datanum=cmd5;
[color=#7E7E7E]//Serial.println(cmd4,DEC);[/color]
[color=#7E7E7E]//Serial.println(cmd5,DEC);[/color]
}
}
[color=#CC6600]else[/color]{
[color=#CC6600]for[/color] ([color=#CC6600]int[/color] a=0;a<howMany;a++){
[color=#CC6600]Wire[/color].[color=#CC6600]read[/color]();
}
}
}
[color=#CC6600]void[/color] ProcessCMD([color=#CC6600]byte[/color] cmd, [color=#CC6600]byte[/color] data){
wdt_reset();
}
[color=#7E7E7E]//End Standard Functions[/color]
[color=#7E7E7E]//*********************************************************************************************************************************[/color]
[color=#7E7E7E]//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.[/color]
[color=#CC6600]void[/color] CalSun(){
[color=#7E7E7E]//Serial.println("CalSun Run Now");[/color]
[color=#7E7E7E]//*********************YOU NEED TO CHANGE THESE VALUES Read instructions in their ENTIRETY and CAREFULLY change to values for your tank and geographical region***************************[/color]
[color=#7E7E7E]//channels 0-5 are PWM expansion board lights 6,7 are ReefAngel Controller PWM outputs[/color]
[color=#7E7E7E]//offsets for rise/set all values in seconds offset from calculated rise or set value (-) am offset=longer day****** (-)pm offset=shorter day)[/color]
[color=#7E7E7E]//array order is ch0 am offset, pm offset, ch1 am offset, pm offset etc..[/color]
[color=#7E7E7E]//THESE values are the number of seconds that a particular channel will be offset from the rise/set time, i.e. negative to rise earlier/set earlier[/color]
[color=#CC6600]int[/color] Choffset[]={
5000,6000,6000,5000,5400,6800,6800,5400,6400,7200,7200,6400,0,0,0,0};
[color=#7E7E7E]// NOW SET YOUR LATIDTUDE AND LONGITUDE COORDINATES as Degrees, Minutes, Seconds of Lat and Lon[/color]
[color=#7E7E7E]//If Your NORTH of the equator your LONGITUDE must START with a NEGATIVE number (the rest are positive) e.g. All of North America, Europe, Russia etc are negative[/color]
[color=#7E7E7E]//If Your EAST of the Prime Meridian your LATITUDE must START with a NEGATIVE number (the rest are positive), e.g. Most of Europe, All of China, India, Austraila, Russia etc are negative[/color]
latitude=dmsToSeconds(20,54,00);[color=#7E7E7E]//United States of America- Salt Lake City, local time is -7 hours GMT [/color]
longitude=dmsToSeconds(-155,35,00);
[color=#7E7E7E]//**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load******************************************** [/color]
[color=#CC6600]if[/color] (dow==0){[color=#7E7E7E]//if the controller has resarted we need to find midnight[/color]
[color=#CC6600]long[/color] hours, minutes;[color=#7E7E7E]//store current elapsed local hours as total seconds from midnight[/color]
[color=#CC6600]time_t[/color] t=[color=#CC6600]now[/color]();[color=#7E7E7E]//store current clock time to parse[/color]
hours=[color=#CC6600]hour[/color](t);
hours=(hours*3600);[color=#7E7E7E]//current hour number 0-23 as seconds[/color]
minutes=[color=#CC6600]minute[/color](t);
minutes=(minutes*60);[color=#7E7E7E]//minutes of current hour as seconds[/color]
newDay=[color=#CC6600]now[/color]();
newDay-=(hours+minutes);[color=#7E7E7E]//Convert current local unix epoch time to local unix epoch time of midnight[/color]
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (dow!=0){[color=#7E7E7E]//if we did not restart but the day is new then it is midnight and were good to go..[/color]
newDay=[color=#CC6600]now[/color]();
}
[color=#7E7E7E]//#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin[/color]
newDay-=946684800;[color=#7E7E7E]//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today[/color]
rise=newDay;[color=#7E7E7E]//set value to send to SunRise as midnight GMT in seconds from Y2K[/color]
set=newDay;[color=#7E7E7E]//[/color]
[color=#7E7E7E]//Calculate rise time and set time using Epherma Library functions (see end of code) [/color]
SunRise(&rise);[color=#7E7E7E]//call to Epherma function[/color]
SunSet(&set);[color=#7E7E7E]//Call to Epherma functionunsigned long newDay;[/color]
[color=#7E7E7E]/*Serial.print("rise and set= ");[/color]
[color=#7E7E7E] Serial.println(rise);[/color]
[color=#7E7E7E] Serial.println(set);[/color]
[color=#7E7E7E] Serial.print("newDay as seconds since 2000 to todays midnight= ");[/color]
[color=#7E7E7E] Serial.println(newDay);*/[/color]
rise=(rise-newDay);[color=#7E7E7E]// set to elapsed seconds of day[/color]
set=(set-newDay);
[color=#7E7E7E]/*Serial.print("rise and set as elapsed seconds of day= ");[/color]
[color=#7E7E7E] Serial.println(rise);[/color]
[color=#7E7E7E] Serial.println(set);*/[/color]
newDay+=946684800;[color=#7E7E7E]//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day) [/color]
[color=#7E7E7E]/*Serial.print("newDay as seconds since since 1970 to todays midnight= ");[/color]
[color=#7E7E7E] Serial.println(newDay);[/color]
[color=#7E7E7E] Serial.print("elapsed is");[/color]
[color=#7E7E7E] long elapsed=now()-newDay;[/color]
[color=#7E7E7E] Serial.println(elapsed);*/[/color]
[color=#7E7E7E]//Calculate rise and set times for all channels in equivlants to elapsed seconds from midnight today[/color]
[color=#7E7E7E]//populate array for chRise and Set as well as chSlope for 0.5pi/ half day lenght for each channel from midday (asymmetric days are allowed)[/color]
[color=#CC6600]float[/color] deltaY=1.570796327;[color=#7E7E7E]//1/2 * pi as integer by scaling* 10^9 to fill UL[/color]
midDay=(((set-rise)/2)+rise);
[color=#CC6600]long[/color] HalfDayLength=((set-rise)/2);
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] b=0;b<16;b++){[color=#7E7E7E]//working as of April 5 2012 serial tested[/color]
[color=#CC6600]if[/color] (b%2==0){
ChRiseSet[b]=rise+(Choffset[b]);
ChSlope[b]=(deltaY/([color=#CC6600]float[/color])(HalfDayLength-(Choffset[b])));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (b%2==1){
ChRiseSet[b]=set+(Choffset[b]);
ChSlope[b]=(deltaY/([color=#CC6600]float[/color])(HalfDayLength+(Choffset[b])));
}
}
[color=#7E7E7E]//***************** to CHANGE THE chance of Clouds actually occuring on a given day************************[/color]
[color=#CC6600]byte[/color] CloudChance=80;[color=#7E7E7E]//% Chance of a Cloud every day[/color]
[color=#7E7E7E]//****************************now were done- did you use a value from 0-100 without a decimal?****************[/color]
[color=#CC6600]byte[/color] RainMaker=[color=#CC6600]random[/color](1,101);
[color=#CC6600]if[/color] (RainMaker<=CloudChance){
CloudToday=[color=#CC6600]true[/color];[color=#7E7E7E]//used to trigger weather function, can also be used to send flag to controller[/color]
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (RainMaker>CloudChance){
CloudToday=[color=#CC6600]false[/color];[color=#7E7E7E]//see above comment on CloudToday[/color]
[color=#CC6600]return[/color];
}
[color=#CC6600]long[/color] dayLength=0;
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=1;a<16;a=(a+2)){[color=#7E7E7E]//determine maximum day length given light on tank that is not moon light, this will yield night clouds and storms (and a storm after dark is severe... always[/color]
[color=#CC6600]if[/color] (a==0){
[color=#CC6600]if[/color] (((set+Choffset[a])-rise)>(set-rise)){
dayLength=((set+Choffset[a])-rise);
}
[color=#CC6600]else[/color] dayLength=(set-rise);
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (a!=0){
[color=#CC6600]if[/color] (dayLength<((set+Choffset[a])-rise)){
dayLength=((set+Choffset[a])-rise);
}
}
}
[color=#7E7E7E]// number of clouds possible for the day, max and min[/color]
[color=#CC6600]byte[/color] CloudsMax=5;[color=#7E7E7E]//DONT INCREASE BEYOND 10 or it will DIE, or increase array size to handle it (among other things)[/color]
[color=#CC6600]byte[/color] CloudsMin=2;[color=#7E7E7E]//use 2 as a minimum[/color]
CloudsTotal=[color=#CC6600]random[/color](CloudsMin,(CloudsMax+1));
[color=#7E7E7E]// Average day is 50,000 secs so if 4 clouds and 10% that gets you 5,000 seconds of clouds (about 1800 seconds length for each of the 4 clouds in independent segments (if 4 is # clouds)[/color]
[color=#CC6600]byte[/color] OvercastMin=((CloudsTotal*10)/5);[color=#7E7E7E]//Min cloud length will be about 1000 seconds (15 mins)- 1 hour min of clouds if you have 4, 2 hours if you have 8[/color]
[color=#CC6600]byte[/color] OvercastMax=((CloudsTotal*10)/2);[color=#7E7E7E]//max cloud length will be about 2500 seconds (45 mins)- 6 hours max of clouds if you have 8, 3 hours max if you have 4[/color]
[color=#CC6600]float[/color] Overcast=[color=#CC6600]random[/color](OvercastMin,OvercastMax);
Overcast=(Overcast/100);
[color=#7E7E7E]// split the total lenght of time for clouds into equal segments and then to randomly chop or add time to the [/color]
[color=#7E7E7E]//segments such that cloud length is variable. Then distribute into random parts of the day and fill array with start,duration pairs for clouds[/color]
[color=#CC6600]int[/color] CloudLength;
CloudLength=((dayLength*Overcast)/CloudsTotal);[color=#7E7E7E]//average cloud length[/color]
[color=#CC6600]long[/color] SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));[color=#7E7E7E]//average sun length between clouds[/color]
[color=#CC6600]float[/color] CloudFraction=0;
[color=#CC6600]float[/color] SunFraction=0;
[color=#7E7E7E]//start by zero filling CloudMaster array[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<20; a++){
CloudMaster[a]=0;
}
[color=#CC6600]byte[/color] b=0;[color=#7E7E7E]//used to get pairs of fraction for SunFraction in for loop[/color]
[color=#CC6600]byte[/color] c=0;[color=#7E7E7E]//used to get pairs of fraction for CloudFraction in for loop[/color]
[color=#7E7E7E]//now randomize cloud length and sunsegment length as pairs to get different looking days- [/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<(CloudsTotal*2); a++){
[color=#CC6600]if[/color] (a%2==0){
[color=#CC6600]if[/color] (b==0){
[color=#CC6600]if[/color] (a==0){
SunFraction=[color=#CC6600]random[/color](20,181);[color=#7E7E7E]//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length[/color]
SunFraction=(SunFraction/100);
CloudMaster[a]=(SunFraction*SunSegment);
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (a<((CloudsTotal*2)-2)){
SunFraction=[color=#CC6600]random[/color](20,181);[color=#7E7E7E]//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length[/color]
SunFraction=(SunFraction/100);
CloudMaster[a]=(SunFraction*SunSegment);
b++;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (a==((CloudsTotal*2)-2)){
SunFraction=(2-(([color=#CC6600]float[/color])CloudMaster[0]/([color=#CC6600]float[/color])SunSegment));
CloudMaster[a]=(SunFraction*SunSegment);
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (b==1){
[color=#CC6600]if[/color] (a<((CloudsTotal*2)-2)){
SunFraction=(2-SunFraction);[color=#7E7E7E]//were on the second part of a pair[/color]
CloudMaster[a]=(SunFraction*SunSegment);
b=0;[color=#7E7E7E]//reset so next time we start a new fraction[/color]
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (a==((CloudsTotal*2)-2)){
SunFraction=(2-(([color=#CC6600]float[/color])CloudMaster[0]/([color=#CC6600]float[/color])SunSegment));
CloudMaster[a]=(SunFraction*SunSegment);
}
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (a%2==1){[color=#7E7E7E]//if were in odd positions we need to determine cloud lengths in random pairs such that each pair =2*CloudLength in length[/color]
[color=#CC6600]if[/color] (c==0){
CloudFraction=[color=#CC6600]random[/color](20,181);[color=#7E7E7E]//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length[/color]
CloudFraction=(CloudFraction/100);
CloudMaster[a]=(CloudFraction*CloudLength);
c++;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (c==1){
CloudFraction=(2-CloudFraction);
CloudMaster[a]=(CloudFraction*CloudLength);
c=0;[color=#7E7E7E]//reset so next loop finds a new fraction[/color]
}
}
}
[color=#7E7E7E]/*Serial.println("here is cloud master in is entirety prior to forming start and end pairs");[/color]
[color=#7E7E7E] for (byte a=0;a<20;a++){[/color]
[color=#7E7E7E] Serial.println(CloudMaster[a]);[/color]
[color=#7E7E7E] }*/[/color]
[color=#7E7E7E]//reframe array to generate cloud start, cloud end, cloud start, cloud end[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<(CloudsTotal*2); a++){
[color=#CC6600]if[/color] (a==0){[color=#7E7E7E]// if were starting our first cloud we need to add to rise value to first sun segment[/color]
CloudMaster[a]=rise+CloudMaster[a];
}
[color=#CC6600]else[/color] {
CloudMaster[a]=(CloudMaster[a-1]+CloudMaster[a]);[color=#7E7E7E]//just add prior values together e.g. (second position is cloud end so to find end add rise corrected start time with duration)[/color]
[color=#7E7E7E]// subsequent start would be end of 1st cloud + next sunsegment fraction[/color]
}
}
[color=#7E7E7E]/*Serial.println("here is cloud master in is entirety as start and end pairs");[/color]
[color=#7E7E7E] for (byte a=0;a<20;a++){[/color]
[color=#7E7E7E] if (a%2==0){[/color]
[color=#7E7E7E] Serial.print("Start time=");[/color]
[color=#7E7E7E] Serial.println(CloudMaster[a]);[/color]
[color=#7E7E7E] }[/color]
[color=#7E7E7E] else {[/color]
[color=#7E7E7E] Serial.print("End time=");[/color]
[color=#7E7E7E] Serial.println(CloudMaster[a]);[/color]
[color=#7E7E7E] }[/color]
[color=#7E7E7E] }*/[/color]
}[color=#7E7E7E]//END SunCalc FUNCTION[/color]
[color=#CC6600]void[/color] Insolation()
{
InsolationAdvance=[color=#CC6600]false[/color];[color=#7E7E7E]//reset this flag now that we have entered function[/color]
[color=#7E7E7E]//define Pi as delta Y for slope since cos 0.5-1.5 Pi goes 0-1-0 in 0.5 pI increments slope of 1/2 day (0-1 intensity) delta Y is 1/2 Pi [/color]
[color=#CC6600]float[/color] Pi=3.1415926;[color=#7E7E7E]//scale to 10^8[/color]
[color=#CC6600]float[/color] PiHalf=1.5707963;[color=#7E7E7E]//scale to 10^8[/color]
[color=#CC6600]float[/color] secSoFar;[color=#7E7E7E]//variable to account for seconds elapsed for each channel 1/2 day period from rise-->midDay and midDay-->set[/color]
[color=#7E7E7E]/* using -cos(pi/2+elapsedTime/slope) calculate fractional intensity of each channel throughout the day[/color]
[color=#7E7E7E] use flicker points to adjust minimum intensity to stable light. Turn off lights after set or before rise etc.[/color]
[color=#7E7E7E] by splitting into half days centered on midday (1/2 ofset-rise) we center exactly the cos function for every channel so color blends are maintained [/color]
[color=#7E7E7E] throughout intensity ramp... more or less ... change intensity every 120 seconds throughout the day*/[/color]
[color=#CC6600]if[/color] (elapsedTime<=midDay){
[color=#CC6600]byte[/color] c=0;[color=#7E7E7E]//loop counter[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] b=0;b<16;b=(b+2)){
[color=#CC6600]if[/color] (elapsedTime>=ChRiseSet[b]){
secSoFar=(elapsedTime-ChRiseSet[b]);[color=#7E7E7E]//just account for length of every channel 1/2 day and switch at midDay[/color]
ChannelValue[c]=flicker[c]+ChMax[c]*(-[color=#CC6600]cos[/color](PiHalf+(ChSlope[b]*secSoFar)));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (elapsedTime<ChRiseSet[b]){
[color=#CC6600]if[/color] (MoonCh[c]==1){
[color=#CC6600]byte[/color] MoonToday=[color=#CC6600]MoonPhase[/color]()*0.5;[color=#7E7E7E]//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light[/color]
[color=#CC6600]if[/color] (MoonToday==0) ChannelValue[c]=0;
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (MoonToday<flicker[c]) ChannelValue[c]=flicker[c];
[color=#CC6600]else[/color] ChannelValue[c]=MoonToday;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (MoonCh[c]==0){
ChannelValue[c]=0;[color=#7E7E7E]//its dark and this is not a moon phase channel[/color]
}
}
c++;[color=#7E7E7E]//index by one so we count 0-7 as b goes 0-14 by twos[/color]
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (elapsedTime>midDay){
[color=#CC6600]byte[/color] c=0;[color=#7E7E7E]//loop counter[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] b=1;b<16;b=b+2){
[color=#CC6600]if[/color] (elapsedTime<=ChRiseSet[b]){
secSoFar=(elapsedTime-midDay);
ChannelValue[c]=flicker[c]+ChMax[c]*(-[color=#CC6600]cos[/color](Pi+(ChSlope[b]*secSoFar)));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (elapsedTime>ChRiseSet[b]){
[color=#CC6600]if[/color] (MoonCh[c]==1){
[color=#CC6600]byte[/color] MoonToday=[color=#CC6600]MoonPhase[/color]()*0.5;[color=#7E7E7E]//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light[/color]
[color=#CC6600]if[/color] (MoonToday==0) ChannelValue[c]=0;
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (MoonToday<flicker[c]) ChannelValue[c]=flicker[c];
[color=#CC6600]else[/color] ChannelValue[c]=MoonToday;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (MoonCh[c]==0){
ChannelValue[c]=0;[color=#7E7E7E]//its dark and this is not a moon phase channel[/color]
}
}
c++;[color=#7E7E7E]//index to count 0-7 as b counts 1-15 by twos.[/color]
}
}
}[color=#7E7E7E]//END function[/color]
[color=#7E7E7E]//WEATHER FUNCTION BEGIN[/color]
[color=#CC6600]void[/color] Weather ()
{
[color=#CC6600]static[/color] [color=#CC6600]byte[/color] loopCount;
[color=#CC6600]static[/color] [color=#CC6600]float[/color] CloudCover; [color=#7E7E7E]// variable to store value in random walk - declared static to accumulate Cloud effect[/color]
[color=#CC6600]static[/color] [color=#CC6600]float[/color] PriorCloudCover; [color=#7E7E7E]//used to "delay" one side of the tank from the other in cloud passing effects[/color]
[color=#CC6600]static[/color] [color=#CC6600]long[/color] StormStart;
[color=#CC6600]static[/color] [color=#CC6600]long[/color] StormEnd;
[color=#CC6600]static[/color] [color=#CC6600]long[/color] CloudEnd;
[color=#CC6600]static[/color] [color=#CC6600]boolean[/color] wtrigger;[color=#7E7E7E]//use to track the first run of functions to calculate random times that become fixed, see change from cloud to storm for useage[/color]
[color=#CC6600]static[/color] [color=#CC6600]byte[/color] Counter;[color=#7E7E7E]//used to trigger storms from cloud you can change its if loop comparison to decrease or increase storm rate see below in cloud==true if loop[/color]
[color=#CC6600]static[/color] [color=#CC6600]byte[/color] Severity;
[color=#CC6600]static[/color] [color=#CC6600]byte[/color] StormCount;[color=#7E7E7E]// used to limit X storms per cloud and to choose which cloud can have a storm[/color]
[color=#CC6600]static[/color] [color=#CC6600]int[/color] StepSize;
[color=#CC6600]static[/color] [color=#CC6600]int[/color] LastStepSize;
[color=#7E7E7E]//check to see if were having a scheduled cloud[/color]
[color=#CC6600]if[/color] (Cloud==[color=#CC6600]false[/color]){
[color=#7E7E7E]//Write Insolation values to TrueIntensity so the loop will pick them up and the cloud/storm will get the right data (since intensity changes during the day)[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<8; a++){[color=#7E7E7E]//this must be above the next loop[/color]
TrueIntensity[a]=ChannelValue[a];[color=#7E7E7E]//this is where intensity is set for the PWM channel analog write in the loop... don't mess with this.[/color]
}
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<(CloudsTotal*2); a=(a+2)){[color=#7E7E7E]//if its time for a cloud, run it[/color]
[color=#CC6600]if[/color] ((elapsedTime>=CloudMaster[a]) && (elapsedTime<=CloudMaster[(a+1)])) {
CloudEnd=CloudMaster[(a+1)];[color=#7E7E7E]//to avoid this loop running true during the compute cycles at the end of the cloud and before elapsedTime advances a second, actual cloud does not[/color]
[color=#7E7E7E]//Serial.print("We started a cloud and its end is=");[/color]
[color=#7E7E7E]//Serial.println(CloudEnd);[/color]
CloudCover=CloudStart(CloudMaster[a]);[color=#7E7E7E]//CloudStart modifies TrueIntensity to get us to 50% intensity at the start of the cloud effect and also sets cloud=true to bypass this[/color]
Counter=0;
StormCount=[color=#CC6600]random[/color](0,3);[color=#7E7E7E]//the number of storms MAX that may occur in this cloud (remember Random is range= -1 on high end)[/color]
loopCount=1;
LastStepSize=0;[color=#7E7E7E]//zero out cloud random walk variables[/color]
StepSize=0;[color=#7E7E7E]//zero out cloud ranodm walk variables[/color]
[color=#CC6600]return[/color];[color=#7E7E7E]//exit having started a cloud in CLoudStart routine called above[/color]
}
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] ((Cloud==[color=#CC6600]true[/color]) && (IsStorm==[color=#CC6600]false[/color])){
[color=#CC6600]if[/color] (StormAdvance==[color=#CC6600]false[/color]){[color=#7E7E7E]//use millis tracker to run this loop every 2 seconds[/color]
[color=#CC6600]return[/color];
}
StormAdvance=[color=#CC6600]false[/color];[color=#7E7E7E]//reset to false when true so we run this once, until time advance is true again[/color]
[color=#CC6600]if[/color] (elapsedTime>=CloudEnd){
ClearSky(CloudCover, CloudEnd);
[color=#CC6600]return[/color];
}
[color=#7E7E7E]/*Use fractional intensity to set minimum value for any channel. Dimming is proportional to actual intensity output [/color]
[color=#7E7E7E] and constrained by flicker point. Random walk uses static variable "CloudCover" constrained to 0-100 to represent fractional intensity (i.e. (1-(CloudCover/100))*Insolation SetPoint [/color]
[color=#7E7E7E] is how the current cloud intensity is set, i.e. cloud cover of 90 gives 10% insolation setpoint unless below flicker in which case = flicker*/[/color]
[color=#CC6600]if[/color] (loopCount==1){
PriorCloudCover=CloudCover; [color=#7E7E7E]//e.g. PriorCloudCover=CloudCover with no float math to screw things up[/color]
LastStepSize=StepSize;
StepSize=([color=#CC6600]random[/color](5,21));[color=#7E7E7E]// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)[/color]
[color=#CC6600]if[/color] (([color=#CC6600]random[/color](0,2)!=1)) StepSize=-(StepSize);
[color=#CC6600]if[/color] ((CloudCover+StepSize)>=100){[color=#7E7E7E]//cannot shut off lights more than 100% so limit here[/color]
StepSize=(100-CloudCover);[color=#7E7E7E]//[/color]
Counter++;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] ((CloudCover+StepSize)<=0){[color=#7E7E7E]//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down[/color]
StepSize=-(StepSize);
[color=#CC6600]if[/color] (Counter>=50) Counter-=[color=#CC6600]random[/color](-1,2);[color=#7E7E7E]//since we got bright... lets further delay and randomize storm occurence [/color]
}
CloudCover=CloudCover+StepSize;
}
[color=#CC6600]if[/color] ((Counter>=60) && ((CloudEnd-elapsedTime)>=300)) {[color=#7E7E7E]//if Counter (indexes when cloud cover reaches 100) has accumulated and we still have time lets make a storm[/color]
[color=#7E7E7E]//to change the frequency of storms increase or decrease the number comparison for counter in the if statement above (larger #== less storms).[/color]
[color=#7E7E7E]//if you change counter comparison here change it in the next loop as well[/color]
[color=#CC6600]if[/color] (StormCount>=1){[color=#7E7E7E]//if we can have storms in this cloud (random- statisticly 1/3 clouds = no storm, 1/3 = 1 possible storm, 1/3 = 2 possible storms)[/color]
[color=#CC6600]byte[/color] RandomStorm;
RandomStorm=[color=#CC6600]random[/color](0,11);[color=#7E7E7E]//this randomizes for longer clouds without storm, avg cloud is much longer prior to storm occuring- thus short clouds will not generally have a storm[/color]
[color=#CC6600]if[/color] (RandomStorm>=4){
StormCount-=1;[color=#7E7E7E]//count down by 1 the number of storms in this cloud- this will not roll the byte since the loop requires it to be at least 1 to ever subtract here. [/color]
Counter=0;[color=#7E7E7E]//reset this variable since Storm loop uses it as well.[/color]
[color=#CC6600]int[/color] LongestStorm;[color=#7E7E7E]//used to pass max possible time to storm if loop from cloud loop within weather function[/color]
LongestStorm=(CloudEnd-elapsedTime);
Severity=StartStorm(LongestStorm, IsStorm, StormEnd);
loopCount=1;[color=#7E7E7E]//reset counting loop for the storm[/color]
}
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] ((Counter>=60) && ((CloudEnd-elapsedTime)<300)){
Counter=0;[color=#7E7E7E]//just reset the counter (does not really matter in this case but its clean)[/color]
}
[color=#CC6600]for[/color] ([color=#CC6600]int[/color] a=0;a<8;a++){
[color=#CC6600]if[/color] (ChannelValue[a]==0) TrueIntensity[a]=0;[color=#7E7E7E]// if were in an evening storm dont reset intensity (it would go to flicker point and possibly flicker)[/color]
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (DimOrder[a]==0){
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a]))*(1-((CloudCover-(StepSize-(StepSize/4)*loopCount))/100))));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (DimOrder[a]==1){
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a]))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/4)*loopCount))/100))));
}
}
loopCount++;
[color=#CC6600]if[/color] (loopCount>4) loopCount=1;
}
[color=#7E7E7E]//enable a flag sent from controller to triger a storm, i.e. IsStorm=true[/color]
[color=#7E7E7E]// set channel intensities for all but white with random walk continuing from above using static variable so should be seamless[/color]
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (((Cloud==[color=#CC6600]true[/color]) && (IsStorm==[color=#CC6600]true[/color])) || ((Cloud==[color=#CC6600]false[/color]) && (IsStorm==[color=#CC6600]true[/color]))){
[color=#7E7E7E]//current else statement covers possibility of triggering storm from controller (i.e. not coming out of a cloud) but remember you need to flag wtrigger as TRUE when you do this[/color]
[color=#CC6600]if[/color] (StormAdvance==[color=#CC6600]false[/color]){[color=#7E7E7E]//Every 1 second duing a storm change intensity, clouds are movin fast baby[/color]
[color=#CC6600]return[/color];
}
StormAdvance=[color=#CC6600]false[/color];[color=#7E7E7E]//reset so we run again in 1 second.[/color]
[color=#CC6600]if[/color] (elapsedTime>=StormEnd){ [color=#7E7E7E]//if were done with the storm we need to stop this loop, but were probably still cloudy so dont mess with that here[/color]
IsStorm=[color=#CC6600]false[/color];
Counter=0;
[color=#CC6600]return[/color];
}
[color=#CC6600]if[/color] (loopCount==1){
PriorCloudCover=CloudCover; [color=#7E7E7E]//e.g. PriorCloudCover=CloudCover with no float math to screw things up[/color]
LastStepSize=StepSize;
StepSize=([color=#CC6600]random[/color](5,21));[color=#7E7E7E]// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)[/color]
[color=#CC6600]if[/color] (([color=#CC6600]random[/color](0,2)!=1)) StepSize=-(StepSize);
[color=#CC6600]if[/color] ((CloudCover+StepSize)>=100){[color=#7E7E7E]//cannot shut off lights more than 100% so limit here[/color]
StepSize=(100-CloudCover);
Counter++;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] ((CloudCover+StepSize)<=0){[color=#7E7E7E]//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down[/color]
StepSize=-(StepSize);
[color=#CC6600]if[/color] (Counter>=2) Counter-=[color=#CC6600]random[/color](0,2);[color=#7E7E7E]//since we got bright... lets further delay and randomize strike occurence [/color]
}
[color=#CC6600]if[/color] (Counter>(Severity+2)) Counter=0;[color=#7E7E7E]//allow if to accumulate on ocassion to train strike sequences 2-3 in a row but then dump it [/color]
CloudCover=CloudCover+StepSize;
}
[color=#CC6600]if[/color] ((Counter>=(Severity+[color=#CC6600]random[/color](-1,4))) && (StrikeNow==[color=#CC6600]false[/color])) {[color=#7E7E7E]//this is where a storm is triggered. Counter indexes when cloud cover reaches 100 on the random walk[/color]
[color=#7E7E7E]//to change the frequency of lightning strikes increase or decrease the number comparison for counter in the if statement above (larger #== less storms).[/color]
[color=#CC6600]byte[/color] RandomStriker;
RandomStriker=[color=#CC6600]random[/color](1,11);
[color=#CC6600]if[/color] (RandomStriker>4){
StrikeNumber=([color=#CC6600]random[/color](2,11)); [color=#7E7E7E]//random high =x-1 so max strike =12 each strike requires a duration and a delay thus StrikeMaster is 18 positions[/color]
[color=#7E7E7E]//ensure the array is zeroed out past the last position required for this strike pattern so each pattern is only as long as generated[/color]
[color=#7E7E7E]//Array is in pairs, position in arry of (0,1) (2,3) etc as strike (delay,duration)[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0;a<20;a++){
[color=#CC6600]if[/color] (a>=(StrikeNumber*2)){
StrikeMaster[a]=0;
}
[color=#CC6600]if[/color] (a%2==0){
[color=#CC6600]if[/color] (a==0){
StrikeMaster[a]=[color=#CC6600]random[/color](300,1601);[color=#7E7E7E]//no need for random here but I am leaving it since I wrote it that way. This must be independent from a=2,4,6 etc...[/color]
}
[color=#CC6600]else[/color] {
StrikeMaster[a]=(StrikeMaster[(a-2)]+[color=#CC6600]random[/color](200,1401));[color=#7E7E7E]//position 0,2,4,6,8.. is strike delay[/color]
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color](a%2!=0){
StrikeMaster[a]=[color=#CC6600]random[/color](50,110);[color=#7E7E7E]//position 1,3,5,7,9... is strike duration (I tried real lightning strike durations and its too short this is adjusted for visual effect[/color]
}
}
StrikeNow=[color=#CC6600]true[/color]; [color=#7E7E7E]//Trigger to start strike sequence in loop[/color]
StrikeStart=[color=#CC6600]millis[/color]();[color=#7E7E7E]//set timer to "zero" now- sequence will start in loop after this function[/color]
StrikeCount=0;
}
Counter=0;
}
[color=#CC6600]if[/color] (Severity>5){
[color=#CC6600]for[/color] ([color=#CC6600]int[/color] a=0;a<8;a++) {
[color=#CC6600]if[/color] (ChannelValue[a]==0) TrueIntensity[a]=0;
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (Wchannel[a]==1){[color=#7E7E7E]//if were in a storm but not a severe storm constrain whites to 50% of Insolation intensity[/color]
[color=#CC6600]if[/color] (DimOrder[a]==0){
TrueIntensity[a]=(flicker[a]+(((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a])/4))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100))));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (DimOrder[a]==1){
TrueIntensity[a]=(flicker[a]+(((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a])/4))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (Wchannel[a]==0){[color=#7E7E7E]//if were blue, we chase as for a cloud[/color]
[color=#CC6600]if[/color] (DimOrder[a]==0){
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100))));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (DimOrder[a]==1){
TrueIntensity[a]=(flicker[a]+(((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
}
}
}
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (Severity<=5){[color=#7E7E7E]// severe storms occur throughout the day, but EVERY storm after sunset is severe...[/color]
[color=#CC6600]for[/color] ([color=#CC6600]int[/color] a=0;a<8;a++) {
[color=#CC6600]if[/color] (ChannelValue[a]==0) TrueIntensity[a]=0;
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (Wchannel[a]==1){[color=#7E7E7E]//if were white we need to be off in a storm[/color]
TrueIntensity[a]=0;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (ChannelValue[a]==0){[color=#7E7E7E]//if this light channel is dark... e.g. after sunset for this channel- it produces no cloud effect[/color]
TrueIntensity[a]=0;
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (Wchannel[a]==0){[color=#7E7E7E]//if were not shut off in a strom and not after our daylight period (this channel) then we produce storm light sequences.[/color]
[color=#CC6600]if[/color] (DimOrder[a]==0){[color=#7E7E7E]//if we dim first... do it[/color]
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100))));
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (DimOrder[a]==1){[color=#7E7E7E]//else we dim second[/color]
TrueIntensity[a]=(flicker[a]+(((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
}
}[color=#7E7E7E]//end of CWHchannel==0 being true[/color]
}[color=#7E7E7E]//end of for loop in severity <5 == true loop[/color]
}[color=#7E7E7E]//end severity compairson loop no more else statements[/color]
loopCount++;
[color=#CC6600]if[/color] (loopCount>3) loopCount=1;
}[color=#7E7E7E]//end of storm if loop[/color]
}[color=#7E7E7E]//End Weather function[/color]
[color=#7E7E7E]//CloudStart drops light intensity to 50% of whatever daylight setting is to start the cloud at 50[/color]
[color=#CC6600]int[/color] CloudStart([color=#CC6600]long[/color] StartTime){
[color=#CC6600]byte[/color] elapsed;
elapsed=(elapsedTime-StartTime);[color=#7E7E7E]//counts up since we start this at elapsedTime=StartTime and StartTime is fixed[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<8; a++){
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])((ChannelValue[a]-flicker[a])))*(1-(elapsed*2.5)/100)));
[color=#CC6600]if[/color] (elapsed>=20);
Cloud=[color=#CC6600]true[/color];[color=#7E7E7E]//start the cloud[/color]
}
[color=#CC6600]return[/color] 50;[color=#7E7E7E]//set CloudCover to 50 [/color]
}[color=#7E7E7E]//end CloudStart function[/color]
[color=#7E7E7E]//StartStorm sets up duration and severity of storm. Its currently limited to 90-600 sec in length- it will rarely be lower than 3 minutes[/color]
[color=#CC6600]byte[/color] StartStorm([color=#CC6600]int[/color] MaxLength, [color=#CC6600]boolean[/color]& trigger, [color=#CC6600]long[/color]& EndTime){
[color=#CC6600]byte[/color] LightningIntensity;
[color=#CC6600]int[/color] StormDuration;
MaxLength-=120;[color=#7E7E7E]//remove 2 mins from longest storm so that we end up with 2 minutes of cloud after the storm before the sky clears to daylight[/color]
[color=#CC6600]if[/color] (MaxLength>720){
MaxLength=720;[color=#7E7E7E]//modify local variable[/color]
StormDuration=[color=#CC6600]random[/color]((MaxLength/3),(MaxLength+1));
EndTime=(elapsedTime+StormDuration);[color=#7E7E7E]//Set by reference StormEnd static variable in weather[/color]
}
[color=#CC6600]else[/color] {
StormDuration=[color=#CC6600]random[/color]((MaxLength/2),(MaxLength+1));
EndTime=(elapsedTime+StormDuration);[color=#7E7E7E]//Set by reference StormEnd static variable in weather[/color]
}
[color=#CC6600]if[/color] (elapsedTime<midDay){
LightningIntensity=[color=#CC6600]random[/color](3,11);[color=#7E7E7E]//morning storms are generally less severe[/color]
}
[color=#CC6600]else[/color] [color=#CC6600]if[/color] (elapsedTime>midDay){[color=#7E7E7E]//afternoon storms are more likely to be severe (every 10-15 sec or less) to about once in a minute or maybe less[/color]
[color=#CC6600]if[/color] (elapsedTime>(set-rise)){[color=#7E7E7E]//Storms after sunset are always severe... it just looks too cool![/color]
LightningIntensity=3;
}
[color=#CC6600]else[/color] {
LightningIntensity=[color=#CC6600]random[/color](3,8);
}
}
trigger=[color=#CC6600]true[/color];
[color=#CC6600]return[/color] (LightningIntensity);
}
[color=#7E7E7E]//End Storm Start Function[/color]
[color=#7E7E7E]//Similar to Cloud start but in reverse... now ramp intensity from wherever we were at the end of the cloud to the value set by Insolation[/color]
[color=#CC6600]void[/color] ClearSky([color=#CC6600]int[/color] CloudPercent, [color=#CC6600]long[/color] TerminationTime)
{
[color=#CC6600]byte[/color] elapsed=(elapsedTime-TerminationTime);[color=#7E7E7E]//Counts up from the scheduled end of the cloud in seconds[/color]
[color=#CC6600]float[/color] slope=(CloudPercent/30);[color=#7E7E7E]//Just calculate how much to increment every second to go from CloudCover to clear sky (CloudCover of zero)[/color]
[color=#CC6600]float[/color] LightAdvance;
LightAdvance=(CloudPercent-(slope*elapsed));[color=#7E7E7E]//were reducing CloudCover from start to zero over 10 seconds.[/color]
[color=#CC6600]for[/color] ([color=#CC6600]byte[/color] a=0; a<8; a++){
TrueIntensity[a]=(flicker[a]+((([color=#CC6600]float[/color])(ChannelValue[a]-flicker[a]))*(1-(LightAdvance/100))));
}
[color=#CC6600]if[/color] (elapsed>=30){[color=#7E7E7E]//at this point lights are back to full Insolation setting so cancel the cloud and start waiting for the next one[/color]
Cloud=[color=#CC6600]false[/color];[color=#7E7E7E]//stop the cloud we are now outside of a true condition in the if loop so it will now stay false and lights are back on[/color]
IsStorm=[color=#CC6600]false[/color];[color=#7E7E7E]//just to be redundant this is not called from a storm... [/color]
}
}[color=#7E7E7E]//End Clear Sky function[/color]
[color=#CC6600]byte[/color] [color=#CC6600]MoonPhase[/color]()
{
[color=#CC6600]int[/color] m,d,y;
[color=#CC6600]int[/color] yy,mm;
[color=#CC6600]long[/color] K1,K2,K3,J,V;
[color=#CC6600]byte[/color] PWMvalue;
m = [color=#CC6600]month[/color]();
d = [color=#CC6600]day[/color]();
y = [color=#CC6600]year[/color]();
yy = y-((12-m)/10);
mm = m+9;
[color=#CC6600]if[/color] (mm>=12) mm -= 12;
K1 = 365.25*(yy+4712);
K2 = 30.6*mm+.5;
K3 = [color=#CC6600]int[/color]([color=#CC6600]int[/color]((yy/100)+49)*.75)-38;
J = K1+K2+d+59-K3;
V = (J-2451550.1)/0.29530588853;
V -= [color=#CC6600]int[/color](V/100)*100;
V = [color=#CC6600]abs[/color](V-50);
PWMvalue = 4*[color=#CC6600]abs[/color](50-V); [color=#7E7E7E]// 5.12=100% 4=~80%[/color]
[color=#7E7E7E]//pinMode(lowATOPin,OUTPUT);[/color]
[color=#7E7E7E]//return (PWMvalue*100)/255; //output is 0-100[/color]
[color=#CC6600]return[/color] PWMvalue;[color=#7E7E7E]//output is 0-255 [/color]
}
[color=#7E7E7E]//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************[/color]
[color=#7E7E7E]//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice. [/color]
[color=#7E7E7E]//this code is being used freely with attribution to Micahel Rice in accord with his request[/color]
[color=#7E7E7E]// A big thank you for these library functions. Its great! [/color]
[color=#7E7E7E]//convert degrees to seconds of arc[/color]
[color=#7E7E7E]// decimal degrees[/color]
[color=#CC6600]long[/color] ddToSeconds([color=#CC6600]float[/color] dd){
[color=#CC6600]return[/color] dd * 3600.0;
}
[color=#7E7E7E]//Degrees, minutes, seconds[/color]
[color=#CC6600]long[/color] dmsToSeconds([color=#CC6600]int[/color] d, [color=#CC6600]unsigned[/color] [color=#CC6600]char[/color] m, [color=#CC6600]unsigned[/color] [color=#CC6600]char[/color] s){
[color=#CC6600]long[/color] ret;
ret = labs(([color=#CC6600]long[/color])d);
ret = ret * 3600L + 60L * m + s;
ret = (d<0L) ? -ret : ret;
[color=#CC6600]return[/color] ret;
}
[color=#7E7E7E]/* ------------------------------------------------------------------------------------------------[/color]
[color=#7E7E7E] 'Equation of Time'[/color]
[color=#7E7E7E] We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.[/color]
[color=#7E7E7E] The returned value is in seconds.[/color]
[color=#7E7E7E]*/[/color]
[color=#CC6600]int[/color] equation_of_time([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] dt){
[color=#CC6600]double[/color] t;
dt -= 192540UL; [color=#7E7E7E]// refer to Jan 3 2000 05:29 (first periapsis)[/color]
dt %= _tropical_year;
t = dt;
t /= _tropical_year;
t *= 6.283185307179586;
t = -459.27672 * [color=#CC6600]sin[/color](t) + 575.333472 * [color=#CC6600]sin[/color](2.0 * t + 3.588414);
[color=#CC6600]return[/color] t;
}
[color=#7E7E7E]/*[/color]
[color=#7E7E7E] 'Solar Noon' adjusts the passed time stamp to the time (GMT) of local solar noon.[/color]
[color=#7E7E7E] The accuracy is about 40 seconds (set by the equation of time).[/color]
[color=#7E7E7E]*/[/color]
[color=#CC6600]void[/color] SolarNoon([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] * dt){
[color=#CC6600]long[/color] r;
[color=#7E7E7E]// Set stamp to noon GMT[/color]
*dt /= 86400UL;
*dt *= 86400UL;
*dt += 43200UL;
[color=#7E7E7E]// adjust for equation of time, at noon GMT[/color]
*dt -= equation_of_time(*dt);
[color=#7E7E7E]// rotate to our longitude[/color]
r = longitude / 15L;
*dt -= r;
}
[color=#7E7E7E]/* -----------------------------------------------------------------------------------------------[/color]
[color=#7E7E7E] 'Solar Declination'[/color]
[color=#7E7E7E] Returns declination in radians[/color]
[color=#7E7E7E] Accurate to within 50 arc-seconds[/color]
[color=#7E7E7E]*/[/color]
[color=#CC6600]double[/color] SolarDeclination([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] dt){
[color=#CC6600]double[/color] y;
dt %= _tropical_year;
y = dt;
y /= _tropical_year; [color=#7E7E7E]// fractional year[/color]
y *= 6.283185307179586;
y=0.006918-0.399912*[color=#CC6600]cos[/color](y)+0.070257*[color=#CC6600]sin[/color](y)-0.006758*[color=#CC6600]cos[/color](y*2)+0.000907*[color=#CC6600]sin[/color](y*2)-0.002697*[color=#CC6600]cos[/color](y*3)+0.00148*[color=#CC6600]sin[/color](y*3);
[color=#CC6600]return[/color] y;
}
[color=#7E7E7E]/* ------------------------------------------------------------------------------------------------[/color]
[color=#7E7E7E] Return the period between sunrise and sunset, in seconds.[/color]
[color=#7E7E7E] At high latitudes around the time of the solstices, this could be zero, or all day.[/color]
[color=#7E7E7E]*/[/color]
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] daylightseconds([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] dt){
[color=#CC6600]float[/color] l, d, e;
[color=#CC6600]long[/color] n;
d = -SolarDeclination(dt); [color=#7E7E7E]// will be positive in Northern winter[/color]
l = latitude / _sec_rad; [color=#7E7E7E]// latitude in radians[/color]
e += 60.0 * l * [color=#CC6600]tan[/color](l + d); [color=#7E7E7E]// latitudinal error[/color]
d = [color=#CC6600]tan[/color](l) * [color=#CC6600]tan[/color](d); [color=#7E7E7E]//[/color]
[color=#CC6600]if[/color](d>1.0) [color=#CC6600]return[/color] 86400UL;
[color=#CC6600]if[/color](d < -1.0) [color=#CC6600]return[/color] 0UL;
d = [color=#CC6600]acos[/color](d);
d /= _zenith;
n = 86400UL * d;
n += e;
[color=#CC6600]return[/color] n;
}
[color=#7E7E7E]/* ------------------------------------------------------------------------------------------------[/color]
[color=#7E7E7E] Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).[/color]
[color=#7E7E7E] Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be[/color]
[color=#7E7E7E] returned if the sun is above the horizon all day, and -1 if the sun is below the horizon[/color]
[color=#7E7E7E] all day.[/color]
[color=#7E7E7E]*/[/color]
[color=#CC6600]char[/color] SunRiseSet([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] * dt, [color=#CC6600]char[/color] set){
[color=#CC6600]unsigned[/color] [color=#CC6600]long[/color] daylen;
daylen = daylightseconds(*dt);
[color=#CC6600]if[/color](daylen == 86400UL) [color=#CC6600]return[/color] 1; [color=#7E7E7E]// there is no 'night' today (midnight sun)[/color]
[color=#CC6600]if[/color](daylen == 0UL) [color=#CC6600]return[/color] -1; [color=#7E7E7E]// there is no 'day' today[/color]
*dt /= 86400UL;
*dt *= 86400UL;
*dt += 43200UL; [color=#7E7E7E]// set the time stamp to 12:00:00 GMT[/color]
*dt -= daylen / 2; [color=#7E7E7E]// sunrise at the prime meridian[/color]
[color=#CC6600]if[/color](set) *dt += daylen; [color=#7E7E7E]// sunset at the prime meridian[/color]
*dt -= equation_of_time(*dt);
[color=#7E7E7E]//*dt -= longitude / 15.0; // rotate to our own meridian[/color]
[color=#CC6600]return[/color] 0;
}
[color=#7E7E7E]// 'short' forms of SunRiseSet[/color]
[color=#CC6600]char[/color] SunRise([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color]* when){
[color=#CC6600]return[/color] SunRiseSet(when, 0);
}
[color=#CC6600]char[/color] SunSet([color=#CC6600]unsigned[/color] [color=#CC6600]long[/color]* when){
[color=#CC6600]return[/color] SunRiseSet(when, 1);
}
[/quote]
Code: Select all
// Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 1