My 2nd Generation RA

Share you PDE file with our community
Post Reply
User avatar
jsclownfish
Posts: 378
Joined: Mon Oct 24, 2011 7:52 pm
Location: Saint Louis

My 2nd Generation RA

Post by jsclownfish »

I am almost finished with my new RA set-up. This is much more intricate and advanced than the previous version that only months ago seemed so complicated to me. I really appreciated all the help and teaching back then and I ask for your expert review once again. :D I'll apologize now for the long post.

The system has five main components, the RA+ head unit, a RA secondary monitor, an I/O expansion module, a relay expansion set, and my DIY sound unit (covered here:http://forum.reefangel.com/viewtopic.php?f=4&t=584)

There is a lot of info being sent around, especially to the 2nd monitor (I found out there is a 32byte limit. ;) )

It looks like this on my dining room table...I made a couple plexiglass holders for the ato and overflow sensor. I still need one for the reservoir, but I haven't found a container yet.
Set-up.jpg
Set-up.jpg (72.02 KiB) Viewed 5332 times
Here is the master RA+ INO:

Code: Select all

/* Main ReefAngelPlus controller with communication for sound,
auxillary monitor, I/O expansion, and extra relaybox, send master time,
flow, buzzer, overflow and reservoir alarms on the I/O

The following features are enabled for this File: 
#define DisplayImages
#define SetupExtras 
#define DateTimeSetup
#define VersionMenu
#define DisplayLEDPWM
#define wifi
#define RelayExp
#define WDT
#define CUSTOM_MAIN
#define COLORS_PDE
#define CUSTOM_VARIABLES
*/


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

//Actinic and Daylight PMW are on the LED moonlights
byte ActinicPWMValue=0;
byte DaylightPWMValue=0;
byte MoonPWMValue;

//wire.write variable number
byte param[2];

//wavemaker dalay variables
byte wmport=Port5;
boolean wmdelay=false;
byte wmpulse=0;

//time variables for time between sounds
long previousMillis = 0;    
long interval = 5000;      // interval at which a sound can play (milliseconds)

//varibles for input module data
byte IOIn[10];
int Flow0;
int Flow1;
int Flow2;
byte buzz = 5;
byte Overflow = 0;
byte Reservoir = 0;
byte HATO = 0;
byte LATO = 0;
byte wm = 0;
byte level = 0;
byte atos = 0;

//timestamp
time_t mastertime=now();

#include <avr/pgmspace.h>
// Labels for the web banner
prog_char id_label[] PROGMEM = "jsclownfish";
prog_char probe1_label[] PROGMEM = "Tank";
prog_char probe2_label[] PROGMEM = "Room";
prog_char probe3_label[] PROGMEM = "Sump";
prog_char relay1_label[] PROGMEM = "Heater1";
prog_char relay2_label[] PROGMEM = "Heater2";
prog_char relay3_label[] PROGMEM = "MHalide";
prog_char relay4_label[] PROGMEM = "Actinic";
prog_char relay5_label[] PROGMEM = "WM1";
prog_char relay6_label[] PROGMEM = "WM2";
prog_char relay7_label[] PROGMEM = "Main";
prog_char relay8_label[] PROGMEM = "Fans";
prog_char relay11_label[] PROGMEM = "Dose#1";
prog_char relay12_label[] PROGMEM = "Dose#2";
prog_char relay13_label[] PROGMEM = "Top-off#3";
prog_char relay14_label[] PROGMEM = "PO4 Flow";
prog_char relay15_label[] PROGMEM = "WM3";
prog_char relay16_label[] PROGMEM = "WM4";
prog_char relay17_label[] PROGMEM = "Chiller";
prog_char relay18_label[] PROGMEM = "Chiller Pump";
PROGMEM const char *webbanner_items[] = {
    id_label, probe1_label, probe2_label, probe3_label, relay1_label, relay2_label,
   relay3_label, relay4_label, relay5_label, relay6_label, relay7_label, relay8_label, 
   relay11_label, relay12_label, relay13_label, relay14_label,relay15_label, relay16_label, 
   relay17_label, relay18_label};
   
//function to backage int into bytes for parameters temp, pH, timers, actinic%)

void paramsend(int a)
{
  param[0]=(byte)(a>>8);
  param[1]=(byte)(a);
  Wire.write(param,2);
}
void FillCircle(int a, int b, int r, byte color) 
{  
   int f = 1 - r;  
   int ddF_x = 1;  
   int ddF_y = -2 * r;  
   int x = 0;  
   int y = r;  
for (int i=b-r; i<=b+r; i++) 
{    
  ReefAngel.LCD.PutPixel(color, a, i);  
}  
while (x<y) 
{    
if (f >= 0) 
{      
   y--;      
   ddF_y += 2;      
   f += ddF_y;    
}    
   x++;    
   ddF_x += 2;    
   f += ddF_x;      
   for (int i=b-y; i<=b+y; i++) 
{      
ReefAngel.LCD.PutPixel(color, a+x, i);      
ReefAngel.LCD.PutPixel(color, a-x, i);    
}     
for (int i=b-x; i<=b+x; i++) 
{      
ReefAngel.LCD.PutPixel(color,a+y, i);     
ReefAngel.LCD.PutPixel(color,a-y, i);    
}      
}
}
void DrawCustomGraph()
{
ReefAngel.LCD.DrawGraph(1, 75);
}
void DrawCustomMain()
{
// the graph is drawn/updated when we exit the main menu &
// when the parameters are saved
ReefAngel.LCD.DrawDate(6, 2);
ReefAngel.LCD.Clear(0, 1, 11, 132, 11);
pingSerial();
GetFlow();
// Display the Tank temperature with color sensors
ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,13,"TK:");
  if (ReefAngel.Params.Temp[T1_PROBE]>830) ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], COLOR_RED, 23, 13, 10); 
  else if (ReefAngel.Params.Temp[T1_PROBE]>810) ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], COLOR_ORANGE, 23, 13, 10); 
  else ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], COLOR_BLACK, 23, 13, 10);
// Display status of ato's
if (Reservoir == 0)
{
FillCircle(15,25,4,COLOR_GREEN);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_GREEN,14,24,"R");
}
else if (Reservoir == 1)
{
FillCircle(15,25,4,COLOR_RED);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_RED,14,22,"R");
}
if (Overflow == 0)
{
FillCircle(26,26,4,COLOR_GREEN);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_GREEN,23,22,"O");
}
else if (Overflow == 1)
{
FillCircle(26,26,4,COLOR_RED);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_RED,23,22,"O");
}
if (ReefAngel.HighATO.IsActive())
{
HATO = 1;
FillCircle(36,26,4,COLOR_GREEN);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_GREEN,33,22,"H");
}
else
{
HATO = 0;
FillCircle(36,26,4,COLOR_RED);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_RED,33,22,"H");
}
if (ReefAngel.LowATO.IsActive())
{
LATO = 1;
FillCircle(46,26,4,COLOR_GREEN);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_GREEN,43,23,"L");
}
else
{
LATO = 0;
FillCircle(46,26,4,COLOR_RED);
ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_RED,43,22,"L");
}
// Display the Flow s
ReefAngel.LCD.DrawText(T3TempColor,DefaultBGColor,5,33,"FL:");
ReefAngel.LCD.DrawSingleMonitor(Flow1, COLOR_BLACK, 23, 33, 1);
// Display the PH with color sensors
ReefAngel.LCD.DrawText(PHColor,DefaultBGColor, 64, 13,"PH:");
  if (ReefAngel.Params.PH>840 || ReefAngel.Params.PH<790) ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, COLOR_RED, 82, 13, 100);
  else if (ReefAngel.Params.PH>830 || ReefAngel.Params.PH<800) ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, COLOR_ORANGE, 82, 13, 100);
  else ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, 0, 82, 13, 100);
// Display the % Moonlight
ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 64, 23,"MN:");
ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetActinicValue(), COLOR_BLACK, 82, 23, 1);
ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 100, 23,"%");
// Display arrows for Wavemaker
ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 64, 33,"WV:");
  if (bitRead(ReefAngel.Relay.RelayData,Port5-1)==1) ReefAngel.LCD.DrawText(APColor,DefaultBGColor, 82, 33,"-->"); //port 5 on
  else if (bitRead(ReefAngel.Relay.RelayData,Port6-1)==1) ReefAngel.LCD.DrawText(APColor,DefaultBGColor, 82, 33,"<--");  //port 6 on
  else ReefAngel.LCD.DrawText(APColor,DefaultBGColor, 82, 33," O "); //port5 and port 6 off, wavemaker delay
pingSerial();
byte TempRelay = ReefAngel.Relay.RelayData;
TempRelay &= ReefAngel.Relay.RelayMaskOff;
TempRelay |= ReefAngel.Relay.RelayMaskOn;
ReefAngel.LCD.DrawOutletBox(12, 45, TempRelay);
#ifdef RelayExp
// draw 1st expansion relay
TempRelay = ReefAngel.Relay.RelayDataE[0];
TempRelay &= ReefAngel.Relay.RelayMaskOffE[0];
TempRelay |= ReefAngel.Relay.RelayMaskOnE[0];
ReefAngel.LCD.DrawOutletBox(12, 55, TempRelay);
#endif
//Timer and trigger to keep wavemaker timing correct after setting date/time
    int t=ReefAngel.Timer[1].Trigger-now();
    if (t>=0)
    {
      ReefAngel.LCD.Clear(255,105,33,135,43);
      ReefAngel.LCD.DrawText(0, DefaultBGColor,105,33,t);
    }
    if (t>300) ReefAngel.Timer[1].ForceTrigger();  
}
void Soundsend(byte x, long y)  //function to play sound and delay before it plays again.
{
  interval = y*100;
  unsigned long currentMillis = millis();
    if(currentMillis - previousMillis > interval) 
      {
      // save the last time you played a sound 
      previousMillis = currentMillis;   
      // if longer than 100 sec than play a sound
         Wire.beginTransmission(9);      // transmit to device #9
         Wire.write('$');         // send the $$$
         Wire.write('$');
         Wire.write('$');
         Wire.write(x);         // send the command
         Wire.endTransmission();      // stop transmitting
        }
}
void setup()
{
  ReefAngel.Init();  //Initialize controller and start web banner timer
  ReefAngel.PHMin=520;
  ReefAngel.PHMax=801;
  ReefAngel.Portal("jsclownfish");
  ReefAngel.CustomVar[1]=Flow0;
  ReefAngel.CustomVar[2]=Flow1;
  ReefAngel.CustomVar[3]=Flow2;
  ReefAngel.CustomVar[6]=buzz;
// Set the ports that get toggled on & off during the following modes
// To enable a port to be toggled, place a 1 in the appropriate position
// Uncomment and update as needed
// Port 87654321
  ReefAngel.FeedingModePorts = B00110000;
  ReefAngel.FeedingModePortsE[1] = B00110100;
  ReefAngel.WaterChangePorts = B10110000;
  ReefAngel.WaterChangePortsE[1] = B11111111;
  // Ports that are always on
  ReefAngel.Relay.On(Port7);  //main pump
  ReefAngel.Relay.On(Box1_Port1); //chiller
  ReefAngel.Relay.On(Box1_Port2); //chiller pump
  ReefAngel.Relay.On(Box1_Port4); //phosban pumpo
// Top smaller pumps once an hour for 5 minutes
  ReefAngel.Relay.On(Box1_Port5);
  ReefAngel.Timer[2].SetInterval(30); 
  ReefAngel.Timer[2].Start();
//Side to side Wavemaker Pumps
  ReefAngel.Relay.On(Port5);
  ReefAngel.Timer[1].SetInterval(200);
  ReefAngel.Timer[1].Start();
}
void loop()
{
  // Specific functions
  //1st Heater at 78.8, 2nd Heater at 77.0, MH on at 10AM off at 8PM, Actinics on at 9AM off at 9PM, Fan kicks on at 82.0
  ReefAngel.StandardHeater(Port1,788,792);
  ReefAngel.StandardHeater(Port2,770,792);
  ReefAngel.MHLights(Port3,10,0,20,0,5);
  ReefAngel.StandardLights(Port4,9,0,21,0);
  ReefAngel.StandardFan(Port8,792,820);
  ReefAngel.DosingPumpRepeat(Box1_Port7,0,60,4); // Dose for 4 seconds every 60 minutes with 0 minutes offset
  ReefAngel.DosingPumpRepeat(Box1_Port8,5,60,4); // Dose for 4 seconds every 60 minutes with 5 minutes offset
//Sump refill, check reservoir level first, then standard ATO
  if (Reservoir == 1)
  {
   ReefAngel.StandardATO(Box1_Port3,60);  //Setup relay unit Port3 as Auto Top-Off function with 60s timeout 
  }
 //Alarm state
  if (ReefAngel.Params.Temp[T1_PROBE]>830) 
  {
  buzz = 1;
  }
  else
  {
  buzz = 0;
  }
  GetFlow();
  
 // Wavemaker Code with night option
  if (ReefAngel.Timer[1].IsTriggered() )
  {
    if ((hour() >= 21) || (hour() <= 8)) //from 9p-Midnight 
    {  //PWMSlope(byte startHour, byte startMinute, byte endHour, byte endMinute, byte startPWM, 
       // byte endPWM, byte Duration, byte oldValue)
      wmpulse=PWMSlope(21,0,8,0,5,30,179,30);
      if (wmdelay)
      {
        ReefAngel.Timer[1].SetInterval(wmpulse);  // WM delay function from 30-170 sec.
        ReefAngel.Timer[1].Start();
        ReefAngel.Relay.Off(Port5);
        ReefAngel.Relay.Off(Port6);
        if (wmport==Port5) wmport=Port6; else wmport=Port5;
        wmdelay=false;
      }
      else
      {
        ReefAngel.Timer[1].SetInterval(200-wmpulse);  // WM bursts timing from 170-30 sec.
        ReefAngel.Timer[1].Start();
        ReefAngel.Relay.On(wmport);
        wmdelay=true;
      }
    }
    else
    {
      //8a-10p normal wave settings
      ReefAngel.Timer[1].SetInterval(200);
      ReefAngel.Timer[1].Start();
      ReefAngel.Relay.Toggle(Port5);
      ReefAngel.Relay.Toggle(Port6);
    }
  }
//extra small powerheads to increase circulation once an hour 
//for 5 min during the day
if (hour()>=8 && hour()<=21)
{
    if (minute()<5)
     {
       if (ReefAngel.Timer[2].IsTriggered())
         {
          ReefAngel.Timer[2].SetInterval(30);
          ReefAngel.Timer[2].Start(); 
          ReefAngel.Relay.Toggle(Box1_Port5);
// if bitRead(ReefAngel.Relay.RelayData,Box1_Port5-1) ReefAngel.Relay.Off(Box1_Port6)
// else ReefAngel.Relay.On(Box1_Port6)
          ReefAngel.Relay.Toggle(Box1_Port6);
         }        
     }
    else
     {
       ReefAngel.Relay.Off(Box1_Port5);
       ReefAngel.Relay.Off(Box1_Port6);
     }
}
else
{
   ReefAngel.Relay.Off(Box1_Port5);
   ReefAngel.Relay.Off(Box1_Port6);
}
//Moonlight
 MoonPWMValue=MoonPhase();
  if (hour()==21 || hour()==8)
     MoonPWMValue=PWMSlope(21,00,9,00,0,MoonPhase(),60,0);
  if (ReefAngel.Relay.Status(Port4)) MoonPWMValue=0;
  ReefAngel.PWM.SetActinic(MoonPWMValue);
  ReefAngel.PWM.SetDaylight(MoonPWMValue);

//I/O module communication
  Wire.beginTransmission(7);      // transmit to device #7
  Wire.write('$');         // send the $$$
  Wire.write('$');
  Wire.write('$');
  Wire.write(buzz);
  Wire.endTransmission();      // stop transmitting  
//Overflow blocked alarm & main pump shut-off, Reservoir alarm
  // Overflow blocked, Reservoir Full
  if (Overflow == 0  && Reservoir == 0) 
   {
     ReefAngel.Relay.Off(Port7);
     ReefAngel.CustomVar[4]=1;  // overflow: 1 good, 0 bad
     ReefAngel.CustomVar[5]=0;  // reservoir: 0 good, 1 bad
     level = 0;
   }
  // Overflow open, Reservoir Full
  else if (Overflow == 1  && Reservoir == 0) 
   {
     ReefAngel.Relay.On(Port7);
     ReefAngel.CustomVar[4]=0;
     ReefAngel.CustomVar[5]=0;
     level = 1;
   }
       // Overflow blocked, Reservoir Empty
  else if (Overflow == 0  && Reservoir == 1) 
   {
     ReefAngel.Relay.Off(Port7);
     ReefAngel.CustomVar[4]=1;
     ReefAngel.CustomVar[5]=1;
     level = 2;
   }
       // Overflow open, Reservoir Empty
  else if (Overflow == 1  && Reservoir == 1) 
   {
     ReefAngel.Relay.On(Port7);
     ReefAngel.CustomVar[4]=0;
     ReefAngel.CustomVar[5]=1;
     level = 3;
   }
//ATO status  
  if (HATO == 1 && LATO == 1) 
   {
     atos = 0;
   }
  else if (HATO == 1 && LATO == 0) 
   {
     atos = 1;
   }
  else if (HATO == 0 && LATO == 1) 
   {
     atos = 2;
   }
  else if (HATO == 0 && LATO == 0) 
   {
     atos = 3;
   } 
//send sound requests
      if (ReefAngel.DisplayedMenu==FEEDING_MODE) Soundsend(7,100);     //feeding time
      if (ReefAngel.DisplayedMenu==WATERCHANGE_MODE) Soundsend(8,100);     //feeding time
         
//send data parameter package to the auxillary monitor
         Wire.beginTransmission(10);      // transmit to device #10
         Wire.write('$');         // send the $$$
         Wire.write('$');
         Wire.write('$');
         paramsend(ReefAngel.Params.Temp[T1_PROBE]);
         paramsend(ReefAngel.Params.Temp[T2_PROBE]);
         paramsend(ReefAngel.Params.Temp[T3_PROBE]);
         paramsend(ReefAngel.Params.PH);
         paramsend(ReefAngel.Timer[1].Trigger-now());
         paramsend(ReefAngel.PWM.GetActinicValue());
         Wire.write(level);
         Wire.write(atos);
         Wire.write(ReefAngel.Relay.RelayData);
         Wire.write(ReefAngel.Relay.RelayDataE[0]);
         paramsend(Flow0);
         paramsend(Flow1);
         paramsend(Flow2);
         Wire.write(hour());
         Wire.write(minute());
         Wire.write(second());
         Wire.write(day());
         Wire.write(month());
         paramsend(year());
         Wire.endTransmission();      // stop transmitting
   ReefAngel.ShowInterface();
}
void GetFlow()
{
  Wire.requestFrom(7,9);
  if(Wire.available())
  {
    for (int a=0;a<10;a++)
      IOIn[a]=Wire.read();
  }
  Flow0=(IOIn[1]<<8)+IOIn[2];
  Flow1=(IOIn[3]<<8)+IOIn[4];
  Flow2=(IOIn[5]<<8)+IOIn[6];
  Overflow=IOIn[7];
  Reservoir=IOIn[8];
}
Here is the expansion module code:

Code: Select all

#include <Wire.h>
#include <avr/wdt.h>

byte Overflow=0;
byte Reservoir=0;
byte Buzzer=0;

byte IOOut[9];
volatile int Flow0 = 0;
volatile int Flow1 = 0;
volatile int Flow2 = 0;
unsigned long lastmillis=millis();

void FlowMeter1()
{
  Flow1++;
}

SIGNAL(PCINT0_vect) {
  Flow0++;
}

SIGNAL(PCINT2_vect) {
  Flow2++;
}

void setup()
{
  //  Serial.begin(57600);
  Wire.begin(7);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
  pinMode(3,INPUT);
  digitalWrite(3,HIGH); //pull up resistor
  pinMode(5,INPUT);
  digitalWrite(5,HIGH); //pull up resistor
  pinMode(6,INPUT);
  digitalWrite(6,HIGH); //pull up resistor
  pinMode(9,INPUT);
  digitalWrite(9,HIGH); //pull up resistor
  pinMode(10,OUTPUT);
  digitalWrite(10,LOW); //pull up resistor
  pinMode(11,INPUT);
  digitalWrite(11,HIGH); //pull up resistor

  PCMSK0 |= (1<<PCINT3); // Pin 11
  PCICR |= (1<<PCIE0); // Interrupt 0

    PCMSK2 |= (1<<PCINT21); // Pin 5
  PCICR |= (1<<PCIE2); // Interrupt 2

    MCUCR = (1<<ISC01) | (1<<ISC01);  //Rising edge

  attachInterrupt(1, FlowMeter1, RISING); // Interrupt 1  
  wdt_enable(WDTO_1S);
}

void loop()
{
  wdt_reset();
  if (millis()-lastmillis>1000)
  {
    lastmillis=millis();
    IOOut[0]=0;
    IOOut[1]=Flow0>>8;
    IOOut[2]=Flow0;
    IOOut[3]=Flow1>>8;
    IOOut[4]=Flow1;
    IOOut[5]=Flow2>>8;
    IOOut[6]=Flow2;
    Flow0=0;
    Flow1=0;
    Flow2=0;
  }

  //overflow sensor
  Overflow = digitalRead(6);

  //reservoir sensor
  Reservoir = digitalRead(9);

  IOOut[7]=Overflow;
  IOOut[8]=Reservoir;

  //overtemp alarm 
 if (Buzzer == 0)
 {
   digitalWrite(10, LOW);
 }
 else if (Buzzer == 1)
 {
   digitalWrite(10, HIGH);
 }
}
void requestEvent() {
  Wire.write(IOOut,sizeof(IOOut));
}

void receiveEvent(int howMany) 
{
  if (howMany==4)  // Our custom protocol is 4 bytes
  {
    byte cmd1,cmd2,cmd3,cmd4;
    cmd1=Wire.read();
    cmd2=Wire.read();
    cmd3=Wire.read();
    cmd4=Wire.read();  
    if (cmd1=='$' && cmd2=='$' && cmd3=='$') // the first 3 bytes of the custom protocol are $$$ to ensure it's coming from RA
    {
      Buzzer=cmd4;
    }
  }
  else
    for (int a=0;a<howMany;a++)
      Wire.read(); // if the number of bytes is not 4, discard everything
} 

and here is the RA used as a second monitor:

Code: Select all

/* Auxillary LCD monitor connected to the ReefAngel Controller (328)
requires a custom cable to connect from I2C main to the serial port

The following features are enabled for this File: 
#define DisplayLEDPWM
#define RelayExp
#define InstalledRelayExpansionModules 1
#define CUSTOM_MAIN
#define COLORS_PDE
#define FONT_8x8
#define NUMBERS_8x8
#define NUMBERS_16x16
*/

#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 <ReefAngel.h>
#include <IO.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <avr/wdt.h>
//screen set-up
#define NumScreens 9
int ScreenID=0;
//define for data over I2C
int maxTemp=0;
int minTemp=2000;
int maintemp=800;
int roomtemp=780;
int sumptemp=780;
int ph=700;
int maxPH=600;
int minPH=900;
int moonlight=0;
byte TempRelay =15;
byte TempRelay2=15;
int wavetime=0;
int wavetime2=0;
int flow0=0;
int flow1=0;
int flow2=0;
int wmaker2;
byte mhour;
byte mminute;
byte msecond;
byte mday;
byte mmonth;
int myear;
byte Overflow;
byte Reservoir;
byte LATO;
byte HATO;
byte level;
byte atos;
//Analog Clock constants
    const float pi = 3.141592653;
    byte clock_center_x = 65;
    byte clock_center_y = 65;
    byte clock_radius = 55;
    byte secondhand_radius = 33;
    float secondangle;
    byte secondend_x;
    byte secondend_y;
    float minuteangle;
    float hourangle;
   // Analog Clock Structure
       int minutebase_radius = 3;
       int minutehand_radius = 33;
       int hourbase_radius = 3;
       int hourhand_radius = 25;   
       int seconds_hand_x, seconds_hand_y, minutes_hand_x, minutes_hand_y, hours_hand_x, hours_hand_y;
       int hours_hand_base_x, hours_hand_base_y, hours_hand_base_x1, hours_hand_base_y1;
       int minutes_hand_base_x, minutes_hand_base_y, minutes_hand_base_x1, minutes_hand_base_y1; 
       int minutebase_x, minutebase_y, minutebase_x1, minutebase_y1; 
       int minuteend_x, minuteend_y; 
       int hourbase_x, hourbase_y, hourbase_x1, hourbase_y1; 
       int hourend_x, hourend_y; 
       float minfloat;
//time stamp
time_t tsmax=now();
time_t tsmin=now();
time_t PHmax=now();
time_t PHmin=now();
int mastertime;

// Moon Global Variables - Thanks Deckoz2302!
byte DayAge;
byte ThisPhase;
int JulianDate(int,int,int);
double MoonAge(int,int,int);
byte MoonState();
char* ThisPhaseLabel[]={
    "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent"                        };
void Moonstate(byte D);
// Draw Minutes Hand
void draw_minute(int color)
{
  Drawline(minutebase_x, minutebase_y, minutebase_x1, minutebase_y1, color); 
  Drawline(minutebase_x, minutebase_y, minuteend_x, minuteend_y, color); 
  Drawline(minutebase_x1, minutebase_y1, minuteend_x, minuteend_y, color); 
}
// Draw Hour Hand
void draw_hour(int color)
{
   Drawline(hourbase_x, hourbase_y, hourbase_x1, hourbase_y1, color); 
   Drawline(hourbase_x, hourbase_y, hourend_x, hourend_y, color); 
   Drawline(hourbase_x1, hourbase_y1, hourend_x, hourend_y, color); 
}
void Drawline (int x0, int y0, int x1, int y1, int color) 
{
  int dy = y1 - y0;
  int dx = x1 - x0;
  int stepx, stepy;
  if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
  if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
  dy <<= 1; // dy is now 2*dy
  dx <<= 1; // dx is now 2*dx
  ReefAngel.LCD.PutPixel(color, x0, y0);
  if (dx > dy) {
    int fraction = dy - (dx >> 1); // same as 2*dy - dx
    while (x0 != x1) {
      if (fraction >= 0) {
        y0 += stepy;
        fraction -= dx; // same as fraction -= 2*dx
        }
      x0 += stepx;
      fraction += dy; // same as fraction -= 2*dy
      ReefAngel.LCD.PutPixel(color, x0, y0);
    }
  } else {
  int fraction = dx - (dy >> 1);
  while (y0 != y1) {
  if (fraction >= 0) {
    x0 += stepx;
    fraction -= dy;
    }
  y0 += stepy;
  fraction += dx;
  ReefAngel.LCD.PutPixel(color, x0, y0);
  }
  }}
void DrawTime(byte x, byte y, byte FGcolor, byte BGcolor, time_t ts)
{
    char text[13];
    char temp[]="  ";
    strcpy(text,"");
    itoa(hourFormat12(ts),temp,10);
    if (temp[1]==0) strcat(text,"0");
    strcat(text,temp);
    strcat(text,":");
    itoa(minute(ts),temp,10);
    if (temp[1]==0) strcat(text,"0");
    strcat(text,temp);
    strcat(text,":");
    itoa(second(ts),temp,10);
    if (temp[1]==0) strcat(text,"0");
    strcat(text,temp);
    if (isAM(ts))
    {
        strcat(text," AM");
    }
    else
    {
        strcat(text," PM");
    }
    ReefAngel.LCD.DrawText(FGcolor, BGcolor, x, y, text);
}
void DrawCircle(int a, int b, int r, byte color)
{
  int f = 1 - r;  
  int ddF_x = 1;  
  int ddF_y = -2 * r;  
  int x = 0;  
  int y = r;
  
 ReefAngel.LCD.PutPixel(color, a, b+r);  
 ReefAngel.LCD.PutPixel(color ,a, b-r);  
 ReefAngel.LCD.PutPixel(color, a+r, b);  
 ReefAngel.LCD.PutPixel(color, a-r, b);
 while (x<y) 
 {    
 if (f >= 0) 
 {      y--;
       ddF_y += 2;      
       f += ddF_y;    
   }    
   x++;    
   ddF_x += 2;    
   f += ddF_x;
   
   ReefAngel.LCD.PutPixel(color, a + x, b + y);
   ReefAngel.LCD.PutPixel(color, a - x, b + y);
   ReefAngel.LCD.PutPixel(color, a + x, b - y);
   ReefAngel.LCD.PutPixel(color, a - x, b - y);
   ReefAngel.LCD.PutPixel(color, a + y, b + x);
   ReefAngel.LCD.PutPixel(color, a - y, b + x);
   ReefAngel.LCD.PutPixel(color, a + y, b - x);
   ReefAngel.LCD.PutPixel(color, a - y, b - x);
   }
}
void FillCircle(int a, int b, int r, byte color) 
{  
   int f = 1 - r;  
   int ddF_x = 1;  
   int ddF_y = -2 * r;  
   int x = 0;  
   int y = r;  
for (int i=b-r; i<=b+r; i++) 
{    
  ReefAngel.LCD.PutPixel(color, a, i);  
}  
while (x<y) 
{    
if (f >= 0) 
{      
   y--;      
   ddF_y += 2;      
   f += ddF_y;    
}    
   x++;    
   ddF_x += 2;    
   f += ddF_x;      
   for (int i=b-y; i<=b+y; i++) 
{      
ReefAngel.LCD.PutPixel(color, a+x, i);      
ReefAngel.LCD.PutPixel(color, a-x, i);    
}     
for (int i=b-x; i<=b+x; i++) 
{      
ReefAngel.LCD.PutPixel(color,a+y, i);     
ReefAngel.LCD.PutPixel(color,a-y, i);    
}      
}
}
void DrawCircleBox (byte x, byte y, byte RelayData)
{
 byte b = 0;
 byte c = 0;
 byte d = 0;
    for (byte a=0;a<2;a++)
      {
        DrawCircle ((a*10)+x,y+30,5,COLOR_BLACK);
        if ((RelayData&(1<<a))==1<<a) 
        {
          FillCircle((a*10)+x,y+30,3,OutletOnBGColor);
        }
        else 
        {
          FillCircle((a*10)+x,y+30,3,OutletOffBGColor);
        }
      }
   for (byte a=2;a<4;a++)
      {
        b=(a-2)*10;
       DrawCircle (b+x,y+20,5,COLOR_BLACK);
        if ((RelayData&(1<<a))==1<<a) 
        {
          FillCircle(b+x,y+20,3,OutletOnBGColor);
        }
        else 
        {
          FillCircle(b+x,y+20,3,OutletOffBGColor);
        }
      }
    for (byte a=4;a<6;a++)
      {
        c=(a-4)*10;
       DrawCircle (c+x,y+10,5,COLOR_BLACK);
        if ((RelayData&(1<<a))==1<<a) 
        {
          FillCircle(c+x,y+10,3,OutletOnBGColor);
        }
        else 
        {
          FillCircle(c+x,y+10,3,OutletOffBGColor);
        }
      }
    for (byte a=6;a<8;a++)
      {
        d=(a-6)*10;
       DrawCircle (d+x,y,5,COLOR_BLACK);
        if ((RelayData&(1<<a))==1<<a) 
        {
          FillCircle(d+x,y,3,OutletOnBGColor);
        }
        else 
        {
          FillCircle(d+x,y,3,OutletOffBGColor);
    
      }
      }
}
void DrawWeeklyAvg (byte hr, int sensor, byte color, byte x, byte y, byte z)
{
  const int numReadings = 7;
  int dailyread;
  int readings[numReadings];      // the readings from the analog input
  int index = 0;                  // the index of the current reading
  int total = 0;                  // the running total
  int average = 0;                // the average
  if (hour()==hr && minute()==0 && second()==0) 
  {
    dailyread=sensor;  
  total= total - readings[index];   // subtract the last reading:         
  readings[index] = dailyread;   // read from the sensor: 
  total= total + readings[index];   // add the reading to the total:       
     index = index + 1;  // advance to the next position in the array:                   
  if (index >= numReadings)     // if we're at the end of the array...           
    index = 0;       // ...wrap around to the beginning:                         
  average = total / numReadings;    // calculate the average:
  delay (1000);
  ReefAngel.LCD.DrawSingleMonitor(average, color, x, y, z);
  }
}
void DrawDailyAvg (int sensor, byte color, byte x, byte y, byte z)
{
  const int numReadings = 24;
  int dailyread;
  int readings[numReadings];      // the readings from the analog input
  int index = 0;                  // the index of the current reading
  int total = 0;                  // the running total
  int average = 0;                // the average
  if (minute()==0 && second()==0) 
  {
    dailyread=sensor;  
  total= total - readings[index];   // subtract the last reading:         
  readings[index] = dailyread;   // read from the sensor: 
  total= total + readings[index];   // add the reading to the total:       
     index = index + 1;  // advance to the next position in the array:                   
  if (index >= numReadings)     // if we're at the end of the array...           
    index = 0;       // ...wrap around to the beginning:                         
  average = total / numReadings;    // calculate the average:
  delay (1000);
  ReefAngel.LCD.DrawSingleMonitor(average, color, x, y, z);
  }
}
void setup()
{
  ReefAngel.Init();  //Initialize controller and start web banner timer
  setTime(19,59,0,6,3,12);
  Wire.begin(10); // I2C address 10
  Wire.onReceive(receiveEvent);
  ReefAngel.Timer[LCD_TIMER].SetInterval(180);
  ReefAngel.Timer[LCD_TIMER].Start();  // start timer 
}
void loop()
{
  setTime(mhour,mminute,msecond,mday,mmonth,myear);
  if ( ReefAngel.Timer[LCD_TIMER].IsTriggered() )  // process screensaver timeout
	{// Screensaver timeout expired
	ReefAngel.LCD.BacklightOff();
	}
  if ( ReefAngel.Joystick.IsButtonPressed())
	{// turn the backlight on
	ReefAngel.LCD.BacklightOn();
        }
  // TODO check Timer[LCD_TIMER] code
  if ( ReefAngel.Timer[LCD_TIMER].Trigger == 0 )
	{
	ReefAngel.Timer[LCD_TIMER].Start();
	return;
	}// update the min and max temps
  if (now()%86400==0)
  {
    minTemp=maintemp;
    maxTemp=maintemp;
  }
  if (maintemp<minTemp) 
  {
    minTemp=maintemp;
    tsmin=now();
  }
  if (maintemp>maxTemp) 
  {
    maxTemp=maintemp;
    tsmax=now();
  }
  // update the min and max pH
if (now()%86400==0)
{
  minPH=ph;
  maxPH=ph;
}
if (ph<minPH) 
{
  minPH=ph;
  PHmin=now();
  
  
}
if (ph>maxPH) 
{
  maxPH=ph;
  PHmax=now();
}
  if (ReefAngel.Joystick.IsLeft() || ReefAngel.Joystick.IsDown()) 
  {
    ReefAngel.ClearScreen(DefaultBGColor);
    ScreenID--;
  }
  if (ReefAngel.Joystick.IsRight() || ReefAngel.Joystick.IsUp()) 
    {
    ReefAngel.ClearScreen(DefaultBGColor);
    ScreenID++;
    }
  if (ScreenID<0) ScreenID=NumScreens;
  if (ScreenID>=NumScreens) ScreenID=0;
  switch (ScreenID)
  {
  case 0:  //Initial first screen
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#1", Font8x8);
    // Display Temp Text
    ReefAngel.LCD.DrawLargeText(0,255,30,15,"Tank Temp", Font8x8);
    // Display the T1 temp  
    char text[7];
    ConvertNumToString(text, maintemp, 10);
    ReefAngel.LCD.Clear(255, 35, 25, 50, 60);
    if (maintemp>830) ReefAngel.LCD.DrawHugeNumbers(COLOR_RED, 255, 35, 25, text);
    else if (maintemp>830) ReefAngel.LCD.DrawHugeNumbers(COLOR_ORANGE, 255, 35, 25, text);
    else ReefAngel.LCD.DrawHugeNumbers(COLOR_MIDNIGHTBLUE, 255, 35, 25, text);
    // Display pH Text
    ReefAngel.LCD.DrawLargeText(0,255,82,85,"  pH  ", Font8x8);
    // Display pH Value
    ConvertNumToString(text, ph, 100);
    ReefAngel.LCD.Clear(255, 65, 95, 132, 115);
    if (ph>840 || ph<790) ReefAngel.LCD.DrawHugeNumbers(COLOR_RED, 255, 65, 95, text);
    else if (ph>830 || ph<800) ReefAngel.LCD.DrawHugeNumbers(COLOR_ORANGE, 255, 65, 95, text);
    else ReefAngel.LCD.DrawHugeNumbers(COLOR_MIDNIGHTBLUE, 255, 65, 95, text);
    // Display Sump Temp
    ReefAngel.LCD.DrawLargeText(0,255,8,50,"Sump Temp", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 50, 132, 60);
    ConvertNumToString(text, sumptemp, 10);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 50, text, Num8x8);
    // Display Room Temp
    ReefAngel.LCD.DrawLargeText(0,255,8,60,"Room Temp", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 60, 132, 70);
    ConvertNumToString(text, roomtemp, 10);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 60, text, Num8x8);   
// Display Moon Phase & State
    DayAge = MoonAge(day(), month(), year());
    MoonState(DayAge);
    ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,5,70,ThisPhaseLabel[ThisPhase]);
    TempRelay &= ReefAngel.Relay.RelayMaskOff;
    TempRelay |= ReefAngel.Relay.RelayMaskOn;  
    DrawCircleBox (10,90,TempRelay);
    DrawCircleBox (40,90,TempRelay2);
    break;
  case 1:  //same as first screen with position changes
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#2", Font8x8);
    // Display Temp Text
    ReefAngel.LCD.DrawLargeText(0,255,30,15,"   pH    ", Font8x8);
    // Display the T1 temp 
    ConvertNumToString(text, ph, 100);
    ReefAngel.LCD.Clear(255, 35, 25, 50, 50);
    if (ph>830) ReefAngel.LCD.DrawHugeNumbers(COLOR_RED, 255, 35, 25, text);
    else if (ph>830) ReefAngel.LCD.DrawHugeNumbers(COLOR_ORANGE, 255, 35, 25, text);
    else ReefAngel.LCD.DrawHugeNumbers(COLOR_MIDNIGHTBLUE, 255, 35, 25, text);
    // Display pH Text
    ReefAngel.LCD.DrawLargeText(0,255,25,105,"Tank", Font8x8);  
    // Display pH Value
    ConvertNumToString(text, maintemp, 10);
    ReefAngel.LCD.Clear(255, 65, 95, 132, 115);
    if (maintemp>840 || ph<790) ReefAngel.LCD.DrawHugeNumbers(COLOR_RED, 255, 65, 95, text);
    else if (maintemp>830 || ph<800) ReefAngel.LCD.DrawHugeNumbers(COLOR_ORANGE, 255, 65, 95, text);
    else ReefAngel.LCD.DrawHugeNumbers(COLOR_MIDNIGHTBLUE, 255, 65, 95, text);
    // Display Flow0
    ReefAngel.LCD.DrawLargeText(0,255,8,50,"Pump1Flow", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 50, 132, 60);
    ConvertNumToString(text, flow0, 10);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 50, text, Num8x8);
    // Display Flow1
    ReefAngel.LCD.DrawLargeText(0,255,8,60,"Pump2Flow", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 60, 132, 70);
    ConvertNumToString(text, flow1, 10);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 60, text, Num8x8);
    // Display Flow2
    ReefAngel.LCD.DrawLargeText(0,255,8,70,"Pump3Flow", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 70, 132, 80);
    ConvertNumToString(text, flow2, 10);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 70, text, Num8x8);
    // Display light level 
    ReefAngel.LCD.DrawLargeText(0,255,8,80,"Moonlight", Font8x8);
    ReefAngel.LCD.Clear(255, 90, 80, 132, 90);
    ConvertNumToString(text, moonlight, 1);
    ReefAngel.LCD.DrawLargeText(COLOR_MIDNIGHTBLUE, 255, 90, 80, text, Num8x8); 
    break;
  case 2:  //Temperature detail screen with sump and room temp, 24 hr. max/min, time stamps, and graph
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#3", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,10,15,"Temp Details", Font8x8);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,33,"TANK:");
    ReefAngel.LCD.DrawSingleMonitor(maintemp, COLOR_BLACK, 36, 33, 10);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,43,"HI:");
    ReefAngel.LCD.DrawSingleMonitor(maxTemp, 0, 23, 43, 10);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,48,43,"at");
    DrawTime(60,43,0, COLOR_WHITE,tsmax);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,53,"LO:");
    ReefAngel.LCD.DrawSingleMonitor(minTemp, 0, 23, 53, 10);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,48,53,"at");
    DrawTime(60,53,0,COLOR_WHITE, tsmin);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,63,"Range:");
    ReefAngel.LCD.DrawSingleMonitor((maxTemp-minTemp), COLOR_BLACK, 46, 63, 10);
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,69,63,"degreesF");
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,73,"Avg:");
    DrawDailyAvg(maintemp, COLOR_BLACK, 43, 73, 1);  
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,5,83,"WeeklyAvg:");
    DrawWeeklyAvg(12,maintemp, COLOR_BLACK, 66, 83, 1);  
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor, 86,83,"at noon");
    ReefAngel.LCD.DrawText(T2TempColor,DefaultBGColor, 5 ,93,"R00M:");
    ReefAngel.LCD.DrawSingleMonitor(roomtemp, COLOR_BLACK, 36, 93, 10);
    ReefAngel.LCD.DrawText(T3TempColor,DefaultBGColor, 5, 103,"SUMP:");
    ReefAngel.LCD.DrawSingleMonitor(sumptemp, COLOR_BLACK, 36, 103, 10);
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#3", Font8x8);
    break;
  case 3:  //pH details
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#4", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,20,15,"pH Details", Font8x8);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,33,"pH:");
    ReefAngel.LCD.DrawSingleMonitor(ph, COLOR_BLACK, 36, 33, 100);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,43,"HI:");
    ReefAngel.LCD.DrawSingleMonitor(maxPH, 0, 23, 43, 100);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,48,43,"at");
    DrawTime(60,43,0, COLOR_WHITE,PHmax);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,53,"LO:");
    ReefAngel.LCD.DrawSingleMonitor(minPH, 0, 23, 53, 100);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,48,53,"at");
    DrawTime(60,53,0,COLOR_WHITE, PHmin);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,63,"Range:");
    ReefAngel.LCD.DrawSingleMonitor((maxPH-minPH), COLOR_BLACK, 46, 63, 100);
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,73,"Avg:");
    DrawDailyAvg(ph, COLOR_BLACK, 43, 73, 100);  
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,83,"WeeklyAvg:");
    DrawWeeklyAvg(12,ph, COLOR_BLACK, 66, 83, 100);  
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor, 86,83,"at noon");
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,5,93,"WeeklyAvg:");
    DrawWeeklyAvg(0,ph, COLOR_BLACK, 66, 93, 100);  
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor, 86,93,"at midnt");
    break;
 case 4:  //relay box key
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#5", Font8x8);
    TempRelay &= ReefAngel.Relay.RelayMaskOff;
    TempRelay |= ReefAngel.Relay.RelayMaskOn;  
    DrawCircleBox (56,46,TempRelay);
    ReefAngel.LCD.DrawLargeText(0,255,27,20,"RelayBox 1", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,44,"MainP", Font8x8); //7
    ReefAngel.LCD.DrawLargeText(0,255,7,54,"WaveL", Font8x8); //5
    ReefAngel.LCD.DrawLargeText(0,255,7,64,"MH LT", Font8x8); //3
    ReefAngel.LCD.DrawLargeText(0,255,7,74,"Heat2", Font8x8); //1
    ReefAngel.LCD.DrawLargeText(0,255,75,44,"Fans", Font8x8);  //8
    ReefAngel.LCD.DrawLargeText(0,255,75,54,"WaveR", Font8x8);  //6
    ReefAngel.LCD.DrawLargeText(0,255,75,64,"StdLT", Font8x8); //4
    ReefAngel.LCD.DrawLargeText(0,255,75,74,"Heat1", Font8x8);  //2
    break;
  case 5:  //relay box key
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#6", Font8x8);
    TempRelay &= ReefAngel.Relay.RelayMaskOff;
    TempRelay |= ReefAngel.Relay.RelayMaskOn;  
    DrawCircleBox (56,46,TempRelay2);
    ReefAngel.LCD.DrawLargeText(0,255,27,20,"RelayBox 2", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,44,"Dose1", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,54,"Wave3", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,64,"  ATO", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,74,"Chill", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,75,44,"Dose2", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,75,54,"Wave4", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,75,64,"PO4", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,75,74,"Chill", Font8x8);
    break;
  case 6:  //level sensors
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#7", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,20,20,"Level Sensors", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,44," Overflow", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,54,"Reservoir", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,64,"  Low ATO", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,7,74," High ATO", Font8x8);
//    ReefAngel.LCD.DrawSingleMonitor(level, COLOR_BLACK, 5, 100, 1);
//    ReefAngel.LCD.DrawSingleMonitor(atos, COLOR_BLACK, 15, 100, 1);
//    ReefAngel.LCD.DrawSingleMonitor(Overflow, COLOR_BLACK, 25, 100, 1);
//    ReefAngel.LCD.DrawSingleMonitor(Reservoir, COLOR_BLACK, 35, 100, 1);
//    ReefAngel.LCD.DrawSingleMonitor(HATO, COLOR_BLACK, 45, 100, 1);
//    ReefAngel.LCD.DrawSingleMonitor(LATO, COLOR_BLACK, 55, 100, 1);
 
    if (level == 0)
    {
      Overflow = 0;
      Reservoir = 0;
    }
    else if (level == 1)
    {
      Overflow = 1;
      Reservoir = 0;
    }
    else if (level == 2)
    {
      Overflow = 0;
      Reservoir = 1;
    }
    else if (level == 3)
    {
      Overflow = 1;
      Reservoir = 1;
    }
    if (atos == 0)
    {
      HATO = 1;
      LATO = 1;
    }
    else if (atos == 1)
    {
      HATO = 1;
      LATO = 0;
    }
    else if (atos == 2)
    {
      HATO = 0;
      LATO = 1;
    }
    else if (atos == 3)
    {
      HATO = 0;
      LATO = 0;
    }
    DrawCircle (90,48,5,COLOR_BLACK);
        if (Overflow == 1) 
        {
          FillCircle(90,48,3,OutletOnBGColor);
          ReefAngel.LCD.Clear(255, 100, 44, 132, 54);

        }
        else if (Overflow == 0)
        {
          FillCircle(90,48,3,OutletOffBGColor);
          ReefAngel.LCD.DrawLargeText(OutletOffBGColor,255,100,44,"HIGH", Font8x8);
        }
    DrawCircle (90,58,5,COLOR_BLACK);
        if (Reservoir == 1) 
        {
          FillCircle(90,58,3,OutletOffBGColor);
          ReefAngel.LCD.DrawLargeText(OutletOffBGColor,255,100,54,"FILL", Font8x8);
        }
        else if (Reservoir == 0)
        {
          FillCircle(90,58,3,OutletOnBGColor);
          ReefAngel.LCD.Clear(255, 100, 54, 132, 64);

        }
    DrawCircle (90,68,5,COLOR_BLACK);
        if (LATO == 1) 
        {
          FillCircle(90,68,3,OutletOffBGColor);
          ReefAngel.LCD.DrawLargeText(OutletOffBGColor,255,100,64,"FILL", Font8x8);
        }
        else if (LATO == 0)
        {
          FillCircle(90,68,3,OutletOnBGColor);
          ReefAngel.LCD.Clear(255, 100, 64, 132, 74);
        }
    DrawCircle (90,78,5,COLOR_BLACK);
        if (HATO == 1) 
        {
          FillCircle(90,78,3,OutletOnBGColor);
          ReefAngel.LCD.Clear(255, 100, 74, 132, 84);
        }
        else if (HATO == 0)
        {
          FillCircle(90,78,3,OutletOffBGColor);
          ReefAngel.LCD.DrawLargeText(OutletOffBGColor,255,100,74,"LOW", Font8x8);
        }        
       break;
  case 7:  //setclock
  // Analog clock is modified from the original source by Jeff Miller
  // Author: Jeff Miller   http://arduinofun.blogspot.com/
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    // Frame
    DrawCircle(clock_center_x, clock_center_y, clock_radius, COLOR_CORNFLOWERBLUE);
    DrawCircle(clock_center_x, clock_center_y, clock_radius - 1, COLOR_CORNFLOWERBLUE);        
    DrawCircle(clock_center_x, clock_center_y, clock_radius - 2, COLOR_CORNFLOWERBLUE);       
    // Face Tick Marks  (x = r cos theta, y = r sin theta)
    for (byte r=0; r < 12; r++) {
      float angle = (2 * pi / 12)*r;
      int tick_x_out = (clock_radius - 8) * cos (angle) + clock_center_x;
      int tick_y_out = (clock_radius - 8) * sin (angle) + clock_center_y;
      int tick_x_in = clock_radius *.72 * cos (angle) + clock_center_x;
      int tick_y_in = clock_radius * .72 * sin (angle) + clock_center_y;                
      Drawline(tick_x_in, tick_y_in, tick_x_out, tick_y_out, COLOR_CORNFLOWERBLUE);
      }
    // Face Characters
    ReefAngel.LCD.DrawLargeText(0,255,clock_center_x - 8,106," 6", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,clock_center_x - 8, 25,"12", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,clock_center_x + 34,clock_center_y," 3", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,clock_center_x - 50, clock_center_y - 4,"9 ", Font8x8);
   // Draw Seconds Hand
       secondangle = (2 * pi / 60) * second();
              if (second() > 15)
        {secondangle = secondangle -1.57;
        }
      else
        {secondangle = secondangle +4.71;
        }
       Drawline(clock_center_x, clock_center_y, secondend_x, secondend_y, WHITE);
       secondend_x = secondhand_radius * cos (secondangle) + clock_center_x;
       secondend_y = secondhand_radius * sin (secondangle) + clock_center_y;
       Drawline(clock_center_x, clock_center_y, secondend_x, secondend_y, BLACK);
// Redraw minutes hand since the section closest to the center gets erased by the seconds hand
       draw_minute(BLACK);
// Redraw hour hand
       draw_hour(BLACK);
// Draw Minutes Hand
       minuteangle = (2 * pi / 60) * minute();
       if (minute() > 15)
        {minuteangle = minuteangle -1.57;
        }
      else
        {minuteangle = minuteangle +4.71;
        }
        // Erase old minutes Hand
       draw_minute(WHITE);
  // Calculate Minute Hand Base
      minutebase_x = minutebase_radius * cos (minuteangle - pi/2) + clock_center_x;
      minutebase_y = minutebase_radius * sin (minuteangle - pi/2) + clock_center_y;
      minutebase_x1 = minutebase_radius * cos (minuteangle + pi/2) + clock_center_x;
      minutebase_y1 = minutebase_radius * sin (minuteangle + pi/2) + clock_center_y;
// Calculate Minute Hand Point
      minuteend_x = minutehand_radius * cos (minuteangle) + clock_center_x;
      minuteend_y = minutehand_radius * sin (minuteangle) + clock_center_y;
// Draw new minutes Hand
      draw_minute(BLACK);                         
// Redraw hour hand
      draw_hour(BLACK);
// Draw Hour Hand   
      minfloat = minute(); // Convert minute short to float
      hourangle = (2 * pi / 12) * ((hourFormat12()) + (minfloat/60)); // Find angle for hour hand
      hourangle = hourangle -1.57;
// Erase old Hour Hand
      draw_hour(WHITE);
// Calculate hour Hand Base
      hourbase_x = hourbase_radius * cos (hourangle - pi/2) + clock_center_x;
      hourbase_y = hourbase_radius * sin (hourangle - pi/2) + clock_center_y;
      hourbase_x1 = hourbase_radius * cos (hourangle + pi/2) + clock_center_x;
      hourbase_y1 = hourbase_radius * sin (hourangle + pi/2) + clock_center_y;
// Calculate hour Hand Point
      hourend_x = hourhand_radius * cos (hourangle) + clock_center_x;
      hourend_y = hourhand_radius * sin (hourangle) + clock_center_y;
// Draw new hours Hand
      draw_hour(BLACK);
    ReefAngel.LCD.DrawLargeText(0,255,110,110,"#8", Font8x8);
    ReefAngel.LCD.DrawDate(5,121);
    break;
  case 8:  //menu
    ReefAngel.Refresh();
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 0, "                 ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(COLOR_WHITE, COLOR_CORNFLOWERBLUE, 0, 1, " Jon's Reef Tank ", Font8x8); //Top Banner
    ReefAngel.LCD.DrawLargeText(0,255,100,120,"#9", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,20,15,"Power Heads", Font8x8);
    ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 8, 33,"Top wave:");
    ReefAngel.LCD.Clear(255,78,33,103,43);
    ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 8, 108,"Side wave:");
    ReefAngel.LCD.Clear(255,78,108,103,120);
    ReefAngel.LCD.DrawText(0,DefaultBGColor, 78, 108, wavetime);
//    ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 104, 113,TempRelay2);
    ReefAngel.LCD.DrawText(DPColor,DefaultBGColor, 104, 108,"sec");
  if ((TempRelay&(1<<5))==1<<5)
    {
    ReefAngel.LCD.Clear(255,5,75,90,105);
    ReefAngel.LCD.DrawLargeText(0,255,90,75,"<---", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,90,85,"<---", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,90,95,"<---", Font8x8);
    }
  else if ((TempRelay&(1<<6))==1<<6) 
    {
    ReefAngel.LCD.Clear(255,45,75,130,105);
    ReefAngel.LCD.DrawLargeText(0,255,5,75,"--->", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,5,85,"--->", Font8x8);
    ReefAngel.LCD.DrawLargeText(0,255,5,95,"--->", Font8x8);
    }
  else
    { 
    ReefAngel.LCD.Clear(255,5,75,45,105);
    ReefAngel.LCD.Clear(255,45,75,130,105);
    ReefAngel.LCD.DrawLargeText(0,255,45,85," ~~~", Font8x8);
    }
  if ((TempRelay2&(1<<5))==1<<5)
  {
   ReefAngel.LCD.Clear(255,80,45,100,65);
   ReefAngel.LCD.DrawLargeText(0,255,40,45,"|", Font8x8);
   ReefAngel.LCD.DrawLargeText(0,255,40,55,"v", Font8x8);
  }
  else if ((TempRelay2&(1<<6))==1<<6) 
    {
   ReefAngel.LCD.Clear(255,40,45,60,65);
   ReefAngel.LCD.DrawLargeText(0,255,80,45,"|", Font8x8);
   ReefAngel.LCD.DrawLargeText(0,255,80,55,"v", Font8x8);
    }  
  else
    { 
   ReefAngel.LCD.Clear(255,40,45,100,65);
    }    
    break;
}
}
void receiveEvent(int howMany) 
{
    if (howMany==32)  // Our custom protocol is 13 bytes
  {
    byte cmd1,cmd2,cmd3,cmd4,cmd5,cmd6,cmd7,cmd8,cmd9,cmd10;
    byte cmd11,cmd12,cmd13,cmd14,cmd15,cmd16,cmd17,cmd18,cmd19,cmd20;
    byte cmd21,cmd22,cmd23,cmd24,cmd25,cmd26,cmd27,cmd28,cmd29,cmd30;
    byte cmd31,cmd32;
    cmd1=Wire.read();
    cmd2=Wire.read();
    cmd3=Wire.read();
    cmd4=Wire.read();
    cmd5=Wire.read();
    cmd6=Wire.read();
    cmd7=Wire.read();
    cmd8=Wire.read();
    cmd9=Wire.read();
    cmd10=Wire.read();
    cmd11=Wire.read();
    cmd12=Wire.read();
    cmd13=Wire.read();
    cmd14=Wire.read();
    cmd15=Wire.read();
    cmd16=Wire.read();
    cmd17=Wire.read();
    cmd18=Wire.read();
    cmd19=Wire.read();
    cmd20=Wire.read();
    cmd21=Wire.read();
    cmd22=Wire.read();
    cmd23=Wire.read();
    cmd24=Wire.read();
    cmd25=Wire.read();
    cmd26=Wire.read();
    cmd27=Wire.read();
    cmd28=Wire.read();
    cmd29=Wire.read();
    cmd30=Wire.read();
    cmd31=Wire.read();
    cmd32=Wire.read();   
   if (cmd1=='$' && cmd2=='$' && cmd3=='$') // the first 3 bytes of the custom protocol are $$$ to ensure it's coming from RA
    {
    maintemp=(cmd4<<8)+cmd5;
    sumptemp=(cmd6<<8)+cmd7;
    roomtemp=(cmd8<<8)+cmd9;
    ph=(cmd10<<8)+cmd11;
    wavetime=(cmd12<<8)+cmd13;
    moonlight=(cmd14<<8)+cmd15;
    level=cmd16;
    atos=cmd17;
    TempRelay=cmd18;
    TempRelay2=cmd19;
    flow0=(cmd20<<8)+cmd21;
    flow1=(cmd22<<8)+cmd23;
    flow2=(cmd24<<8)+cmd25;
    mhour=cmd26;
    mminute=cmd27;
    msecond=cmd28;
    mday=cmd29;
    mmonth=cmd30;
    myear=(cmd31<<8)+cmd32;
    }
  }
  else
    for (int a=0;a<howMany;a++)
      Wire.read(); // if the number of bytes is not 32, discard everything
} 
// Moon Calculators - Thanks Deckoz2302!
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;
}
void MoonState(byte D)
{
  switch(D){
  default:
  case 1: 
    //0, 29;
    ThisPhase = 0;
   break;
  case 2: 
    //1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
   break;
  case 3: 
    //7;
    ThisPhase = 2;
   break;
  case 4: 
    //8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
   break;
  case 5: 
    //14;
    ThisPhase = 4;
   break;
  case 6: 
    //15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
   break;
  case 7: 
    //22;
    ThisPhase = 6;
   break;
  case 8: 
    //23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
   break;
  }
}
Everything seems to be working, except the moonphase on the 2nd monitor. Here are some pics of the displays on the second monitor. Some of these look off just because all the probes, etc are not hooked up yet and some require data collection to calculate.
Screens.jpg
Screens.jpg (68.12 KiB) Viewed 5332 times
Any and all comments welcome. I haven't tried to write this much code in 30 years (visual basic ;) ) so please let me know if any of this is suspect or could be written better. Thanks again for all the help!
-Jon
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: My 2nd Generation RA

Post by binder »

What version of the libraries are you running? 0.9.0 or later I presume.

If so, then this block of code isn't needed:

Code: Select all

#include <avr/pgmspace.h>
// Labels for the web banner
prog_char id_label[] PROGMEM = "jsclownfish";
prog_char probe1_label[] PROGMEM = "Tank";
prog_char probe2_label[] PROGMEM = "Room";
prog_char probe3_label[] PROGMEM = "Sump";
prog_char relay1_label[] PROGMEM = "Heater1";
prog_char relay2_label[] PROGMEM = "Heater2";
prog_char relay3_label[] PROGMEM = "MHalide";
prog_char relay4_label[] PROGMEM = "Actinic";
prog_char relay5_label[] PROGMEM = "WM1";
prog_char relay6_label[] PROGMEM = "WM2";
prog_char relay7_label[] PROGMEM = "Main";
prog_char relay8_label[] PROGMEM = "Fans";
prog_char relay11_label[] PROGMEM = "Dose#1";
prog_char relay12_label[] PROGMEM = "Dose#2";
prog_char relay13_label[] PROGMEM = "Top-off#3";
prog_char relay14_label[] PROGMEM = "PO4 Flow";
prog_char relay15_label[] PROGMEM = "WM3";
prog_char relay16_label[] PROGMEM = "WM4";
prog_char relay17_label[] PROGMEM = "Chiller";
prog_char relay18_label[] PROGMEM = "Chiller Pump";
PROGMEM const char *webbanner_items[] = {
    id_label, probe1_label, probe2_label, probe3_label, relay1_label, relay2_label,
   relay3_label, relay4_label, relay5_label, relay6_label, relay7_label, relay8_label, 
   relay11_label, relay12_label, relay13_label, relay14_label,relay15_label, relay16_label, 
   relay17_label, relay18_label};
I say that because the Portal handles the labels on the portal and not on the controller. Now if you are using those labels then you can keep them, but I didn't think I saw any reference to them.
User avatar
jsclownfish
Posts: 378
Joined: Mon Oct 24, 2011 7:52 pm
Location: Saint Louis

Re: My 2nd Generation RA

Post by jsclownfish »

Thanks Curt, yes I am on 0.9.0. I wasn't sure if I still needed those for the web banner.

-Jon
psyrob
Posts: 247
Joined: Thu Sep 01, 2011 8:44 pm

Re: My 2nd Generation RA

Post by psyrob »

Jon:
This is really impressive, what a set up!! this is really something to aspire to!
Image
rimai
Posts: 12881
Joined: Fri Mar 18, 2011 6:47 pm

Re: My 2nd Generation RA

Post by rimai »

Wow, that is awesome!!!
That analog clock is sooo cool!!!
Roberto.
Post Reply