Water Heater Control

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");
  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");
    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");


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.setCursor(0, 0);
  lcd.print("Up Sensor");
  lcd.setCursor(12, 0);
  lcd.setCursor(0, 1);
  lcd.print("low Sensor");
  lcd.setCursor(12, 1);
  delay(1000);  // delays are not always the best choice but in this application speed is not critical.  it is a slow moving process