Code Version 2.2

From Reef Projects

Jump to: navigation, search

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


#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
Personal tools