Weather Simulation for Dimming expansion module

Share you PDE file with our community
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Hello-

I actually left the serial debugging for this segment into the code you downloaded... for this exact reason ;)

Uncomment this stuff and then run your serial monitor when you Dimming module is plugged into the computer. I would test and trouble shoot this in a much simpler fashion. Plug in the dimming module into the computer, and even unplug the USB from the controller to the dimming module. Then load your code during daylight hours (or really, even at night) and as SOON as the load finishes hit the command key sequence or use the menu in Arduino to start the serial monitor. You need to be fairly quick since the part of the code that calculates and displays this only runs at boot or at midnight... Then just divide your rise and set values by 3600 to get decimal hours into todays day for the rise and set. Do not expect it to be exact but it should be recognizable as a normal day +/- some minutes or even 10's of minutes depending on your lat/lon and the date (OH... CHECK to be sure you using the correct sign on the lat and lon... and don't go back to the tutorial use an external web resource to be sure I did not screw that up :? ) And remember, you only put the sign on the FIRST part of the array... i.e.{-XX,XX,XX} NOT {-XX,-XX,-XX}.

I would be very hesitant to be using an external clock set to test this, I am not even sure the code I have is correctly syncing to the controller (I think it is.. but it could very well NOT be.. never thought to test this... maybe you can let us know).

Then, for all practical purposes you could basically leave the Dimming module plugged into your computer for a full day to be sure it sets etc and when this works (if its convenient) you could then simply plug it back into the controller and let it run and see that it performs identically.

I am betting its something with the way you set it up, if you have ANY more issues, please post the lines of code (in contiguous blocks) that you edited (easy to do) so I can look at the, and also please post your location (you do not need to be exact, a city is close enough).

Here is the stuff to uncomment (its in the CalcSun function), remove the /* */ from the top and bottom of each of these segments.....

/*Serial.print("rise and set= ");
Serial.println(rise);
Serial.println(set);
Serial.print("newDay as seconds since 2000 to todays midnight= ");
Serial.println(newDay);*/

rise=(rise-newDay);// set to elapsed seconds of day
set=(set-newDay);

/*Serial.print("rise and set as elapsed seconds of day= ");
Serial.println(rise);
Serial.println(set);*/

newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day)

/*Serial.print("newDay as seconds since since 1970 to todays midnight= ");
Serial.println(newDay);
Serial.print("elapsed is");
long elapsed=now()-newDay;
Serial.println(elapsed);*/

What this is going to do is output to serial the rise and set times as seconds since the year 2000
Then its going to output the value for midnight of todays date as seconds since the year 2000

Then the next block will correct the rise and set to elapsed times for the day (in seconds since midnight)

The final block will print out the actual Unix Epoch time (i.e. based on elapsed seconds since 1970) for today at midnight, (calling it newDay as seconds since 1970 to todays midnight) which is then used to calculate the elapsed time into the day which is also printed out as "elapsed is"

If your having issues, please post your set up file and the serial output as well as the time by your watch when you ran the serial debugging. The code will run just fine with the serial debugging on..... but its going to be dumping stuff to serial 1x/day or on every restart so once we get this figured out you probably should comment it back out.


Good luck, I think it will be fine. I am fairly confident this is a simple problem with a configuration setting... so you could just post that and your location so I can look it over... or if you like doing this stuff and want to know more about how the program is working you could do the test as I described and we can look at the whole thing but I am betting its in your set up or possibly (maybe, kinda unlikely) in the way that your system is configured, thus my suggestion to run your lights based upon ONLY the dimming module with it actually unplugged from the controller. MAKE sure the lights have power to them! hehehe...
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

As for the sync interval...

This is something I basically just copied so I *think* its correct.

These lines are in the setup of the code...

setSyncProvider(RTC.get); // the function to get the time from the RTC
setSyncInterval(SECS_PER_HOUR); // Changed to sync every hour.

So it *should* be syncing with your Controller every hour... so your testing method was not likely going to be working unless you happen to cross an hour along the way.
TanksNStuff
Posts: 188
Joined: Fri Dec 30, 2011 6:57 am

Re: Weather Simulation for Dimming expansion module

Post by TanksNStuff »

OK, I uncommented the serial.prints and this is what I got in serial monitor at 11:17 PM on 8/10/12 (Immediately after I uploaded the code)

Code: Select all

rise and set=  1137914262
1137949476
newDay as seconds since 2000 to todays midnight=  1137888009
rise and set as elapsed seconds of day=  26253
61467
newDay as seconds since since 1970 to todays midnight=  2084572809
elapsed is62280
rise and set=  1944882942
1944931838
newDay as seconds since 2000 to todays midnight=  1944926289
rise and set as elapsed seconds of day=  4294923949
5549
newDay as seconds since since 1970 to todays midnight=  -1403356207
elapsed is0
I live in Blackwood, NJ 08012.

Now explain to me how that 1.13+ billion seconds converts to an actual time of day for sunrise/sunset please.

Here's my setup code uploaded to the dimming module:

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

#include <Time.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <avr/wdt.h>
//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>


//***********************************ARRAYS YOU MUST MODIFY TO MAKE YOUR TANK SET UP WORK*****************************
//YOU MUST READ EVERY WORD IN THIS SECTION IN ORDER TO APPROPRIATELY CONFIGURE THIS PROGERAM- READ EVERY LINE BELOW///
byte ChMax[]={225,225,200,200,0,0,0,0};//Incremental value (Max-flicker) above flicker you want as max intensity (!!!!!!! Light Set Point is ChMax PLUS Flicker !!!!!!) 
byte flicker[]={30,30,30,30,0,0,0,0};//need to input actual values here for flicker point on all channels in PWM expansion box
boolean Wchannel[]={0,0,1,1,0,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[]={1,0,1,0,0,0,0,0};
//set all channel positions that you would like to use for the lightning strike effect to 1 (0-5 are PWM channels 6,7 are Main PWM outs)- and channels with a 0 are not used in strike
byte StrikeChannel[]={1,1,1,1,1,0,0,0};
byte MoonCh[]={0,0,0,0,1,0,0,0};//place a 1 in the array position of all lighting channels you would like to use a moon lighting (this does not preclude their use in other phases (day, storm etc)
//**********************************DONE CHANGING THINGS HERE BUT YOU MUST CHANGE ChOffset array IN CalcSUN function******


//defines for SWFLTEK functions
// 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

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
//Unless your planning on editing the program DO NOT CHANGE ANYTHING HERE
long latitude, longitude;
byte TrueIntensity[8];//array used to place hold final write values for PWM intensity setting
long elapsedTime;//used multiple places as elapsed since midnight
long newDay;
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
long ChRiseSet[16];//times of rise and set for all 8 channels based upon offsets from calc rise and set values
float ChSlope[16];//slopes for 1/2 day calculations based upon time from offset to midday for channel 1-8
long CloudMaster[20];// Set up array to hold start and end times for clouds for the day-
long midDay;// exactly 1/2 way between rise and set, i.e. solar noon for latitudes <60 close enough for us... 
byte PWMports[] ={
    3,5,6,9,10,11};
byte ChannelValue[8];// Array to store output of insolaiton which may be modified and stored in TrueIntensity which is used to write to the PWM channels
unsigned long StrikeStart;//timer to keep track of strike sequence
int StrikeMaster[20];//Array to hold random strike pattern generated by weather array is sized to MAX needed given strike patter generator (8 strikes=16 positions)
byte StrikeNumber;//place to hold total number of strikes this sequence
boolean StrikeNow;//starts lightning strike sequence in loop state change made in weather/storm loop
byte StrikeCount;//Used to properly sequence strike sequence for delay between strikes
byte cmdnum=255;
byte datanum=255;
byte dow=0;//day of week
byte strikePattern, strikeTime;//used in Lightning() for timing of particular instance of strike 
boolean Cloud;// are we in a cloud interval on days that have clouds
boolean CloudToday;//set in CalcSun if randomization yields a day with clouds.
boolean IsStorm;// are we in a storm
byte CloudsTotal;// how many clouds today
long lastmillis;// variable to track millis to enable cloud and insolation loop restriction by time
boolean StormAdvance;//storm timer for light effect advance
boolean InsolationAdvance;//when true we recalculate light intensity during clear sky (every 3 seconds seems more than often enough)
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);

    pinMode(3,OUTPUT);
    pinMode(5,OUTPUT);
    pinMode(6,OUTPUT);
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    pinMode(11,OUTPUT);
    wdt_enable(WDTO_1S);
    unsigned long seed=0, count=32;
    while (--count){
      seed = (seed<<1) | (analogRead(5)&1);
    }
      randomSeed(seed);//start random generator at a different point each time (not perfect but whatever its gonna be pretty damn random)
    setSyncProvider(RTC.get);   // the function to get the time from the RTC
    setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
    
    dow=0;//set Day Of Week (dow) to a 0 value which is impossible (day()=1-7)... so we trigger calcSun on restart 
    StrikeNow=false;//no lightning strike yet
    CloudToday=false;//set to no clouds so CalcSun can set correctly if should be true
    Cloud=false;//set cloud to false
    IsStorm=false;//set storm to false
    lastmillis=millis();//start our millis timer now
    counter=0;//used in weather for triggering a storm, triggering lightning in a storm.
    StrikeCount=0;//Number of lightning strikes in the sequence.. set to zero until initialized in sequence
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop(){ 
    elapsedTime=(now()-newDay);//Elapsed time is seconds from midnight of today- local processor time.
    wdt_reset();
    if (cmdnum!=255){
        ProcessCMD(cmdnum,datanum);    
        cmdnum=255;
        datanum=255;
    }
    
    if (dow!=day()){ //used to see that were in a new day and need to recalculate sunrise and sunset
      CalSun();
      dow=day();
    }
  
    //Use millis to enable tracking of time interval
    if ((millis()-lastmillis)>=100){
        lastmillis=millis();
        counter+=1;
        StormAdvance=true;
        //now a bunch of stuff that may or may not be true at the same time but that all needs to happen when its true
        if (counter==0){
          InsolationAdvance=true;//so that it runs on start up to provide light immediately 
        }  
        if (counter%30==0){
          InsolationAdvance=true;
        }
        if (counter==210) counter=0; 
    }     

   if (InsolationAdvance==true) Insolation();//calculate clear sky solar intensity as the day advances
   Weather();//run the weather overlay (cloud, storm)
   //check to see if were need to have a lightning strike
    if (StrikeNow==true){
       if ((millis()-StrikeStart)>=StrikeMaster[(StrikeCount*2)]){//check if time has passed the delay (position 0,2,4,6,8 etc in StrikeMaster)-StrikeCount is indexed up by 1 after each strike so we see positions 0,2,4,6,etc in sequence
          byte intensity;
          intensity=random(180,256);// this little bit should generate a randomly bright flash variation between the series of flashes in StrikeMaster
              for (byte b=0; b<6; b++){
                  if (StrikeChannel[b]==1) analogWrite(PWMports[b],intensity);// set all strike channels to intensity of strike
              }
          delay(StrikeMaster[((StrikeCount*2)+1)]);//index to +1 position in array from 0,2,4, etc to 1,3,5 etc
          StrikeCount++;//so that the next time we look at elapsed time were looking at the right array position
          if (StrikeCount==(StrikeNumber-1)){
            StrikeNow=false;
            StrikeCount=0;
          }
       }
    }
    for (byte a=0;a<6;a++){//using all prior mods to light intensity (Insolation-->Cloud-->Storm) lets make some light
      analogWrite(PWMports[a],TrueIntensity[a]);//dont change this to 8 to refelct array for channels.. we only have 6 here!
    }
}
//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
//*********************************************************************************************************************************
//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
   //Serial.println("CalSun Run Now");

        //*********************YOU NEED TO CHANGE THESE VALUES Read instructions in their ENTIRETY and CAREFULLY change to values for your tank and geographical region***************************
        //channels 0-5 are PWM expansion board lights 6,7 are ReefAngel Controller PWM outputs
        //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[]={
            0,600,-600,0,9000,-8400,8400,-9000,0,0,0,0,0,0,0,0};
        // NOW SET YOUR LATIDTUDE AND LONGITUDE COORDINATES as Degrees, Minutes, Seconds of Lat and Lon
        //If Your NORTH of the equator your LONGITUDE must START with a NEGATIVE number (the rest are positive) e.g. All of North America, Europe, Russia etc are negative
        //If Your EAST of the Prime Meridian your LATITUDE must START with a NEGATIVE number (the rest are positive), e.g. Most of Europe, All of China, India, Austraila, Russia etc are negative
       latitude=dmsToSeconds(39,49,19);//United States of America- Salt Lake City, local time is -7 hours GMT 
       longitude=dmsToSeconds(-75,3,30);
        //**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load******************************************** 
   
    if (dow==0){//if the controller has resarted we need to find midnight
      long hours, minutes;//store current elapsed local hours as total seconds from midnight
      time_t t=now();//store current clock time to parse
      hours=hour(t);
      hours=(hours*3600);//current hour number 0-23 as seconds
      minutes=minute(t);
      minutes=(minutes*60);//minutes of current hour as seconds
      newDay=now();
      newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
    }
    else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
      newDay=now();
    }
      
    //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
    newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
    rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
    set=newDay;//
    //Calculate rise time and set time using Epherma Library functions (see end of code) 
    SunRise(&rise);//call to Epherma function
    SunSet(&set);//Call to Epherma functionunsigned long newDay;
  
   Serial.print("rise and set=  ");
   Serial.println(rise);
   Serial.println(set);
   Serial.print("newDay as seconds since 2000 to todays midnight=  ");
   Serial.println(newDay);
    rise=(rise-newDay);// set to elapsed seconds of day
    set=(set-newDay);
   Serial.print("rise and set as elapsed seconds of day=  ");
   Serial.println(rise);
   Serial.println(set);
    newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day) 
   Serial.print("newDay as seconds since since 1970 to todays midnight=  ");
   Serial.println(newDay);
   Serial.print("elapsed is");
   long elapsed=now()-newDay;
   Serial.println(elapsed);
   
        //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);
       
        for (byte b=0;b<16;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])));
            }
            else if (b%2==1){
                ChRiseSet[b]=set+(Choffset[b]);
                ChSlope[b]=(deltaY/(float)(HalfDayLength+(Choffset[b])));
            }
        }  
        
        //***************** 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?****************
        
  
        byte RainMaker=random(1,101); 
        if (RainMaker<=CloudChance){
            CloudToday=true;//used to trigger weather function, can also be used to send flag to controller
        }
        else if (RainMaker>CloudChance){
            CloudToday=false;//see above comment on CloudToday
            return;
        }
        long dayLength=0;
        for (byte a=1;a<16;a=(a+2)){//determine maximum day length given light on tank that is not moon light, this will yield night clouds and storms (and a storm after dark is severe... always
          if (a==0){
            if (((set+Choffset[a])-rise)>(set-rise)){
              dayLength=((set+Choffset[a])-rise);  
            }
            else dayLength=(set-rise);
          }
          else if (a!=0){
            if (dayLength<((set+Choffset[a])-rise)){
              dayLength=((set+Choffset[a])-rise);
            }
          }
        }
        
        // number of clouds possible for the day, max and min
        byte CloudsMax=10;//DONT INCREASE BEYOND 10 or it will DIE, or increase array size to handle it (among other things)
        byte CloudsMin=4;//use 2 as a minimum
        CloudsTotal=random(CloudsMin,(CloudsMax+1));
        
        // 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);
        
        // 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
        int CloudLength;
        CloudLength=((dayLength*Overcast)/CloudsTotal);//average cloud length
        long SunSegment=((dayLength-(dayLength*Overcast))/(CloudsTotal+1));//average sun length between clouds
        float CloudFraction=0;
        float SunFraction=0;
       
        //start by zero filling CloudMaster array
        for (byte a=0; a<20; a++){
          CloudMaster[a]=0;
        }
        
        byte b=0;//used to get pairs of fraction for SunFraction in for loop
        byte c=0;//used to get pairs of fraction for CloudFraction in for loop
        //now randomize cloud length and sunsegment length as pairs to get different looking days- 
        for (byte a=0; a<(CloudsTotal*2); a++){
          if (a%2==0){
            if (b==0){
              if (a==0){
                SunFraction=random(20,181);//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length
                SunFraction=(SunFraction/100);
                CloudMaster[a]=(SunFraction*SunSegment);
              }
              else if (a<((CloudsTotal*2)-2)){
                SunFraction=random(20,181);//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length
                SunFraction=(SunFraction/100);
                CloudMaster[a]=(SunFraction*SunSegment);
                b++;
              }
              else if (a==((CloudsTotal*2)-2)){
                SunFraction=(2-((float)CloudMaster[0]/(float)SunSegment));
                CloudMaster[a]=(SunFraction*SunSegment);
              }
                
            }
            else if (b==1){
              if (a<((CloudsTotal*2)-2)){
                SunFraction=(2-SunFraction);//were on the second part of a pair
                CloudMaster[a]=(SunFraction*SunSegment);
                b=0;//reset so next time we start a new fraction
              }
              else if (a==((CloudsTotal*2)-2)){
                SunFraction=(2-((float)CloudMaster[0]/(float)SunSegment));
                CloudMaster[a]=(SunFraction*SunSegment); 
              }
            }
          }
          else if (a%2==1){//if were in odd positions we need to determine cloud lengths in random pairs such that each pair =2*CloudLength in length
            if (c==0){
              CloudFraction=random(20,181);//vary each pair of SunSegments from 20%-180% of possible length such that every pair =2*SunSegment in length
              CloudFraction=(CloudFraction/100);
              CloudMaster[a]=(CloudFraction*CloudLength);
              c++;       
            }
            else if (c==1){
              CloudFraction=(2-CloudFraction);
              CloudMaster[a]=(CloudFraction*CloudLength);
              c=0;//reset so next loop finds a new fraction
            }
         }
      }
        /*Serial.println("here is cloud master in is entirety prior to forming start and end pairs");
        for (byte a=0;a<20;a++){
          Serial.println(CloudMaster[a]);
        }*/
      //reframe array to generate cloud start, cloud end, cloud start, cloud end
      for (byte a=0; a<(CloudsTotal*2); a++){
        if (a==0){// if were starting our first cloud we need to add to rise value to first sun segment
          CloudMaster[a]=rise+CloudMaster[a];
        }
        else {
          CloudMaster[a]=(CloudMaster[a-1]+CloudMaster[a]);//just add prior values together e.g. (second position is cloud end so to find end add rise corrected start time with duration)
                                                           // subsequent start would be end of 1st cloud + next sunsegment fraction
        }
      }
        /*Serial.println("here is cloud master in is entirety as start and end pairs");
        for (byte a=0;a<20;a++){
          if (a%2==0){
            Serial.print("Start time=");
            Serial.println(CloudMaster[a]);
          }
          else {
            Serial.print("End time=");
            Serial.println(CloudMaster[a]);
          }
        }*/
}//END SunCalc FUNCTION

void Insolation()
{
  InsolationAdvance=false;//reset this flag now that we have entered function

        //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){
           byte c=0;//loop counter
            for (byte b=0;b<16;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
                    ChannelValue[c]=flicker[c]+ChMax[c]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                 }
                 else if (elapsedTime<ChRiseSet[b]){
                   if (MoonCh[c]==1){  
                      byte MoonToday=MoonPhase()*0.5;//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light
                      if (MoonToday==0) ChannelValue[c]=0;
                      else if (MoonToday<flicker[c]) ChannelValue[c]=flicker[c];
                      else ChannelValue[c]=MoonToday;
                   }
                   else if (MoonCh[c]==0){
                     ChannelValue[c]=0;//its dark and this is not a moon phase channel
                   }
                 }
              c++;//index by one so we count 0-7 as b goes 0-14 by twos
            }
        }  
        else if (elapsedTime>midDay){
            byte c=0;//loop counter
            for (byte b=1;b<16;b=b+2){
              if (elapsedTime<=ChRiseSet[b]){
                    secSoFar=(elapsedTime-midDay);
                    ChannelValue[c]=flicker[c]+ChMax[c]*(-cos(Pi+(ChSlope[b]*secSoFar)));          
               }
               else if (elapsedTime>ChRiseSet[b]){
                   if (MoonCh[c]==1){  
                      byte MoonToday=MoonPhase()*0.5;//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light
                      if (MoonToday==0) ChannelValue[c]=0;
                      else if (MoonToday<flicker[c]) ChannelValue[c]=flicker[c];
                      else ChannelValue[c]=MoonToday;
                   }
                   else if (MoonCh[c]==0){
                     ChannelValue[c]=0;//its dark and this is not a moon phase channel
                   }
                }
              c++;//index to count 0-7 as b counts 1-15 by twos.
            }
        }   
}//END function
//WEATHER FUNCTION BEGIN
void Weather ()  
{
    static byte loopCount;
    static float CloudCover; // variable to store value in random walk - declared static to accumulate Cloud effect
    static float 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
    static byte Counter;//used to trigger storms from cloud you can change its if loop comparison to decrease or increase storm rate see below in cloud==true if loop
    static byte Severity;
    static byte StormCount;// used to limit X storms per cloud and to choose which cloud can have a storm
    static int StepSize;
    static int LastStepSize;
    //check to see if were having a scheduled cloud
    if (Cloud==false){
      //Write Insolation values to TrueIntensity so the loop will pick them up and the cloud/storm will get the right data (since intensity changes during the day)
      for (byte a=0; a<8; a++){//this must be above the next loop
            TrueIntensity[a]=ChannelValue[a];//this is where intensity is set for the PWM channel analog write in the loop... don't mess with this.
        }   
      for (byte a=0; a<(CloudsTotal*2); a=(a+2)){//if its time for a cloud, run it
         if ((elapsedTime>=CloudMaster[a]) && (elapsedTime<=CloudMaster[(a+1)])) {
             CloudEnd=CloudMaster[(a+1)];//to avoid this loop running true during the compute cycles at the end of the cloud and before elapsedTime advances a second, actual cloud does not
             //Serial.print("We started a cloud and its end is=");
             //Serial.println(CloudEnd);
             CloudCover=CloudStart(CloudMaster[a]);//CloudStart modifies TrueIntensity to get us to 50% intensity at the start of the cloud effect and also sets cloud=true to bypass this
             Counter=0;
             StormCount=random(0,3);//the number of storms MAX that may occur in this cloud (remember Random is range= -1 on high end)
             loopCount=1;
             LastStepSize=0;//zero out cloud random walk variables
             StepSize=0;//zero out cloud ranodm walk variables
             return;//exit having started a cloud in CLoudStart routine called above
         }
       } 
     }
         
    else if ((Cloud==true) && (IsStorm==false)){
        if (StormAdvance==false){//use millis tracker to run this loop every 2 seconds
            return;
        }
        StormAdvance=false;//reset to false when true so we run this once, until time advance is true again
        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*/
        if (loopCount==1){
          PriorCloudCover=CloudCover; //e.g. PriorCloudCover=CloudCover with no float math to screw things up
          LastStepSize=StepSize;
          StepSize=(random(5,21));// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)
          if ((random(0,2)!=1)) StepSize=-(StepSize);
          if ((CloudCover+StepSize)>=100){//cannot shut off lights more than 100% so limit here
            StepSize=(100-CloudCover);//
            Counter++;
          }
          else if ((CloudCover+StepSize)<=0){//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down
            StepSize=-(StepSize);
            if (Counter>=50) Counter-=random(-1,2);//since we got bright... lets further delay and randomize storm occurence  
          }
          CloudCover=CloudCover+StepSize;
        }

        if ((Counter>=60) && ((CloudEnd-elapsedTime)>=300)) {//if Counter (indexes when cloud cover reaches 100) has accumulated and we still have time lets make a storm
        //to change the frequency of storms increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
        //if you change counter comparison here change it in the next loop as well
           if (StormCount>=1){//if we can have storms in this cloud (random- statisticly 1/3 clouds = no storm, 1/3 = 1 possible storm, 1/3 = 2 possible storms)
             byte RandomStorm;
             RandomStorm=random(0,11);//this randomizes for longer clouds without storm, avg cloud is much longer prior to storm occuring- thus short clouds will not generally have a storm
               if (RandomStorm>=4){
                 StormCount-=1;//count down by 1 the number of storms in this cloud- this will not roll the byte since the loop requires it to be at least 1 to ever subtract here. 
                 Counter=0;//reset this variable since Storm loop uses it as well.
                 int LongestStorm;//used to pass max possible time to storm if loop from cloud loop within weather function
                 LongestStorm=(CloudEnd-elapsedTime);
                 Severity=StartStorm(LongestStorm, IsStorm, StormEnd);
                 loopCount=1;//reset counting loop for the storm
               } 
           }
        }
        else if ((Counter>=60) && ((CloudEnd-elapsedTime)<300)){
           Counter=0;//just reset the counter (does not really matter in this case but its clean)
        }
        
        for (int a=0;a<8;a++){
            if (ChannelValue[a]==0) TrueIntensity[a]=0;// if were in an evening storm dont reset intensity (it would go to flicker point and possibly flicker)
            else if (DimOrder[a]==0){
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((CloudCover-(StepSize-(StepSize/4)*loopCount))/100))));
            }
            else if (DimOrder[a]==1){
                TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/4)*loopCount))/100))));
            }     
         }
         loopCount++;
         if (loopCount>4) loopCount=1;
    }  
    //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
        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;
            Counter=0;
            return;
        }
        if (loopCount==1){
          PriorCloudCover=CloudCover; //e.g. PriorCloudCover=CloudCover with no float math to screw things up
          LastStepSize=StepSize;
          StepSize=(random(5,21));// in Percent% (0-100) This is how much light intensity will change over the loop count interval (this actual time is dependent upon the call frequency of StromAdvance as set in the loop)
          if ((random(0,2)!=1)) StepSize=-(StepSize);
          if ((CloudCover+StepSize)>=100){//cannot shut off lights more than 100% so limit here
            StepSize=(100-CloudCover);
            Counter++;
          }
          else if ((CloudCover+StepSize)<=0){//cannot be brighter than 100% so since were in a cloud dont "limit" it but reflect it back down
            StepSize=-(StepSize);
            if (Counter>=2) Counter-=random(0,2);//since we got bright... lets further delay and randomize strike occurence    
          }
          if (Counter>(Severity+2)) Counter=0;//allow if to accumulate on ocassion to train strike sequences 2-3 in a row but then dump it 
          CloudCover=CloudCover+StepSize;
        }
   
        if ((Counter>=(Severity+random(-1,4))) && (StrikeNow==false)) {//this is where a storm is triggered.  Counter indexes when cloud cover reaches 100 on the random walk
        //to change the frequency of lightning strikes increase or decrease the number comparison for counter in the if statement above (larger #== less storms).
          byte RandomStriker;
          RandomStriker=random(1,11);
          if (RandomStriker>4){
            StrikeNumber=(random(2,11)); //random high =x-1 so max strike =12 each strike requires a duration and a delay thus StrikeMaster is 18 positions
            //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 in pairs, position in arry of (0,1) (2,3) etc as strike (delay,duration)
            for (byte a=0;a<20;a++){
                if (a>=(StrikeNumber*2)){
                    StrikeMaster[a]=0;
                }
                if (a%2==0){
                   if (a==0){
                      StrikeMaster[a]=random(300,1601);//no need for random here but I am leaving it since I wrote it that way.  This must be independent from a=2,4,6 etc...
                    }
                    else {
                       StrikeMaster[a]=(StrikeMaster[(a-2)]+random(200,1401));//position 0,2,4,6,8.. is strike delay
                    } 
                 }
                 else if(a%2!=0){
                    StrikeMaster[a]=random(50,110);//position 1,3,5,7,9... is strike duration (I tried real lightning strike durations and its too short this is adjusted for visual effect
                 }
            }
            StrikeNow=true; //Trigger to start strike sequence in loop
            StrikeStart=millis();//set timer to "zero" now- sequence will start in loop after this function
            StrikeCount=0;
          }
          Counter=0;
        }
        
        if (Severity>5){
          for (int a=0;a<8;a++) {
              if (ChannelValue[a]==0) TrueIntensity[a]=0;
              else if (Wchannel[a]==1){//if were in a storm but not a severe storm constrain whites to 50% of Insolation intensity
                if (DimOrder[a]==0){
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])/4))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                  }
                  else if (DimOrder[a]==1){
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])/4))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                  }
              }
              else if (Wchannel[a]==0){//if were blue, we chase as for a cloud
                  if (DimOrder[a]==0){
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                  }
                  else if (DimOrder[a]==1){
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                  }
              }    
          }
        }
        else if (Severity<=5){// severe storms occur throughout the day, but EVERY storm after sunset is severe...
          for (int a=0;a<8;a++) {
              if (ChannelValue[a]==0) TrueIntensity[a]=0;
              else if (Wchannel[a]==1){//if were white we need to be off in a storm
                TrueIntensity[a]=0;
              }
              else if (ChannelValue[a]==0){//if this light channel is dark... e.g. after sunset for this channel- it produces no cloud effect
                  TrueIntensity[a]=0;
              }
              else if (Wchannel[a]==0){//if were not shut off in a strom and not after our daylight period (this channel) then we produce storm light sequences.
                  if (DimOrder[a]==0){//if we dim first... do it
                      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-((CloudCover-(StepSize-(StepSize/3)*loopCount))/100)))); 
                  }
                  else if (DimOrder[a]==1){//else we dim second
                      TrueIntensity[a]=(flicker[a]+((((float)(ChannelValue[a]-flicker[a])))*(1-((PriorCloudCover-(LastStepSize-(LastStepSize/3)*loopCount))/100))));
                  }
              }//end of CWHchannel==0 being true
           }//end of for loop in severity <5 == true loop
        }//end severity compairson loop no more else statements
       
        loopCount++;
        if (loopCount>3) loopCount=1;
    }//end of storm if loop
}//End Weather function

//CloudStart drops light intensity to 50% of whatever daylight setting is to start the cloud at 50
int CloudStart(long StartTime){
    byte elapsed;
    elapsed=(elapsedTime-StartTime);//counts up since we start this at elapsedTime=StartTime and StartTime is fixed
    for (byte a=0; a<8; a++){ 
      TrueIntensity[a]=(flicker[a]+(((float)((ChannelValue[a]-flicker[a])))*(1-(elapsed*2.5)/100))); 
    if (elapsed>=20);
      Cloud=true;//start the cloud
    }
    return 50;//set CloudCover to 50 
}//end CloudStart function

//StartStorm sets up duration and severity of storm. Its currently limited to 90-600 sec in length- it will rarely be lower than 3 minutes
byte StartStorm(int MaxLength, boolean& trigger, long& EndTime){
      byte LightningIntensity;
      int StormDuration;
      MaxLength-=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
      if (MaxLength>720){
         MaxLength=720;//modify local variable
         StormDuration=random((MaxLength/3),(MaxLength+1));
         EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
      }
      else {
         StormDuration=random((MaxLength/2),(MaxLength+1));
         EndTime=(elapsedTime+StormDuration);//Set by reference StormEnd static variable in weather
      }
      if (elapsedTime<midDay){
         LightningIntensity=random(3,11);//morning storms are generally less severe
      }
      else if (elapsedTime>midDay){//afternoon storms are more likely to be severe (every 10-15 sec or less) to about once in a minute or maybe less
         if (elapsedTime>(set-rise)){//Storms after sunset are always severe... it just looks too cool!
           LightningIntensity=3;
         }
         else {
         LightningIntensity=random(3,8);
         }
      }
      trigger=true;
      return (LightningIntensity);
}
//End Storm Start Function
//Similar to Cloud start but in reverse... now ramp intensity from wherever we were at the end of the cloud to the value set by Insolation
void ClearSky(int CloudPercent, long TerminationTime)
{
    byte elapsed=(elapsedTime-TerminationTime);//Counts up from the scheduled end of the cloud in seconds
    float slope=(CloudPercent/30);//Just calculate how much to increment every second to go from CloudCover to clear sky (CloudCover of zero)
    float LightAdvance;
    LightAdvance=(CloudPercent-(slope*elapsed));//were reducing CloudCover from start to zero over 10 seconds.
    for (byte a=0; a<8; a++){ 
    TrueIntensity[a]=(flicker[a]+(((float)(ChannelValue[a]-flicker[a]))*(1-(LightAdvance/100)))); 
    }
    if (elapsed>=30){//at this point lights are back to full Insolation setting so cancel the cloud and start waiting for the next one
        Cloud=false;//stop the cloud we are now outside of a true condition in the if loop so it will now stay false and lights are back on
        IsStorm=false;//just to be redundant this is not called from a storm... 
    }
}//End Clear Sky function

byte MoonPhase()
{
   int m,d,y;
   int yy,mm;
   long K1,K2,K3,J,V;
   byte PWMvalue;
   m = month();
   d = day();
   y = year();
   yy = y-((12-m)/10);
   mm = m+9;
   if (mm>=12) mm -= 12;
   K1 = 365.25*(yy+4712);
   K2 = 30.6*mm+.5;
   K3 = int(int((yy/100)+49)*.75)-38;
   J = K1+K2+d+59-K3;
   V = (J-2451550.1)/0.29530588853;
   V -= int(V/100)*100;
   V = abs(V-50);
   PWMvalue = 4*abs(50-V);  // 5.12=100%    4=~80%
   //pinMode(lowATOPin,OUTPUT);
   //return (PWMvalue*100)/255; //output is 0-100
   return PWMvalue;//output is 0-255 
}

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 


//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 form 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);
}
Here's a brief summary of my setup:
- 2 fixtures with 2 channels each... 1 white, 1 actinic
- My left fixture uses channel 0 for actinic, channel 2 for whites
- My right fixture uses channel 1 for actinic, channel 3 for whites
- I want my dimming order to go from right to left (since that's an east to west flow, which would be "natural")
- My intention was to have my sunrise/set offsets like this: (RB = Right Blue, LB = Left Blue, RW = Right White, LW = Left White)
RB - Start 10 mins early (-600), End at sunset (0)
LB - Start at sunrise (0), End 10 mins after sunset (600)
RW - Start 140 mins after sunrise (8400), End 150 mins before sunset (-9000)
LW - Start 150 mins after sunrise (9000), End 140 mins before sunset (-8400)

That gives me actinics starting at roughly sunrise (with 10 minute difference between the fixtures) and ending at roughly sunset (again with 10 minute difference). It also gives me whites starting roughly 2.5 hours after sunrise (also with a 10 minute difference) and ending roughly 2.5 hours before sunset (again keeping with the 10 minute difference between fixtures all the way through.)

The reason I did this was to keep it similar to the 2.5 hour offset I have currently with my slope code.

That's about it. If you need any other info please ask. Hopefully you can tell me if I made the proper edits for this to work as intended... and what my sunrise/sunset times should be as I have it coded.

Thanks in advance for both making this code and helping me figure out if it's working right.
TanksNStuff
Posts: 188
Joined: Fri Dec 30, 2011 6:57 am

Re: Weather Simulation for Dimming expansion module

Post by TanksNStuff »

Well, apparently I did something wrong or this code isn't working.

When I manually turned on my relay power ports for my leds last night (just after midnight) both whites and actinics came on at what looked like 100%.

I turned the power ports back off and went to bed... then got up at 6 am to try again. Yep... all dimming channels were on at about 100%.

Either I screwed up on the lat/long or it's just not working.

Sent from my DROID RAZR using Tapatalk 2
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Its looking ok.... but something got FUBAR... looking into it but most of the calculations are correct...
you post

rise and set as elapsed seconds of day= 26253
61467
newDay as seconds since since 1970 to todays midnight= 2084572809
elapsed is62280

up to here its about right...
26253/(3600sec/hr)=7:30 am rise
61467/3600=after 5 pm ish (which may be a bit off?) but close...

then its kinda corrupted, but this is really simple math. I either loaded something bad (mines working as are others... but I will check) or something got moved... its a very simple error... at work now... will post later. Sorry for delay, was out for the weekend.

rise and set= 1944882942
1944931838
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Also.... most of those billions of seconds are correct, its just how computers (ALL Computers) keep track of time. They count seconds since either 1970 (unix, Arduino etc) or since 2000 (some other types). So... thats a lot of seconds. Its really really hard to look at since the numbers are so big which is why I convert them to human readable format (somewhat) as elapsed seconds since midnight of the current day... its after that conversion is done (which looks more or less fine) that I see your numbers going very funky. No big deal, its two lines of code... I will post the error or a fix tonight. It will be working soon.... very trivial error. I hope its not mine!!!
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Ok... two things are weird.

First... your program is somehow running the CalcSun loop twice...

Second- your system time is seriously wrong... which is probably causing error #1

Since your setting your time with external programming, I would look at whats going on. To PROVE that its wrong... (I am sure it is)

Just add this line to the part I asked you to uncomment...
Serial.print("System time is now = ");
Serial.println(now());

Maybe put that at the end of the rest of the serial.print area... then run the newly edited program and copy the results of that line.... and


Then go here....
http://www.epochconverter.com/
and copy the number into the box and reply with what time it is that your system thinks it is. I get something like the year 2036 and the Date of Jan 21st... which makes perfect sense for a 7:30 am rise and 5:02 set as thats the middle of the winter some 24 years from now...

Fix this, then run it again and send me the results... it may simple work or there may yet be something else going on, but until you set your system clock to the correct date, nothing I can do to help you here.

The really odd thing, is the next time your program runs that section and outputs to serial (your serial output contains two iterations of the same output) its showing seriously different system times... so something is seriously wrong with you clock.... which is causing the program to basically re-run as its sensing a new day basically instantly... not to repeat... but clearly your system time setting or server, or something is wrong.

I would worry a little tiny bit about the synch and your using a unix server to set time etc... its getting a bit complicated... Just check things carefully run it over serial again and use those numbers I just showed you how to print to figure out whats going on with your system clock. I have never seen this before, so I bet its something to do with your externally setting the clock.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Weather Simulation for Dimming expansion module

Post by lnevo »

Hey rufessor, I'm trying to use your code for calculating sunrise/sunset just to use with on/off lights without dimming control.

When I left your coordiantes in-tact for Salt lake city, I get a Sunrise/Sunset value that is off by 1:28 minutes for both rise and set. I changed the coordinates to the GBR in Australia and I am off by +59 minutes fro Sunrise and -19 minutes for Sunset.

Anyway, was hoping you could take a look at what I've scavanged and see if I missed anything critical or if I'm doing something wrong with getting hour() and minute() from the rise and set variables...

Here's a link to the thread where I'm working on the code... http://forum.reefangel.com/viewtopic.php?p=12941#p12941

Thanks in advance.
TanksNStuff
Posts: 188
Joined: Fri Dec 30, 2011 6:57 am

Re: Weather Simulation for Dimming expansion module

Post by TanksNStuff »

Rufessor, you are correct. My system clock was bonkers at the time I tested it. Well, not initially, but right afterwards.

My controller occasionally shows a blinking red status light on it when my temp probe wires get tugged on. I don't know if there's some loose wires in there or if it's just jostling the part where the connectors attach to the main board... but it basically throws my whole system out of whack until I jiggle the temp probe connections. Sometimes I even have to reboot the controller to get it to work.

So, I think once I uploaded it to the dimming module and closed my stand door, it freaked out my controller and caused me to reboot it. I think that's the reason why there is 2 time stamp serial readings too. I noticed this right after I made my last post here, and I was too tired to edit it to note that happened. Sorry, I forgot to do it later the next day too.

The other possibility is that I did this so close to midnight that perhaps the 2nd set of serial readings were sent just after midnight or on-the-hour as you said it's supposed to sync?

Also, when I said I was manually changing my system clock time on my previous tests, I was doing it right from the menu on the main controller. The time shown on the controller was exactly what the android app showed when I hit the refresh button, so I know it was correctly changing the system clock.

At the end of my tests, I just used the android app to "set current time" which reset the time shown on the controller to the current time.

I was wondering if the dimming module board has it's own clock cycle separate from the main controllers board? And does the dimming module also have an internal memory file that may need to be replaced when using your weather sim code?

Anyway, I'll take one more stab at it next weekend. If I can't get it to work then I'll look for other options.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Ok-

A few comments.

The fact that you had two consecutive serial outputs (which says nothing about when in real time this happened) is unlikely to be due to actually crossing midnight as the actual unix epoch time is different by many years between them... so obviously your clock was somehow reset yet again.

The dimming module is an entirely separate independent arduino processor exactly (IDENTICAL) like the one in the head unit. Its THE SAME. Thus, it has a clock etc etc... its just constrained in its input/output to control dimming features, not temp, pumps etc etc. It can be configured to synch time with the main processor, I pasted some lines of code that look like they are supposed to do this (its in the set up) but I have no idea if its actually working.

I have not a clue why your system is set up incorrectly with time.

I would say that at this point, you have not even begun to try this program. I would not abandon the effort if its not working over the weekend, because you have not even run it under conditions that are required to work. It REQUIRES the correct time, so it can determine the day, the year etc... then it calculates rise and set times.

Every time you post an error wondering why its not working, its because your system time is goofy... when you get this fixed, figured out and can prove that your clock on the dimming module is correct, and your lights have power, and nothing is being reset/hammered... and its not working. Then- we can look into it.

If you need help figuring out the clock, unfortunately I cannot help you. But, I have looked into this a little bit and its not complex, ask Roberto and he will probably be able to help you immediately. If I was you, I would unplug the dimming module from the main completely- I would never ever even think about running whatever time sync server your using. I would simply start up my computer, run arduino, load a program with the serial output set up to tell you the current time (like I asked you previously) and input that value into the server I provided a link to. Then, when that comes back as the correct year, date, and time, see if the program works without altering a single line outside of the tutorial.

Then, post back the serial output for rise and set times etc and let us know whats going on. You CANNOT trouble shoot anything when your processor does not know what day it is, what year it is, and the time of day. Sorry, not trying to be harsh, but thats the simple fact. UNPLUG everything, your dimming module will power and run through the USB connection to the computer, it DOES NOT NEED THE MAIN. PERIOD. In fact, in your case, I would REMOVE the time sync lines from the dimming module code, load it and leave it. You would of course need to plug it back into the main for this to run long term (cannot reasonably leave it plugged into your computer), but if your removing the time sync lines, whatever happens to the main time will NOT affect the dimming.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Just an FYI for anyone considering this, the sunrise and sunset calculations as well as the time handling parts of my code have been adopted by another user who is putting it on the main (no clouds/weather etc) and its working for them using lat lon pairs from yet another place on the globe.

At this point, I am pretty much willing to say that the code works, period. If its not working for you, you did something wrong. Its actually not super hard to do something wrong as it requires a few things... so please post Q's and we will get you working. If your reading this, you know to beware of setups that are externally being given time settings... I myself was unaware that this could/would be an issue... so my recommendation would be to run this off your dimming module UNPLUGGED from the main (unplug the USB mini from the side) and use the USB/pinout cable plugged into the board to load your configured code, then simply LEAVE it plugged into a power up computer and let it run for a day or two until your sure its working..... and it really should be... if its not triple check your LATITUDE and LONGITUDE. It seems like EVERYONE gets this wrong at least one time. Then run it again and see how its going.

It has been run independently by numerous people, code works, so far NO BUGS have been identified for quite some time.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Ok.... so following up a post saying no bugs have been detected for some time.. with this..... is pretty funny.



I woke this morning to my tank blinking like a strobe. I reloaded software, still doing it, I reset controller, still doing it, I loaded a very simple PWM PDE that just sets lights to steady intensity- thats it... and at least thats working. But... I have no idea whats up- is there something evil about Sept 1 2012 because I cannot even get serial output from my dimming module when I try to load the weather package... so odd... Its been basically perfect but for one (again very recent- like 2 days ago) issue with very low lights on at night, and now its freaking. OUT>

????????????????????????????????

2-3 months of ZERO issues and suddenly it pukes?????
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Weather Simulation for Dimming expansion module

Post by lnevo »

When i get some time again Im going to try and compile this in a standalone binary and run it through a loop of date times to see if I can see what's up.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

that looks to be required at this point. arduino is if nothing else a huge pain in the a.. To troubleshoot. let me know if I can help. I tried to do this once and bailed out as I was in a program with way to many reef angel specific calls but just the date stuff should be revisited. I don't get it at all.... Makes no sense that changing a few 84,600 seconds every day would do anything at all in terms of riseset dying....
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Weather Simulation for Dimming expansion module

Post by lnevo »

Ok, I did manage to run the algorithms through as a standalone binary and the output worked great. I did find one bug in my code that I'll discuss in the other thread where I'll specify my corrections, but even looking a few years into the future, the dates all look stable with nothing out of whack. There were some variable casting issues that I had to address which may lead to problems on the arduino that may not be checking as strictly, but I doubt that's getting in the way.... the issue that I had was pretty obvious (I hope that you didn't copy some of mine and introduce the same bug... :) )

Anyway, just wanted to assist in your sanity and the fact that the actual calculations look good. The only big difference now is whether or not to leave that rotation code (but I think we talked about it in the other thread, and I'll leave the discussion there.)

Lee
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Ok... awesome. Using your thread for the port to the main I had a question to ensure I understand how that global declaration was FUBAR the calculation. Once I clarify I will post here the fixed code- If you have tried this code and ran into issues I apologize- but thanks to lnevo helping out and taking an interest we appear to have figured out a very strange behavior.. and it would appear to have arisen through a very unlikely seemingly standard call. Watch here for new code to be loaded and PLEASE try it again if you had issues. It may be a few days but I will be working on this now and get something out ASAP.

My bad- but much THANKS to lnevo! This is what makes open source so awesome!!!!!!!! He stepped in with an interest in what I started and used his skill set to really help out! Very very cool. Thanks again and Congrats! Great things are happening on ReefAngel- no way you get this functionality out of other controllers and this much testing!

My fish miss the storms... so I gotta get this fix done!

Also, have some plans to write in an algorithm that enforces a Minimum day length for those of us using local lat lon but that live in regions with substantial yearly variation in rise and set.... cause my tank is starting to notice the shorter days as we have lost over 1 hour of light all ready. This will be invisible to the end user but for the addition (I think... just planning this) of a single variable specifying the minimum desired day length (probably in total seconds). I think I will just distribute the day extension equally so that the tank rises and sets earlier/later by 1/2 of the total difference in actual v.s. minimum day length. Didn't know I would need this, but its becoming apparent that I/we (depending on your Lat/Lon choice) probably do.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Weather Simulation for Dimming expansion module

Post by lnevo »

rufessor, not sure if the issue I had was the same as what you experienced...unless you copied something from my code. I'll post the explanation on the other thread in a moment what was going on...
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Negative on that... unsure why my dimming module went whacky.

Re loading current working code to see if it was just a random thing. The error you found was indeed specific to how you had coded in the Lat/Lon conversion and could have produced exactly what you found, because on the second calculation run through you would have basically been using some mythical location with a very very large number... Your fix seems to be working, congrats on figuring that out. I had not gone through it step by step like a processor would so I missed that it would calculate the conversion using the prior result of that calculation. They way I originally coded it does not do that... and thus there is no apparent reason for it to have freaked out.

For those interested, I will update here when I reinstall and see what happens. I guess its just possible that after 2 months continuous running the processor simply finally lost track of memory and I need to do a complete reload of the code, and a good power off (for a minute or something) cycle to truly clear all memory etc. If thats all it takes and I am up and running, then I am just going to chalk this up to random crap, especially now that the Lat Lon code has been validated completely independently of my saying its working just fine.

Sorry for the confusion, I initially had read the error report thinking he had caught something in my code when he ported it, but it was an introduced error and is NOT part of the code attached to this thread. Which is nice- since I thought it was OK.

I guess were back to square one, the code should work for you, post here if its not.

I will reinstall on my own module and get it up and running using the actual code from this thread as a further test since I want to re-load anyhow.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Weather Simulation for Dimming expansion module

Post by lnevo »

Why not leave your code and pick a location closer to equator and more conducive to corals rather than mess with your already good functions :)
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

I like having real sunrise and sunset but the short winter kinda requires zi think either editing of the choffset array, which is how I am running it now, but it is super easy to justdistributethe minimum day length so I will add it and make it so if you enter a zero it's ignored.

I am tying it on my system but my pmw board clock is now wrong, never has been before so this must have caused it. Gotta figure out how to set it without main working....
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Sooooo..... I figured out why my dimming module suddenly went crap-0-la and stopped working.

Damn fish :twisted:

I recently added a Niger (red-tooth) trigger to my tank. It is doing very well, eats like a pig and also apparently enjoys surfing the stream coming out of one of my power heads up near the back left corner (top) of my tank, which is- you guessed it- right over my dimming module sitting down near the ground. :o

Summary and basic electronics tutorial.

Corals and trigger fish live in salt water.
Salt water is apparently corrosive to PCB circuitry (who'd a thunk :mrgreen: )
Short circuiting PCB circuitry causes massive issues with actually running a program
I need a new dimming module

So- PLEASE... if your like me and were loading a ton of stuff to a dimming module during development, then got it all working and had it running for a few months but were too lazy to screw on the nice plastic case to keep the water off it... well... you might consider doing that ASAP. Its mUCH cheaper.
Soehl
Posts: 14
Joined: Wed Jul 04, 2012 7:03 pm

Re: Weather Simulation for Dimming expansion module

Post by Soehl »

I have a MP40w and a MP60w running on my tank. Would it be that difficult to work them into the code?

I am a complete newb and get frustrated(and scared) faster than I can learn this stuff. lol
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Sorry for the long delay... since my module was fried by the damn fish I have been using the very sophisticated method of plug the lights in in the morning and un-plug them at night... it almost exactly mirrors my awake time :)

But... my new module will be here this week so I can play more. The code running on the dimming module really is isolated from the pump. However, and I keep saying I am going to do this.... it is pretty easy to get the dimming module to talk to the main and tell it its a storm, cloud, sunny etc which then could be used to change pump settings. However, although I may put up something with comm in it, I do not have programmable in tank pumps/power heads so I would not be able to help you. That said, there all ready exist a number of implementations for the pumps you have and it would be trivial to change a few lines to get them to run based upon the storm/cloud/etc output from this program.


Just to reiterate for everyone else looking here... the code for the weather package is working 100% and some of it has been ported to the main by lenovo. The only reason I was worried about bugs was the unknown fact that my fish was slowly killing my dimming module... ;
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

So here is a bit of code that might be one solution to the short winter days, and to fixing the bleaching I was getting due to the 17 hour summer days... first bit can be used to symmetrically shift the day to a longer time by just using set=set(5e-1*etc... for each line...

Second bit is not going to work currently due to issues with ChOffset needing to be defined here so its not continually edited downward.... and I am not filling it correctly, I just copy and pasted so ignore that array statement stuff but once I get that fixed the for loop should do what we need... it indexes intensity down as a decimal percentage of the length of the extra daylight (beyond desired light on time) as compared to the desired day... so if your desired day is 100 seconds and your sun rise and set yield 110 your extra day light is 10 seconds, which is 10% of 100 so your lights would be set to 0.9*ChMax values... hope that makes sense...

This is just to gather comment, the first part would probably run now, the second is a SKETCH... with known bugs and issues, it will not even compile.. just thinking this through a bit.

if ((set-rise)<45000){
long dayDelta;
dayDelta=45000-(set-rise);
//change following constants to sum to 1.0 to distribute additional seconds of day accordingly
set=set+(7e-1*dayDelta);
rise=rise-(3e-1*dayDelta);
}
if ((set-rise)>45000){
float dayDelta;
dayDelta=(set-rise)-45000;
dayDelta=1-(dayDelta/45000);//as fractional decrease in intensity max setting for the day (every 10% longer than 12.5 hours reduces ChMax to 0.9*ChMax)

//adjust intensity to compensate for long days
for (byte b=0; b<8; b++){
ChMax=ChMax*dayDelta;
}
}
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

Ok... so I have code compiled that I will be uploading.

What I noticed is that as the seasons changed I got better coloration in my corals, which has now transitioned into some of the Zoas that are partially shaded have begun growing long necks "reaching for the light" so I know that given my latitude and longitude I have too much PAR variation throughout the year, with peak summer causing some loss of coloration even slight bleaching and the depths of winter causing light stress due to too little light.

I decided to take a two pronged approach. Since you cannot go higher than full PWM 100% output which is constrained by the dimmer at whatever max current output you set, the only way to fix the winter light shortage is to enforce a longer day. Since I like to view my tank at night and with sunset before 5 pm where I live, the tank is going dark before I get a chance to fully enjoy it so my solution to adding time to winter days is to basically set a minimal daylight period of 12.5 hours. Then when days are less than that, add back the required time to get to 12.5 hours (45000) seconds and distribute it asymmetrically such that the sunset is delayed more than the rise is accelerated.

Then, if the day is longer than 45000 seconds I decided to use PWM dimming to REDUCE the channel intensity set points by the fractional day length difference

e.g. if the day is 49500 long, which is 4500 longer than 45000 seconds (10%) the individual channel intensities are set back by 10% of their value (in the last part of the code which is from insolation). I am using integer math to do fractions so I am calculating the percent difference and adding 100 to it... if that helps make sense.. this is stripped out in the last bit.


First - you need to globally initialize a variable so add this to the setup

float fracInt;

Then... this code goes into CalSun right below the final calculations to rise and set for elapsed day time.

Code: Select all

//Minimum desired day length is inside if statement 45000=12.5 hrs
 //calculation distributes additional time required to yield 12.5 hour light cycle symetrically to rise and set if you use 5e-1 as constant in if loop below
   if ((set-rise)<45000){
     long dayDelta;
     dayDelta=45000-(set-rise);
     //change following constants to sum to 1.0 to distribute additional seconds of day accordingly
     set=set+(75e-2*dayDelta);//75% of the additional time is used to delay set
     rise=rise-(25e-2*dayDelta);//25% of the additional time is used to set rise to earlier value
     fracInt=100;//set this to make no correction to intensity in insolation for short days
   }
   //Max desired day length at 54000 is 15 hours
   //If calc day length exceeds trim to 54000 and set rise to 0.7 of difference (adding 0.7 of excess) with the rest coming off set 
 else if ((set-rise)>45000){
     float dayDelta;
     dayDelta=100*(((set-rise)-45000)/45000);
     //change following constants to sum to 1.0 to distribute additional seconds of day accordingly
     fracInt=dayDelta+100;//fractional math with integers requires scaling to remove decimal
   }  
 else fracInt=100;//set this for days that are exactly equal to 45000
Then, I changed the Insolation routine a little bit so that once the actual fractional intensity required at the given point of the day (modeling solar insolation using a cosine) the actual output light setting is modified by the fracIntensity to downgrade intensity set point on long days to avoid bleaching.

To assist in not having to find the small changes I made throughout this routine, I am just copy pasting the entire function.

Code: Select all

void Insolation()
{
  InsolationAdvance=false;//reset this flag now that we have entered function

        //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){
           byte c=0;//loop counter
            for (byte b=0;b<16;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
                    ChannelValue[c]=ChMax[c]*(-cos(PiHalf+(ChSlope[b]*secSoFar)));
                 }
                 else if (elapsedTime<ChRiseSet[b]){
                   if (MoonCh[c]==1){  
                      byte MoonToday=MoonPhase()*0.5;//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light
                      if (MoonToday==0) ChannelValue[c]=0;
                      else ChannelValue[c]=MoonToday;
                   }
                   else if (MoonCh[c]==0){
                     ChannelValue[c]=0;//its dark and this is not a moon phase channel
                   }
                 }
              c++;//index by one so we count 0-7 as b goes 0-14 by twos
            }
        }  
        else if (elapsedTime>midDay){
            byte c=0;//loop counter
            for (byte b=1;b<16;b=b+2){
              if (elapsedTime<=ChRiseSet[b]){
                    secSoFar=(elapsedTime-midDay);
                    ChannelValue[c]=ChMax[c]*(-cos(Pi+(ChSlope[b]*secSoFar)));          
               }
               else if (elapsedTime>ChRiseSet[b]){
                   if (MoonCh[c]==1){  
                      byte MoonToday=MoonPhase()*0.5;//SCALE FACTOR to DIM moon setting for use with HIGH power LED as moon light
                      if (MoonToday==0) ChannelValue[c]=0;
                      else ChannelValue[c]=MoonToday;
                   }
                   else if (MoonCh[c]==0){
                     ChannelValue[c]=0;//its dark and this is not a moon phase channel
                   }
                }
              c++;//index to count 0-7 as b counts 1-15 by twos.
            }
        }  
     //The next bit modifies the actual ouput intensity to account for day length variations in attempt to maintain PAR levels 
     //throughout the year.
     //You must set ChMax to obtain adequate PAR for a 12.5 hr (45000) light day
     //This modifies that by REDUCING indensity linearly by % day length over 12.5 Hr for any day over 12.5 Hr.
     //Add Flicker setting to all utilized channels (ChannelValue!=0) to avoid flicker
     byte a=0;
     for (a=0;a<8;a++){
       if (ChannelValue[a]!=0){
         ChannelValue[a]=((ChannelValue[a]/fracInt)*100)+flicker[a];
       }
     }  
   }//END function
//WEATHER FUNCTION BEGIN
The rational i use for taking a direct percent of daylength to modify PWM setting is that the PAR value is "set" at 100% of whatever that is with a PMW setting of 255. Using PWM dimming you are NOT altering the intensity of the LED by modifying current, you are simply flashing it at 100% current- very quickly. So, if you simply flash it 10% less you aught to deliver 10% less PAR to the tank... we shall see.. but it will literally take nearly a year to tell if this is a fix as there is no way to assess this in terms of coral coloration and growth other than to watch the tank.

I AM NOT CERTAIN THAT THIS IS A GOOD APPROACH for analog dimming... maybe someone can chime in.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

I have the mods running and all the code for expanding the daylight times if the day is less than 12.5 hours is working correctly. So if you want this, go ahead and use it. As for the code to dim according to % day length exceeding 45000 seconds- I am not planning on really testing this until summer rolls around and then only will have any idea if its working after we get well into summer and I can see if the corals avoid the bleaching I was seeing- although I suspect it will fix the problem its going to be a months long process to determine if its an adequate adjustment... so when we roll though July I will post back how things are working.
EOD80904
Posts: 1
Joined: Sun Mar 10, 2013 10:12 pm

Re: Weather Simulation for Dimming expansion module

Post by EOD80904 »

So I figured out what to change within the sketch to use with a mega.I also added some code to the loop to see each channel intensity in 0-100%.

Code: Select all

for (int i = 0; i < 8; i++) {  
        Serial.print(" ");
      if (ChannelValue[i] < 10)
        Serial.print(" ");
      else if (ChannelValue[i] < 100)
        Serial.print(" ");
        Serial.print(map(ChannelValue[i], 0, 255, 0, 100));
        Serial.print(" ");
      }
cgaubuchon
Posts: 2
Joined: Fri Mar 22, 2013 12:06 pm

Re: Weather Simulation for Dimming expansion module

Post by cgaubuchon »

So my (first) question is why did you go the route of making this a PWM expansion from the start?It seems like in doing so you automatically limit or make things more difficult to communicate with the Portal and the Controller.

Being somewhat new to RA but well seasoned with Arduino I may be missing the reason but it seems like the code could pretty easier just as well work if flashed to the controller instead.

Just curious as taking apart the PWM to flash changes seems like an extra step without any real benefit other than maybe saving memory space on the controller.

Thanks!
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Weather Simulation for Dimming expansion module

Post by lnevo »

I think it was initially a memory decision. This code may even be out pre ra+ the independent sunrise/sunset class can be used on the main unit and this code could probably be easily ported for the main unit as well.
rufessor
Posts: 293
Joined: Tue Oct 25, 2011 7:39 am

Re: Weather Simulation for Dimming expansion module

Post by rufessor »

This was indeed created pre RA+ and was specifically coded on the PMW expansion module as the older (and many are still current use) RA controllers were pretty constrained with respect to memory and thus to enable full use of the controller I wrote it to be run on the PMW board since it of course has a processor equivalent to the original RA. I am actually going through this code and restructuring it into compact subs so that it can be more easily incorporated into the library and would then be available to all users regardless of configuration so long as memory was in fact free.


I should also note that there is an error in the code in two places, my recently posted update to allow for PWM intensity alteration during extended photo periods during the summer months has a simple error in the math which I have now corrected. I also found an error in the cloud period calculation that is invisible to the user but results in some differences in length of cloud period throughout the days from what I had intended. I will be posting the new package with compact subs relatively soon. My coding skills have moved significantly as I have been working very intensively on PERL for my job and thus I anticipate that I will likely make wholesale changes to the code base since this code evolved as I in fact taught my self the version of C that arduino makes use of and as anyone who has spent any significant time coding will tell you.... your first efforts, although functional, begin to look pretty darn ugly as you learn.

The reason I have not pushed to get this into the library is that the code is very difficult to maintain currently as some subs are pages long which is a cardinal sin- thus I am repenting for my sins and re-doing his in my limited spare time.

I have also made some more changes to the clouds allowing for random drift to occur between the different colors available so that during clouds the actual color temperature of the tank changes. I had initially thought this would look stupid but I tried it and find that it in-fact is a subtle but noticeable improvement as it adds additional variety.

If your using this code with the day length mods for summer and winter as I posted above your probably keeping up with this and would not mind making the simple changes to the lines I have corrected so if you PM me I can send you the changes to make. If your thinking about using it... maybe give me a week or two to get it reworked and tested then I will see if Kurt has time and interest to incorporate this into the library so all can use it.

The other reason I have not gone so far as to ask for it to be put into the library, is that making changes then becomes a much different task as they would be needed to be pushed into the current implementation of the library and then become part of an update and until I am happy with it being maintainable, error free, and truly worthy of incorporation I was hesitant to push for it being in the library.

I promise to get this out. I had been thinking that very few people were using it and just playing with it on my own but have been made to believe that perhaps there is a wider audience and thus will do my part to ensure that once its "out of my hands" and put into the library, its as good as I can make it and easier to understand.
Post Reply