My 2nd Generation RA
Posted: Sun Mar 25, 2012 1:15 pm
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.
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. Here is the master RA+ INO:
Here is the expansion module code:
and here is the RA used as a second monitor:
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.
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
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. 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];
}
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
}
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;
}
}
-Jon