How to Build a Remote Water Meter and Valve Controller

7
2201
Water flow sensor

BACKGROUND

A friend of mine has a small duck farm in Texas. (He also hosts a podcast at tspc.co.) Every morning he fills up a half-dozen kiddy pools for the ducks. This involves moving the hose from pool to pool while he goes about his other daily chores. On occasion, he gets preoccupied and returns later in the day

Water flow sensor

to find things have overflowed and made a large, messy pond where one isn’t supposed to be. Being the “automation guy”, he asked me how hard it would be to connect a flow sensor to the water line and have it send him an alert in case he forgets and leaves the hose running. And oh, by the way, he wanted to be able to turn the water off even if he’d run to the store.

Turns out the solution was fairly simple.  Here’s what I came up with …

THE SOLUTION

After pondering a bit, I decided to use an in-line flow sensor (called a Hall sensor) and a generic sprinkler valve.  The flow sensor has a little water wheel inside that spins as the water passes by.  The wheel has a magnet attached to it, which creates electrical pulses on the output line as it rotates.  The net effect is you can tell how fast the water is moving by how many pulses you see every second.  The valve is the exact same kind used in irrigation systems, which opens when you apply 24 V AC power to it, and shuts when you turn it off.

Flow sensor and sprinkler valve
Flow sensor and sprinkler valve

The brains of the system is a microcontroller (a Particle Photon), which does all the calculations to determine if the water is running, controls the valve, and sends data back and forth to the user.  I also used a “relay” board, which allows the microcontroller which runs on a relatively low voltage to control the much higher voltage used by the irrigation valve.

Control electronics

To allow remote interaction with the sensor and valve, I used an application called Blynk. This software lets you create a dashboard / control panel by dragging and dropping different widgets onto the “canvas”.  Below is the app I created.  It’s fairly crude, but it gets the job done.  Here are the widgets I decided to use:

  • Water Running box – Shows a 1 when there’s water flowing, and 0 when it’s not.  This display is updated once every minute.
  • Water Meter display – Counts the number of pulses as the water wheel turns.  This display is updated every 5 seconds.  (I didn’t calibrate the sensor, but in theory this could be used to show the actual gallons of water used.)
  • Toggle Button – Turns the valve on and off.  (The Blynk software wouldn’t let me use a single widget to control the valve AND detect whether it’s on or off, so I made this a “toggle” button.  If he’s using the app remotely, my buddy will have to make sure he checks the Water Running and Water Meter displays to make sure things are off.)
  • System Uptime – Shows how many seconds the microcontroller has been running since it was last rebooted.
  • Notification box – Used to display notifications such as “Hey, Jack! Your Water has been running for more than 30 minutes!”

 

DEMO

 

 

HARDWARE

 

SOFTWARE / CLOUD SERVICES

 

THE CODE

// This #include statement was automatically added by the Particle IDE.
#include <SparkCorePolledTimer.h>

// This #include statement was automatically added by the Particle IDE.
#include <blynk.h>

char auth[] = "[YOUR AUTH CODE HERE]";
void pulseCounter(void);
long pulseCount = 0;
long lastPulseCount = 0;
long lastTime = millis();
bool isRunning = false;
int  lastWaterReading = 0;
int  currentWaterReading = 0;
bool lastRunning = false;
bool currentRunning = false;

SparkCorePolledTimer updateTimer(30000);    //This is a 30 second timer that contains its own 30 minute counter used to determine whether the water has been running for 30+ minutes

SparkCorePolledTimer upTimeTimer(5000);  //This is a 5 second timer used to update the System Uptime display in Blynk
SparkCorePolledTimer waterRunningTimer(60000);  //This is a 1 minute timer used to calculate whether the water is running
void OnTimer(void);   //Prototype for timer callback method
void upTimeEvent(void);
void onMinuteEvent(void);

void setup() {
    
    delay(5000); // Allow board to settle
    Blynk.begin(auth);
    pinMode(D0, OUTPUT);
    pinMode(D4, INPUT_PULLUP);
    attachInterrupt(D4, pulseCounter, RISING);
    digitalWrite(D0, LOW);
    updateTimer.SetCallback(OnTimer);
    upTimeTimer.SetCallback(upTimeEvent);
    waterRunningTimer.SetCallback(oneMinuteEvent);

}

void loop() {

    Blynk.run();
    updateTimer.Update();
    upTimeTimer.Update();
    waterRunningTimer.Update();

}


void OnTimer(void) {  //Handler for the timer, will be called automatically

    long currentTime = millis();
    currentRunning = isRunning;
    
    if (currentTime-lastTime > 1800000) {
        if (currentRunning && lastRunning) {
            Blynk.notify("Hey, Jack! Your Water has been running for more than 30 minutes!");
        }
        lastRunning = currentRunning;
        lastTime = currentTime;
    }; 
    
}


//  The following timer handler determines whether the water is running by checking whether the pulseCount counter has increased by more than 3 increments over a one minute period.
void oneMinuteEvent(void) {  

    int blynkReading;    
    currentWaterReading = pulseCount;
    
    if ((currentWaterReading-lastWaterReading) > 3) {
        isRunning = true;
    }
    else {
        isRunning = false;
    };
    
    if (isRunning){
        blynkReading = 1;
    }
    else {
        blynkReading = 0;
    };
    lastWaterReading = currentWaterReading;
    Blynk.virtualWrite(6, blynkReading);
}


// This function sends Arduino's up time every second to Virtual Pin (5). In the app, Widget's reading frequency should be set to PUSH. This means that you define how often to send data to Blynk App.
void upTimeEvent(void)
{
  // You can send any value at any time.
  // Please don't send more that 10 values per second.
    Blynk.virtualWrite(5, millis()/1000);
    Blynk.virtualWrite(1, pulseCount);
}


void pulseCounter()
{

    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 30) 
    {
    pulseCount++;
    }
    last_interrupt_time = interrupt_time;

}

 

Help support my work via Patreon!

7 COMMENTS

  1. Fantastic! So you can toggle on/off remotely and get alerts for leaving it on for x amount of time. Can you program to shut of on its own after x time or x gallons?

    • There’s a lot of flexibility when you build systems like these. And yes, shutting the water off after a certain amount of time (or water) is a great example.

    • Good eye, Alex! Yes, this is a carryover from the original Arduino code. The “Photon”, which I used for this project, is 95% code compatible, which makes it easy to cut & paste from other projects.

LEAVE A REPLY

Please enter your comment!
Please enter your name here