Code Version 2.2
From Reef Projects
Code is fairly well commented. (12632 bytes)
Current Features:
- Controls lights/timer function
- Kills lights in the event of over heating
- If lights are killed because of restart (power fail) or overheating, there is a delay before turning on
- Controls heater/fan
- Feed mode button
- ATO
- Alarm, although not used yet
- Prints to LCD
- Displays the last time the ATO ran
- Countdown to when pumps or lights will resume
- PH monitor
- Choose mode
- Normal Running mode
- Set time and date from keypad
- Works with Arduino 0015 with keypad() function modified v.1 and LCDtoI2C Library
#include <OneWire.h>
#include <LCDI2C.h>
#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>
/*
To do:
* integrate keypad
A-normal mode ----Done
B-set time ----Done
*Set hours and minutes ---Done
C-feed mode
*turn off pumps/skimmer for 5 min
D-water change mode
*turn off pumps/skimmer/heater/ato/fan
Arduino Reef Controller
Analog Pin 1 = PH Probe
Analog Pin 2 = **ORP Probe?
Analog Pin 3 =
Analog Pin 4 = SDA for I2C
Analog Pin 5 = SCL for I2C
Digital Pin 0 = RX
Digital Pin 1 = TX
Digital Pin 2 = Temp Sensor
Digital Pin 3 = ATO Input
Digital Pin 4 = Feed Mode Button
Digital Pin 5 = Relay 1 Heater
Digital Pin 6 = Relay 2 Day Light
Digital Pin 7 = Relay 3 Moon Light
Digital Pin 8 = Relay 4 Fan
Digital Pin 9 = Relay 5 Automatic Top-off
Digital Pin 10 = Relay 6 Power Head #1
Digital Pin 11 = Relay 7 Power Head #2
Digital Pin 12 = Relay 8 Skimmer
Digital Pin 13 = Alarm
*/
LCDI2C lcd = LCDI2C(4,20,0x4C,1); //[# of lines on LCD],[address of serial LCD controller on I2C]
OneWire ds(2); // Temp Sensor on pin 2
int ato_input = 3; //ATO input on pin 3
int feed = 4; //Feed Mode Button
int ph_probe = 1; //PH probe on analog 1
int heater = 5;
int day_light = 6;
int moon_light = 7;
int fan = 8;
int ato = 9;
int ph_1 = 10;
int ph_2 = 11;
int skimmer = 12;
int alarm = 13;
int heater_on_temp = 7850; //Turn on the heater at this temp ex 78 degrees = 7800, 78.5 degrees = 7850
int heater_off_temp = 8000; //Turn off heater at this temp
int fan_on_temp = 8150; //Turn on fan at this temp
int fan_off_temp = 8050; //turn fan off once below this temp
int lights_off_temp = 8300; //Turn off the lights if the temp rises above this temp
int lights_on_time = 1300; //Turn day lights on at this time (military time)
int lights_off_time = 2100; //Turn day lights off at this time
int wait_time = 30; //how long to wait before turning on lights if they are turned off due to power loss or overheating
int lights_out = -100; //placeholder --don't change
int moonl_on_time = 2100; //Turn on moon lights at this time
int moonl_off_time = 700; //Turn off moon lights at this time
int feed_time = 5; //Turn off power heads for this amount of time when feed mode button is pressed.
int pumps_off = -10; //placeholder --don't change
int ato_time = 3; //Number of seconds for teh ATO to run each time the switch is on.
#define NUMREADINGS 10
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
int keypad_delay = 15; //necessary delay to keep from having scrambled characters on the display
void setup(void) {
Wire.begin(); //initialize the I2C bus
lcd.init(); //initialize LCD
/*Un-comment to set the time
RTC.stop();
RTC.set(DS1307_SEC,01); //set the seconds
RTC.set(DS1307_MIN,51); //set the minutes
RTC.set(DS1307_HR,20); //set the hours (military)
RTC.set(DS1307_DOW,7); //set the day of the week
RTC.set(DS1307_DATE,29); //set the date
RTC.set(DS1307_MTH,3); //set the month
RTC.set(DS1307_YR,9); //set the year
RTC.start();
//****** initialize inputs/outputs ************************************/
pinMode(heater, OUTPUT); // digital pin for heater as output
pinMode(day_light, OUTPUT); // digital pin for day light as output
pinMode(moon_light, OUTPUT); // digital pin for moon light as output
pinMode(fan, OUTPUT); // digital pin for fan as output
pinMode(ato, OUTPUT); // digital pin for auto top off as output
pinMode(ph_1, OUTPUT); // digital pin for power head 1 as output
pinMode(ph_2, OUTPUT); // digital pin for power head 2 as output
pinMode(skimmer, OUTPUT); // digital pin for skimmer as output
pinMode(ato_input, INPUT); //digital pin for ATO float switch as input
pinMode(feed, INPUT); //digital pin for feed mode button as input
pinMode(alarm, OUTPUT); // digital pin for alarm as output
digitalWrite(day_light, LOW);
for (int i = 0; i < NUMREADINGS; i++)
readings[i] = 0;
}
int High = 0;
int Low = 10000;
int ato_hour = 0;
int on_minute = 1; //indicates that this is the first time through the program.
void loop(void){
int mode = 100;
if(lcd.keypad() != -1){
mode = lcd.keypad();
}
switch (mode) {
//***********Mode B Set Time*************************************************************************************************
case 101:{
int minute, hour, second, date, month, year, tens, ones, key;
int set_time = 0;
while(set_time == 0){
lcd.clear();
hour = RTC.get(DS1307_HR,true); //This is in military time [0,23]
minute = RTC.get(DS1307_MIN,false);
second = RTC.get(DS1307_SEC,false);
date = RTC.get(DS1307_DATE,false);
month = RTC.get(DS1307_MTH,false);
year = RTC.get(DS1307_YR,false);
lcd.setCursor(0,0);
if(hour < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(hour);
delay(keypad_delay);
lcd.print(":");
delay(keypad_delay);
if(minute < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(minute);
delay(keypad_delay);
/* Don't print seconds
lcd.print(":");
delay(keypad_delay);
if(second < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(second);
delay(keypad_delay);
*/
lcd.setCursor(1,0);
if(month < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(month);
delay(keypad_delay);
lcd.print("/");
delay(keypad_delay);
if(date < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(date);
delay(keypad_delay);
lcd.print("/");
delay(keypad_delay);
if(year < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(year);
delay(keypad_delay);
lcd.setCursor(0,0);
lcd.cursor_on();
//**************Set Hour*********************************************************************************************
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 2){tens = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.print(tens);
delay(keypad_delay);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){ones = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.right();
RTC.stop();
RTC.set(DS1307_HR,tens * 10 + ones);
RTC.start();
lcd.setCursor(0,0);
hour = RTC.get(DS1307_HR,true);
if(hour < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(hour);
delay(keypad_delay);
lcd.right();
//**************Set Minute*********************************************************************************************
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 6){tens = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.print(tens);
delay(keypad_delay);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){ones = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.right();
RTC.stop();
RTC.set(DS1307_MIN,tens * 10 + ones);
RTC.start();
lcd.setCursor(0,3);
minute = RTC.get(DS1307_MIN,true);
if(minute < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(minute);
delay(keypad_delay);
//**************Set Month*********************************************************************************************
lcd.setCursor(1,0);
for (;;){
key = lcd.keypad();
if(key == 0 || key == 1){tens = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.print(tens);
delay(keypad_delay);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){ones = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.right();
RTC.stop();
RTC.set(DS1307_MTH,tens * 10 + ones);
RTC.start();
lcd.setCursor(1,0);
month = RTC.get(DS1307_MTH,true);
if(month < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(month);
delay(keypad_delay);
//**************Set Date*********************************************************************************************
lcd.setCursor(1,3);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 3){tens = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.print(tens);
delay(keypad_delay);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){ones = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.right();
RTC.stop();
RTC.set(DS1307_DATE,tens * 10 + ones);
RTC.start();
lcd.setCursor(1,3);
date = RTC.get(DS1307_DATE,true);
if(date < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(date);
delay(keypad_delay);
//**************Set Year*********************************************************************************************
lcd.setCursor(1,8);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){tens = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.print(tens);
delay(keypad_delay);
for (;;){
key = lcd.keypad();
if(key >= 0 && key <= 9){ones = key; break;}
}
for (;;){
key = lcd.keypad();
if(key == -1){break;}
}
lcd.right();
RTC.stop();
RTC.set(DS1307_YR,tens * 10 + ones);
RTC.start();
lcd.setCursor(1,6);
year = RTC.get(DS1307_YR,true);
if(year < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(year);
delay(keypad_delay);
//**************Finish Up*********************************************************************************************
for (;;){
lcd.cursor_off();
lcd.setCursor(2,0);
lcd.print("Time & Date Set");
delay(keypad_delay);
lcd.setCursor(3,0);
lcd.print("Choose Mode A or B");
delay(keypad_delay);
if(lcd.keypad() == 100){set_time = 1; break;}
if(lcd.keypad() == 101){break;}
}
lcd.clear();
}
break;
}
case 100:{
byte i;
byte present = 0;
byte data[12];
byte addr[8];
long ph_val;
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract, minute, hour, second, date, month, year, mil_time, ph_read;
char buf[12]; //used to convert int to string for displaying on LCD
//Get time from DS1307**********************************************************************************************
hour = RTC.get(DS1307_HR,true); //This is in military time [0,23]
minute = RTC.get(DS1307_MIN,false);
second = RTC.get(DS1307_SEC,false);
// date = RTC.get(DS1307_DATE,false);
// month = RTC.get(DS1307_MTH,false);
// year = RTC.get(DS1307_YR,false);
mil_time = (hour * 100) + minute; //create military time output [0000,2400)
//Get temp data from DS18B20 ***************************************************************************************
if ( !ds.search(addr)) {
ds.reset_search();
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
LowByte = data[0];
HighByte = data[1];
TReading = (HighByte << 8) + LowByte;
SignBit = TReading & 0x8000; // test most sig bit
if (SignBit) // negative
{
TReading = (TReading ^ 0xffff) + 1; // 2's comp
}
Tc_100 = (6 * TReading) + TReading / 4; // multiply by (100 * 0.0625) or 6.25
Tc_100 = (Tc_100 * 9/5) + 3200; //Convert to fahrenheit, comment this out to display in celcius
//Display current temperature*****************************************************************************
lcd.setCursor(0,0);
Whole = (Tc_100 / 100); // separate off the whole and fractional portions
Fract = (Tc_100 % 100);
lcd.print(Whole, DEC);
delay(keypad_delay);
lcd.print(".");
delay(keypad_delay);
if (Fract < 10)
{
lcd.print("0");
delay(keypad_delay);
}
lcd.print(Fract, DEC);
delay(keypad_delay);
lcd.write(0xDF);
delay(keypad_delay);
lcd.print("F ");
delay(keypad_delay);
//Display Time******************************************************************************************
lcd.setCursor(0,10);
if(hour < 10 || (hour > 12 && hour - 12 < 10)){
lcd.print(" ");
delay(keypad_delay);
}
if(hour > 12){
lcd.print(hour - 12, DEC);
delay(keypad_delay);
}
if(hour == 0){
lcd.print(12, DEC);
delay(keypad_delay);
}
if(hour > 0 && hour < 13){
lcd.print(hour, DEC);
delay(keypad_delay);
}
lcd.print(":");
delay(keypad_delay);
if(minute < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(minute, DEC);
delay(keypad_delay);
lcd.print(":");
delay(keypad_delay);
if(second < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(second, DEC);
delay(keypad_delay);
if(hour < 12 || hour == 0){
lcd.print("AM");
delay(keypad_delay);
}
else{
lcd.print("PM");
delay(keypad_delay);
}
//Display High Temp***********************************************************************************
if(on_minute == 0){ //used so if bad data is sent for the first reading, it is not saved
lcd.setCursor(1,0);
if(Tc_100 > High){
High = Tc_100;
}
Whole = (High / 100); // separate off the whole and fractional portions
Fract = (High % 100);
lcd.print("H= ");
delay(keypad_delay);
lcd.print(Whole, DEC);
delay(keypad_delay);
lcd.print(".");
delay(keypad_delay);
if (Fract < 10)
{
lcd.print("0");
delay(keypad_delay);
}
lcd.print(Fract, DEC);
delay(keypad_delay);
lcd.write(0xDF);
delay(keypad_delay);
lcd.print(" ");
delay(keypad_delay);
}
//Display Low Temp***************************************************************************************
if(on_minute == 0){ //used so if bad data is sent for the first reading, it is not saved
if(Tc_100 < Low){
Low = Tc_100;
}
Whole = (Low / 100); // separate off the whole and fractional portions
Fract = (Low % 100);
lcd.print("L= ");
delay(keypad_delay);
lcd.print(Whole, DEC);
delay(keypad_delay);
lcd.print(".");
delay(keypad_delay);
if (Fract < 10)
{
lcd.print("0");
delay(keypad_delay);
}
lcd.print(Fract, DEC);
delay(keypad_delay);
lcd.write(0xDF);
delay(keypad_delay);
lcd.print(" ");
delay(keypad_delay);
}
//Relay Controls ***************************************************************************************
//****************Heater
if(Tc_100 < heater_on_temp){ // turn heater on if temp is below heater_on_temp
digitalWrite(heater, HIGH);
}
if(Tc_100 > heater_off_temp){ //turn heater off if temp is above heater_off_temp
digitalWrite(heater, LOW);
}
//****************Day Lights****************************************************************************
if((lights_off_temp < Tc_100 && digitalRead(day_light) == HIGH) || (digitalRead(day_light) == LOW && on_minute == 1)){
lights_out = (minute + wait_time) % 60;
}
if((lights_out == minute) || (lights_on_time > mil_time || lights_off_time <= mil_time)){
lights_out = -100;
}
if(lights_out == -100 && lights_off_temp > Tc_100 && lights_on_time <= mil_time && lights_off_time > mil_time){
digitalWrite(day_light, HIGH);
}
else{
digitalWrite(day_light, LOW);
}
//****************Moon Lights*******************************************************************************
if(moonl_on_time <= mil_time || moonl_off_time > mil_time){
digitalWrite(moon_light, HIGH);
}
else{
digitalWrite(moon_light, LOW);
}
//****************Fan
if(Tc_100 > fan_on_temp){ // turn fan on if temp is above fan_on_temp
digitalWrite(fan, HIGH);
}
if(Tc_100 < fan_off_temp){ //turn fan off if temp is below fan_off_temp
digitalWrite(fan, LOW);
}
//****************Auto top off********************************************************************************
if(digitalRead(ato_input) == LOW && ato_hour != hour){
digitalWrite(ato, HIGH);
delay(ato_time * 1000); //Turn on ATO pump for the number of seconds defined by ato_time
digitalWrite(ato, LOW); //Turn off ATO Pump
ato_hour = hour; //only allow the ATO to run once per hour.
lcd.setCursor(3,0); //Display the last time the ATO ran
lcd.print("ATO ");
delay(keypad_delay);
if(hour < 10 || (hour > 12 && hour - 12 < 10)){
lcd.print(" ");
delay(keypad_delay);
}
if(hour > 12){
lcd.print(hour - 12, DEC);
delay(keypad_delay);
}
if(hour == 0){
lcd.print(12, DEC);
delay(keypad_delay);
}
if(hour > 0 && hour < 13){
lcd.print(hour, DEC);
delay(keypad_delay);
}
lcd.print(":");
delay(keypad_delay);
if(minute < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(minute, DEC);
delay(keypad_delay);
if(hour < 12){
lcd.print("AM ");
delay(keypad_delay);
}
else{
lcd.print("PM ");
delay(keypad_delay);
}
}
//***********PH Probe**********************************************************************************************
// To calibrate ph probe set 7ph to 2V and 10PH to 1V
total -= readings[index]; // subtract the last reading
readings[index] = analogRead(ph_probe); // read from the sensor
total += readings[index]; // add the reading to the total
index = (index + 1); // advance to the next index
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
ph_val = (-1.47 * average + 1300); //ph is stored 100 times value
Whole = (ph_val / 100); // separate off the whole and fractional portions
Fract = (ph_val % 100);
lcd.setCursor(3,12);
lcd.print("PH ");
delay(keypad_delay);
if (Whole < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print(Whole, DEC);
delay(keypad_delay);
lcd.print(".");
delay(keypad_delay);
if (Fract < 10){
lcd.print("0");
delay(keypad_delay);
}
lcd.print(Fract, DEC);
delay(keypad_delay);
//****************Power heads and skimmer feed mode******************************************************************
while(digitalRead(feed) == LOW && digitalRead(ph_1) == LOW){
pumps_off = -10;
}
while(digitalRead(feed) == LOW && digitalRead(ph_1) == HIGH){
pumps_off = (minute + feed_time) % 60;
}
if(pumps_off == minute || pumps_off == -10){
digitalWrite(ph_1, HIGH);
digitalWrite(ph_2, HIGH);
digitalWrite(skimmer, HIGH);
pumps_off = -10;
}
else{
digitalWrite(ph_1, LOW);
digitalWrite(ph_2, LOW);
digitalWrite(skimmer, LOW);
}
//Display what relays are on******************************************************************************************
lcd.setCursor(2,0);
if(digitalRead(heater) == HIGH){lcd.print("H1 ");delay(keypad_delay);}
else{lcd.print("H0 ");delay(keypad_delay);}
if(digitalRead(day_light) == HIGH){lcd.print("L1 ");delay(keypad_delay);}
else{lcd.print("L0 ");delay(keypad_delay);}
if(digitalRead(moon_light) == HIGH){lcd.print("M1 ");delay(keypad_delay);}
else{lcd.print("M0 ");delay(keypad_delay);}
if(digitalRead(fan) == HIGH){lcd.print("F1 ");delay(keypad_delay);}
else{lcd.print("F0 ");delay(keypad_delay);}
if(digitalRead(ph_1) == HIGH){lcd.print("P1 ");delay(keypad_delay);}
else{lcd.print("P0 ");delay(keypad_delay);}
//Display when normal operation will resume****************************************************************************
if(lights_out != -100){
lcd.setCursor(2,18);
if((lights_out - (minute - 60)) % 60 < 10){
lcd.print(" ");
delay(keypad_delay);
}
lcd.print((lights_out - (minute - 60)) % 60, DEC); //time until lights turn on
delay(keypad_delay);
}
else{
lcd.setCursor(2,18);
lcd.print(" ");
delay(keypad_delay);
}
if(pumps_off != -10){
lcd.setCursor(2,16);
lcd.print((pumps_off - (minute - 60)) % 60, DEC); //time until pumps turn on
delay(keypad_delay);
}
else{
lcd.setCursor(2,16);
lcd.print(" ");
delay(keypad_delay);
}
on_minute = 0; //signals that the program has been run once
} //end case 0
} //end switch
} //end loop
