Clouds/Lightning/Storms for 16 Channel PWM Expansion

Do you have a question on how to do something.
Ask in here.
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by rimai »

You already had the code and the idea in place already... :)
You were just missing the same delay for when the cloud starts.
You need to have it offset by the start and duration.
Try something like this:

Code: Select all

      //dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[0],0,60);      
        PWMChannel[8]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[8],0,60); 
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[1],0,60);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[9],0,60); 
[code]
Roberto.
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

Oh duh.... That makes sense.

So this is my code so far. I made that change and added in a random effect for the speed of clouds. The next thing I'm going to work on (if this looks good) is that I would like to have it to where on the fast clouds, there would be no lightning and the cloud would not last long; maybe a minute or two at most.

Code: Select all

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

////// Place global variable code below here
int PWMChannel[16];
int DaylightPWMValue;
////// Place global variable code above here

void setup()

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

 ReefAngel.Use2014Screen();  
// Let's use 2014 Screen 
    

ReefAngel.AddPHExpansion();  
// pH Expansion Module
    

// Ports toggled in Feeding Mode
    
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port8Bit;
    
ReefAngel.FeedingModePortsE[0] = Port1Bit;
    

// Ports toggled in Water Change Mode
    
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port8Bit;
    
ReefAngel.WaterChangePortsE[0] = Port1Bit;
    

// Ports toggled when Lights On / Off menu entry selected
    
ReefAngel.LightsOnPorts = 0;
    
ReefAngel.LightsOnPortsE[0] = 0;
    

// Ports turned off when Overheat temperature exceeded
    
ReefAngel.OverheatShutoffPorts = Port2Bit | Port3Bit;
    
ReefAngel.OverheatShutoffPortsE[0] = 0;
    

// Use T1 probe as temperature and overheat functions
    
ReefAngel.TempProbe = T1_PROBE;
    
ReefAngel.OverheatProbe = T1_PROBE;
    

// Set the Overheat temperature setting
    
InternalMemory.OverheatTemp_write( 869 );


// Ports that are always on
    
ReefAngel.Relay.On( Port4 );


// Add support for 16 channel PWM board
ReefAngel.Add16ChPWM();


////// Place additional initialization code below here
////// Place additional initialization code above here

}

void loop()

{

ReefAngel.SingleATO( true,Port1,90,0 );
    
ReefAngel.StandardHeater( Port2,820,825 );
    
ReefAngel.StandardHeater( Port3,815,823 );
    
ReefAngel.WavemakerRandom( Port5,25,45 );
    
ReefAngel.WavemakerRandom( Port6,25,45 );
    
ReefAngel.StandardLights( Port7,21,0,7,0 );
    
ReefAngel.CO2Control( Port8,670,675 );
    
ReefAngel.DosingPumpRepeat( Box1_Port1,0,60,10 );
    

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


//0 = White Far East
PWMChannel[0]=PWMParabola(8,0,20,30,0,80,0);
//1 = White Near East
PWMChannel[1]=PWMParabola(8,5,20,35,0,80,0);
//2 = White Near West
PWMChannel[2]=PWMParabola(8,10,20,40,0,80,0);
//3 = White Far West
PWMChannel[3]=PWMParabola(8,15,20,45,0,80,0);
//4 = Blue Far East
PWMChannel[4]=PWMParabola(7,15,21,15,0,80,0);
//5 = Blue Near East
PWMChannel[5]=PWMParabola(7,20,21,20,0,80,0);
//6 = Blue Near West
PWMChannel[6]=PWMParabola(7,25,21,25,0,80,0);
//7 = Blue Far West
PWMChannel[7]=PWMParabola(7,30,21,30,0,80,0);
//8 = OCW Far East
PWMChannel[8]=PWMParabola(8,15,20,15,0,60,0);
//9 = OCW Near East
PWMChannel[9]=PWMParabola(8,20,20,20,0,60,0);
//10 = OCW Near West
PWMChannel[10]=PWMParabola(8,25,20,25,0,60,0);
//11 = OCW Far West
PWMChannel[11]=PWMParabola(8,30,20,30,0,60,0);
//12 = Violets
PWMChannel[12]=PWMParabola(8,15,20,30,0,65,0);
//13 = Moonlight Far East
PWMChannel[13]=PWMParabola(20,15,8,30,0,15,0);
//14 = Moonlight Near East and West
PWMChannel[14]=PWMParabola(20,20,8,35,0,15,0);
//15 = Moonlight Far West
PWMChannel[15]=PWMParabola(20,25,8,40,0,15,0);
   
CheckCloud();

ReefAngel.PWM.Set16Channel(0,PWMChannel[0]);
ReefAngel.PWM.Set16Channel(1,PWMChannel[1]);
ReefAngel.PWM.Set16Channel(2,PWMChannel[2]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[3]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[4]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[5]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[6]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[7]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[8]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[9]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[10]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[11]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[12]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[13]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[14]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[15]);

// This should always be the last line
    
ReefAngel.AddWifi();
    
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 1

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

 // Only start the cloud effect after this setting
 // In this example, start could after 8:30am
#define Start_Cloud_After 510

 // Always end the cloud effect before this setting
 // In this example, end could before 7:00pm
#define End_Cloud_Before 1140

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

 // 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
 // 0000111100000000
#define Actinic_Channels 384

 // 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
 // 1111000000000000
#define Daylight_Channels 61440

 // 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 resul could happen.
 // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
 // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can fit in that 510 minutes window.
 // It's a tight fit, but it did.

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


    // Add Random Lightning modes
#define Calm 0    // No lightning
#define Slow 1    // 5 seconds of slow lightning in the middle of a cloud for ELN style (slow response) drivers
#define Fast 2    // 5 seconds of fast lightning in the middle of a cloud for LDD style (fast response) drivers
#define Mega 3    // Lightning throughout the cloud, higher chance as it gets darker
#define Mega2 4   // Like Mega, but with more lightning
  // Set which modes you want to use
  // Example:  { Slow, Fast, Mega, Mega2 } to randomize all four modes.  
  // { Mega2 } for just Mega2.  { Mega, Mega, Fast} for Mega and Fast, with twice the chance of Mega.
  byte LightningModes[] = { Mega, Mega, Mega2, Calm, Calm, Fast };

   // Add Random Cloud modes
#define FastEast 0  //Short cloud coming from the east
#define FastWest 1  //Short cloud coming from the west
#define SlowEast 2  //Long cloud coming from the east
#define SlowWest 3  //Long cloud coming from the west
  // Set which modes you want to use
  //Example:  {FastEast, FastWest, SlowEast, SlowWest } to randomize all four modes
  byte CloudModes[] = { FastEast, FastWest, FastEast, FastWest, SlowEast, SlowWest };



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

 static byte cloudchance=255;
 static byte cloudduration=0;
 static int cloudstart=0;
 static byte numclouds=0;
 static byte lightningchance=0;
 static byte cloudindex=0;
 static byte lightningstatus=0;
 static int LastNumMins=0;
 static byte lightningMode=0;
 static boolean chooseLightning=true;
 static boolean chooseCloud=true;
 static byte cloudMode=0;

 static time_t DelayCounter=millis();    // Variable for lightning timing.  
 static int DelayTime=random(1000);      // Variable for lightning timimg.
 static time_t DelayCount=millis();
 static int DelayTiming1=2000;
 static int DelayTiming2=4000;
 static int DelayTiming3=6000;



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

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

 if (cloudchance)
 {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
       
	if (chooseCloud)
		{
		cloudMode=CloudModes[random(100)%sizeof(CloudModes)];
		chooseCloud=false;
		}


	switch (cloudMode)
	{

	case FastEast;
	{

	//dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[0],0,20);      
        PWMChannel[8]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[8],0,20); 
        
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[1],0,20);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[9],0,20); 
     
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[2],0,20);     
        PWMChannel[10]=ReversePWMSlope(cloudstartDelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[10],0,20);  
     
        //dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[3],0,20);     
        PWMChannel[11]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[11],0,20);        
      
	break;
	}

	case FastWest;
	{

	//dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[3],0,20);      
        PWMChannel[11]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[11],0,20); 
        
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[2],0,20);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[10],0,20); 
       
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[1],0,20);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[9],0,20);       

        //dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[0],0,20);     
        PWMChannel[8]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[8],0,20);        
        
	break;
	}


	case SlowEast;
	{

	//dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[0],0,180);      
        PWMChannel[8]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[8],0,180); 
        
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[1],0,180);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTIming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[9],0,180); 
        
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[2],0,180);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[10],0,180);

        //dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[3],0,180);     
        PWMChannel[11]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[11],0,180);        
      
	break;
	}

	case SlowWest;
	{

	//dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[3],0,180);      
        PWMChannel[11]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[11],0,180); 

        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[2],0,180);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[10],0,180); 

        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[1],0,180);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[9],0,180);

        //dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[0],0,180);     
        PWMChannel[8]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[8],0,180);        	

	break;
	}

	}

	else 
   	{
   	  chooseCloud=true; // Reset the flag to choose a new cloud type
    	}
      


int newdata=4095;
byte channel=0;
      
if (chooseLightning) 
      { 
        lightningMode=LightningModes[random(100)%sizeof(LightningModes)]; 
        chooseLightning=false; 
      } 

     
      
      switch (lightningMode) 
      {
      case Calm:
        {
        break;
        }
      case Mega:
        {
        // Lightning chance from beginning of cloud through the end.  Chance increases with darkness of cloud.
        if (lightning		cloudstart+cloudduration,100,0,180))<1 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {
            for (int g=0;g<16;g++)
            {
      
              if (bitRead(Daylight_Channels,g)==1)
               {
                  newdata=random(DaylightPWMValue*40.95, 4095);
                  channel=g;
                  Wire.beginTransmission(0x41);
                  Wire.write(0x8+(4*channel));
                  Wire.write(newdata&0xff);
                  Wire.write(newdata>>8);
                  Wire.endTransmission();
                }
             }

            int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms
        
             for (int h=0;h<16;h++)
             {
      
                if (bitRead(Daylight_Channels,h)==1)
                 {
                    newdata=DaylightPWMValue*40.95;
                    channel=h;
                    Wire.beginTransmission(0x41);
                    Wire.write(0x8+(4*channel));
                    Wire.write(newdata&0xff);
                    Wire.write(newdata>>8);
                    Wire.endTransmission();
                  }
              }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Mega2:
        {
        // Higher lightning chance from beginning of cloud through the end.  Chance increases with darkness of cloud.
        if (lightningchance && random(ReversePWMSlope(cloudstart,cloudstart+cloudduration,100,0,180))<2 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5);    // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {


      for (int k=0;k<16;k++)
       {
      
      if (bitRead(Daylight_Channels,k)==1)
       {
      newdata=random(DaylightPWMValue*40.95, 4095);
          channel=k;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }

            int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms

      for (int l=0;l<16;l++)
       {
      
      if (bitRead(Daylight_Channels,l)==1)
       {
      newdata=DaylightPWMValue*40.95;
          channel=l;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Fast:
        {
        // 5 seconds of lightning in the middle of the cloud
        if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5);    // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {

            for (int e=0;e<16;e++)
       {
      
      if (bitRead(Daylight_Channels,e)==1)
       {
      newdata=random(DaylightPWMValue*40.95, 4095);
          channel=e;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }
       int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms
            
       for (int f=0;f<16;f++)
       {
      
      if (bitRead(Daylight_Channels,f)==1)
       {
      newdata=DaylightPWMValue*40.95;
          channel=f;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Slow:
        {
        // Slow lightning for 5 seconds in the middle of the cloud.  Suitable for slower ELN style drivers
        if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
        {
          if (random(100)<20) lightningstatus=1; 
          else lightningstatus=0;
          if (lightningstatus)
          {
            newdata=4095;
      for (int c=0;c<16;c++)
       {
      if (bitRead(Daylight_Channels,c)==1)
          {
         channel=c;
             Wire.beginTransmission(0x41);
             Wire.write(0x8+(4*channel));
             Wire.write(newdata&0xff);
             Wire.write(newdata>>8);
             Wire.endTransmission();   
         }   
          }
          }
          }
          else 
          {
           
      newdata=0;
      for (int d=0;d<16;d++)
       {
      if (bitRead(Daylight_Channels,d)==1)
          {
         channel=d;
             Wire.beginTransmission(0x41);
             Wire.write(0x8+(4*channel));
             Wire.write(newdata&0xff);
             Wire.write(newdata>>8);
             Wire.endTransmission();   
         }   
      }

          }
          delay(1);
        }
        break;
      default:
        break;
      }
    } 
    }
    else 
    {
      chooseLightning=true; // Reset the flag to choose a new lightning type
    }



    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
       cloudindex++;
       if (cloudindex < numclouds)
       {
          cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
         // pick a random number for the cloud duration of first cloud.
          cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
          //Pick a random number between 0 and 99
          lightningchance=random(100);
          // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
          if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
	

       }
    }
 }

 


  // Write the times of the next cloud, next lightning, and cloud duration to the screen.
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;
}
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

So I haven't had a lot of time to work on this, but I think I have it doing most of what I want as far as the clouds go. My code is posted below. I do have a few concerns though. Is my code using 12-bit resolution for the dimming for light cycles as well as for clouds? When it is dimming for the clouds how quickly is it sending new data to the dimming expansion (once a second, minute, etc)?
Also, how would I be able to change the shortcloudduration to seconds instead of minutes?


Code: Select all

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

////// Place global variable code below here
int PWMChannel[16];
int DaylightPWMValue;
////// Place global variable code above here

void setup()

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

 ReefAngel.Use2014Screen();  
// Let's use 2014 Screen 
    

ReefAngel.AddPHExpansion();  
// pH Expansion Module
    

// Ports toggled in Feeding Mode
    
ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port8Bit;
    
ReefAngel.FeedingModePortsE[0] = Port1Bit;
    

// Ports toggled in Water Change Mode
    
ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port8Bit;
    
ReefAngel.WaterChangePortsE[0] = Port1Bit;
    

// Ports toggled when Lights On / Off menu entry selected
    
ReefAngel.LightsOnPorts = 0;
    
ReefAngel.LightsOnPortsE[0] = 0;
    

// Ports turned off when Overheat temperature exceeded
    
ReefAngel.OverheatShutoffPorts = Port2Bit | Port3Bit;
    
ReefAngel.OverheatShutoffPortsE[0] = 0;
    

// Use T1 probe as temperature and overheat functions
    
ReefAngel.TempProbe = T1_PROBE;
    
ReefAngel.OverheatProbe = T1_PROBE;
    

// Set the Overheat temperature setting
    
InternalMemory.OverheatTemp_write( 869 );


// Ports that are always on
    
ReefAngel.Relay.On( Port4 );


// Add support for 16 channel PWM board
ReefAngel.Add16ChPWM();


////// Place additional initialization code below here
////// Place additional initialization code above here

}

void loop()

{

ReefAngel.SingleATO( true,Port1,90,0 );
    
ReefAngel.StandardHeater( Port2,820,825 );
    
ReefAngel.StandardHeater( Port3,815,823 );
    
ReefAngel.WavemakerRandom( Port5,25,45 );
    
ReefAngel.WavemakerRandom( Port6,25,45 );
    
ReefAngel.StandardLights( Port7,21,0,7,0 );
    
ReefAngel.CO2Control( Port8,670,675 );
    
ReefAngel.DosingPumpRepeat( Box1_Port1,0,60,10 );
    

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


//0 = White Far East
PWMChannel[0]=PWMParabola(8,0,20,30,0,80,0);
//1 = White Near East
PWMChannel[1]=PWMParabola(8,5,20,35,0,80,0);
//2 = White Near West
PWMChannel[2]=PWMParabola(8,10,20,40,0,80,0);
//3 = White Far West
PWMChannel[3]=PWMParabola(8,15,20,45,0,80,0);
//4 = Blue Far East
PWMChannel[4]=PWMParabola(7,15,21,15,0,80,0);
//5 = Blue Near East
PWMChannel[5]=PWMParabola(7,20,21,20,0,80,0);
//6 = Blue Near West
PWMChannel[6]=PWMParabola(7,25,21,25,0,80,0);
//7 = Blue Far West
PWMChannel[7]=PWMParabola(7,30,21,30,0,80,0);
//8 = OCW Far East
PWMChannel[8]=PWMParabola(8,15,20,15,0,60,0);
//9 = OCW Near East
PWMChannel[9]=PWMParabola(8,20,20,20,0,60,0);
//10 = OCW Near West
PWMChannel[10]=PWMParabola(8,25,20,25,0,60,0);
//11 = OCW Far West
PWMChannel[11]=PWMParabola(8,30,20,30,0,60,0);
//12 = Violets
PWMChannel[12]=PWMParabola(8,15,20,30,0,65,0);
//13 = Moonlight Far East
PWMChannel[13]=PWMParabola(20,15,8,30,0,15,0);
//14 = Moonlight Near East and West
PWMChannel[14]=PWMParabola(20,20,8,35,0,15,0);
//15 = Moonlight Far West
PWMChannel[15]=PWMParabola(20,25,8,40,0,15,0);
   
CheckCloud();

ReefAngel.PWM.Set16Channel(0,PWMChannel[0]);
ReefAngel.PWM.Set16Channel(1,PWMChannel[1]);
ReefAngel.PWM.Set16Channel(2,PWMChannel[2]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[3]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[4]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[5]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[6]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[7]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[8]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[9]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[10]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[11]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[12]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[13]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[14]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[15]);

// This should always be the last line
    
ReefAngel.AddWifi();
    
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 long cloud duration. Don't use min duration of less than 6
#define Min_Cloud_Duration 7

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

 // Minimum number of minutes for short cloud duration.
#define Min_ShortCloud_Duration 1

 // Maximum number of minutes for short cloud duration.
#define Max_ShortCloud_Duration 2

 // 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 12

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

 // Maximum number of short clouds that can happen per day
//#define Max_ShortClouds_per_day 10

 // Only start the cloud effect after this setting
 // In this example, start could after 8:30am (in minutes since midnight)
#define Start_Cloud_After 510

 // Always end the cloud effect before this setting
 // In this example, end could before 7:00pm (in minutes since midnight)
#define End_Cloud_Before 1140

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

 // 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
 // 0000111100000000
#define Actinic_Channels 384

 // 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
 // 1111000000000000
#define Daylight_Channels 61440

 // 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 resul could happen.
 // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
 // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can fit in that 510 minutes window.
 // It's a tight fit, but it did.

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


    // Add Random Lightning modes
#define Calm 0    // No lightning
#define Slow 1    // 5 seconds of slow lightning in the middle of a cloud for ELN style (slow response) drivers
#define Fast 2    // 5 seconds of fast lightning in the middle of a cloud for LDD style (fast response) drivers
#define Mega 3    // Lightning throughout the cloud, higher chance as it gets darker
#define Mega2 4   // Like Mega, but with more lightning
  
  // Set which lightning modes you want to use
  // Example:  { Slow, Fast, Mega, Mega2 } to randomize all four modes.  
  // { Mega2 } for just Mega2.  { Mega, Mega, Fast} for Mega and Fast, with twice the chance of Mega.
  byte LightningModes[] = { Mega, Mega, Mega2, Calm, Calm, Fast };

   // Add Random Cloud modes
#define FastEast 0  //Short cloud coming from the east - No Lightning
#define FastWest 1  //Short cloud coming from the west - No Lightning
#define SlowEast 2  //Long cloud coming from the east - Chance of Lightning
#define SlowWest 3  //Long cloud coming from the west - Chance of Lightning
  
  // Set which cloud modes you want to use
  //Example:  {FastEast, FastWest, SlowEast, SlowWest } to randomize all four modes
  byte CloudModes[] = { FastEast, FastWest, FastEast, FastWest, FastEast, FastWest, SlowEast, SlowWest };



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

 static byte cloudchance=255;
 static byte cloudduration=0;
 static int cloudstart=0;
 static byte numclouds=0;
 static byte lightningchance=0;
 static byte cloudindex=0;
 static byte lightningstatus=0;
 static int LastNumMins=0;
 static byte lightningMode=0;
 static boolean chooseLightning=true;
 static boolean chooseCloud=true;
 static byte cloudMode=0;

 static time_t DelayCounter=millis();    // Variable for lightning timing.  
 static int DelayTime=random(1000);      // Variable for lightning timimg.
 static time_t DelayCount=millis();
 static int DelayTiming1=2000;
 static int DelayTiming2=4000;
 static int DelayTiming3=6000;



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

#ifdef forcecloudcalculation
 if (cloudchance==255)
#else
 if (hour()==0 && minute()==0 && second()==1 && cloudchance==255) 
#endif
 {
 randomSeed(millis());    // Seed the random number generator
 //Pick a random number between 0 and 99
 cloudchance=random(100); 
 // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
 if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
 // Check if today is day for clouds. 
 if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
 // If we have cloud today
 if (cloudchance)
 {
 // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
 numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
 // pick the time that the first cloud will start
 // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
 cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
 // pick a random number for the cloud duration of first cloud.
 cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
 // pick a random number for the short cloud duration
 shortcloudduration=random(Min_ShortCloud_Duration,Max_ShortCloud_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))
    {
       
	if (chooseCloud)
		{
		cloudMode=CloudModes[random(100)%sizeof(CloudModes)];
		chooseCloud=false;
		}


	switch (cloudMode)
	{

	case FastEast;
	{

	//dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart,cloudstart+shortcloudduration,PWMChannel[0],0,20);      
        PWMChannel[8]=ReversePWMSlope(cloudstart,cloudstart+shortcloudduration,PWMChannel[8],0,20); 
        
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+shortcloudduration+DelayTiming1,PWMChannel[1],0,20);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+shortcloudduration+DelayTiming1,PWMChannel[9],0,20); 
     
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+shortcloudduration+DelayTiming2,PWMChannel[2],0,20);     
        PWMChannel[10]=ReversePWMSlope(cloudstartDelayTiming2,cloudstart+shortcloudduration+DelayTiming2,PWMChannel[10],0,20);  
     
        //dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+shortcloudduration+DelayTiming3,PWMChannel[3],0,20);     
        PWMChannel[11]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+shortcloudduration+DelayTiming3,PWMChannel[11],0,20);        
      
	chooseLightning=false; 

	break;
	}

	case FastWest;
	{

	//dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart,cloudstart+shortcloudduration,PWMChannel[3],0,20);      
        PWMChannel[11]=ReversePWMSlope(cloudstart,cloudstart+shortcloudduration,PWMChannel[11],0,20); 
        
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+shortcloudduration+DelayTiming1,PWMChannel[2],0,20);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+shortcloudduration+DelayTiming1,PWMChannel[10],0,20); 
       
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+shortcloudduration+DelayTiming2,PWMChannel[1],0,20);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+shortcloudduration+DelayTiming2,PWMChannel[9],0,20);       

        //dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+shortcloudduration+DelayTiming3,PWMChannel[0],0,20);     
        PWMChannel[8]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+shortcloudduration+DelayTiming3,PWMChannel[8],0,20);        
        
	chooseLightning=false; 

	break;
	}


	case SlowEast;
	{

	//dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[0],0,180);      
        PWMChannel[8]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[8],0,180); 
        
        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[1],0,180);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTIming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[9],0,180); 
        
        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[2],0,180);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[10],0,180);

        //dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[3],0,180);     
        PWMChannel[11]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[11],0,180);        
      
	break;
	}

	case SlowWest;
	{

	//dim down the far west channels
        PWMChannel[3]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[3],0,180);      
        PWMChannel[11]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[11],0,180); 

        //dim down the near west channels
        PWMChannel[2]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[2],0,180);     
        PWMChannel[10]=ReversePWMSlope(cloudstart+DelayTiming1,cloudstart+cloudduration+DelayTiming1,PWMChannel[10],0,180); 

        //dim down the near east channels
        PWMChannel[1]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[1],0,180);     
        PWMChannel[9]=ReversePWMSlope(cloudstart+DelayTiming2,cloudstart+cloudduration+DelayTiming2,PWMChannel[9],0,180);

        //dim down the far east channels
        PWMChannel[0]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[0],0,180);     
        PWMChannel[8]=ReversePWMSlope(cloudstart+DelayTiming3,cloudstart+cloudduration+DelayTiming3,PWMChannel[8],0,180);        	

	break;
	}

	}

	else 
   	{
   	  chooseCloud=true; // Reset the flag to choose a new cloud type
    	}
      


int newdata=4095;
byte channel=0;
      
if (chooseLightning) 
      { 
        lightningMode=LightningModes[random(100)%sizeof(LightningModes)]; 
        chooseLightning=false; 
      } 

     
      
      switch (lightningMode) 
      {
      case Calm:
        {
        break;
        }
      case Mega:
        {
        // Lightning chance from beginning of cloud through the end.  Chance increases with darkness of cloud.
        if (lightningchance && random(ReversePWMSlope(cloudstart,cloudstart+cloudduration,100,0,180))<1 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5); // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {
            for (int g=0;g<16;g++)
            {
      
              if (bitRead(Daylight_Channels,g)==1)
               {
                  newdata=random(DaylightPWMValue*40.95, 4095);
                  channel=g;
                  Wire.beginTransmission(0x41);
                  Wire.write(0x8+(4*channel));
                  Wire.write(newdata&0xff);
                  Wire.write(newdata>>8);
                  Wire.endTransmission();
                }
             }

            int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms
        
             for (int h=0;h<16;h++)
             {
      
                if (bitRead(Daylight_Channels,h)==1)
                 {
                    newdata=DaylightPWMValue*40.95;
                    channel=h;
                    Wire.beginTransmission(0x41);
                    Wire.write(0x8+(4*channel));
                    Wire.write(newdata&0xff);
                    Wire.write(newdata>>8);
                    Wire.endTransmission();
                  }
              }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Mega2:
        {
        // Higher lightning chance from beginning of cloud through the end.  Chance increases with darkness of cloud.
        if (lightningchance && random(ReversePWMSlope(cloudstart,cloudstart+cloudduration,100,0,180))<2 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5);    // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {


      for (int k=0;k<16;k++)
       {
      
      if (bitRead(Daylight_Channels,k)==1)
       {
      newdata=random(DaylightPWMValue*40.95, 4095);
          channel=k;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }

            int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms

      for (int l=0;l<16;l++)
       {
      
      if (bitRead(Daylight_Channels,l)==1)
       {
      newdata=DaylightPWMValue*40.95;
          channel=l;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Fast:
        {
        // 5 seconds of lightning in the middle of the cloud
        if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5 && (millis()-DelayCounter)>DelayTime)
        {
          int a=random(1,5);    // Pick a number of consecutive flashes from 1 to 4.  
          for (int i=0; i<a; i++)
          {

            for (int e=0;e<16;e++)
       {
      
      if (bitRead(Daylight_Channels,e)==1)
       {
      newdata=random(DaylightPWMValue*40.95, 4095);
          channel=e;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }
       int randy=random(20,80);    // Random number for a delay
            if (randy>71) randy=((randy-70)/2)*100;    // Small chance of a longer delay
            delay(randy);                // Wait from 20 to 69 ms, or 100-400 ms
            
       for (int f=0;f<16;f++)
       {
      
      if (bitRead(Daylight_Channels,f)==1)
       {
      newdata=DaylightPWMValue*40.95;
          channel=f;
          Wire.beginTransmission(0x41);
          Wire.write(0x8+(4*channel));
          Wire.write(newdata&0xff);
          Wire.write(newdata>>8);
          Wire.endTransmission();
      }
      }

            delay(random(30,50));                // Wait from 30 to 49 ms 
            wdt_reset();
          }
          DelayCounter=millis();    // If we just had a round of flashes, then lets put in a longer delay
          DelayTime=random(1000);   // of up to a second for dramatic effect before we do another round. 
        }
        break;
        }
      case Slow:
        {
        // Slow lightning for 5 seconds in the middle of the cloud.  Suitable for slower ELN style drivers
        if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
        {
          if (random(100)<20) lightningstatus=1; 
          else lightningstatus=0;
          if (lightningstatus)
          {
            newdata=4095;
      for (int c=0;c<16;c++)
       {
      if (bitRead(Daylight_Channels,c)==1)
          {
         channel=c;
             Wire.beginTransmission(0x41);
             Wire.write(0x8+(4*channel));
             Wire.write(newdata&0xff);
             Wire.write(newdata>>8);
             Wire.endTransmission();   
         }   
          }
          }
          }
          else 
          {
           
      newdata=0;
      for (int d=0;d<16;d++)
       {
      if (bitRead(Daylight_Channels,d)==1)
          {
         channel=d;
             Wire.beginTransmission(0x41);
             Wire.write(0x8+(4*channel));
             Wire.write(newdata&0xff);
             Wire.write(newdata>>8);
             Wire.endTransmission();   
         }   
      }

          }
          delay(1);
        }
        break;
      default:
        break;
      }
    } 
    }
    else 
    {
      chooseLightning=true; // Reset the flag to choose a new lightning type
    }



    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
       cloudindex++;
       if (cloudindex < numclouds)
       {
          cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
         // pick a random number for the cloud duration of first cloud.
          cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
          //Pick a random number between 0 and 99
 	  // pick a random number for the short cloud duration
 	  shortcloudduration=random(Min_ShortCloud_Duration,Max_ShortCloud_Duration);
          lightningchance=random(100);
          // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
          if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
	

       }
    }
 }

 


  // Write the times of the next cloud, next lightning, and cloud duration to the screen.
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;
}
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by rimai »

When dimming for the clouds, it sends data every second.
When dimming on regular parabola, it sends once a minute.
Yu would have to change the entire logic to make it in seconds, but when a cloud is activated it takes 20 seconds to ramp down and 20 seconds to ramp up. That's already 40 seconds as it is.
Unless you were planning to have a really fast ramp up/down, the benefit to refactor the entire code is not worth it.
Roberto.
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

What I'm trying to recreate is the clouds that you see on a sunny day that are small and pass by really quickly.
The sun dims down very quickly, like in 1-2 seconds and the cloud may last for 5-30 seconds, and then the sun comes back just as quick.

Also, unless its a really slow dimming, the full 12-bit resolution is not being used if it is just updating once a minute (or once a second for the clouds).

Which parts would have to be changed?
Would this be too taxing for the controller?


Thanks!
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by AlanM »

rimai wrote:When dimming for the clouds, it sends data every second.
When dimming on regular parabola, it sends once a minute.
Yu would have to change the entire logic to make it in seconds, but when a cloud is activated it takes 20 seconds to ramp down and 20 seconds to ramp up. That's already 40 seconds as it is.
Unless you were planning to have a really fast ramp up/down, the benefit to refactor the entire code is not worth it.
I hven't been following the whole thread, but if you use ReefAngel.PWM.PWMParabola functions, they now use the HighRes versions and send once per second in the dev libraries. If you're calling PWMParabola directly from Globals then you can instead call PWMParabolaHighRes and get values once per second.

The output is different, though. In both of them you give an input of startPWM and endPWM as a percent, but the output of PWMParabola will be in percent and change once per minute.

The output of PWMParabolaHighRes will be in values from 0 to 4095 where 4095 is 100 percent, gets updated each second, and can be written to the 16 channel dimming expansion directly.
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by rimai »

If you want a really fast cloud, you might as well just recreate a new ReversePWMSlope to use milliseconds instead of seconds and use the regular ReversePWMSlope for long clouds and your new ReversePWMSlope for fast clouds.
You code would also need to be conditional to call one of these depending on your preference.
Roberto.
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

As far as the high res stuff, would this be right?

Code: Select all

//0 = White Far East
PWMChannel[0]=PWMParabolaHighRes(8,0,20,30,0,80,0);
//1 = White Near East
PWMChannel[1]=PWMParabolaHighRes(8,5,20,35,0,80,0);
//2 = White Near West
PWMChannel[2]=PWMParabolaHighRes(8,10,20,40,0,80,0);
//3 = White Far West
PWMChannel[3]=PWMParabolaHighRes(8,15,20,45,0,80,0);
//4 = Blue Far East
PWMChannel[4]=PWMParabolaHighRes(7,15,21,15,0,80,0);
//5 = Blue Near East
PWMChannel[5]=PWMParabolaHighRes(7,20,21,20,0,80,0);
//6 = Blue Near West
PWMChannel[6]=PWMParabolaHighRes(7,25,21,25,0,80,0);
//7 = Blue Far West
PWMChannel[7]=PWMParabolaHighRes(7,30,21,30,0,80,0);
//8 = OCW Far East
PWMChannel[8]=PWMParabolaHighRes(8,15,20,15,0,60,0);
//9 = OCW Near East
PWMChannel[9]=PWMParabolaHighRes(8,20,20,20,0,60,0);
//10 = OCW Near West
PWMChannel[10]=PWMParabolaHighRes(8,25,20,25,0,60,0);
//11 = OCW Far West
PWMChannel[11]=PWMParabolaHighRes(8,30,20,30,0,60,0);
//12 = Violets
PWMChannel[12]=PWMParabolaHighRes(8,15,20,30,0,65,0);
//13 = Moonlight Far East
PWMChannel[13]=PWMParabolaHighRes(20,15,8,30,0,15,0);
//14 = Moonlight Near East and West
PWMChannel[14]=PWMParabolaHighRes(20,20,8,35,0,15,0);
//15 = Moonlight Far West
PWMChannel[15]=PWMParabolaHighRes(20,25,8,40,0,15,0);
   

or would I have to convert the 80, 60, 65, 15 to what they would be 0-4095?

Thanks
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by AlanM »

rimai wrote:If you want a really fast cloud, you might as well just recreate a new ReversePWMSlope to use milliseconds instead of seconds and use the regular ReversePWMSlope for long clouds and your new ReversePWMSlope for fast clouds.
You code would also need to be conditional to call one of these depending on your preference.
This is a great idea. I don't know how fast each loop will be with all that stuff in it, but I'd be interested in seeing how fast you can send new values to the 16 channel PWM for dimming. 8)
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by AlanM »

I don't know if that will be right what you posted above because I don't know for sure what you're doing with those PWMChannel array values. Your inputs are fine as 80, 60, 65, but the PWMChannel array will not have larger numbers.

I suspect that you'll need to change something further down to account for the fact that those PWMChannel[x] now have values up to 4095 in them instead of up to 100 and maybe not multiply something by 40.95 before you're writing the values to the dimming expansion, but I'm not positive.
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

So this is how I would write the values?

Code: Select all

//0 = White Far East
PWMChannel[0]=PWMParabolaHighRes(8,0,20,30,0,3276,0);  //80%
//1 = White Near East
PWMChannel[1]=PWMParabolaHighRes(8,5,20,35,0,3276,0);  //80%
//2 = White Near West
PWMChannel[2]=PWMParabolaHighRes(8,10,20,40,0,3276,0);  //80%
//3 = White Far West
PWMChannel[3]=PWMParabolaHighRes(8,15,20,45,0,3276,0);  //80%
//4 = Blue Far East
PWMChannel[4]=PWMParabolaHighRes(7,15,21,15,0,3276,0);  //80%
//5 = Blue Near East
PWMChannel[5]=PWMParabolaHighRes(7,20,21,20,0,3276,0);  //80%
//6 = Blue Near West
PWMChannel[6]=PWMParabolaHighRes(7,25,21,25,0,3276,0);  //80%
//7 = Blue Far West
PWMChannel[7]=PWMParabolaHighRes(7,30,21,30,0,3276,0);  //80%
//8 = OCW Far East
PWMChannel[8]=PWMParabolaHighRes(8,15,20,15,0,2457,0);  //60%
//9 = OCW Near East
PWMChannel[9]=PWMParabolaHighRes(8,20,20,20,0,2457,0);  //60%
//10 = OCW Near West
PWMChannel[10]=PWMParabolaHighRes(8,25,20,25,0,2457,0);  //60%
//11 = OCW Far West
PWMChannel[11]=PWMParabolaHighRes(8,30,20,30,0,2457,0);  //60%
//12 = Violets
PWMChannel[12]=PWMParabolaHighRes(8,15,20,30,0,2662,0);  //65%
//13 = Moonlight Far East
PWMChannel[13]=PWMParabolaHighRes(20,15,8,30,0,614,0);  //15%
//14 = Moonlight Near East and West
PWMChannel[14]=PWMParabolaHighRes(20,20,8,35,0,614,0);  //15%
//15 = Moonlight Far West
PWMChannel[15]=PWMParabolaHighRes(20,25,8,40,0,614,0);  //15%
   
CheckCloud();

ReefAngel.PWM.Set16Channel(0,PWMChannel[0]);
ReefAngel.PWM.Set16Channel(1,PWMChannel[1]);
ReefAngel.PWM.Set16Channel(2,PWMChannel[2]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[3]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[4]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[5]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[6]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[7]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[8]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[9]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[10]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[11]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[12]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[13]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[14]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[15]);
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by AlanM »

No, youre getting it backwards. The highres functions take percents as arguments and return values up to 4095. They dont take those values as arguments.
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

ok, so it should be like this, right?

Code: Select all

//0 = White Far East
PWMChannel[0]=PWMParabolaHighRes(8,0,20,30,0,80,0);  //80%
//1 = White Near East
PWMChannel[1]=PWMParabolaHighRes(8,5,20,35,0,80,0);  //80%
//2 = White Near West
PWMChannel[2]=PWMParabolaHighRes(8,10,20,40,0,80,0);  //80%
//3 = White Far West
PWMChannel[3]=PWMParabolaHighRes(8,15,20,45,0,80,0);  //80%
//4 = Blue Far East
PWMChannel[4]=PWMParabolaHighRes(7,15,21,15,0,80,0);  //80%
//5 = Blue Near East
PWMChannel[5]=PWMParabolaHighRes(7,20,21,20,0,80,0);  //80%
//6 = Blue Near West
PWMChannel[6]=PWMParabolaHighRes(7,25,21,25,0,80,0);  //80%
//7 = Blue Far West
PWMChannel[7]=PWMParabolaHighRes(7,30,21,30,0,80,0);  //80%
//8 = OCW Far East
PWMChannel[8]=PWMParabolaHighRes(8,15,20,15,0,60,0);  //60%
//9 = OCW Near East
PWMChannel[9]=PWMParabolaHighRes(8,20,20,20,0,60,0);  //60%
//10 = OCW Near West
PWMChannel[10]=PWMParabolaHighRes(8,25,20,25,0,60,0);  //60%
//11 = OCW Far West
PWMChannel[11]=PWMParabolaHighRes(8,30,20,30,0,60,0);  //60%
//12 = Violets
PWMChannel[12]=PWMParabolaHighRes(8,15,20,30,0,65,0);  //65%
//13 = Moonlight Far East
PWMChannel[13]=PWMParabolaHighRes(20,15,8,30,0,15,0);  //15%
//14 = Moonlight Near East and West
PWMChannel[14]=PWMParabolaHighRes(20,20,8,35,0,15,0);  //15%
//15 = Moonlight Far West
PWMChannel[15]=PWMParabolaHighRes(20,25,8,40,0,15,0);  //15%
   
CheckCloud();

ReefAngel.PWM.Set16Channel(0,PWMChannel[0]);
ReefAngel.PWM.Set16Channel(1,PWMChannel[1]);
ReefAngel.PWM.Set16Channel(2,PWMChannel[2]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[3]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[4]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[5]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[6]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[7]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[8]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[9]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[10]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[11]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[12]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[13]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[14]);
ReefAngel.PWM.Set16Channel(3,PWMChannel[15]);

if it is taking percents would it only have 100 levels of resolution or would it take 46.29% for example and convert that to 1896?
dlplunkett44
Posts: 74
Joined: Mon Aug 05, 2013 3:16 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by dlplunkett44 »

Also, as far as the new reversePWMSlope function goes, would this look good?

Code: Select all

int FastReversePWMSlope(long fcstart,long fcend,byte fPWMStart,byte fPWMEnd, byte fclength)
{
 long n=1000*elapsedSecsToday(now());
 fcstart*=60000;
 fcend*=60000;
 if (n<fcstart) return fPWMStart;
 if (n>=fcstart && n<=(fcstart+fclength)) return map(n,fcstart,fcstart+fclength,fPWMStart,fPWMEnd);
 if (n>(fcstart+fclength) && n<(fcend-fclength)) return fPWMEnd;
 if (n>=(fcend-fclength) && n<=fcend) return map(n,fcend-fclength,fcend,fPWMEnd,fPWMStart);
 if (n>fcend) return fPWMStart;
}
Thanks again for all your help!
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Clouds/Lightning/Storms for 16 Channel PWM Expansion

Post by AlanM »

Sorry I went afk from this forum. Is it working for you now?
Post Reply