/*
Purpose: Aquarium Controller
Version 2.00, 2012-07-25
Software: Arduino Version 022
Date Version Details
2010, Sept 1.00 Originally written by Christian, cptbjorn@gmail.com
2011, Nov 1.00 Modified by Cesar, caddnima@gmail.com,
https://sites.google.com/site/caddnima/myreeftank/led_controller
a) Arduino Uno, 16x2 LCD Shield used
b) modified to accept analog LED drivers, ELN 60-48D, in 10 steps.
c) modified to keep running even when restarted during ramp up or down.
d) added wavemaker via 5v relays to control powerheads on and off at given interval.
2012, August 2.00 Added temperature display
a) NTC Temperature Sensor Thermistor used. Temp smoothing to 30 sec added.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 3
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Functions:
10:00am = The program begins. See "int ontime = 10 ;" below.
10:00am to 11:00am = Blue light will ramp up to max. See "int blueramptime = 60 ;" below.
11:00am to 2:00pm = White light will ramp up to max. See "int whiteramptime = 180 ;" below.
2:00pm to 6:00pm = Both blue and white is at max. See "int photoperiod = 240 ;" below.
6:00 to 9:00pm = White light will ramp down to min. See "int whiteramptime = 180 ;" below.
9:00pm to 10:00pm = Blue light will ramp down to min. see "int blueramptime = 60 ;" below.
Misc:
Relay 1 = Will go on and off every 15 seconds. See "long interval1 = 15000; " below.
Relay 2 = Will go on and off every 30 seconds. See "long interval2 = 30000; " below.
Temperature = Set at 81F deg. See "void printTemp()" below.
= Temp readings are averaged between 30 readings. See "void printTemp()" below.
Why Morning and Evening Sleep:
For some reason, when connected to several relay switches. It is pulling too much power from
Arduino board, and it is creating malfunction on the current time. The time jumps everywhere,
thus creating premature triggers on the program. And it only happens outside of the loop function,
while the led is not ramping up, max and ramping down.
*/
#define DS1307_I2C_ADDRESS 0x68 // address used for clock
#include "Wire.h" // library used
#include <LiquidCrystal.h> // library used
/* ******************************************************************************************************************** */
/* * * */
/* * R E L A Y P A R T * */
/* * S I M P L E O N A N D O F F F E A T U R E * */
/* * * */
/* ******************************************************************************************************************** */
const int relayPin1 = 2; // arduino pin (non pwm) used for relay 1
const int relayPin2 = 8; // arduino pin (non pwm) used for relay 2
const int tempPin1 = 10; // arduino pin (non pwm) used for temperature/relay
int relayState1 = LOW;
int relayState2 = LOW;
long previousMillis1 = 0; // assigned for relay 1
long previousMillis2 = 0; // assigned for relay 2
long tempMillis = 0; // assigned for temp sensor
long interval1 = 10000; // how many milliseconds to turn it on and off for RELAY1
long interval2 = 20000; // how many milliseconds to turn it on and off for RELAY2
/* ******************************************************************************************************************** */
/* * * */
/* * L E D D I M M I N G P A R T * */
/* * F A D E S I N A N D O U T * */
/* * * */
/* ******************************************************************************************************************** */
/* ********************************************************************************* */
/* * * */
/* * Note: time for ramp down is the same as time for ramp up. * */
/* * * */
/* ********************************************************************************* */
int ontime = 10 ; // what time do you want the blue to start to ramp up?
int blueramptime = 60 ; // how many minutes do you want the blue to ramp up?
int whiteramptime = 180 ; // after the blue, how many minutes do you want the white to ramp up?
int photoperiod = 240 ; // after the white, how many minutes do you want it to stay at max intensity?
int blue = 3; // which arduino pwm pin are you using for the blue?
int white = 11; // which arduino pwm pin are you using for the white?
int bluemin = 0 ;
int whitemin = 0 ;
int total = 0; // used for temp average
int t = 0; // used for temp smoothing function
int average = 0; // used to average or smooth temp reading
/* ********************************************************************************* */
/* * * */
/* * the line below is needed if you are using meanwell ELN60-48D, for analog * */
/* * these are the values in 10% increments * */
/* * [11] is not the arduino pin but the number of steps used from 0 to 255 * */
/* * * */
/* ********************************************************************************* */
int bluepercent[11] = { 0, 1, 2, 5, 8 ,12, 18, 27, 44, 80, 255 };
int whitepercent[11] = { 0, 1, 2, 5, 8 ,12, 18, 27, 44, 80, 255 };
/* ********************************************************************************* */
/* * * */
/* * the line below is needed if you are using meanwell ELN60-48P, for pwm * */
/* * these are the values in 10% increments * */
/* * [11] is not the arduino pin but the number of steps used from 0 to 255 * */
/* * just remove // sign below for the blue and white * */
/* * and add // sign above for the blue and white * */
/* * * */
/* ********************************************************************************* */
//int bluepercent[11] = { 0, 26, 52, 78, 103, 128, 154, 180, 205, 230, 255 };
//int whitepercent[11] = { 0, 26, 52, 78, 103, 128, 154, 180, 205, 230, 255 };
int sleeppercent[11] = { 0, 0, 0, 0, 0 ,0, 0, 0, 0, 0, 0 };
// int pwm_one = 10; // extra pwm pin for future use (spare)
// int pwm_one = 9; // extra pwm pin for future use (spare)
/* ********************************************************************************* */
/* * * */
/* * originally arduino pins 8, 9, 4, 5, 6, 7 are used, * */
/* * I have to use different arduino non pwm pins, so I can reserved the pwm * */
/* * pins for other use. * */
/* * * */
/* ********************************************************************************* */
LiquidCrystal lcd(12, 13, 4, 5, 6, 7);
/* ******************************************************************************************************************** */
/* * * */
/* * R T C C L O C K D S 1 3 0 7 * */
/* * * */
/* ******************************************************************************************************************** */
byte decToBcd(byte val) // Convert normal decimal numbers to binary coded decimal
{
return ( (val/10*16) + (val%10) );
}
byte bcdToDec(byte val) // Convert binary coded decimal to normal decimal numbers
{
return ( (val/16*10) + (val%16) );
}
// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers
void setDateDs1307(byte second, // 0-59
byte minute, // 0-59
byte hour, // 1-23
byte dayOfWeek, // 1-7
byte dayOfMonth, // 1-28/29/30/31
byte month, // 1-12
byte year) // 0-99
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.send(0);
Wire.send(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.send(decToBcd(minute));
Wire.send(decToBcd(hour)); // If you want 12 hour am/pm you need to set
// bit 6 (also need to change readDateDs1307)
Wire.send(decToBcd(dayOfWeek));
Wire.send(decToBcd(dayOfMonth));
Wire.send(decToBcd(month));
Wire.send(decToBcd(year));
Wire.endTransmission();
}
// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
// Reset the register pointer
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.send(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
// A few of these need masks because certain bits are control bits
*second = bcdToDec(Wire.receive() & 0x7f);
*minute = bcdToDec(Wire.receive());
*hour = bcdToDec(Wire.receive() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.receive());
*dayOfMonth = bcdToDec(Wire.receive());
*month = bcdToDec(Wire.receive());
*year = bcdToDec(Wire.receive());
}
/* ******************************************************************************************************************** */
/* * * */
/* * D E F I N E : O N E S E C O N D * */
/* * * */
/* ******************************************************************************************************************** */
void onesecond() //function that runs once per second while program is running
{
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
lcd.setCursor(0, 0);
if(hour>0){
if(hour<=12)
{
lcd.print(hour, DEC);
}
else
{
lcd.print(hour-12, DEC);
}
}
else
{
lcd.print("12");
}
lcd.print(":");
if (minute < 10) {
lcd.print("0");
}
lcd.print(minute, DEC);
if(hour<12)
{
lcd.print("am");
}
else
{
lcd.print("pm");
}
lcd.print(" ");
delay(1000);
}
/* ******************************************************************************************************************** */
/* * * */
/* * D E F I N E : T H E R M I S T O R * */
/* * * */
/* ******************************************************************************************************************** */
double Thermistor(int RawADC) {
double Temp;
// See See http://en.wikipedia.org/wiki/Thermistor for explanation of formula
Temp = log(((10240000/RawADC) - 10000));
Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
Temp = Temp - 273.15; // Convert Kelvin to Celcius
Temp = (Temp * 1.8) + 32.0; // Convert to Fahrenheit
return Temp;
}
/* ******************************************************************************************************************** */
/* * * */
/* * D E F I N E : P R I N T T E M P * */
/* * * */
/* ******************************************************************************************************************** */
void printTemp()
{
double temp = Thermistor(analogRead(3)); // Read sensor on Analog Pin 3
if (t <= 30) (t = t + 1); else (t = 1);
if (t <= 30) (total=total+temp); else (total=0);
if (t == 30) (average=total/30);
lcd.setCursor(9,0);
lcd.print(" ");
lcd.print(average); // Prints out Temp, 0 converts it to real numbers, no decimal
lcd.print((char)223); // Prints degree character
lcd.print("F ");
/* ------------------------------------------------------------------------------------------------------
This is where we control the FAN to go on or off.
------------------------------------------------------------------------------------------------------*/
unsigned long currenttempMillis = millis(); // Use blink without delay to set temp sampling frequency
if(currenttempMillis - tempMillis > 10000) // 30 seconds delay between on and off. We do not want
{ // for our fan to be ON and off too fast.
tempMillis = currenttempMillis;
if (average > 81)
{
digitalWrite(tempPin1, HIGH); // If temp is greater than 81, fan is on
lcd.setCursor(10,0);
lcd.print("Fan:On ");
Serial.println(" Fan:On ");
}
else
{
digitalWrite(tempPin1, LOW); // If temp is less than 81, fan is off
lcd.setCursor(9,0);
lcd.print("Fan:Off ");
Serial.println(" Fan:Off ");
}
}
} // End printTemp
/* ******************************************************************************************************************** */
/* * * */
/* * D E F I N E : R E L A Y 1 * */
/* * * */
/* ******************************************************************************************************************** */
void relay1() //FUNCTION TO TURN ON AND OFF RELAY 1.
{
unsigned long currentMillis = millis();
if(currentMillis - previousMillis1 > interval1)
{
previousMillis1 = currentMillis;
if (relayState1 == LOW)
relayState1 = HIGH;
else
relayState1 = LOW;
digitalWrite(relayPin1, relayState1);
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * D E F I N E : R E L A Y 2 * */
/* * * */
/* ******************************************************************************************************************** */
void relay2()
{
unsigned long currentMillis2 = millis();
if(currentMillis2 - previousMillis2 > interval2)
{
previousMillis2 = currentMillis2;
if (relayState2 == LOW)
relayState2 = HIGH;
else
relayState2 = LOW;
digitalWrite(relayPin2, relayState2);
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * S E T U P * */
/* * * */
/* ******************************************************************************************************************** */
void setup() {
pinMode(relayPin1, OUTPUT); // set the digital pin as output:
pinMode(relayPin2, OUTPUT); // set the digital pin as output:
pinMode(tempPin1, OUTPUT); // set the digital pin as output:
Serial.begin(9600);
/* ******************************************************************************************************************** */
/* * * */
/* * S E T U P - D I S P L A Y * */
/* * * */
/* ******************************************************************************************************************** */
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
Wire.begin();
second = 57;
minute = 39;
hour = 18;
dayOfWeek = 0; // Sunday is 0
dayOfMonth = 12;
month = 8;
year = 12;
/* ********************************************************************************* */
/* * HOW TO SETUP TIME * */
/* * * */
/* * 1) change the second, minute, hour, etc... above * */
/* * 2) remove the "//" before setDateDs1307 below * */
/* * 3) and load this sketch to your arduino * */
/* * 4) after loading the sketch, put the "//" back again * */
/* * 5) and load this sketch again to your arduino, and save * */
/* * * */
/* * * */
/* ********************************************************************************* */
setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
analogWrite(blue, bluemin);
analogWrite(white, whitemin);
lcd.begin(16, 2); // set up the LCD's number of rows and columns:
// lcd.print("12:00 80.6"); // Print a message to the LCD.
// lcd.print(char(223));
lcd.setCursor(0, 1);
lcd.print("blue:");
lcd.print(33*bluemin/85);
lcd.setCursor(8, 1);
lcd.print("white:");
lcd.print(33*whitemin/85);
}
/* ******************************************************************************************************************** */
/* * * */
/* * L O O P * */
/* * * */
/* ******************************************************************************************************************** */
void loop()
{
onesecond();
relay2();
relay1();
printTemp();
/* ******************************************************************************************************************** */
/* * * */
/* * L O O P - D I M F U N C T I O N * */
/* * * */
/* ******************************************************************************************************************** */
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
int daybyminute = ((hour * 60) + minute); //converts time of day to a single value in minutes
// When controller gets reset during morning sleep.
int morning;
if (daybyminute >= 0)
morning = ((ontime*60) - daybyminute);
// When controller gets reset during BLUE ramping up.
int bluerampup;
if (daybyminute >= (ontime*60))
bluerampup = (((ontime*60) + blueramptime) - daybyminute);
else
bluerampup = blueramptime;
// When controller gets reset during WHITE ramping up.
int whiterampup;
if (daybyminute >= (ontime*60 + blueramptime))
whiterampup = (((ontime*60) + blueramptime + whiteramptime) - daybyminute);
else
whiterampup = whiteramptime;
// When controller gets reset during MAX time.
int photomax;
if (daybyminute >= ((ontime * 60) + blueramptime + whiteramptime))
photomax = (((ontime*60) + photoperiod + blueramptime + whiteramptime) - daybyminute);
else
photomax = photoperiod;
// When controller gets reset during WHITE ramping down.
int whiterampdown;
if (((ontime * 60) + photoperiod + blueramptime + whiteramptime) <= daybyminute)
whiterampdown = (((ontime*60) + photoperiod + blueramptime + 2*whiteramptime) - daybyminute);
else
whiterampdown = whiteramptime;
// When controller gets reset during BLUE ramping down.
int bluerampdown;
if (((ontime * 60) + photoperiod + blueramptime + 2*whiteramptime) <= daybyminute)
bluerampdown = (((ontime*60) + photoperiod + 2*blueramptime + 2*whiteramptime) - daybyminute);
else
bluerampdown = blueramptime;
// When controller gets reset during evening sleep.
int evening;
if (daybyminute >= 1320)
evening = (1440 - daybyminute); //1440 is 12:00 midnight
/* ******************************************************************************************************************** */
/* * * */
/* * M O R N I N G - S L E E P * */
/* * * */
/* ******************************************************************************************************************** */
if (daybyminute >= 0)
{
if (daybyminute <= ((ontime*60)-1)) //if time is in range, make sure that it is off.
{
// make sure that all light is off during morning sleep.
for (int x = 1; x <= 10; x++)
{
int i = 0;
analogWrite(blue, bluepercent[i]);
lcd.setCursor(5, 1);
lcd.print(i);
analogWrite(white, whitepercent[i]);
lcd.setCursor(14, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((morning*60)/10); // calculates seconds to next step
Serial.print("Morning: ");
Serial.println(morning);
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.print(" Morning Countdown: ");
Serial.println(countdown);
Serial.print(" Morning Intensity: ");
Serial.println(i);
relay2();
relay1();
printTemp();
}
}
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * L O O P - F A D E I N * */
/* * * */
/* ******************************************************************************************************************** */
if (daybyminute >= (ontime*60))
{
if (daybyminute <= ((ontime*60) + blueramptime + (whiteramptime - 1))) //if time is in range of fade in, start fading in + (whiteramptime/10*9)
{
// fade blue LEDs in from min to max.
for (int i = 1; i <= 10; i++) // setting i value for 10% increment. Start with 0%
{
analogWrite(blue, bluepercent[i]);
lcd.setCursor(5, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((bluerampup*60)/10); // calculates seconds to next step
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.println(countdown);
relay2();
relay1();
printTemp();
}
}
// fade white LEDs in from min to max.
for (int i = 1; i <= 10; i++) // setting i value for 10% increment. Start with 0%
{
analogWrite(white, whitepercent[i]);
lcd.setCursor(14, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((whiterampup*60)/10); // calculates seconds to next step
Serial.print("whiterampup: ");
Serial.println(whiterampup);
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.println(countdown);
relay2();
relay1();
printTemp();
}
}
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * L O O P - M A X V A L U E * */
/* * * */
/* ******************************************************************************************************************** */
if (daybyminute >= ((ontime * 60) + blueramptime + whiteramptime))
{
if ( daybyminute < ((ontime * 60) + blueramptime + whiteramptime + (photoperiod - 1))) // if time is in range of photoperiod, turn lights on to maximum fade value
{
// make sure that all light is off during morning sleep.
for (int x = 1; x <= 10; x++)
{
int i = 10;
analogWrite(blue, bluepercent[i]);
lcd.setCursor(5, 1);
lcd.print(i);
analogWrite(white, whitepercent[i]);
lcd.setCursor(14, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((photomax*60)/10); // calculates seconds to next step
Serial.print("Lunch MAX: ");
Serial.println(photomax);
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.print(" Lunch MAX Countdown: ");
Serial.println(countdown);
Serial.print(" Lunch MAX Intensity: ");
Serial.println(i);
relay2();
relay1();
printTemp();
}
}
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * L O O P - F A D E O U T * */
/* * * */
/* ******************************************************************************************************************** */
if (daybyminute >= ((ontime * 60) + blueramptime + whiteramptime + photoperiod))
{
if (daybyminute <= ((ontime * 60) + (photoperiod - 1) + 2*whiteramptime + 2*blueramptime))
{
// fade white LEDs out from max to min in increments of 1 point:
for (int i = 10; i >= 0; i--) // setting i value for 10% increment. Start with 10%
{
analogWrite(blue, 255);
lcd.setCursor(5, 1);
lcd.print(10);
lcd.print(" ");
analogWrite(white, whitepercent[i]);
lcd.setCursor(14, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((whiterampdown*60)/10); // calculates seconds to next step
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.print(" Fade Out Countdown: ");
Serial.println(countdown);
Serial.print(" Fadeo Out Intensity: ");
Serial.println(i);
relay2();
relay1();
printTemp();
}
}
// fade blue LEDs out from max to min in increments of 1 point:
for (int i = 10; i >= 0; i--) // setting i value for 10% increment. Start with 10%
{
analogWrite(blue, bluepercent[i]);
lcd.setCursor(5, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((bluerampdown*60)/10); // calculates seconds to next step
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.println(countdown);
Serial.print(" Bluerampdown Intensity: ");
Serial.println(i);
relay2();
relay1();
printTemp();
}
}
}
}
/* ******************************************************************************************************************** */
/* * * */
/* * E V E N I N G - S L E E P * */
/* * * */
/* ******************************************************************************************************************** */
if (daybyminute >= 1320)
{
if (daybyminute <= 1440) //if time is in range, make sure that it is off.
{
// make sure that all light is off during morning sleep.
for (int x = 1; x <= 10; x++)
{
int i = 0;
analogWrite(blue, bluepercent[i]);
lcd.setCursor(5, 1);
lcd.print(i);
analogWrite(white, whitepercent[i]);
lcd.setCursor(14, 1);
lcd.print(i);
lcd.print(" ");
int countdown = ((evening*60)/10); // calculates seconds to next step
Serial.print("Evening: ");
Serial.println(evening);
while (countdown>0)
{
onesecond(); // updates clock once per second
countdown--;
Serial.print(" Evening Countdown: ");
Serial.println(countdown);
Serial.print(" Evening Intensity: ");
Serial.println(i);
relay2();
relay1();
printTemp();
}
}
}
}
//if (daybyminute >= 1339) {return;}
} // END LOOP