We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you've provided to them or that they've collected from your use of their services.

Measuring liquid levels

The title sounds great but it is just to turn your attention away from the fact that we are talking about a sewage tank level sensor. It happened to me lately that I had forgotten about emptying it when the time came.  I learnt about the error quite suddenly, when the bathroom at ground floor started to spread anunpleasant stench.  It was Friday evening, no chance to get the tank emptied whatsoever... I claim to know so much about intelligent homes and here I am, sewage in the bathroom.  That was it...

I needed 8$ to buy 5 HC-SR04 sensors at www.aliexpress.com.  The package arrived in 12 days.  The price is ridiculous; the extra unis will always come in handy, especially when you use the for them first time and might break something. The sensors are very easy to use: you need to get them connected to 5V (VCC and GND), send a pulse to TRIG and wait for answer (pulse) on ECHO.  The time between sending and receiving the pulse tells us about the measured distance.


The idea how to connect the sensor to the RPi and the code in Python comes entirely from the web page of Matt Hawkins from raspberry-spy.co.uk.  Since the sensor is powered with 5V, the pulse which is returned as echo is also 5V.  That is too much for the RPi, which accepts 3,3V on the GPIOs. The voltage must therefore be lowered with the use of 2 resistors:

US RPi connection

In my case I connected the sensor to my GPIO replication (described here), which already protects the RPi against over-voltage with a zener diode.  The R330 resistor is also there.  Consequently I needed nothing additional to use the HC-SR04 right from the box.

I decided to use the Z54JH case from Kradex as the case for the sensor.  I also bought a standard plastic pipe (18mm) used for electric installations.  I drilled two holes in the case and glued-in the pipes with a 2-component glue. Why the pipes?  During my raw test I found that the sensor, when place aside a flat surface, has problem reading the distance.  The well to the sewage tank is in my case 1.5m deep.  I would not be able to mount the sensor at the very low-end of it.  It might also get wet hanging so low.  I had to place it just deep enough to still be able to connect it and replace it when needed.  There, however, it would hang on the wall and the readings would be false.  I do not know if this is the right way to do such things, but it works. With the pipes connected there is no echo from the wall the sensor hangs on.  Additionally the precision is not the key here.  I do not need exact millimeters, I just need proportions to know when the tank is full and when it is empty.


Finally the whole mounting exercise took me two weeks as I had to remove and place back 12meters of the pavement to prepare the wire.  Then, with my body half way in the well I drilled the walls and soldered the plug (made of a gold-pin bar).  Here is how it all looks like:


As you see the pipes are mounted with a standard wall hanger used for electric installations.  The set-out of the mount is slightly to broad, but it does not disturb the sensor.

Now the time has come for the coding.  Here is the file for reading the distance (us.py):

# ultrasonic_1.py
# Measure distance using an ultrasonic module
# Author : Matt Hawkins
# Date   : 09/01/2013

# Import required Python libraries
import time
import RPi.GPIO as GPIO

# Use BCM GPIO references
# instead of physical pin numbers

# Define GPIO to use on Pi

# Set pins as output and input
GPIO.setup(GPIO_ECHO,GPIO.IN)      # Echo

# Set trigger to False (Low)
GPIO.output(GPIO_TRIGGER, False)

# Allow module to settle

# Send 10us pulse to trigger
GPIO.output(GPIO_TRIGGER, False)
start = time.time()
while GPIO.input(GPIO_ECHO)==0:
  start = time.time()

while GPIO.input(GPIO_ECHO)==1:
  stop = time.time()

# Calculate pulse length
elapsed = stop-start

# Distance pulse travelled in that time is time
# multiplied by the speed of sound (cm/s)
distance = elapsed * 34000

# That was the distance there and back so halve the value
distance = distance / 2

print "%.1f" % distance

# Reset GPIO settings

Additionally, to facilitate sending data to PLC, I have prepared a file us.php.  It returns the distance multiplied by 10 separated by a ";".  This way I can read it on the side of the PLC with the same code I use to read the temperature (you can find more information in a separate article):

  level = exec('sudo python /var/python/us.py');
  echo $level*10;
  echo ";";

 In order to send the reading to an SQL database (sql_us.py):


import MySQLdb
import subprocess

from time import localtime, strftime
timer = strftime("%Y-%m-%d %H:%M:%S", localtime())

  process = subprocess.Popen("/var/python/us.py", stdout=subprocess.PIPE)
  level = float(process.stdout.readline())*10

  print timer + " : Error reading us distance (us.py)"


    db = MySQLdb.connect("yourserver","youruser","yourpassword","yourdatabase")

    print timer + " : Error connecting to the SQL database"

    cursor = db.cursor()
      cursor.execute("INSERT INTO TankLevel(Time, Level)  VALUES (%s, %s)", (timer, level))
      print timer + " : Sending data ok"
      print timer + " : Error executing query"


A the end you can also set up your system to send the data automatically with the use of RPi's crone (crontab -e):

5 8,20 * * * /var/python/sql_us.py >> /var/python/log/sql_us.log 2>&1