'ESP8266 running NodeMCU - Detect Pulse Stream Start/Stop

I've built an automatic, self-filling water bowl for my dogs that's working perfectly with a water level controller and I'd like to now add some monitoring capabilities using an ESP8266. Like detecting if water is flowing, or an overflow has occurred.

Watching the level sensors and an overflow sensor are no problem, but I'm stuck at detecting the water flow.

I have a hall-effect flow sensor that sends a 1/0 pulse stream when water is flowing through it, and I can detect it fine using an interrupt on a GPIO pin. My issue is that I can't wrap my head around a way to reliably detect when the pulses stop.

My solution so far is to count the pulses and write the value to a counter and then set a test counter equal to it. My thought being that, as long as the water is flowing, the count will continue to change and once the water stops, the counts will stay equal. This all happens using a timer that starts when the GPIO interrupt is triggered.

It works mostly as intended, but when the timer fires, it checks the two counter values, and for a moment they're the same, so it indicates that the flow has stopped for one cycle and then picks up the flow again. This is the behavior that I'm trying to overcome.

My code looks like this:

flow_sense_pin = 1

flow_counter = 0

test_counter = 0

flow = false

flow_timer = tmr.create()
flow_timer:register(4000, tmr.ALARM_AUTO, function() test_flow() end)

gpio.mode(flow_sense_pin,gpio.INT)

function flow_pin_cb(level)
    gpio.trig(flow_sense_pin, level == gpio.HIGH and "down" or "up")
    flow_counter = flow_counter + 1
    test_counter = flow_counter
    if flow == true then else print("Flow Detected") end
    flow = true
    flow_timer:start()
end

function test_flow()
    if test_counter == flow_counter then flow = false end
    if flow == false then flow_timer:stop() print("Flow Stopped") end
end

gpio.trig(flow_sense_pin, "down", flow_pin_cb)

And the output at the terminal is such:

1

I'm sure I'm overlooking something obvious but I've beat my head against it for hours and I'm not getting anywhere.



Solution 1:[1]

I'd take this simpler and probably more robust approach:

  • create the flow_timer object as you did

  • in the function triggered by the flow pulse (flow_pin_cb), call

    flow_timer:alarm(timeout, tmr.ALARM_SINGLE, flow_stop)

    where timeout is a time just a little longer than the maximum time between pulses of a continuous flow and flow_stop is a function which is then called when the flow stopped

  • use a flag, let's call it flowing

    in flow_pin_cb, if flowing is false, print("Flow Detected") and set flowing true

    in flow_stop, print("Flow Stopped") and set flowing false

Solution 2:[2]

I still need to do some pulse width testing but that works perfectly and the new code is MUCH simpler! Thank you!

The new code is MUCH simpler:

flow_sense_pin = 1

timeout = 1000

flow_timer = tmr.create()

flowing = false

gpio.mode(flow_sense_pin,gpio.INT)

function flow_pin_cb(level)
    gpio.trig(flow_sense_pin, level == gpio.HIGH and "down" or "up")
    if flowing == false then print("Flow Detected") flowing = true end
    flow_timer:alarm(timeout, tmr.ALARM_SINGLE, flow_stop)
end

function flow_stop()
    print("Flow Stopped")
    flowing = false
end

gpio.trig(flow_sense_pin, "down", flow_pin_cb)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Armali
Solution 2 Jeff