Need help in cleanup and a couple cloud question...

Do you have a question on how to do something.
Ask in here.
Post Reply
sceia
Posts: 30
Joined: Mon Nov 14, 2011 8:06 pm

Need help in cleanup and a couple cloud question...

Post by sceia »

I have a few functions commented out in the feed coral, ph cal, and custom display functions to get it to compile... I need help getting it smaller. Here's my code...

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 <ReefAngel.h>
#include <Memory.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>

#define AutoTopOff          1 
#define Refuge              2 
#define MHLight             3 
#define Actinic             4 
#define WaveController      5 
#define Heater              6 
#define Fans                7 
#define Pumps               8 

#define NW1        0
#define NW2        1
#define NW3        2
#define Blue       3
#define Green      4
#define Red        5
// RB      Actinic
// Violet     Daylight

static bool fOverride = false;
static bool rOverride = false;

boolean ForceCloud=false;
boolean ForceLightning=false;
byte PWMChannel[]={
  0,0,0,0,0,0};

#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 

#include <avr/pgmspace.h>
// Create the menu entries
prog_char menu1_label[] PROGMEM = "Feed Fish";
prog_char menu2_label[] PROGMEM = "Feed Coral";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "PH Calibration";
prog_char menu5_label[] PROGMEM = "Actinic LEDs";
prog_char menu6_label[] PROGMEM = "100% Daylight LEDs";
prog_char menu7_label[] PROGMEM = "Refuge LEDs";
prog_char menu8_label[] PROGMEM = "Force Clouds";
prog_char menu9_label[] PROGMEM = "Storm Mode";


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

void TogglefOverride()
{
    if ( fOverride )
      fOverride = false;
    else
      fOverride = true;
}

void TogglerOverride()
{
    if ( rOverride )
      rOverride = false;
    else
      rOverride = true;
}
  

void MenuEntry1() //"Feed Fish"
{
  ReefAngel.FeedingModeStart();
}

void MenuEntry2() //"Feed Coral"
{
    /*                 // clear the screen
                 ReefAngel.ClearScreen(DefaultBGColor);
                 ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, MENU_START_COL, MENU_START_ROW, "Feeding Coral");
                 // Turn off the return port first
                 ReefAngel.Relay.Off(Port8);
                 ReefAngel.Relay.Write();
                 char msg[8];
                 bool done = false;
                 int t;
                 byte row;
                 // Have 2 loops / passes
                 // The first pass will just wait 5 minutes (or whatever value is stored in the feeding timer)
                 // The second pass will turn off the wavemaker port and wait 5 minutes
                 // Then the loop will finish and we turn the ports back on
                 for ( byte i = 0; i < 2; i++ )
                 {
                    sprintf(msg, "Phase %d", i+1);
                    // leave a blank row and start on the 3rd line
                    row = MENU_START_ROW*(i+3);
                    ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, MENU_START_COL, row, msg);
                    if ( i == 1 )
                    {
                       // second time through, we enter phase 2, so shutoff the wavemaker now
                       ReefAngel.Relay.Off(Port5);
                       ReefAngel.Relay.Write();
                    }
                    // start the feeding timer
                    ReefAngel.Timer[FEEDING_TIMER].Start();
                    do
                    {
                       // just loop until the joystick button is pressed or the timer is triggered
                       // then break out of the loop and proceed
            #if defined WDT || defined WDT_FORCE
                     wdt_reset();
            #endif  // defined WDT || defined WDT_FORCE
                       // update the timer on the screen
                       t = ReefAngel.Timer[FEEDING_TIMER].Trigger - now();
                       if ( (t >= 0) && ! ReefAngel.Timer[FEEDING_TIMER].IsTriggered() )
                       {
                          ReefAngel.LCD.Clear(DefaultBGColor,60+(intlength(t)*5),row,100,row+8);
                          ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,60,row,t);
                          delay(200);  // to keep from redraw flicker on timer
                       }
                       else
                       {
                          done = true;
                       }
                       if ( ReefAngel.Joystick.IsButtonPressed() )
                       {
                          // joystick button pressed, so we stop
                          done = true;
                       }
                    } while ( ! done );
            #if defined WDT || defined WDT_FORCE
                     wdt_reset();
            #endif  // defined WDT || defined WDT_FORCE
                    // reset the variable for the inner do while loop
                    done = false;
                 }
                 // Restore ports & return to the main screen
                 ReefAngel.Relay.On(Port8);
                 ReefAngel.Relay.On(Port5);
                 ReefAngel.Relay.Write();
                 ReefAngel.ExitMenu();
*/
  
}

void MenuEntry3()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO");
}

void MenuEntry4() 
{
 // ReefAngel.SetupCalibratePH();
 // ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry5() //"Actinic LEDs"
{
  TogglefOverride();
    // ... set the channels on
  ReefAngel.PWM.SetChannel(NW1,15);
  ReefAngel.PWM.SetChannel(NW2,15);
  ReefAngel.PWM.SetChannel(NW3,15);
  ReefAngel.PWM.SetChannel(Blue,15);
  ReefAngel.PWM.SetChannel(Green,15);
  ReefAngel.PWM.SetChannel(Red,15);
  ReefAngel.PWM.SetActinic(100);
  ReefAngel.PWM.SetDaylight(15);
}

void MenuEntry6() //"100% Daylight LEDs"
{
  TogglefOverride();
  ReefAngel.PWM.SetChannel(NW1,90);
  ReefAngel.PWM.SetChannel(NW2,90);
  ReefAngel.PWM.SetChannel(NW3,90);
  ReefAngel.PWM.SetChannel(Blue,90);
  ReefAngel.PWM.SetChannel(Green,90);
  ReefAngel.PWM.SetChannel(Red,90);
  ReefAngel.PWM.SetActinic(90);
  ReefAngel.PWM.SetDaylight(90);
}

void MenuEntry7() //"Refuge LEDs"
{
  TogglefOverride();
}

void MenuEntry8() //"Force Clouds
{
  ForceCloud=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}

void MenuEntry9() //"Storm
{
  ForceLightning=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;

}

byte x,y;
char text[10];

void DrawCustomMain()
{
byte x = 6;
byte y = 2;
byte t;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 6, 3, "Scott's Reef");
ReefAngel.LCD.DrawDate(6, 120);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 14, 131, 14);
//pingSerial();
x = 12;
y += MENU_START_ROW+1;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+6, "Temp:        pH:");
//char text[7];
//ConvertNumToString(text, ReefAngel.Params.PH, 100);
//ReefAngel.LCD.Clear(DefaultBGColor, x+16, y+65, x+65, y+16);
//ReefAngel.LCD.DrawText(PHColor, DefaultBGColor, x+75, y+18, text);
//pingSerial();

//ConvertNumToString(text, ReefAngel.Params.Temp1, 10);
y += MENU_START_ROW*2;
x = 10;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(16*4),y+16);
//pingSerial();
ReefAngel.LCD.DrawText(T1TempColor, DefaultBGColor, x, y, text);
//pingSerial(); 

/* //Start Moon Phase Display
x += (16*4) + 8;
y += MENU_START_ROW+3;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 12, 55, "Moon Phase:");
if (ReefAngel.PWM.GetActinicValue() ==0) ReefAngel.LCD.DrawText(20,255,81,55,"Off");
else if(ReefAngel.PWM.GetActinicValue() >=1 && ReefAngel.PWM.GetActinicValue() < 20) ReefAngel.LCD.DrawText(20,255,81,55,"New");
else if(ReefAngel.PWM.GetActinicValue() >=20 && ReefAngel.PWM.GetActinicValue() < 35) ReefAngel.LCD.DrawText(20,255,81,55,"Cresent");
else if(ReefAngel.PWM.GetActinicValue() >=35 && ReefAngel.PWM.GetActinicValue() < 45) ReefAngel.LCD.DrawText(20,255,81,55,"Half");
else if(ReefAngel.PWM.GetActinicValue() >=45 && ReefAngel.PWM.GetActinicValue() < 60) ReefAngel.LCD.DrawText(20,255,81,55,"Gibbous");
else if(ReefAngel.PWM.GetActinicValue() >=60 && ReefAngel.PWM.GetActinicValue() < 80) ReefAngel.LCD.DrawText(20,255,81,55,"Full");
//End Display Moon Phase Display*/



byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 102, TempRelay);
}

void DrawCustomGraph()
{
}

void setup() 
{ 
  //Init and custom menus
  ReefAngel.Init();  //Initialize controller

/*
AutoTopOff          1 
Refuge              2 
MHLight             3 
Actinic             4 
WaveController      5 
Heater              6 
Fans                7 
Pumps               8 
*/
    //Menu Modes 
    ReefAngel.FeedingModePorts = B10010000;
    ReefAngel.WaterChangePorts = B10010000;
  
  //Pumps and waves on
  ReefAngel.Relay.On(Pumps);  //Turn Sump/Skimmer on at startup
  ReefAngel.Relay.On(WaveController);  //Turn WaveController on at startup
}

void loop() 
{ 
  
  
  ReefAngel.LCD.SetContrast(60);  // Set contrast to 60
  ReefAngel.Relay.On(WaveController);
    

  
  //Top Off
  ReefAngel.StandardATO(AutoTopOff,500);  //Setup AutoTopOff as Auto Top-Off function with 500s timeout
  
  //Light Schedule  
  
 if ( ! fOverride )
  {
            if (hour()>=21 || hour()<10)
            {
              ReefAngel.PWM.SetActinic(MoonPhase());
            }
            else
            {
                  ReefAngel.PWM.SetChannel(NW1,PWMParabola(14,0,19,0,15,80,15));
                  ReefAngel.PWM.SetChannel(NW2,PWMParabola(14,30,19,30,15,90,15));
                  ReefAngel.PWM.SetChannel(NW3,PWMParabola(15,0,20,0,15,80,15));
                  ReefAngel.PWM.SetChannel(Blue,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Green,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Red,PWMParabola(14,0,20,0,15,85,15));
                  ReefAngel.PWM.SetActinic(PWMParabola(12,00,22,0,15,90,15));
                  ReefAngel.PWM.SetDaylight(PWMParabola(13,30,20,30,15,95,15));
            }
  }
  
  if ( ! rOverride )
        ReefAngel.Relay.On(Refuge);
  else
        ReefAngel.Relay.Off(Refuge);
  
   CheckCloud();
  
  
  //Heating/Cooling
  ReefAngel.StandardHeater(Heater,778,782);  // Setup Heater to turn on at 77.8F and off at 78.2F 
  ReefAngel.StandardFan(Fans,780,790);  // Setup fans
  
  //Dosing
    ReefAngel.DosingPumpRepeat(Port1,0,60,4); // Dose for 4 seconds every 60 minutes with 0 minutes offset
    ReefAngel.DosingPumpRepeat(Port2,5,60,4); // Dose for 4 seconds every 60 minutes with 5 minutes offset
  
  ReefAngel.ShowInterface();
  
  ReefAngel.Relay.Write();  // Make relay changes effective 
}


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

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

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(11,30)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(18,30)

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

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

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

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


    // 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;
  // 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
    {
      //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 (ForceCloud)
  {
  ForceCloud=false;
  cloudchance=1;
  cloudduration=10;
//  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (ForceLightning)
  {
  ForceLightning=false;
  cloudchance=1;
  cloudduration=10;
  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          PWMChannel[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) PWMChannel[b]=100; 
            else PWMChannel[b]=0;
            //delay(10);
          }
          else
          {
            PWMChannel[b]=20;
          }
        }
      }
    }
    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;
      }
    }
  }
  
  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;
}
Here's my other question....

In the following snippet:

Code: Select all

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

  // 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
#define Daylight_Channels B000111
I have my actinics on the standard actinic channel (not the pwm expansion)... I'm using all 8 channels... How do I code the define statement for the Actinic Channel?

Scott.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: Need help in cleanup and a couple cloud question...

Post by rimai »

Wow!!

Amazing code!!
What features do you have enabled?
For the CheckCloud() function, there are 2 versions. One for PWM expansion module and one for standard PWM channels.
If you want to use both, you will need to merge them.
http://forum.reefangel.com/viewtopic.php?f=14&t=288
http://forum.reefangel.com/viewtopic.php?f=12&t=170
But, basically you need to change only this section:

Code: Select all

    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          PWMChannel[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) PWMChannel[b]=100; 
            else PWMChannel[b]=0;
            //delay(10);
          }
          else
          {
            PWMChannel[b]=20;
          }
        }
      }
    }
All you need is to add the standard actinic channel handling in there.
Roberto.
sceia
Posts: 30
Joined: Mon Nov 14, 2011 8:06 pm

Re: Need help in cleanup and a couple cloud question...

Post by sceia »

Yea, it's pretty amazing but pretty large...lol. I am trying to get dosing pumps, random and forced clouds and storm, ph cal, feeding (fish and coral).... ... ... 32k is killing me...lol. I'll see if I can merge the 2 clouds together but I'm already limited by size... Any thoughts?!?!

Scott.
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: Need help in cleanup and a couple cloud question...

Post by rimai »

Seems like you need one of these:
http://forum.reefangel.com/viewtopic.php?f=2&t=778
256K is plenty.
The other alternative is to remove the ph cal to get a little more. I can't remember the last time I calibrated a probe :oops:
Roberto.
sceia
Posts: 30
Joined: Mon Nov 14, 2011 8:06 pm

Re: Need help in cleanup and a couple cloud question...

Post by sceia »

Ok, so I cleaned up a bit... I'm not sure if the changed feed fish and feed corals will work. Can you check them? I removed the drawdate from the custom screen...seems like it was taking up some memory there. I'm unsure of how I should merge the 2 cloud functions...can you assist?

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 <ReefAngel.h>
#include <Memory.h>

#define AutoTopOff          1 
#define Refuge              2 
#define MHLight             3 
#define Actinic             4 
#define WaveController      5 
#define Heater              6 
#define Fans                7 
#define Pumps               8 

#define NW1        0
#define NW2        1
#define NW3        2
#define Blue       3
#define Green      4
#define Red        5
// RB      Actinic
// Violet     Daylight

static bool fOverride = false;
static bool rOverride = false;

boolean ForceCloud=false;
boolean ForceLightning=false;
byte PWMChannel[]={
  0,0,0,0,0,0};

#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 

#include <avr/pgmspace.h>
// Create the menu entries
prog_char menu1_label[] PROGMEM = "Feed Fish";
prog_char menu2_label[] PROGMEM = "Feed Coral";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "Actinic LEDs";
prog_char menu5_label[] PROGMEM = "100% Daylight LEDs";
prog_char menu6_label[] PROGMEM = "Refuge LEDs";
prog_char menu7_label[] PROGMEM = "Force Clouds";
prog_char menu8_label[] PROGMEM = "Storm Mode";


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

void TogglefOverride()
{
    if ( fOverride )
      fOverride = false;
    else
      fOverride = true;
}

void TogglerOverride()
{
    if ( rOverride )
      rOverride = false;
    else
      rOverride = true;
}
  

void MenuEntry1() //"Feed Fish"
{
  ReefAngel.FeedingModePorts = B10010000;
  ReefAngel.FeedingModeStart();
}

void MenuEntry2() //"Feed Coral"
{
  ReefAngel.FeedingModePorts = B10000000;
  ReefAngel.FeedingModeStart();
}

void MenuEntry3()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO");
}

void MenuEntry4() //"Actinic LEDs"
{
  TogglefOverride();
    // ... set the channels on
  ReefAngel.PWM.SetChannel(NW1,15);
  ReefAngel.PWM.SetChannel(NW2,15);
  ReefAngel.PWM.SetChannel(NW3,15);
  ReefAngel.PWM.SetChannel(Blue,15);
  ReefAngel.PWM.SetChannel(Green,15);
  ReefAngel.PWM.SetChannel(Red,15);
  ReefAngel.PWM.SetActinic(100);
  ReefAngel.PWM.SetDaylight(15);
}

void MenuEntry5() //"100% Daylight LEDs"
{
  TogglefOverride();
  ReefAngel.PWM.SetChannel(NW1,90);
  ReefAngel.PWM.SetChannel(NW2,90);
  ReefAngel.PWM.SetChannel(NW3,90);
  ReefAngel.PWM.SetChannel(Blue,90);
  ReefAngel.PWM.SetChannel(Green,90);
  ReefAngel.PWM.SetChannel(Red,90);
  ReefAngel.PWM.SetActinic(90);
  ReefAngel.PWM.SetDaylight(90);
}

void MenuEntry6() //"Refuge LEDs"
{
  TogglefOverride();
}

void MenuEntry7() //"Force Clouds
{
  ForceCloud=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}

void MenuEntry8() //"Storm
{
  ForceLightning=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;

}

byte x,y;
char text[10];

void DrawCustomMain()
{
byte x = 6;
byte y = 2;
byte t;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 6, 3, "Scott's Reef");
//ReefAngel.LCD.DrawDate(6, 120);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 14, 131, 14);
//pingSerial();
x = 12;
y += MENU_START_ROW+1;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+6, "Temp:        pH:");
//char text[7];
//ConvertNumToString(text, ReefAngel.Params.PH, 100);
//ReefAngel.LCD.Clear(DefaultBGColor, x+16, y+65, x+65, y+16);
//ReefAngel.LCD.DrawText(PHColor, DefaultBGColor, x+75, y+18, text);
//pingSerial();

//ConvertNumToString(text, ReefAngel.Params.Temp1, 10);
y += MENU_START_ROW*2;
x = 10;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(16*4),y+16);
//pingSerial();
ReefAngel.LCD.DrawText(T1TempColor, DefaultBGColor, x, y, text);
//pingSerial(); 

 //Start Moon Phase Display
x += (16*4) + 8;
y += MENU_START_ROW+3;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 12, 55, "Moon Phase:");
if (ReefAngel.PWM.GetActinicValue() ==0) ReefAngel.LCD.DrawText(20,255,81,55,"Off");
else if(ReefAngel.PWM.GetActinicValue() >=1 && ReefAngel.PWM.GetActinicValue() < 20) ReefAngel.LCD.DrawText(20,255,81,55,"New");
else if(ReefAngel.PWM.GetActinicValue() < 35) ReefAngel.LCD.DrawText(20,255,81,55,"Cresent");
else if(ReefAngel.PWM.GetActinicValue() < 45) ReefAngel.LCD.DrawText(20,255,81,55,"Half");
else if(ReefAngel.PWM.GetActinicValue() < 60) ReefAngel.LCD.DrawText(20,255,81,55,"Gibbous");
else ReefAngel.LCD.DrawText(20,255,81,55,"Full");
//End Display Moon Phase Display



byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 102, TempRelay);
}

void DrawCustomGraph()
{
}

void setup() 
{ 
  //Init and custom menus
  ReefAngel.Init();  //Initialize controller

/*
AutoTopOff          1 
Refuge              2 
MHLight             3 
Actinic             4 
WaveController      5 
Heater              6 
Fans                7 
Pumps               8 
*/
    //Menu Modes 

    ReefAngel.WaterChangePorts = B10010000;
  
  //Pumps and waves on
  ReefAngel.Relay.On(Pumps);  //Turn Sump/Skimmer on at startup
  ReefAngel.Relay.On(WaveController);  //Turn WaveController on at startup
}

void loop() 
{ 
  
  
  ReefAngel.LCD.SetContrast(60);  // Set contrast to 60
  ReefAngel.Relay.On(WaveController);
    

  
  //Top Off
  ReefAngel.StandardATO(AutoTopOff,500);  //Setup AutoTopOff as Auto Top-Off function with 500s timeout
  
  //Light Schedule  
  
 if ( ! fOverride )
  {
            if (hour()>=21 || hour()<10)
            {
              ReefAngel.PWM.SetActinic(MoonPhase());
            }
            else
            {
                  ReefAngel.PWM.SetChannel(NW1,PWMParabola(14,0,19,0,15,80,15));
                  ReefAngel.PWM.SetChannel(NW2,PWMParabola(14,30,19,30,15,90,15));
                  ReefAngel.PWM.SetChannel(NW3,PWMParabola(15,0,20,0,15,80,15));
                  ReefAngel.PWM.SetChannel(Blue,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Green,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Red,PWMParabola(14,0,20,0,15,85,15));
                  ReefAngel.PWM.SetActinic(PWMParabola(12,00,22,0,15,90,15));
                  ReefAngel.PWM.SetDaylight(PWMParabola(13,30,20,30,15,95,15));
            }
  }
  
  if ( ! rOverride )
        ReefAngel.Relay.On(Refuge);
  else
        ReefAngel.Relay.Off(Refuge);
  
   CheckCloud();
  
  
  //Heating/Cooling
  ReefAngel.StandardHeater(Heater,778,782);  // Setup Heater to turn on at 77.8F and off at 78.2F 
  ReefAngel.StandardFan(Fans,780,790);  // Setup fans
  
  //Dosing
    ReefAngel.DosingPumpRepeat(Port1,0,60,4); // Dose for 4 seconds every 60 minutes with 0 minutes offset
    ReefAngel.DosingPumpRepeat(Port2,5,60,4); // Dose for 4 seconds every 60 minutes with 5 minutes offset
  
  ReefAngel.ShowInterface();
  
  ReefAngel.Relay.Write();  // Make relay changes effective 
}


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

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

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(11,30)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(18,30)

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

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

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

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


    // 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;
  // 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
    {
      //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 (ForceCloud)
  {
  ForceCloud=false;
  cloudchance=1;
  cloudduration=10;
//  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (ForceLightning)
  {
  ForceLightning=false;
  cloudchance=1;
  cloudduration=10;
  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          PWMChannel[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) PWMChannel[b]=100; 
            else PWMChannel[b]=0;
            //delay(10);
          }
          else
          {
            PWMChannel[b]=20;
          }
        }
      }
    }
    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;
      }
    }
  }
  
  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;
}
sceia
Posts: 30
Joined: Mon Nov 14, 2011 8:06 pm

Re: Need help in cleanup and a couple cloud question...

Post by sceia »

I think this is the only place I would need to set the actinic values from the onboard pwm...

Code: Select all

 // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          PWMChannel[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180);
          ReefAngel.PWM.SetActinic(ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180));
        }
      }
rimai
Posts: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: Need help in cleanup and a couple cloud question...

Post by rimai »

Yeah, but it would be best if you created a new variable to store the values.
Declare it above setup():

Code: Select all

byte ActinicPWMValue=0;
Then on loop():

Code: Select all

...
...
ActinicPWMValue=PWMParabola(12,00,22,0,15,90,15);
...
...
CheckCloud();
ReefAngel.PWM.SetActinic(ActinicPWMValue);
And on the CheckCloud() function:

Code: Select all

ActinicPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ActinicPWMValue,0,180);
Roberto.
sceia
Posts: 30
Joined: Mon Nov 14, 2011 8:06 pm

Re: Need help in cleanup and a couple cloud question...

Post by sceia »

So like this?

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 <ReefAngel.h>
#include <Memory.h>

#define AutoTopOff          1 
#define Refuge              2 
#define MHLight             3 
#define Actinic             4 
#define WaveController      5 
#define Heater              6 
#define Fans                7 
#define Pumps               8 

#define NW1        0
#define NW2        1
#define NW3        2
#define Blue       3
#define Green      4
#define Red        5
// RB      Actinic
// Violet     Daylight

static bool fOverride = false;
static bool rOverride = false;

byte ActinicPWMValue=0;

boolean ForceCloud=false;
boolean ForceLightning=false;
byte PWMChannel[]={
  0,0,0,0,0,0};

#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES 

#include <avr/pgmspace.h>
// Create the menu entries
prog_char menu1_label[] PROGMEM = "Feed Fish";
prog_char menu2_label[] PROGMEM = "Feed Coral";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "Actinic LEDs";
prog_char menu5_label[] PROGMEM = "100% Daylight LEDs";
prog_char menu6_label[] PROGMEM = "Refuge LEDs";
prog_char menu7_label[] PROGMEM = "Force Clouds";
prog_char menu8_label[] PROGMEM = "Storm Mode";


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

void TogglefOverride()
{
    if ( fOverride )
      fOverride = false;
    else
      fOverride = true;
}

void TogglerOverride()
{
    if ( rOverride )
      rOverride = false;
    else
      rOverride = true;
}
  

void MenuEntry1() //"Feed Fish"
{
  ReefAngel.FeedingModePorts = B10010000;
  ReefAngel.FeedingModeStart();
}

void MenuEntry2() //"Feed Coral"
{
  ReefAngel.FeedingModePorts = B10000000;
  ReefAngel.FeedingModeStart();
}

void MenuEntry3()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO");
}

void MenuEntry4() //"Actinic LEDs"
{
  TogglefOverride();
    // ... set the channels on
  ReefAngel.PWM.SetChannel(NW1,15);
  ReefAngel.PWM.SetChannel(NW2,15);
  ReefAngel.PWM.SetChannel(NW3,15);
  ReefAngel.PWM.SetChannel(Blue,15);
  ReefAngel.PWM.SetChannel(Green,15);
  ReefAngel.PWM.SetChannel(Red,15);
  ReefAngel.PWM.SetActinic(100);
  ReefAngel.PWM.SetDaylight(15);
}

void MenuEntry5() //"100% Daylight LEDs"
{
  TogglefOverride();
  ReefAngel.PWM.SetChannel(NW1,90);
  ReefAngel.PWM.SetChannel(NW2,90);
  ReefAngel.PWM.SetChannel(NW3,90);
  ReefAngel.PWM.SetChannel(Blue,90);
  ReefAngel.PWM.SetChannel(Green,90);
  ReefAngel.PWM.SetChannel(Red,90);
  ReefAngel.PWM.SetActinic(90);
  ReefAngel.PWM.SetDaylight(90);
}

void MenuEntry6() //"Refuge LEDs"
{
  TogglefOverride();
}

void MenuEntry7() //"Force Clouds
{
  ForceCloud=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}

void MenuEntry8() //"Storm
{
  ForceLightning=true;
  ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;

}

byte x,y;
char text[10];

void DrawCustomMain()
{
byte x = 6;
byte y = 2;
byte t;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 6, 3, "Scott's Reef");
//ReefAngel.LCD.DrawDate(6, 120);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 14, 131, 14);
//pingSerial();
x = 12;
y += MENU_START_ROW+1;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+6, "Temp:        pH:");
//char text[7];
//ConvertNumToString(text, ReefAngel.Params.PH, 100);
//ReefAngel.LCD.Clear(DefaultBGColor, x+16, y+65, x+65, y+16);
//ReefAngel.LCD.DrawText(PHColor, DefaultBGColor, x+75, y+18, text);
//pingSerial();

//ConvertNumToString(text, ReefAngel.Params.Temp1, 10);
y += MENU_START_ROW*2;
x = 10;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(16*4),y+16);
//pingSerial();
ReefAngel.LCD.DrawText(T1TempColor, DefaultBGColor, x, y, text);
//pingSerial(); 

 //Start Moon Phase Display
x += (16*4) + 8;
y += MENU_START_ROW+3;
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 12, 55, "Moon Phase:");
if (ReefAngel.PWM.GetActinicValue() ==0) ReefAngel.LCD.DrawText(20,255,81,55,"Off");
else if(ReefAngel.PWM.GetActinicValue() >=1 && ReefAngel.PWM.GetActinicValue() < 20) ReefAngel.LCD.DrawText(20,255,81,55,"New");
else if(ReefAngel.PWM.GetActinicValue() < 35) ReefAngel.LCD.DrawText(20,255,81,55,"Cresent");
else if(ReefAngel.PWM.GetActinicValue() < 45) ReefAngel.LCD.DrawText(20,255,81,55,"Half");
else if(ReefAngel.PWM.GetActinicValue() < 60) ReefAngel.LCD.DrawText(20,255,81,55,"Gibbous");
else ReefAngel.LCD.DrawText(20,255,81,55,"Full");
//End Display Moon Phase Display



byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 102, TempRelay);
}

void DrawCustomGraph()
{
}

void setup() 
{ 
  //Init and custom menus
  ReefAngel.Init();  //Initialize controller

/*
AutoTopOff          1 
Refuge              2 
MHLight             3 
Actinic             4 
WaveController      5 
Heater              6 
Fans                7 
Pumps               8 
*/
    //Menu Modes 

    ReefAngel.WaterChangePorts = B10010000;
  
  //Pumps and waves on
  ReefAngel.Relay.On(Pumps);  //Turn Sump/Skimmer on at startup
  ReefAngel.Relay.On(WaveController);  //Turn WaveController on at startup
}

void loop() 
{ 
  
  
  ReefAngel.LCD.SetContrast(60);  // Set contrast to 60
  ReefAngel.Relay.On(WaveController);
    

  
  //Top Off
  ReefAngel.StandardATO(AutoTopOff,500);  //Setup AutoTopOff as Auto Top-Off function with 500s timeout
  
  //Light Schedule  
 ActinicPWMValue=PWMParabola(12,00,22,0,15,90,15);
 if ( ! fOverride )
  {
            if (hour()>=21 || hour()<10)
            {
              ReefAngel.PWM.SetActinic(MoonPhase());
            }
            else
            {
                  ReefAngel.PWM.SetChannel(NW1,PWMParabola(14,0,19,0,15,80,15));
                  ReefAngel.PWM.SetChannel(NW2,PWMParabola(14,30,19,30,15,90,15));
                  ReefAngel.PWM.SetChannel(NW3,PWMParabola(15,0,20,0,15,80,15));
                  ReefAngel.PWM.SetChannel(Blue,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Green,PWMParabola(13,30,20,30,15,85,15));
                  ReefAngel.PWM.SetChannel(Red,PWMParabola(14,0,20,0,15,85,15));
                  ReefAngel.PWM.SetActinic(ActinicPWMValue);
                  ReefAngel.PWM.SetDaylight(PWMParabola(13,30,20,30,15,95,15));
            }
  }
  
  if ( ! rOverride )
        ReefAngel.Relay.On(Refuge);
  else
        ReefAngel.Relay.Off(Refuge);
  
   CheckCloud();
   ReefAngel.PWM.SetActinic(ActinicPWMValue);

  
  //Heating/Cooling
  ReefAngel.StandardHeater(Heater,778,782);  // Setup Heater to turn on at 77.8F and off at 78.2F 
  ReefAngel.StandardFan(Fans,780,790);  // Setup fans
  
  //Dosing
    ReefAngel.DosingPumpRepeat(Port1,0,60,4); // Dose for 4 seconds every 60 minutes with 0 minutes offset
    ReefAngel.DosingPumpRepeat(Port2,5,60,4); // Dose for 4 seconds every 60 minutes with 5 minutes offset
  
  ReefAngel.ShowInterface();
  
  ReefAngel.Relay.Write();  // Make relay changes effective 
}


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

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

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(11,30)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(18,30)

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

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

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

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


    // 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;
  // 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
    {
      //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 (ForceCloud)
  {
  ForceCloud=false;
  cloudchance=1;
  cloudduration=10;
//  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (ForceLightning)
  {
  ForceLightning=false;
  cloudchance=1;
  cloudduration=10;
  lightningchance=1;
  cloudstart=NumMins(hour(),minute())+1;
  }
  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          PWMChannel[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,PWMChannel[a],0,180);
          ActinicPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ActinicPWMValue,0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) 
            {
                PWMChannel[b]=100; 
                
            }
            else 
            {
                PWMChannel[b]=0;
                
            //delay(10);
            }                
          }
          else
          {
            PWMChannel[b]=20;
         
          }
        }
      }
    }
    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;
      }
    }
  }
  
  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: 12857
Joined: Fri Mar 18, 2011 6:47 pm

Re: Need help in cleanup and a couple cloud question...

Post by rimai »

That should work.
Roberto.
Post Reply