Using ESPhome to build a water flow rate meter

I have used a YF-B5 - it was pretty accurate on my heating circuit. I used a D1Mini to connect it up. Worked well.

My one word of warning; it’s very noisy!!!

As it has an impeller, it introduces mechanical noise into the water. Water is a great conductor of source.

As I’m using mine on a heating circuit, when my boiler turns on, we can hear the noise in every room in my house. The radiators are acting as speakers I think!

I will be removing mine as soon as I can.

1 Like

@DeeBeeKay Sorry to trouble you. In your photo of Jun 2019, you mention a resistor, but I cannot see it in the picture. Do you (or anyone else) by any chance, have a simple circuit diagram for the connections from the flow meter to the D1-Mini, so I can see where the resistor should go? Thank you.

can someone recap the code right?
i saw over 100 posts.

1 Like

Hi @donparlor .

I know it’s been a long time since your post.
Typing on phone, so I’ll be short.
I’m struggling here, just as you did. Found out the divider does not work on this setup. I’ll give details later.

But what I’d like to know is how did you get the lambda. Why did you multillied 6.6 to 60 to get the 396. I have the same sensor as you. I think I’m misunderstanding this frequency formula on the specs.

Thanks.
My best regards

Finally. Let me explain better. Need some help/advice.
This esphome is making me crazy.

I have a canister on my aquarium and installed this YF-B5 on it. It’s nominal flux is 1000L/h.
All details of my project I’m putting in this topic:

So, I programmed the ESPHome on a ESP32 and it was not working. I was getting no signal, no pulses. I tested before on a test circuit using a ESP8266 (d1_mini) and it was working. So I decided to back to D1 mini. It also stopped working, until I remove the divider resistors (5V → 3.3V) and plugged the wire directly on the pin.

Now I have pulses ! But depending on how I configure esphome, I have a different result.

  1. First problem/doubt:
    I’ve seen some calculations above, multiplying the factor 6.68 to 60 and getting L/min. My frequency formula is F=6.68*Q.
    Considering F = Hz, is it the number of pulses the sensor outputs ? Does it mean L/min ? Because If I get this number and divide by 6.68 I will get the Q=L/min. So I don’tknow why are you multipliyng the factor by 60 and dividing the “x” (the value read from the sensor) by this number.

  2. SECOND doubt:
    I made just like you did, but if I change the way I code ESPHOME, I’m getting different results, I don’t know why:

If I declare like this:

 - platform: pulse_counter
    name: "pulse flux"
    id: freq_fluxo_sala
    pin: D6

I’m getting a decent result:

Flux Canister         815,3 L/h
pulses                326.785 pul/min
Liters   290,4 L

Obs.: the lambda is calculated and published to a template sensor:

!lambda return ( x / ( 6.68 * 60 ) );   #to return L/h

But if code configuring the PULL_UP resistor:


 - platform: pulse_counter
    name: "pulses_min"
    id: freq_fluxo_sala
    pin:
      number: GPIO12     # (I also tried D6 and number 12) 
      mode:
        input: true
        pullup: true 

I get a very different result:

Flux Canister      6,1 L/h
pulses               2.436 pul/min
Liters                112,2 L

So, could somebody explain me how to get the right formula or what is those variables mean ???

Thanks.

The lambda value comes from the sensor’s frequency-to-flow formula:


where F is the pulse frequency (Hz), and Q is the flow rate (L/min).

ESPHome’s pulse_counter doesn’t measure frequency directly but counts pulses over a time interval (5 seconds in my setup). To get the flow rate:

  1. Convert pulse count x to frequency:

  2. Solve for Q:

So, the correct lambda is:

filters:
  - lambda: return (x / 33);

The 396 comes from a different sensor model with a different formula. In our case with YF-B5, it uses F = 6.6 × Q, then dividing by 33 is correct. I hope it helps!

3 Likes

Thank you ! Now it’s clear what you did. Everything depends on the time delta the pulses are gotten.

But even this I`m getting strange results:

Getting about 300.000 pulses/min.
Teoretically, the result of Q should be L/min, but it seems to be in L/h. My canister has a nominal flux of 1000L/hr.

My colect timer is set to 60s.

Flux Hour      45.990,1 L/h
Flux Min.           766,5 L/min
Pulses / min     307.214 pulses

This is my code:

substitutions:
  flux_time: "60"

globals:
  - id: freq_fluxo
    type: float
    restore_value: yes
    initial_value: "6.6"

- platform: pulse_counter
    name: "pulses per minute"
    id: freq_fluxo_sala
    pin: D6
    update_interval: "${flux_time}s"
    icon: "mdi:wave-undercurrent"
    unit_of_measurement: "pul/min"
    accuracy_decimals: 0
    filters:
      - filter_out: nan
    on_value:
      - sensor.template.publish:
          id: fluxo_min
          state: !lambda return ( (x / ${flux_time}) / ( id(freq_fluxo) ) );
      - sensor.template.publish:
          id: fluxo
          state: !lambda return ( (x / ${flux_time}) / ( id(freq_fluxo) ) * 60 );

An interesting site I found some time ago is the one below. The update interval of his setup is 1s and his formula is x/400 for L/min (x / 6.68 * 60). So confusing…
https://tecnoyfoto.com/sensor-de-flujo-de-agua-yf-b5

Your logic is mostly correct, but there’s a small mistake in the way the flow rate is being calculated. The issue is that you’re already collecting pulses per minute, yet you’re treating the result as if it’s per second.

You have:

update_interval: "${flux_time}s"

where flux_time = 60.

This means you’re already counting pulses per minute, so there’s no need to divide by 60 again when calculating liters per hour.

Your current formula:

return ( (x / ${flux_time}) / ( id(freq_fluxo) ) * 60 );

is incorrect because you already measured pulses per minute.

Here is a fix for your code:

substitutions:
  flux_time: "60"

globals:
  - id: freq_fluxo
    type: float
    restore_value: yes
    initial_value: "6.6"  # Ensure this matches the correct pulse-to-flow conversion

- platform: pulse_counter
    name: "pulses per minute"
    id: freq_fluxo_sala
    pin: D6
    update_interval: "${flux_time}s"
    icon: "mdi:wave-undercurrent"
    unit_of_measurement: "pul/min"
    accuracy_decimals: 0
    filters:
      - filter_out: nan
    on_value:
      - sensor.template.publish:
          id: fluxo_min
          state: !lambda return ( x / id(freq_fluxo) );  // No need to divide by 60
      - sensor.template.publish:
          id: fluxo
          state: !lambda return ( x / id(freq_fluxo) );  // Directly gives L/h

Let me know if the revised code works for you.

1 Like

Thanks, I think I started understanding, but the code didn’t work. Both formulas are the same, for min and for hr.

I multiplied the hr calculation by 60, but I’m getting 1.400L/min on the other. There’s still something odd.

Initially, I just adjusted your formula substituting the 5s to 60s from mine.

EDIT: I’m getting some weird readings. 2.400 pulses/min (I was getting over 300.000!)

Please tell me: the F (frequency) is the reading the sensor returns, right (the X)?
If yes, then if I have a flux of 800l/h, that gives me 800/60 that is: 13.3 L/min. So, the number of pulses I should get should be F /60 = 6.68 * 13.33 = 5.343 pulses/min.

I don’t know if it right. I’m getting 2.400 pulses. This could be right, but the 300.000 I was getting before seems strange.
Of course, I’m assuming the F will be the reading x I get on ESPHome.

thanks

Your ESPHome pulse_counter is already measuring pulses per minute because the update_interval is set to 60 seconds (flux_time = 60). To convert pulses per minute to liters per minute, you must divide by the pulse-to-flow conversion factor (id(freq_fluxo)), and to convert liters per minute to liters per hour, you must multiply by 60.

1 Like

Hi, I need help to make the best use of this device:

I did some tests, and more or less when I turn on the water I get between 1600/1800 pulses/hz???

I can’t get a real value.

I am interested in having live consumption and total liters

Hello, I have a water flow sensor like below. I can convert it to real code with Esphome. What I want is the amount of water that passes through it.

Product item no.: DN50-S
Material : S304
Specification :
Function: Sensor, flow rate control
Thread size: Male thread DN50
Size 75MMX60MM
Flow rate :10~200L/min
Flow Pulse: F(Hz)=(0.2xQ) -3% Q=L/min
Max.Working Current: 15mA (DC5 V)
Min.Working Voltage: DC 5V
Working Voltage:DC5V~24V
Load Capacity: =10 mA (DC 5 V)
Working Temperature :
-25 Degree centigrade to +80 Degree centigrade
Liquid Temperature: 80 Degree C
Accuracy : 5%
1L water = 12 Pulses
Working medium: water, liquid, light oil,
Wire Connection: Red: Positive+ (IN); Black: Negative- (GND); Yellow: NPN Pulse signal (OUT)

Hello, I wrote an article on this subject that may interest you. HA-Mesurer la consommation d’eau de ma piscine avec un ESP8266 et ESPHome – Domo Rem81

1 Like

Hello, thank you very much for your interest.

If I need to adapt the code to my device, how should I write the formula?
What Paris are you in?

Hello, I live in Albi in the Tarn region. In your case, try this formula.:

sensor:
  - platform: pulse_meter
    pin: RX
    name: "${friendly_name} Cpt Eau Piscine"
    unit_of_measurement: "L/min"
    icon: "mdi:water"
    filters:
      - lambda: return x / 12.0;  # Convertit impulsions/min en L/min (1 litre = 12 impulsions)
    total:
      name: "${friendly_name} Cpt Eau Piscine Total"
      unit_of_measurement: "m³"
      accuracy_decimals: 3
      device_class: water
      state_class: total_increasing
      filters:
        - lambda: return x / 12000.0;  # Convertit impulsions en m³ (1 m³ = 12 000 impulsions)

Good morning everyone…
I have build one of these discussed waterflow meters with the YF-B5 and so far it seems to work, but the devider is not correct…396 is not giving back enough flow…
Now before i start inching single digits wise towards the right value, i thought i could steal some code from another d1 min project i have where i can calibrate my sd30 co2 sensor right from the web page…
Can someone tell me if this could work and how i can fix this error?
I am not so into the lambda

Code:

- platform: pulse_counter
    pin:
      number: GPIO2
      mode:
        input: true
        pullup: true
    unit_of_measurement: 'L/min'
    id: water_usage
    name: "Flow in L/min"
    update_interval: 5s
    filters:
      - lambda: return (x / !lambda 'return id(water_cali).state;');      #(x / 396);

number:
  - platform: template
    name: "Flow calibration value"
    optimistic: true
    min_value: 300
    max_value: 500
    step: 1
    id: water_cali
    entity_category: "config"

error:

/config/esphome/waterflowmeter.yaml:108:27: warning: character constant too long for its type
  108 | 
      |                           ^                          
/config/esphome/waterflowmeter.yaml: In lambda function:
/config/esphome/waterflowmeter.yaml:108:20: error: 'lambda' was not declared in this scope
  108 | 
      |                    ^     
/config/esphome/waterflowmeter.yaml:108:26: error: expected ')' before '\x6174653b'
  108 | 
      |                          ^                           
      |                          )
*** [.pioenvs/waterflowmeter/src/main.cpp.o] Error 1

- lambda: return x / id(water_cali).state;

Ui…trying it right now…thx so much for that fast response…that will help fine tune it big time…

1 Like

Thanks to all for making this so easy, it’s like LEGO. I found a steel case NPT meter here:

I connected it directly to the inlet side of my sprinkler master valve (which is 1"). I’ve wired it to an XIAO ESP32C3 and a 26V AC to 5V converter since that’s what’s available where the sprinkler valves are. The 3.3V/pulse/gnd are wired directly to the esp32 and the 5V/Gnd from the power converter are wired to the 5V/gnd of the esp32.

My YAML looks like this:

sensor:
  - platform: pulse_counter
    id: flow_rate
    pin: GPIO10
    name: "Flow rate"
    update_interval: 1s
    unit_of_measurement: "L/hr"
    device_class: volume_flow_rate
    # Flow formula F = (5Q – 3) ±10%
    # * F is the pulse frequency in Hz
    # * Q is the flow in l/min
    # Input (x) is "pulses/min"
    filters:
    - lambda: 'return x > 0 ? (x / 5.0 + 36.0) : 0;'
  
  - platform: integration
    name: "Total volume"
    sensor: flow_rate
    time_unit: h
    unit_of_measurement: "L"
    device_class: water
    restore: true
    state_class: total_increasing

  - platform: template
    name: "Flow rate (gpm)"
    unit_of_measurement: "gal/min"
    device_class: volume_flow_rate
    update_interval: 1s
    lambda: return id(flow_rate).state * 0.004402867;

Note the ternary in the lambda. A naive usage of the flow equation would log a constant non-zero flow. Additionally because this makes the relation non-linear, I use an integration for total volume rather than relying on pulses. A GPM sensor is added but not a total gallons as HA does that conversion itself in the UI (depending on your units).

I did a test by running about 2.5 gallons through the system into a bucket and weighing the bucket. It was within about 1%.

Now if I could just figure out some automated integration with sprinkler, such as watering by volume rather than by time.

1 Like

I’m using this integration from HACS, https://github.com/petergridge/Irrigation-V5
It has an option for watering by volume, but i’m not using it, so i can’t share experience.

1 Like