DIY ESPHome Water Flow Meter

I wanted a reliable, local-only way to monitor water usage and water temperature for my water/irrigation. I currently have a Yo-Link shutoff/flow sensor, but it doesn’t report in real time and the temperature isn’t exposed to Home Assistant there is a feature request pending. But that’s not really what this post is about.

I also didn’t really find a clean or reliable approach that others were using for this, at least not one that stayed fully local and gave real-time data. Most solutions I came across were either overly complicated, cloud-dependent, or didn’t actually solve the real-time aspect for me with well water.

I looked at ultrasonic options as well, but I didn’t really trust the readings enough for something like this, especially long-term.

So I figured I’d put this together and share it, because it ended up being way simpler than I expected. Honestly, this ended up being one of those “why didn’t I do this sooner” setups.


I put together a really simple and easy ESPHome setup using a pulse-output water meter DAE MJ-100a and a DS18B20 Digital Temperature Sensor Thread Probe

The goal was:

  • Real-time flow (not delayed like Yo-Link)
  • Total usage tracking
  • Water Temperature (used to calculate usable hot water minutes from my heat pump water heater based on incoming well temp)
  • Fully local (no cloud dependency)

Real-Time Flow (big difference vs Yo-Link)

This is honestly the biggest improvement.

Using pulse_meter means ESPHome calculates flow based on pulses as they happen, instead of reporting averaged or delayed values like most sensors I’ve used. So instead of waiting for an update, you get:

  • near instant response when water starts/stops
  • visible ramp-up and ramp-down of flow
  • short bursts actually show up instead of getting missed

It feels a lot more like watching a live sensor instead of something that reports every X seconds.


Hardware Used


Hardware / Wiring Notes

  • The MJ-100a is just a dry contact pulse output, so it wires straight into a GPIO + GND
  • I’m using GPIO5 with pullup + inverted in ESPHome on my ESP32
  • The DS18B20 is on GPIO42, mainly because it’s physically close to 3V3/GND on my board for putting a 4.7k resistor between the GPIO and 3V3

ESPHome Side

Nothing too crazy in the config, but a couple things worth pointing out:

  • The MJ-100a I’m using outputs 1 pulse per 0.1 gallon, so everything is multiplied by 0.1
  • I exposed both:
    • GPM (flow rate)
    • Total gallons
    • Raw pulse count (mainly for sanity checking / debugging)

Having the pulse count available actually helped a lot while testing as it made it easy to confirm I wasn’t missing anything.


Utility Meter (this made it way more useful)

Once I had total gallons working, I added a utility_meter in Home Assistant so I could break it down into something more useful:

  • daily usage
  • weekly usage
  • monthly usage

Since this meter is installed after my main FloSmart, I’m using it specifically for irrigation. So now I effectively have:

  • total house water (FloSmart)
  • irrigation-only usage (this meter)
  • and I can subtract the two if I want non-irrigation usage

That part ended up being more useful than I expected especially for understanding how much water irrigation is actually using over time.


What this actually gives you

From this setup I now have:

  • Real-time flow rate (GPM)
  • Total gallons used
  • Raw pulse count (for verification/debugging)
  • Water temperature at the pipe
  • Daily / weekly / monthly irrigation usage (via utility_meter)

Which is way more visibility than I had before, especially for irrigation.

esphome:
  name: water 
  friendly_name: Water Meter

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf

logger:

api:
  encryption:
    key: ""

ota:
  - platform: esphome
    password: "7b0a878b86e1562891756486028c7c43"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Water Fallback Hotspot"
    password: !secret wifi_password

captive_portal:

one_wire:
  - platform: gpio
    pin: GPIO42

light:
  - platform: esp32_rmt_led_strip
    name: "Water RGB"
    id: water_rgb
    pin: GPIO48
    num_leds: 1
    chipset: ws2812
    rgb_order: GRB
    restore_mode: ALWAYS_OFF

sensor:
  - platform: dallas_temp
    name: "Water Temperature"
    id: water_temperature
    update_interval: 30s

  - platform: pulse_meter
    pin:
      number: GPIO5
      mode:
        input: true
        pullup: true
      inverted: true
    name: "Water Flow Rate GPM"
    id: water_flow_gpm
    unit_of_measurement: "gal/min"
    device_class: water
    state_class: measurement
    accuracy_decimals: 2
    timeout: 10s
    internal_filter: 100ms
    filters:
      - multiply: 0.1

    total:
      name: "Water Total Gallons"
      id: water_total_gallons
      unit_of_measurement: "gal"
      device_class: water
      state_class: total_increasing
      accuracy_decimals: 1
      filters:
        - multiply: 0.1

  - platform: template
    name: "Water Flow Rate PPM"
    id: water_flow_ppm
    unit_of_measurement: "pulses/min"
    icon: mdi:counter
    accuracy_decimals: 0
    lambda: |-
      if (isnan(id(water_flow_gpm).state)) return NAN;
      return id(water_flow_gpm).state / 0.1;
    update_interval: 10s

  - platform: template
    name: "Water Total Pulses"
    id: water_total_pulses
    unit_of_measurement: "pulses"
    state_class: total_increasing
    icon: mdi:counter
    accuracy_decimals: 0
    lambda: |-
      if (isnan(id(water_total_gallons).state)) return NAN;
      return id(water_total_gallons).state / 0.1;
    update_interval: 30s

binary_sensor:
  - platform: status
    name: "Water ESP Status"



1 Like