Thursday, November 15, 2012

Putting a backflow preventer on the Internet

WARNING: THIS POST GETS TECHNICAL

Back in August 2012 we had a bad storm in Toronto.  It was bad enough that the storm drains in my neighbourhood (and many others around the city) got overwhelmed with rain water. The end result was a lot of unnecessary floods in people's homes as the water had to go somewhere.  Ours was one of them - so far costing about $26,000 to fix the damage and try to prevent it happening again.

The problem was the storm drain under the street was built about 50 years ago for the houses on the street.  Since then, it's been connected to other streets, large carparks that don't let water soak into the ground but send storm water straight into the sewers, etc.  All this run-off water has to squeeze into the pipe.  When it doesn't fit, it goes two places:

1.  Back up through the catch-basins onto the streets.
2.  Up any other pipes underground that feed into it.



Just to give you an idea what we're talking about, the above hole with the cable coming out of it is my 4-inch storm pipe where it meets the street storm drain (as you can see it's 68ft out).  If my pipe is 4-inches, then that storm sewer looks to be about 12 inches across.  Whilst there are run-off calculations (see section 5.4.1 here) to tell you how much water would flow, this is a 60 yr old sewer that is just inadequate and overloaded.

What I did was install some backflow valves.  We put one on the storm pipe and one on the sewer pipe.  In our case, they're these ones.  What they are, are a box with a flap in it - so that when the sewer gets overloaded and water rushes up the pipes towards the house, the flap goes up and seals off the pipe.  When the water recedes, the flap falls down and the pipe re-opens.

Whilst this is very simple, it has one draw-back because when the flap is up, I need to know this due to the fact that a closed pipe won't let water in, but also doesn't let water out.  This includes toilets, showers, washing machines, dishwashers, and anything else (sinks for example) that generates waste water.

It became very clear that I needed to monitor the pipes or the back-flow preventers and raise an alarm when it blocks off...   It has to be cheap, and have low running costs.

My plan was simple:
  • Take an old Arduino Uno I had kicking about (~$30).
  • Bolt on a Sharp GP2D120X Infra-Red distance sensor ($25 with cable)
  • Find some method of getting the data out off the Arduino and onto the Internet... This bit was not quite sorted out as I had some options (will cover this later).

The Sharp IR distance sensor was wired up as follows:
Red wire into the 3.3V Power Line
Black wire into GND
White wire into A0 (Analog In)

The first thing was to build a C++ class for the drain.  This is what V.1 came up as:

For the header, I had this:

/*
  StormDrain.h - Drain Monitoring Library.
  (C) 2012, Jason Coulls.

  This code was used with a Sharp GP2D120X IR Sensor wired as follows:
  Red:     3.3V Power Line
  Black:   GND
  White:   A0 (Analog In)
  
*/
#include "Arduino.h"

#ifndef StormDrain_h
#define StormDrain_h

class StormDrain
{
  
  public:

    //The public class constructor
    StormDrain(String description);

    //Public functions.
    String   getDescription();      //Returns the name of the drain.
    void     updateState();         //Queries the backflow preventer and updates it's state.
    boolean  isFlooded();           //Returns the state of the backflow preventor.
  
  private:

    //Private members.
    String   _description;
    boolean  _flooded;

};

#endif

As you can see, the drain can be labelled in the constructor.  There's only two private members with public functions to retrieve each value, and a simple void function to update the state of the drain.

For the .CPP file, I had this:

/*
  StormDrain.h - Drain Monitoring Library.
  (C) 2012, Jason Coulls.
*/


#include "Arduino.h"
#include "StormDrain.h"

#define IRPIN 0            //Analogue Pin 0.
#define OVERSAMPLERATE 15  //Used to get a proper reading.  

//Class Constructor - Takes the name of the drain.
StormDrain::StormDrain(String description)
{
    //Store the name.
    _description = description;
}

//Returns the name of the drain.
String StormDrain::getDescription()
{
    return _description;  
}

//Returns a boolean indicator of if the drain is flooded (true) or not (false)
boolean StormDrain::isFlooded()
{
    return _flooded;
}

//Queries the physical state of the drain.
void StormDrain::updateState()
{

    //Get ready to read. 
    int iAllSamples=0;
    
    //Grab the values a number of times.
    for(int i=0; i<OVERSAMPLERATE; i++)
    {
        //Query the Sharp IR Sensor.
        iAllSamples += analogRead(IRPIN);
        //Give it a breather before going again.
        delay(20);
    }
    
    //Now divide down the total of the samples.
    //(This gives a smoother version of the actual reading).
    int reading = iAllSamples/OVERSAMPLERATE;
    
    if(reading > 300) {
       //Drain is up.
       _flooded = true; 
       
    } else {
       //Drain is down.
       _flooded = false;
    }
    
    Serial.println(reading);    
    Serial.println("\n\r");
}

In the method updateState, you'll notice some funky oversampling.  If I asked the sensor for it's current distance reading twice, I'd get two different readings.  One way to get a good smooth reading is ask a bunch of times, then divide down the answer by the number of times we asked (to get the average).  

What I get is a bunch of values between ~40 (40 cm) and ~450 (4cm).  To make life easy at this prototyping stage, I just said anything over 300 is probably worth getting alarmed about.

The code that uses the above class was then written thus:

/*
  SurgeAlarm - Drain Monitoring Application
  (C) 2012, Jason Coulls.
  
  Besides the StormDrain class that ships with this app,
  a library (http://arduino-tweet.appspot.com) is also
  used to provide the Twitter interface.
  
*/

#include "StormDrain.h"
#include <stdio.h>
#include <Ethernet.h>
#include <Twitter.h>

#if defined(ARDUINO) && ARDUINO > 18   // Arduino 0019 or later
#include <SPI.h>
#endif

#if defined(ARDUINO) && ARDUINO < 22   // Arduino 0022 or earlier
#include <EthernetDNS.h>
#endif

//Create an instance of the drain, and give it a name.
StormDrain thisDrain("Storm Sewer");

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 10, 0, 0, 177 };

//Your Key from (http://arduino-tweet.appspot.com) goes here.
Twitter twitter("<YOUR_KEY_HERE>");

char closedMessage[] = "@coulls - Storm surge detected. BFP Closed";
char openMessage[] = "@coulls - Storm surge receeded. BFP Open";

void setup()
{
    //Open the ethernet.
    Ethernet.begin(mac, ip);

    Serial.begin(9600);
}

void loop()
{
     

    //Ask the drain to take a look at itself.
    thisDrain.updateState();

    //Print the state.
    if(thisDrain.isFlooded()){
        if (twitter.post(closedMessage)) {
            int status = twitter.wait();
            if (status == 200) {
                Serial.println("OK.");
            } else {
                Serial.print("failed : code ");
                Serial.println(status);
            }
        } else {
            Serial.println("connection failed.");
        }
    } else {
        if (twitter.post(openMessage)) {
            int status = twitter.wait();
            if (status == 200) {
                Serial.println("OK.");
            } else {
                Serial.print("failed : code ");
                Serial.println(status);
            }
        } else {
            Serial.println("connection failed.");
        }
    }

    //Stop for 5 seconds before checking again.
    delay(5000);

}


As you can see here, the idea is to check the drain, and if it's up, then tweet it to me.  This has two fatal flaws.

1.  I'd get flooded in tweets.  This code needs to be added to, so it does things only when the state changes.
2.  I've still no idea if the Sharp sensor can see "through" the top of the Mainline Fullport Backflow preventer to the flap, or whether it will only measure to the top of the valve case.  

So, as you can see, the idea is coming along.  I ran out of time to do more on this today as dealing with family, but I have to amend the code further to suit the application.  It's not really difficult at all - just time consuming.

For the first run at prototyping, I will run an Ethernet cable through a shield and connect that directly into my Bell modem.  A big unknown question is whether to implement a ZigBee wireless transmitter.  I don't have one kicking around, but it might be fun (if a little over budget) to implement.

I will update this story in a consequent post, when I get time to throw the hardware down the hole and try IR sensing through the back-flow preventer itself.

Stay tuned.