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;
}