Rainwater tank level monitoring with Linux

I’ve had a rainwater tank for a couple of years and because it’s located beside my garage where one of my Linux servers is I’ve always thought it would be cool to be able to graph the water level and display it on a web page. I’ve looked into it a few times but couldn’t find anything that would measure the water level and interface to a PC for a reasonable price.

The Aquagauge

Early in 2007 I finally found what I was looking for – the Aquagauge made by an Australian company Electrosense Technologies.

The monitor itself transmits the water level and temperature to a receiver unit which has an LCD display and an RS232 output for interfacing to a computer. The company has Windows software but when I explained that I wanted to use it under Linux they said that they can program the receiver to output the data in the following plain ASCII format, which is pretty easy to parse:

a1248b0c205d

In this example 1248 converts to a depth of 124.8 cm, 0 is the sign bit for the temperature (0 = +,1 = -) and 205 is the temperature in the transmitter, i.e. 20.5 deg C.

The Aquagauge was pretty easy to install and my only concern was that because my rainwater tank sits beside my garage I was concerned that the transmitter and receiver would be too close (< 3m). As it turned out, it wasn't a problem. Interfacing to a Linux box

I already use a couple of free packages for producing graphs on my web site; MRTG and RRDtool, so I wanted to make use of them for monitoring the water level and temperature. All that was needed was a couple of scripts to make it happen.

Scripts

The main script aqualogger.pl is a perl script which monitors the serial port for the periodic data reporting, parses it and saves the water level and temperature in a temporary file. It is started at boot time and runs as a daemon.

#!/usr/bin/perl

use Device::SerialPort qw( :PARAM :STAT 0.07 );

my $MAXVAR = 20;        # Maximum data variation (%)

my $port=Device::SerialPort->new("/dev/ttyS0");
my $tmpfile = "/var/tmp/aqualogger";

$port->baudrate(2400);
$port->databits(8);
$port->parity("none");
$port->read_char_time(0);     # don't wait for each character
$port->read_const_time(1000); # 1 second per unfulfilled "read" call
$port->are_match("d");

# Main loop

while (1) {
        my $gotit = "";
        until ("" ne $gotit) {
                $gotit = $port->lookfor;       # poll until data ready
                sleep 1;                          # polling sample time
        }

        $gotit =~ /a(\d+)b\dc(\d+)/;
        $level = $1 / 10;
        $temp = $2 / 10;

        #print "Level: $level, Temp: $temp\n";

        # Sanity check the values - occasionally it spits out a way off value

        $previous_level = $level unless (defined $previous_level);
        $previous_temp = $temp unless (defined $previous_temp);

        $level_var = $level / $previous_level;
        $temp_var = $temp / $previous_temp;

        if (($level_var < (1 - $MAXVAR/100)) || ($level_var > (1 + $MAXVAR/100))) {
                #print "Skipping level of $level\n";
                next;
        }
        if (($temp_var < (1 - $MAXVAR/100)) || ($temp_var > (1 + $MAXVAR/100))) {
                #print "Skipping temp of $temp\n";
                next;
        }

        # Save water level and temperature

        open (fh, ">$tmpfile")
                or die ("Unable to open $tmpfile : $!");
        print (fh $level . "\n");
        print (fh $temp . "\n");
        close (fh);

        $previous_level = $level;
        $previous_temp = $temp;
}

The second script aquadata.sh is a simple shell scripts which reads the two values from the temporary file and prints them out in a format suitable for mrtg to read.

#!/bin/sh
LOGFILE=/var/tmp/aqualogger

level=`head -1 $LOGFILE`
temp=`tail -1 $LOGFILE`

echo $level
echo $temp
echo 0
echo "Aquaguage data"

Configering mrtg and rrdtool

This is an extract from my mrtg.cfg file:

WorkDir: /var/db/mrtg
Interval: 2
RunAsDaemon: Yes

Title[aquaguage]: Aquaguage
Target[aquaguage]: `/share/scripts/aquadata.sh`
Options[aquaguage]: gauge
LogFormat: rrdtool

I run mrtg as a daemon but it can also be run periodically as a cron job.

Finally, I run a shell script every 15 minutes to produce the graphs:

RRDTOOL=/usr/bin/rrdtool
OUTDIR=/var/www/html/images

$RRDTOOL graph $OUTDIR/outside-temp.png \
       -a PNG \
       -h 80 -w 300 \
       -s -1week \
       -u 30 \
       -l 0 \
       -Y \
       -E \
       -v "Deg C" \
       -t "Outside Temperature (Past Week)" \
        -x HOUR:6:DAY:1:DAY:1:86400:'%a' \
DEF:ts1=/var/db/mrtg/aquaguage.rrd:ds1:AVERAGE \
VDEF:now=ts1,LAST \
VDEF:min=ts1,MINIMUM \
VDEF:max=ts1,MAXIMUM \
LINE1:ts1#001FFF:"Temperature" \
COMMENT:"Now\:" \
GPRINT:now:"%3.1lf" \
COMMENT:"Min\:" \
GPRINT:min:"%3.1lf" \
COMMENT:"Max\:" \
GPRINT:max:"%3.1lf" > /dev/null

$RRDTOOL graph $OUTDIR/waterlevel.png \
       -a PNG \
       -h 80 -w 300 \
       -s -1week \
       -u 100 \
       -l 0 \
       --rigid \
       -Y \
       -E \
       -v "%" \
       -t "Rainwater Tank Level (Past Week)" \
        -x HOUR:6:DAY:1:DAY:1:86400:'%a' \
DEF:ds0=/var/db/mrtg/aquaguage.rrd:ds0:AVERAGE \
CDEF:ts1=ds0,130,/,100,* \
VDEF:now=ts1,LAST \
VDEF:min=ts1,MINIMUM \
VDEF:max=ts1,MAXIMUM \
AREA:ts1#001FFF:"Level" \
COMMENT:"Now\:" \
GPRINT:now:"%3.1lf" \
COMMENT:"Min\:" \
GPRINT:min:"%3.1lf" \
COMMENT:"Max\:" \
GPRINT:max:"%3.1lf" > /dev/null

Note the CDEF line – it converts the water level to a percentage. In my case the maximum depth of my tank is 130cm.

One of the nice things about using RRDtool is that you can produce multiple graphs for different time ranges (e.g. weekly, monthly, yearly) because all the data is stored in an RRD database.

The Results?

You can see the graphs produced in my Environmental Statistics page.

Download

All the scripts and config files files are available for downloading in this Aquagauge scripts zip file.

Steve’s Nachos

Until the other day my Girlfriend considered herself the Nacho master, but after I whipped this up I think the she’s ready to hand the title over…

Nachos:

    Large pack of plain corn chips
    1 Jar (375gm) of Old El Paso Thick ‘n Chunky Salsa (Medium)
    1 Chorizo – quartered length ways then chopped
    1 Small onion – chopped
    140g Tomato paste
    1 Can Mexican chilli beans
    1 Clove of garlic – finely chopped
    1 Small chilli – finely chopped (Optional)
    1/2 Tsp cumin
    Paprika
    250g grated cheese (Cheddar, Mozarella etc)

Guacamole:

    1 – 2 Avocados (depending on size)
    1 Ripe tomato chopped finely
    1 Spring onion
    1 Lime
    1 – 2 Tbsp chopped coriander
    Salt and Pepper to taste

First, make the sauce:

  1. Fry chorizo over a medium heat for 2-3 minutes
  2. Add cumin, chilli, onion and garlic and fry for 1-2 minutes
  3. Add tomato paste, salsa and beans and simmer for about 10 minutes

While the sauce is simmering away you can make the guacamole:

  1. Mash the two avocados in a bowl
  2. Mix in the juice from the lime
  3. Stir in the chopped tomato, onion and coriander
  4. Season with salt and pepper

To assemble the nachos:

  1. Preheat oven to 180 degrees
  2. Line the bottom of a baking pan or dish with a layer of corn chips
  3. Sprinkle over some grated cheese
  4. Spoon over some of the sauce
  5. Add another layer or two of corn chips, cheese and sauce – try to finish with cheese
  6. Sprinkle over with paprika
  7. Bake for 10-15 minutes

Serve with the guacamole on the side or on top.