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

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

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

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

// Only start the cloud effect after this setting // In this example, start cloud after 12:00pm#define Start_Cloud_After NumMins(12,00)

// Always end the cloud effect before this setting // In this example, end cloud before 9:00pm#define End_Cloud_Before NumMins(21,00)

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

// 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 result 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.

// Set which modes you want to use // Example: { Calm, 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.

// 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) { if (ReefAngel.Relay.isMaskOn(Box1_Port5)) // Change this to whatever port you want to use as a trigger. { cloudstart = NumMins(hour(), minute()); ReefAngel.Relay.Auto(Box1_Port5); // Here, too. } //is it time for cloud yet? if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration)) { // Increase Blue channel first, for better effect and to compensate for drop in Whites

DelayCounter=millis(); // If we just had a round of flashes, then lets put in a longer delay DelayTime=random(1800); // 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) { Strike(); } 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) { Strike();

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) { SlowStrike(); } 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; } } }

void DrawClouds(int x, int y) { // Write the times of the next cloud, next lightning, and cloud duration to the screen and into some customvars for the Portal. ReefAngel.LCD.DrawText(0,255,x,y,"C"); x+=6; ReefAngel.LCD.DrawText(0,255,x,y,"00:00"); x+=34; ReefAngel.LCD.DrawText(0,255,x,y,"L"); x+=6; ReefAngel.LCD.DrawText(0,255,x,y,"00:00"); x=5; if (cloudchance && (NumMins(hour(),minute())<cloudstart)) { int x=0; if ((cloudstart/60)>=10) x=11; else x=17; ReefAngel.LCD.DrawText(0,255,x,y,(cloudstart/60)); //ReefAngel.CustomVar[3]=cloudstart/60; // Write the hour of the next cloud to custom variable for Portal reporting if ((cloudstart%60)>=10) x=29; else x=35; ReefAngel.LCD.DrawText(0,255,x,y,(cloudstart%60)); //ReefAngel.CustomVar[4]=cloudstart%60; // Write the minute of the next cloud to custom variable for Portal reporting

} ReefAngel.LCD.DrawText(0,255,x+85,y,cloudduration); //ReefAngel.CustomVar[7]=(cloudduration); // Put the duration of the next cloud in a custom var for the portal if (lightningchance) { int x=0; if (((cloudstart+(cloudduration/3))/60)>=10) x=51; else x=57; ReefAngel.LCD.DrawText(0,255,x,y,((cloudstart+(cloudduration/3))/60)); //ReefAngel.CustomVar[5]=(cloudstart+(cloudduration/2))/60; // Write the hour of the next lightning to a custom variable for the Portal if (((cloudstart+(cloudduration/3))%60)>=10) x=69; else x=75; ReefAngel.LCD.DrawText(0,255,x,y,((cloudstart+(cloudduration/3))%60)); // Write the minute of the next lightning to a custom variable for the Portal //ReefAngel.CustomVar[6]=(cloudstart+(cloudduration/2))%60; }}