My smart energy water heater controller stopped working, so i thought i would just build one.
For those that dont know most water heaters have two heating elements. One for the top half and one for the bottom have. Conventional water heaters are setup so that only one element can work at a time. The priority goes to the top element since the cold water inlet is piped thru the heater to the bottom of the tank. This way the most available water gets heated first then the water on the bottom.
Below is my code. I plan on making a second version with a menu in order to change set points and possibly modes of operation, but for now i just need a water heater! Will also incorporate the EEPROM to save user changed variables in case of power failure.
I welcome feedback on my code and/or hope maybe this can help someone understand a problem they may be having.
Happy Arduinoing everyone.
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //setup lcd pin connections
int tempup; //declare upper temp sensor
int celsiusup; // declare upper celsius temp (for converting purposes)
int fahrenheitup; // declare upper fahrenheit temp
int tempdown; // these three are same as above for lower sensor
int celsiusdown;
int fahrenheitdown;
const int onsetpt = 110; // setup variable for turn on temp
const int offsetpt = 120; // setup varible for turn off temp
const int upheat = 9; // setup variable for output to upper heater
const int downheat = 10; // setup variable for output to lower heater
bool checkup = false; //setup upper sensor test
bool checkdown = false; //setup lower sensor test
void setup() {
lcd.begin(16, 2); // initialize lcd as 16 character 2 line
pinMode (upheat, OUTPUT); // define upper heat output pin
pinMode (downheat, OUTPUT); // define lower heat output pin
digitalWrite (upheat, HIGH); //initialize failsafe for upper heat
digitalWrite (downheat, HIGH); //initialize faisafe for lower heat
}
void loop() {
readtemp(); // go read temps for upper and lower sensors
checksensors(); // check that sensors are reliable
setoutputs(); // change relay states as needed to maintain temp
setdisplay(); // show temps on lcd
}
//=====================End of Main Loop, subs Below===========
void readtemp() {
tempup = analogRead(A0); // read raw value at analog in 0
tempdown = analogRead(A1); // read raw value at analog in 1
celsiusup = tempup / 2; // required with the LM35DZ temp sensor designed to read Celsius
fahrenheitup = (celsiusup * 18) + 320; // formula to convert Celsius to Fahrenheit (factors are multiplied by 10)
fahrenheitup = fahrenheitup / 10; // and divided back out here. This increases accuracy while still using integars
celsiusdown = tempdown / 2; // same as above just the other sensor
fahrenheitdown = (celsiusdown * 18) + 320;
fahrenheitdown = fahrenheitdown / 10;
}
void checksensors() {
readtemp(); // this is needed so when a sensor fails and restores the program can see it and break out of loop
if (checkup == false || checkdown == false) { // these will always be false on first run, ensures code checks sensors before setting output
digitalWrite(upheat, HIGH); // while any sensor is invalid force outputs off. i am using a 2 relay module with opto-isolators so high=off and low=on
digitalWrite(downheat, HIGH);
lcd.clear(); // this code will run on first run and anytime below checks return a false. causes display to show "checking sensors"
lcd.setCursor(0, 0);
lcd.print("Checking Sensors");
delay(1000);
}
if (fahrenheitup > 32 && fahrenheitup < 200) { // test that sensor is between a beliveable range
checkup = true; // set check to true
}
else {
checkup = false; // if not return a false check and display on lcd
lcd.setCursor(0, 1);
lcd.print("up sensor fail");
delay(500);
checksensors(); // this forces controler to continue checking sensor until it is good result
}
if (fahrenheitdown > 32 && fahrenheitdown < 200) { // see above as this is same but for lower sensor
checkdown = true;
}
else {
checkdown = false;
lcd.setCursor(0, 1);
lcd.print("Low sensor Fail");
delay(500);
checksensors();
}
}
void setoutputs() {
if (fahrenheitup <= onsetpt) { // compare upper reading to set point; if less then turn on upper element and turn off lower
digitalWrite(downheat, HIGH); // remember with relay module high=off, low=on
digitalWrite(upheat, LOW);
}
if (fahrenheitup >= offsetpt) { // turn off upper element when temp is at off set point
digitalWrite(upheat, HIGH);
}
if ((digitalRead(upheat) == HIGH) && (fahrenheitdown <= onsetpt)) { // as long as upper heat is not on it is ok to turn on lower heat if below on set point
digitalWrite(downheat, LOW);
}
if (fahrenheitdown >= offsetpt) { // turn off lower heat when temp gets to off set point
digitalWrite(downheat, HIGH);
}
}
void setdisplay() { // this is just displaying temps on lcd at pre-defined locations
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Up Sensor");
lcd.setCursor(12, 0);
lcd.print(fahrenheitup);
lcd.setCursor(0, 1);
lcd.print("low Sensor");
lcd.setCursor(12, 1);
lcd.print(fahrenheitdown);
delay(1000); // delays are not always the best choice but in this application speed is not critical. it is a slow moving process
}