Tuesday, 6 January 2015

Arduino Water Tank Level Sensor with LCD Display and Dry Run Pump Protection

I live on a large liveaboard boat with my wife and two cats which keep mice at bay - the cats, not my wife! - Anyways... all our water on the boat is tanked and needs to be filled every few days. To date this has been either waiting until it has run out (normally while my wife is running a sodding bath!) or checking the tanks to make sure we have enough (involves lifting hatches and other ball-ache-worthy problems). 


I have always wanted to install a level sensor but have been so busy with work and other projects that I never got around to it. I recently saw the HC SR04 ultrasonic sensor being used in a robotics project and thought about using it for level sensing instead. I use utrasonic sensors at work (as a process engineer) and they are great, no maintenance, highly accurate BUT seriously pricy! So, the fact that I could get these for a couple of quid sold me on the idea.


The sensor can be "potted" in epoxy to waterproof it and fitted to the top of the tank quite easily, there are a number of online tutorials for potting PCB boards (I would avoid the silicone sealent methods - not very durable and it will peal after a few months). This is a good quide:

http://www.submarineboat.com/waterproofing.htm





The project utilises the following items:


  • Arduino Shield (Any really, I am not using many IO's.
  • HC SR04 Ultrasonic sensor.
  • Relay Shield - I am using a 2 relay model, you can use as many as you wish you could add code for any number of pump controls, buzzers, LED's etc.
  • An LCD display - I am using a 20x04 display, if you use the more common 16x02 you will need to change the LCD code to suit, easy enough though.

 Pin Outs:

  SR04 VCC to Arduino 5V
  SR04 GND to Arduino GND
  SR04 Echo to Arduino pin 13
  SR04 Trig to Arduino pin 10

  Standard LCD configuration.

  Relay shield VCC to Arduino 5V
  Relay shield GND to Arduino GND
  Relay Shield Inx to Arduino pin 9

 

Description:

The initial Ultrasonic portion of this code has been adapted and modified from the HC SR04 ping sketch from Trollmaker.com
 
 The code was created to provide a tank volume indicator in a liveaboard boat, using a 2004 LCD display which shows the current volume of water and at certain volumes to prompt the user to be cautious of their water usage.
  

When the tank volume reaches less than 175 litres a screen prompt says that there is not enough water for a bath or to run the washing machine.
 
  If the tank has less than 45 litres (only a couple of inches left in this particular tank) it will pull down the relay signal (DRRelay) on pin 9 and energise the relay. The pump 12VDC supply is then cut and protects it from running dry.
 
  If the tank has more than 175 Litres the system runs with a default display of volume in litres and level OK prompts. I like to have a splash screen on all my projects within the void setup(), though this is not necessary at all, feel free to edit as much as you like.
  



VARIABLES WHICH YOU WILL NEED TO CHANGE:


  • The "volume" float is currenty scaled (* 0.001) to give an output in litres, this could easily be changed to ImpG (* 0.000212) or USGal (* 0.000264).
  •  The "tankarea" float will need to be changed depending on the area of the tank base, this could be an int, but I have put it in as a float incase anyone has a cylindrical tank and wants to maintain decimals after calculating.
  • The "distance" is dependant upon the first integer of the "distance = x" in the void loop()(in my case "45") this is the distance from the SR04 unit to the bottom of the tank; therefore distance = total depth of tank - distance travelled/2 in order to give the depth of the water rather the depth of the air above the water.

     
     Code:


#define trigPin 10
#define echoPin 13
#include                   // lib. for LCD
#include                            // lib. for Wire
int DRRelay = 9;                            // pin which connects to Dry Run Relay coil +5V.
float volume;                               // define name of volume formula.
float tankarea;                             // define name of tank footprint area.
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);      // pins connected to LCD

void setup() 

{
  Serial.begin (9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(9,OUTPUT);                  // Output for Dry Run Relay.
  lcd.begin(20, 4);
  lcd.setCursor(0,0);
  lcd.print ("                "); // Welcome Splash, write whatever you want here.
  lcd.setCursor(0,1);
  lcd.print ("                "); // Welcome Splash, write whatever you want here.
  lcd.setCursor(0,2);
  lcd.print ("                "); // Welcome Splash, write whatever you want here.
  lcd.setCursor(0,3);
  lcd.print ("                "); // Welcome Splash, write whatever you want here.
  delay(2000);
  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print (" Tank Level Monitor "); // Welcome Splash. Page two.
  lcd.setCursor(0,2);
  lcd.print ("   Version 1.0.1b   "); // Welcome Splash. Page two.
  delay(2000);
  lcd.clear();
}

void loop() 

{
  long duration, distance;
  digitalWrite(DRRelay, HIGH);
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = 45 - ((duration/2) / 29.1);            // depth of water where "45" = dist from sensor to bottom of tank CMs.
  tankarea = 2700;
  volume = ((distance * tankarea) * 0.001);


  if (volume <= 45)                                 // If the tank has less than or equal to 45 litres, then:
    {
    digitalWrite(DRRelay, LOW);                     // Protects pump from dry running, switches off.
    lcd.clear();
    lcd.setCursor(0,0);                             // sets cursor to 1st line
    lcd.print (" Water Tank TOO LOW ");             // Indicates the tank level is getting low
    lcd.setCursor(0,1);                             // sets cursor to 2nd line
    lcd.print (volume);                             // Prints the volume.
    lcd.setCursor(12,1);                            // sets cursor to 2nd line 12th char.
    lcd.print ("Litres");                           // Text.
    lcd.setCursor(0,2);                             // sets cursor to 3rd line
    lcd.print (" Fill Tank To Start ");             // Text.
    lcd.setCursor(0,3);                             // sets cursor to 4th line
    lcd.print ("     Pump Again     ");             // Text.
    delay(50);
    }
    
   else if(volume <= 174)                           // if the tank has less than 175 litres, then:
    {
    digitalWrite(DRRelay, HIGH);                    // maintains pump function.
    lcd.clear();
    lcd.setCursor(0,0);                             // sets cursor to 1st line
    lcd.print ("  Tank Getting Low  ");             // Indicates the tank level is getting low
    lcd.setCursor(0,1);                             // sets cursor to 2nd line
    lcd.print (volume);                             // Prints the volume.
    lcd.setCursor(12,1);                            // sets cursor to 2nd line 12th char.
    lcd.print ("Litres");                           // Text.
    lcd.setCursor(0,2);                             // sets cursor to 3rd line
    lcd.print ("Fill Tank Before");                 // Text.
    lcd.setCursor(0,3);                             // sets cursor to 4th line
    lcd.print ("Bath or Washing M/C");              // Text.
    delay(50);
    }
    
  else 
    {
    digitalWrite(DRRelay, HIGH);                    // maintains pump function.
    lcd.clear();
    lcd.setCursor(0,0);                             // sets cursor to 1st line
    lcd.print ("Mains Water Tank OK");              // Indicates the tank level is getting low
    lcd.setCursor(0,1);                             // sets cursor to 2nd line
    lcd.print (volume);                             // Prints the volume.
    lcd.setCursor(12,1);                            // sets cursor to 2nd line 12th char.
    lcd.print ("Litres");                           // Text.
    lcd.setCursor(0,2);                             // sets cursor to 3rd line
    lcd.print ("OK for Bath, Shower ");                 // Text.
    lcd.setCursor(0,3);                             // sets cursor to 4th line
    lcd.print ("  or Washing M/C   ");              // Text.
    delay(50);
    }
  
  delay(500);
  
}
/* --(end main loop )-- */

/* ( THE END ) */

So, that's it really. Feel free to use this and modify it however you see fit.