C library for Sun/Moon effects
Re: C library for Sun/Moon effects
THANK YOU. I would not have thought of that. THAT is the easy way, for sure.
I kept at it and could never quite get around the fact that even if you set a flag as a global and trigger it with seconds, the processor simply out runs your logic. Millis will solve that problem instantly.
One step closer to seeing it in action.
AND SOO CLOSE... seriously, if the time accounting is working, all the cloud setup, insolation, and the program is running cleanly without corrupting memory (which is seems to be) I am down to a single function to trouble shoot (weather), and that one is the least complex- its just watching elapsed time and triggering a few things.... this really should be working soon. I need perhaps a couple more nights working on it unless something turns out to be broken that I thought was working...
I kept at it and could never quite get around the fact that even if you set a flag as a global and trigger it with seconds, the processor simply out runs your logic. Millis will solve that problem instantly.
One step closer to seeing it in action.
AND SOO CLOSE... seriously, if the time accounting is working, all the cloud setup, insolation, and the program is running cleanly without corrupting memory (which is seems to be) I am down to a single function to trouble shoot (weather), and that one is the least complex- its just watching elapsed time and triggering a few things.... this really should be working soon. I need perhaps a couple more nights working on it unless something turns out to be broken that I thought was working...
Re: C library for Sun/Moon effects
Ok... So I think this should run... but I need to load it.
Couple things I am nervous about that I want to ask about here prior to loading.
I totally reworked the strike pattern generation.
Please let me know if this should work.
Basically, I create a random number of instances of a lightning strike called StrikeNumber which is an byte that
can be between 2-9- i.e. lights will flash in random sequence 2-9 times.
I set up an array called StrikeMaster that is StrikeNumber-1 long.
The array has random delay values (mills) of 100-700 milliseconds between strikes which occupy array positions 0,2,4,6,8... to fill array based upon StrikeNumber size required.
The values for 1,3,5,7.... are the duration of the strike, which are also randomly generated and vary from 30-80 mills (which is about how long a real lightning strike can be in nature.
Thus, an array for StrikeMaster might look like this (for StrikeNumber==3)
{500,50,250,35,600,70}
I am trying to get it to strike only 1 time per delay interval... so what I ended up doing it creating a counter for the loop called StrikeCount which starts at 0 and indexes by 2 every time a strike is written to the PWM board... and then use that value to sum the total delay across the StrikeMaster up to the point for the next strike... thus using elapsed mills when it is greater than the additive delay it will strike, then advance StrikeCount and then the next iteration of the loop will have a greater delay and should not trigger until milles has elapsed up to or past the scheduled delay (in this way its also time flexible since i am not requiring equality.. so if it takes a few 10's of mills to loop through all the other functions we will still get another strike with a bit of wobble in the actual delay)
So can someone help me with looking this over and seeing if you think it would work.
here is the entire code for the loop which is mostly just this with calls to CalcSun, Insolation, and Weather and a time tracker for triggering a boolean variable to run segments of insolation every 3 seconds, cloud every 2 seconds and storm every 1 seconds.... this is a neat way to get them to run only then... based in part on prior suggestions.
Finally, does this look right to actually write the values of the ChannelValue array to the PWM board at the bottom of every loop....
I.E. since I use a delay, the strike will happen, then it will hit the final analog.Write at the end of the loop, which will shut off the whites during a storm... so the flash should end immediately after the delay when it hits the loop segment of the analog.Write.
I assume this bit
for (byte a=0;a<6;a++){
analogWrite(PWMports[a],ChannelValue[a]);
}
will function without a define statement for PMWPorts, i.e. this is correct syntax and is in the library somewhere in terms of defining what to do with this.
here is the loop
I have another question which is below this part if you can help with that too...
The whole flow to the program is this.
1 time per day (at midnight or upon a restart of the PWM board) run calcsun and determine sunrise and sunset as well as calculate if we will have clouds, determine how many, determine their start and end points.. all these are global arrays
then insolation gets called which determines how far into the day we are and sets the base line intensity for every channel based upon the cos function for solar insolation and takes into account the desired offsets for rise/set for every channel, i.e. if you want you blues on early and to stay late etc...
Insolation just takes the 1/2 day length of every channel (i.e. 1/2 sunrise+sunset) plus morning or evening offsets and converts the time of day to an intensity setting (fraction of MAX) using the calculated 1/2 day length to (using a slope function) time to span a value from 1/2 pi (the number pi) at sunrise (i.e. -cos(1/2 pi)==0 to at midday -cos(pi)=1 so if you simply use this decimal value of -cos(pi conversion) you get 0-100% lights according to a cosine function over the morning to "noon" and then simply continues adding to pi over the pm 1/2 of the day to get from -cos(pi) at noon to -cos(1.5pi) at sunset==0
These decimals are simply used as floating point conversions- i.e. max desired intensity*float -cos(val) =set point through day in 3 second interval (rate value is recalculated) and are written to the ChannelValue array
Then weather gets called.... which modifies the values in ChannelValue by introducing cloud cover which can both reduce or increase intensity based upon a random walk, but I limit (duh) values to 100%of the insolation value, or the flicker point of the light.
Finally within weather a storm can happen if your channel intensities get low enough (by successive addition of random walk variables which can be either positive or negative) to trigger a storm, at which point whites get turned off (in ChannelValue array) and then a strike can happen... which is the code above.
So, since all light but for the strike which uses a direct analog.Write with a delay, is occurring ONLY at the end to the loop with the an along.Write and everyone uses ChannelValue[a] to set values, everything should work out to a coherent change...
Here is the part I am concerned with....
I am unsure about float conversions in terms of math
I use a lot of functions that require float math to establish the actual PWMChannel set points using stuff that I made to look like this...
ChannelValue is a BYTE array
flicker is a BYTE array
CLoudCover is a BYTE value and is ALWAYS positive (its reflected back if it tries to go negative in the random walk)
What happens is I convert cloud cover to a fraction (possible values are 0-80) by CloudCover/100 and use that
to set intensity, so I need that as a FLOAT. But I want to write a BYTE to the final PWM controller from ChannelValue
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)CloudCover/100)))+flicker[a]);
basically I want to write a byte to ChannelValue (0-255).
This is part of the cloud function in weather() which takes a set point (ChannelValue[a])
and modifies it based upon how cloudy we are which is CloudCover that can run from 0-100%
at 0% cloud cover, you get 1-0=1+flicker from the right part (past the (float) and since we subtracted flicker from the left part, we simply get back the original value
whereas when cloud cover is set to say 50%...
we get 50/100=0.5
1-0.5=0.5
0.5*ChannelValue-flicker=((50% of light past flicker point)+flicker)=set point for 50% cloudy.
is the notation I am using (float) correct, my understanding is that if forces a type conversion to the nearest float value (which is more exact) than the final which is the byte conversion.
I have not ever done this before but started adding all this in with all the trouble I have had with math occurring in unexpected ways based upon the variable type... Hope this is not too confusing of a question.
I will sit down and look at values but I am trying to figure out if this is the way to go
I tried integer conversion for all the intensity set point values, but it will not work due to the way my slope function works... you always end up producing a decimal so I cannot get around floats.
What I want to be sure, is that the FINAL analog.Write value is a BYTE (I know it will be between 0-255, if the conversions work).
What would happen if I wrote a float? I assume it would simply truncate... so do I need to worry about that???
Does it look like I have the math right.
Couple things I am nervous about that I want to ask about here prior to loading.
I totally reworked the strike pattern generation.
Please let me know if this should work.
Basically, I create a random number of instances of a lightning strike called StrikeNumber which is an byte that
can be between 2-9- i.e. lights will flash in random sequence 2-9 times.
I set up an array called StrikeMaster that is StrikeNumber-1 long.
The array has random delay values (mills) of 100-700 milliseconds between strikes which occupy array positions 0,2,4,6,8... to fill array based upon StrikeNumber size required.
The values for 1,3,5,7.... are the duration of the strike, which are also randomly generated and vary from 30-80 mills (which is about how long a real lightning strike can be in nature.
Thus, an array for StrikeMaster might look like this (for StrikeNumber==3)
{500,50,250,35,600,70}
I am trying to get it to strike only 1 time per delay interval... so what I ended up doing it creating a counter for the loop called StrikeCount which starts at 0 and indexes by 2 every time a strike is written to the PWM board... and then use that value to sum the total delay across the StrikeMaster up to the point for the next strike... thus using elapsed mills when it is greater than the additive delay it will strike, then advance StrikeCount and then the next iteration of the loop will have a greater delay and should not trigger until milles has elapsed up to or past the scheduled delay (in this way its also time flexible since i am not requiring equality.. so if it takes a few 10's of mills to loop through all the other functions we will still get another strike with a bit of wobble in the actual delay)
So can someone help me with looking this over and seeing if you think it would work.
here is the entire code for the loop which is mostly just this with calls to CalcSun, Insolation, and Weather and a time tracker for triggering a boolean variable to run segments of insolation every 3 seconds, cloud every 2 seconds and storm every 1 seconds.... this is a neat way to get them to run only then... based in part on prior suggestions.
Finally, does this look right to actually write the values of the ChannelValue array to the PWM board at the bottom of every loop....
I.E. since I use a delay, the strike will happen, then it will hit the final analog.Write at the end of the loop, which will shut off the whites during a storm... so the flash should end immediately after the delay when it hits the loop segment of the analog.Write.
I assume this bit
for (byte a=0;a<6;a++){
analogWrite(PWMports[a],ChannelValue[a]);
}
will function without a define statement for PMWPorts, i.e. this is correct syntax and is in the library somewhere in terms of defining what to do with this.
here is the loop
I have another question which is below this part if you can help with that too...
Code: Select all
//Loop
void loop()
{
wdt_reset();
//Serial.println(ChannelValue[0],DEC);
if (cmdnum!=255){
ProcessCMD(cmdnum,datanum);
cmdnum=255;
datanum=255;
}
//Use millis to enable tracking of time interval
if ((millis()-lastmillis)>1000) {
StormAdvance=true;
lastmillis=millis();
counter++;//use this to see if time has moved ahead 2, or, 3 seconds from first check which is 1 second advance
switch (counter){
case (2):
CloudAdvance=true;
break;
case (3):
InsolationAdvance=true;
counter=0;
break;
}
}
// now run through rise/set calculation and then intensity set and finally weather overlay
CalSun();
Insolation();
Weather();
if (StrikeNow==false){
StrikeStart=millis();
StrikeCount=0;
}
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{
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(StikeMaster[(strikeCount+1)];
StrikeCount+=2;
}
}
//track time in seconds-dont move this up in the loop, CalSun, DST must run first or your going to get a error with uninitialized variables
if (isDST==true){
elapsedTime=(((now()-946684800)+3600)-newDay);
}
else{
elapsedTime=((now()-946684800)-newDay);
}
//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
//*********************************************************************************************************************************
The whole flow to the program is this.
1 time per day (at midnight or upon a restart of the PWM board) run calcsun and determine sunrise and sunset as well as calculate if we will have clouds, determine how many, determine their start and end points.. all these are global arrays
then insolation gets called which determines how far into the day we are and sets the base line intensity for every channel based upon the cos function for solar insolation and takes into account the desired offsets for rise/set for every channel, i.e. if you want you blues on early and to stay late etc...
Insolation just takes the 1/2 day length of every channel (i.e. 1/2 sunrise+sunset) plus morning or evening offsets and converts the time of day to an intensity setting (fraction of MAX) using the calculated 1/2 day length to (using a slope function) time to span a value from 1/2 pi (the number pi) at sunrise (i.e. -cos(1/2 pi)==0 to at midday -cos(pi)=1 so if you simply use this decimal value of -cos(pi conversion) you get 0-100% lights according to a cosine function over the morning to "noon" and then simply continues adding to pi over the pm 1/2 of the day to get from -cos(pi) at noon to -cos(1.5pi) at sunset==0
These decimals are simply used as floating point conversions- i.e. max desired intensity*float -cos(val) =set point through day in 3 second interval (rate value is recalculated) and are written to the ChannelValue array
Then weather gets called.... which modifies the values in ChannelValue by introducing cloud cover which can both reduce or increase intensity based upon a random walk, but I limit (duh) values to 100%of the insolation value, or the flicker point of the light.
Finally within weather a storm can happen if your channel intensities get low enough (by successive addition of random walk variables which can be either positive or negative) to trigger a storm, at which point whites get turned off (in ChannelValue array) and then a strike can happen... which is the code above.
So, since all light but for the strike which uses a direct analog.Write with a delay, is occurring ONLY at the end to the loop with the an along.Write and everyone uses ChannelValue[a] to set values, everything should work out to a coherent change...
Here is the part I am concerned with....
I am unsure about float conversions in terms of math
I use a lot of functions that require float math to establish the actual PWMChannel set points using stuff that I made to look like this...
ChannelValue is a BYTE array
flicker is a BYTE array
CLoudCover is a BYTE value and is ALWAYS positive (its reflected back if it tries to go negative in the random walk)
What happens is I convert cloud cover to a fraction (possible values are 0-80) by CloudCover/100 and use that
to set intensity, so I need that as a FLOAT. But I want to write a BYTE to the final PWM controller from ChannelValue
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)CloudCover/100)))+flicker[a]);
basically I want to write a byte to ChannelValue (0-255).
This is part of the cloud function in weather() which takes a set point (ChannelValue[a])
and modifies it based upon how cloudy we are which is CloudCover that can run from 0-100%
at 0% cloud cover, you get 1-0=1+flicker from the right part (past the (float) and since we subtracted flicker from the left part, we simply get back the original value
whereas when cloud cover is set to say 50%...
we get 50/100=0.5
1-0.5=0.5
0.5*ChannelValue-flicker=((50% of light past flicker point)+flicker)=set point for 50% cloudy.
is the notation I am using (float) correct, my understanding is that if forces a type conversion to the nearest float value (which is more exact) than the final which is the byte conversion.
I have not ever done this before but started adding all this in with all the trouble I have had with math occurring in unexpected ways based upon the variable type... Hope this is not too confusing of a question.
I will sit down and look at values but I am trying to figure out if this is the way to go
I tried integer conversion for all the intensity set point values, but it will not work due to the way my slope function works... you always end up producing a decimal so I cannot get around floats.
What I want to be sure, is that the FINAL analog.Write value is a BYTE (I know it will be between 0-255, if the conversions work).
What would happen if I wrote a float? I assume it would simply truncate... so do I need to worry about that???
Does it look like I have the math right.
Re: C library for Sun/Moon effects
Sorry for the long questions.... its really done... so this should be the last bit of trouble shooting and I am rather hoping we are good to go if I can get all the math right. I am relatively confident in all the "complex" conversions and routines- if I took the algorithms and plugged it all into excel, I am confident It would produce exactly what I want (some of that has been done) but I am really not too sure about the whole arduino/c++ type casting and the effects on the math.
Anyhow, this will be a little work to grasp and actually help me out, so THANKS if your able! Happy to move to email if things require more back and forth or whatever.
Finally, I think that once I have it running which may be today... should I start a new thread and post working code with some instructions on what needs to be modified or keep it here in the development thread. I note that among other things, the way I have presented the library files for the EPHMERA library is WRONG and I had to create a single file, rather than the split .h .cpp files as I posted... so there is just enough WRONG about this thread as an ongoing debugging effort that a new thread that is more user friendly may be appropriate with working code posted from the beginning?
Thoughts from Roberto/Kurt or others with experience would be great.
Anyhow, this will be a little work to grasp and actually help me out, so THANKS if your able! Happy to move to email if things require more back and forth or whatever.
Finally, I think that once I have it running which may be today... should I start a new thread and post working code with some instructions on what needs to be modified or keep it here in the development thread. I note that among other things, the way I have presented the library files for the EPHMERA library is WRONG and I had to create a single file, rather than the split .h .cpp files as I posted... so there is just enough WRONG about this thread as an ongoing debugging effort that a new thread that is more user friendly may be appropriate with working code posted from the beginning?
Thoughts from Roberto/Kurt or others with experience would be great.
Re: C library for Sun/Moon effects
Ok- its working
. I played with the code a little bit more last night, and actually removed a lot of the casting and rearranged the parenthetical expressions for much of the insolation loop to calculate the sun "path" via cosine functions.
As of 6:20 this morning, when I loaded the code up... I didn't get to watch sunrise as that occurred at about 20,000 seconds into the day, but the lights were on, the PWM settings looked correct (i.e. 58 as a byte value written to PWMChannel which makes sense for about being about 20-30 minutes from sunrise). The insolation function runs every 3 seconds as intended and CalSun runs only 1/day. I was able to see that a cloud was scheduled and I think it started but morning and late day clouds are hard to "see" because the cloud effect is constrained to being within the current insolation MAX channel value, so 30 min after sunrise, the cloud can only dim from 58-25 as byte values written to the PWMChannel- which your not going to be able to see as much of an effect... during the mid day it can dim from basically 100% PWM out to the flicker point, which you will see.... so obviously much has yet to be confirmed working but the "difficult" parts would appear more or less functional and the PWM board seems to EASILY handle the program.
Its currently about 20,500 bytes compiled leaving about 10,000 bytes available for more lines, so we can add comm to the main board etc easily.
Have not seen a storm, and since this is pretty much entirely random it may be a bit before I am there to observe it and the attendant random lightning strikes (I would be surprised if I did not need to adjust the chance for any one of these events to occur- it may create a storm every time a cloud appears (kinda doubt it) or have extremely infrequent strikes etc etc... but these are simply changes to variable ranges allowed and everyone can tune to their desire.
I will write up a description of the specific configurations that need to be set so that others can try this without having to read the entire program line by line and then try to figure out what needs to be changed.
Anyhow, its running all day unattended for the first time, but I am at work.... oh well. Will post code when I get a chance. May be a little bit for description of what to change.
Maybe its just me... but the lights just looked so much better this morning


As of 6:20 this morning, when I loaded the code up... I didn't get to watch sunrise as that occurred at about 20,000 seconds into the day, but the lights were on, the PWM settings looked correct (i.e. 58 as a byte value written to PWMChannel which makes sense for about being about 20-30 minutes from sunrise). The insolation function runs every 3 seconds as intended and CalSun runs only 1/day. I was able to see that a cloud was scheduled and I think it started but morning and late day clouds are hard to "see" because the cloud effect is constrained to being within the current insolation MAX channel value, so 30 min after sunrise, the cloud can only dim from 58-25 as byte values written to the PWMChannel- which your not going to be able to see as much of an effect... during the mid day it can dim from basically 100% PWM out to the flicker point, which you will see.... so obviously much has yet to be confirmed working but the "difficult" parts would appear more or less functional and the PWM board seems to EASILY handle the program.
Its currently about 20,500 bytes compiled leaving about 10,000 bytes available for more lines, so we can add comm to the main board etc easily.
Have not seen a storm, and since this is pretty much entirely random it may be a bit before I am there to observe it and the attendant random lightning strikes (I would be surprised if I did not need to adjust the chance for any one of these events to occur- it may create a storm every time a cloud appears (kinda doubt it) or have extremely infrequent strikes etc etc... but these are simply changes to variable ranges allowed and everyone can tune to their desire.
I will write up a description of the specific configurations that need to be set so that others can try this without having to read the entire program line by line and then try to figure out what needs to be changed.
Anyhow, its running all day unattended for the first time, but I am at work.... oh well. Will post code when I get a chance. May be a little bit for description of what to change.
Maybe its just me... but the lights just looked so much better this morning

Re: C library for Sun/Moon effects
Cool 
Can't wait to read your observations during the next few days

Can't wait to read your observations during the next few days

Roberto.
Re: C library for Sun/Moon effects
OK-
first thing I need to correct so that if you want to try this... you might be able to.
I suffered much confusion in getting the library written by Michael Rice at swfltek.com to work for me. This was entirely my fault and related to my jumping into a project that was clearly over my head in many ways when I started to code this.
I changed essentially NOTHING, the library is NOT my work... READ the above again if you want to know who to thank and shower with glowing praise for making this possible.
I post the library file that I am using, unaltered from Michael Rice below, I use the following directory and file name.
the relevant call in the header to the program I wrote is
#include <SWFLTEK_EPHERMA.h>
which then looks for this directory /SWFLTEK_EPHERMA in the arduino library directory.
which has the following file in it
SWFLTEK_EPHERMA.h
When I started this thread I thought the library needed to be split into a .cpp file and a .h file, which yielded unending frustration with duplicate variable declaration (or some such error) and only when I simply used a single .h file was I able to get this to work. Don't ask me why, I can only make guesses informed by experience but the compilation structure of Arduino is beyond my grasp. This file works- I would just use it as its in fact even easier than making two files.
here is the entire code pulled from my SWFLTEK_EPHERMA.h file
The licensing for the source code from Michael was posted on the first page of this thread-
first thing I need to correct so that if you want to try this... you might be able to.
I suffered much confusion in getting the library written by Michael Rice at swfltek.com to work for me. This was entirely my fault and related to my jumping into a project that was clearly over my head in many ways when I started to code this.
I changed essentially NOTHING, the library is NOT my work... READ the above again if you want to know who to thank and shower with glowing praise for making this possible.
I post the library file that I am using, unaltered from Michael Rice below, I use the following directory and file name.
the relevant call in the header to the program I wrote is
#include <SWFLTEK_EPHERMA.h>
which then looks for this directory /SWFLTEK_EPHERMA in the arduino library directory.
which has the following file in it
SWFLTEK_EPHERMA.h
When I started this thread I thought the library needed to be split into a .cpp file and a .h file, which yielded unending frustration with duplicate variable declaration (or some such error) and only when I simply used a single .h file was I able to get this to work. Don't ask me why, I can only make guesses informed by experience but the compilation structure of Arduino is beyond my grasp. This file works- I would just use it as its in fact even easier than making two files.
here is the entire code pulled from my SWFLTEK_EPHERMA.h file
The licensing for the source code from Michael was posted on the first page of this thread-
Code: Select all
/*
SWFLTEK_Ephemera copyright 2010 by Michael Rice
Swfltek Ephemera is a set of astronomical functions often of interest.
Though written for avr-gcc, it should convert easily to other compilers.
Most functions require an unsigned long time stamp as an argument. This 32 bit value represents
the number of seconds elapsed since midnight Jan 1 2000 GMT.
*/
#ifndef SWFLTEK_EPHEMERA_h
#define SWFLTEK_EPHEMERA_h
// geographic coordinates, in seconds of arc North and East
long latitude, longitude;
// conveniences to convert two typical representations into seconds of arc
long ddToSeconds(float);
long dmsToSeconds(int, unsigned char, unsigned char);
// return equation of time in seconds
int equation_of_time(unsigned long);
// adjust stamp to Solar noon
void SolarNoon(unsigned long * );
// return solar declination in radians
double SolarDeclination(unsigned long dt);
// return seconds between sunrise and sunset
unsigned long daylightseconds(unsigned long);
// compute time of sun rise or sunset
char SunRiseSet(unsigned long*, char);
// shorthand form
char SunRise(unsigned long*);
char SunSet(unsigned long*);
// convert from GMT to Mean Sidereal Time
unsigned long GMSidereal(unsigned long);
unsigned long LMSidereal(unsigned long);
// approximate phase of the moon
char MoonPhase(unsigned long);
// season
unsigned char season(unsigned long);
/*
==================================================================================================
SNIP HERE SNIP HERE SNIP HERE SNIP HERE SNIP HERE
==================================================================================================
*/
#include <stdlib.h>
#include <math.h>
// arc-seconds per radian
#define _sec_rad 206264.806247096370813
// axial tilt of earth at epoch, in radians
#define _tilt 0.409092804222329
// tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _tropical_year 31556925
// 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)
#define _zenith 3.11250383272322
//#include "SWFLTEK_EPHERMA.h"
/*------------------------------------------------------------------------------------------------
convert degrees to seconds of arc
*/
// decimal degrees
long ddToSeconds(float dd){
return dd * 3600.0;
}
//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;
ret = labs((long)d);
ret = ret * 3600L + 60L * m + s;
ret = (d<0L) ? -ret : ret;
return ret;
}
/* ------------------------------------------------------------------------------------------------
'Equation of Time'
We use the 'short for' equation, which has a theoretical accuracy of about 40 seconds.
The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;
dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
dt %= _tropical_year;
t = dt;
t /= _tropical_year;
t *= 6.283185307179586;
t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
return t;
}
/*
'Solar Noon' adjusts the passed time stamp to the time (GMT) of local solar noon.
The accuracy is about 40 seconds (set by the equation of time).
*/
void SolarNoon(unsigned long * dt){
long r;
// Set stamp to noon GMT
*dt /= 86400UL;
*dt *= 86400UL;
*dt += 43200UL;
// adjust for equation of time, at noon GMT
*dt -= equation_of_time(*dt);
// rotate to our longitude
r = longitude / 15L;
*dt -= r;
}
/* -----------------------------------------------------------------------------------------------
'Solar Declination'
Returns declination in radians
Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;
dt %= _tropical_year;
y = dt;
y /= _tropical_year; // fractional year
y *= 6.283185307179586;
y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
return y;
}
/* ------------------------------------------------------------------------------------------------
Return the period between sunrise and sunset, in seconds.
At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;
d = -SolarDeclination(dt); // will be positive in Northern winter
l = latitude / _sec_rad; // latitude in radians
e += 60.0 * l * tan(l + d); // latitudinal error
d = tan(l) * tan(d); //
if(d>1.0) return 86400UL;
if(d < -1.0) return 0UL;
d = acos(d);
d /= _zenith;
n = 86400UL * d;
n += e;
return n;
}
/* ------------------------------------------------------------------------------------------------
Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
all day.
*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;
daylen = daylightseconds(*dt);
if(daylen == 86400UL) return 1; // there is no 'night' today (midnight sun)
if(daylen == 0UL) return -1; // there is no 'day' today
*dt /= 86400UL;
*dt *= 86400UL;
*dt += 43200UL; // set the time stamp to 12:00:00 GMT
*dt -= daylen / 2; // sunrise at the prime meridian
if(set) *dt += daylen; // sunset at the prime meridian
*dt -= equation_of_time(*dt);
*dt -= longitude / 15.0; // rotate to our own meridian
return 0;
}
// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
return SunRiseSet(when, 1);
}
/* ------------------------------------------------------------------------------------------------
Mean Sidereal Time.
Accurate to within 1 sidereal second.
*/
unsigned long GMSidereal(unsigned long gmt){
unsigned long long sidereal;
sidereal = gmt * 10027379094LL; // multiply by 10 Billion times the ratio
sidereal /= 10000000000LL; // and divide by 10 billion
sidereal += 23992LL; // add sidereal time at the epoch
return sidereal;
}
unsigned long LMSidereal(unsigned long gmt){
return GMSidereal(gmt ) + longitude / 15L;
}
/* ------------------------------------------------------------------------------------------------
An approximation of the moons phase.
Magnitude of the result approximates the percentage of the moons illuminated surface.
Sign of the result indicates a Waxing (+) or Waning (-) moon.
It uses the mean lunar cycle, which may differ from the actual by many hours.
As such, it may vary from the correct value by 20%.
*/
char MoonPhase(unsigned long d){
long r;
// the first full moon of the epoch was Jan 21 at 04:40
// but the 'mean' full moon was 4 hours later
d -= 1759365UL; // subtract time of first full moon
d %= 2551443L; // mod by the mean lunar cycle
r = d - 1275721L;
r /= 12757L;
return r;
}
// Season of the year
// 0 = winter, 1 = spring, 2 = summer, 3 = fall
unsigned char season(unsigned long dt){
dt += 838800UL;// refer to prior winter solstice
dt %= _tropical_year;
dt /= 7889400UL; // 91.3125 days
if(latitude<0) dt += 2UL;
return dt % 4;
}
/*
==================================================================================================
SNIP HERE SNIP HERE SNIP HERE SNIP HERE SNIP HERE
==================================================================================================
*/
#endif // SWFLTEK_EPHEMERA_h
Re: C library for Sun/Moon effects
Here is the current version of my source code.
I am beta testing it, its working but I have no idea whats going to happen through an entire day cycle, its a working beta and likely has bugs. I am running it unattended (which is optimistic), when I catch bugs I will post a description of the bug, and the new fixed source code.
You likely will need to change a few lines to get this to work for your tank as it is unlikely in the extreme that we have identical channel lay outs etc etc.
I tried to comment the code heavily (for my own sanity and in hope that it would ease adoption).
I have NOT gone through the comments lately so there are probably many that are now in error as the code evolved SIGNIFICANTLY during development.
I will post a HOW TO guide tonight- but here is what I am now considering a beta version of the code. It would be awesome to see someone else try this out-
I am beta testing it, its working but I have no idea whats going to happen through an entire day cycle, its a working beta and likely has bugs. I am running it unattended (which is optimistic), when I catch bugs I will post a description of the bug, and the new fixed source code.
You likely will need to change a few lines to get this to work for your tank as it is unlikely in the extreme that we have identical channel lay outs etc etc.
I tried to comment the code heavily (for my own sanity and in hope that it would ease adoption).
I have NOT gone through the comments lately so there are probably many that are now in error as the code evolved SIGNIFICANTLY during development.
I will post a HOW TO guide tonight- but here is what I am now considering a beta version of the code. It would be awesome to see someone else try this out-
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 <Globals.h>
#include <ReefAngel_Features.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>
#include <SWFLTEK_EPHERMA.h>
//*********************************************************************************************************************************
//IF YOU DO NOT WISH TO USE the U.S.A. Day Light Savings Time rules to modify the time stamp for rise/set calculations...set to false- i.e. if you dont live in the USA etc
boolean ApplyDST=true;
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[6];// Array to store current setpoint for PMW control of output to ch (0,1,2,3,4,5)
//set up channel max intensity set points
byte ChMax[]={255,255,255,255,255,0};//max values you want to hit at solar noon scaled as byte values for LED output PWM write
byte flicker[]={25,25,25,25,10,0};//need to input actual values here for your flicker points for EACH channel..
byte ChInt[5];
//YOU MUST CHANGE THIS- designate channels of white light (channels you want off during Storm and used for lightning strikes)
//use 1 to designate white channel. Array corresponds to PWM channel 0-5 in order
boolean Wchannel[]={1,0,1,0,0,0,0};
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;
boolean trigger; //used a few places as a switch to ensure things only run 1x when needed
byte strikePattern, strikeTime;//used in Lightning() for timing of particular instance of strike
boolean isDST, Cloud, Storm, CloudToday, StrikeNow; // 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-
long *pCloudPoint;//Use global to point to static memory location of array CloudMaster created 1/x day in calcSun.
unsigned long lastmillis;// variable to track millis to enable cloud and insolation loop restriction by time
boolean CloudAdvance=false;//cloud timer for light effect advance
boolean StormAdvance=false;//storm timer for light effect advance
boolean InsolationAdvance=false;//when true we recalculate light intensity during clear sky
byte counter;//used to track millis advance for insolation,cloud trigger
//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,};
//****************************************
//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.
now();//why are you here?
CloudToday=false;
trigger=true;
StrikeNow=false;
Cloud=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;
}
//Use millis to enable tracking of time interval
if ((millis()-lastmillis)>1000) {
StormAdvance=true;
lastmillis=millis();
counter++;//use this to see if time has moved ahead 2, or, 3 seconds from first check which is 1 second advance
switch (counter){
case (2):
CloudAdvance=true;
break;
case (3):
InsolationAdvance=true;
counter=0;
break;
}
}
// now run through rise/set calculation and then intensity set and finally weather overlay
CalSun();
Insolation();
Weather();
if (StrikeNow==false){
StrikeStart=millis();
StrikeCount=0;
}
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.
if (isDST==true){
elapsedTime=(((now()-946684800)+3600)-newDay);
}
else{
elapsedTime=((now()-946684800)-newDay);
}
//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();
// Individual Channel
if (cmd>=0 && cmd<=5){
ChannelValue[cmd]=data;
analogWrite(PWMports[cmd],data);
}
if (cmd==6){
Storm=true;
}
}
//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()
{
//this entire function runs only based upon the first if being true... so its a LONG ways down to the else if trigger is false statement to finish this if
// Every day at midnight, we calculate rise, set, time of highest sun, moon phase, or we do this if system has reset
if ((hour()==0 && minute()==0 && second()==0) || (trigger==true)){
Serial.println("trigger 1");
Serial.println(trigger);
unsigned long SecInput;
//void CalcDST(day(),month(),weekday()) modifies isDST memory to appropriate true/false based upon USA rules
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
long hours;//possible to be reset at 23 hours*3600=long required
long minutes;// int is enough as 60*60 is max val but math is conducted with possibility of roll over in intermediate so to be sure.. use long
time_t t=now();
hours=hour(t);
minutes=minute(t);
if ((ApplyDST==true) && (isDST==true)){
if ((hours!=0) || (minutes!=0)){
hours=hours+1;
newDay=(now()-(946684800+3600+((hours*3600)+(minutes*60)))); //yes I am ignoring seconds... if your worried about your rise being off by <60 seconds on a day of a power failure... your more anal than I
Serial.print("DST Hours,Min");
Serial.print(hours);
Serial.print(",");
Serial.println(minutes);
}
else{
newDay=(now()-946684800+3600);
}
}
else if ((isDST==false) || (ApplyDST==false)){
if (hours!=0 || minutes!=0){
newDay=(now()-(946684800+((hours*3600)+(minutes*60)))); //yes I am ignoring seconds... if your worried about your rise being off by <60 seconds on a day of a power failure... your more anal than I
Serial.print("Hours,Min");
Serial.print(hours);
Serial.print(",");
Serial.println(minutes);
}
}
SecInput=((newDay)+25200);//modify to GMT time (i.e. MST + 7 hours = GMT)
//Calculate Latitude and Longitude converting from Degree, Minute, Seconds to seconds
latitude=dmsToSeconds(40,45,39); //Set to about the latitude of the great barrier reef rotated into the morthern hemisphere
longitude=dmsToSeconds(-111,53,25); //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
rise=SecInput;
set=SecInput;
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
rise-=25200; //Correct back to MST from GMT- DST still accounted for
set-=25200;
Serial.print("newDay=");
Serial.println(newDay);
Serial.print("Local uncorrected rise, set=");
Serial.print(rise);
Serial.print(",");
Serial.print(set);
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);
//*********************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
//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,-3600,5400,0,600,-3600,5400,-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);
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)(midDay-(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)(midDay+(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=70;//% 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;
}
else if (RainMaker>=CloudChance){
CloudToday=false;
trigger=false;
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);
byte OvercastMin=10;//% as fraction (0-1 is 0-100%) of daylight hours that would minimially be cloudy on a cloud day
byte OvercastMax=31;//% of daylight hours that would maximally be cloudy on a cloud day (then add 1 to that #)
float Overcast=random(OvercastMin,OvercastMax);
Overcast=(Overcast/100);
Serial.println("Overcast");
Serial.println(Overcast);
// number of clouds possible for the day, max and min
byte CloudsMax=9;
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);
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*/
long CloudLength;
CloudLength=((dayLength*Overcast)/CloudsTotal);//average cloud length
float fraction=random(20,181);
fraction=(fraction/100);//vary each cloud from 20%-180% of an equal fraction of total cloud for day
Serial.print("fraction ");
Serial.println(fraction);
Serial.print("CloudLength ");
Serial.println(CloudLength);
//using fraction to vary cloud length throughout day fill array CloudMaster[] at positions 0,2,4,6 to full with durations in seconds
//this way the MAX cloud duration is 180% of the average cloud druation while the min is 20%
byte b=0;//set up counter to alter cloud length and initialize to zero outside of loop
for (byte a=0; a<CloudsTotal; a++){
int CurrentCloudLength;
// if were having an odd # of clouds make sure last one is full length
if ((CloudsTotal%2!=0) && (a==(CloudsTotal-1))){
CloudMaster[a*2]=CloudLength;
Serial.print("last odd cloud Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
break;
}
// else we make clouds variable in length short then long repeated with every two cloud sets being different in short and long but adding up equally by sets
else {
if (fraction<1){
CurrentCloudLength=abs(CloudLength*b+(b*CloudLength-(CloudLength*fraction)));
CloudMaster[a*2]=CurrentCloudLength;
Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);
}
if (fraction==1){
CloudMaster[a*2]=CloudLength;
Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);
}
if (fraction>1){
CurrentCloudLength=((CloudLength*fraction)-(b*CloudLength*fraction))+(b*((2-fraction)*CloudLength));
CloudMaster[a*2]=CurrentCloudLength;
Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);
}
}
b=b+1;
if (b==2){
b=0;
fraction=random(20,181);//every other cloud recalculate a new fractional distribution for the next set
fraction=(fraction/100);
}
}
randomSeed(now());
// Now space the clouds out during the day using random fractionation of day segments as per cloud calculation
b=0;//reset counter used for fraction reset
byte c=0;//counter for indexing # of itterations in loop
long StartTime;//Used in loop to assign cloud spacing, i.e. these loop values determine daylight interval between clouds
fraction=random(10,181);
fraction=(fraction/100);
for (byte a=1; a<(CloudsTotal*2); a=(a+2)){
//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.
// must index segment time by +1 segment for each cloud completed
long SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));
if (fraction<1){
abs(CloudLength*b+(b*CloudLength-(CloudLength*fraction)));
StartTime=rise+((SunSegment*b+abs(b*SunSegment-(SunSegment*fraction)))+(c*SunSegment));
CloudMaster[a]=StartTime;
StartTime=0;
b=b+1;
}
else if (fraction==1){
StartTime=rise+(SunSegment+(SunSegment*c));
CloudMaster[a]=StartTime;
StartTime=0;
b=2;//by doing this I ensure that new fraction will be calculated so next interval will not be 1 (or rarely so)
}
else if (fraction>1){
StartTime=rise+(((SunSegment*fraction)-(b*SunSegment*fraction))+(b*((2-fraction)*SunSegment))+(c*SunSegment));
CloudMaster[a]=StartTime;
StartTime=0;
b=b+1;
}
if (b==2){
b=0;
fraction=random(20,181);//every other cloud recalculate a new fractional distribution for the next set
fraction=(fraction/100);
}
c++; //index loop counter by one now
Serial.print("CloudMaster position number, ");
Serial.print(a);
Serial.print(" is= ");
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<(CloudsTotal*2);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]);
}
trigger=false;
Serial.println("Trigger 2");
Serial.println(trigger);
//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]);
}
}//Finally end loop if trigger is true of hr=0 min=0 sec=0 .... i.e. basically entire function runs only 1x per day or on restart of controller
else if (trigger==false){
return;
}
}//END FUNCTION
void Insolation()
{
if (InsolationAdvance==true){//only change lights every 3 seconds
Serial.println("Insolation 2 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){
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.print("ch0 setting first 1/2 day=");
Serial.println((byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b])+flicker[b],6);
break;
case 2:
ChannelValue[1]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-1])+flicker[b-1];
break;
case 4:
ChannelValue[2]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
break;
case 6:
ChannelValue[3]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-3])+flicker[b-3];
break;
case 8:
ChannelValue[4]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-4])+flicker[b-4];
break;
case 10:
ChannelValue[5]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-5])+flicker[b-5];
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;
}
}
}
}
if (elapsedTime>=midDay){
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.print("ch0 setting second 1/2 day=");
Serial.println((-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-1]),6);
break;
case 3:
ChannelValue[1]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
break;
case 5:
ChannelValue[2]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-3])+flicker[b-3];
break;
case 7:
ChannelValue[3]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-4])+flicker[b-4];
break;
case 9:
ChannelValue[4]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-5])+flicker[b-5];
break;
case 11:
ChannelValue[5]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-6])+flicker[b-6];
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;
}
}
}
}
InsolationAdvance=false;
Serial.print("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
if (Cloud==false){
for (byte a=0; a<=(CloudsTotal-1); a=(a+2)){
if ((elapsedTime>=*(pCloudPoint+a)) && (elapsedTime<=*(pCloudPoint+a+1))){//time needs to be within a cloud segment, not just greater than any start
CloudEnd=*(pCloudPoint+(a+1));
CloudEnd=CloudEnd-120;
Cloud=true;
}
else{
Cloud=false;
CloudCover=0;
PriorCloudCover=0;
}
}
}
else if ((Cloud==true) && (Storm==false)){
if (CloudAdvance==false){//use millis tracker to run this loop every 2 seconds
return;
}
CloudAdvance=false;//if were going to run this, stop it from happening again until time advance
boolean skipthis=false;
Serial.println("in a clouddoing stuff every 2 seconds");
//now is a good time to check if our cloud is ending, prior to setting more PWM outputs send to ClearSky if times u
//seems backwards since CloudCover has not been calculated if you read linearly but it will always be set prior to this=true
if (elapsedTime>=CloudEnd){
skipthis=true;
}
/*Use fractional intensity to set minimum value for any channel its WAY to confusing to try to set up otherwise. 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. (CloudCover/100)*Insolation SetPoint
is how the current cloud intensity is set*/
if (skipthis==false){
randomSeed(millis());
byte stepsize=random(-10,11);
PriorCloudCover=CloudCover;
CloudCover = CloudCover + stepsize;
if (CloudCover>=80){ //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
Storm=true;
wtrigger=true;
StormStart=elapsedTime;
}
else if (CloudCover<=0){
CloudCover-=(stepsize*1.5);
}
}
for (int a=0;a<6;a++){
if (DimOrder[a]==0){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(PriorCloudCover/100))))+flicker[a]; ;
}
if (DimOrder[a]==1){
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)CloudCover/100)))+flicker[a]);
}
}
if (skipthis==true){
ClearSky(CloudCover, CloudEnd);
}
}
//enable a flag sent from controller to triger a storm, i.e. Storm=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) && (Storm=true)) || ((Cloud=false) && (Storm=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){
int LongestStorm=((CloudEnd-StormStart)-120);//remove the last 2 minutes I use to clear the sky from the longest possible storm duration
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
StormStart=elapsedTime;
StormEnd=((StormStart+StormDuration)+elapsedTime);
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.
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
Storm=false;
wtrigger=false;//allow next strom to be randomized correctly
return;
}
byte stepsize;
if (StrikeNow==false){
// step through intensity values
randomSeed(millis());
stepsize=random(-15,16);
if ((stepsize<-12) || (stepsize>12)){ //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
int StartStrike=0;
//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){
StartStrike+=random(100,701);//position 0,2,4,6,8.. is strike delay
StrikeMaster[a]=StartStrike;
StartStrike=0;
}
else if (a%2!=0){
StartStrike+=random(30,81);//position 1,3,5,7,9... is strike duration- added to StartStrike=StrikeEnd
StrikeMaster[a]=StartStrike;
StartStrike=0;
}
}
StrikeNow=true; //Trigger start of timming loop in "loop" to initiate Strike pattern do this last so were all good on settings
}
strikeCount=0;
}
PriorCloudCover=CloudCover;
CloudCover = CloudCover + stepsize;
if (CloudCover>=100){//will never set intensity below flicker point... so no issue with this.
CloudCover-=(stepsize*1.5);//if were too dark reflect random path to light
}
else if (CloudCover<=0){
CloudCover-=(stepsize*1.5);//if were 100% intensity of light in a storm reflect random path to less intensity
}
for (int a=0;a<6;a++) {
if (DimOrder[a]==0){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(PriorCloudCover/100))))+flicker[a];
}
else if (DimOrder[a]==1){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(CloudCover/100))))+flicker[a];
}
if (Wchannel[a]==1){
ChannelValue[a]=0;//turn off whites during a storm
}
}
}//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){
Storm=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
Re: C library for Sun/Moon effects
A few observations-
There remain a few things that need looking into, seems like it gets sunrise and sunset right some times but also seems to have a bug related to exiting storms or something like that, somehow its setting correctly but the next morning I am sometimes not getting lights on but when I restart the system the lights come on normally... so its probably something stupid with the new day calculation at midnight, as I never did test that until now. Have not seen any lightning, and I am not entirely sure if the cloud effect is running as intended, I need to sit down with a volt meter to make sure during what I KNOW is a cloud...so its working but only mostly, I would not yet put this on a tank and rely upon it to produce the required photo periods. I am guessing this will be easily solved and is a minor issue, just need to look at the weather loop a bit more closely. Basically, I am going to speed up the cloud, make bigger changes in intensity, and look the code over closely and then let it run for the weekend and note issues that appear (if more arise).
Still not sure I have ever seen a cloud, as I am home only at the beginning and end of the light cycles... The weekend will tell more. At least its baseline (with obvious bugs) functional, all the offsets are working, sun rises right to left blues first, sets left to right whites first, then blues go off then violets finally set for lights out etc etc...
There remain a few things that need looking into, seems like it gets sunrise and sunset right some times but also seems to have a bug related to exiting storms or something like that, somehow its setting correctly but the next morning I am sometimes not getting lights on but when I restart the system the lights come on normally... so its probably something stupid with the new day calculation at midnight, as I never did test that until now. Have not seen any lightning, and I am not entirely sure if the cloud effect is running as intended, I need to sit down with a volt meter to make sure during what I KNOW is a cloud...so its working but only mostly, I would not yet put this on a tank and rely upon it to produce the required photo periods. I am guessing this will be easily solved and is a minor issue, just need to look at the weather loop a bit more closely. Basically, I am going to speed up the cloud, make bigger changes in intensity, and look the code over closely and then let it run for the weekend and note issues that appear (if more arise).
Still not sure I have ever seen a cloud, as I am home only at the beginning and end of the light cycles... The weekend will tell more. At least its baseline (with obvious bugs) functional, all the offsets are working, sun rises right to left blues first, sets left to right whites first, then blues go off then violets finally set for lights out etc etc...
Re: C library for Sun/Moon effects
Ok... I guess I get beaten by the stupid stick again.
I found (the final I hope) if statement that I used VBasic syntax in (i.e. if (x=y) rather than x==y).
This should fix the issue I was having in getting stuck in a storm... I believe
But now I am getting a error on compile that I have not seen, I looked it up and to be honest I don't see where its coming from.... I think I know why (read to end) but an unsure how to check for this, I blame the library update (or perhaps my choosing a variable that is used in a newer library, chicken and egg I guess).
I am declaring a global variable
boolean Storm;
which is being set to false in the setup (as a just to be sure step) and then is triggered variously to true or false depending on if the storm is starting or ending etc etc....
I only declare it ONCE, and I have checked.
But on compile it gives me this error
expected un-qualified id before numeric constant
and pulls up every instance of a toggle to the boolean variable in the code
LatLong_PMW_Backup031612:58: error: expected unqualified-id before numeric constant
LatLong_PMW_Backup031612.cpp: In function 'void setup()':
LatLong_PMW_Backup031612:98: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612.cpp: In function 'void Weather()':
LatLong_PMW_Backup031612:724: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612:761: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612.cpp: In function 'void ClearSky(byte, int)':
LatLong_PMW_Backup031612:834: error: lvalue required as left operand of assignment
and the basics are that the code always looks like this
if (elapsed<=120){
Storm=false;
Cloud=false;
return;
}
of some variation of it... this specific line is in the error (it is line 834 if you care). NOTHING changed with this, so I don't get the error, it was compiling previously just fine and I was running the program... what gives?
Do I need a bigger stupid stick??
seems a waste to post the entire thousand lines of code as none of this part has been changed... but let me know and I will put it up.
My only guess is that this is defined somewhere in one of the library files I have referenced... but thats impossible since it was working for the last few months of testing... aha....
I UPDATED MY LIBRARIES.... is this why?????????????
10 bucks says it is.... and that Storm is now some numeric constant specified in one of the #include statements I make to the RA libraries.
I found (the final I hope) if statement that I used VBasic syntax in (i.e. if (x=y) rather than x==y).
This should fix the issue I was having in getting stuck in a storm... I believe
But now I am getting a error on compile that I have not seen, I looked it up and to be honest I don't see where its coming from.... I think I know why (read to end) but an unsure how to check for this, I blame the library update (or perhaps my choosing a variable that is used in a newer library, chicken and egg I guess).
I am declaring a global variable
boolean Storm;
which is being set to false in the setup (as a just to be sure step) and then is triggered variously to true or false depending on if the storm is starting or ending etc etc....
I only declare it ONCE, and I have checked.
But on compile it gives me this error
expected un-qualified id before numeric constant
and pulls up every instance of a toggle to the boolean variable in the code
LatLong_PMW_Backup031612:58: error: expected unqualified-id before numeric constant
LatLong_PMW_Backup031612.cpp: In function 'void setup()':
LatLong_PMW_Backup031612:98: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612.cpp: In function 'void Weather()':
LatLong_PMW_Backup031612:724: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612:761: error: lvalue required as left operand of assignment
LatLong_PMW_Backup031612.cpp: In function 'void ClearSky(byte, int)':
LatLong_PMW_Backup031612:834: error: lvalue required as left operand of assignment
and the basics are that the code always looks like this
if (elapsed<=120){
Storm=false;
Cloud=false;
return;
}
of some variation of it... this specific line is in the error (it is line 834 if you care). NOTHING changed with this, so I don't get the error, it was compiling previously just fine and I was running the program... what gives?
Do I need a bigger stupid stick??
seems a waste to post the entire thousand lines of code as none of this part has been changed... but let me know and I will put it up.
My only guess is that this is defined somewhere in one of the library files I have referenced... but thats impossible since it was working for the last few months of testing... aha....
I UPDATED MY LIBRARIES.... is this why?????????????
10 bucks says it is.... and that Storm is now some numeric constant specified in one of the #include statements I make to the RA libraries.
Re: C library for Sun/Moon effects
guessing this was the correct thinking, changing it to boolean IsStorm; and changing out all other instances fixes compile error...
WOuld appreciate confirmation to be sure I did not just gloss over another problem I am unaware of.
WOuld appreciate confirmation to be sure I did not just gloss over another problem I am unaware of.
Re: C library for Sun/Moon effects
Open up Globals.h. Around line 612....rufessor wrote:guessing this was the correct thinking, changing it to boolean IsStorm; and changing out all other instances fixes compile error...
WOuld appreciate confirmation to be sure I did not just gloss over another problem I am unaware of.
here's the exert:
Code: Select all
/*
Vortech Modes
Used by the RF Expansion Module
*/
#define Constant 0
#define Random1 1 // Lagoonal
#define Random2 2 // Reef Crest
#define ShortWave 3
#define LongWave 4
#define Smart_NTM 5 // Nutrient Transport Mode
#define Smart_TSM 6 // Tidal Swell Mode
#define Feeding_Start 7
#define Feeding_Stop 8
#define Night_Stop 8
#define Night 9
#define Storm 10
#define Custom 11
#define Slave_Start 97
#define Slave_Stop 98
#define None 99
#define Radion 100
As a side note, I personally try to use this as a convention for my variable names:
All Caps for #define statements or constants
Lower case letter for variable type followed by variable name for all variables in a mixture of upper & lower case letters.
Sometimes I will put a m_ for a class member variable. Sometimes I will put a g_ for a global variable.
I "try" to follow this convention. It's a little tricky to do and since there are several people contributing code to the libraries, it's tough to keep things straight. I haven't just gone through and changed everything because when I do that, lots of peoples code will break and often times there's not a major benefit from it.
Anyways, that's just a side note on variable naming conventions.
Re: C library for Sun/Moon effects
Thanks-
I am not completely sure when I last had updated... but it was working with the last major update some months ago and then as of a few days ago I updated to whatever is the current version (using the mac updater) which then led to the error. I am pretty sure that
#define Storm
line did it.
I changed mine to IsStorm and it now compiles fine....
I *Think* I am kinda sorta following convention, I do not however use any #define statements in my code. I was under the impression that this was for a CONSTANT, i.e. the compiler simply replaces and instance of the keyword XX after the #define XXX in the code with whatever XXX was set to in the define statement. Since I have very few instances of constants in my code I use the header portion of the code to set up any variables (of any type, array, int, boolean, float) that are required within more than one function within my code. So since I have many instances in which this is the case, my code is "bloated" with tons of variable declarations but no #define statements.
I am (obviously) doing this for my own "fun" but also based upon some comments from Roberto, trying to make it portable by for instance enabling the DST calculation to be flagged OFF for individuals who do not use DST corrections for time. Should anyone end up using this they will need to play with a few (not many) mostly arrays that specify which lights are blue/white, and then with the "order" physically of the arrays over the tank to get the effects in terms of sunrise and sunset to work "right" as well as the could effect. Who knows if this will ever occur... for now I just want it to work on my tank. I *think* that its got to be really close with the changes I made and errors I caught so I will post back when I reload tonight and let it run for a few days.
Curious, thats what I am. I once was able to find video of someone who had something (I THINK) similar where it appeared that their LED would rapidly fade in and out for a cloud, and it looked super cool... so I am hoping this is what I get.
THanks.
If you have any plans for this *should* it work, maybe PM me with a list of stuff that you would like to see. I am totally willing to work on it to get it out for wider use but don't have any background on this- at all. I taught myself C++ to write this and it may or may not be user friendly, its always hard to read other peoples code (for me) so I kinda tried to format it according to some conventions I found on line in developer forums and follow rules or guide lines when possible so that it would at least be consistent. Changing variable names is not too difficult... but I HATE that the find and replace finds instances of say... Cloud in all these statements.
I.e.
int Cloud;// which is what I want to find
but also in
CloudEnd etc etc... everywhere there is a Cloud inside a phrase... anyway to get it to ignore that stuff... else changing is going to be more painful. Probably need to go to XCode or something, which I am currently playing with...
I am not completely sure when I last had updated... but it was working with the last major update some months ago and then as of a few days ago I updated to whatever is the current version (using the mac updater) which then led to the error. I am pretty sure that
#define Storm
line did it.
I changed mine to IsStorm and it now compiles fine....
I *Think* I am kinda sorta following convention, I do not however use any #define statements in my code. I was under the impression that this was for a CONSTANT, i.e. the compiler simply replaces and instance of the keyword XX after the #define XXX in the code with whatever XXX was set to in the define statement. Since I have very few instances of constants in my code I use the header portion of the code to set up any variables (of any type, array, int, boolean, float) that are required within more than one function within my code. So since I have many instances in which this is the case, my code is "bloated" with tons of variable declarations but no #define statements.
I am (obviously) doing this for my own "fun" but also based upon some comments from Roberto, trying to make it portable by for instance enabling the DST calculation to be flagged OFF for individuals who do not use DST corrections for time. Should anyone end up using this they will need to play with a few (not many) mostly arrays that specify which lights are blue/white, and then with the "order" physically of the arrays over the tank to get the effects in terms of sunrise and sunset to work "right" as well as the could effect. Who knows if this will ever occur... for now I just want it to work on my tank. I *think* that its got to be really close with the changes I made and errors I caught so I will post back when I reload tonight and let it run for a few days.
Curious, thats what I am. I once was able to find video of someone who had something (I THINK) similar where it appeared that their LED would rapidly fade in and out for a cloud, and it looked super cool... so I am hoping this is what I get.
THanks.
If you have any plans for this *should* it work, maybe PM me with a list of stuff that you would like to see. I am totally willing to work on it to get it out for wider use but don't have any background on this- at all. I taught myself C++ to write this and it may or may not be user friendly, its always hard to read other peoples code (for me) so I kinda tried to format it according to some conventions I found on line in developer forums and follow rules or guide lines when possible so that it would at least be consistent. Changing variable names is not too difficult... but I HATE that the find and replace finds instances of say... Cloud in all these statements.
I.e.
int Cloud;// which is what I want to find
but also in
CloudEnd etc etc... everywhere there is a Cloud inside a phrase... anyway to get it to ignore that stuff... else changing is going to be more painful. Probably need to go to XCode or something, which I am currently playing with...
Re: C library for Sun/Moon effects
LOL!!!
Hardly worth an apology. Its not like Storm was some original call on my part! And its really my mess... I am working in YOUR environment and loving it!
I reloaded fixed code after self administered beatings with the stupid stick... at least its appearing to work for now but the sun was in the process of setting just as I loaded code so I saw white light for about 30 seconds on one side of the tank then blues... will probably have to wait for the weekend again to catch a storm/cloud effect. But I SHOULD know if I fixed something by tomorrow evening, it was getting stuck in storms with whites off... so tomorrow (unless its not a cloudy day.. which is random) if its not stuck on BLUE when I get home, there is about a 70% chance I fixed it (which is the random cloud chance I have currently). Weekend will tell for sure. Probably other issues will crop up... but at least its kinda sorta working.
I was just happy that I had the right idea on the compile error
I hate to say this, but once this works I am going to have to actually use the controller and learn how to really program that! For now its monitoring my temperature and doing a BANG UP JOB displaying that on the LED
I am going to go back to square one figuring out how to set up the relays for different modes etc... but I think I should be able to move a bit more quickly given this experience. Probably should have done it in reverse- but this part seemed more interesting
Hardly worth an apology. Its not like Storm was some original call on my part! And its really my mess... I am working in YOUR environment and loving it!
I reloaded fixed code after self administered beatings with the stupid stick... at least its appearing to work for now but the sun was in the process of setting just as I loaded code so I saw white light for about 30 seconds on one side of the tank then blues... will probably have to wait for the weekend again to catch a storm/cloud effect. But I SHOULD know if I fixed something by tomorrow evening, it was getting stuck in storms with whites off... so tomorrow (unless its not a cloudy day.. which is random) if its not stuck on BLUE when I get home, there is about a 70% chance I fixed it (which is the random cloud chance I have currently). Weekend will tell for sure. Probably other issues will crop up... but at least its kinda sorta working.
I was just happy that I had the right idea on the compile error


I am going to go back to square one figuring out how to set up the relays for different modes etc... but I think I should be able to move a bit more quickly given this experience. Probably should have done it in reverse- but this part seemed more interesting

-
- Posts: 3
- Joined: Sun Apr 22, 2012 6:48 pm
Re: C library for Sun/Moon effects
Rufessor, do you have any updated data for your system? I am currently assembling an 8 channel LED lighting system using 3 PWM expansion outputs for Cree cool white LED's (78), 3 PWM expansion channels for Cree Royal Blue LED's 75), and the 2 PWM channels on the controller for Cree blue LED's (30). I would love to try your programming for my system. This system is over a 180 tank that is 72" in length, and if my calculations are correct should have more than adequate PAR at below full output for the deepest tanks.
I had the same idea to travel the lighting intensity across the lighting array to simulate the traverse of the sun (as well as the moon). I have been working on coding to do the same but have approached the problem from a different viewpoint.
I have uploaded year long sunrise.sunset table from the US Astronomical Society as well as their moonrise/moonset table. I then import this to Excel which I then import using C commands into a 2 dimensional array data file. My program then pulls the data from the array.
This is much simpler than the method you are using, as I don't need all the calculations necessary for determing the data in the tables. Several tables can be stored for multible years just renaming the tables so the program draws data from the proper table for a particular year which increments each year (I have 10 years stored).
Though not nearly as elegant as yours, it should function and take less memory.
I have not tried to write the code for clouds and storms, and your program seems to accomplish all of this.
Please let me know how your beta testing is looking.
I had the same idea to travel the lighting intensity across the lighting array to simulate the traverse of the sun (as well as the moon). I have been working on coding to do the same but have approached the problem from a different viewpoint.
I have uploaded year long sunrise.sunset table from the US Astronomical Society as well as their moonrise/moonset table. I then import this to Excel which I then import using C commands into a 2 dimensional array data file. My program then pulls the data from the array.
This is much simpler than the method you are using, as I don't need all the calculations necessary for determing the data in the tables. Several tables can be stored for multible years just renaming the tables so the program draws data from the proper table for a particular year which increments each year (I have 10 years stored).
Though not nearly as elegant as yours, it should function and take less memory.
I have not tried to write the code for clouds and storms, and your program seems to accomplish all of this.
Please let me know how your beta testing is looking.
Re: C library for Sun/Moon effects
Hi-
Thanks for the interest-
It is currently still a "beta" thats for sure, I have not completely fixed the issues I was and still am having, but I have spent in total about 10 minutes on it in the last 10 days or so as the last few weeks or more have been consumed with other responsibilities that must be placed higher in the que of things to do. I hope to actually get time to work on this this week as things are easing up a bit for the moment. I think you said you had 8 light strings... that might be somewhat of an issue, as I wrote this to work predominantly with a single PWM output module and did not account for the possibility to use the two on the controller as well, but its just a bit of looking to be sure the arrays will work. Let me get it working to my satisfaction, then we can PM about moving to your system, very possible and once I have it trouble free, probably only a very little bit of modification.
Check back here, I will post when its working to my satisfaction, or with additional questions if I run into issues I dont fully grasp.
Thanks for the interest-
It is currently still a "beta" thats for sure, I have not completely fixed the issues I was and still am having, but I have spent in total about 10 minutes on it in the last 10 days or so as the last few weeks or more have been consumed with other responsibilities that must be placed higher in the que of things to do. I hope to actually get time to work on this this week as things are easing up a bit for the moment. I think you said you had 8 light strings... that might be somewhat of an issue, as I wrote this to work predominantly with a single PWM output module and did not account for the possibility to use the two on the controller as well, but its just a bit of looking to be sure the arrays will work. Let me get it working to my satisfaction, then we can PM about moving to your system, very possible and once I have it trouble free, probably only a very little bit of modification.
Check back here, I will post when its working to my satisfaction, or with additional questions if I run into issues I dont fully grasp.
Re: C library for Sun/Moon effects
Found a "*small*" math error, adding anywhere from 0-~75,000 seconds to the length of a storm, depending upon where in the day you were. I guess this would make it appear that you were stuck in a storm!
We shall see if this was the issue.

We shall see if this was the issue.
Re: C library for Sun/Moon effects
Ok-
So although I am not yet prepared to declare it working, I am feeling like its down to a very few issues and have it running on my tank now. So- time to get back into my completely clueless hat (damn, did I ever take that off?)
So I left some serial comm stuff in the program because I knew I would want to communicate with the main controller. I am now at the point where it would be useful to have this going so that I could tell what the PWM output values were without a voltmeter, see if it was supposed to be cloudy, stormy, and check elapsed time and sunrise sunset without using the serial monitor, cause I need my lap top and everytime I start up the serial monitor (I.E. plug it back in) it basically resets the controller and then I end up having to wait another day or two to see if its working correctly as I am now checking that the overnight calculations occur but if I pulg and unplug the laptop I loose all that as it resets.
SO. 1st- my controller is NOT doing anything currently really, its plugged in mounted and the temp probe is in the tank, but I dont have anything plugged into it, not even the pH probe since I knew I wanted to get the lights done FIRST.
Therefore-
IF I want it to do the following,
Run the Refugium lighting, main pump, skimmer, and both heaters as well as both in tank powerheads (these may not be good for wavemaker functionality but at least would like to be able see that they are ON) based upon power to one of the outlets on the strip connected to the main controller, monitor temp, pH, and then allow for say a feeding/water change mode where the main pump and skimmer shut down... and then display on the main unit the following,
Sunrise
Sunset
Current time (I presuppose they will be using the same, do I need to enable this somehow?)
CLoud (only when its cloudy)
Storm (only when its stormy)
and Channel 0-5 current intensity output (0-255)
pH
Temp,
Powerstrip ON/Off as say green or red dots (whatever)
Whats the best way to start, i.e. run a RAgen to create a basic script, then put in the comm, and display? Or is there a somewhat pre-built code I could grab.
Wish I felt confident about this part, but its starting from zero as I have focused on the coding part, so now... I need to learn more about the controller. Not starting from zero but have played MUCH less here.
Basically, if I can get a handle on a prebuilt set of code I am fairly confident I can follow it and modify it where needed based upon my experience writing this... I feel like I can now read and write code but I am sure I will be tripped up a bit here and there.
I think If I get it to do this much, should people have say an RF module with a Vortech pump, if I have it bringing in information to say that its a storm, they would then easily be able to use that to change modes on the pump etc etc.
For me, its the final stage of both trouble shooting as well as functionality. Reason I ask, is if I run RAGen I will end up with a ton of adds... so if anyone knows some .ino file that has comm to the PWM board built in, and a little display mod built in, I could probably modify this faster than building it- maybe.
ADVICE?
So although I am not yet prepared to declare it working, I am feeling like its down to a very few issues and have it running on my tank now. So- time to get back into my completely clueless hat (damn, did I ever take that off?)
So I left some serial comm stuff in the program because I knew I would want to communicate with the main controller. I am now at the point where it would be useful to have this going so that I could tell what the PWM output values were without a voltmeter, see if it was supposed to be cloudy, stormy, and check elapsed time and sunrise sunset without using the serial monitor, cause I need my lap top and everytime I start up the serial monitor (I.E. plug it back in) it basically resets the controller and then I end up having to wait another day or two to see if its working correctly as I am now checking that the overnight calculations occur but if I pulg and unplug the laptop I loose all that as it resets.
SO. 1st- my controller is NOT doing anything currently really, its plugged in mounted and the temp probe is in the tank, but I dont have anything plugged into it, not even the pH probe since I knew I wanted to get the lights done FIRST.
Therefore-
IF I want it to do the following,
Run the Refugium lighting, main pump, skimmer, and both heaters as well as both in tank powerheads (these may not be good for wavemaker functionality but at least would like to be able see that they are ON) based upon power to one of the outlets on the strip connected to the main controller, monitor temp, pH, and then allow for say a feeding/water change mode where the main pump and skimmer shut down... and then display on the main unit the following,
Sunrise
Sunset
Current time (I presuppose they will be using the same, do I need to enable this somehow?)
CLoud (only when its cloudy)
Storm (only when its stormy)
and Channel 0-5 current intensity output (0-255)
pH
Temp,
Powerstrip ON/Off as say green or red dots (whatever)
Whats the best way to start, i.e. run a RAgen to create a basic script, then put in the comm, and display? Or is there a somewhat pre-built code I could grab.
Wish I felt confident about this part, but its starting from zero as I have focused on the coding part, so now... I need to learn more about the controller. Not starting from zero but have played MUCH less here.
Basically, if I can get a handle on a prebuilt set of code I am fairly confident I can follow it and modify it where needed based upon my experience writing this... I feel like I can now read and write code but I am sure I will be tripped up a bit here and there.
I think If I get it to do this much, should people have say an RF module with a Vortech pump, if I have it bringing in information to say that its a storm, they would then easily be able to use that to change modes on the pump etc etc.
For me, its the final stage of both trouble shooting as well as functionality. Reason I ask, is if I run RAGen I will end up with a ton of adds... so if anyone knows some .ino file that has comm to the PWM board built in, and a little display mod built in, I could probably modify this faster than building it- maybe.
ADVICE?
Re: C library for Sun/Moon effects
Have you looked at the Example Code from the Installed inside Arduino? That contains a lot of good examples. It's under File->Sketchbook->Examples Codes.
I would suggest you just generate a quick "default" base code from RAGen and add the following features: Custom Main Screen, Custom Menu (have it mimic the Simple Menu), PWM Expansion.
Then obviously set your devices on the proper ports and what ports you want toggled on/off during the modes. Don't get carried away....keep it simple.
This will give you a nice and clean base code to start. Once you have this base set, use some of the examples to tweak a main screen and then tweak some of your menu entries to trigger modes and such.
I would suggest you just generate a quick "default" base code from RAGen and add the following features: Custom Main Screen, Custom Menu (have it mimic the Simple Menu), PWM Expansion.
Then obviously set your devices on the proper ports and what ports you want toggled on/off during the modes. Don't get carried away....keep it simple.
This will give you a nice and clean base code to start. Once you have this base set, use some of the examples to tweak a main screen and then tweak some of your menu entries to trigger modes and such.
Re: C library for Sun/Moon effects
Thanks-
I will do EXACTLY as you say with RAGen and move ahead from there. Was trying to decide what was best way forward, thanks for the advice.
I will do EXACTLY as you say with RAGen and move ahead from there. Was trying to decide what was best way forward, thanks for the advice.
Re: C library for Sun/Moon effects
No problem at all. Let me know if you need help with things.rufessor wrote:Thanks-
I will do EXACTLY as you say with RAGen and move ahead from there. Was trying to decide what was best way forward, thanks for the advice.
Re: C library for Sun/Moon effects
OK-
I am prepared to say that I *think* that the code is now working with a few unknowns.
I am fairly sure I am getting correct sunrise and sunset values and the lights are coming on over my tank according to the offsets I have programmed. I caught a 1 hr problem so the actual out the window sunrise and sunset should now match perfectly to a few seconds the tank rise and set.
I am less sure that the cloud/storm affects are entirely working, but if others load and run this it will actually help with trouble shooting this. I do think that at least it will no longer get stuck in storms.
IF your interested in trying this on your tank, I think I am confident enough to say lets give it a try. Problems if any should be fairly easy to fix and I will try to be responsive to issues and trouble shooting.
Two things-
It may NOT be visually apparent that the lights are dimming or brightening over the tank during a cloud as you WATCH the tank in real time, but it should become OBVIOUS as they dim to very low levels. It would help to figure out what type of ramp/sec values I need to use to get a visual effect - the current values are what I think should be almost TOO much light variation but what I think and what is visually apparent are not probably perfectly correlated. Feed back if its used would be very welcome
When you hit a storm (which may or may not happen often enough... so let me know) the tank will go to ALL blues but STILL ramp from the flicker point to the 100% intensity value you set and should ramp more quickly, changes in intensity every 1 second. I would anticipate that this should be immediately apparent as I have it set to maximally ramp by 10% in intensity every second... but its a random number so I MAY need to move this to 20 or even 25 % as the random ramp point will vary from +/- the max value, and thus would be expected to kinda center on zero... so I might need to re-evaluate this but other examples of random walks using similar algorithms do show random walks. Let me know what you see.
You will or at least SHOULD get lightning at some point in a storm. It should FLASH the whites some random number of times in a row with random spacing between the flashes of up to about 1.5 seconds with the flash duration randomly centered on the duration of a real lightning strike (like 40-80 msec or something)
I have not yet been home to see any of the storm/cloud stuff but its RUNNING over my tank and I am getting lights on and off as scheduled and thus am ASSUMING that this part is either working, or at least not killing the program. IT IS POSSIBLE that I have not hit a cloudy day and that there is a serious error that will result in your lights going to blue and not coming back or something like that (thats about the worst case).
The lights will NEVER be set above your max values (0-255 output on PWM Channel0-5)
For now, I guess I will post the code.
Anyone trying this, grab the code- LOOK through it more or less line by line with some emphasis on the header then PM me and I will try to get you all the information on what NEEDs to be customized. Once I get this into a list that works for one person I will reproduce that list here as a how to guide.
Please be aware you will be helping to develop this and thus it may not work 100% at this point, but I am confident enough to put it up for real use now hoping that any additional errors will be caught MORE quickly with other users trying this out.
KEEP Your old PWM code that your using and be ready to load it if you run into trouble. I am going to try to be responsive but this is NOT in any way part of my normal job/schedule and I may or may not see PM or posts here as quickly as the true developers do so be patient and simply move back to what was working for you if things go wrong, BUT LET ME KNOW.
I am prepared to say that I *think* that the code is now working with a few unknowns.
I am fairly sure I am getting correct sunrise and sunset values and the lights are coming on over my tank according to the offsets I have programmed. I caught a 1 hr problem so the actual out the window sunrise and sunset should now match perfectly to a few seconds the tank rise and set.
I am less sure that the cloud/storm affects are entirely working, but if others load and run this it will actually help with trouble shooting this. I do think that at least it will no longer get stuck in storms.
IF your interested in trying this on your tank, I think I am confident enough to say lets give it a try. Problems if any should be fairly easy to fix and I will try to be responsive to issues and trouble shooting.
Two things-
It may NOT be visually apparent that the lights are dimming or brightening over the tank during a cloud as you WATCH the tank in real time, but it should become OBVIOUS as they dim to very low levels. It would help to figure out what type of ramp/sec values I need to use to get a visual effect - the current values are what I think should be almost TOO much light variation but what I think and what is visually apparent are not probably perfectly correlated. Feed back if its used would be very welcome
When you hit a storm (which may or may not happen often enough... so let me know) the tank will go to ALL blues but STILL ramp from the flicker point to the 100% intensity value you set and should ramp more quickly, changes in intensity every 1 second. I would anticipate that this should be immediately apparent as I have it set to maximally ramp by 10% in intensity every second... but its a random number so I MAY need to move this to 20 or even 25 % as the random ramp point will vary from +/- the max value, and thus would be expected to kinda center on zero... so I might need to re-evaluate this but other examples of random walks using similar algorithms do show random walks. Let me know what you see.
You will or at least SHOULD get lightning at some point in a storm. It should FLASH the whites some random number of times in a row with random spacing between the flashes of up to about 1.5 seconds with the flash duration randomly centered on the duration of a real lightning strike (like 40-80 msec or something)
I have not yet been home to see any of the storm/cloud stuff but its RUNNING over my tank and I am getting lights on and off as scheduled and thus am ASSUMING that this part is either working, or at least not killing the program. IT IS POSSIBLE that I have not hit a cloudy day and that there is a serious error that will result in your lights going to blue and not coming back or something like that (thats about the worst case).
The lights will NEVER be set above your max values (0-255 output on PWM Channel0-5)
For now, I guess I will post the code.
Anyone trying this, grab the code- LOOK through it more or less line by line with some emphasis on the header then PM me and I will try to get you all the information on what NEEDs to be customized. Once I get this into a list that works for one person I will reproduce that list here as a how to guide.
Please be aware you will be helping to develop this and thus it may not work 100% at this point, but I am confident enough to put it up for real use now hoping that any additional errors will be caught MORE quickly with other users trying this out.
KEEP Your old PWM code that your using and be ready to load it if you run into trouble. I am going to try to be responsive but this is NOT in any way part of my normal job/schedule and I may or may not see PM or posts here as quickly as the true developers do so be patient and simply move back to what was working for you if things go wrong, BUT LET ME KNOW.
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 <Globals.h>
#include <ReefAngel_Features.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>
#include <SWFLTEK_EPHERMA.h>
//*********************************************************************************************************************************
//IF YOU DO NOT WISH TO USE the U.S.A. Day Light Savings Time rules to modify the time stamp for rise/set calculations...set to false- i.e. if you dont live in the USA etc
boolean ApplyDST=true;
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[6];// Array to store current setpoint for PMW control of output to ch (0,1,2,3,4,5)
//set up channel max intensity arrays. comment is just illustrating value range possible
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[]={26,26,26,26,10,0};//need to input actual values here
byte ChInt[5];
//YOU MUST CHANGE THIS- designate channels of white light (channels you want off during Storm and used for lightning strikes)
//use 1 to designate white channel. Array corresponds to PWM channel 0-5 in order
boolean Wchannel[]={1,0,1,0,0,0,0};
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;
boolean trigger, trigger2; //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, Cloud, CloudToday, 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.
unsigned long lastmillis;// variable to track millis to enable cloud and insolation loop restriction by time
boolean CloudAdvance=false;//cloud timer for light effect advance
boolean StormAdvance=false;//storm timer for light effect advance
boolean InsolationAdvance=false;//when true we recalculate light intensity during clear sky
byte counter;//used to track millis advance for insolation,cloud trigger
//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,};
//****************************************
//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.
now();//why are you here?
CloudToday=false;
trigger=true;
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 ((hour())==0 && (minute())==0 && (second())==0){
trigger2=true;//decided to put this in loop to avoid continued redundant calls to CalSun()- note trigger is set to true on resart
}
//Use millis to enable tracking of time interval
if ((millis()-lastmillis)>=1000){
StormAdvance=true;
lastmillis=millis();
counter++;//use this to see if time has moved ahead 2, or, 3 seconds from first check which is 1 second advance
switch (counter){
case (2):
CloudAdvance=true;
break;
case (3):
InsolationAdvance=true;
counter=0;
break;
}
}
// now run through rise/set calculation and then intensity set and finally weather overlay
if ((trigger==true) || (trigger2==true)){
CalSun();
}
Insolation();
Weather();
//check to see if were need to have a lightning strike
if (StrikeNow==false){
StrikeStart=millis();
StrikeCount=0;
}
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.
if (isDST==true){
elapsedTime=(((now()-946684800)+3600)-newDay);
}
else if (isDST==false){
elapsedTime=((now()-946684800)-newDay);
}
//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();
// Individual Channel
if (cmd>=0 && cmd<=5){
ChannelValue[cmd]=data;
analogWrite(PWMports[cmd],data);
}
if (cmd==6){
//Storm=true;
}
}
//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()
{
// Every day at midnight, we calculate rise, set, time of highest sun, moon phase, or we do this if system has reset
// Serial.println("trigger 1");
// Serial.println(trigger);
unsigned long SecInput;
//void CalcDST(day(),month(),weekday()) modifies isDST memory to appropriate true/false based upon USA rules
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
if (trigger==true){//ON RESET need to calculate how far into day we are to feed correct val to sunrise sunset for midnight
long hours;//possible to be reset at 23 hours*3600=long required
long minutes;// int is enough as 60*60 is max val but math is conducted with possibility of roll over in intermediate so to be sure.. use long
time_t t=now();
hours=hour(t);
minutes=minute(t);
if ((ApplyDST==true) && (isDST==true)){
hours=hours+1;//DST correction
newDay=(now()-(946684800+((hours*3600)+(minutes*60)))); //yes I am ignoring seconds... if your worried about your rise being off by <60 seconds on a day of a power failure... your more anal than I
/* Serial.print("DST Hours,Min");
Serial.print(hours);
Serial.print(",");
Serial.println(minutes);*/
}
else if ((isDST==false) || (ApplyDST==false)){
newDay=(now()-(946684800+((hours*3600)+(minutes*60)))); //yes I am ignoring seconds... if your worried about your rise being off by <60 seconds on a day of a power failure... your more anal than I
/* Serial.print("Hours,Min");
Serial.print(hours);
Serial.print(",");
Serial.println(minutes);*/
}
}
if (trigger2==true){// When we are at midnight we just use simple correction of unix time to year 2000 time keeping
if ((ApplyDST==true) && (isDST==true)){
newDay=(now()-946684800+3600);
}
else if ((isDST==false) || (ApplyDST==false)){
newDay=(now()-946684800);
}
}
SecInput=((newDay)+25200);//modify to GMT time (i.e. MST + 7 hours = GMT)
//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
rise=SecInput;
set=SecInput;
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
rise-=25200; //Correct back to MST from GMT- DST still accounted for
set-=25200;
/*Serial.print("newDay=");
Serial.println(newDay);
Serial.print("Local uncorrected rise, set=");
Serial.print(rise);
Serial.print(",");
Serial.print(set);*/
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);
//*********************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=70;//% 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;
}
else if (RainMaker>=CloudChance){
CloudToday=false;
trigger=false;
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);
byte OvercastMin=10;//% as fraction (0-1 is 0-100%) of daylight hours that would minimially be cloudy on a cloud day
byte OvercastMax=31;//% of daylight hours that would maximally be cloudy on a cloud day (then add 1 to that #)
float Overcast=random(OvercastMin,OvercastMax);
Overcast=(Overcast/100);
//Serial.println("Overcast");
//Serial.println(Overcast);
// number of clouds possible for the day, max and min
byte CloudsMax=9;
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);
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*/
long CloudLength;
CloudLength=((dayLength*Overcast)/CloudsTotal);//average cloud length
float fraction=random(20,181);
fraction=(fraction/100);//vary each cloud from 20%-180% of an equal fraction of total cloud for day
//Serial.print("fraction ");
//Serial.println(fraction);
//Serial.print("CloudLength ");
// Serial.println(CloudLength);
//using fraction to vary cloud length throughout day fill array CloudMaster[] at positions 0,2,4,6 to full with durations in seconds
//this way the MAX cloud duration is 180% of the average cloud druation while the min is 20%
byte b=0;//set up counter to alter cloud length and initialize to zero outside of loop
for (byte a=0; a<CloudsTotal; a++){
int CurrentCloudLength;
// if were having an odd # of clouds make sure last one is full length
if ((CloudsTotal%2!=0) && (a==(CloudsTotal-1))){
CloudMaster[a*2]=CloudLength;
/*Serial.print("last odd cloud Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);*/
}
// else we make clouds variable in length short then long repeated with every two cloud sets being different in short and long but adding up equally by sets
else {
if (fraction<1){
CurrentCloudLength=abs(CloudLength*b+(b*CloudLength-(CloudLength*fraction)));
CloudMaster[a*2]=CurrentCloudLength;
/*Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);*/
}
if (fraction==1){
CloudMaster[a*2]=CloudLength;
/*Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);*/
}
if (fraction>1){
CurrentCloudLength=((CloudLength*fraction)-(b*CloudLength*fraction))+(b*((2-fraction)*CloudLength));
CloudMaster[a*2]=CurrentCloudLength;
/*Serial.print("Cloudmaster[a]=");
Serial.println(a*2);
Serial.print("fraction=");
Serial.println(fraction);
Serial.print("CloudMaster=");
Serial.println(CloudMaster[a*2]);
Serial.print("b=");
Serial.println(b);*/
}
}
b=b+1;
if (b==2){
b=0;
fraction=random(20,181);//every other cloud recalculate a new fractional distribution for the next set
fraction=(fraction/100);
}
}
randomSeed(now());
// Now space the clouds out during the day using random fractionation of day segments as per cloud calculation
b=0;//reset counter used for fraction reset
byte c=0;//counter for indexing # of itterations in loop
long StartTime;//Used in loop to assign cloud spacing, i.e. these loop values determine daylight interval between clouds
fraction=random(10,181);
fraction=(fraction/100);
for (byte a=1; a<(CloudsTotal*2); a=(a+2)){
//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.
// must index segment time by +1 segment for each cloud completed
long SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));
if (fraction<1){
abs(CloudLength*b+(b*CloudLength-(CloudLength*fraction)));
StartTime=rise+((SunSegment*b+abs(b*SunSegment-(SunSegment*fraction)))+(c*SunSegment));
CloudMaster[a]=StartTime;
StartTime=0;
b=b+1;
}
else if (fraction==1){
StartTime=rise+(SunSegment+(SunSegment*c));
CloudMaster[a]=StartTime;
StartTime=0;
b=2;//by doing this I ensure that new fraction will be calculated so next interval will not be 1 (or rarely so)
}
else if (fraction>1){
StartTime=rise+(((SunSegment*fraction)-(b*SunSegment*fraction))+(b*((2-fraction)*SunSegment))+(c*SunSegment));
CloudMaster[a]=StartTime;
StartTime=0;
b=b+1;
}
if (b==2){
b=0;
fraction=random(20,181);//every other cloud recalculate a new fractional distribution for the next set
fraction=(fraction/100);
}
c++; //index loop counter by one now
//Serial.print("CloudMaster position number, ");
//Serial.print(a);
//Serial.print(" is= ");
//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<(CloudsTotal*2);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]);
}
//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]);
}
}//END FUNCTION
void Insolation()
{
if (InsolationAdvance==true){//only change lights every 3 seconds
//Serial.println("Insolation 2 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){
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.print("ch0 setting first 1/2 day=");
//Serial.println((byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b])+flicker[b],6);
break;
case 2:
ChannelValue[1]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-1])+flicker[b-1];
break;
case 4:
ChannelValue[2]=(byte)(-cos(PiHalf+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
break;
case 6:
ChannelValue[3]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-3])+flicker[b-3];
break;
case 8:
ChannelValue[4]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-4])+flicker[b-4];
break;
case 10:
ChannelValue[5]=(byte)(-cos(PiHalf+(ChSlope[b])*secSoFar)*ChInt[b-5])+flicker[b-5];
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;
}
}
}
}
if (elapsedTime>=midDay){
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.print("ch0 setting second 1/2 day=");
//Serial.println((-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-1]),6);
break;
case 3:
ChannelValue[1]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-2])+flicker[b-2];
break;
case 5:
ChannelValue[2]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-3])+flicker[b-3];
break;
case 7:
ChannelValue[3]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-4])+flicker[b-4];
break;
case 9:
ChannelValue[4]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-5])+flicker[b-5];
break;
case 11:
ChannelValue[5]=(byte)(-cos(Pi+(ChSlope[b]*secSoFar))*ChInt[b-6])+flicker[b-6];
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;
}
}
}
}
InsolationAdvance=false;
/* Serial.print("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
if (Cloud==false){
for (byte a=0; a<CloudsTotal; a=(a+2)){
if ((elapsedTime>=*(pCloudPoint+a)) && (elapsedTime<=*(pCloudPoint+(a+1)))){//time needs to be within a cloud segment, not just greater than any start
CloudEnd=*(pCloudPoint+(a+1));
CloudEnd=CloudEnd-120;
Cloud=true;
}
else{
CloudCover=0;
PriorCloudCover=0;
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 every 2 seconds");
if (elapsedTime>=CloudEnd){
ClearSky(CloudCover, CloudEnd);
return;
}
/*Use fractional intensity to set minimum value for any channel its WAY to confusing to try to set up otherwise. 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. (CloudCover/100)*Insolation SetPoint
is how the current cloud intensity is set*/
randomSeed(millis());
byte stepsize=random(-20,21);
PriorCloudCover=CloudCover;
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
long CloudLeft=(CloudEnd-elapsedTime);//Check to be sure we have enough cloud left to fit a storm in if not skip it)
if (CloudLeft>300){
IsStorm=true;
wtrigger=true;
}
if (CloudLeft<=300){
CloudCover-=(stepsize*1.5);//reflect to clearer sky (i.e. we had to be adding to get here so subtract val)
}
}
else if (CloudCover<=0){//i.e if were bright sky in a cloud.. uhh.. we need a cloud
CloudCover-=(stepsize*1.5);
}
for (int a=0;a<6;a++){
if (DimOrder[a]==0){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(PriorCloudCover/100))))+flicker[a]; ;
}
if (DimOrder[a]==1){
ChannelValue[a]=(byte)(((ChannelValue[a]-flicker[a])*(float)(1-((float)CloudCover/100)))+flicker[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;
int LongestStorm=(CloudEnd-StormStart);
LongestStorm-=120;//remove 2 mins from longest storm so that we end up with 2 minutes of cloud after the storm
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.
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;
wtrigger=false;//allow next strom to be randomized correctly, redundant call but just to be safe.
return;
}
byte stepsize;
randomSeed(millis());
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
int StartStrike=0;
//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){
StartStrike+=random(100,1500);//position 0,2,4,6,8.. is strike delay
StrikeMaster[a]=StartStrike;
StartStrike=0;
}
else if (a%2!=0){
StartStrike+=random(30,81);//position 1,3,5,7,9... is strike duration- added to StartStrike=StrikeEnd
StrikeMaster[a]=StartStrike;
StartStrike=0;
}
}
StrikeNow=true; //Trigger start of timming loop in "loop" to initiate Strike pattern do this last so were all good on settings
}
strikeCount=0;
}
PriorCloudCover=CloudCover;
CloudCover = CloudCover + stepsize;
if (CloudCover>=100){//if were too dark reflect random path to light
CloudCover-=(stepsize*2);
}
else if (CloudCover<=0){
CloudCover-=(stepsize*2);//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)(PriorCloudCover/100))))+flicker[a];
}
else if (DimOrder[a]==1){
ChannelValue[a]=(byte)((ChannelValue[a]-flicker[a])*(float)(1-((float)(CloudCover/100))))+flicker[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
-
- Posts: 3
- Joined: Sun Apr 22, 2012 6:48 pm
Re: C library for Sun/Moon effects
Thanks for the heads-up! However, I have been frequently checking the forum and saw your post this morning. I have already downloaded and printed out your program, and will hopefully learn how you are doing everything. My light should be completed this week.
It's taking me a while since this is my first LED fixture and I've done extensive research with optics, and have spent a good deal of time on the phone with Cree and LuxDrive. Hopefully it will work as well as anticipated.
I will have the 3 white channels and the 3 Royal Blue channels on the expansion, and the 2 channels for the blues on the controller.
I'll update you further on what I'm changing after I fully comprehend your program.
Thanks alot!
It's taking me a while since this is my first LED fixture and I've done extensive research with optics, and have spent a good deal of time on the phone with Cree and LuxDrive. Hopefully it will work as well as anticipated.
I will have the 3 white channels and the 3 Royal Blue channels on the expansion, and the 2 channels for the blues on the controller.
I'll update you further on what I'm changing after I fully comprehend your program.
Thanks alot!
Re: C library for Sun/Moon effects
Just a quick note. I discovered a fault in how I have assigned the triggers to get the clouds to update intensity every 2 seconds. It just takes longer than 2 the way I have it written. These are the types of errors I suspect will arrise with more testing, not deal breakers but subtle errors or at least deviations from what I want to happen. I will post a correction to the few lines of code involved.
I also note that if you restart the program late at night, i.e. before midnight but such that correcting to GMT pushes the sunrise sunset calculation into the next day (i.e. I have it set to ADD 7 hours- you will need to correct this if your not in my time zone), even though its late, when it reboots it will light up the tank. Probably explained by edit I just added
EDIT-
I JUST figured out that I have been wrongly correcting the Unix time stamp to GMT... its IN GMT.
Working on this now.
I plan to implement a new variable to indicate the number of seconds of offset to GMT for YOUR time zone... for now you will need to find that correction (its 25200... in my code) if your looking for it.
Will figure out how to do this to make it more portable, fix the time advance problem (does not effect function, just timing) and anything else I spot and continue to post updates. If they are EASY to copy and paste I will just post new code with old code that needs to be pasted over, if its a distributed fix over the entire code I will simply post a new .ino in entirety as I find that its really hard for me to figure out what people want to remove/fix when they post incremental fixes to lines that are not in a block.
I also note that if you restart the program late at night, i.e. before midnight but such that correcting to GMT pushes the sunrise sunset calculation into the next day (i.e. I have it set to ADD 7 hours- you will need to correct this if your not in my time zone), even though its late, when it reboots it will light up the tank. Probably explained by edit I just added
EDIT-
I JUST figured out that I have been wrongly correcting the Unix time stamp to GMT... its IN GMT.
Working on this now.
I plan to implement a new variable to indicate the number of seconds of offset to GMT for YOUR time zone... for now you will need to find that correction (its 25200... in my code) if your looking for it.
Will figure out how to do this to make it more portable, fix the time advance problem (does not effect function, just timing) and anything else I spot and continue to post updates. If they are EASY to copy and paste I will just post new code with old code that needs to be pasted over, if its a distributed fix over the entire code I will simply post a new .ino in entirety as I find that its really hard for me to figure out what people want to remove/fix when they post incremental fixes to lines that are not in a block.
-
- Posts: 3
- Joined: Sun Apr 22, 2012 6:48 pm
Re: C library for Sun/Moon effects
When trying to compile your program, I get errors in CalSun of undeclared latitude, longitude, dmsToSeconds, SunRise and Sunset. Am I missing something? Also, where does dmsToSec come from?
Re: C library for Sun/Moon effects
Ok... first things first. I made some changes to make it easier for others to use this, so new code will be posted after I test it (it compiles but I want to be sure it also mostly works, i.e. didn't break it further than it perhaps was).
Then
I assume you have the .h file and named it exactly as I did in the #include statement and that its in your library folder in arduino wherever that exits on your system.
Then, if you look for dmsToSec you will find it in the Void CalSun() function
It converts a latitude and longitude specified in degrees minutes and seconds to a the format required for the library (which I did not write), that is a call to the library.
SunRise and SunSet are also calls to the library- which uses pointers to modify the memory location for the variables rise and set to the values calculated by the library.
latitude and longitude are the variables that hold the dmsToSeconds and are declared in the header but initialized with a value in CalSun using the dmsToSeconds library call..
I would guess you don't have the library correctly installed. Look back a few pages... the initial description I posted at the beginning of the thread is WRONG as I was assuming things that I thought the original author wanted to be done, which in fact were NOT supposed to be done. It SHOULD be a SINGLE file.
I posted the entire file a bit ago with basic instructions to make it. Super easy.
But WAIT for the update, I had some weird crap dealing with DST corrections and was getting wrong sunrise and sunset values, I made it MUCH less complex now and I think it should work, but gotta try it 1st. Will post when I know its working or at least that its still being worked on.
Then
I assume you have the .h file and named it exactly as I did in the #include statement and that its in your library folder in arduino wherever that exits on your system.
Then, if you look for dmsToSec you will find it in the Void CalSun() function
It converts a latitude and longitude specified in degrees minutes and seconds to a the format required for the library (which I did not write), that is a call to the library.
SunRise and SunSet are also calls to the library- which uses pointers to modify the memory location for the variables rise and set to the values calculated by the library.
latitude and longitude are the variables that hold the dmsToSeconds and are declared in the header but initialized with a value in CalSun using the dmsToSeconds library call..
I would guess you don't have the library correctly installed. Look back a few pages... the initial description I posted at the beginning of the thread is WRONG as I was assuming things that I thought the original author wanted to be done, which in fact were NOT supposed to be done. It SHOULD be a SINGLE file.
I posted the entire file a bit ago with basic instructions to make it. Super easy.
But WAIT for the update, I had some weird crap dealing with DST corrections and was getting wrong sunrise and sunset values, I made it MUCH less complex now and I think it should work, but gotta try it 1st. Will post when I know its working or at least that its still being worked on.
Re: C library for Sun/Moon effects
Ok-
I think I was causing myself some strange issues with the system clock by having some old serial comm code in which was not being used but removing it fixed things... at least I cannot find the time error anymore.
I also re-wrote a few parts that were difficult for me to follow/I didn't like because I was not sure I was getting everything just right.
Now, I am confident that at least testing it tonight, its getting absolutely correct rise and set as well as elapsed times of the day that match my LOCAL time zone.
I have implemented a number of changes to the code.
Please look at the header and change everything that is on top grouped together, these are MOST but not all of the variables or arrays that are or may be different on your system.
Then in the CODE.
byte CloudChance=100; this is the percent chance of cloudy day change it if you desire
byte CloudsMax=8 is the MAXIMUM NUMBER ALLOWED DO NOT GO OVER THIS
byte CLoudsMin=4 is where I have it set, you could use 2-8 maybe not 1 (didn't fully work this out, might be ok)
byte OvercastMin=10 is where I have it, its the minimum % of daylight that will be cloudy (distributed over 4-8 clouds)
byte OvercastMax=31 is the MAX% of daylight hours that might be cloudy (distributed in4-8 clouds)
LongestStorm- LOOK in the weather function in the if loop with Storm==true you can modify the max duration of a storm, I used to let it run for a random duration of the remaining cloud, but this can get too long... so now you can limit it to XXX seconds (or XXXX or XX or whatever you want)
CHANGE all these if you want.
I think thats about it (probably missed a few but its more details like how to mod storm and cloud intensity changes but really, read the code that part is well annotated)
Then finally
THIS IS DESIGNED TO WORK WITH THE PWM EXPANSION BOX ONLY. It is not designed to run the PWM channels on the controller, easy to do probably but I would need help and someone who knows how to comm BACK to the controller to send it the set points, and a test system since mine DOES NOT HAVE THESE channels.
I think I was causing myself some strange issues with the system clock by having some old serial comm code in which was not being used but removing it fixed things... at least I cannot find the time error anymore.
I also re-wrote a few parts that were difficult for me to follow/I didn't like because I was not sure I was getting everything just right.
Now, I am confident that at least testing it tonight, its getting absolutely correct rise and set as well as elapsed times of the day that match my LOCAL time zone.
I have implemented a number of changes to the code.
Please look at the header and change everything that is on top grouped together, these are MOST but not all of the variables or arrays that are or may be different on your system.
Then in the CODE.
byte CloudChance=100; this is the percent chance of cloudy day change it if you desire
byte CloudsMax=8 is the MAXIMUM NUMBER ALLOWED DO NOT GO OVER THIS
byte CLoudsMin=4 is where I have it set, you could use 2-8 maybe not 1 (didn't fully work this out, might be ok)
byte OvercastMin=10 is where I have it, its the minimum % of daylight that will be cloudy (distributed over 4-8 clouds)
byte OvercastMax=31 is the MAX% of daylight hours that might be cloudy (distributed in4-8 clouds)
LongestStorm- LOOK in the weather function in the if loop with Storm==true you can modify the max duration of a storm, I used to let it run for a random duration of the remaining cloud, but this can get too long... so now you can limit it to XXX seconds (or XXXX or XX or whatever you want)
CHANGE all these if you want.
I think thats about it (probably missed a few but its more details like how to mod storm and cloud intensity changes but really, read the code that part is well annotated)
Then finally
THIS IS DESIGNED TO WORK WITH THE PWM EXPANSION BOX ONLY. It is not designed to run the PWM channels on the controller, easy to do probably but I would need help and someone who knows how to comm BACK to the controller to send it the set points, and a test system since mine DOES NOT HAVE THESE channels.
Re: C library for Sun/Moon effects
OK... So I fixed a lot of stuff that was sorta not working, and managed to at one point overwrite an array by a fairly large amount (say double its size) which somehow ran into the memory spot on the PWM board for the time so that my board briefly thought it was about 1 billion seconds prior to today... which was interesting to trouble shoot.
But. Past this and I am convinced that much is working, but somehow I have managed to screw up the sunrise and sunset which was working PERFECTLY the last few days, literally I could look out my window and watch the sun hit the horizon and the lights in the tank would set (whites dim and then off)... I have my blues on extended and that worked as well.
But now its telling me sunrise is like 11 am or some screwed up value and I swear I did not F*$% with that at all.... so this is why I removed the prior posted code.
When its fixed I will put it back up, but with people wanting to try this I would rather make you wait and use something that works than figure its a POS and never try it after working on it... so please be patient. I am beginning to appreciate that this is a fairly complex bit of code for me and lots of pieces interact so since I taught myself how to code and the last computer course I took was in junior high on an apple II or something... well... bare (or is it bear) with me~!
But. Past this and I am convinced that much is working, but somehow I have managed to screw up the sunrise and sunset which was working PERFECTLY the last few days, literally I could look out my window and watch the sun hit the horizon and the lights in the tank would set (whites dim and then off)... I have my blues on extended and that worked as well.
But now its telling me sunrise is like 11 am or some screwed up value and I swear I did not F*$% with that at all.... so this is why I removed the prior posted code.
When its fixed I will put it back up, but with people wanting to try this I would rather make you wait and use something that works than figure its a POS and never try it after working on it... so please be patient. I am beginning to appreciate that this is a fairly complex bit of code for me and lots of pieces interact so since I taught myself how to code and the last computer course I took was in junior high on an apple II or something... well... bare (or is it bear) with me~!
Re: C library for Sun/Moon effects
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
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
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
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