Code Version 2.0
From Reef Projects
Code is fairly well commented. (9874 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
#include <OneWire.h>
#include <LCDI2C.h>
//#include <stdio.h>
#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>
/*
To do:
* if lights are turned out for one reason or another, extend the photo-period by that amount
* adjust time for daylight savings
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,0x4C); //[# 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
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,30); //set the minutes
RTC.set(DS1307_HR,11); //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){
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.position(0,0);
Whole = (Tc_100 / 100); // separate off the whole and fractional portions
Fract = (Tc_100 % 100);
lcd.println(itoa(Whole, buf, 10));
lcd.println(".");
if (Fract < 10)
{
lcd.println("0");
}
lcd.println(itoa(Fract, buf, 10));
lcd.print(0xDF);
lcd.println("F ");
//Display Time******************************************************************************************
lcd.position(0,10);
if(hour < 10 || (hour > 12 && hour - 12 < 10)){
lcd.println(" ");
}
if(hour > 12){
lcd.println(itoa(hour - 12, buf, 10));
}
if(hour == 0){
lcd.println(itoa(12, buf, 10));
}
if(hour > 0 && hour < 13){
lcd.println(itoa(hour, buf, 10));
}
lcd.println(":");
if(minute < 10){
lcd.println("0");
}
lcd.println(itoa(minute, buf, 10));
lcd.println(":");
if(second < 10){
lcd.println("0");
}
lcd.println(itoa(second, buf, 10));
if(hour < 12 || hour == 0){
lcd.println("AM");
}
else{
lcd.println("PM");
}
//Display High Temp***********************************************************************************
if(on_minute == 0){ //used so if bad data is sent for the first reading, it is not saved
lcd.position(1,0);
if(Tc_100 > High){
High = Tc_100;
}
Whole = (High / 100); // separate off the whole and fractional portions
Fract = (High % 100);
lcd.println("H= ");
lcd.println(itoa(Whole, buf, 10));
lcd.println(".");
if (Fract < 10)
{
lcd.println("0");
}
lcd.println(itoa(Fract, buf, 10));
lcd.print(0xDF);
lcd.println(" ");
}
//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.println("L= ");
lcd.println(itoa(Whole, buf, 10));
lcd.println(".");
if (Fract < 10)
{
lcd.println("0");
}
lcd.println(itoa(Fract, buf, 10));
lcd.print(0xDF);
}
//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.position(3,0); //Display the last time the ATO ran
lcd.println("ATO ");
if(hour < 10 || (hour > 12 && hour - 12 < 10)){
lcd.println(" ");
}
if(hour > 12){
lcd.println(itoa(hour - 12, buf, 10));
}
if(hour == 0){
lcd.println(itoa(12, buf, 10));
}
if(hour > 0 && hour < 13){
lcd.println(itoa(hour, buf, 10));
}
lcd.println(":");
if(minute < 10){
lcd.println("0");
}
lcd.println(itoa(minute, buf, 10));
if(hour < 12){
lcd.println("AM ");
}
else{
lcd.println("PM ");
}
}
//***********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.position(3,12);
lcd.println("PH ");
if (Whole < 10){
lcd.println(" ");
}
lcd.println(itoa(Whole, buf, 10));
lcd.println(".");
if (Fract < 10){
lcd.println("0");
}
lcd.println(itoa(Fract, buf, 10));
//****************Power heads and skimmer feed mode******************************************************************
if(digitalRead(feed) == LOW){
pumps_off = (minute + feed_time) % 60;
}
if(digitalRead(feed) == LOW && digitalRead(ph_1) == LOW){
pumps_off = -10;
}
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.position(2,0);
if(digitalRead(heater) == HIGH){lcd.println("H1 ");}
else{lcd.println("H0 ");}
if(digitalRead(day_light) == HIGH){lcd.println("L1 ");}
else{lcd.println("L0 ");}
if(digitalRead(moon_light) == HIGH){lcd.println("M1 ");}
else{lcd.println("M0 ");}
if(digitalRead(fan) == HIGH){lcd.println("F1 ");}
else{lcd.println("F0 ");}
if(digitalRead(ph_1) == HIGH){lcd.println("P1 ");}
else{lcd.println("P0 ");}
//Display when normal operation will resume****************************************************************************
if(lights_out != -100){
lcd.position(2,18);
if((lights_out - (minute - 60)) % 60 < 10){
lcd.println(" ");
}
lcd.println(itoa((lights_out - (minute - 60)) % 60, buf, 10)); //time until lights turn on
}
else{
lcd.position(2,18);
lcd.println(" ");
}
if(pumps_off != -10){
lcd.position(2,16);
lcd.println(itoa((pumps_off - (minute - 60)) % 60, buf, 10)); //time until pumps turn on
}
else{
lcd.position(2,16);
lcd.println(" ");
}
on_minute = 0; //signals that the program has been run once
} //end loop
