Storm in code not working

Do you have a question on how to do something.
Ask in here.
Post Reply
troylong45
Posts: 214
Joined: Sat Oct 10, 2015 9:17 pm

Storm in code not working

Post by troylong45 »

i thought it worked at one time and now is not

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 <RA_Colors.h>
    #include <RA_CustomColors.h>
    #include <ReefAngel.h>
    #include <SunLocation.h>
    #include <Tide.h>
    #include <DCPump.h>
    #include <WiFiAlert.h>
    #include <DCPump.h>

    // Won't compile without this...
    // ReefAngel.DCPump.UseMemory=true; 
    // Custom menus
    #include <avr/pgmspace.h>
    prog_char menu1_label[] PROGMEM = "Feeding Mode";
    prog_char menu2_label[] PROGMEM = "Water Change";
    prog_char menu3_label[] PROGMEM = "ATO Clear";
    prog_char menu4_label[] PROGMEM = "Overheat Clear";
    prog_char menu5_label[] PROGMEM = "PH Calibration";
    prog_char menu6_label[] PROGMEM = "Date / Time";

    // Group the menu entries together
    PROGMEM const char *menu_items[] = {
    menu1_label, menu2_label, menu3_label,
    menu4_label, menu5_label, menu6_label
    };

    // Define Custom Memory Locations
    #define Mem_I_Latitude        108
    #define Mem_I_Longitude       110
    #define Mem_B_AcclRiseOffset  112
    #define Mem_B_AcclSetOffset   113
    #define Mem_B_AcclDay         114
    #define Mem_B_LightMode       160
    #define Mem_B_LightOffset     161
    #define Mem_I_RiseOffset      162
    #define Mem_I_SetOffset       164
    #define Mem_B_AcclActinicOffset     166
    #define Mem_B_AcclDaylightOffset    167
    #define Mem_B_LightsOffPerc   171

    #define Mem_B_PrintDebug      198
    #define Mem_B_ResetMemory     199

    void init_memory() {
      // Initialize Custom Memory Locations
      InternalMemory.write_int(Mem_I_Latitude,-21);
      InternalMemory.write_int(Mem_I_Longitude,-147);
      InternalMemory.write(Mem_B_AcclRiseOffset,12);
      InternalMemory.write(Mem_B_AcclSetOffset,13);
      InternalMemory.write(Mem_B_AcclDay,0);
      InternalMemory.write(Mem_B_LightOffset,10);
      InternalMemory.write(Mem_B_LightMode,1);
      InternalMemory.write_int(Mem_I_RiseOffset,21);
      InternalMemory.write_int(Mem_I_SetOffset,21);
      InternalMemory.write(Mem_B_AcclActinicOffset,250);
      InternalMemory.write(Mem_B_AcclDaylightOffset,125);
      InternalMemory.write(Mem_B_LightsOffPerc,1);

      InternalMemory.write(Mem_B_ResetMemory,false);
    }

    #define NUMBERS_8x16


    // Define Relay Ports by Name
    #define Return             1
    #define Heater             2
    #define WhiteLeft          3
    #define BlueLeft           4
    #define WhiteRight         5
    #define BlueRight          6
    #define Autotopoff         7
    #define Skimmer            8

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

    // Custom classes
    SunLocation sun;
    Tide tide;

    // For Cloud and preset code
    int DaylightPWMValue=0;
    int ActinicPWMValue=0;
    int DaylightPWMValue0=0;        // For cloud code, channel 0
    int DaylightPWMValue2=0;        // For cloud code, chennel 2
    int ActinicPWMValue1=0;        // For cloud code, channel 0
    int ActinicPWMValue3=0;        // For cloud code, chennel 2

    // Needs to be global for DrawCustomGraph()
    int ScreenID=1;
    ////// Place global variable code above here

    // Setup on controller startup/reset
    void setup()
    {
      // This must be the first line
      ReefAngel.Init();  //Initialize controller
      ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items)); // Initialize Menu
     
      // Ports toggled in Feeding Mode
      ReefAngel.FeedingModePorts = Port1Bit | Port2Bit ;
      // Ports toggled in Water Change Mode
      ReefAngel.WaterChangePorts = Port1Bit | Port2Bit  | Port7Bit  | Port8Bit;
      // Ports toggled when Lights On / Off menu entry selected
      ReefAngel.LightsOnPorts = Port3Bit | Port4Bit | Port5Bit | Port6Bit;
      // Ports turned off when Overheat temperature exceeded
      ReefAngel.OverheatShutoffPorts = Port2Bit;
      // Use T1 probe as temperature and overheat functions
      ReefAngel.TempProbe = T1_PROBE;
      ReefAngel.OverheatProbe = T1_PROBE;
      
      // Feeeding and Water Change mode speed
      ReefAngel.DCPump.FeedingSpeed=0;
      ReefAngel.DCPump.WaterChangeSpeed=0;
      
       // Ports that are always on
    ReefAngel.Relay.On( Port1 ); // Return Pump
     
       
      ////// Place additional initialization code below here
    
       
      if (InternalMemory.read(Mem_B_ResetMemory))
        init_memory();
     ////// Place additional initialization code above here
    }

    void loop()
    {
    ReefAngel.Relay.DelayedOn( Port1 ); // Return Pump
    ReefAngel.SingleATO(true, Port7, InternalMemory.ATOExtendedTimeout_read(), 3); // ato cycle once every 3 hour
    ReefAngel.Relay.Set(Port8, ReefAngel.HighATO.IsActive()); // Skimmer no time out
    ReefAngel.DCPump.ExpansionChannel[4] = Sync; // Left Jebao RW4
    ReefAngel.DCPump.ExpansionChannel[5] = Sync; // jebao rw4
    ReefAngel.StandardHeater(Heater);
     
      ////// Place your custom code below here
     
      // Lighting and Flow
      SetSun();               // Setup Sun rise/set lighting
      AcclimateLED();         // Apply acclimation dimming
      LEDPresets();
      CheckCloud();           //  Check for cloud and lightning.
      UpdateLED();
     
     
      ////// Place your custom code above here

      // This should always be the last line
       ReefAngel.Portal( "troylong45" );
    ReefAngel.DDNS( "1" ); // Your DDNS is troylong45-1.myreefangel.com
    ReefAngel.ShowInterface();
    }

   

    void SetSun() {
      // Start acclimation routine
      int acclRiseOffset=InternalMemory.read(Mem_B_AcclRiseOffset)*60;
      int acclSetOffset=InternalMemory.read(Mem_B_AcclSetOffset)*60;
      byte acclDay=InternalMemory.read(Mem_B_AcclDay);
     
      // See if we are acclimating corals and decrease the countdown each day
      static boolean acclCounterReady=false;
      if (now()%SECS_PER_DAY!=0) acclCounterReady=true;
      if (now()%SECS_PER_DAY==0 && acclCounterReady && acclDay>0) {
        acclDay--;
        acclCounterReady=false;
        InternalMemory.write(Mem_B_AcclDay,acclDay);
      }
      // End acclimation

      // Add some customizable offsets
      sun.Init(InternalMemory.read_int(Mem_I_Latitude), InternalMemory.read_int(Mem_I_Longitude));
      int riseOffset=InternalMemory.read_int(Mem_I_RiseOffset);
      int setOffset=InternalMemory.read_int(Mem_I_SetOffset);
     
      sun.SetOffset(riseOffset,(acclDay*acclRiseOffset),setOffset,(-acclDay*acclSetOffset)); // Bahamas
      sun.CheckAndUpdate(); // Calculate today's Sunrise / Sunset

      byte lightOffset=InternalMemory.read(Mem_B_LightOffset); // left right separation
      byte actinicOffset=InternalMemory.ActinicOffset_read();
     
      // Make sure light resets to zero at night.
      for(int i=0;i<4;i++) { ReefAngel.PWM.SetChannel(i,0); }
     
      switch(InternalMemory.read(Mem_B_LightMode)) {   
        case 0: {
          // Daylights
          ReefAngel.PWM.Channel0PWMSlope(lightOffset,0);
          ReefAngel.PWM.Channel2PWMSlope(0,lightOffset);
          // Actinics
          ReefAngel.PWM.Channel1PWMSlope(actinicOffset+lightOffset,actinicOffset);
          ReefAngel.PWM.Channel3PWMSlope(actinicOffset,actinicOffset+lightOffset);
          break;
        }
        case 1: {
          // Daylights
          ReefAngel.PWM.Channel0PWMParabola(lightOffset,0);
          ReefAngel.PWM.Channel2PWMParabola(0,lightOffset);
          // Actinics
          ReefAngel.PWM.Channel1PWMParabola(actinicOffset+lightOffset,actinicOffset);
          ReefAngel.PWM.Channel3PWMParabola(actinicOffset,actinicOffset+lightOffset);
          break;
        }
      case 2: {
          // Daylights
          ReefAngel.PWM.Channel0PWMSmoothRamp(lightOffset,0);
          ReefAngel.PWM.Channel2PWMSmoothRamp(0,lightOffset);
          // Actinics
          ReefAngel.PWM.Channel1PWMSmoothRamp(actinicOffset+lightOffset,actinicOffset);
          ReefAngel.PWM.Channel3PWMSmoothRamp(actinicOffset,actinicOffset+lightOffset);
          break;
        }
      case 3: {
          // Daylights
          ReefAngel.PWM.Channel0PWMSigmoid(lightOffset,0);
          ReefAngel.PWM.Channel2PWMSigmoid(0,lightOffset);
          // Actinics
          ReefAngel.PWM.Channel1PWMSigmoid(actinicOffset+lightOffset,actinicOffset);
          ReefAngel.PWM.Channel3PWMSigmoid(actinicOffset,actinicOffset+lightOffset);
          break;
        }
      case 4: { // Reverse the actinics in the morning
          // Daylights
          ReefAngel.PWM.Channel0PWMParabola(lightOffset+actinicOffset,0);
          ReefAngel.PWM.Channel2PWMParabola(actinicOffset,lightOffset);
          // Actinics
          ReefAngel.PWM.Channel1PWMParabola(lightOffset,actinicOffset);
          ReefAngel.PWM.Channel3PWMParabola(0,actinicOffset+lightOffset);
          break;
        }
      }
    }


    void AcclimateLED() {
      byte acclDay=InternalMemory.read(Mem_B_AcclDay);
     
      if (acclDay > 0) {
        float acclActinicOffset=acclDay*(40.95*(((float)InternalMemory.read(Mem_B_AcclActinicOffset)/100)));
        float acclDaylightOffset=acclDay*(40.95*((float)InternalMemory.read(Mem_B_AcclDaylightOffset)/100));
        float endPerc;
     
        endPerc=40.95*InternalMemory.PWMSlopeEnd1_read();
        ReefAngel.PWM.SetChannelRaw(1,map(ReefAngel.PWM.GetChannelValueRaw(1),0,endPerc,0,endPerc-acclActinicOffset));
        endPerc=40.95*InternalMemory.PWMSlopeEnd3_read();
        ReefAngel.PWM.SetChannelRaw(3,map(ReefAngel.PWM.GetChannelValueRaw(3),0,endPerc,0,endPerc-acclActinicOffset));
        endPerc=40.95*InternalMemory.PWMSlopeEnd0_read();
        ReefAngel.PWM.SetChannelRaw(0,map(ReefAngel.PWM.GetChannelValueRaw(0),0,endPerc,0,endPerc-acclDaylightOffset));
        endPerc=40.95*InternalMemory.PWMSlopeEnd2_read();
        ReefAngel.PWM.SetChannelRaw(2,map(ReefAngel.PWM.GetChannelValueRaw(2),0,endPerc,0,endPerc-acclDaylightOffset));
      }
    }

    #define LED_1to1  Box1_Port1
    #define LED_2to1  Box1_Port2
    #define LED_3to1  Box1_Port3
    #define LED_4to1  Box1_Port4
    #define LED_BLUE  Box1_Port5
    #define LED_WHITE Box1_Port6
    #define LED_STORM Box1_Port8

    void resetRelayBox(byte ID) {
      // toggle all relays except for the one selected
      for (int i=Box1_Port1;i<=Box1_Port4;i++) {
        if (i!=ID) ReefAngel.Relay.Auto(i);
      }
    }

    void LEDPresets() {
      static byte lastPreset=0;
     
      DaylightPWMValue0=ReefAngel.PWM.GetChannelValueRaw(0);
      ActinicPWMValue1=ReefAngel.PWM.GetChannelValueRaw(1);
      DaylightPWMValue2=ReefAngel.PWM.GetChannelValueRaw(2);
      ActinicPWMValue3=ReefAngel.PWM.GetChannelValueRaw(3);
      DaylightPWMValue=ReefAngel.PWM.GetDaylightValueRaw();
      ActinicPWMValue=ReefAngel.PWM.GetActinicValueRaw();

      if (ReefAngel.Relay.isMaskOn(LED_1to1)) {
        if (lastPreset!=1) resetRelayBox(LED_1to1);
        DaylightPWMValue0=90*40.95;
        ActinicPWMValue1=10*40.95;
        DaylightPWMValue2=90*40.95;
        ActinicPWMValue3=10*40.95;
        lastPreset=1;
      }
     
      if (ReefAngel.Relay.isMaskOff(LED_1to1)) {
        if (lastPreset!=2) resetRelayBox(LED_1to1);
        DaylightPWMValue0=10*40.95;
        ActinicPWMValue1=90*40.95;
        DaylightPWMValue2=10*40.95;
        ActinicPWMValue3=90*40.95;
        lastPreset=2;
      }
     
      if (ReefAngel.Relay.isMaskOn(LED_2to1)) {
        if (lastPreset!=3) resetRelayBox(LED_2to1);
        DaylightPWMValue0=60*40.95;
        ActinicPWMValue1=40*40.95;
        DaylightPWMValue2=60*40.95;
        ActinicPWMValue3=40*40.95;
        lastPreset=3;
      }

      if (ReefAngel.Relay.isMaskOff(LED_2to1)) {
        if (lastPreset!=4) resetRelayBox(LED_2to1);
        DaylightPWMValue0=40*40.95;
        ActinicPWMValue1=60*40.95;
        DaylightPWMValue2=40*40.95;
        ActinicPWMValue3=60*40.95;
        lastPreset=4;
      }

      if (ReefAngel.Relay.isMaskOn(LED_3to1)) {
        if (lastPreset!=5) resetRelayBox(LED_3to1);
        DaylightPWMValue0=75*40.95;
        ActinicPWMValue1=25*40.95;
        DaylightPWMValue2=75*40.95;
        ActinicPWMValue3=25*40.95;
        lastPreset=5;
      }

      if (ReefAngel.Relay.isMaskOff(LED_3to1)) {
        if (lastPreset!=6) resetRelayBox(LED_3to1);
        DaylightPWMValue0=25*40.95;
        ActinicPWMValue1=75*40.95;
        DaylightPWMValue2=25*40.95;
        ActinicPWMValue3=75*40.95;
        lastPreset=6;
      }

      if (ReefAngel.Relay.isMaskOn(LED_4to1)) {
        if (lastPreset!=7) resetRelayBox(LED_4to1);
        DaylightPWMValue0=80*40.95;
        ActinicPWMValue1=20*40.95;
        DaylightPWMValue2=80*40.95;
        ActinicPWMValue3=20*40.95;
        lastPreset=7;
      }

      if (ReefAngel.Relay.isMaskOff(LED_4to1)) {
        if (lastPreset!=8) resetRelayBox(LED_4to1);
        DaylightPWMValue0=20*40.95;
        ActinicPWMValue1=80*40.95;
        DaylightPWMValue2=20*40.95;
        ActinicPWMValue3=80*40.95;
        lastPreset=8;
      }

      if (ReefAngel.Relay.isMaskOn(LED_BLUE)) {
        if (lastPreset!=9) resetRelayBox(LED_BLUE);
        DaylightPWMValue0=0;
        ActinicPWMValue1=80*40.95;
        DaylightPWMValue2=0;
        ActinicPWMValue3=80*40.95;
        lastPreset=9;
      }

      if (ReefAngel.Relay.isMaskOff(LED_BLUE)) {
        if (lastPreset!=10) resetRelayBox(LED_BLUE);
        ActinicPWMValue1=0;
        ActinicPWMValue3=0;
        lastPreset=10;
      }   
     
      if (ReefAngel.Relay.isMaskOn(LED_WHITE)) {
        if (lastPreset!=11) resetRelayBox(LED_WHITE);
        DaylightPWMValue0=80*40.95;
        ActinicPWMValue1=0;
        DaylightPWMValue2=80*40.95;
        ActinicPWMValue3=0;
        lastPreset=11;
      }

      if (ReefAngel.Relay.isMaskOff(LED_WHITE)) {
        if (lastPreset!=12) resetRelayBox(LED_WHITE);
        DaylightPWMValue0=0;
        DaylightPWMValue2=0;
        lastPreset=12;
      }  
      }

    // Write updated values to the channels
    void UpdateLED() {
      ReefAngel.PWM.SetChannelRaw(0,DaylightPWMValue0);
      ReefAngel.PWM.SetChannelRaw(1,ActinicPWMValue1); 
      ReefAngel.PWM.SetChannelRaw(2,DaylightPWMValue2);
      ReefAngel.PWM.SetChannelRaw(3,ActinicPWMValue3); 
      ReefAngel.PWM.SetDaylightRaw(DaylightPWMValue);   
      ReefAngel.PWM.SetActinicRaw(ActinicPWMValue); 

      byte LightsOffPerc=40.95*InternalMemory.read(Mem_B_LightsOffPerc);
     
      if (ReefAngel.PWM.GetChannelValueRaw(0)>=LightsOffPerc) ReefAngel.Relay.On(WhiteLeft); else ReefAngel.Relay.Off(WhiteLeft);
      if (ReefAngel.PWM.GetChannelValueRaw(1)>=LightsOffPerc) ReefAngel.Relay.On(BlueLeft); else ReefAngel.Relay.Off(BlueLeft);
      if (ReefAngel.PWM.GetChannelValueRaw(2)>=LightsOffPerc) ReefAngel.Relay.On(WhiteRight); else ReefAngel.Relay.Off(WhiteRight);
      if (ReefAngel.PWM.GetChannelValueRaw(3)>=LightsOffPerc) ReefAngel.Relay.On(BlueRight); else ReefAngel.Relay.Off(BlueRight);
    }


    // Menu Code
    void MenuEntry1() {
      ReefAngel.FeedingModeStart();
    }
    void MenuEntry2() {
      ReefAngel.WaterChangeModeStart();
    }
    void MenuEntry3() {
      ReefAngel.ATOClear();
      ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
    }
    void MenuEntry4() {
      ReefAngel.OverheatClear();
      ReefAngel.DisplayMenuEntry("Clear Overheat");
    }
    void MenuEntry5() {
      ReefAngel.SetupCalibratePH();
    }
    void MenuEntry6() {
      ReefAngel.SetupDateTime();
    }
    // Custom Main Screen
    void DrawCustomMain() {
      const int NumScreens=4;
      static boolean drawGraph=true;
     
      // Main Header
      // ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, 2,"Troy's Reef");
      ReefAngel.LCD.DrawDate(5,2);
      ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 128, 11);

      // Param Header
      DrawParams(5,14);
     
      switch (ScreenID) {
        case 0:
        {
          if (drawGraph) { ReefAngel.LCD.DrawGraph(5,40); drawGraph=false; }
          break;
        }
        case 1: { DrawStatus(5,40); break; }
        case 2: { DrawSunMoon(5,40); break; }


      }
     
      // Draw Relays
      DrawRelays(12,94);
     
      // Date+Time
      // ReefAngel.LCD.DrawDate(5,122);
     
      if (ReefAngel.Joystick.IsLeft()) {
        ReefAngel.ClearScreen(DefaultBGColor);
        ScreenID--; drawGraph=true;
      }
      if (ReefAngel.Joystick.IsRight()) {
        ReefAngel.ClearScreen(DefaultBGColor);
        ScreenID++; drawGraph=true;
      }
      if (ScreenID<0) ScreenID=NumScreens-1;
      if (ScreenID>=NumScreens) ScreenID=0;
     
    }

    void DrawCustomGraph() {
      if (ScreenID==0)
        ReefAngel.LCD.DrawGraph(5, 40);
    }

    void DrawParams(int x, int y) {
      char buf[16];

      ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
      ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
      // Temp and PH
      y+=2;
      ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
      ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
      y+=6;
      ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
      ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
      ConvertNumToString(buf, ReefAngel.Params.PH, 100);
      ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
      y+=5;
      ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
      ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
    }

    void DrawStatus(int x, int y) {
      int t=x;
     
      ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,15,y,"High",Font8x16);
      ReefAngel.LCD.DrawLargeText(COLOR_INDIGO,DefaultBGColor,85,y,"Low",Font8x16);
     
      if (ReefAngel.HighATO.IsActive()) {
        ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_GREEN);
      } else {
        ReefAngel.LCD.FillCircle(55,y+3,5,COLOR_RED);
      }
     
      if (ReefAngel.LowATO.IsActive()) {
        ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_GREEN);
      } else {
        ReefAngel.LCD.FillCircle(70,y+3,5,COLOR_RED);
      }
      y+=12;
     
     
      // Display Acclimation timer
      byte acclDay=InternalMemory.read(Mem_B_AcclDay);
     
      if (acclDay > 0) {
        ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"Acclimation Day:"); x+=100;
        ReefAngel.LCD.DrawSingleMonitor(acclDay,DefaultFGColor,x,y,1);
      } else {
        ReefAngel.LCD.Clear(DefaultBGColor,x,y,128,y+8);
      }
    }

    void DrawSunMoon(int x, int y) {
      char buf[16];
      int t=x;

      y+=2;
      /// Display Sunrise / Sunset
      sprintf(buf, "%02d:%02d", sun.GetRiseHour(), sun.GetRiseMinute());
      ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Rise:"); x+=31;
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
      sprintf(buf, "%02d:%02d", sun.GetSetHour(), sun.GetSetMinute()); x+=36;
      ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Set:"); x+=25;
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,x,y,buf);
      y+=15; x=t;
     
    }

    void DrawRelays(int x, int y) {
      // Draw Relays
      byte TempRelay = ReefAngel.Relay.RelayData;
      TempRelay &= ReefAngel.Relay.RelayMaskOff;
      TempRelay |= ReefAngel.Relay.RelayMaskOn;
      ReefAngel.LCD.DrawOutletBox(x, y, TempRelay);
    }

    void DelayedOnModes(byte relay) {
      static unsigned long startTime=now();

      if ( (startTime==LastStart) && ReefAngel.HighATO.IsActive()) {
        ReefAngel.Relay.On(relay);
      } else {
        ReefAngel.Relay.DelayedOn(relay);
      }
    }

     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 50

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

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

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

          // Only start the cloud effect after this setting
          // In this example, start cloud after noon
        #define Start_Cloud_After NumMins(9,00)

          // Always end the cloud effect before this setting
          // In this example, end cloud before 9:00pm
        #define End_Cloud_Before NumMins(18,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 50

          // 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[] = { Slow };

          // 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 time_t DelayCounter=millis();    // Variable for lightning timing.
          static int DelayTime=random(1000);      // Variable for lightning timimg.

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

        void Strike()
        {
          int a=random(1,5);    // Pick a number of consecutive flashes from 1 to 4.
          for (int i=0; i<a; i++)
          {
            // Flash on
            int newdata=4095;
            Wire.beginTransmission(0x40);      // Address of the dimming expansion module
            Wire.write(0x8+(4*0));             // 0x8 is channel 0, 0x12 is channel 1, etc.  I'm using channel 1.
            Wire.write(newdata&0xff);          // Send the data 8 bits at a time.  This sends the LSB
            Wire.write(newdata>>8);            // This sends the MSB
            Wire.endTransmission();
            
            Wire.beginTransmission(0x40);      // Address of the dimming expansion module
            Wire.write(0x8+(4*2));             // 0x8 is channel 0, 0x12 is channel 1, etc.  I'm using channel 1.
            Wire.write(newdata&0xff);          // Send the data 8 bits at a time.  This sends the LSB
            Wire.write(newdata>>8);            // This sends the MSB
            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
           
            // Flash off.  Return to baseline.
            newdata=ReefAngel.PWM.GetChannelValueRaw(0);   // Use the channel number you're flashing here
            Wire.beginTransmission(0x40);    // Same as above
            Wire.write(0x8+(4*0));
            Wire.write(newdata&0xff);
            Wire.write(newdata>>8);
            Wire.endTransmission();
            
            newdata=ReefAngel.PWM.GetChannelValueRaw(0);   // Use the channel number you're flashing here
            Wire.beginTransmission(0x40);    // Same as above
            Wire.write(0x8+(4*2));
            Wire.write(newdata&0xff);
            Wire.write(newdata>>8);
            Wire.endTransmission();
           
            delay(random(30,50));                // Wait from 30 to 49 ms
            wdt_reset();    // Reset watchdog timer to avoid re-boots
          }
        }

        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 (int) PWMStart;
        }
Image
Post Reply