Sunrise / sunset code based on location

Do you have a question on how to do something.
Ask in here.
thekameleon
Posts: 137
Joined: Sat Feb 16, 2013 7:44 am

Re: Sunrise / sunset code based on location

Post by thekameleon »

Fun times... Well Let me take a look how NOAA does it... I am sure it is similar but maybe if I play around with moving the decimal point... I can figure something out... Do you know the floating point precision limitations or a link to them?
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Truthfully i think its good enough. I would think time would better be spent making it an easy package to add to the libraries and all the other stuff. You could always fine tune the times with the offsets...
thekameleon
Posts: 137
Joined: Sat Feb 16, 2013 7:44 am

Re: Sunrise / sunset code based on location

Post by thekameleon »

Agreed. If accuracy is not important, do you think it might be just easier to just re-write this as a simple sin wave for seasonal changes of sunrise and sunset. Provide a parameter that can adjust the amplitude and another as the start date of the wave? i.e. the crest of the wave could be on 12/21 and the trough on 6/21.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

I just double checked my rise/ set which is coming up as 9:10-11:10 currently and compared to Bahamas rise / set.. Bahamas is:

Jun 8, 2013 6:19 AM 7:58 PM 13h 39m 20s + 24s 1:08 PM 87.8° 94.362

So I'm 10 minutes each direction 20 minutes total...I personally like being tied to gps coordinates event though its a bit wacky with the way the GMT time is in the
RA. If I had to do it over again a sine wave would be fine...a similar function would work for moon, but I'm not so sure on that implementation..l
thekameleon
Posts: 137
Joined: Sat Feb 16, 2013 7:44 am

Re: Sunrise / sunset code based on location

Post by thekameleon »

I took a look an NOAA's website and I might try converting it to javascript. My thinking was to create it as a Sin Wav and then have templates for the caribeean, barrier reef, Hawaii, etc... While not 100% accurate, it would be simple for the average reefer to implement.
clw143
Posts: 118
Joined: Fri Jun 21, 2013 8:20 pm
Location: Louisiana

Re: Sunrise / sunset code based on location

Post by clw143 »

Please look at my code below, the on time for today for my lights is 10:46 and off time of 00:00.

What did I miss?

Code: Select all

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>
#include <SunLocation.h>

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

byte wp40mode; //Mode Byte
SunLocation sl;

////// Place global variable code above here


void setup()
{
  // This must be the first line
  ReefAngel.Init();  //Initialize controller
  // Add Standard Menu
  ReefAngel.AddStandardMenu();
  // Ports toggled in Feeding Mode
  ReefAngel.FeedingModePorts = Port3Bit | Port5Bit | Port6Bit | Port7Bit;
  // Ports toggled in Water Change Mode
  ReefAngel.WaterChangePorts = Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port7Bit | Port8Bit;
  // Ports toggled when Lights On / Off menu entry selected
  ReefAngel.LightsOnPorts = Port1Bit | Port2Bit;
  // Ports turned off when Overheat temperature exceeded
  ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
  // Use T1 probe as temperature and overheat functions
  ReefAngel.TempProbe = T1_PROBE;
  ReefAngel.OverheatProbe = T1_PROBE;

  // Ports that are always on
  ReefAngel.Relay.On( Port3 ); //Mag 12 Return Pump

  ////// Place additional initialization code below here

  {
    ReefAngel.Init();
    // initialize the sunrise / sunset location
    sl.Init(28.253831, -81.264846); // Latitude , Longitude values set
  }

  ////// Place additional initialization code above here
}

void loop()
{

  ReefAngel.DayLights( Port1 ); //T5HO Day Lights (Whites)
  ReefAngel.ActinicLights( Port2 ); //T5HO Actinic Lights (Blues)
  ReefAngel.Relay.DelayedOn( Port4 );  //Reef Octopus NW150 Skimmer and Phosban
  ReefAngel.StandardFan( Port6 ); //Cooling Fans
  ReefAngel.DosingPumpRepeat1( Port7 ); //Dosing Pump
  ReefAngel.StandardATO( Port8 ); //ATO Pump
  
  ////// Place your custom code below here

  {
    // handle updating sunrise and sunset values
    sl.CheckAndUpdate();
    ReefAngel.ShowInterface();
  }

  // Dimmable royal blues 100% if actinics (relay port 2) are on, else moonlight
  if ReefAngel.Relay.IsMaskOn(Port2); {
    ReefAngel.PWM.SetActinic( 100 );
  }
  else {
    ReefAngel.PWM.SetActinic( MoonPhase() );
  }

  //Start Feeding Mode at 17:00 (hour*60*60)
  if ( (now()%86400==61200) /*|| (now()%86400==39600) */) {  
    ReefAngel.FeedingModeStart();
  } 

  //Hydor Evolution 1400 Circulation Pump Port5 Off Between 10pm and 8am
  if (hour()<8 || hour()>=22)  {
    ReefAngel.Relay.Off(Port5);
  }  
  else {
    ReefAngel.Relay.On(Port5);
  }

  //assignmenets are = and comparisons are ==
  if (hour()>=8 && hour()<12) wp40mode=7; //8am - 11:59am
  else if (hour()>=12 && hour()<17) wp40mode=6; //12pm - 4:59pm
  else if (hour()>=17 && hour()<18) wp40mode=5; //5pm - 5:59pm
  else if (hour()>=18 && hour()<24) wp40mode=3; //6pm - 11:59pm
  else wp40mode=4; //12am - 7:59am

  if (wp40mode==9) ReefAngel.PWM.SetDaylight( 100 ); //Full Speed
  if (wp40mode==8) ReefAngel.PWM.SetDaylight( 100/3 ); //Third Speed
  if (wp40mode==7) ReefAngel.PWM.SetDaylight( millis()%1100>800?75:0); //Custom "W1"
  if (wp40mode==6) ReefAngel.PWM.SetDaylight( TidalSwellMode(80,true ) ); //WaveMaxSpeed, PulseSync
  if (wp40mode==5) ReefAngel.PWM.SetDaylight( NutrientTransportMode(0,65,2000,true ) ); //PulseMinSpeed, PulseMaxSpeed, PulseDuration (ms), PulseSync
  if (wp40mode==4) ReefAngel.PWM.SetDaylight( LongPulseMode(0,50,3,true ) ); //PulseMinSpeed, PulseMaxSpeed, PulseDuration (s), PulseSync
  if (wp40mode==3) ReefAngel.PWM.SetDaylight( ShortPulseMode(0,80,1200,true ) ); //PulseMinSpeed, PulseMaxSpeed, PulseDuration (ms), PulseSync
  if (wp40mode==2) ReefAngel.PWM.SetDaylight( ReefCrestMode(50,20,true ) ); //WaveSpeed, WaveOffset, PulseSync
  if (wp40mode==1) ReefAngel.PWM.SetDaylight( SineMode(0,100,10,true ) ); //PulseMinSpeed, PulseMaxSpeed, PulseDuration (ms), PulseSync
  if (wp40mode==0) ReefAngel.PWM.SetDaylight( TideMode(20,100,20 ) ); //WaveSpeed, minOffset, maxOffset

  ////// Place your custom code above here

  // This should always be the last line
  ReefAngel.Portal( "clw143" );
  ReefAngel.ShowInterface();
}

void DrawCustomMain()
{
  byte x = 6;
  byte y = 2;
  byte t;
  char text[7];

  ReefAngel.LCD.Clear(DefaultBGColor,0,0,132,132);
  ReefAngel.LCD.DrawDate(6, 3);
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 132, 11);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,18,14,"Jebao WP40 Mode:");
  if (wp40mode==0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,42,25,"Tide");
  else if(wp40mode==1) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,42,25,"Sine");
  else if (wp40mode==2) ReefAngel.LCD.DrawLargeText(COLOR_ORANGE,255,23,25,"Reef Crest");
  else if (wp40mode==3) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,23,25,"Short Pulse");
  else if (wp40mode==4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,23,25,"Long Pulse");
  else if (wp40mode==5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,8,25,"Nutrient Trnsp.");
  else if (wp40mode==6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,23,25,"Tidal Swell");
  else if (wp40mode==7) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,40,25,"Custom");
  else if (wp40mode==8) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,25,25,"1/3 Power");
  else if (wp40mode==9) ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,23,25,"Full Power");
  pingSerial();

  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 34, 132, 34);
  ReefAngel.LCD.DrawText(0,255,10,37,"Disp");
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(COLOR_BLUE, 255, 8, 45, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,53,37,"Room");
  ConvertNumToString(text, ReefAngel.Params.Temp[T2_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(COLOR_BLUE, 255, 50, 45, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,95,37,"Sump");
  ConvertNumToString(text, ReefAngel.Params.Temp[T3_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(COLOR_BLUE, 255, 93, 45, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 53, 132, 53);
  ReefAngel.LCD.DrawText(0,255,10,56,"pH");
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(COLOR_BLUE, 255, 27, 56, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,67,56,"WP40");
  ConvertNumToString(text, ReefAngel.PWM.GetDaylightValue(),1);
  ReefAngel.LCD.DrawLargeText(COLOR_BLUE, 255, 95, 56, text, Num8x8);
  ReefAngel.LCD.DrawText(0,255,118,56," %");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 65, 132, 65);
  pingSerial();

  ReefAngel.LCD.DrawLargeText(COLOR_BLUE,255,7,85,"Shaun");
  ReefAngel.LCD.DrawHugeText(COLOR_RED, 255, 50, 88,"&");
  ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,70,98,"Kerri's");
  ReefAngel.LCD.DrawLargeText(0,255,7,108,"90Gal Reef Tank");
  pingSerial();

  {
    char buf[16];
    sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,10,67,"Sunrise: ");
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,58,67,buf);
    sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetHour());
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,10,75,"Sunset:  ");
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,58,75,buf);
  }

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

}
void DrawCustomGraph()
{
}
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Where are those coordinates supposed to be and what timezone are you in?

One thing to note is that the controller assumed it is in GMT timezone so whatever coordinates you calculate the time will be set accordingly. You also probably need to adjust the offset so it's in the window you want your lighting schedule to be.

Also your code is a bit odd in that the check and update is combined with showinterface and inside { } which changes the scope. I dont think thats interfering but i wouldnt do that and you should only have showinterface called once at the end of loop which you do.
clw143
Posts: 118
Joined: Fri Jun 21, 2013 8:20 pm
Location: Louisiana

Re: Sunrise / sunset code based on location

Post by clw143 »

location is in Florida EST -5

Code: Select all

sl.Init(28.253831, -81.264846); // Latitude , Longitude values set
All the code added was from a post on usage from Binder on page 5, maybe that was wrong.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Try this code

http://forum.reefangel.com/viewtopic.php?p=17804#p17804

I think there were some bugs in the initial version. Otherwise start setting an offset so that it gets earlier and see how many hours you need to pull back to not have midnight as the set time.
clw143
Posts: 118
Joined: Fri Jun 21, 2013 8:20 pm
Location: Louisiana

Re: Sunrise / sunset code based on location

Post by clw143 »

What is the difference in what you have done here

Code: Select all

ReefAngel.StandardLights( Port1 );
ReefAngel.ActinicLights( Port2 );
And what the wizard has done here

Code: Select all

  ReefAngel.DayLights( Port1 ); //T5HO Day Lights (Whites)
  ReefAngel.ActinicLights( Port2 ); //T5HO Actinic Lights (Blues)
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: Sunrise / sunset code based on location

Post by rimai »

If you look inside the ReefAngel.cpp line 1054, you will see that they are the exact same thing :)

Code: Select all

void ReefAngelClass::DayLights(byte Relay)
{
	StandardLights(Relay);
}
Roberto.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Latest version I'm using
Attachments
SunLocation.zip
(4.17 KiB) Downloaded 432 times
DesignInstinct
Posts: 1
Joined: Thu Jan 23, 2014 9:29 am

Re: Dawn/dusk simulator

Post by DesignInstinct »

Hi, I'm looking for somebody to assist with a standalone Arduino project using this code to control LED lights.
Want to simulate accurate sunrise/set times...and *proper* twilight length of 60-90 minutes.
Need to ramp the LED lights smoothly over this time period. Just like what happens when looking out a window at the sun. Want to match real life fairly closely.

I'm also going to purchase a Reef Angel Plus anyway to do aquaponics/greenhouse control.

I'm NOT a programmer...but very computer literate.
Would NOT like to become a programmer ;)
Far too busy with other projects in other fields.

Will gladly pay to have this "done" for me.
Would like to do a lot of it over Team Viewer in realtime (voice/video).
I can work faster this way. Yes, they have Team Viewer for Linux. I'm on Win 8.1.

"Several inexpensive “light alarm clocks” have been marketed, cloning the DDS concept, but with rapid dawn ramps (far faster than natural dawns) and restricted fields of illumination. These mass-market products have not yet received adequate clinical evaluation. "

Yeah...that's an understatement.
Can't post links here so PM me for similar devices and why they fail at doing this.

This will benefit reef tanks, greenhouse applications, and humans living in dark houses up North.

I want to simulate sunrise/sunset and evening lighting no matter what the cloud cover outside. May be moving from New Mexico back to the frozen North and will need this badly. Most people need this and don't even know it.

Any improvements to the Reef Angel code and the standalone Arduino version code would be kept open source. This stuff is too important to be proprietary.

Google gizmag NASA led lights

Gonna let NASA and Boeing steal our thunder?
Any takers? Reply here, PM me whatever!
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

lnevo....

I've just finished compiling my sketch for my RA+ I recently got and it works great. Am using the Clouds and weather patterns on the standard pwm channels from http://forum.reefangel.com/viewtopic.ph ... 8&start=50. ButI was wondering how to incorporate the location based sunrise/sunset code into it as i've only seen it being used for the standard lights on relays. Thanks for any help.

Code: Select all

#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>

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


byte ActinicPWMValue=0;
byte DaylightPWMValue=0;




void DrawCustomMain()
{
byte x = 6;
byte y = 2;
byte t;
ReefAngel.LCD.DrawLargeText(COLOR_BLACK, COLOR_WHITE, 6, 4, " Martin's BC29");
ReefAngel.LCD.DrawDate(6, 118);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 17, 132, 17);
pingSerial();
x = 12;
y += MENU_START_ROW+1;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+6, " Display      PH");
char text[7];
ConvertNumToString(text, ReefAngel.Params.PH, 100);
ReefAngel.LCD.Clear(DefaultBGColor, x+16, y+65, x+65, y+16);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+75, y+18, text);
pingSerial();

ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
y += MENU_START_ROW*2;
x = 10;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(16*4),y+16);
pingSerial();
ReefAngel.LCD.DrawHugeNumbers(T1TempColor, DefaultBGColor, x, y, text);
pingSerial(); 
x += (16*4) + 8;
ReefAngel.LCD.DrawText(T2TempColor,DefaultBGColor,8,y+25,"Hood:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T2_PROBE], T2TempColor, 
9, y+35, 10);
ReefAngel.LCD.DrawText(T3TempColor,DefaultBGColor,x+8,y+25,"Room:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T3_PROBE], T3TempColor, 
x+9, y+35, 10);


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

//ReefAngel.LCD.DrawText(0,255,20,80,MoonPhaseLabel());


}

void DrawCustomGraph()
{
}

////// Place global variable code above here


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    //ReefAngel.Use2014Screen();  // Let's use 2014 Screen 
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port3Bit | Port6Bit | Port7Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port7Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // 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;


    // Ports that are always on
    ReefAngel.Relay.On( Port1 );
    ReefAngel.Relay.On( Port3 );
    ReefAngel.Relay.On( Port7 );
    ReefAngel.Relay.On( Port8 );

    ////// Place additional initialization code below here
    

    ////// Place additional initialization code above here
}

void loop()
{
  

        
    ReefAngel.StandardHeater( Port2 );
    ReefAngel.DosingPumpRepeat1( Port4 );
    ReefAngel.DosingPumpRepeat2( Port5 );
    ReefAngel.StandardATO( Port6 );
    //ReefAngel.PWM.SetDaylight( MoonPhase() );
    //ReefAngel.PWM.SetActinic( MoonPhase() );
    ////// Place your custom code below here
    

    ////// Place your custom code above here

    // This should always be the last line
    ReefAngel.Portal( "89delta" );
    ReefAngel.ShowInterface();
    
    
  // Calculate your regular sunrise/sunset PWM value
  ActinicPWMValue=PWMSlope(10,00,22,15,0,50,40,ActinicPWMValue);
  DaylightPWMValue=PWMSlope(10,00,22,15,0,50,40,DaylightPWMValue);
  CheckCloud();
  ReefAngel.PWM.SetActinic(ActinicPWMValue);
  ReefAngel.PWM.SetDaylight(DaylightPWMValue);    
}





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

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

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

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

  // 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 (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue,0,180);
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        if (random(100)<20) lightningstatus=1; 
        else lightningstatus=0;
        if (lightningstatus)
        {
          DaylightPWMValue=100; 
          ActinicPWMValue=100;
        }
        else 
        {
          DaylightPWMValue=0;
          ActinicPWMValue=0;
        }
        delay(1);
      }
    }
    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;
}
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

You would use it in the section that says Calculate your regular sunrise/sunset PWM value. The sunlocation class which i dont see declared will have methods to get the rise hour/minute and set hour/minute. You'll plug those into the pwmslope function arguments.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

You would use it in the section that says Calculate your regular sunrise/sunset PWM value. The sunlocation class which i dont see declared will have methods to get the rise hour/minute and set hour/minute. You'll plug those into the pwmslope function arguments.
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

Thanks for the fast reply lnevo...I know i've still gotta add the rest of the code to my sketch, but something 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 <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>
#include <SunLocation.h>

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

SunLocation sl;


byte ActinicPWMValue=0;
byte DaylightPWMValue=0;




void DrawCustomMain()
{
byte x = 6;
byte y = 2;
byte t;
ReefAngel.LCD.DrawLargeText(COLOR_BLACK, COLOR_WHITE, 6, 4, " Martin's BC29");
ReefAngel.LCD.DrawDate(6, 118);
ReefAngel.LCD.Clear(COLOR_BLACK, 1, 17, 132, 17);
pingSerial();
x = 12;
y += MENU_START_ROW+1;
ReefAngel.LCD.DrawText(COLOR_BLUE, COLOR_WHITE, x, y+6, " Display      PH");
char text[7];
ConvertNumToString(text, ReefAngel.Params.PH, 100);
ReefAngel.LCD.Clear(DefaultBGColor, x+16, y+65, x+65, y+16);
ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+75, y+18, text);
pingSerial();

ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
y += MENU_START_ROW*2;
x = 10;
ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(16*4),y+16);
pingSerial();
ReefAngel.LCD.DrawHugeNumbers(T1TempColor, DefaultBGColor, x, y, text);
pingSerial(); 
x += (16*4) + 8;
ReefAngel.LCD.DrawText(T2TempColor,DefaultBGColor,8,y+25,"Hood:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T2_PROBE], T2TempColor, 
9, y+35, 10);
ReefAngel.LCD.DrawText(T3TempColor,DefaultBGColor,x+8,y+25,"Room:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T3_PROBE], T3TempColor, 
x+9, y+35, 10);


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

//ReefAngel.LCD.DrawText(0,255,20,80,MoonPhaseLabel());


}

void DrawCustomGraph()
{
}

////// Place global variable code above here


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    //ReefAngel.Use2014Screen();  // Let's use 2014 Screen 
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port3Bit | Port6Bit | Port7Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port3Bit | Port4Bit | Port5Bit | Port6Bit | Port7Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = 0;
    // 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;


    // Ports that are always on
    ReefAngel.Relay.On( Port1 );
    ReefAngel.Relay.On( Port3 );
    ReefAngel.Relay.On( Port7 );
    ReefAngel.Relay.On( Port8 );

    ////// Place additional initialization code below here
    
  sl.Init(-18.285833, 147.699722); // In decimal numbers (i.e. -18.285833, 147.699722)

    ////// Place additional initialization code above here
}

void loop()
{
  

        
    ReefAngel.StandardHeater( Port2 );
    ReefAngel.DosingPumpRepeat1( Port4 );
    ReefAngel.DosingPumpRepeat2( Port5 );
    ReefAngel.StandardATO( Port6 );
    //ReefAngel.PWM.SetDaylight( MoonPhase() );
    //ReefAngel.PWM.SetActinic( MoonPhase() );
    ////// Place your custom code below here
    
    // handle updating sunrise and sunset values
    sl.CheckAndUpdate();
    sl.SetOffset(0,0,0,0); // rise_hour, rise_seconds, set_hour, set_seconds
    
    ////// Place your custom code above here

    // This should always be the last line
    ReefAngel.Portal( "89delta" );
    ReefAngel.ShowInterface();
    
    
  // Calculate your regular sunrise/sunset PWM value
  ActinicPWMValue=PWMSlope(sl.GetRiseHour(),sl.GetRiseMinute(),sl.GetSetHour(),sl.GetSetMinute(),0,50,40,ActinicPWMValue);
  DaylightPWMValue=PWMSlope(sl.GetRiseHour(),sl.GetRiseMinute(),sl.GetSetHour(),sl.GetSetMinute(),0,50,40,DaylightPWMValue);
  CheckCloud();
  ReefAngel.PWM.SetActinic(ActinicPWMValue);
  ReefAngel.PWM.SetDaylight(DaylightPWMValue);    
}





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

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

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

  // 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 (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      DaylightPWMValue=ReversePWMSlope(cloudstart,cloudstart+cloudduration,DaylightPWMValue,0,180);
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        if (random(100)<20) lightningstatus=1; 
        else lightningstatus=0;
        if (lightningstatus)
        {
          DaylightPWMValue=100; 
          ActinicPWMValue=100;
        }
        else 
        {
          DaylightPWMValue=0;
          ActinicPWMValue=0;
        }
        delay(1);
      }
    }
    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;
}
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

I see you figured it out! Yes exactly :) let us know if that worked for you?
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

Nice....thanks for looking lnevo. My other question is in regards to the moon phases. Can I use sl.GetRiseHour(),sl.GetRiseMinute(),sl.GetSetHour(),sl.GetSetMinute() in switching over to the moonphase() for my blue leds after the day has finished.
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Yes, but you might want to also look at my moon rise/set class that you can use too which calculates the rise/set times for the the Moon :) However, you can use the sl.GetXXX() for whatever scheduling you want. You could also look at the option sl.UseMemory=true which will set the StandardLight schedule into memory so you could have other devices that aren't dimming let's say based off the sunrise/sunset schedule.
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

How would I incorporate your moon class into it lnevo.....thanks for your help.
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

Sweet....thank lnevo. Will try it out tonight when i get home.

Sent from my SAMSUNG-SGH-I717 using Tapatalk
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

So everything is working correctly with my sketch, Sunrise/sunset, Weather and moon phases. Although i've noticed that the sunrise/sunset times don't really change but by +/- 2 minutes. Is that normal for the most part? My Long/Lat is set to the GBR between Paul Reef and Storm Reef off of Queensland,AU.

sl.Init(-21.285833, 150.999722); // In decimal numbers (i.e. -18.285833, 147.699722)
sl.SetOffset(11,0,11,0); // rise_hour, rise_seconds, set_hour, set_seconds
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Sunrise / sunset code based on location

Post by lnevo »

Yep, try tracking it over the course of a month or two. Area's closer to the equator are going to have the smallest amount of shift daily as the fluctuation is only going to be +/- 2 hours or so over the course of the seasons.
89delta
Posts: 163
Joined: Mon Oct 15, 2012 7:21 pm
Location: Leesburg, GA

Re: Sunrise / sunset code based on location

Post by 89delta »

Ok, thanks for the clarification lnevo. Love everything you guys have done with coding and everything else.

Sent from my SAMSUNG-SGH-I717 using Tapatalk
Post Reply