Page 1 of 2

Got my controller,

Posted: Thu Aug 09, 2012 7:14 pm
by lnevo
Woohoo! Got my controller hooked up to the Mac (no issues communicating!!) got the wifi module configured thru serial (Wifi Utility didn't do anything...) Connected to controller and tested through web. Port forwarding configured and working! iOS app working... Just have to configure portal now...but first, have to load some code!!!

The git commenting Doxygen step will now take a short intermission...maybe back to it later tonight or this weekend. Let the fun begin!!!

Thanks Roberto, package came nicely packed and everything looks great!!

Re: Got my controller,

Posted: Thu Aug 09, 2012 10:59 pm
by lnevo
So here's what I got after my first night of coding... let me know what you think.

Next up is to customize the menu and main screens to show the MoonPhase and Vortech Mode. Anyone have any input on changing the vortech mode through a custom menu? I saw the thread detailing this, but wasn't sure how that code ever ended up working...

Also need to see about working with the Sunrise/Sunset stuff I wanted to work on. But at least the controller is currently usable and ready to be connected up. Will probably code a few more features before making the swap.

Feedback is more than welcome....

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 <ReefAngel.h>

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

byte vtechmode;
boolean bFeeding=false;
boolean isNight=false;

//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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


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


    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here
    

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

void loop()
{
    ReefAngel.Relay.DelayedOn( Vortech,5 );
    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardLights( WhiteLEDs,12,0,20,0 );
    ReefAngel.StandardLights( BlueLEDs,11,0,23,0 );
    ReefAngel.StandardHeater( Heater,788,792 );
    
    ////// Place your custom code below here
    
    if (( hour() <= 11 ) || ( hour() >= 23 )) 
    {
      isNight=true;
    } else {
      isNight=false;
    }
    
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,155,5);
      ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
      ReefAngel.Timer[4].Start();
      vtechmode = 5;
    }
    
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered()) 
    {
      if (isNight) 
      {
        ReefAngel.RF.UseMemory=false;
        ReefAngel.RF.SetMode(Night,15,0);
        vtechmode = 9;
      } else {
        ReefAngel.RF.UseMemory=true;
        vtechmode = InternalMemory.RFMode_read();
      }
    }
    
    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
    } else { 
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );
    }   
    
    ////// Place your custom code above here

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


Re: Got my controller,

Posted: Fri Aug 10, 2012 8:06 am
by rimai
Nice job!!! :)
The easiest way of changing modes using the custom menu is by cycling modes everytime you choose that menu, then go to memory mode as the last choice.

Re: Got my controller,

Posted: Sat Aug 11, 2012 8:28 pm
by lnevo
If I did that, how do I prevent my loop() from reverting it back to memory mode? Also how do I set the default memory mode?

Re: Got my controller,

Posted: Sat Aug 11, 2012 8:55 pm
by rimai
To prevent it from going back to memory mode, just use this:

Code: Select all

ReefAngel.RF.UseMemory=false;
It will simply ignore whatever is in the memory setting.

To change the memory setting, there are several ways:
Java status app
Android app
Portal
Built-in webserver

Re: Got my controller,

Posted: Sun Aug 12, 2012 5:16 am
by lnevo
Nevermind, re-reading my code, I only go back to memory mode if the timer is trigged. But can I set the default memory mode in my setup()? and change later through the apps?

Re: Got my controller,

Posted: Sun Aug 12, 2012 7:10 am
by rimai
Yes.

Code: Select all

  InternalMemory.RFMode_write(3);
  InternalMemory.RFSpeed_write(50);
  InternalMemory.RFDuration_write(10);

Re: Got my controller,

Posted: Sun Aug 12, 2012 9:20 pm
by lnevo
Ok, here is my current code...

I still haven't sync'd up my Vortech yet, but now that I have the laptop able to upload code, I can work on that. Also should have pH calibration fluid in the LFS by Wednesday... I still have to setup the Vortech control menu so I can play with it from the controller. (Do the mode buttons still work on the unit itself after it's sync'd?) The moonlights are hooked up and look sweet! They are really low, even on 100% which is perfect.. I just want a touch of light when everything is off so it's not so dark... Right now we're at 20% and it gives a nice glimmer. Too bad I can't get a good photo.

Anyway, does anyone have a good code to add the Vortech controls? I want to preserve most of the Simple_Menu and have to start playing around with that.

I've also added the Sunrise / Sunset, but my string writing seems a bit messed up. I think the calculations are good, but without seeing the results, it's hard to tell. It seemed about an hour off from the Utah given as the example, so I really need some time to troubleshoot. I've got the base code stripped of all weather and dimming functions, so it's pretty basic. If someone can take a look and give me another set of eyes would be great!

Anyway, here it is...sorry to be soo long winded..

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

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

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

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>

//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude, longitude;
long elapsedTime;//used multiple places as elapsed since midnight
long newDay;
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dow=0;//day of week
//END HEADER FOR MHOCKIN Weather package //

#define NUMBERS_8x16
byte vtechmode;
boolean bFeeding=false;
boolean isNight=false;
double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;


//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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

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

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
 
  // Param Header
  y=y+20;
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10;
  ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+16);
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
  
  y=y+20;
  /*
  /// Display Sunrise / Sunset (to be calculated later...)
  char riseTime[5];
  char setTime[5];
  char temp[]="  ";
  
  itoa(SunriseHour,temp,10);
  strcat(riseTime, temp); 
  strcat(riseTime, ":"); 
  itoa(SunriseMin,temp,10);
  strcat(riseTime, temp);
  
  itoa(SunsetHour,temp,10);
  strcat(setTime, temp); 
  strcat(setTime, ":"); 
  itoa(SunsetMin,temp,10);
  strcat(setTime, temp);
  
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"Rise:");
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+32,y,riseTime);
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+70,y,"Set:");
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+98,y,setTime);
  */
  // MoonLight %
  y=y+10;  
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLight:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+63, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+63+t, y, "%");
  pingSerial();
  
  // MoonPhase
  y=y+10;
  DayAge = MoonAge(day(), month(), year());
  MoonState(DayAge);
  char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,x+32,y,ThisPhaseLabel[ThisPhase]);
  pingSerial();
  
  // Vortech Mode
  y=y+10;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+20,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawText(COLOR_LIMEGREEN,255,x+20,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawText(COLOR_GOLD,255,x+20,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawText(COLOR_GOLD,255,x+20,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,x+20,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawText(COLOR_PINK,255,x+20,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+20,y,"Nutrient");
  else if (vtechmode == 6) ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+2,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawText(COLOR_WHITE,0,x+20,y,"Night");
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
//  ReefAngel.LCD. DrawGraph(5, 15);
}


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

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here
    

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

void loop()
{
    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    elapsedTime=(now()-newDay);  //Elapsed time is seconds from midnight of today- local processor time.
    if (dow!=day()){ //used to see that were in a new day and need to recalculate sunrise and sunset
      CalSun();
      dow=day();
    } 

    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardLights( WhiteLEDs,12,0,20,0 );
    ReefAngel.StandardLights( BlueLEDs,11,0,23,0 );
    ReefAngel.StandardHeater( Heater,788,792 );
    
    ////// Place your custom code below here
    
    if (( hour() <= 11 ) || ( hour() >= 23 )) isNight=true; else isNight=false;
    
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,155,5);
      ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
      ReefAngel.Timer[4].Start();
      vtechmode = 5;
    }
    
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered()) 
    {
      if (isNight) 
      {
        ReefAngel.RF.UseMemory=false;
        ReefAngel.RF.SetMode(Night,15,0);
        vtechmode = 9;
      } else {
        ReefAngel.RF.UseMemory=true;
        vtechmode = InternalMemory.RFMode_read();
      }
    }
    
    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
    } else { 
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );
    }   
    
    ////// Place your custom code above here

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

int JulianDate(int d, int m, int y)
{ 
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{ 
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip); 
  //After several trials I've seen to add the following lines, 
  //which gave the result was not bad 
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1: 
    0, 29;
    ThisPhase = 0;
  case 2: 
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3: 
    7;
    ThisPhase = 2;
  case 4: 
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5: 
    14;
    ThisPhase = 4;
  case 6: 
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7: 
    22;
    ThisPhase = 6;
  case 8: 
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default: 
    return 0;
  }
}

//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
   //Serial.println("CalSun Run Now");

       latitude=dmsToSeconds(40,44,00);//United States of America- Salt Lake City, local time is -7 hours GMT 
       longitude=dmsToSeconds(-111,47,00);
       //**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load******************************************** 
   
    if (dow==0){//if the controller has resarted we need to find midnight
      long hours, minutes;//store current elapsed local hours as total seconds from midnight
      time_t t=now();//store current clock time to parse
      hours=hour(t);
      hours=(hours*3600);//current hour number 0-23 as seconds
      minutes=minute(t);
      minutes=(minutes*60);//minutes of current hour as seconds
      newDay=now();
      newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
    }
    else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
      newDay=now();
    }
      
    //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
    newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
    rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
    set=newDay;//
    //Calculate rise time and set time using Epherma Library functions (see end of code) 
    SunRise(&rise);//call to Epherma function
    SunSet(&set);//Call to Epherma functionunsigned long newDay;
  
   /*Serial.print("rise and set=  ");
   Serial.println(rise);
   Serial.println(set);
   Serial.print("newDay as seconds since 2000 to todays midnight=  ");
   Serial.println(newDay);*/
   rise=(rise-newDay);// set to elapsed seconds of day
   set=(set-newDay);
   /*Serial.print("rise and set as elapsed seconds of day=  ");
   Serial.println(rise);
   Serial.println(set);*/
   newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day) 
   /*Serial.print("newDay as seconds since since 1970 to todays midnight=  ");
   Serial.println(newDay);
   Serial.print("elapsed is");
   long elapsed=now()-newDay;
   Serial.println(elapsed);*/
   SunriseHour=hour(rise);
   SunriseMin=minute(rise);
   SunsetHour=hour(set);
   SunsetMin=minute(set);
 }//END SunCalc FUNCTION

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
   return dd * 3600.0;
}

//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;

   ret = labs((long)d);
   ret = ret * 3600L + 60L * m + s;
   ret = (d<0L) ? -ret : ret;
   return ret;
}
/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;

   dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
   dt %= _tropical_year;
   t = dt;
   t /= _tropical_year;
   t *= 6.283185307179586;
   t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
   return t;
}


/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/

double SolarDeclination(unsigned long dt){
double y;

   dt %= _tropical_year;
   y = dt;
   y /= _tropical_year; // fractional year
   y *= 6.283185307179586;
   y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
   return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;

   d = -SolarDeclination(dt); // will be positive in Northern winter
   l = latitude / _sec_rad; // latitude in radians

   e += 60.0 * l * tan(l + d); // latitudinal error
   d = tan(l) * tan(d); //

   if(d>1.0) return 86400UL;
   if(d < -1.0) return 0UL;

   d = acos(d);
   d /= _zenith;

   n = 86400UL * d;
   n += e;
   return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.

*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

   daylen = daylightseconds(*dt);
   if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
   if(daylen == 0UL) return -1; // there is no 'day' today

   *dt /= 86400UL;
   *dt *= 86400UL;
   *dt += 43200UL; // set the time stamp to 12:00:00 GMT

   *dt -= daylen / 2; //        sunrise at the prime meridian
   if(set) *dt += daylen; //     sunset at the prime meridian

   *dt -= equation_of_time(*dt);

   //*dt -= longitude / 15.0; // rotate to our own meridian

   return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
    return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
    return SunRiseSet(when, 1);
}

Re: Got my controller,

Posted: Mon Aug 13, 2012 9:56 pm
by lnevo
Ok roberto, I've made some progress. I have the custom menu cycling through my vortech modes. The only issue I have at the moment is the logic I'm doing for my scheduling...

I wanted to be able to override night mode to go into feed mode, so I've got that working, but I'd also like to be able to change the pumps if I'm in night mode as well. I've put a wrapper around the menu code so that it wouldn't change the screen if I'm set to not use memory.

Can you just look at my scheduling code and let me know if there's a better way? I should probably just simplify it since I probably shouldn't change my vortech or feed after lights out... but I want FULL control :)

Thanks,
Lee

Code is already posted in the thread below, so don't want to duplicate.

http://forum.reefangel.com/viewtopic.php?p=12941#p12941

Re: Got my controller,

Posted: Mon Aug 13, 2012 10:05 pm
by rimai
Looks awesome!!
I think the only thing you may have problems is to come out of night mode.
Let me know if this is the case.

Got my controller,

Posted: Mon Aug 13, 2012 10:05 pm
by lnevo
I probably need to redo my scheduling anyway since i think the only thing synced to the GBR right now is my refugium light, vortech and moonlights :)

Got my controller,

Posted: Tue Aug 14, 2012 5:27 am
by lnevo
Never mind, I think I got it...

What I need to do is change my vortech menu function to use SetMode instead of changing the memory value when it's not in memory mode. Then I can start the timer just like feed mode, so that I can set it to what I want and go to bed and it will revert after x time...I think that should be all I need...

And I guess then I can call StandardLights in the section of loop that decides if it's a new day so it only runs once each day when the sunrise/sunset variables change...Does that sound right?

I think at this point I've got all the feature planned except for setting up my float switches....need to figure out a bracket for my ato reservoir and DT... Thinking about aqua-hub moldable plastic strips (except for the $10 shipping for a $10 product :( ), but for the reservoir a magnetic might be better since I've got it in a closed container...so we'll see.

I'm having a lot of fun with this. Soo happy I got it over the apex...I know I would have felt constrained in the long run. Thanks for building a great product and for some great code!

Re: Got my controller,

Posted: Tue Aug 14, 2012 7:28 am
by rimai
Cool!!
Yeah, I think you are in the right track.

Re: Got my controller,

Posted: Tue Aug 14, 2012 10:16 am
by lnevo
rimai wrote:Looks awesome!!
I think the only thing you may have problems is to come out of night mode.
Let me know if this is the case.
You're right! Today was the first night I had the vortech setup and thankfully I just checked. I put it into feed mode which should trigger the function to put it back into UseMemory mode. I know I also have an issue if the controller resets during night mode as it stays in whatever active mode in memory. So I really need to restructure that part of the scheduling...
rimai wrote:Cool!!
Yeah, I think you are in the right track.
Thanks, definitely in the right direction, just spinning right now trying to figure out the scheduling part without having access to my IDE at work :(

Re: Got my controller,

Posted: Tue Aug 14, 2012 2:34 pm
by lnevo
Ok, I think I've resolved my scheduling issues... also cleaned up a lot... still waiting to hear from rufessor on the time calculations being off... but anyway, here's the current code.

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

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

////// Place global variable code below here
#define NUMBERS_8x16
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>
//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude, longitude;
long newDay;
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
byte dow=0;//day of week
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

// Additional variables added for Sunrise/Sunset calculation
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
int UserOffset=5*3600; // 5 hours

byte vtechmode=Random2;
byte vtechDefault=Random2;
int vtechSpeed=50;
int vtechDuration=10;
boolean bFeeding=false;
boolean isNight=false;

double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;

#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";
// 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
};

//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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

void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}

void MenuEntry3()
{
  byte vtmode=vtechmode;
  
  vtmode++;
  if ( vtmode == 7 ) vtmode = 9;
  if ( vtmode > 9 ) vtmode = 0;
  vtechmode=vtmode;
 
  ReefAngel.RF.UseMemory=false;
  ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
  ReefAngel.RF.SetMode(vtmode,ReefAngel.RF.Speed,ReefAngel.RF.Duration);
  ReefAngel.Timer[4].SetInterval(10); // Timer for 30min
  ReefAngel.Timer[4].Start();
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

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

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y=y+20; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10; 
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y=y+20; t=x;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  itoa(SunriseHour,text,10);
  if (SunriseHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  }
  itoa(SunriseMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=20;
  
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
  itoa(SunsetHour,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=10;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunsetMin<10) ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  itoa(SunsetMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);

  // MoonPhase
  y=y+10; 
  DayAge = MoonAge(day(), month(), year());
  MoonState(DayAge);
  char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,x+32,y,ThisPhaseLabel[ThisPhase]);
  pingSerial();
  
  // MoonLight %
  y=y+10;  
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y=y+10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,t,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,t,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here
    
    // Reset Vortech to preferred defaults
    InternalMemory.RFMode_write(vtechDefault);
    InternalMemory.RFSpeed_write(vtechSpeed);
    InternalMemory.RFDuration_write(vtechDuration);
    ReefAngel.RF.UseMemory=true;
   
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardHeater( Heater,788,792 );
    
    ////// Place your custom code below here

    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dow!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dow=day();
      // And let's reset the light schedule
      ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3,SunsetMin );
      ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
    } 
    
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin;
    int setTime=SunsetHour*100+SunsetMin;
    
    if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
    
    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode!=Night) { 
        ReefAngel.RF.SetMode(Night,15,0);
      }
    } else { 
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );

      // It's Daytime!
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode==Night) {
          ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
          ReefAngel.RF.SetMode(vtechDefault,15,0);
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,100,5);
      ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
      ReefAngel.Timer[4].Start();
      vtechmode = Smart_NTM;
    }
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered()) 
    {
      ReefAngel.RF.UseMemory=true;
    }

    if (ReefAngel.RF.UseMemory) { 
      vtechmode=ReefAngel.RF.Mode;
    }
    
    ////// Place your custom code above here

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

int JulianDate(int d, int m, int y)
{ 
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{ 
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip); 
  //After several trials I've seen to add the following lines, 
  //which gave the result was not bad 
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1: 
    0, 29;
    ThisPhase = 0;
  case 2: 
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3: 
    7;
    ThisPhase = 2;
  case 4: 
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5: 
    14;
    ThisPhase = 4;
  case 6: 
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7: 
    22;
    ThisPhase = 6;
  case 8: 
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default: 
    return 0;
  }
}

//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
   //Serial.println("CalSun Run Now");

       // latitude=dmsToSeconds(40,44,00);//United States of America- Salt Lake City, local time is -7 hours GMT 
       // longitude=dmsToSeconds(-111,47,00);
       latitude=dmsToSeconds(18,34,4);// Great Barrier Reef, Australia
       longitude=dmsToSeconds(148,33,19); // Great Barrier Reef, Australia
       //**********************ok now were done changing things IF YOU CHANGED the Top part of the GLOBAL variable decleration AND this... your FULLY configured and ready to load******************************************** 
   
    if (dow==0){//if the controller has resarted we need to find midnight
      long hours, minutes;//store current elapsed local hours as total seconds from midnight
      time_t t=now();//store current clock time to parse
      hours=hour(t);
      hours=(hours*3600);//current hour number 0-23 as seconds
      minutes=minute(t);
      minutes=(minutes*60);//minutes of current hour as seconds
      newDay=now();
      newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
    }
    else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
      newDay=now();
    }
      
    //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
    newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
    rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
    set=newDay;//
    //Calculate rise time and set time using Epherma Library functions (see end of code) 
    SunRise(&rise);//call to Epherma function
    SunSet(&set);//Call to Epherma functionunsigned long newDay;
  
   // Convert rise/set to normal hours/minutes before we make it an offset from midnight. 
   // Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
   SunriseHour=hour(rise+(56*60)+UserOffset);
   SunriseMin=minute(rise+(56*60)+UserOffset);
   SunsetHour=hour(set-(19*60)+UserOffset);
   SunsetMin=minute(set-(19*60)+UserOffset);
   
   /*Serial.print("rise and set=  ");
   Serial.println(rise);
   Serial.println(set);
   Serial.print("newDay as seconds since 2000 to todays midnight=  ");
   Serial.println(newDay);*/
   rise=(rise-newDay);// set to elapsed seconds of day
   set=(set-newDay);
   /*Serial.print("rise and set as elapsed seconds of day=  ");
   Serial.println(rise);
   Serial.println(set);*/
   newDay+=946684800;//Convert newDay back to unix epoch GMT midnight today (used in loop to determine how far we are into the day) 
   /*Serial.print("newDay as seconds since since 1970 to todays midnight=  ");
   Serial.println(newDay);
   Serial.print("elapsed is");
   long elapsed=now()-newDay;
   Serial.println(elapsed);*/
   
}//END SunCalc FUNCTION

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
   return dd * 3600.0;
}

//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;

   ret = labs((long)d);
   ret = ret * 3600L + 60L * m + s;
   ret = (d<0L) ? -ret : ret;
   return ret;
}
/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;

   dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
   dt %= _tropical_year;
   t = dt;
   t /= _tropical_year;
   t *= 6.283185307179586;
   t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
   return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;

   dt %= _tropical_year;
   y = dt;
   y /= _tropical_year; // fractional year
   y *= 6.283185307179586;
   y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
   return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;

   d = -SolarDeclination(dt); // will be positive in Northern winter
   l = latitude / _sec_rad; // latitude in radians

   e += 60.0 * l * tan(l + d); // latitudinal error
   d = tan(l) * tan(d); //

   if(d>1.0) return 86400UL;
   if(d < -1.0) return 0UL;

   d = acos(d);
   d /= _zenith;

   n = 86400UL * d;
   n += e;
   return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.

*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

   daylen = daylightseconds(*dt);
   if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
   if(daylen == 0UL) return -1; // there is no 'day' today

   *dt /= 86400UL;
   *dt *= 86400UL;
   *dt += 43200UL; // set the time stamp to 12:00:00 GMT

   *dt -= daylen / 2; //        sunrise at the prime meridian
   if(set) *dt += daylen; //     sunset at the prime meridian

   *dt -= equation_of_time(*dt);

   //*dt -= longitude / 15.0; // rotate to our own meridian

   return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
    return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
    return SunRiseSet(when, 1);
}
Let me know if you think the scheduling and overrides look better now. I'll be testing this later tonight and my wife would appreciate if I wasn't online all night working on it...

Lee

Edit: Posted some bug fixes... Everything is working :)

Re: Got my controller,

Posted: Fri Aug 17, 2012 9:40 pm
by lnevo
Some bugfixes, slight modifications, some cleanup, and so forth...

Code: Select all

//By Matthew Hockin 2012.  
//SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
//Epherma (SWFLTEK.com) was written by Michael Rice- thanks Michael it works great. 
//If you copy from this (its all open source) please 
//acknowledge Michael for SWFLTEK library code (obviously labeled) or Matthew Hockin (for the rest).

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

////// Place global variable code below here
#define NUMBERS_8x16
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>

//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
byte dow=0;//day of week
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

// Additional variables added for Sunrise/Sunset calculation
int SunriseHour, SunriseMin, SunsetHour, SunsetMin;
int userOffset=-(9*3600); // -9 hours


byte vtechmode=Random2;
byte vtechDefault=Random2;
int vtechSpeed=50;
int vtechDuration=10;
boolean bFeeding=false;
boolean isNight=false;

double MoonAge(int,int,int);
int JulianDate(int,int,int);
byte MoonState();
byte ThisPhase;
byte DayAge;

#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";
// 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
};

//*********************************************************************************************************************************
//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Vortech            5
#define Heater             6
#define Refugium           7
#define UPS                8
//*********************************************************************************************************************************

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

void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}

void MenuEntry3()
{
  byte vtmode=vtechmode;
  
  vtmode++;
  if ( vtmode == 7 ) vtmode = 9;
  if ( vtmode > 9 ) vtmode = 0;
  vtechmode=vtmode;
 
  ReefAngel.RF.UseMemory=false;
  ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
  ReefAngel.RF.SetMode(vtmode,ReefAngel.RF.Speed,ReefAngel.RF.Duration);
  ReefAngel.Timer[4].SetInterval(1200); // Timer for 20min
  ReefAngel.Timer[4].Start();
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
  CalSun();
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

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

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y=y+20; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10; 
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y=y+20; t=x;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  itoa(SunriseHour,text,10);
  if (SunriseHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  }
  itoa(SunriseMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=18;
  
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
  itoa(SunsetHour,text,10);
  if (SunsetHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunsetMin<10) { ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6; }
  itoa(SunsetMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);

  // MoonPhase
  y=y+10; 
  DayAge = MoonAge(day(), month(), year());
  MoonState(DayAge);
  char* ThisPhaseLabel[]={ "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent" };
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,ThisPhaseLabel[ThisPhase]);
  pingSerial();
  
  // MoonLight %
  y=y+10;  
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y=y+10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,t,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,t,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}


void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = 0;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here
    
    // Reset Vortech to preferred defaults
    InternalMemory.RFMode_write(vtechDefault);
    InternalMemory.RFSpeed_write(vtechSpeed);
    InternalMemory.RFDuration_write(vtechDuration);
    ReefAngel.RF.UseMemory=true;
   
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer,5 );
    ReefAngel.StandardHeater( Heater,783,798 );
    
    ////// Place your custom code below here

    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dow!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dow=day();
      // And let's reset the light schedule
    } 
    
    ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3,SunsetMin );
    ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
    
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin;
    int setTime=SunsetHour*100+SunsetMin;
    
    if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
    
    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode!=Night) { 
        ReefAngel.RF.SetMode(Night,15,0);
      }
    } else { 
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );

      // It's Daytime!
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode==Night) {
          ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
          ReefAngel.RF.SetMode(vtechDefault,15,0);
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,80,5);
      ReefAngel.Timer[4].SetInterval(900); // Timer for 15min
      ReefAngel.Timer[4].Start();
      vtechmode = Smart_NTM;
    }
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered()) 
    {
      ReefAngel.RF.UseMemory=true;
    }

    if (ReefAngel.RF.UseMemory) { 
      vtechmode=ReefAngel.RF.Mode;
    }
    
    ////// Place your custom code above here

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

int JulianDate(int d, int m, int y)
{ 
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{ 
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip); 
  //After several trials I've seen to add the following lines, 
  //which gave the result was not bad 
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1: 
    0, 29;
    ThisPhase = 0;
  case 2: 
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3: 
    7;
    ThisPhase = 2;
  case 4: 
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5: 
    14;
    ThisPhase = 4;
  case 6: 
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7: 
    22;
    ThisPhase = 6;
  case 8: 
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default: 
    return 0;
  }
}

//Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
long newDay; 
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise

  //Serial.println("CalSun Run Now");
  
  latitude=ddToSeconds(latitude);
  longitude=ddToSeconds(longitude);
   
  if (dow==0){//if the controller has resarted we need to find midnight
    long hours, minutes;//store current elapsed local hours as total seconds from midnight
    time_t t=now();//store current clock time to parse
    hours=hour(t);
    hours=(hours*3600);//current hour number 0-23 as seconds
    minutes=minute(t);
    minutes=(minutes*60);//minutes of current hour as seconds
    newDay=now();
    newDay-=(hours+minutes);//Convert current local unix epoch time to local unix epoch time of midnight
  } 
  else if (dow!=0){//if we did not restart but the day is new then it is midnight and were good to go..
    newDay=now();
  }
      
  //#define SECS_YR_2000 (946684800) the time at the start of y2k (need to subtract from unix epoch time to bring to Y2K origin
  newDay-=946684800;//convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today
    
  rise=newDay;//set value to send to SunRise as midnight GMT in seconds from Y2K
  set=newDay;//
  //Calculate rise time and set time using Epherma Library functions (see end of code) 
  SunRise(&rise);//call to Epherma function
  SunSet(&set);//Call to Epherma functionunsigned long newDay;
  /*
  Serial.print("rise and set=  ");
  Serial.println(rise);
  Serial.println(set);
  Serial.print("newDay as seconds since 2000 to todays midnight=  ");
  Serial.println(newDay);
  */
  
  // Convert rise/set to normal hours/minutes before we make it an offset from midnight. 
  // Currently correcting for a 56 minute correction to Sunrise and -19 minute correction to Sunset (8/14/2012@GBR)
  SunriseHour=hour(rise+userOffset);
  SunriseMin=minute(rise+userOffset);
  SunsetHour=hour(set+userOffset);
  SunsetMin=minute(set+userOffset);
  
}//END SunCalc FUNCTION

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
//THE CODE BELOW THIS copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
//this code is being used freely with attribution to Micahel Rice in accord with his request
//  A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
  return dd * 3600.0;
}

//Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s){
long ret;

  ret = labs((long)d);
  ret = ret * 3600L + 60L * m + s;
  ret = (d<0L) ? -ret : ret;
  return ret;
}
/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt){
double t;

  dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
  dt %= _tropical_year;
  t = dt;
  t /= _tropical_year;
  t *= 6.283185307179586;
  t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
  return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt){
double y;

  dt %= _tropical_year;
  y = dt;
  y /= _tropical_year; // fractional year
  y *= 6.283185307179586;
  y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
  return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt){
float l, d, e;
long n;

  d = -SolarDeclination(dt); // will be positive in Northern winter
  l = latitude / _sec_rad; // latitude in radians

  e += 60.0 * l * tan(l + d); // latitudinal error
  d = tan(l) * tan(d); //

  if(d>1.0) return 86400UL;
  if(d < -1.0) return 0UL;

  d = acos(d);
  d /= _zenith;

  n = 86400UL * d;
  n += e;
  return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.

*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

  daylen = daylightseconds(*dt);
  if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
  if(daylen == 0UL) return -1; // there is no 'day' today

  *dt /= 86400UL;
  *dt *= 86400UL;
  *dt += 43200UL; // set the time stamp to 12:00:00 GMT

  *dt -= daylen / 2; //        sunrise at the prime meridian
  if(set) *dt += daylen; //     sunset at the prime meridian

  *dt -= equation_of_time(*dt);

  *dt -= longitude / 15.0; // rotate to our own meridian

  return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
  return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
  return SunRiseSet(when, 1);
}

Re: Got my controller,

Posted: Tue Sep 04, 2012 8:17 pm
by lnevo
Latest version... bugs fixed in Sunrise/Sunset. Still have some issues with Vortech controlling in feeding mode, etc. It doesn't change the DISPLAY_MODE when I go into feeding mode from my iphone app... and so the vortech never changes mode...but my skimmer goes off... not sure what's going on yet... Also need to try and use ONLY memory through the various modes because that's the only way I can easily change it from other places...

Code: Select all

// By Matthew Hockin 2012.  
// SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
// Epherma (SWFLTEK.com) was written by Michael Rice - thanks Michael it works great. 
// If you copy from this (its all open source) please acknowledge Michael for SWFLTEK library 
// code (obviously labeled) and Matthew Hockin (for the rest).

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

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>

//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//variables for SWFLTEK functions
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dom=0;//day of month

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
int userRiseOffset=-(9*3600)+472;
int userSetOffset=-(9*3600)+506;
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

//#define Constant      0
//#define Random1       1 // Lagoonal
//#define Random2       2 // Reef Crest
//#define ShortWave     3
//#define LongWave      4
//#define Smart_NTM     5 // Nutrient Transport Mode
//#define Smart_TSM     6 // Tidal Swell Mode
//#define Feeding_Start 7
//#define Feeding_Stop  8
//#define Night         9
//#define Slave_Start   97
//#define Slave_Stop    98
//#define None          99

//Vortech Mode Definitions
byte vtechmode=Smart_TSM;
byte vtechDefault=Smart_TSM;
byte vtechDuration=10;
byte vtechSpeed=40;
byte vtechNTMSpeed=65;
byte vtechNightSpeed=10;

//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define UPS                5
#define Heater             6
#define Refugium           7
#define Vortech            8

boolean bFeeding=false;
boolean isNight=false;

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

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";

// 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 MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}

void MenuEntry3()
{
  byte vtmode=vtechmode;
  
  vtmode++;
  if ( vtmode == 7 ) vtmode = 9;
  if ( vtmode > 9 ) vtmode = 0;
  vtechmode=vtmode;

  ReefAngel.RF.UseMemory=false;
  ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
  ReefAngel.RF.SetMode(vtmode,40,10);
  ReefAngel.Timer[1].SetInterval(1200); // Timer for 20min
  ReefAngel.Timer[1].Start();
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

// Custom Main Screen
void DrawCustomMain()
{
  char text[7];
  byte x = 5;
  byte y = 2;
  byte t;

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y=y+20; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y=y+10; 
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y=y+20; t=x;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  itoa(SunriseHour,text,10);
  if (SunriseHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  }
  itoa(SunriseMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=14;
  
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
  itoa(SunsetHour,text,10);
  if (SunsetHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunsetMin<10) { ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6; }
  itoa(SunsetMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);

  // MoonPhase
  y=y+10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y=y+10;  
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y=y+10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,t,y,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,t,y,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port2Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here

    // Convert lat/long to proper format
    latitude=ddToSeconds(latitude);
    longitude=ddToSeconds(longitude);
  
    // Reset Vortech to preferred defaults
    InternalMemory.RFMode_write(vtechDefault);
    InternalMemory.RFSpeed_write(vtechSpeed);
    InternalMemory.RFDuration_write(vtechDuration);
    ReefAngel.RF.UseMemory=true;
   
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer, 15 );
    ReefAngel.StandardHeater( Heater,783,788 );
    
    ////// Place your custom code below here
    
    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dom!=day()) // used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dom=day();
    } 

    // Send the date/time to the portal...
    ReefAngel.CustomVar[0]=SunriseHour;
    ReefAngel.CustomVar[1]=SunriseMin;
    ReefAngel.CustomVar[2]=SunsetHour;
    ReefAngel.CustomVar[3]=SunsetMin;

    ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
    ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );

    // Let's make the time one number so we can figure out it's night or day...
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin;
    int setTime=SunsetHour*100+SunsetMin;
        
    if (( currTime <= riseTime ) || ( currTime >= setTime )) isNight=true; else isNight=false;
    
    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode!=Night) { 
        ReefAngel.RF.SetMode(Night,vtechNightSpeed,0);
      }
    } else { 
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );
      
      // It's Daytime!
      if (ReefAngel.RF.UseMemory && ReefAngel.RF.Mode==Night) {
          ReefAngel.RF.SetMode(Feeding_Stop,0,0); //Temp fix for coming out of Night mode
          ReefAngel.RF.SetMode(vtechDefault,vtechNightSpeed,0);
      }
    }   

    if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
    
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,vtechNTMSpeed,5);
      ReefAngel.Timer[1].SetInterval(45*60); // Timer for 45 minutes
      ReefAngel.Timer[1].Start();
      vtechmode = Smart_NTM;
    }
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[1].IsTriggered()) 
    {
      ReefAngel.RF.UseMemory=true;
    }

    if (ReefAngel.RF.UseMemory) { 
      vtechmode=ReefAngel.RF.Mode;
    }
    
    ////// Place your custom code above here

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

// Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
long hours, minutes, seconds;//store current elapsed local hours as total seconds from midnight
long newDay; 

   //Serial.println("CalSun Run Now");
    time_t t=now(); //store current clock time to parse
    hours=hour(t);
    hours=(hours*3600); //current hour number 0-23 as seconds
    minutes=minute(t);
    minutes=(minutes*60); //minutes of current hour as seconds
    seconds=second(t);
   
    newDay=now();
    newDay-=(hours+minutes+seconds); //Convert current local unix epoch time to local unix epoch time of midnight
    newDay-=SECS_YR_2000; // Convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today (+946684800)

    //set value to send to SunRise as midnight GMT in seconds from Y2K
    rise=newDay;
    set=newDay;
    
    // Calculate rise time and set time using Epherma Library functions (see end of code) 
    SunRise(&rise);
    SunSet(&set);

    // Bring us back to real time.
    rise+=SECS_YR_2000;
    set+=SECS_YR_2000;
    newDay+=SECS_YR_2000;
    
    // Set the invididual variables and add userOffsets.
    SunriseHour=hour(rise+userRiseOffset);
    SunriseMin=minute(rise+userRiseOffset);
    SunsetHour=hour(set+userSetOffset);
    SunsetMin=minute(set+userSetOffset); 
} 

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
// The code below this is copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
// This code is being used freely with attribution to Micahel Rice in accord with his request
// A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
   return dd * 3600.0;
}

// Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s) {
long ret;

   ret = labs((long)d);
   ret = ret * 3600L + 60L * m + s;
   ret = (d<0L) ? -ret : ret;
   return ret;
}

/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt) {
double t;

   dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
   dt %= _tropical_year;
   t = dt;
   t /= _tropical_year;
   t *= 6.283185307179586;
   t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
   return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt) {
double y;

   dt %= _tropical_year;
   y = dt;
   y /= _tropical_year; // fractional year
   y *= 6.283185307179586;
   y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
   return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt) {
float l, d, e;
long n;

   d = -SolarDeclination(dt); // will be positive in Northern winter
   l = latitude / _sec_rad; // latitude in radians

   e += 60.0 * l * tan(l + d); // latitudinal error
   d = tan(l) * tan(d); //

   if(d>1.0) return 86400UL;
   if(d < -1.0) return 0UL;

   d = acos(d);
   d /= _zenith;

   n = 86400UL * d;
   n += e;
   return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.
*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

   daylen = daylightseconds(*dt);
   if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
   if(daylen == 0UL) return -1; // there is no 'day' today

   *dt /= 86400UL;
   *dt *= 86400UL;
   *dt += 43200UL; // set the time stamp to 12:00:00 GMT

   *dt -= daylen / 2; // sunrise at the prime meridian
   if(set) *dt += daylen; // sunset at the prime meridian

   *dt -= equation_of_time(*dt);

   *dt -= longitude / 15.0; // rotate to our own meridian

   return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
    return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
    return SunRiseSet(when, 1);
}

Re: Got my controller,

Posted: Sat Sep 08, 2012 12:42 pm
by lnevo
OK. Finally after an almost all nighter of attempting to fix some bugs. I've converted all the operations in the RF mode to use InternalMemory. This means that I can change from portal, iPhone (if it didn't crash when I try to save the memory..) or from the Menu. I have full control from the menu with custom durations and speeds for each mode. I'm also displaying the speed/duration on the main screen as well. All the Sunrise/Sunset code is working good. No more issues there. Only thing long term left is to get more temp probes and hook up the float switches :)

The only outstanding issues are that when in night mode, it does not kick out to feeding mode. Also, sometimes when going into feeding mode, the LCD does not clear completely even though i added a flag to return and clearscreen in my CustomMain. Would love some thoughts on these two issues. In the meantime... here's the current code.

Code: Select all

// By Matthew Hockin 2012.  
// SWFLTEK library functions, #includes, and #define were copied directly from the Epherma library
// Epherma (SWFLTEK.com) was written by Michael Rice - thanks Michael it works great. 
// If you copy from this (its all open source) please acknowledge Michael for SWFLTEK library 
// code (obviously labeled) and Matthew Hockin (for the rest).

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

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "ATO Clear";
prog_char menu5_label[] PROGMEM = "Overheat Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";

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

//includes for SWFLTEK functions
#include <stdlib.h>
#include <math.h>

//defines for SWFLTEK functions
#define _sec_rad 206264.806247096370813 // arc-seconds per radian
#define _tilt 0.409092804222329 // axial tilt of earth at epoch, in radians
#define _tropical_year 31556925 // tropical year in seconds... rounding error accumulates to 26 seconds by the year 2136
#define _zenith 3.11250383272322 // 'zenith' of rising (setting) sun in radians (360 - 2 * 90.833 degrees)

//variables for SWFLTEK functions
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;
byte dom=0;//day of month

//*******************GLOBAL VARIABLE DECLERATIONS FOR MHOCKIN Weather package*************************************
long latitude=-18.2861; // Great Barrier Reef, Australia
long longitude=147.7000; // Great Barrier Reef, Australia
int userRiseOffset=-(9*3600)+472;
int userSetOffset=-(9*3600)+506;
//*******************END HEADER FOR MHOCKIN Weather package*******************************************************

// Vortech Defaults
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;

byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;

byte vtPrevMode;
byte vtPrevSpeed;
byte vtPrevDuration;

boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;

//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define UPS                5
#define Heater             6
#define Refugium           7
#define Vortech            8

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

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port2Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here

    // Convert lat/long to proper format
    latitude=ddToSeconds(latitude);
    longitude=ddToSeconds(longitude);
  
    // Reset Vortech to preferred defaults
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
    
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer, 20 );
    ReefAngel.StandardHeater( Heater,778,782 );
    
    ////// Place your custom code below here

    // Set Sunrise and Sunset from MHOCKIN Weather package functions. 
    if (dom!=day()) // Used to see that were in a new day and need to recalculate sunrise and sunset
    {
      CalSun();
      dom=day();
      
      // Send the date/time to the portal
      ReefAngel.CustomVar[0]=SunriseHour;
      ReefAngel.CustomVar[1]=SunriseMin;
      ReefAngel.CustomVar[2]=SunsetHour;
      ReefAngel.CustomVar[3]=SunsetMin;
    } 
    
    ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
    ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );
    
    // Let's make the time one number so we can figure out it's night or day...
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin; 
    int setTime=SunsetHour*100+SunsetMin;
    
    if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;

    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (vtOverride==false && isFeeding==false) {
        setRFmode(Night,vtNightSpeed,vtNightDuration);
      }
    } else { 
      // It's Daytime!
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
     
      ReefAngel.Relay.Off( Refugium );
      
      if (vtOverride==false && vtMode==Night) {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
    if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
 
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
    {
      isFeeding=false;
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
    }

    if (vtOverride && ReefAngel.Timer[1].IsTriggered()) 
    {
      vtOverride=false;
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
    } else {
      setRFmode(); // Update if set externally
    }

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

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

// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {

  // Check if mode has changed
  if (mode!=vtMode) {  
    vtPrevMode=vtMode;
    vtMode=mode; 
  
    if (mode!=InternalMemory.RFMode_read()) { 
      InternalMemory.RFMode_write(mode);
    }
    
    // Temp fix for coming out of night mode
    if (vtPrevMode==Night) {
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Feeding_Stop,0,0);
      ReefAngel.RF.UseMemory=true;
    }
    
    if (isNight || vtMode==Smart_NTM) {
      setRFtimer(30);
    }
  } 

  // Check if speed has changed
  if (speed!=vtSpeed) {  
    vtPrevSpeed=vtSpeed;
    vtSpeed=speed;
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    } 
  }

  // Check if duration has changed
  if (duration!=vtDuration) {  
    vtPrevDuration=vtDuration;
    vtDuration=duration; 
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    }
  }
}
void setRFmode() {
  setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
  ReefAngel.Timer[1].SetInterval(minutes*60);
  ReefAngel.Timer[1].Start();
  vtOverride=true;
}

// Menu Code
void MenuEntry1()
{
  ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
  byte mode, speed,duration;
  mode=vtMode;
  mode++;
  
  if (mode > 9 ) { 
    mode=0; 
    speed=50; duration=0; // Constant
  } else if (mode == 1) { 
    speed=40; duration=0; // Lagoon
  } else if (mode == 2) { 
    speed=45; duration=0; // Reef Crest
  } else if (mode == 3) {  
    speed=50; duration=10; // Short Pulse
  } else if (mode == 4) {
    speed=50; duration=20; // Long Pulse
  } else if (mode == 6) {
    speed=45; duration=10; // Smart_TSM
  } else if (mode == 5) {
    speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
  } else if (mode == 7) {
    speed=vtNightSpeed; duration=vtNightDuration; // Night
    mode=9; 
  }  

  setRFmode(mode,speed,duration);
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry5()
{
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

// Custom Main Screen
void DrawCustomMain()
{
  char text[7];
  byte x = 5;
  byte y = 2;
  byte t;

  if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }


  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y+=20; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y+=10; 
  ConvertNumToString(text, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, text, Num8x16);
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, text, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y+=20; t=x;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  itoa(SunriseHour,text,10);
  if (SunriseHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunriseMin<10) {
      ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
  }
  itoa(SunriseMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=14;
  
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=24;
  itoa(SunsetHour,text,10);
  if (SunsetHour<10) { 
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6;
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=6;
  } else {
    ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text); t+=12;
  }
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,":"); t+=6;
  if (SunsetMin<10) { ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,"0"); t+=6; }
  itoa(SunsetMin,text,10);
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,text);

  // MoonPhase
  y+=10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y+=10;  
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
  else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
  else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}

// Start of sunrise, sunset and cloud calculations- runs on reset and once a day thereafter.
void CalSun(){
unsigned long rise;//time in seconds from the year 2000 (GMT) for sunrise
unsigned long set;//time in seconds from the year 2000 (GMT) for sunrise
long hours, minutes, seconds;//store current elapsed local hours as total seconds from midnight
long newDay; 

    time_t t=now(); //store current clock time to parse
    hours=hour(t);
    hours=(hours*3600); //current hour number 0-23 as seconds
    minutes=minute(t);
    minutes=(minutes*60); //minutes of current hour as seconds
    seconds=second(t);
   
    newDay=now();
    newDay-=(hours+minutes+seconds); //Convert current local unix epoch time to local unix epoch time of midnight
    newDay-=SECS_YR_2000; // Convert GMT unix Epoch to seconds elasped since 2000 for GMT midnight of today (+946684800)

    //set value to send to SunRise as midnight GMT in seconds from Y2K
    rise=newDay;
    set=newDay;
    
    // Calculate rise time and set time using Epherma Library functions (see end of code) 
    SunRise(&rise);
    SunSet(&set);

    // Bring us back to real time.
    rise+=SECS_YR_2000;
    set+=SECS_YR_2000;
    newDay+=SECS_YR_2000;
    
    // Set the invididual variables and add userOffsets.
    SunriseHour=hour(rise+userRiseOffset);
    SunriseMin=minute(rise+userRiseOffset);
    SunsetHour=hour(set+userSetOffset);
    SunsetMin=minute(set+userSetOffset); 
} 

//********************** DO NOT MESS WITH THIS UNLESS YOU KNOW WHAT YOUR DOING****************************
// The code below this is copied directly from the SWFLTEK Epherma library constructed by Michael Rice.  
// This code is being used freely with attribution to Micahel Rice in accord with his request
// A big thank you for these library functions.  Its great! 

// decimal degrees
long ddToSeconds(float dd){
   return dd * 3600.0;
}

// Degrees, minutes, seconds
long dmsToSeconds(int d, unsigned char m, unsigned char s) {
long ret;

   ret = labs((long)d);
   ret = ret * 3600L + 60L * m + s;
   ret = (d<0L) ? -ret : ret;
   return ret;
}

/* ------------------------------------------------------------------------------------------------
   'Equation of Time'
   We use the 'short form equation, which has a theoretical accuracy of about 40 seconds.
   The returned value is in seconds.
*/
int equation_of_time(unsigned long dt) {
double t;

   dt -= 192540UL; // refer to Jan 3 2000 05:29 (first periapsis)
   dt %= _tropical_year;
   t = dt;
   t /= _tropical_year;
   t *= 6.283185307179586;
   t = -459.27672 * sin(t) + 575.333472 * sin(2.0 * t + 3.588414);
   return t;
}

/* -----------------------------------------------------------------------------------------------
   'Solar Declination'
   Returns declination in radians
   Accurate to within 50 arc-seconds
*/
double SolarDeclination(unsigned long dt) {
double y;

   dt %= _tropical_year;
   y = dt;
   y /= _tropical_year; // fractional year
   y *= 6.283185307179586;
   y=0.006918-0.399912*cos(y)+0.070257*sin(y)-0.006758*cos(y*2)+0.000907*sin(y*2)-0.002697*cos(y*3)+0.00148*sin(y*3);
   return y;
}

/* ------------------------------------------------------------------------------------------------
   Return the period between sunrise and sunset, in seconds.
   At high latitudes around the time of the solstices, this could be zero, or all day.
*/
unsigned long daylightseconds(unsigned long dt) {
float l, d, e;
long n;

   d = -SolarDeclination(dt); // will be positive in Northern winter
   l = latitude / _sec_rad; // latitude in radians

   e += 60.0 * l * tan(l + d); // latitudinal error
   d = tan(l) * tan(d); //

   if(d>1.0) return 86400UL;
   if(d < -1.0) return 0UL;

   d = acos(d);
   d /= _zenith;

   n = 86400UL * d;
   n += e;
   return n;
}


/* ------------------------------------------------------------------------------------------------
   Modify the passed time stamp to the time of sunrise (or sunset if 'set' is non-zero).
   Returns 0 to signal 'normal' completion. If the position is in a polar circle, 1 will be
   returned if the sun is above the horizon all day, and -1 if the sun is below the horizon
   all day.
*/
char SunRiseSet(unsigned long * dt, char set){
unsigned long daylen;

   daylen = daylightseconds(*dt);
   if(daylen == 86400UL) return 1;   // there is no 'night' today (midnight sun)
   if(daylen == 0UL) return -1; // there is no 'day' today

   *dt /= 86400UL;
   *dt *= 86400UL;
   *dt += 43200UL; // set the time stamp to 12:00:00 GMT

   *dt -= daylen / 2; // sunrise at the prime meridian
   if(set) *dt += daylen; // sunset at the prime meridian

   *dt -= equation_of_time(*dt);

   *dt -= longitude / 15.0; // rotate to our own meridian

   return 0;
}

// 'short' forms of SunRiseSet
char SunRise(unsigned long* when){
    return SunRiseSet(when, 0);
}
char SunSet(unsigned long* when){
    return SunRiseSet(when, 1);
}

Re: Got my controller,

Posted: Sat Sep 08, 2012 4:01 pm
by rimai
I think the screen clear issue is only when you use app, right?

Got my controller,

Posted: Sat Sep 08, 2012 4:04 pm
by lnevo
Yes only with the app. It does like half the screen..

Re: Got my controller,

Posted: Sat Sep 08, 2012 4:05 pm
by rimai

Got my controller,

Posted: Sat Sep 08, 2012 4:16 pm
by lnevo
Yes only with the app. It does like half the screen..

Re: Got my controller,

Posted: Fri Sep 21, 2012 9:46 pm
by lnevo
Ok, have some bug fixes and some cleanup. My Vortech's were going in and out of night mode all night because of how I was setting the timer. I hope this resolves it. I also implemented the SunLocation class that binder put together from the code I scraped together. Deails on that class are located on this post (with fixes) http://forum.reefangel.com/viewtopic.php?p=14508#p14508

I have one issue still.. I'm trying to add a menu option to toggle the refugium light.. not working as I'd hope. If there is a simple way to do it, I'd be grateful :)

Thanks and Enjoy

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

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";

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

// Vortech Defaults
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;

byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;

byte vtPrevMode;
byte vtPrevSpeed;
byte vtPrevDuration;

boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;

SunLocation sl;
byte SunriseHour, SunriseMin, SunsetHour, SunsetMin;

//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define UPS                5
#define Heater             6
#define Refugium           7
#define Vortech            8

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

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here

    // Setup SunLocation class
    sl.Init(-18.285833, 147.699722);
    sl.SetOffset(-9,472,-9,506);
    
    // Save previous used vortech mode.
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
    
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer, 20 );
    ReefAngel.StandardHeater( Heater,778,782 );
    
    ////// Place your custom code below here
    sl.CheckAndUpdate();
    
    SunriseHour=sl.GetRiseHour(); SunriseMin=sl.GetRiseMinute();
    SunsetHour=sl.GetSetHour(); SunsetMin=sl.GetSetMinute();
    
    // Send the SunLocation info to the portal
    ReefAngel.CustomVar[0]=SunriseHour; ReefAngel.CustomVar[1]=SunriseMin;
    ReefAngel.CustomVar[2]=SunsetHour; ReefAngel.CustomVar[3]=SunsetMin;

    // Turn the lights on
    ReefAngel.StandardLights( WhiteLEDs,SunriseHour+1,SunriseMin,SunsetHour-3, SunsetMin );
    ReefAngel.StandardLights( BlueLEDs,SunriseHour,SunriseMin,SunsetHour,SunsetMin );

    // Let's make the time one number so we can figure out it's night or day...
    int currTime = hour()*100+minute();
    int riseTime=SunriseHour*100+SunriseMin; 
    int setTime=SunsetHour*100+SunsetMin;
    
    if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;

    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (vtOverride==false && isFeeding==false) {
        setRFmode(Night,vtNightSpeed,vtNightDuration);
      }
    } else { 
      // It's Daytime!
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
     
      ReefAngel.Relay.Off( Refugium );
      
      if (vtOverride==false && vtMode==Night) {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
    if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
 
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
    {
      isFeeding=false;
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
    }

    if (vtOverride && ReefAngel.Timer[1].IsTriggered()) 
    {
      vtOverride=false;
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
    } else {
      setRFmode(); // Update if set externally
    }

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

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

// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {

  // Check if mode has changed
  if (mode!=vtMode) {  
    vtPrevMode=vtMode;
    vtMode=mode; 
  
    if (mode!=InternalMemory.RFMode_read()) { 
      InternalMemory.RFMode_write(mode);
    }
    
    // Temp fix for coming out of night mode
    if (vtPrevMode==Night) {
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Feeding_Stop,0,0);
      ReefAngel.RF.UseMemory=true;
    }
    
    if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
      setRFtimer(30);
    }
  } 

  // Check if speed has changed
  if (speed!=vtSpeed) {  
    vtPrevSpeed=vtSpeed;
    vtSpeed=speed;
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    } 
  }

  // Check if duration has changed
  if (duration!=vtDuration) {  
    vtPrevDuration=vtDuration;
    vtDuration=duration; 
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    }
  }
}
void setRFmode() {
  setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
  ReefAngel.Timer[1].SetInterval(minutes*60);
  ReefAngel.Timer[1].Start();
  vtOverride=true;
}

// Menu Code
void MenuEntry1()
{
  ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
  byte mode, speed,duration;
  mode=vtMode;
  mode++;
  
  if (mode > 9 ) { 
    mode=0; 
    speed=50; duration=0; // Constant
  } else if (mode == 1) { 
    speed=40; duration=0; // Lagoon
  } else if (mode == 2) { 
    speed=45; duration=0; // Reef Crest
  } else if (mode == 3) {  
    speed=50; duration=10; // Short Pulse
  } else if (mode == 4) {
    speed=50; duration=20; // Long Pulse
  } else if (mode == 6) {
    speed=45; duration=10; // Smart_TSM
  } else if (mode == 5) {
    speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
  } else if (mode == 7) {
    speed=vtNightSpeed; duration=vtNightDuration; // Night
    mode=9; 
  }  

  setRFmode(mode,speed,duration);
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  ReefAngel.Relay.Toggle ( Refugium );
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

// Custom Main Screen
void DrawCustomMain()
{
  char buf[16];
  byte x = 5;
  byte y = 2;
  byte t;

  if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y+=18; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y+=8; 
  ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
  ConvertNumToString(buf, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, buf, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y+=20; t=x;
  sprintf(buf, "%02d:%02d", SunriseHour, SunriseMin);
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf); 
  sprintf(buf, "%02d:%02d", SunsetHour, SunsetMin); t+=36;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);

  // MoonPhase
  y+=10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y+=10;
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
  else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
  else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}

Re: Got my controller,

Posted: Sun Sep 23, 2012 2:20 pm
by rimai
lnevo wrote:Also, sometimes when going into feeding mode, the LCD does not clear completely even though i added a flag to return and clearscreen in my CustomMain.
Can you help me replicate this issue?
I was unable to do so...
I'd like to fix in the libraries, but I can't get it to do that.

Re: Got my controller,

Posted: Sun Sep 23, 2012 3:31 pm
by lnevo
I'll take a few more peaks the next few feedings and see if I'm still experiencing it. If you have any troubleshooting you'd like me to try... You could also run my code (but now you'll need the SunLocation class :) ) as a test...if I'm just crazy...

Re: Got my controller,

Posted: Sun Oct 21, 2012 5:18 pm
by lnevo
Modified my code to use heater temp settings from memory so I can modify without changing code. I also added a function to the SunLocation class so I can have my lighting times controlled by memory. I'll be posting that separately. I also added my float switches and set the Low to turn off my pump / skimmer when the water level in the return chamber gets too low (drain problem, ato problem?). The high I am using to send me an alert if something happens and the sump gets too full (return pump issue, return line obstruction...) Next up waiting for my water level sensor and I'll be able to monitor my ATO reservoir.

Edit: Needed to add a flag instead of just checking the mode, otherwise it would never turn return pumps back on after water change / feeding mode... The flag is used if triggered, mode change, or controller restart and is reset once the HighATO.IsActive function comes up false. This way it's only disabled once and not when we trigger it on purpose.

Here's the 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 <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 <ReefAngel.h>
#include <SunLocation.h>

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO Clear";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "Date / Time";
prog_char menu8_label[] PROGMEM = "Version";

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

// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;

byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;

byte vtNTMSpeed=55;
byte vtNTMDuration=5;
byte vtNightSpeed=10;
byte vtNightDuration=20;

boolean isNight=false;
boolean isFeeding=false;
boolean vtOverride=false;
boolean activatedHighATO=true;

SunLocation sl;

//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define UPS                5
#define Heater             6
#define Refugium           7
#define Vortech            8

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

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech );
    ReefAngel.Relay.On( UPS );
    
    ////// Place additional initialization code below here

    // Setup SunLocation class
    sl.Init(-18.285833, 147.699722); // GBR
    sl.SetOffset(-9,472,-9,506); // 9 hour difference
    
    // Save previous used vortech mode.
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
        
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.Relay.DelayedOn( Skimmer, 5 );
    ReefAngel.StandardHeater( Heater );
    
    ////// Place your custom code below here
    sl.CheckAndUpdate();
        
    // Turn on the lights   
    ReefAngel.StandardLights( WhiteLEDs );
    ReefAngel.ActinicLights( BlueLEDs );

    // Let's make the time one number so we can figure out it's night or day...
    int currTime=NumMins(hour(),minute());
    int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute()); 
    int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
    
    if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;

    if (isNight) 
    {
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
      ReefAngel.Relay.On( Refugium );
      
      if (vtOverride==false && isFeeding==false) {
        setRFmode(Night,vtNightSpeed,vtNightDuration);
      }
      
      // Trigger night mode again to extend built-in timer
      if ( ((hour() == 6) && (minute() == 00) && (second() == 00) ) ) {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
      }
    } else { 
      // It's Daytime!
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
     
      ReefAngel.Relay.Off( Refugium );
      
      if (vtOverride==false && vtMode==Night) {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
        ReefAngel.Timer[1].ForceTrigger();
      }
    }   

    if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
    if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
    if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) activatedHighATO=true;
    
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
    {
      isFeeding=false; 
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
    } 
    else if (vtOverride && ReefAngel.Timer[1].IsTriggered()) 
    {
      vtOverride=false;
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
    } 
    else 
    {
      setRFmode(); // Update if set externally
    }

    if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
    }
    
    if (ReefAngel.LowATO.IsActive()) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
      bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
    } 
 
    if (ReefAngel.HighATO.IsActive()) {
      if (!activatedHighATO) {
        activatedHighATO=true;
        ReefAngel.CustomVar[0]=2;
        bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
        bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);  
      }  
    } else {
      ReefAngel.CustomVar[0]=1;
      activatedHighATO=false;
    }  
      
    // Turn off Skimmer if Return pump is shutoff.   
    if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
    }
    
    ////// Place your custom code above here

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

// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {

  // Check if mode has changed
  if (mode!=vtMode) {  
    vtPrevMode=vtMode;
    vtMode=mode; 
  
    if (mode!=InternalMemory.RFMode_read()) { 
      InternalMemory.RFMode_write(mode);
    }
    
    // Temp fix for coming out of night mode
    if (vtPrevMode==Night) {
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Feeding_Stop,0,0);
      ReefAngel.RF.UseMemory=true;
    }
    
    if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
      setRFtimer(30);
    }
  } 

  // Check if speed has changed
  if (speed!=vtSpeed) {  
    vtPrevSpeed=vtSpeed;
    vtSpeed=speed;
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    } 
  }

  // Check if duration has changed
  if (duration!=vtDuration) {  
    vtPrevDuration=vtDuration;
    vtDuration=duration; 
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    }
  }
}
void setRFmode() {
  setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
  ReefAngel.Timer[1].SetInterval(minutes*60);
  ReefAngel.Timer[1].Start();
  vtOverride=true;
}

// Menu Code
void MenuEntry1()
{
  ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
  byte mode,speed,duration,prev_mode,prev_speed,prev_dur;
  mode=vtMode;
  mode++;
  
  if (mode > 9 ) { 
    mode=0; 
    speed=50; duration=0; // Constant
  } else if (mode == 1) { 
    speed=40; duration=0; // Lagoon
  } else if (mode == 2) { 
    speed=45; duration=0; // Reef Crest
  } else if (mode == 3) {  
    speed=50; duration=10; // Short Pulse
  } else if (mode == 4) {
    speed=50; duration=20; // Long Pulse
  } else if (mode == 6) {
    speed=45; duration=10; // Smart_TSM
  } else if (mode == 5) {
    speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
  } else if (mode == 7) {
    speed=vtNightSpeed; duration=vtNightDuration; // Night
    mode=9; 
  }  

  prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
  setRFmode(mode,speed,duration);
  
  if (!isNight) {
    vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
  }
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  if (isNight) {
    if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    }
  } else { 
    if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    }
  }
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.DisplayVersion();
}

// Custom Main Screen
void DrawCustomMain()
{
  char buf[16];
  byte x = 5;
  byte y = 2;
  byte t;

  if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }

  // Main Header
  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);

  // Param Header
  y+=18; 
  ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60, y, "PH:");

  // Temp and PH
  y+=8; 
  ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
  ConvertNumToString(buf, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+60, y, buf, Num8x16);
  pingSerial();
  
  /// Display Sunrise / Sunset (to be calculated later...)
  y+=20; t=x;
  sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf); 
  sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);

  // MoonPhase
  y+=10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y+=10;
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Vortech Mode
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
  else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
  else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
  pingSerial();
  
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 103, TempRelay);
  pingSerial();
  
  // Date+Time
  ReefAngel.LCD.DrawDate(6, 122);
  pingSerial();
}

void DrawCustomGraph()
{
}

Re: Got my controller,

Posted: Wed Dec 26, 2012 8:40 am
by lnevo
Added my relay expansion yesterday. Lot's of new additions to the code. The following features have been added:

Vacation Mode - Use WL sensor and turn the extension cord I have plugged in, into an ATO port
ATO Refill - Use WL sensor to fill my ATO reservoir to 100%
Power Outage detection - Turn off all unnecessary items if we lose power.
Complete Darkness - Added 2 hours without moonlghts to my schedule
Smart_NTM Delay - Added a delay after feeding mode before Smart_NTM is activated
Dosing Pumps - Added these, but I need to get better pumps ;)
Coral Acclimation Schedule - Need to test this, but should be working good.

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

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "Refugium Light";
prog_char menu5_label[] PROGMEM = "Clear Overrides";
prog_char menu6_label[] PROGMEM = "PH Calibration";
prog_char menu7_label[] PROGMEM = "WLS Calibration";
prog_char menu8_label[] PROGMEM = "Date / Time";
prog_char menu9_label[] PROGMEM = "Version";

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

// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;

byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;

byte vtNTMSpeed=65;
byte vtNTMDuration=5;
byte vtNightSpeed=20;
byte vtNightDuration=10;


boolean isNight=false;
boolean isFeeding=false;
boolean feedDelay=false;
boolean vtOverride=false;
boolean floatHigh=true;
boolean powerOutage=true;

int riseOffset=472;
int setOffset=506;
byte acclTime=0;
SunLocation sl;

//Define Custom Memory Location
#define Mem_B_RefillATO   100
#define Mem_B_Vacation    101
#define Mem_B_AcclTime    102

//Define Relay Ports
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Extension          5
#define Heater             6
#define Refugium           7
#define Reactor            8

#define Unused1            Box1_Port1
#define Unused2            Box1_Port2
#define Vortech1           Box1_Port3
#define Vortech2           Box1_Port4
#define VortechUPS         Box1_Port5
#define Unused3            Box1_Port6
#define DPump1             Box1_Port7
#define DPump2             Box1_Port8


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

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port2Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port4Bit | Port7Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that are always on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech1 );
    ReefAngel.Relay.On( Vortech2 );
    ReefAngel.Relay.On( VortechUPS );
    ReefAngel.Relay.Off( Extension );
    ReefAngel.Relay.Off( Unused1 );
    ReefAngel.Relay.Off( Unused2 );
    ReefAngel.Relay.Off( Unused3 );
    
    ////// Place additional initialization code below here

    // Setup SunLocation class
    sl.Init(-18.285833, 147.699722); // GBR
    
    // Save previous used vortech mode.
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
    
    ReefAngel.CustomVar[7]=255;
    
    ////// Place additional initialization code above here
}

void loop()
{
    ReefAngel.StandardHeater( Heater );
    ReefAngel.DosingPumpRepeat1( DPump1 );
    ReefAngel.DosingPumpRepeat2( DPump2 );
    ReefAngel.StandardLights( WhiteLEDs );
    ReefAngel.ActinicLights( BlueLEDs );
    
    if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
    {
      powerOutage=false;
      LastStart=now();
      ReefAngel.CustomVar[1]=0;
    }
    ReefAngel.Relay.DelayedOn( Skimmer, 5 );
    ReefAngel.Relay.DelayedOn( Reactor, 10 );
    
    if (InternalMemory.read( Mem_B_Vacation )==1) {
      ReefAngel.WaterLevelATO( Extension,30,61,63 );
      ReefAngel.CustomVar[3]=1;  
    } else {
      ReefAngel.CustomVar[3]=0;
      ReefAngel.Relay.Off( Extension );
    }
    
    if (InternalMemory.read( Mem_B_RefillATO )==1) {
       if (ReefAngel.WaterLevel.GetLevel()<=100) {
         ReefAngel.Relay.On( Extension );
         ReefAngel.CustomVar[2]=1;
       } else {
         ReefAngel.Relay.Off( Extension );
         InternalMemory.write( Mem_B_RefillATO, 0);
         ReefAngel.CustomVar[2]=0;
       }
    }
    
    acclTime=InternalMemory.read( Mem_B_AcclTime );
    if (acclTime > 0) { 
      ReefAngel.CustomVar[4]=acclTime;
      if ( (hour()==0) && (minute()==0) && (second()==0)) {
        acclTime--;
        InternalMemory.write( Mem_B_AcclTime, acclTime );
      } 
    }
    
    ////// Place your custom code below here
    sl.SetOffset(-9,riseOffset+(acclTime*240),-9,setOffset+(acclTime*120)); // 9 hour difference
    sl.CheckAndUpdate();
            
    // Let's make the time one number so we can figure out it's night or day...
    int currTime=NumMins(hour(),minute());
    int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute()); 
    int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
    if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;
    
    if (isNight) 
    {
      if ((hour()>=3) && (hour()<=5)) {
        ReefAngel.PWM.SetDaylight( 0 );
        ReefAngel.PWM.SetActinic( 0 );
      } else {
        ReefAngel.PWM.SetDaylight( MoonPhase() );
        ReefAngel.PWM.SetActinic( MoonPhase() );
      }  
      
      if (vtOverride==false && isFeeding==false) {
        setRFmode(Night,vtNightSpeed,vtNightDuration);
      }
      
      ReefAngel.Relay.On( Refugium );
      
    } else { 
      // It's Daytime!
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
      ReefAngel.Relay.Off( Refugium );
      
      if (vtOverride==false && vtMode==Night) {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
        ReefAngel.Timer[1].ForceTrigger();
      }
    }   
    
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
    if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
    if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) floatHigh=true;
    
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding )
    {
      isFeeding=false; 
      feedDelay=true;
      setRFtimer(30); // Stay in FeedMode for a while longer.
    } 
    else if (vtOverride && ReefAngel.Timer[1].IsTriggered()) 
    {
      vtOverride=false;

      if(feedDelay) {
        feedDelay=false;
        setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
      } else {
        setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
      }
    } 
    else 
    {
      setRFmode(); // Update if set externally
    }

    if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
    }
    
    if (ReefAngel.LowATO.IsActive()) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
      bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
    } 
 
    if (ReefAngel.HighATO.IsActive()) {
      if (!floatHigh) {
        floatHigh=true;
        ReefAngel.CustomVar[0]=1;
        bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
        bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);  
      }  
    } else {
      ReefAngel.CustomVar[0]=0;
      floatHigh=false;
    }  
      
    // Turn off Skimmer if Return pump is shutoff.   
    if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
    }
    
    // Power Outage - Only Return Pump
    if (!ReefAngel.Relay.IsRelayPresent(EXP1_RELAY)) // Exp1 Relay NOT present
    {
      powerOutage=true;
      ReefAngel.Relay.Off (Skimmer); 
      ReefAngel.Relay.Off (WhiteLEDs); 
      ReefAngel.Relay.Off (BlueLEDs); 
      ReefAngel.Relay.Off (Extension);
      ReefAngel.Relay.Off (Heater);
      ReefAngel.Relay.Off (Refugium); 
      ReefAngel.Relay.Off (Reactor); 
      ReefAngel.CustomVar[1]=1;
    }
    ////// Place your custom code above here

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

// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {

  // Check if mode has changed
  if (mode!=vtMode) {  
    vtPrevMode=vtMode;
    vtMode=mode; 
  
    if (mode!=InternalMemory.RFMode_read()) { 
      InternalMemory.RFMode_write(mode);
    }
    
    // Temp fix for coming out of night mode
    if (vtPrevMode==Night) {
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Feeding_Stop,0,0);
      ReefAngel.RF.UseMemory=true;
    }
    
    if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
      setRFtimer(60);
    }
  } 

  // Check if speed has changed
  if (speed!=vtSpeed) {  
    vtPrevSpeed=vtSpeed;
    vtSpeed=speed;
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    } 
  }

  // Check if duration has changed
  if (duration!=vtDuration) {  
    vtPrevDuration=vtDuration;
    vtDuration=duration; 
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    }
  }
}
void setRFmode() {
  setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
  ReefAngel.Timer[1].SetInterval(minutes*60);
  ReefAngel.Timer[1].Start();
  vtOverride=true;
}

// Menu Code
void MenuEntry1()
{
  ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
  byte mode,speed,duration,prev_mode,prev_speed,prev_dur;
  mode=vtMode;
  mode++;
  
  prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
  
  if (mode > 9 ) { 
    mode=0; 
    speed=50; duration=0; // Constant
  } else if (mode == 1) { 
    speed=40; duration=0; // Lagoon
  } else if (mode == 2) { 
    speed=45; duration=0; // Reef Crest
  } else if (mode == 3) {  
    speed=55; duration=10; // Short Pulse
  } else if (mode == 4) {
    speed=55; duration=20; // Long Pulse
  } else if (mode == 6) {
    speed=50; duration=10; // Smart_TSM
  } else if (mode == 5) {
    speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
  } else if (mode == 7) {
    speed=vtNightSpeed; duration=vtNightDuration; // Night
    mode=9; 
  }  

  prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
  setRFmode(mode,speed,duration);
  
  if (!isNight) {
    vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
  }
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4()
{
  if (isNight) {
    if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    }
  } else { 
    if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    }
  }
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5()
{
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry6()
{
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
  ReefAngel.SetupCalibrateWaterLevel();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8()
{
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry9()
{
  ReefAngel.DisplayVersion();
}

// Custom Main Screen
void DrawCustomMain()
{
  char buf[16];
  byte x = 5;
  byte y = 2;
  byte t;

//  if (isFeeding) { ReefAngel.ClearScreen(DefaultBGColor); return; }

  // Main Header
//  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  
  // Date+Time
  ReefAngel.LCD.DrawDate(x+1, y);
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
  
  // Param Header
  y+=12; 
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
  // Temp and PH
  y+=2;
  ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
  ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
  y+=6; 
  ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
  ConvertNumToString(buf, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
  y+=5;
  ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
  ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
  pingSerial();
    
  /// Display Sunrise / Sunset (to be calculated later...)
  y+=15; t=x;
  sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf); 
  sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
  pingSerial();

  // MoonPhase
  y+=10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y+=10;
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Display Water level
  y+=10; t=x;
  ConvertNumToString(buf, ReefAngel.WaterLevel.GetLevel(), 1);
  strcat(buf,"  ");
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); t+=60;
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);

  // Vortech Mode
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
  else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
  else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
  pingSerial();
  
  y+=15;
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
  pingSerial();
  
  y+=12;
  TempRelay = ReefAngel.Relay.RelayDataE[0];
  TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
  TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
  ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
  pingSerial();
}

void DrawCustomGraph()
{
}

Re: Got my controller,

Posted: Thu Dec 27, 2012 6:29 pm
by lnevo
Bugs worked out with Coral Acclimation counter and a lot of comments / code cleanup

Code: Select all

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

////// Place global variable code below here
#define CUSTOM_MENU
#define CUSTOM_MENU_ENTRIES   8
#define NUMBERS_8x16

// Custom Menu Code
#include <avr/pgmspace.h>
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "Vortech Mode";
prog_char menu4_label[] PROGMEM = "Refugium Light";
prog_char menu5_label[] PROGMEM = "ATO Clear";
prog_char menu6_label[] PROGMEM = "Overheat Clear";
prog_char menu7_label[] PROGMEM = "PH Calibration";
prog_char menu8_label[] PROGMEM = "WLS Calibration";
prog_char menu9_label[] PROGMEM = "Date / Time";

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

// Vortech Defaults
byte vtPrevMode=0;
byte vtPrevSpeed=0;
byte vtPrevDuration=0;
// Default Mode
byte vtMode=Random2;
byte vtSpeed=45;
byte vtDuration=5;
// NTM mode
byte vtNTMSpeed=65;
byte vtNTMDuration=5;
// Night Mode
byte vtNightSpeed=20;
byte vtNightDuration=10;


boolean isNight=false;
boolean isFeeding=false;
boolean feedDelay=false;
boolean vtOverride=false;
boolean floatHigh=true;
boolean powerOutage=true;

byte acclTime=0;
SunLocation sl;

//Define Custom Memory Location
#define Mem_B_RefillATO   100
#define Mem_B_Vacation    101
#define Mem_B_AcclTime    102
#define Mem_B_ClearATO    103

//Define Relay Ports by Name
#define Return             1
#define Skimmer            2
#define WhiteLEDs          3
#define BlueLEDs           4
#define Extension          5
#define Heater             6
#define Refugium           7
#define Reactor            8

#define Unused1            Box1_Port1
#define Unused2            Box1_Port2
#define Vortech1           Box1_Port3
#define Vortech2           Box1_Port4
#define VortechUPS         Box1_Port5
#define Unused3            Box1_Port6
#define DPump1             Box1_Port7
#define DPump2             Box1_Port8


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

// Setup on controller startup/reset
void setup()
{
    // This must be the first line
    ReefAngel.Init();  //Initialize controller
    // Initialize Menu
    ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));

    // Ports toggled in Feeding Mode
    ReefAngel.FeedingModePorts = Port1Bit | Port2Bit | Port8Bit;
    // Ports toggled in Water Change Mode
    ReefAngel.WaterChangePorts = Port1Bit | Port2Bit | Port6Bit | Port8Bit;
    // Ports toggled when Lights On / Off menu entry selected
    ReefAngel.LightsOnPorts = Port3Bit | Port4Bit ;
    // Ports turned off when Overheat temperature exceeded
    ReefAngel.OverheatShutoffPorts = Port3Bit | Port4Bit | Port6Bit;
    // Use T1 probe as temperature and overheat functions
    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;
    // Set the Overheat temperature setting
    InternalMemory.OverheatTemp_write( 820 );

    // Ports that default on
    ReefAngel.Relay.On( Return );
    ReefAngel.Relay.On( Vortech1 );
    ReefAngel.Relay.On( Vortech2 );
    ReefAngel.Relay.On( VortechUPS );
    // Ports that default off
    ReefAngel.Relay.Off( Extension );
    ReefAngel.Relay.Off( Unused1 );
    ReefAngel.Relay.Off( Unused2 );
    ReefAngel.Relay.Off( Unused3 );
    
    ////// Place additional initialization code below here

    // Setup SunLocation class
    sl.Init(-18.285833, 147.699722); // Great Barrier Reef
    
    // What was our previous modes before we restarted?
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
    
    ReefAngel.CustomVar[7]=255;
    
    ////// Place additional initialization code above here
}

void loop()
{
  // Default port modes. Use Memory settings for external control
  ReefAngel.StandardHeater( Heater );
  ReefAngel.DosingPumpRepeat1( DPump1 );
  ReefAngel.DosingPumpRepeat2( DPump2 );
  ReefAngel.StandardLights( WhiteLEDs );
  ReefAngel.ActinicLights( BlueLEDs );
    
  ////// Place your custom code below here
    
  // See if power is back on so DelayedOn ports will reset.
  if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
  {
    powerOutage=false;
    LastStart=now();
    ReefAngel.CustomVar[1]=0;
  }
  ReefAngel.Relay.DelayedOn( Skimmer ); 
  ReefAngel.Relay.DelayedOn( Reactor ); 

  // We're on vacation! Use external pump to top off the top off.    
  if (InternalMemory.read( Mem_B_Vacation )==1) {
    ReefAngel.WaterLevelATO( Extension,30,61,63 );
    ReefAngel.CustomVar[3]=1;  
  } else {
    ReefAngel.CustomVar[3]=0;
    ReefAngel.Relay.Off( Extension );
  }
  
  // Real ATO Clear
  if (InternalMemory.read( Mem_B_ClearATO )==1) {
    ReefAngel.ATOClear();
  }  
  // ATO Refill mode. Top off ATO reservoir until it's at 100%    
  if (InternalMemory.read( Mem_B_RefillATO )==1) {
     if (ReefAngel.WaterLevel.GetLevel()<=100) {
       ReefAngel.Relay.On( Extension );
       ReefAngel.CustomVar[2]=1;
     } else {
       ReefAngel.Relay.Off( Extension );
       InternalMemory.write( Mem_B_RefillATO, 0);
       ReefAngel.CustomVar[2]=0;
     }
  }
    
  // See if we are acclimating corals and decrement the countdown each night
  static boolean acclCounterReady=false;
  if ( acclCounterReady && now()%86400==0) {
    acclTime--;
    acclCounterReady=false;
    InternalMemory.write( Mem_B_AcclTime, acclTime );
  } 
  if ( now()%86400!=0 ) acclCounterReady=true;
    
  // -9 hour difference for time zone. 472/506 seconds were calculation corrections
  // The acclTime will adjust the sunrise/sunset if we are adjusting for new coral
  sl.SetOffset(-9,472+(acclTime*240),-9,506-(acclTime*120)); 
  // Calculate the new Sunrise / Sunset based on our GPS coordinates
  sl.CheckAndUpdate();
            
  // Let's make the time one number so we can figure out it's night or day...
  int currTime=NumMins(hour(),minute());
  int riseTime=NumMins(sl.GetRiseHour(),sl.GetRiseMinute()); 
  int setTime=NumMins(sl.GetSetHour(),sl.GetSetMinute());
  if ( (currTime <= riseTime) || (currTime >= setTime) ) isNight=true; else isNight=false;
    
  if (isNight) 
  {
    if ((hour()>=3) && (hour()<=5)) {
      // Some complete darkness
      ReefAngel.PWM.SetDaylight( 0 );
      ReefAngel.PWM.SetActinic( 0 );
    } else {
      // Set moonlights to the MoonPhase
      ReefAngel.PWM.SetDaylight( MoonPhase() );
      ReefAngel.PWM.SetActinic( MoonPhase() );
    }  
      
    // Vortech's to night mode.
    if (vtOverride==false && isFeeding==false) {
      setRFmode(Night,vtNightSpeed,vtNightDuration);
    }
    
    // Turn on refugium light opposite normal lights
    ReefAngel.Relay.On( Refugium );
  
  } else { // It's Daytime!
    // Regular lights are controlled by Memory variables set in SunLocation class
    ReefAngel.PWM.SetDaylight( 0 );
    ReefAngel.PWM.SetActinic( 0 );
    ReefAngel.Relay.Off( Refugium );
      
    // Go back to our regularly scheduled program.
    if (vtOverride==false && vtMode==Night) {
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
      // Force the trigger to make sure we break feeding/night modes.
      ReefAngel.Timer[1].ForceTrigger();
    }
  }   
    
  // Enable Feeding Mode flag
  if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
  // Turn on Refugium light during water change mode
  if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) ReefAngel.Relay.On( Refugium );
  // Enable ATOHigh flag on purpose during feed/water change mode so we don't get alerts.
  if (ReefAngel.DisplayedMenu==FEEDING_MODE || ReefAngel.DisplayedMenu==WATERCHANGE_MODE) floatHigh=true;
    
  /*** RF Modes ***/
  // Here's what we do if we're just out of feeding mode...
  if (ReefAngel.DisplayedMenu==DEFAULT_MENU && isFeeding ) {
    isFeeding=false; 
    feedDelay=true; // This will let us know we want some extra time before Smart_NTM
    setRFtimer(30); // Start Smart_NTM in 30 minutes...
  } else if (vtOverride && ReefAngel.Timer[1].IsTriggered()) { // Our RF timer is over. 
    vtOverride=false; // Stop overriding the default RF mode

    // First let's deal with that extra 30 minutes
    if(feedDelay) {
      feedDelay=false; // Reset the feedDelay flag
      setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration); // Smart_NTM time!
    } else {
      // Otherwise go to Previous settings
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration); 
    }
  } else   {
    setRFmode(); // Update the mode if we change it remotely
  }
    
  // A little extra Smart_NTM never hurt anyone
  if ( ((hour() == 15) && (minute() == 00) && (second() == 00) ) ) {
    setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
  }
  /*** End of RF modes ***/
    
  // Turn off return pump if we run out of water!
  if (ReefAngel.LowATO.IsActive()) {
    bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
  } 
 
  // Turn off return if we are somehow overflowing the sump
  if (ReefAngel.HighATO.IsActive()) {
    if (!floatHigh) {
      floatHigh=true;
      ReefAngel.CustomVar[0]=1;
      bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
    }  
  } else {
    ReefAngel.CustomVar[0]=0;
    floatHigh=false;
  }  
      
  // Turn off Skimmer if Return pump is shutoff.   
  if (bitRead(ReefAngel.Relay.RelayMaskOff,Return-1)==0) {
    bitClear(ReefAngel.Relay.RelayMaskOff,Skimmer-1);
  }
    
  // Power Outage - Only Return Pump should be active
  if (!ReefAngel.Relay.IsRelayPresent(EXP1_RELAY)) // Expansion Relay NOT present
  {
    powerOutage=true;
    ReefAngel.Relay.Off (Skimmer); 
    ReefAngel.Relay.Off (WhiteLEDs); 
    ReefAngel.Relay.Off (BlueLEDs); 
    ReefAngel.Relay.Off (Extension);
    ReefAngel.Relay.Off (Heater);
    ReefAngel.Relay.Off (Refugium); 
    ReefAngel.Relay.Off (Reactor); 
    ReefAngel.CustomVar[1]=1;
  }
    
  ////// Place your custom code above here

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

// Vortech Helper functions
void setRFmode(int mode, int speed, int duration) {

  // Check if mode has changed
  if (mode!=vtMode) {  
    vtPrevMode=vtMode;
    vtMode=mode; 
  
    if (mode!=InternalMemory.RFMode_read()) { 
      InternalMemory.RFMode_write(mode); 
    }
    
    // Fix for coming out of night mode
    if (vtPrevMode==Night) {
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Feeding_Stop,0,0);
      ReefAngel.RF.UseMemory=true;
    }
    
    // If it's at night or we are setting NTM, do this only temporarily
    if ( (isNight && vtMode != Night) || vtMode==Smart_NTM) {
      setRFtimer(60);
    }
  } 

  // Check if speed has changed
  if (speed!=vtSpeed) {  
    vtPrevSpeed=vtSpeed;
    vtSpeed=speed;
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    } 
  }

  // Check if duration has changed
  if (duration!=vtDuration) {  
    vtPrevDuration=vtDuration;
    vtDuration=duration; 
    
    if (speed!=InternalMemory.RFSpeed_read()) {
      InternalMemory.RFSpeed_write(speed);
    }
  }
}
void setRFmode() {
  setRFmode(InternalMemory.RFMode_read(), InternalMemory.RFSpeed_read(), InternalMemory.RFDuration_read());
}
void setRFtimer(int minutes) {
  ReefAngel.Timer[1].SetInterval(minutes*60);
  ReefAngel.Timer[1].Start();
  vtOverride=true;
}

// Menu Code
void MenuEntry1() {
  ReefAngel.FeedingModeStart();
}
void MenuEntry2() {
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3() {
  byte mode,speed,duration;
  byte prev_mode,prev_speed,prev_dur;
  
  mode=vtMode;
  mode++;
  
  prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
  
  if (mode > 9 ) { 
    mode=0; 
    speed=50; duration=0; // Constant
  } else if (mode == 1) { 
    speed=40; duration=0; // Lagoon
  } else if (mode == 2) { 
    speed=45; duration=0; // Reef Crest
  } else if (mode == 3) {  
    speed=55; duration=10; // Short Pulse
  } else if (mode == 4) {
    speed=55; duration=20; // Long Pulse
  } else if (mode == 6) {
    speed=50; duration=10; // Smart_TSM
  } else if (mode == 5) {
    speed=vtNTMSpeed; duration=vtNTMDuration; // Smart_NTM
  } else if (mode == 7) {
    speed=vtNightSpeed; duration=vtNightDuration; // Night
    mode=9; 
  }  

  prev_mode=vtPrevMode; prev_speed=vtPrevSpeed; prev_dur=vtPrevDuration;
  setRFmode(mode,speed,duration);
  
  // If it's night time, don't overwrite the default daytime mode when using the menu
  if (!isNight) {
    vtPrevMode=prev_mode; vtPrevSpeed=prev_speed; vtPrevDuration=prev_dur;
  }
  
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4() {
  if (isNight) { // Turn off the Refugium light
    if (bitRead(ReefAngel.Relay.RelayMaskOff,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOff,Refugium-1);
    }
  } else { // Turn on the Refugium light
    if (bitRead(ReefAngel.Relay.RelayMaskOn,Refugium-1)==1) {
      bitClear(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    } else {
      bitSet(ReefAngel.Relay.RelayMaskOn,Refugium-1);
    }
  }
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;
}
void MenuEntry5() {
  ReefAngel.ATOClear();
  ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry6() {
  ReefAngel.OverheatClear();
  ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry7() {
  ReefAngel.SetupCalibratePH();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry8() {
  ReefAngel.SetupCalibrateWaterLevel();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry9() {
  ReefAngel.SetupDateTime();
  ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}

// Custom Main Screen
void DrawCustomMain() {
  char buf[16];
  byte x = 5;
  byte y = 2;
  byte t;


  // Main Header
  //  ReefAngel.LCD.DrawText(DefaultFGColor, DefaultBGColor, 35, y,"Lee's Reef");
  
  // Date+Time
  ReefAngel.LCD.DrawDate(x+1, y);
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, y+9, 128, y+9);
  
  // Param Header
  y+=12; 
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+5,y,"Temp:");
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x+80, y, "PH:");
  // Temp and PH
  y+=2;
  ConvertNumToString(buf, ReefAngel.Params.Temp[T2_PROBE], 10);
  ReefAngel.LCD.DrawText(T2TempColor, DefaultBGColor, x+45, y, buf);
  y+=6; 
  ConvertNumToString(buf, ReefAngel.Params.Temp[T1_PROBE], 10);
  ReefAngel.LCD.DrawLargeText(T1TempColor, DefaultBGColor, x+5, y, buf, Num8x16);
  ConvertNumToString(buf, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(PHColor, DefaultBGColor, x+80, y, buf, Num8x16);
  y+=5;
  ConvertNumToString(buf, ReefAngel.Params.Temp[T3_PROBE], 10);
  ReefAngel.LCD.DrawText(T3TempColor, DefaultBGColor, x+45, y, buf);
  pingSerial();
    
  /// Display Sunrise / Sunset (to be calculated later...)
  y+=15; t=x;
  sprintf(buf, "%02d:%02d", sl.GetRiseHour(), sl.GetRiseMinute());
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Rise:"); t+=31;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf); 
  sprintf(buf, "%02d:%02d", sl.GetSetHour(), sl.GetSetMinute()); t+=36;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,t,y,"Set:"); t+=25;
  ReefAngel.LCD.DrawText(COLOR_RED,DefaultBGColor,t,y,buf);
  pingSerial();

  // MoonPhase
  y+=10; 
  ReefAngel.LCD.DrawText(0,255,x,y,"Moon:");
  ReefAngel.LCD.Clear(DefaultBGColor,x+32,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_MAGENTA,255,x+32,y,MoonPhaseLabel());
  pingSerial();
  
  // MoonLight %
  y+=10;
  t = intlength(ReefAngel.PWM.GetDaylightValue()) + 1;  t *= 5;
  ReefAngel.LCD.DrawText(COLOR_BLACK,DefaultBGColor,x,y,"MoonLights:"); 
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+68, y, 1);
  ReefAngel.LCD.DrawText(DPColor, DefaultBGColor, x+68+t, y, "%");
  pingSerial();

  // Display Water level
  y+=10; t=x;
  ConvertNumToString(buf, ReefAngel.WaterLevel.GetLevel(), 1);
  strcat(buf,"  ");
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"AT0 Level:"); t+=60;
  ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);

  // Vortech Mode
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF:"); t+=20;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  if (vtMode == 0) ReefAngel.LCD.DrawLargeText(COLOR_GREEN,255,t,y,"Constant");
  else if(vtMode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Lagoon");
  else if (vtMode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,t,y,"Reef Crest");
  else if (vtMode == 3) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Short Pulse");
  else if (vtMode == 4) ReefAngel.LCD.DrawLargeText(COLOR_RED,255,t,y,"Long Pulse");
  else if (vtMode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Smart NTM");
  else if (vtMode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,t,y,"Tidal Swell");
  else if (vtMode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,t,y,"Night");
  y+=10; t=x;
  ReefAngel.LCD.DrawText(0,255,x,y,"RF Speed:"); t+=60;
  ReefAngel.LCD.Clear(DefaultBGColor,t,y,x+(128-x),y+8);
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFSpeed_read()); t+=15;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,"/"); t+=10;
  ReefAngel.LCD.DrawText(COLOR_BLUE, DefaultBGColor,t,y,InternalMemory.RFDuration_read());
  pingSerial();
  
  y+=15;
  // Relays
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
  pingSerial();
  
  y+=12;
  TempRelay = ReefAngel.Relay.RelayDataE[0];
  TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
  TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
  ReefAngel.LCD.DrawOutletBox(12, y, TempRelay);
  pingSerial();
}

void DrawCustomGraph() {
}

Got my controller,

Posted: Fri Dec 28, 2012 6:09 am
by lnevo
Hmm timer bug not worked out...

Got my controller,

Posted: Fri Dec 28, 2012 6:32 am
by lnevo
Ok, figured out what happened. I overwrote the code that reads the timer from memory and updates my CustomVar when I pasted roberto's timer change. So acclTime reverted to 0 and memory was written as 255. Portal showed 0 since I was no longer updating that variable. I will post my fixes and final code tonight.