Let's Build Troubadour11's Code

Share you PDE file with our community

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Sat Mar 15, 2014 12:31 pm
Hello Everybody!

So it's been a long time coming, but I'm finally making some good progress on my aquarium build and am jumping into the Reef Angel Programming. I am sure I will be needing help with the code, both now and as I continue to build my system. So I'll be keeping this thread up to date with my latest code set up for my system. That way there will be a place for me to ask for help, with all the development history right in one place.

A short intro to where the system I'm developing is currently at equipment wise for programming the controller...
- 90 gallon mixed reef display with 75 gallon refugium
- protein skimmer
- uv sterilizer
- return pump
- 2 heaters
- refugium canister filter/pump
- (2) 1200gph power heads and wavemaker timer
- Static powerhead for added refugium flow
- Reef Angel Plus (PWM signal) and Dimming Expansion
- Gravity fed 20 gallon ATO
- 10 gallon QT w/ filter, air pump, heater, light
- DIY Rapid LED Dimmable Lighting
- 2 fixtures each with 3 strings
- Clear White / Neutral White / Deep Red / Cyan / Blue - Daylight C/W
- Royal Blue / UV (Violet, near UV) - Daylight Actinic B/UV
- Blue/UV - Moonlight
- All 3 strings per fixture are separately dimmable.
- The daylights are PWM signal, and the moonlights are 0-10v Analog
- I also have the refugium lighting running on a separate surge strip timer.

I expect to add the WiFi attachment eventually but do not have it yet.

That basically covers my equipment that's currently in the set up and I'm working toward controlling. I expect at some point to need to add things like dosing pumps, better powerheads to code functions for, LED refugium lights, and the list could go on and on I'm sure :lol: But you get the idea.

Now onto me setting up and building my code. Obviously I'm not going to tackle all those things at once. I've got my system up and running and just completed the Rapid LED build. I have the RA unit mounted and running the LED lights and equipment. But it is definitely a work in progress and just the barebones code to get the lights to dim on and off.

My first step here was to get it up and running and powering equipment, monitoring heaters, and dimming the new LEDs. Then I'd like to move onto custom menus, custom information displays and adding some functions into the menus. I'm thinking things like a "forced weather simulation" (for example fun), lighting cycle test (run 24 hour program over shorter time period), functional wave profiles for powerheads, a menu selection to run a programmable coral acclimation lighting schedule. I'd also like to learn a bit about information logging and portal use. But I expect those options to be quite limited until I can add on the WiFi attachment.

So those are the general long term goals of my RA code development. I'm sure it will morph and change as I accomplish phases and the system develops. But I want to log and share my set up and code work in case it can help anyone reach their goals in the future.

Now onto my first code... 8-) and thanks for your interest and help!

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Sat Mar 15, 2014 12:49 pm
So this is where I started...

I'm pretty sure I've uploaded the initial memory file correctly, but feel I need some clarification on what I should be storing in that file. And what is safe to store in the PDE code. Do lightning schedules and things need to be stored in memory in case of power outages and things? What really should I be keeping in this location and how to set it up?

Then I used the Wizard to step through and generate some barebones code to get me started. Since running through the wizard a few times, I've moved on to figuring out more advanced control through mostly copy and paste from the forums. Cheers to everybody's help that you've already given! ;)

So here is what my "1st code" turned into after a little dabbling. For the most part, it has a staggered sun rise / sun set effect. I believe I also have a moon rise / moon set function started. I think the cloud weather simulation is working correctly for only the left display tank light fixture. But I have been at work and not seen the weather sim yet. And it only works on the left display fixture because that is the fixture wired to the (2) PWM ports on the power relay. I used the "Weather Sim for Standard PWM Ports" code to get this set up.

A Starting Point:
Code: Select all
//*************************************************************************************************************
//Start of PWM Expansion Code Header

// This is just how we are going to reference the PWM expansion ports within the code.
// You can change the labels if you would like, just as long as they are changed all throughout the code too.
#define LEDPWM0 0 //R-DT - White/Color
#define LEDPWM1 1 //R-DT - Blue/UV
#define LEDPWM2 2 //L-DT - MoonLight
#define LEDPWM3 3 //R-DT - Moonlight
#define LEDPWM4 4 //EMPTY
#define LEDPWM5 5 //EMPTY

// Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={0,0,0,0,0,0};

//End of PWM Expansion Code Header
//*************************************************************************************************************

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>

////// Place global variable code below here

byte LDT_DaylightPWMValue=0;
byte LDT_ActinicPWMValue=0;
byte RDT_DaylightPWMValue=0;
byte RDT_ActinicPWMValue=0;

////// Place global variable code above here


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.AddStandardMenu();  // Add Standard Menu

    ReefAngel.Use2014Screen();  // Let's use 2014 Screen
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port5Bit | Port6Bit | Port7Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;

    // Ports that are always on
    ReefAngel.Relay.On( Port3 );
    ReefAngel.Relay.On( Port4 );
    ReefAngel.Relay.On( Port5 );
    ReefAngel.Relay.On( Port6 );
    ReefAngel.Relay.On( Port7 );
    ReefAngel.Relay.On( Port8 );
      // Port1 = Heater
      // Port2 = Heater
      // Port3 = EMPTY - Always On
      // Port4 = EMPTY - Always On
      // Port5 = Return Pump
      // Port6 = Protein Skimmer
      // Port7 = UV Sterilizer
      // Port8 = Refugium Pump
   
    ////// Place additional initialization code below here //////

    // Adds the date and time menu to controller. **Takes up lots of memory space**
    // - Can be removed after change or adding wifi. Time can be changed in portal or through phone app.
    ReefAngel.AddDateTimeMenu();

    ////// Place additional initialization code above here //////
}

void loop()
{
    ReefAngel.StandardHeater(Port1);
    ReefAngel.StandardHeater(Port2);
   
    ////// Place your custom code below here //////
     
//*************************************************************************************************************

    /// Calculate your regular sunrise/sunset PWM values ///
   
    //RA Power Unit - Left Display Tank White/Color - on @ 9:20 a.m. - off @ 9:20 p.m.
    // - Parabola dimming 10%-65%-10%
    LDT_DaylightPWMValue=PWMParabola(9,20,21,20,10,65,LDT_DaylightPWMValue);
   
    //RA Power Unit - Left Display Tank Blue/UV - on @ 8:50 a.m. - off @ 8:50 p.m.
    // - Parabola dimming 10%-45%-10%
    LDT_ActinicPWMValue=PWMParabola(8,50,20,50,10,45,LDT_ActinicPWMValue);
   
    //Dimming Expansion - Right Display Tank White/Color - on @ 9:00 a.m. - off @ 9:00 p.m.
    // - Parabola dimming 10%-65%-10%
    RDT_DaylightPWMValue=PWMParabola(9,00,21,00,10,65,RDT_DaylightPWMValue);
 
    //Dimming Expansion - Right Display Tank Blue/UV - on @ 8:30 a.m. - off @ 8:30 p.m.
    // - Parabola dimming 10%-45%-10%
    RDT_ActinicPWMValue=PWMParabola(8,30,20,30,10,45,RDT_ActinicPWMValue);
   
    CheckCloud();
    ReefAngel.PWM.SetDaylight(LDT_DaylightPWMValue);
    ReefAngel.PWM.SetActinic(LDT_ActinicPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM0,RDT_DaylightPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM1,RDT_ActinicPWMValue);
   
//*************************************************************************************************************

    /// Set MoonLights Cycles ///
   
    //Dimming Expansion - Left Display Tank Moonlight - on @ 9:30 p.m. - off @ 1:00 a.m.
    // - Slope dimming 10% up to (1/2 * MoonPhase) over 30 minutes
    ReefAngel.PWM.SetChannel(LEDPWM2,PWMSlope(21,30,1,00,10,MoonPhase()/5,30,10));
   
    //Dimming Expansion - Right Display Tank Moonlight - on @ 9:10 p.m. - off @ 12:30 a.m.
    // - Slope dimming 10% up to (1/2 * MoonPhase) over 30 minutes
    ReefAngel.PWM.SetChannel(LEDPWM3,PWMSlope(21,10,0,30,10,MoonPhase()/5,30,10));
   
//*************************************************************************************************************

    //Turns all W/C and B/UV channels to 0% power between 9:21 p.m. and 8:29 a.m. to conserve energy
     if ((NumMins(hour(now()),minute(now())) > NumMins(21,21)) && (NumMins(hour(now()),minute(now())) < NumMins(8,29)))
   {
     ReefAngel.PWM.SetDaylight(0);
     ReefAngel.PWM.SetActinic(0);
     ReefAngel.PWM.SetChannel(LEDPWM0,0);
     ReefAngel.PWM.SetChannel(LEDPWM1,0);
   }

    //Turns Moonlight channels to 0% power between 1:01 a.m. and 9:09 p.m. to conserve energy
     if ((NumMins(hour(now()),minute(now())) > NumMins(1,1)) && (NumMins(hour(now()),minute(now())) < NumMins(21,9)))
   {
     ReefAngel.PWM.SetChannel(LEDPWM2,0);
     ReefAngel.PWM.SetChannel(LEDPWM3,0);
   }
   
//*************************************************************************************************************

   
    ////// Place your custom code above here //////

    // This should always be the last line
    ReefAngel.ShowInterface();
}

//*************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 100

  // Minimum number of minutes for cloud duration.  Don't use min duration of less than 6
#define Min_Cloud_Duration 7

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 15

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 3

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 5

  // Only start the cloud effect after this setting
     // In this example, start could happen after 10:30am
#define Start_Cloud_After NumMins(10,30)

  // Always end the cloud effect before this setting
     // In this example, end must be before 8:10pm
#define End_Cloud_Before NumMins(20,10)

  // Percentage chance of lightning happening for every cloud
     // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 100

  // Channels used by the actinic LEDs on the PWM Expansion module
     // These channels will not be dimmed when the cloud effect is triggered
     // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B000010
     
  // Channels used by the daylight LEDs on the PWM Expansion module
     // These channels will be used for the spike when lightning effect is triggered
     // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000001
     
  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen results could happen.
    // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can fit in that 510 minutes window.
    // It's a tight fit, but it did.

    //#define printdebug // Uncomment this for debug print on Serial Monitor window
  //#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process.


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte cloudduration=0;
  static int cloudstart=0;
  static byte numclouds=0;
  static byte lightningchance=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
    if (cloudchance==255)
#else
    if (hour()==0 && minute()==0 && second()==1 && cloudchance==255)
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100);
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds.
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0;
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day.
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      LDT_DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_DaylightPWMValue,0,180);
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5)
      {
        if (random(100)<20) lightningstatus=1;
        else lightningstatus=0;
        if (lightningstatus)
        {
          LDT_DaylightPWMValue=100;
          LDT_ActinicPWMValue=100;
        }
        else
        {
          LDT_DaylightPWMValue=0;
          LDT_ActinicPWMValue=0;
        }
        delay(1);
      }
    }
    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
 
  if (LastNumMins!=NumMins(hour(),minute()))
  {
    LastNumMins=NumMins(hour(),minute());
    ReefAngel.LCD.Clear(255,0,120,132,132);
    ReefAngel.LCD.DrawText(0,255,5,120,"C");
    ReefAngel.LCD.DrawText(0,255,11,120,"00:00");
    ReefAngel.LCD.DrawText(0,255,45,120,"L");
    ReefAngel.LCD.DrawText(0,255,51,120,"00:00");
    if (cloudchance && (NumMins(hour(),minute())<cloudstart))
    {
      int x=0;
      if ((cloudstart/60)>=10) x=11; else x=17;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart/60));
      if ((cloudstart%60)>=10) x=29; else x=35;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart%60));
    }
    ReefAngel.LCD.DrawText(0,255,90,120,cloudduration);
    if (lightningchance)
    {
      int x=0;
      if (((cloudstart+(cloudduration/2))/60)>=10) x=51; else x=57;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/60));
      if (((cloudstart+(cloudduration/2))%60)>=10) x=69; else x=75;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60));
    }
  }   
}

byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
  //End Cloud & Lighting
}


So there is my start to setting up my code. I know it works... more or less... there may be a few things to straighten out. And it's not everything I wanted yet. But baby steps! :lol: I got no errors when I verified it, and it successfully ran my lights and equipment for about a week.

And that is where I started. In a moment, I'll add a new post with the code I just verified uploaded to my controller. I'm hoping the next step works correctly! This next code was built from the one above with added work towards the lighting and weather simulation.

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Sat Mar 15, 2014 1:13 pm
So here is what I just uploaded.

I would appreciate we maybe start building from this code, if it is worthy. :? I will admit that I didn't entirely "know what I was doing." So I have noted the code with everything I added, altered, was trying to accomplish, and even a few questions. Right now this code is on the RA unit and I'm waiting to see if it works correctly.

I did go into the code for the "Weather Simulation for Standard PWM Ports" and added some lines to try and create an offset that would work for the right display tank fixture (which runs on channels 0 & 1 of the dimming expansion). I also re-worked my previous sun/moon, rise/set code. Now I believe it should turn each individual driver to 0% when it's not in use, otherwise runs the lighting, moon cycle, and weather sim.

New Code with variable offset weather sim attmept (Using BOTH Standard Dimming Ports and Dimming Expansion):
Code: Select all
//*************************************************************************************************************
//Start of PWM Expansion Code Header

//This is just how we are going to reference the PWM expansion ports within the code.
//You can change the labels if you would like, just as long as they are changed all throughout the code too.
                  //Standard PowerRelay PWM Daylight = L-DT White/Color
                  //Standard PowerRelay PWM Actinic = L-DT Blue/UV
#define LEDPWM0 0 // R-DT = White/Color
#define LEDPWM1 1 // R-DT = Blue/UV
#define LEDPWM2 2 // L-DT = MoonLight
#define LEDPWM3 3 // R-DT = Moonlight
#define LEDPWM4 4 // - EMPTY -
#define LEDPWM5 5 // - EMPTY -

//Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={0,0,0,0,0,0};

//End of PWM Expansion Code Header
//*************************************************************************************************************

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>

////// Place global variable code below here //////

byte LDT_DaylightPWMValue=0;
byte LDT_ActinicPWMValue=0;
byte RDT_DaylightPWMValue=0;
byte RDT_ActinicPWMValue=0;

////// Place global variable code above here //////


void setup()
{
    //This must be the first line.
    ReefAngel.Init();  //Initialize controller
    //This must be the first line.

    ReefAngel.AddStandardMenu();  // Add Standard Menu
    ReefAngel.Use2014Screen();  // Let's use 2014 Screen
   
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port5Bit | Port6Bit | Port7Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;

    //Ports that are always on
    ReefAngel.Relay.On( Port3 );
    ReefAngel.Relay.On( Port4 );
    ReefAngel.Relay.On( Port5 );
    ReefAngel.Relay.On( Port6 );
    ReefAngel.Relay.On( Port7 );
    ReefAngel.Relay.On( Port8 );
      // Port1 = Heater
      // Port2 = Heater
      // Port3 = CURRENTLY EMPTY - Always On
      // Port4 = CURRENTLY EMPTY - Always On
      // Port5 = Return Pump
      // Port6 = Protein Skimmer
      // Port7 = UV Sterilizer
      // Port8 = Refugium Pump
   
    ////// Place additional initialization code below here //////
   
// ***PLEASE HELP HERE********************************************************************************************************************************************
    // Currently all lights look like they kick on and flash at 100% when I upload code, then turn off and a second later they ramp up to PWM-Parabola value.
    // All drivers are plugged into separate static ON Surge Strip.
    // I thought these 2 pieces of code written above accomplished this but it doesn't seem to be doing what I expect. That code is commented here for reference,
    //                 //byte PWMChannel[]={0,0,0,0,0,0};
    //                 //byte LDT_DaylightPWMValue=0; byte LDT_ActinicPWMValue=0; byte RDT_DaylightPWMValue=0;  byte RDT_ActinicPWMValue=0;

    // Will the below turn all lights to 0% on start up so they don't flash 100% then adjust to correct values after running through the loop?
    // Is what I'm trying to do possible? I'm sure I have some redundancies in here somewhere.

    //Sets values of ALL lighting channels to 0% on Start Up
//     ReefAngel.PWM.SetDaylight(0);
//     ReefAngel.PWM.SetActinic(0);
//     ReefAngel.PWM.SetChannel(LEDPWM0,0);
//     ReefAngel.PWM.SetChannel(LEDPWM1,0);
//     ReefAngel.PWM.SetChannel(LEDPWM2,0);
//     ReefAngel.PWM.SetChannel(LEDPWM3,0);
//     ReefAngel.PWM.SetChannel(LEDPWM4,0);
//     ReefAngel.PWM.SetChannel(LEDPWM5,0);
//****************************************************************************************************************************************************************

    // Adds the date and time menu to controller so I can change for Daylight Savings Time. **Takes up memory space**
    // - Can be removed after change or adding wifi. Time can be changed in portal or through phone app. eventually
    ReefAngel.AddDateTimeMenu();

    ////// Place additional initialization code above here //////
}

void loop()
{
    ReefAngel.StandardHeater(Port1);
    ReefAngel.StandardHeater(Port2);
 
    ////// Place your custom code below here //////

//*********************************************************************************************************************************************************
                                   //// Calculate your regular sunrise/sunset PWM values ////
//*********************************************************************************************************************************************************
 //_____________________________________________________________________________________________________________
    // - Turns RDT - B/UV channel to 0% power between 8:30 p.m. and 8:30 a.m. to conserve energy
    // - Dimming Expansion - LEDPWM1 - RDT Blue/UV - on @ 8:30 a.m. - off @ 8:30 p.m.
    // - Parabola dimming 11%-75%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(20,30)) && (NumMins(hour(now()),minute(now())) < NumMins(8,30)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours?
     ReefAngel.PWM.SetChannel(LEDPWM1,0);
//   LEDPWM1=0;
//   RDT_ActinicPWMValue=0;
      }
      else
      {
    RDT_ActinicPWMValue=PWMParabola(8,30,20,30,11,75,RDT_ActinicPWMValue);
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns LDT - B/UV channel to 0% power between 8:50 p.m. and 8:50 a.m. to conserve energy
    // - RA Power Unit - LDT Blue/UV - on @ 8:50 a.m. - off @ 8:50 p.m.
    // - Parabola dimming 11%-65%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(20,50)) && (NumMins(hour(now()),minute(now())) < NumMins(8,50)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours?
      ReefAngel.PWM.SetActinic(0);
//    LDT_ActinicPWMValue=0;
      }
      else
      {
      LDT_ActinicPWMValue=PWMParabola(8,50,20,50,11,65,LDT_ActinicPWMValue);
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns RDT - W/C channel to 0% power between 9:00 p.m. and 9:00 a.m. to conserve energy
    // - Dimming Expansion - RDT White/Color - on @ 9:00 a.m. - off @ 9:00 p.m.
    // - Parabola dimming 11%-85%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(21,0)) && (NumMins(hour(now()),minute(now())) < NumMins(9,0)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours?
     ReefAngel.PWM.SetChannel(LEDPWM0,0);
//   LEDPWM0=0;
//   RDT_DaylightPWMValue=0;
      }
      else
      {
      RDT_DaylightPWMValue=PWMParabola(9,00,21,00,11,85,RDT_DaylightPWMValue);
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns LDT - W/C channel to 0% power between 9:20 p.m. and 9:20 a.m. to conserve energy
    // - RA Power Unit - LDT White/Color - on @ 9:20 a.m. - off @ 9:20 p.m.
    // - Parabola dimming 11%-75%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(21,20)) && (NumMins(hour(now()),minute(now())) < NumMins(9,20)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
      ReefAngel.PWM.SetDaylight(0);
//    LDT_DaylightPWMValue=0;
      }
      else
      {
      LDT_DaylightPWMValue=PWMParabola(9,20,21,20,11,75,LDT_DaylightPWMValue);
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
    CheckCloud();
    ReefAngel.PWM.SetDaylight(LDT_DaylightPWMValue);
    ReefAngel.PWM.SetActinic(LDT_ActinicPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM0,RDT_DaylightPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM1,RDT_ActinicPWMValue);
//_________________________________________________________________________________________________   



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                   //// Set MoonLights Cycles ////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//_________________________________________________________________________________________________________________________________________________
    // - Turns RDT Moonlights to 0% power between 12:30 a.m. and 9:10 p.m. to conserve energy.
    // - Slope dimming 10% up to 1/2 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 30 minutes.
    // - Dimming Expansion - LEDPWM3 - RDT Moonlight - on @ 9:10 p.m. - off @ 12:30 a.m.
//_________________________________________________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(0,30)) && (NumMins(hour(now()),minute(now())) < NumMins(21,10)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
      ReefAngel.PWM.SetChannel(LEDPWM3,0);
//    LEDPWM3=0;
      }
      else
      {
       ReefAngel.PWM.SetChannel(LEDPWM3,PWMSlope(21,10,0,30,10,MoonPhase()/2+10,30,LEDPWM3));
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_________________________________________________________________________________________________________________________________________________
    // - Turns LDT Moonlights to 0% power between 1:00 a.m. and 9:30 p.m. to conserve energy
    // - Slope dimming - 10% up to 1/2 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 30 minutes
    // - Dimming Expansion - LEDPWM2 - LDT Moonlight - on @ 9:30 p.m. - off @ 1:00 a.m.
//_________________________________________________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(1,0)) && (NumMins(hour(now()),minute(now())) < NumMins(21,30)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
      ReefAngel.PWM.SetChannel(LEDPWM2,0);
//    LEDPWM2=0;
      }
      else
      {
      ReefAngel.PWM.SetChannel(LEDPWM2,PWMSlope(21,30,1,00,10,MoonPhase()/2+10,30,LEDPWM2));
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    ////// Place your custom code above here //////

    // This should always be the last line
    ReefAngel.ShowInterface();
}


//************************************************************************************************************************************************************************************************************************
  // - My lighting is set up as:
  //      - LDT (Left Display Tank) fixture on the "Standard" 2 (PWM signal) slots on my power relay box.
  //      - RDT (Right Display Tank) fixture on channels 0=daylight and 1=actinic (PWM signal) of the Dimming Expansion.
  //      - Moonlights = channels 2 and 3 of the PWM expansion. These drivers are Analog and the jumper pins have been set for 0-10v Analog in Dimming Expansion.
 
 // - I'd like to calculate the cloud effect for the LDT lights using the "Weather Sim for Standard PWM" code which I think
 //   I got copied, set up, and working correctly for the 1 fixture on the Standard Ports.
 //                 - Then find a way to feed those values into the RDT lights with a "Random Sample" of + or - a small time offset for the cloud effect on the RDT
 //                                - This way I could randomly generate a weather sim, and randomly have the cloud move Right-to-Left or Left-to-Right each time a cloud occurs

//                  - Lightning effect could either be off set with a similar way with +/- value OR left sync'd to LDT lightning (would need to test what looks good).
//                  - Or possibly more or less lightning per fixture during cloud.
//*************************************************************************************************************************************************************************************************************************

// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 100

  // Minimum number of minutes for cloud duration.  Don't use min duration of less than 6
#define Min_Cloud_Duration 7

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 15

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 3

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 5

  // Only start the cloud effect after this setting
     // In this example, start could happen after 10:00am
#define Start_Cloud_After NumMins(10,00)

  // Always end the cloud effect before this setting
     // In this example, end must be before 8:20pm
#define End_Cloud_Before NumMins(20,20)

  // Percentage chance of lightning happening for every cloud
     // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 100

//*********************************************************************************************************************************************************************
  // I'm not sure if these binary labels below are needed or not with they way I was thinking of setting this up.?.?. Please Advise...
  // Should the RDT_DaylightPWMValue be based off the LDT_DaylightPWMValue?

  // Channels used by the actinic LEDs on the PWM Expansion module
     // These channels will not be dimmed when the cloud effect is triggered
     // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
//#define Actinic_Channels B000010
   
  // Channels used by the daylight LEDs on the PWM Expansion module
     // These channels will be used for the spike when lightning effect is triggered
     // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
//#define Daylight_Channels B000001
//*********************************************************************************************************************************************************************
   
  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen results could happen.
    // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can fit in that 510 minutes window.
    // It's a tight fit, but it did.
//____________________________________________________________________________________________________________________________________________________________
  //***Question***
  // Why do you have to double the amount of time from 250 to 500 in your example? - This is just a question to understand the programming better, I don't see why it needs to be doubled.
  // I see it in the code dividing by (numclouds*2), etc. What's the purpose of multiplying it up by 2? I'm just trying to learn on this question if you have time to explain. Thanks :-)
//____________________________________________________________________________________________________________________________________________________________

    //#define printdebug // Uncomment this for debug print on Serial Monitor window
  //#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process.


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte cloudduration=0;
  static int cloudstart=0;

//********************************************************************************************************************************************************
  // - I created this value to use as +/- offset value to make clouds pass left-to-right or vice-versa.
  static int RDTcloudstartOffset=0;

  // - I added this to use as a randomly chosen value between (-)x to y.
  // - This will be added to "cloudstart" in order to determine the number of minutes plus, or minus to offset the RDT cloud by.
  static byte RDTcloudstartRandom=0;

  // - I added this to create a random sample between 50% and 100% dimming power for "random" lightning intensity effect.
  static byte LightningFlashPower=0;
//********************************************************************************************************************************************************

  static byte numclouds=0;
  static byte lightningchance=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;

  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
    if (cloudchance==255)
#else
    if (hour()==0 && minute()==0 && second()==1 && cloudchance==255)
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100);
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds.
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0;
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day.
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

//********************************************************************************************************************************************************
        // pick a random number between these values to offset the RDT cloud by this many minutes
        RDTcloudstartRandom=random(-2,2);
        // adds the random offset value to "cloudstart" and sets the cloudstart time for RDT light fixture
        RDTcloudstartOffset=cloudstart+RDTcloudstartRandom;
//********************************************************************************************************************************************************

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      LDT_DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_DaylightPWMValue,0,180);

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Can I re-use the "ReversePWMSlope" funtion, or do I need to duplicate this at the bottom and make a separate RDT_ReversePWMSlope?
      // What if I do not want the Cloud Effect to dim my white lights all the way to 0%...
      // If I only want them to drop down to the min. 11% that my MeanWells can run the strings at, and not completely turn off the Daylights during a cloud, do I change the 0 here to 11?
      // And what does the 180 value set or tell the program here?

      RDT_DaylightPWMValue=ReversePWMSlope(RDTcloudstartOffset,RDTcloudstartOffset+cloudduration,RDT_DaylightPWMValue,0,180);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5)
      {
        if (random(100)<20) lightningstatus=1;
        else lightningstatus=0;
        if (lightningstatus)
        {
          // - I will remove these 2 lines below if the code I commented out just a few lines below will set the lightning flashes correctly.
          LDT_DaylightPWMValue=100;
          LDT_ActinicPWMValue=100;
          // - I added this to include the RDT lights in the lightning event.
          RDT_DaylightPWMValue=100;
          RDT_ActinicPWMValue=100;
//_______________________________________________________________________________________________________
          // - If there will be lightning, pick a random number between 49 and 99 for the lightning power.
          //LightningFlashPower=random(50,100);

          // - Trying to make a random sample between, say 50% & 100% for the brightness of the lightning?
          //LDT_DaylightPWMValue=LightningFlashPower;
          //LDT_ActinicPWMValue=LightningFlashPower;
          // - Will this work correctly for synced lightning?
          //RDT_DaylightPWMValue=LightningFlashPower;
          //RDT_ActinicPWMValue=LightningFlashPower;
//_______________________________________________________________________________________________________

        }
        else
        {
          LDT_DaylightPWMValue=0;
          LDT_ActinicPWMValue=0;
//------------------------------------------------------------------
          // - IS this correctl?
          RDT_DaylightPWMValue=0;
          RDT_ActinicPWMValue=0;
//------------------------------------------------------------------

        }
        delay(1);
      }
    }
    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
 
  if (LastNumMins!=NumMins(hour(),minute()))
  {
    LastNumMins=NumMins(hour(),minute());
    ReefAngel.LCD.Clear(255,0,120,132,132);
    ReefAngel.LCD.DrawText(0,255,5,120,"C");
    ReefAngel.LCD.DrawText(0,255,11,120,"00:00");
    ReefAngel.LCD.DrawText(0,255,45,120,"L");
    ReefAngel.LCD.DrawText(0,255,51,120,"00:00");
    if (cloudchance && (NumMins(hour(),minute())<cloudstart))
    {
      int x=0;
      if ((cloudstart/60)>=10) x=11; else x=17;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart/60));
      if ((cloudstart%60)>=10) x=29; else x=35;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart%60));
    }
    ReefAngel.LCD.DrawText(0,255,90,120,cloudduration);
    if (lightningchance)
    {
      int x=0;
      if (((cloudstart+(cloudduration/2))/60)>=10) x=51; else x=57;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/60));
      if (((cloudstart+(cloudduration/2))%60)>=10) x=69; else x=75;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60));
    }
  } 
}

byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
  //End Cloud & Lighting
}


All right, so that is where I am currently at. I thank you for making it through these first 3 drawn out posts. I'll keep them much shorter in the future :mrgreen: I promise.

Posts: 11845
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Sat Mar 15, 2014 1:38 pm
Awesome!!!
That's a pretty good start :)
Roberto.

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Mon Mar 17, 2014 7:40 pm
Thanks Roboerto! I thought it was pretty good for not really knowing what I was doing and just figuring it out based off of what was already there. Especially for my first try!

But I am having a few issues with the code that I'm struggling to straighten out. And it's making it really difficult to trouble shoot and try any modifications.

To start with, I have slightly tweaked my last posted code. So here is the latest version I have loaded to my head unit.
Code: Select all
//*************************************************************************************************************
//Start of PWM Expansion Code Header

//This is just how we are going to reference the PWM expansion ports within the code.
//You can change the labels if you would like, just as long as they are changed all throughout the code too.
                  //Standard PowerRelay PWM Daylight = L-DT White/Color
                  //Standard PowerRelay PWM Actinic = L-DT Blue/UV
#define LEDPWM0 0 // R-DT = White/Color
#define LEDPWM1 1 // R-DT = Blue/UV
#define LEDPWM2 2 // L-DT = MoonLight
#define LEDPWM3 3 // R-DT = Moonlight
#define LEDPWM4 4 // - EMPTY -
#define LEDPWM5 5 // - EMPTY -

//Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={0,0,0,0,0,0};

//End of PWM Expansion Code Header
//*************************************************************************************************************

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>

////// Place global variable code below here //////

byte LDT_DaylightPWMValue=0;
byte LDT_ActinicPWMValue=0;
byte RDT_DaylightPWMValue=0;
byte RDT_ActinicPWMValue=0;

////// Place global variable code above here //////


void setup()
{
    //This must be the first line.
    ReefAngel.Init();  //Initialize controller
    //This must be the first line.

    ReefAngel.AddStandardMenu();  // Add Standard Menu
    ReefAngel.Use2014Screen();  // Let's use 2014 Screen
   
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port5Bit | Port6Bit | Port7Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;

    //Ports that are always on
    ReefAngel.Relay.On( Port3 );
    ReefAngel.Relay.On( Port4 );
    ReefAngel.Relay.On( Port5 );
    ReefAngel.Relay.On( Port6 );
    ReefAngel.Relay.On( Port7 );
    ReefAngel.Relay.On( Port8 );
      // Port1 = Heater
      // Port2 = Heater
      // Port3 = CURRENTLY EMPTY - Always On
      // Port4 = CURRENTLY EMPTY - Always On
      // Port5 = Return Pump
      // Port6 = Protein Skimmer
      // Port7 = UV Sterilizer
      // Port8 = Refugium Pump
   
    ////// Place additional initialization code below here //////
   
// ***PLEASE HELP HERE********************************************************************************************************************************************
    // Currently all lights look like they kick on and flash at 100% when I upload code, then turn off and a second later they ramp up to PWM-Parabola value.
    // All drivers are plugged into separate static ON Surge Strip.
    // I thought these 2 pieces of code written above accomplished this but it doesn't seem to be doing what I expect. That code is commented here for reference,
    //                 //byte PWMChannel[]={0,0,0,0,0,0};
    //                 //byte LDT_DaylightPWMValue=0; byte LDT_ActinicPWMValue=0; byte RDT_DaylightPWMValue=0;  byte RDT_ActinicPWMValue=0;

    // Will the below turn all lights to 0% on start up so they don't flash 100% then adjust to correct values after running through the loop?
    // Is what I'm trying to do possible? I'm sure I have some redundancies in here somewhere.

    //Sets values of ALL lighting channels to 0% on Start Up correctly, I think.
     ReefAngel.PWM.SetDaylight(0);
     ReefAngel.PWM.SetActinic(0);
     ReefAngel.PWM.SetChannel(LEDPWM0,0);
     ReefAngel.PWM.SetChannel(LEDPWM1,0);
     ReefAngel.PWM.SetChannel(LEDPWM2,0);
     ReefAngel.PWM.SetChannel(LEDPWM3,0);
     ReefAngel.PWM.SetChannel(LEDPWM4,0);
     ReefAngel.PWM.SetChannel(LEDPWM5,0);
     // I believe using this got rid of my bright flash to 100% when I uploaded new code. Specifically setting the Standard Daylight/Actinic
//****************************************************************************************************************************************************************

    // Adds the date and time menu to controller so I can change for Daylight Savings Time. **Takes up memory space**
    // - Can be removed after change or adding wifi. Time can be changed in portal or through phone app. eventually
    ReefAngel.AddDateTimeMenu();
   
    //Forces "CheckCloud();" on Startup to calculate/recalculate weather sim data so it runs correctly after uploading new code or power interruption.
    // - I'm not sure this will work???
    CheckCloud();
    // - Above did not reset the menu clock or storm fuction correctly. I uploaded around 1:30 or 2:30 and no lightning events have occured as of 6:30.
   
    ////// Place additional initialization code above here //////
}

void loop()
{
    ReefAngel.StandardHeater(Port1);
    ReefAngel.StandardHeater(Port2);
 
    ////// Place your custom code below here //////

//*********************************************************************************************************************************************************
                                   //// Calculate your regular sunrise/sunset PWM values ////
//*********************************************************************************************************************************************************
 //_____________________________________________________________________________________________________________
    // - Turns RDT - B/UV channel to 0% power between 8:30 p.m. and 8:30 a.m. to conserve energy
    // - Dimming Expansion - LEDPWM1 - RDT Blue/UV - on @ 8:30 a.m. - off @ 8:30 p.m.
    // - Parabola dimming 11%-75%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(8,30)) && (NumMins(hour(now()),minute(now())) < NumMins(20,30)))
      {
         RDT_ActinicPWMValue=PWMParabola(8,30,20,30,11,75,RDT_ActinicPWMValue);
      }
      else
      {
//  - Which one is the correct way to set these to 0% during non-operational hours?
//    ReefAngel.PWM.SetChannel(LEDPWM1,0);
//    LEDPWM1=0;
         RDT_ActinicPWMValue=0;
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns LDT - B/UV channel to 0% power between 8:50 p.m. and 8:50 a.m. to conserve energy
    // - RA Standard Power Unit - LDT Blue/UV - on @ 8:50 a.m. - off @ 8:50 p.m.
    // - Parabola dimming 11%-65%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(8,50)) && (NumMins(hour(now()),minute(now())) < NumMins(20,50)))
     {
        LDT_ActinicPWMValue=PWMParabola(8,50,20,50,11,75,LDT_ActinicPWMValue);
      }
      else
      {
//  - Which one is the correct way to set these to 0% during non-operational hours?
//    ReefAngel.PWM.SetActinic(0);
        LDT_ActinicPWMValue=0;
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns RDT - W/C channel to 0% power between 9:00 p.m. and 9:00 a.m. to conserve energy
    // - Dimming Expansion - RDT White/Color - on @ 9:00 a.m. - off @ 9:00 p.m.
    // - Parabola dimming 11%-85%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(9,0)) && (NumMins(hour(now()),minute(now())) < NumMins(21,0)))
     {
        RDT_DaylightPWMValue=PWMParabola(9,00,21,00,11,85,RDT_DaylightPWMValue);
      }
      else
      {
//  - Which one is the correct way to set these to 0% during non-operational hours?
//   ReefAngel.PWM.SetChannel(LEDPWM0,0);
//   LEDPWM0=0;
        RDT_DaylightPWMValue=0;
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_____________________________________________________________________________________________________________
    // - Turns LDT - W/C channel to 0% power between 9:20 p.m. and 9:20 a.m. to conserve energy
    // - RA Power Unit - LDT White/Color - on @ 9:20 a.m. - off @ 9:20 p.m.
    // - Parabola dimming 11%-75%-11%
//_____________________________________________________________________________________________________________
     if ((NumMins(hour(now()),minute(now())) > NumMins(9,20)) && (NumMins(hour(now()),minute(now())) < NumMins(21,20)))
     {
        LDT_DaylightPWMValue=PWMParabola(9,20,21,20,11,75,LDT_DaylightPWMValue);
      }
      else
      {
//   - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
//    ReefAngel.PWM.SetDaylight(0);
        LDT_DaylightPWMValue=0;
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
    CheckCloud();
    ReefAngel.PWM.SetDaylight(LDT_DaylightPWMValue);
    ReefAngel.PWM.SetActinic(LDT_ActinicPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM0,RDT_DaylightPWMValue);
    ReefAngel.PWM.SetChannel(LEDPWM1,RDT_ActinicPWMValue);
//_________________________________________________________________________________________________   



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                   //// Set MoonLights Cycles ////
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//_________________________________________________________________________________________________________________________________________________
    // - Turns RDT Moonlights to 0% power between 12:30 a.m. and 9:10 p.m. to conserve energy.
    // - Slope dimming 10% up to 1/2 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 50 minutes.
    // - Dimming Expansion - LEDPWM3 - RDT Moonlight - on @ 9:10 p.m. - off @ 12:30 a.m.
//_________________________________________________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(0,30)) && (NumMins(hour(now()),minute(now())) < NumMins(21,10)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
      ReefAngel.PWM.SetChannel(LEDPWM3,0);
//    LEDPWM3=0;
      }
      else
      {
       ReefAngel.PWM.SetChannel(LEDPWM3,PWMSlope(21,10,0,30,10,MoonPhase()/2+10,50,LEDPWM3));
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//_________________________________________________________________________________________________________________________________________________
    // - Turns LDT Moonlights to 0% power between 1:00 a.m. and 9:30 p.m. to conserve energy
    // - Slope dimming - 10% up to 1/2 * MoonPhase +10 (to offest for 10% min driver start power). Fades up to current Moonphase over 50 minutes
    // - Dimming Expansion - LEDPWM2 - LDT Moonlight - on @ 9:30 p.m. - off @ 1:00 a.m.
//_________________________________________________________________________________________________________________________________________________
if ((NumMins(hour(now()),minute(now())) > NumMins(1,0)) && (NumMins(hour(now()),minute(now())) < NumMins(21,30)))
     {
// - Which one is the correct way to set these to 0% during non-operational hours with the code I'm attempting?
      ReefAngel.PWM.SetChannel(LEDPWM2,0);
//    LEDPWM2=0;
      }
      else
      {
      ReefAngel.PWM.SetChannel(LEDPWM2,PWMSlope(21,30,1,00,10,MoonPhase()/2+10,50,LEDPWM2));
      }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    ////// Place your custom code above here //////

    // This should always be the last line
    ReefAngel.ShowInterface();
}


//************************************************************************************************************************************************************************************************************************
  // - My lighting is set up as:
  //      - LDT (Left Display Tank) fixture on the "Standard" 2 (PWM signal) slots on my power relay box.
  //      - RDT (Right Display Tank) fixture on channels 0=daylight and 1=actinic (PWM signal) of the Dimming Expansion.
  //      - Moonlights = channels 2 and 3 of the PWM expansion. These drivers are Analog and the jumper pins have been set for 0-10v Analog in Dimming Expansion.
 
 // - I'd like to calculate the cloud effect for the LDT lights using the "Weather Sim for Standard PWM" code which I think
 //   I got copied, set up, and working correctly for the 1 fixture on the Standard Ports.
 //                 - Then find a way to feed those values into the RDT lights with a "Random Sample" of + or - a small time offset for the cloud effect on the RDT
 //                                - This way I could randomly generate a weather sim, and randomly have the cloud move Right-to-Left or Left-to-Right each time a cloud occurs

//                  - Lightning effect could either be off set with a similar way with +/- value OR left sync'd to LDT lightning (would need to test what looks good).
//                  - Or possibly more or less lightning per fixture during cloud.
//*************************************************************************************************************************************************************************************************************************

// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 100

  // Minimum number of minutes for cloud duration.  Don't use min duration of less than 6
#define Min_Cloud_Duration 7

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 15

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 10

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 18

  // Only start the cloud effect after this setting
     // In this example, start could happen after 10:00am
#define Start_Cloud_After NumMins(10,00)

  // Always end the cloud effect before this setting
     // In this example, end must be before 8:20pm
#define End_Cloud_Before NumMins(20,20)

  // Percentage chance of lightning happening for every cloud
     // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 100
   
  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen results could happen.
    // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can fit in that 510 minutes window.
    // It's a tight fit, but it did.
//____________________________________________________________________________________________________________________________________________________________
  //***Question***
  // Why do you have to double the amount of time from 250 to 500 in your example? - This is just a question to understand the programming better, I don't see why it needs to be doubled.
  // I see it in the code dividing by (numclouds*2), etc. What's the purpose of multiplying it up by 2? I'm just trying to learn on this question if you have time to explain. Thanks :-)
//____________________________________________________________________________________________________________________________________________________________

    //#define printdebug // Uncomment this for debug print on Serial Monitor window
  //#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process.


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance = 255;
  static byte cloudduration=0;
  static int cloudstart=0;

//********************************************************************************************************************************************************
  // - I created this value to use as +/- offset value to make clouds pass left-to-right or vice-versa.
  static int RDTcloudstartOffset=0;

  // - I added this to use as a randomly chosen value between (-)x to y.
  // - This will be added to "cloudstart" in order to determine the number of minutes plus, or minus to offset the RDT cloud by.
  static byte RDTcloudstartRandom=0;

  // - I added this to create a random sample between 50% and 100% dimming power for "random" lightning intensity effect on each fixture.
  static byte LDT_LightningFlashPower=0;
  static byte RDT_LightningFlashPower=0;
//********************************************************************************************************************************************************

  static byte numclouds=0;
  static byte lightningchance=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;

  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
    if (cloudchance==255)
#else
    if (hour()==0 && minute()==0 && second()==1 && cloudchance==255)
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100);
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds.
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0;
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day.
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

//********************************************************************************************************************************************************
        // pick a random number between these values to offset the RDT cloud by this many minutes
        RDTcloudstartRandom=random(-2,2);
        // adds the random offset value to "cloudstart" and sets the cloudstart time for RDT light fixture
        RDTcloudstartOffset=cloudstart+RDTcloudstartRandom;
//********************************************************************************************************************************************************

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      //changed 0 to 11 in order to make minimum cloud power 11% so the lights don't click off.
      LDT_DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,LDT_DaylightPWMValue,11,180);

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Can I re-use the "ReversePWMSlope" funtion, or do I need to duplicate this at the bottom and make a separate RDT_ReversePWMSlope?
      // What if I do not want the Cloud Effect to dim my white lights all the way to 0%...
      // If I only want them to drop down to the min. 11% that my MeanWells can run the strings at, and not completely turn off the Daylights during a cloud, do I change the 0 here to 11?
      // And what does the 180 value set or tell the program here?

      //changed 0 to 11 in order to make minimum cloud power 11% so the lights don't click off.
      RDT_DaylightPWMValue=ReversePWMSlope(RDTcloudstartOffset,RDTcloudstartOffset+cloudduration,RDT_DaylightPWMValue,11,180);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     // To adjust possible length of time of lightning strike, Change the "second()< 5 " ... where 5 is the # of seconds of lightning during cloud.
     // and the "minute())=(cloudstart_(cloudduration/2)))" sets start time... dividing by 2 is exactly half way through cloud to trigger 5 seconds lightning.
     // if there is lightning and current time is 1/2 way through cloud, but less then 1/2 way through cloud + 5 seconds
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5)
      {
        if (random(100)<20) lightningstatus=1;
        else lightningstatus=0;
        if (lightningstatus)
        {
          // - Thes 4 lines sync the lightning to flash at the same time at identical levels on both fixtures correctly -
          //LDT_DaylightPWMValue=100;
          //LDT_ActinicPWMValue=100;
          //RDT_DaylightPWMValue=100;
          //RDT_ActinicPWMValue=100;
          // - Thes 4 lines sync the lightning to flash at the same time at identical levels on both fixtures correctly -
//_______________________________________________________________________________________________________
          // - Trying to make a random sample between, say 50% & 100% for the brightness of the lightning?
          // - If there will be lightning, pick a random number between 49 and 99 for the lightning power.
          LDT_LightningFlashPower=random(50,100);
          RDT_LightningFlashPower=random(50,100);

          LDT_DaylightPWMValue=LDT_LightningFlashPower;
          LDT_ActinicPWMValue=LDT_LightningFlashPower;
          RDT_DaylightPWMValue=RDT_LightningFlashPower;
          RDT_ActinicPWMValue=RDT_LightningFlashPower;
//_______________________________________________________________________________________________________

        }
        else
        {
          // - Changed these values from 0 to 11 so at end of lightning cycle, the go to min. 11% power
          LDT_DaylightPWMValue=11;
          LDT_ActinicPWMValue=11;
          RDT_DaylightPWMValue=11;
          RDT_ActinicPWMValue=11;
//------------------------------------------------------------------

        }
        delay(1);
      }
    }
    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
//***********************************************************************************************************************************************************************
//// - I think this resets the random offset and cloudstart time for the RDT Fixture to match the new/next "cloudstart" time if there will be another cloud today ////
//***********************************************************************************************************************************************************************
        // pick a random number between these values to offset the RDT cloud by this many minutes
        RDTcloudstartRandom=random(-2,2);
        // adds the random offset value to "cloudstart" and sets the cloudstart time for RDT light fixture
        RDTcloudstartOffset=cloudstart+RDTcloudstartRandom;
//***********************************************************************************************************************************************************************
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
 
  if (LastNumMins!=NumMins(hour(),minute()))
  {
    LastNumMins=NumMins(hour(),minute());
    ReefAngel.LCD.Clear(255,0,120,132,132);
    ReefAngel.LCD.DrawText(0,255,5,120,"C");
    ReefAngel.LCD.DrawText(0,255,11,120,"00:00");
    ReefAngel.LCD.DrawText(0,255,45,120,"L");
    ReefAngel.LCD.DrawText(0,255,51,120,"00:00");
    if (cloudchance && (NumMins(hour(),minute())<cloudstart))
    {
      int x=0;
      if ((cloudstart/60)>=10) x=11; else x=17;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart/60));
      if ((cloudstart%60)>=10) x=29; else x=35;
      ReefAngel.LCD.DrawText(0,255,x,120,(cloudstart%60));
    }
    ReefAngel.LCD.DrawText(0,255,90,120,cloudduration);
    if (lightningchance)
    {
      int x=0;
      if (((cloudstart+(cloudduration/2))/60)>=10) x=51; else x=57;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/60));
      if (((cloudstart+(cloudduration/2))%60)>=10) x=69; else x=75;
      ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60));
    }
  } 
}

byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
  //End Cloud & Lighting
}

Mostly I just swapped around the "if" statements for my sunrise/set effect. But I did add in a couple variables into the lightning function in the weather sim to try and create a random power variable for each lightning flash. That's mostly what I changed.

As before, there are lots of comments and things noted inside the code wherever I've added or changed things along with some questions on things I'm trying to understand or figure out. But there are a few issues that I'm trying to tackle right now that are leaving me scratching my head.

Both the previously posted code that I had loaded in and this current code *seem* to be working correctly. But both have had similar unexpected issues.

**Note, I have not seem a cloud/lightning sim on this new code yet. I mention why as an issue after running through the lighting loop here.

Starting at midnight. All daylights and actinics are turned off and at 0% with the moonlights at their appropriate values. Each morning I've gotten up, lights are still off and there are new numbers displayed on the Cloud Sim Info section of the head unit display. Which is as expected, those numbers have been correct for the first cloud occurrence 2 days in a row.

Also, the sun rise programming turns on exactly as expected, and looks awesome 8-) . A staggered start up based on the coded times beginning with the blue/uvs and then the white/colors.

It gets to the designated time for the first cloud simulation and I believe is running mostly correctly. Both times I've seen this happen, the LDT has dimmed 1 or 2 minutes before the RDT fixture. So there is an offset being established between the 2 fixtures, although for the 2 mornings I've seen it, it's been the same offset of LDT prior to RDT.
- I think I fixed this back to a random offset in this code version by adding some lines in near the end of the cloudcheck function that should be noted. But again, this has not been tested and viewed yet.

The lightning on first cloud event was as expected. Both fixtures flash at same power and timing.

After the lightning, the clouds ran the remainder of their time and then adjusted back to their correct values.

The weather sim clock on head unit now displays the next times and data for the next cloud event as expected. But this is where things begin to unravel.

On the next cloud event, and every subsequent one for the rest of the day. At the time corresponding to the next event, only the LDT fixture dims for the cloud effect. But BOTH fixtures flash correctly for the lightning event during these remaining clouds.
- I wonder if this is fixed by the offset code I noted adding to this version above.

The LDT fixture stays in cloud mode for the simulated amount of time after lightning and then ramps back up to correct values.
- So why AND how can my cloud sim work with an offset on the 1st event of the day, but then not work for the remaining events in the same day?

Then if the code is left to run without interruption. At the sunset times, all channels dim to their lowest outputs as expected at the right times. But then none of the 4 daylight channels turn off.
- This was the reason for re-working my IF statements on my normal sun rise/set parabolas in this code version. But again, has not been tested yet.

Finally, with all the day light channels at their lowest out put the moonlights kick on and ramp as expected.

So that is what is happening if I let the code run for 24 hours.

The first night of running this code, I waited until both the RDT_Daylight and RDT_Actinic channels were supposed to be turned off and only the LDT fixture on. I re-uploaded the exact previous code that was running on the unit. After it uploaded, the lights on the RDT fixture turned off like it was supposed to. I waited and the RDT moonlights kicked on correctly. But then the LDT_Daylight and LDT_Actinic never turned off and stayed at the min %. So after the LDT fixture was supposed to be turned off, I again re-uploaded the exact same code. After the upload, all daylight and actinic channels were correctly off and the moonlights kicked in and ran their code as expected.

Now, another issue that's REALLY bothering me that I'd like to get worked around is this. Anytime I upload code or a new version of the code. It completely turns off the cloud sim for the remainder of the day. How do I get the cloud calculation to run for the remainder of the day if I adjust code to test the cloud/lightning effect?

Currently, I have to watch what happens on the 1st sim. Then wait for the second sim to see if it's only the LDT fixture again. Adjust new code to try, upload new code. Then wait basically 24 hours for it to recalculate at midnight and be ready to run the next cloud sim sometime the next morning. I need to be able to test the outcome faster then that or it will take me months to do these minor tweaks.

So I'd love any thoughts or ideas on these code issues. I think primarily getting it so I can test new weather sim code easier and faster then once every 24 hours would help a ton. Whether it's just a temp button in a temp menu to force a cloud event, or just getting the cloud sim to run the rest of the day if I upload new code. Then we can move onto to trouble shooting. Because like I mentioned, I think I have some potential solutions possibly in place already in this new posted code. But I can't check or test it since I won't see a cloud event from 2:00 this afternoon until sometime tomorrow morning.

Once again, thanks... and sorry for the book again! :P

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Mon Mar 17, 2014 9:39 pm
Hey, hey! Good news!

I'm happy to report the latest updated code worked perfectly for the sunset effect tonight. Everything dimmed and turned off at the exact right times. So check that off the list for now. I like checking things off the list!!! :mrgreen:

For now I'm going to wait until tomorrow and see what happens with the cloud effect. I expect everything to run correctly until after the first cloud sim. I'll have to see where it goes from there. Maybe it will be fixed too with the latest updates! :-D I'm anxious to see what happens with the random lightning power code I tried in there. I'm not really sure what that will look like.

It's fun playing with this stuff. I'd still be interested in trouble shooting being able to upload new code and quickly see a cloud/lightning sim test if anyone has any thoughts.

I'll let you all know what happens tomorrow with the cloud effects once I see them run 2 times...

Posts: 11845
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Mon Mar 17, 2014 10:23 pm
One easy way is to change the time of the controller forward to be like 1 minute before the cloud.
There is a custom menu in the forums that we worked in the past that fires off the cloud.
Take a look at this thread:
viewtopic.php?f=12&t=425
Roberto.

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Wed Mar 19, 2014 9:59 am
Ah ha! Oh man. I love it when I'm so focused and close to a problem that I can't see the simple solution that's right in front of me. :roll:

Thanks for the tips Roberto! Switching the time is truly a simple solution that can work just fine for now. I'm surprised and embarrassed I didn't think of that. LOL

And I haven't had free time the last few days to mess with the weather sim updates I've been working on. But I can easily trouble shoot at night now, if I have the time by, just changing the clock to 1 minute before midnight. Waiting for it to calculate the clouds for the day, then switch the clock to just before the effect. I am going to take a look at the code in the link you gave me though. I'd still like to add in a menu option for the long term. So after I get a moment to check that out some more, I'll see if I can work it into my code update soon.

I have been taking some videos to try and capture the weather sim effect on my tank. So far I have 2 vids capturing the different incarnations of the code. I'll share those when I have some more time. Maybe I can go back and edit the previous posts so the videos are in with the code that made them.?.?.

Thanks for the tips, and I'll let you all know how it goes on the next update 8-)

Posts: 18
Joined: Tue Nov 12, 2013 2:24 pm
PostPosted: Wed Mar 19, 2014 12:52 pm
Quick question...

I was starting to add in the code you linked to above to add a menu function to force a weather sim. I haven't done any real menu editing on here yet. Other then adding the Date/Time menu to adjust for daylight savings time.

So where does this code go?
Code: Select all
    void MenuEntry1()
    {
    ForceCloud=true;
    ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
    }

- Dose this get added to the "void setup()" section to add the menu?

Thanks!

Posts: 11845
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Wed Mar 19, 2014 12:55 pm
There is a custom menu tutorial. You should read through it before you add the code.

Sent from my Galaxy S3 using Tapatalk
Roberto.
Next

Return to My PDE/INO file

Who is online

Users browsing this forum: No registered users and 1 guest