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

// Feeeding and Water Change mode speed ReefAngel.DCPump.FeedingSpeed=0; ReefAngel.DCPump.WaterChangeSpeed=0; ReefAngel.DCPump.ActinicChannel=Sync; // Now you're pump will be affected by the portal settings. ReefAngel.DCPump.DaylightChannel=AntiSync; // Now you're pump will be affected by the portal settings. ReefAngel.DCPump.Threshold=30; //Jebao won't go over 30%

// Add random mode if we set to Mode to Custom in portalstatic int rmode;static boolean changeMode=true;

// These are the modes we can cycle through. You can add more and even repeat...byte modes[] = { ReefCrest, Gyre, Lagoon, Constant, TidalSwell, ShortPulse, LongPulse, Else, };

if (now()%SECS_PER_DAY==0 || changeMode==true) { // Change at midnight or if controller rebootedrmode=random(100)%sizeof(modes); // Change the mode once per day to pick from our arraychangeMode=false;}

// Set timer when in feeding modestatic unsigned long feeding;if (ReefAngel.DisplayedMenu==FEEDING_MODE) feeding=now();

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

// 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 5pm#define Start_Cloud_After NumMins(17,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 0

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

// 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: { 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. byte LightningModes[] = {Calm,};

// 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)) { DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue/40.95,0,180)*40.95; 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) { // Send the trigger 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 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) { if (random(100)<20) lightningstatus=1; else lightningstatus=0; if (lightningstatus) { DaylightPWMValue=4095; } else { DaylightPWMValue=0; } 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 and into some customvars for the Portal. 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)); 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,120,(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,90,120,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/2))/60)>=10) x=51; else x=57; //ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))/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/2))%60)>=10) x=69; else x=75; //ReefAngel.LCD.DrawText(0,255,x,120,((cloudstart+(cloudduration/2))%60)); // Write the minute of the next lightning to a custom variable for the Portal ReefAngel.CustomVar[6]=(cloudstart+(cloudduration/2))%60; } } }