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