I apologize for a VERY long post..... but I am stuck and its not a normal behavior and the error is complex... so lets start with the good part. I corrected the overflow of the array so I don't think this is the issue... but am confused to say the least.
Ok... as an aid to people wanting to try this, and to illustrate my problem. Here is the serial output from a debugging session after I got everything working in the CalcSun() function which does the majority of the work in terms of sunrise sunset, correcting individual channels to the offset values you want (i.e. blues on before whites and off later), calculates the number of clouds (0,4,5,6,7,8) i.e. none or 4-8, calculates the % of the day that will be cloudy, then divides it up into random length clouds and spaces them randomly throughout the day such that days with less clouds will be correspondingly less overcast as a % total of the day length, i.e. the AVERAGE cloud length will be about the same every day, so regardless of Total cloud # you will get about the same length of clouds, i.e. low cloud days will not simply have REALLY long clouds, they instead will be correspondingly shorter. It then calculates how much daylight to stuff between the clouds (randomly but so that the clouds stay within daylight hours with daylight prior to the first cloud and after the last).
The MAJOR problem I am now seeing is that somehow the processor is changing when it thinks now() is! Which is I thought something that would be impossible to achieve given that NOTHING in my code plays with this. I wonder if I am somehow getting an update from the main controller which is corrupted because of all the serial debugging going on? Would this be possible? I do HAVE the getRTC statements in the setup and loop so it I think it synching but this is beyond me. So I am going to post the code but Its CURRENTLY BROKEN> do not load it until I can figure this out.
What happens is when I first reboot the PWMBoard it executes it perfectly (see serial debugging immediately below) then it gets stuck for a few seconds, and seems to change now() by about 42000 seconds (see serial debugging posted below the code but this came off serial about 5-10 seconds after I capture the first and NOTHING else came off serial... so it running, getting stuck.. then seemingly rebooting and then has the wrong system clock time.
I have no idea if the change in time is causing the reboot or is a symptom of a reboot- seems to be a symptom because the change is NOT enough to have pushed the controller into a new day which would trigger the recalculation of CalcSun and result in serial debugging showing this running again (which it does). Since the change in now() is not pushing us to a new day.. the only other way CalcSun() would rerun (it obviously does) , is a reboot, so I think its getting corrupted somehow and rebooting, then its further screwed because now() becomes somehow WRONG> The results of the serial debugging after the pause and reboot is posted below the code, keep in mind this comes off serial about 5 seconds after the first execution.... but the time changes by 11.75 hours!
Serial debugging out put followed by code followed by symptom of problem in second serial output
Please note that the CODE is SUBSTANTIALLY ALTERED from prior versions, I fixed some things that were causing me problems and vastly improved some of the code from both a readability and simplicity standpoint. CalcSun() is VERY different than prior versions. The rest has small distributed changes.
One thing to see, the timing loop is working, if you look at the bottom of the second serial output you will see insolation advance occurs every third time that storm advance occurs (i.e. storm advance is every second, cloud is every two, and insolation is every three) so the program is actually working but for the weird issue with the "reboot" and the change in now() which also corrupts the sunrise and sunset values which is REALLY weird, because they should NEVER be the values they become for my lat lon coordinates, and its really NOT important what time of the day you feed sunrise and sunset the time, it just figures out what day it is and when sunrise and sunset are for THAT day, NO DAY IN HISTORY should have the values I get after the restart!!
Hope I can figure this out.
Here is serial debugging.
CalSun Run Now
Hours:Mins
7
15
CalSun now()=1337238940 second value on restart is CalSun now()=1337196544
newDay=390528040
Local Elapsed Seconds from NewDay--rise=
22104
Local Elapsed Seconds from NewDay--set=
74296
Rise/Set a=0
Value=21504Slope0.000058840141
Rise/Set a=1
Value=74296Slope0.000060192995
Rise/Set a=2
Value=17904Slope0.000051848311
Rise/Set a=3
Value=79696Slope0.000049872879
Rise/Set a=4
Value=22104Slope0.000060192995
Rise/Set a=5
Value=74896Slope0.000058840141
Rise/Set a=6
Value=18504Slope0.000052895889
Rise/Set a=7
Value=80296Slope0.000048940563
Rise/Set a=8
Value=17604Slope0.000051339921
Rise/Set a=9
Value=81496Slope0.000047176733
Rise/Set a=10
Value=22104Slope0.000060192995
Rise/Set a=11
Value=74296Slope0.000060192995
DayLength
52192
CloudsTotal
4
Overcast
0.10
CloudLength 1304
a is even fraction=0.41
Cloudmaster position=0
CloudMaster=534
a is odd fraction=0.41
Cloudmaster position=2
Cloud Master=2073
a is even fraction=0.56
Cloudmaster position=4
CloudMaster=730
a is odd fraction=0.56
Cloudmaster position=6
Cloud Master=1877
SunSegment=9394
even a; CloudMaster position number, 1
is= 34128
odd a; CloudMaster position number, 3
is= 40892
even a; CloudMaster position number, 5
is= 54795
odd a; CloudMaster position number, 7
is= 59680
here is cloud master in is entirety
534
34128
2073
40892
730
54795
1877
59680
0
0
0
0
0
0
0
0
now we print start and end times for clouds
34128
34662
40892
42965
54795
55525
59680
61557
0
0
0
0
0
0
0
0
Code: Select all
//Written by Matthew Hockin.
//Intended for use and distribution to the open source Reef Angel community- but freely available to all.
//Caveat Emptor- buyer beware- no warranties implied or intended :)
//If you want to use this code or pieces of it elsewhere please simply acknowledge the source and author.
//You MUST have installed the SWFLTEK Ephmerma library- this library was written by Michael Rice (swfltek.com)
//its source code and distribution rights are stated in the .h file - Thanks to Michael for doing such a great job
#include <Time.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <avr/wdt.h>
#include <SWFLTEK_EPHERMA.h>
//CHANGE this to the number of seconds your time zone is offset from GMT (WITHOUT REGARD TO DAYLIGHT SAVINGS TIME)
int GMToffset=25200;
//GLOBAL Variables YOU NEED TO CHANGE
boolean ApplyDST=true;//CHANGE THIS IF YOUR NOT USING DST
byte ChMax[]={220,220,220,220,220,0};//max values you want to hit at solar noon scaled as byte values for LED output PWM write
byte flicker[]={30,30,30,30,10,0};//need to input actual values here for flicker point on all channels in PWM expansion box
boolean Wchannel[]={1,0,1,0,0,0}; //use 1 to designate white channel (i.e. off during storm and used for lightning). Array corresponds to PWM channel 0-5 in order
//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
//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
//(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.
byte DimOrder[]={0,0,1,1,0,0};
//*******************GLOBAL VARIABLE DECLERATIONS*************************************
//Unless your planning on editing the program DO NOT CHANGE ANYTHING HERE
long elapsedTime;//used multiple places as elapsed since new day.
unsigned long elapsedMillis;//used in strike timing
//Globals Vavriables for ReefAngel PWM Cloud, PWM Settings, Epherma Lat/Long Sunrise/Sunset
unsigned long rise;
unsigned long set;
unsigned long newDay;// time in seconds since 2000 adjusted for DST (if used) at a new day (12:00:00am)
long ChRiseSet[12];
float ChSlope[12];
long midDay;// exactly 1/2 way between rise and set, i.e. my take on solar noon- which its not...
byte PWMports[] ={
3,5,6,9,10,11};
byte ChannelValue[5];// Array to store current setpoint for PMW control of output to ch (0,1,2,3,4,5)
byte ChInt[5];
unsigned long StrikeStart;//timer to keep track of strike sequence
int StrikeMaster[17];//Array to hold random strike pattern generated by weather array is sized to MAX needed given strike patter generator
byte StrikeNumber;//place to hold total number of strikes this sequence
byte StrikeCount;//used in loop for strike intensity varaition when StrikeNow is triggered true within the Weather function starting a strike sequence in the loop
byte cmdnum=255;
byte datanum=255;
byte dow=0;//day of week
boolean trigger; //used a few places as a switch to ensure things only run 1x when needed trigger 2 is for daily reset of sunrise sunset
byte strikePattern, strikeTime;//used in Lightning() for timing of particular instance of strike
boolean isDST;
boolean Cloud;
boolean CloudToday;
boolean StrikeNow;
boolean IsStorm;// what they seem, is it DST?, Are we in a cloud?
byte CloudsTotal;
byte strikeCount;//required to know how large the array for CloudMaster will be- I need to use Vectors.... I know.
long *pCloudPoint;//Use global to point to static memory location of array CloudMaster created 1/x day in calcSun.
long lastmillis;// variable to track millis to enable cloud and insolation loop restriction by time
boolean CloudAdvance;//cloud timer for light effect advance
boolean StormAdvance;//storm timer for light effect advance
boolean InsolationAdvance;//when true we recalculate light intensity during clear sky
byte counter;//used to track millis advance for insolation,cloud trigger
//****************************************
//END HEADER/Global Variable declaration//
//****************************************
//Setup
void setup()
{
Serial.begin(57600);
// Wire.begin(8);
// Wire.onReceive(receiveEvent);
// Wire.onRequest(requestEvent);
randomSeed(analogRead(0));
pinMode(3,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
pinMode(11,OUTPUT);
//wdt_enable(WDTO_1S);
setSyncProvider(RTC.get); // the function to get the time from the RTC
setSyncInterval(SECS_PER_HOUR); // Changed to sync every hour.
isDST=false;
trigger=false;
dow=0;
StrikeNow=false;
Cloud=false;
IsStorm=false;
lastmillis=millis();//start our millis timer now
counter=0;
}
//End Setup
//*********************************************************************************************************************************
//*********************************************************************************************************************************
//Loop
void loop()
{
wdt_reset();
//Serial.println(ChannelValue[0],DEC);
if (cmdnum!=255){
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
if (dow!=day()){ //be aware that during DST times the new day calculation will occur 1 hour early....but its corrected out
trigger=true;//on reset this will also be true as numbering is 1-31 max
Serial.println("We set trigger to true");
dow=day();
}
//Use millis to enable tracking of time interval
//this will ensure at least minimally 1 sec intervals but may be longer if program is slow during a long calculation but at least never shorter.
if ((millis()-lastmillis)>=1000){
lastmillis=millis();
counter+=1;
StormAdvance=true;
Serial.println("StormAdvance=true");
if (counter%2==0){
CloudAdvance=true;
Serial.println("CloudAdvane=true");
}
if (counter%3==0){
InsolationAdvance=true;
Serial.println("InsolationAdvance=true");
}
if (counter==255) {
counter=0;
Serial.println("Counter reset");
}
}
// now run through rise/set calculation and then intensity set and finally weather overlay when required
if (trigger==true) CalSun();
if (InsolationAdvance==true) Insolation();
Weather();//due to variable storm vs cloud update timing this must be run from loop without condition
//check to see if were need to have a lightning strike
if (StrikeNow==false){
StrikeStart=millis();
StrikeCount=0;
}
else if (StrikeNow==true){
elapsedMillis=(millis()-StrikeStart);
if (StrikeCount>StrikeNumber){
StrikeNow=false;
StrikeCount=0;
elapsedMillis=0;
}
int additiveDelay;//value combining dealys across array to the stike number were at
for (byte a=0; a=StrikeCount;a++){//StrikeCount goes by 2 and starts at zero... so just add it up
additiveDelay+=StrikeMaster[a];//if were at zero we get 1 val... if were at 2 we get first two delays
}
if (elapsedMillis>=additiveDelay){
byte intensity;
intensity=random(155-255);// this little bit should generate a randomly bright flash variation between the series of flashes in StrikeMaster
for (byte a=0; a<6; a++){
if (Wchannel[a]==1) analogWrite(PWMports[a],intensity);
}
delay(StrikeMaster[(strikeCount+1)]);
StrikeCount+=2;
}
}
//track time in seconds-dont move this part up in the loop it really should stay below the rest as DST is not calculated until CalSun is run 1 time.
elapsedTime=((now()-946684800)-newDay);//this is uncorrected for DST, the ONLY thing that is, since its all relative... is the actual sunrise and set seconds
//Now that we have generated our sun pattern, Weather as clouds, and possibly a storm, and possibly lightning each of which override the prior channel setting
//lets actually make some light.
for (byte a=0;a<6;a++){
analogWrite(PWMports[a],ChannelValue[a]);
}
}
//End Loop
//*********************************************************************************************************************************
//*********************************************************************************************************************************
//Standard PWM Functions Receive/Process
void receiveEvent(int howMany) {
wdt_reset();
if (howMany==5){
byte cmd1, cmd2, cmd3, cmd4, cmd5;
cmd1=Wire.read();
cmd2=Wire.read();
cmd3=Wire.read();
cmd4=Wire.read();
cmd5=Wire.read();
if (cmd1=='$' && cmd2=='$' && cmd3=='$'){
cmdnum=cmd4;
datanum=cmd5;
//Serial.println(cmd4,DEC);
//Serial.println(cmd5,DEC);
}
}
else{
for (int a=0;a<howMany;a++){
Wire.read();
}
}
}
void ProcessCMD(byte cmd, byte data)
{
wdt_reset();
}
//End Standard Functions
//*********************************************************************************************************************************
// Function to run Epherma Calc for rise/set/noon/moon phase
void CalcDST(byte D, byte M, byte dow)
{
//January, february, and december are out.
if (M < 3 || M > 11){
isDST=false;
}
//April to October are in
else if (M > 4 && M < 11){
isDST=true;
}
else{
int previousSunday = D - dow; // Sunday=1 Sat=7
if (M==3 && previousSunday > 7){
isDST=true;
}
else if (M==11 && previousSunday<=0){
isDST=false;
}
}
}
void CalSun()
{
Serial.println("CalSun Run Now");
trigger=false;
// Every day at midnight, we calculate rise, set, time of highest sun, moon phase, or we do this if system has reset
if (ApplyDST==true){
CalcDST(day(),month(),weekday());
}
//Using time library from Arduino.time_t now(); returns seconds since 1970
//Library states #seconds to subtract to bring to Jan 1 2000 as origin for time is
//#define SECS_YR_2000 (946684800) the time at the start of y2k
byte hours;//
byte minutes;//
hours=hour();
minutes=minute();
Serial.println("Hours:Mins");
Serial.println(hours);
Serial.println(minutes);
//Seems weird, but actually DO NOT correct new Day for DST
newDay=(now()-(946684800+(3600*hours)+(60*minutes)));//local time uncorrected for DST
rise=(newDay+GMToffset);// Dont screw with DST in sunrise sunset input its NOT part of GMT
set=rise;//were not converted yet- library uses POINTERS.. to modify these values
Serial.print("CalSun now()=");
Serial.println(now());
//Calculate Latitude and Longitude converting from Degree, Minute, Seconds to decimal
latitude=dmsToSeconds(40,44,11); //Set to about the latitude of Salt Lake City
longitude=dmsToSeconds(-111,48,32); //Set to Salt lake City, or MST zone, USA or there abouts- actually the AT921 remote weather station in Salt Lake City UT. Random web grab.
//Calculate rise time and set time using Epherma Library
SunRise(&rise);//call to Epherma Library using reference- memory is modified by Epherma
SunSet(&set);//Call to Epherma library using reference- memory position is modified by Epherma
if (isDST==true){//must correct DST NOW by subtracting 1 hour from the offset (i.e. clocks are 1 hr ahead and thus closer to GMT)
rise-=(GMToffset-3600);//during DST rise is 1 hour earlier than calculated i.e. clocks ahead 1 hour 6 am occurs "at 5am" if that makes sense
set-=(GMToffset-3600);
}
else if (isDST==false){
rise-=GMToffset;
set-=GMToffset;
}
Serial.print("newDay=");
Serial.println(newDay);
rise=(rise-newDay);//set to elapsed seconds today
set=(set-newDay);
Serial.println("Local Elapsed Seconds from NewDay--rise=");
Serial.println(rise);
Serial.println("Local Elapsed Seconds from NewDay--set=");
Serial.println(set);
//DONT MOVE THIS LINE ABOVE FINAL rise and set decleration it will SCREW up your rise and set during DST by 1 hour
if (isDST==true) newDay-=3600;//subtract an hour from the time that will be subtracted from GMT i.e. were one hour closer during DST
//*********************UNLESS YOUR TANK LIGHTING IS IDENTICAL IN EVERY WAY TO MINE----YOU NEED TO CHANGE THESE VALUES***************************
//offsets for rise/set all values in seconds offset from calculated rise or set value (-) am offset=longer day****** (-)pm offset=shorter day)
//array order is ch0 am offset, pm offset, ch1 am offset, pm offset etc..
//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
int Choffset[]={
-600,0,-4200,5400,0,600,-3600,6000,-4500,7200,0,0};
//**********************ok now were done changing things here********************************************
//Calculate rise and set times for all channels in equivlants to elapsed seconds from midnight today
//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)
float deltaY=1.570796327;//1/2 * pi as integer by scaling* 10^9 to fill UL
midDay=(((set-rise)/2)+rise);
long HalfDayLength=((set-rise)/2);
//Serial.print("MidDay");
//Serial.println(midDay);
for (byte b=0;b<12;b++){//working as of April 5 2012 serial tested
if (b%2==0){
ChRiseSet[b]=rise+(Choffset[b]);
ChSlope[b]=(deltaY/(float)(HalfDayLength-(Choffset[b])));
Serial.print("Rise/Set a=");
Serial.println(b);
Serial.print("Value=");
Serial.print(ChRiseSet[b]);
Serial.print("Slope");
Serial.println(ChSlope[b], 12);
}
else if (b%2==1){
ChRiseSet[b]=set+(Choffset[b]);
ChSlope[b]=(deltaY/(float)(HalfDayLength+(Choffset[b])));
Serial.print("Rise/Set a=");
Serial.println(b);
Serial.print("Value=");
Serial.print(ChRiseSet[b]);
Serial.print("Slope");
Serial.println(ChSlope[b], 12);
}
}
//***************** to CHANGE THE chance of Clouds actually occuring on a given day************************
byte CloudChance=100;//% Chance of a Cloud every day
//****************************now were done- did you use a value from 0-100 without a decimal?*****************
//once a day, after rise set has been calculated and populated we need to trigger a reset to the Cloud time calculation loop
randomSeed(now()-(random(30000,1000000001))); //so that we are now more trully random
byte RainMaker=random(1,101);
if (RainMaker<=CloudChance){
CloudToday=true;//this is a holding variable to trigger display on the Main currently not implemented
}
else if (RainMaker>CloudChance){
CloudToday=false;//see above comment on CloudToday
return;
}
// to "simplify" things and avoid redundant calculation loops all cloud times are in fraction of day lengths
//*********** this is only executed 1x /day unless power outage and ONLY if last statement is true****************
///ALl cloud set up is done here... weather subroutine just implements it
/*The general strategy for this algorithim is as follows. Calculate random cloud cover as percent of day,
then randomly generate the # of discreet cloud instances for the day,
then determine cloud length by (daylight seconds * percent overcast)/#clouds
then randomize cloud lengths by splitting into groups of twos correcting for if we have odd # of clouds today*/
long dayLength=(set-rise);
Serial.println("DayLength");
Serial.println(dayLength);
// number of clouds possible for the day, max and min
byte CloudsMax=8;//DONT INCREASE BEYOND 8 or it will DIE, or increase array size to handle it
byte CloudsMin=4;//dont use 1 it may or may not work in the next loops and the cloud will likely be very long, i.e. daylength*overcast fraction
CloudsTotal=random(CloudsMin,(CloudsMax+1));
Serial.println("CloudsTotal");
Serial.println(CloudsTotal);
// 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)
byte OvercastMin=((CloudsTotal*10)/5);//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
byte OvercastMax=((CloudsTotal*10)/2);//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
float Overcast=random(OvercastMin,OvercastMax);
Overcast=(Overcast/100);
Serial.println("Overcast");
Serial.println(Overcast);
static long CloudMaster[15];// Set up array to hold start and end times for clouds for the day- dont size it dynamically make it big enough for any day
pCloudPoint=&CloudMaster[0];//use address of pointer to find cloud data in weather, array is declared static to persist throughout day.
// set up start time for clouds
/*The way this is done is to split the total lenght of time for clouds into equal segments and then to randomly chop or add time to the
segments such that cloud length is variable. Then distribute into random parts of the day and fill array with start,duration pairs for clouds*/
randomSeed(analogRead(0));
int CloudLength;
CloudLength=((dayLength*Overcast)/CloudsTotal);//average cloud length
float fraction;
Serial.print("CloudLength ");
Serial.println(CloudLength);
byte b=0;
//using fraction to vary cloud length throughout day fill array CloudMaster[] at positions 0,2,4,6 etc with durations in seconds
for (byte a=0; a<8; a++){
if (a>=CloudsTotal){
for (byte b=CloudsTotal*2; b<16;b++){//zero fill unused array positions on days with less than 8 clouds
CloudMaster[b]=0;
}
break;
}
// if were having an odd # of clouds make sure last one is full length
if ((CloudsTotal%2==1) && (a==(CloudsTotal-1))){
CloudMaster[(a+b)]=CloudLength;
Serial.print("last odd cloud Cloudmaster[a]=");
Serial.println(a+b);
Serial.println(CloudMaster[(a+b)]);
}
// else we make clouds variable in length with every two cloud sets being different in short and long but adding up equally by sets
else if (a%2==0){
fraction=random(20,181);//vary each cloud from 20%-180% of an equal fraction of total cloud for day
fraction=(fraction/100);
CloudMaster[(a+b)]=(CloudLength*fraction);
Serial.print("a is even fraction=");
Serial.println(fraction);
Serial.print("Cloudmaster position=");
Serial.println(a+b);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[(a+b)]);
}
else if (a%2==1){
CloudMaster[(a+b)]=(CloudLength*(2-fraction));
Serial.print("a is odd fraction=");
Serial.println(fraction);
Serial.print("Cloudmaster position=");
Serial.println(a+b);
Serial.print("Cloud Master=");
Serial.println(CloudMaster[(a+b)]);
}
b++;
}
// Now space the clouds out during the day using random fractionation of day segments as per cloud calculation
long SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));//yields amount of sun on either side of a cloud that exactly center the clouds, next part randomizes the centering part
Serial.print("SunSegment=");
Serial.println(SunSegment);
b=0;//counter for indexing # of itterations in loop
byte c=1;
for (byte a=0; a<(CloudsTotal); a++){
// array has been zero filled in unused positions in last routine so dont worry here as were limited to needed space in this for loop
//calculate amount of sun during day (i.e. total day lenth - portion cloudy), divide into equal parts then randomly initiate cloud within sun segment for each part of the day.
if (a%2==0){
fraction=random(10,181);
fraction=(fraction/100);
CloudMaster[c]=(rise+(fraction*(SunSegment))+(SunSegment*b));
Serial.print("even a; CloudMaster position number, ");
Serial.println(c);
Serial.print(" is= ");
Serial.println(CloudMaster[c]);
c=c+2;
}
else if (a%2==1){
b=b+2;
CloudMaster[c]=(rise+(SunSegment*b));
Serial.print(" odd a; CloudMaster position number, ");
Serial.println(c);
Serial.print(" is= ");
Serial.println(CloudMaster[c]);
c=c+2;
}
}
Serial.println("here is cloud master in is entirety");
for (byte a=0;a<16;a++){
Serial.println(CloudMaster[a]);
}
Serial.println("now we print start and end times for clouds");
//just reframe CloudMaster into array of start and end times for clouds (CLoudMaster[]={start,end,start,end,start,end} as progression through day in elapsed seconds from midnight.
for (byte a=0; a<16;a=(a+2)){
long endT, startT;
startT=CloudMaster[(a+1)];
endT=(CloudMaster[a]+startT);
CloudMaster[a]=startT;
CloudMaster[a+1]=endT;
Serial.println(CloudMaster[a]);
Serial.println(CloudMaster[a+1]);
b=b+2;
}
//As the LAST thing we need to do today and only once... frame ChInt array as max value-flicker point... so it can be correctly set in Insolation
for (byte a=0; a<6;a++){
ChInt[a]=(ChMax[a]-flicker[a]);
Serial.println("ChMax[] setpoints in order 0-5 are=")
Serial.println(ChInt[a]);
if (ChMax[a]==0){
ChInt[a]=0;//you should not have a flicker point for an unsued PWM port but just to be sure
Serial.println("zero setting for channels");
Serial.println(a);
}
}//END FUNCTION
void Insolation()
{
InsolationAdvance=false;//reset this flag now that we have entered function
Serial.println("Insolation 3 sec elapsed");
Serial.print("Elapsed Time is=");
Serial.println(elapsedTime);
//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
float Pi=3.1415926;//scale to 10^8
float PiHalf=1.5707963;//scale to 10^8
float secSoFar;//variable to account for seconds elapsed for each channel 1/2 day period from rise-->midDay and midDay-->set
/* using -cos(pi/2+elapsedTime/slope) calculate fractional intensity of each channel throughout the day
use flicker points to adjust minimum intensity to stable light. Turn off lights after set or before rise etc.
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
throughout intensity ramp... more or less ... change intensity every 120 seconds throughout the day*/
if (elapsedTime<midDay){
Serial.println("in first half of day");
for (byte b=0;b<12;b=b+2){
if (elapsedTime>=ChRiseSet[b]){
secSoFar=(elapsedTime-ChRiseSet[b]);//just account for length of every channel 1/2 day and switch at midDay
switch (b){
case 0:
ChannelValue[0]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b])+flicker[b];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 2:
ChannelValue[1]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-1])+flicker[b-1];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 4:
ChannelValue[2]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 6:
ChannelValue[3]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-3])+flicker[b-3];
Serial.println(-cos(PiHalf+(ChSlope[b-3]*secSoFar)),4);
break;
case 8:
ChannelValue[4]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-4])+flicker[b-4];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 10:
ChannelValue[5]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-5])+flicker[b-5];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
}
}
else if (elapsedTime<ChRiseSet[b]){
switch (b){
case 0:
ChannelValue[0]=0;
break;
case 2:
ChannelValue[1]=0;
break;
case 4:
ChannelValue[2]=0;
break;
case 6:
ChannelValue[3]=0;
break;
case 8:
ChannelValue[4]=0;
break;
case 10:
ChannelValue[5]=0;
break;
}
}
}
}
else if (elapsedTime>=midDay){
Serial.println("were in the second half of the day");
for (byte b=1;b<12;b=b+2){
if (elapsedTime<=ChRiseSet[b]){
secSoFar=(elapsedTime-midDay);
switch (b){
case 1:
ChannelValue[0]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-1])+flicker[b-1];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 3:
ChannelValue[1]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 5:
ChannelValue[2]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-3])+flicker[b-3];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 7:
ChannelValue[3]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-4])+flicker[b-4];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 9:
ChannelValue[4]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-5])+flicker[b-5];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
case 11:
ChannelValue[5]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-6])+flicker[b-6];
Serial.println(-cos(PiHalf+(ChSlope[b]*secSoFar)),4);
break;
}
}
else if (elapsedTime>ChRiseSet[b]){
switch (b){
case 1:
ChannelValue[0]=0;
break;
case 3:
ChannelValue[1]=0;
break;
case 5:
ChannelValue[2]=0;
break;
case 7:
ChannelValue[3]=0;
break;
case 9:
ChannelValue[4]=0;
break;
case 11:
ChannelValue[5]=0;
break;
}
}
}
}
Serial.println("Insolation settings=");
Serial.println(ChannelValue[0]);
Serial.println(ChannelValue[1]);
Serial.println(ChannelValue[2]);
Serial.println(ChannelValue[3]);
Serial.println(ChannelValue[4]);
Serial.println(ChannelValue[5]);
}//END function
//This is where modification of insolation by clouds or a storm occurs
//Its worth noting that the clouds simply modify (depending on how much cloud cover there is) the existing light set by insolation as a fractional value
void Weather ()
{
static byte CloudCover; // variable to store value in random walk - declared static to accumulate Cloud effect
static byte PriorCloudCover; //used to "delay" one side of the tank from the other in cloud passing effects
static long StormStart;
static long StormEnd;
static long CloudEnd;
static boolean wtrigger;//use to track the first run of functions to calculate random times that become fixed, see change from cloud to storm for useage
// see insolation elapsed time function for assigmnent of Cloud start times and set of Cloud=true
//check to see if were having a scheduled cloud
int LongestStorm;//used to pass max possible time to storm if loop from cloud within weather function
if (Cloud==false){
for (byte a=0; a<CloudsTotal; a=(a+2)){
if ((elapsedTime>=*(pCloudPoint+a)) && ((elapsedTime+125)<=*(pCloudPoint+(a+1)))){//time needs to be within a cloud segment, not just greater than any start, add 125 to elased to ensure
//that clear sky function can finish before it sets clud to true again.
CloudEnd=*(pCloudPoint+(a+1));
CloudEnd-=120;
Cloud=true;
CloudCover=0;
PriorCloudCover=0;
}
else{
return;
}
}
}
else if ((Cloud==true) && (IsStorm==false)){
if (CloudAdvance==false){//use millis tracker to run this loop every 2 seconds
return;
}
CloudAdvance=false;//reset to false when true so we run this once, until time advance is true again
Serial.println("in a cloud doing stuff");
if (elapsedTime>=CloudEnd){
ClearSky(CloudCover, CloudEnd);
return;
}
/*Use fractional intensity to set minimum value for any channel. Dimming is proportional to actual intensity output
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
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*/
randomSeed(random(-500,500));
byte stepsize;
stepsize=random(-10,11); //this works out to about a percent value change in lighting intensity 0-100 per step...based upon random bounds (+1 to upper bounds if using equal numbers)
PriorCloudCover=CloudCover;
CloudCover+=stepsize;
if (CloudCover>=90){ //arbitrary "STORM" cutoff... i.e. if the sky gets dark enough we must be having a storm- if you dont get enough storms change this value to a lower number
if ((CloudEnd-elapsedTime)>=300){//if storm can last 5 minutes before cloud would end anyways.. do it
IsStorm=true;
wtrigger=true;
LongestStorm=(CloudEnd-elapsedTime);
}
else CloudCover-=(stepsize*2.0);//if not lets get a bit lighter out.
}
else if (CloudCover<=0){//i.e if were bright sky in a cloud.. uhh.. we need a cloud
CloudCover-=(stepsize*1.5);//reflect to less positive value, i.e. we had to be adding a negative so subtract it instead (i.e. add a positive)
}
for (int a=0;a<6;a++){
if (DimOrder[a]==0){
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)(CloudCover/100))))+flicker[a]);
Serial.print("DimOrder 0 setting is");
Serial.println(ChannelValue[a]);
}
if (DimOrder[a]==1){
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)(PriorCloudCover/100))))+flicker[a]);
Serial.print("DimOrder 1 setting is");
Serial.println(ChannelValue[a]);
}
}
}
//enable a flag sent from controller to triger a storm, i.e. IsStorm=true
// set channel intensities for all but white with random walk continuing from above using static variable so should be seamless
else if (((Cloud==true) && (IsStorm==true)) || ((Cloud==false) && (IsStorm==true))){
//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
//do this next part exactly once per storm then loop past
if (wtrigger==true){
StormStart=elapsedTime;
LongestStorm-=120;//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
//*****************To CHANGE THE LENGTH OF THE LONGEST POSSIBLE STORM DURING A CLOUD read next line comments****************************
if (LongestStorm>900) LongestStorm=900;//YOU CAN CHANGE THE value in the comparison and the define to be whatever you want your max lenght to be
////*************************EDIT ABOVE***************************************************************************************************
int StormDuration=random((LongestStorm/4),(LongestStorm+1));//set (min,max) seconds any single Storm can exist currently 1/4 length to full length remaining cloudtime
StormEnd=(StormStart+StormDuration);
wtrigger=false;
}
if (StormAdvance==false){//Every 1 second duing a storm change intensity, clouds are movin fast baby
return;
}
StormAdvance=false;//reset so we run again in 1 second.
Serial.print("in a storm now");
if (elapsedTime>=StormEnd){ //if were done with the storm we need to stop this loop, but were probably still cloudy so dont mess with that here
IsStorm=false;
return;
}
byte stepsize;
stepsize=random(-20,21);
if (StrikeNow==false){
// check to see if we are going to have a lightning strike now
if ((stepsize<(-15)) || (stepsize>15)){ //it is convient to use this random call to generate a strike during a storm for more frequent strikes adjust down
randomSeed(lastmillis); //start random with a new value to avoid repetitive stike generations
StrikeNumber=(random(2,10)); //random high =x-1 so max strike =9 and array=17 for StrikeMaster
//Rather than bother with Vector and dynamic allocation with push/pull use an array big enough to accomadate the largest strike pattern and then
//ensure the array is zeroed out past the last position required for this strike pattern so each pattern is only as long as generated
//Array is Strike Start millis, strike end millis, etc... total of StrikeNumber pairs.
for (byte a=0;a<18;a++){
if (a>=(StrikeNumber*2)){
StrikeMaster[a]=0;
}
else if (a%2==0){
StrikeMaster[a]=random(100,1500);//position 0,2,4,6,8.. is strike delay
}
else if (a%2!=0){
StrikeMaster[a]=random(30,81);//position 1,3,5,7,9... is strike duration- added to StartStrike=StrikeEnd
}
}
StrikeNow=true; //Trigger start of timming loop in "loop" to initiate Strike pattern do this last so were all good on settings
}
strikeCount=0;
}
//get back to setting cloud intensity for storm
PriorCloudCover=CloudCover;
CloudCover+=stepsize;
if (CloudCover>=100){//if were too dark reflect random path to light
CloudCover-=(stepsize);
}
else if (CloudCover<=0){
CloudCover-=(stepsize);//if were 100% intensity of light in a storm reflect random path to less intensity
}
for (int a=0;a<6;a++) {
if (Wchannel[a]==1){
ChannelValue[a]=0;
}
else if (DimOrder[a]==0){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(CloudCover/100))))+flicker[a];
Serial.print("In a storm and DimOrder0 Int are as follows no whites here");
Serial.println(ChannelValue[a]);
}
else if (DimOrder[a]==1){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(PriorCloudCover/100))))+flicker[a];
Serial.print("In a storm and DimOrder1 Int are as follows no whites here");
Serial.println(ChannelValue[a]);
}
}
}//end of storm if loop
}//End Weather function
//THE LAST 2 mins of ANY Storm, or cloud, are used to ramp linearlly to full daylight setting, whatever that is at the given time of day
void ClearSky(byte CloudCover, int CloudEnd)
{
// no need to restrict time to avoid resource hogging, its only called every x seconds from weather.
//I AM ASSUMING THAT everything calling this does so with 120 seconds left in the cloud/storm etc to clear the sky.
//if you do this incorrectly (i.e. change this and dont change it everywhere I am not sure what would happen for certain, but it may cause issues... almost certainly will
int elapsed=(120-((CloudEnd+120)-elapsedTime));//For ease of all other calculations previousl in weather... CloudEnd is set as ArrayEndpoint-120... so I need to add it back here... but only here
int LightNeeded=CloudCover;//Since CloudCover is amount taken away as decimal fraction of InsolationSetting (i.e. 70 produces light at 30% of insolation setpoint... we need to come up 70 to get back.
int slope=((LightNeeded*100)/120);//trying to convert float to decimal math... 70*100=7,000/120=58 as int which is close enough i.e. every second increase by 58 then correct to int with /100
if (elapsed<=120){
IsStorm=false;
Cloud=false;
return;
}
if (InsolationAdvance==true){//using Int converted decimal math we should typically see an advance in intensity as integer every 3 seconds, not much sooner so limit to this interval
int LightAdvance;
LightAdvance=(CloudCover-((elapsed*slope)/100));//were reducing CloudCover from start to zero over 120 seconds every 3 seconds... so this goes from 0-cloud cover over 120 seconds (approximately) and value trends to zero
for (byte a=0; a<6; a++){
ChannelValue[a]=(ChannelValue[a]*(float)(1-(LightAdvance)));//finally need to convert to float to get setpoint- dont think you can avoid the float conversion... but only here is it required
}
}
}//End Clear Sky function
here is the second serial out put some 5-10 seconds after the first
We set trigger to true
CalSun Run Now
Hours:Mins
19
29
CalSun now()=1337196544
newDay=390507140/first value was 390528040 delta is 20,900 seconds (DST correction is 25,200 so its not this and its also not DST which would reduce it to 21600- not 20,900 and it should not be doing that anyway)
Local Elapsed Seconds from NewDay--rise=
43036
Local Elapsed Seconds from NewDay--set=
95196
Rise/Set a=0
Value=42436Slope0.000058875422
Rise/Set a=1
Value=95196Slope0.000060229926
Rise/Set a=2
Value=38836Slope0.000051875705
Rise/Set a=3
Value=100596Slope0.000049898233
Rise/Set a=4
Value=43036Slope0.000060229926
Rise/Set a=5
Value=95796Slope0.000058875422
Rise/Set a=6
Value=39436Slope0.000052924404
Rise/Set a=7
Value=101196Slope0.000048964977
Rise/Set a=8
Value=38536Slope0.000051366796
Rise/Set a=9
Value=102396Slope0.000047199411
Rise/Set a=10
Value=43036Slope0.000060229926
Rise/Set a=11
Value=95196Slope0.000060229926
DayLength
52160
CloudsTotal
8
Overcast
0.20
CloudLength 1304
a is even fraction=0.48
Cloudmaster position=0
CloudMaster=625
a is odd fraction=0.48
Cloudmaster position=2
Cloud Master=1982
a is even fraction=1.34
Cloudmaster position=4
CloudMaster=1747
a is odd fraction=1.34
Cloudmaster position=6
Cloud Master=860
a is even fraction=0.21
Cloudmaster position=8
CloudMaster=273
a is odd fraction=0.21
Cloudmaster position=10
Cloud Master=2334
a is even fraction=0.98
Cloudmaster position=12
CloudMaster=1277
a is odd fraction=0.98
Cloudmaster position=14
Cloud Master=1330
SunSegment=4636
even a; CloudMaster position number, 1
is= 46281
odd a; CloudMaster position number, 3
is= 52308
even a; CloudMaster position number, 5
is= 53420
odd a; CloudMaster position number, 7
is= 61580
even a; CloudMaster position number, 9
is= 63666
odd a; CloudMaster position number, 11
is= 70852
even a; CloudMaster position number, 13
is= 78918
odd a; CloudMaster position number, 15
is= 80124
here is cloud master in is entirety
625
46281
1982
52308
1747
53420
860
61580
273
63666
2334
70852
1277
78918
1330
80124
now we print start and end times for clouds
46281
46906
52308
54290
53420
55167
61580
62440
63666
63939
70852
73186
78918
80195
80124
81454
StormAdvance=true
StormAdvance=true
CloudAdvane=true
StormAdvance=true
InsolationAdvance=true
Insolation 3 sec elapsed
Elapsed Time is=8208
in first half of day
Insolation settings=
0
0
0
0
0
0
StormAdvance=true
CloudAdvane=true
StormAdvance=true
StormAdvance=true
CloudAdvane=true
InsolationAdvance=true
Insolation 3 sec elapsed
Elapsed Time is=8211
in first half of day
Insolation settings=
0
0
0
0
0
0
StormAdvance=true
StormAdvance=true
CloudAdvane=true
StormAdvance=true
InsolationAdvance=true
Insolation 3 sec elapsed
Elapsed Time is=8214
in first half of day
Insolation settings=
0
0
0
0
0
0
StormAdvance=true
CloudAdvane=true
StormAdvance=true
StormAdvance=true
CloudAdvane=true
InsolationAdvance=true
Insolation 3 sec elapsed
Elapsed Time is=8217
in first half of day
Insolation settings=
0
0
0
0
0
0