Lee's Feature Complete PDE

Share you PDE file with our community
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Wed Jan 02, 2013 5:54 pm
Added in the PWMSlopeOvernight() function so that now my moonlights will dim off and then dim back on (Well actually they are going the opposite way, but "overnight" so it just seems that way :D )

Also simplified my isNight check using the new GetSunRise() and GetSunSet() functions added to the SunLocation class. Much easier now since we can compare to now().

Finally, added a function (RefugiumLights()) similar to MoonLights() that will respect the ActinicOffset() and turn on my refugium light opposite my actinics based on the StandardLights memory variables.

Posts: 12270
Joined: Fri Mar 18, 2011 6:47 pm
PostPosted: Wed Jan 02, 2013 6:53 pm
Cool!!
Roberto.

Posts: 188
Joined: Fri Dec 30, 2011 6:57 am
PostPosted: Thu Jan 03, 2013 10:22 am
Lee, was just reviewing your code from your 2nd post in this thread. I like a lot of the features you included and I will likely try to adopt a few of them for my custom code.

However, I was curious/confused as to why you put all this at the end when typically the custom menu and display stuff is usually at the beginning:

Code: Select all
  ////// 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;
}

byte PWMSlopeOvernight(byte startHour, byte startMinute, byte endHour, byte
endMinute, byte startPWM, byte endPWM, byte Duration, byte oldValue)
{
 
  unsigned long Start = previousMidnight(now())+((unsigned long)NumMins(startHour, startMinute)*60);
  if (hour()<startHour) Start-=86400;
  unsigned long StartD = Start + (Duration*60);
  unsigned long End = nextMidnight(now())+((unsigned long)NumMins(endHour, endMinute)*60);
  if (hour()<startHour) End-=86400;
  unsigned long StopD = End - (Duration*60);
  if ( now() >= Start && now() <= StartD )
    return constrain(map(now(), Start, StartD, startPWM, endPWM),startPWM,
    endPWM);
  else if ( now() >= StopD && now() <= End )
  {
    byte v = constrain(map(now(), StopD, End, startPWM, endPWM),startPWM,
    endPWM);
    return endPWM-v+startPWM;
  }
  else if ( now() > StartD && now() < StopD )
    return endPWM;

  // lastly return the existing value
  return oldValue;
}

// Similar to MoonLights() but adding in ActinicOffset
void RefugiumLights(byte Relay)
{
  int MinuteOffset=InternalMemory.ActinicOffset_read();
  int onTime=NumMins(InternalMemory.StdLightsOffHour_read(),InternalMemory.StdLightsOffMinute_read())+MinuteOffset;
  int offTime=NumMins(InternalMemory.StdLightsOnHour_read(),InternalMemory.StdLightsOnMinute_read())-MinuteOffset;
 
  ReefAngel.StandardLights(Relay,onTime/60,onTime%60,offTime/60,offTime%60);
}

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

  // Backup the previous modes. We don't want Night to become default...
  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() {
  // Toggle the refugium light if we choose this menu entry.
  // Behavior is opposite for night vs day.
 
  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");
  // Had no room for this anymore :(
 
  // 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+=12; 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();
 
  // Display Water level
  y+=10; t=x;
  if (acclDay > 0) {
    ConvertNumToString(buf, acclDay, 1);
    strcat(buf,"  ");
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"Acclimation Day:"); t+=100;
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);
  } else {
    ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+8);
  }
 
  // Relays
  y+=10; t=x+7;
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
  pingSerial();
  y+=12;
  TempRelay = ReefAngel.Relay.RelayDataE[0];
  TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
  TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
  ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
  pingSerial();
}

void DrawCustomGraph() {
}



Was it necessary to put it there for some reason? You didn't follow the " // This should always be the last line
" comments.
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Thu Jan 03, 2013 12:52 pm
TanksNStuff wrote:
Code: Select all
  ////// Place your custom code above here

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


Was it necessary to put it there for some reason? You didn't follow the " // This should always be the last line
" comments.


If you look at the function loop() I did follow the " // This should always be the last line" comment. That comment means those two calls should be the last things that are executed by the loop() function.

The reason the rest of the stuff is below it was just a means to make it easier for me to edit and update the code. I was tried of scrolling through all the menu code and helper functions. This way, after my variable declarations and such, I would just have setup() followed by loop() where most of my code is located. It's really just a matter of preference :)

Posts: 188
Joined: Fri Dec 30, 2011 6:57 am
PostPosted: Thu Jan 03, 2013 1:39 pm
Oh, OK. Making it more convenient is a good excuse. I just thought that all of that had to be up top. I didn't realize it would still work if it was after the loop.
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Thu Jan 03, 2013 3:45 pm
A little bit of cleanup today. I removed the reliance on my isNight boolean variable.... I didn't think it was really needed.

In doing so, I streamlined my toggle for the Refugium light from the menu.
Code: Select all
  if (bitRead(ReefAngel.Relay.RelayData, Refugium-1)) { // If relay is on.
    // Toggle MaskOff for the light
    bitWrite(ReefAngel.Relay.RelayMaskOff, Refugium-1, 1-bitRead(ReefAngel.Relay.RelayMaskOff, Refugium-1));
  } else {
    // Toggle the MaskOn for the light
    bitWrite(ReefAngel.Relay.RelayMaskOn, Refugium-1, 1-bitRead(ReefAngel.Relay.RelayMaskOn, Refugium-1));
  }

I also streamlined some of my RF menu functions, so if I change from the menu, it's always a temporary change. I only set the timer when I go to Smart_NTM now or if done from the menu. Changing the memory setting is now the only way to change the default RF mode. If it's changed at night, it should only change momentarily (unless Smart_NTM which will trigger the timer..)

I also came up with a neat trick to have something run at a particular time of day. Instead of doing comparing now() to hour(), minute(), and second()...
Code: Select all
  if (now()%SECS_PER_DAY==54000) { // 3pm.
    setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
  }
User avatar
Posts: 2743
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
PostPosted: Sat Jan 05, 2013 7:58 am
TanksNStuff wrote:Oh, OK. Making it more convenient is a good excuse. I just thought that all of that had to be up top. I didn't realize it would still work if it was after the loop.

Technically it shouldn't work at the end of the file unless you define the functions up top before they are used (standard C/C++ syntax). However, arduino does things a little different because what we write in our PDE/INO files is actually included into a "base" main file that runs our setup() function first and then falls into an infinite loop calling the loop() function. Plus the arduino preprocessor appears to scan and generate other files needed to make things work. So the functions at the bottom of our file are already defined before they are used.
Yeah, it's a little confusing and it took me a while to understand what it was doing too. I looked at this when I was considering adding in compilation to my old RAGen program.
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Jan 07, 2013 10:32 pm
Hey it works... :) I moved around some of the code actually because my helper functions were starting to take more room and getting in the way between my loop() and Menu functions which I edit more often.

Also, played around with different GPS coordinates. Ended up off the coast of Chile to get a smaller timezone offset, but maintaining the day length and season cycle of GBR. I may switch in the spring to North of the equator to get in sync with our Winter/Summer day length schedule.. we'll see how this shapes up...
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Mar 11, 2013 7:04 am
So this week I did quite a bit of work on my code. I hacked up a routine to automate my water change and finished up the Tide class code. I had some time to test the water change code this weekend and it did take some tweaking because my float switch I'm trying to use is upside down for the SingleATO function to work properly. Currently I hacked the SingleATO function :)

Anyway, when I get my skimmate collector, I will rewire and re-orient my float switches. I was going to put both switches in parallel but since I need the float switch in active mode to trigger the ATO functions, I'm going to put them in series and have them both activated. If either goes in-active it will break the circuit and disable my return pump. When in water change mode though, it will turn my WC pump into an ATO pump :)

I also finished up the design for my Night mode since with my Tide program, I didn't want to interrupt the high tide/low tide functionality. I used the PWMSlope to create a transition between Night Speed and my normal speed. The value will be used to adjust the Tide class speed.

Anyway, code will be posted up soon. Looking forward to testing the tide stuff and getting to do more frequent water changes. I'm really pleased with my method for water changing now, just need to fine tune the plumbing and the process. I really like being able to rinse new media while simultaneously doing a water change :)
User avatar
Posts: 5349
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Mar 11, 2013 4:11 pm
Ok, I'm archiving my last version before I moved forward with my non-memory based RF code if anyone needs to reference it. The new version still uses memory but since the speed is going to change constantly based on my new Custom mode, I didn't want to keep reading/writing from memory. So here you go if anyone needs the reference for some reason.

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

#define NUMBERS_8x16

////// Place global variable code below 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 = "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;
TimerClass rfTimer;

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

SunLocation sl;
byte acclDay=0;
byte vacationMode=0;

byte wcReady=0;
int wcFillTime=0;
TimerClass wcTimer;

//Define Custom Memory Location
#define Mem_B_RefillATO   100
#define Mem_B_Vacation    102
#define Mem_B_AcclDay     103
#define Mem_B_WaterChange 105
#define Mem_I_WCFillTime  106

#define Var_HighATO    0
#define Var_Power      1
#define Var_Vacation   2
#define Var_AcclDay    3

//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 = Port2Bit | 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;
   
    // 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
    // sl.Init(-21.08931,-147.699722); // Default - GBR
    // sl.Init(21.30891,-147.699722); // Near Honolulu, HI
    sl.Init(-21.08931, -73.528383); // Off the coast of Chile
   
    // What was our previous modes before we restarted?
    vtPrevMode=InternalMemory.RFMode_read();
    vtPrevSpeed=InternalMemory.RFSpeed_read();
    vtPrevDuration=InternalMemory.RFDuration_read();
   
    // Dummy CustomVar to activate portal feature
    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

  RefugiumLights(Refugium);
 
  // Moonlights on from 5:00am-3:00am so 3am-5am is complete darkness
  ReefAngel.PWM.SetDaylight(PWMSlopeOvernight(5,0,3,0,0,MoonPhase(),30,0));
  ReefAngel.PWM.SetActinic(PWMSlopeOvernight(5,0,3,0,0,MoonPhase(),30,0));
 
  // See if power is back on so DelayedOn ports will reset.
  if (powerOutage && ReefAngel.Relay.IsRelayPresent(EXP1_RELAY))
  {
    powerOutage=false;
    LastStart=now();
    ReefAngel.CustomVar[Var_Power]=0;
  }
  ReefAngel.Relay.DelayedOn(Skimmer);
  ReefAngel.Relay.DelayedOn(Reactor,1);

  // See if we are acclimating corals and decrement the countdown each night
  static boolean acclCounterReady=false;
  if (now()%SECS_PER_DAY!=0) acclCounterReady=true;
 
  acclDay=InternalMemory.read(Mem_B_AcclDay);
  ReefAngel.CustomVar[Var_AcclDay]=acclDay;
  if (acclDay > 0) {
    if (acclCounterReady && now()%SECS_PER_DAY==0) {
      acclDay--;
      acclCounterReady=false;
      InternalMemory.write(Mem_B_AcclDay,acclDay);
    }
  } 
 
  // -9 hour difference for time zone. 472/506 seconds were calculation corrections
  // The acclDay will adjust the sunrise/sunset if we are adjusting for new coral
  // sl.SetOffset(14,472+(acclDay*240),14,506-(acclDay*120)); // GBR
  // sl.SetOffset(-6,472(acclDay*240),-6,506-(acclDay*120)); // Honolulu, HI
  sl.SetOffset(-1,(acclDay*240),-1,(-acclDay*120)); // Off the coast of Chile
 
  // Calculate the new Sunrise / Sunset based on our GPS coordinates
  sl.CheckAndUpdate();

  if ( (now() >= sl.GetSunRise()) && (now() <= (sl.GetSunSet()-30)) ) // It's Daytime
  {
    // Turn off MoonLights
    ReefAngel.PWM.SetDaylight(0);
    ReefAngel.PWM.SetActinic(0);

    // Come out of Night mode.
    if (vtOverride==false && vtMode==Night) {
      setRFmode(vtPrevMode,vtPrevSpeed,vtPrevDuration);
    }
  } else {
    // Set Vortech's to Night mode.
    if (vtOverride==false && isFeeding==false) {
      setRFmode(Night,vtNightSpeed,vtNightDuration);
    }
  }   
 
  // Some Tidal Swell
  if (now()%SECS_PER_DAY==43200) { // 12pm.
    setRFmode(Smart_TSM,50,10);
    setRFtimer(60);
  } 
 
  // Some Short Pulse action
  if (now()%SECS_PER_DAY==50400) { // 2pm.
    setRFmode(ShortPulse,55,10);
    setRFtimer(30);
  } 
 
  // A little extra Smart_NTM never hurt anyone
  if (now()%SECS_PER_DAY==54000) { // 3pm.
    setRFmode(Smart_NTM,vtNTMSpeed,vtNTMDuration);
  }
   
  // Some lagoon action
  if (now()%SECS_PER_DAY==59400) { // 4:30pm.
    setRFmode(Lagoon,40,0);
    setRFtimer(30);
  }
 
  // Some Long Pulse action
  if (now()%SECS_PER_DAY==64800) { // 6pm
    setRFmode(LongPulse,55,20);
    setRFtimer(30);
  }

  // Enable Feeding Mode flag
  if (ReefAngel.DisplayedMenu==FEEDING_MODE) isFeeding=true;
  // Turn on Refugium light during feeding anwater change mode
  if (ReefAngel.DisplayedMenu==FEEDING_MODE || 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;
   
  // 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 && rfTimer.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
  }
 
  // 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);
     } else {
       ReefAngel.Relay.Off(Extension);
       InternalMemory.write(Mem_B_RefillATO, 0);
     }
  }

  // Turn off return pump if we run out of water!
  if (ReefAngel.LowATO.IsActive()) {
    bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
  }
 
  if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) {
    // Start automatic water change here.
    // This function is currently modified to work with the float switch as-is.
    ReefAngel.SingleATOHigh(Extension); // Refill from SW bucket

    wcReady=InternalMemory.read(Mem_B_WaterChange); // Trigger to start
    wcFillTime=InternalMemory.read_int(Mem_I_WCFillTime);
    // Let's get started
    if(wcReady) {
      wcTimer.SetInterval(wcFillTime); // One bucket at a time
      wcTimer.Start();
      InternalMemory.write(Mem_B_WaterChange, 0);
      bitSet(ReefAngel.Relay.RelayMaskOff,Reactor-1); // Start draining
    }
    if(wcTimer.IsTriggered()) {
      bitClear(ReefAngel.Relay.RelayMaskOff,Reactor-1); // Stop draining
    }   
  } else {
    // Find out if we are on vacation
    ReefAngel.CustomVar[Var_Vacation]=InternalMemory.read(Mem_B_Vacation);
    vacationMode=ReefAngel.CustomVar[Var_Vacation];
 
    // We're on vacation. Keep the ATO reservoir filled.
    if (vacationMode==1) {
      ReefAngel.WaterLevelATO(Extension,30,61,63);
    } else {
      ReefAngel.Relay.Off(Extension);
    }

    // Turn off return if we are somehow overflowing the sump
    if (ReefAngel.HighATO.IsActive()) {
      if (!floatHigh) {
        floatHigh=true;
       ReefAngel.CustomVar[Var_HighATO]=1;
       bitClear(ReefAngel.Relay.RelayMaskOff,Return-1);
     } 
    } else {
      floatHigh=false;
     ReefAngel.CustomVar[Var_HighATO]=0;
    } 
  }
     
  // 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[Var_Power]=1;
  }
   
  ////// Place your custom code above here

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

// Menu Code
void MenuEntry1() {
  ReefAngel.FeedingModeStart();
}
void MenuEntry2() {
  ReefAngel.WaterChangeModeStart();
}
void MenuEntry3() {
  byte mode,speed,duration;
  byte prevMode,prevSpeed,prevDur;
 
  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=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;
  } 

  // Backup the previous modes. We don't want to change the default...
  prevMode=vtPrevMode; prevSpeed=vtPrevSpeed; prevDur=vtPrevDuration;
  setRFmode(mode,speed,duration);
  vtPrevMode=prevMode; vtPrevSpeed=prevSpeed; vtPrevDuration=prevDur;
  setRFtimer();
 
  ReefAngel.DisplayedMenu = RETURN_MAIN_MODE;   
}
void MenuEntry4() {
  // Toggle the refugium light if we choose this menu entry.
  if (bitRead(ReefAngel.Relay.RelayData, Refugium-1)) { // If relay is on.
    // Toggle MaskOff for the light
    bitWrite(ReefAngel.Relay.RelayMaskOff, Refugium-1, 1-bitRead(ReefAngel.Relay.RelayMaskOff, Refugium-1));
  } else {
    // Toggle the MaskOn for the light
    bitWrite(ReefAngel.Relay.RelayMaskOn, Refugium-1, 1-bitRead(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");
  // Had no room for this anymore :(
 
  // 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+=12; 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();
 
  // Display Water level
  y+=10; t=x;
  if (acclDay > 0) {
    ConvertNumToString(buf, acclDay, 1);
    strcat(buf,"  ");
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,x,y,"Acclimation Day:"); t+=100;
    ReefAngel.LCD.DrawText(DefaultFGColor,DefaultBGColor,t,y,buf);
  } else {
    ReefAngel.LCD.Clear(DefaultBGColor,x,y,x+(128-x),y+8);
  }
 
  // Relays
  y+=10; t=x+7;
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
  pingSerial();
  y+=12;
  TempRelay = ReefAngel.Relay.RelayDataE[0];
  TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
  TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
  ReefAngel.LCD.DrawOutletBox(t, y, TempRelay);
  pingSerial();
}

void DrawCustomGraph() {
}

byte PWMSlopeOvernight(byte startHour, byte startMinute, byte endHour, byte
endMinute, byte startPWM, byte endPWM, byte Duration, byte oldValue)
{
 
  unsigned long Start = previousMidnight(now())+((unsigned long)NumMins(startHour, startMinute)*60);
  if (hour()<startHour) Start-=86400;
  unsigned long StartD = Start + (Duration*60);
  unsigned long End = nextMidnight(now())+((unsigned long)NumMins(endHour, endMinute)*60);
  if (hour()<startHour) End-=86400;
  unsigned long StopD = End - (Duration*60);
  if ( now() >= Start && now() <= StartD )
    return constrain(map(now(), Start, StartD, startPWM, endPWM),startPWM,
    endPWM);
  else if ( now() >= StopD && now() <= End )
  {
    byte v = constrain(map(now(), StopD, End, startPWM, endPWM),startPWM,
    endPWM);
    return endPWM-v+startPWM;
  }
  else if ( now() > StartD && now() < StopD )
    return endPWM;

  // lastly return the existing value
  return oldValue;
}

// Similar to MoonLights() but adding in ActinicOffset
void RefugiumLights(byte Relay)
{
  int MinuteOffset=InternalMemory.ActinicOffset_read();
  int onTime=NumMins(InternalMemory.StdLightsOffHour_read(),InternalMemory.StdLightsOffMinute_read())+MinuteOffset;
  int offTime=NumMins(InternalMemory.StdLightsOnHour_read(),InternalMemory.StdLightsOnMinute_read())-MinuteOffset;
 
  ReefAngel.StandardLights(Relay,onTime/60,onTime%60,offTime/60,offTime%60);
}

// 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;
    }
   
    // Smart_NTM is on timer mode.
    if (vtMode==Smart_NTM) {
      setRFtimer();
    }
  }

  // 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) {
  rfTimer.SetInterval(minutes*60);
  rfTimer.Start();
  vtOverride=true;
}
void setRFtimer() {
  setRFtimer(60);
}
PreviousNext

Return to My PDE/INO file

Who is online

Users browsing this forum: No registered users and 1 guest

cron