Calculations with pulse_counter values

I’ve got an ESP32 and a reed sensor, with which I can count rotations of my water meter, using a pulse_counter. With it, I assume I can keep track of the total pulses, as well as the amount of pulses per minute.

I still have to figure out how many rotations go into a liter, but I assume 2-4. That means that the amount of pulses needs to be divided by some integer, and the result will most likely be a float, rather than an integer. And why not keep track of the amount of pulses as well? The more data, the merrier.

But I can’t figure out how to use the sensor values and calculate them, and then publish those calculated values as well. I thought numbers might do the trick. But then it turns out you can’t use number without a template. And you can’t use a template number without max_value. But a maximum value makes no sense on a water meter, which will obviously only increment its value.

And how can I get the sensor values into those numbers…?

This is what I have so far:

sensor:
  - platform: pulse_counter
    pin:
      number: 14
      inverted: true
      mode:
        input: true
        pullup: true
    name: watermeter_pulsering_per_minuut
    id: watermeter_pulsering_per_minuut
    update_interval: 1s

    total:
      name: watermeter_totaal_aantal_pulseringen
      id: watermeter_totaal_aantal_pulseringen

I would like to add two floats:

  • watermeter_verbruik_per_minuut = the value of watermeter_pulsering_per_minuut divided by 4 (let’s say)
  • watermeter_totaal_verbruik = = the value of watermeter_totaal_aantal_pulseringen divided by 4 (let’s say)

I tried something like this:

number:
  - platform: template
    name: watermeter_verbruik_per_minuut
    id: watermeter_verbruik_per_minuut
    update_interval: 1s
    entity_category: ''
    device_class: volume_flow_rate
    unit_of_measurement: L/min
    lambda: |-
      return id(watermeter_pulsering_per_minuut).state/4;
  - platform: template
    name: watermeter_totaal_verbruik
    id: watermeter_totaal_verbruik
    update_interval: 1s
    entity_category: state
    device_class: water
    unit_of_measurement: L
    lambda: |-
      return id(watermeter_totaal_aantal_pulseringen).state/4;

But then the compiler complained about the missing max_value… I suppose I can set that to an unrealistically high integer, but I assume there’s a better way?

Thanks in advance for everyone’s help!

Number is used for values input by the user.

You just need a template sensor:

sensor:
  - platform: template
    name: watermeter_verbruik_per_minuut
    id: watermeter_verbruik_per_minuut
    update_interval: 1s
    entity_category: ''
    device_class: volume_flow_rate
    unit_of_measurement: L/min
    lambda: |-
      return id(watermeter_pulsering_per_minuut).state/4;

EDIT: You may want to get rid of the update_interval: and add a sensor.template.publish: action in the source sensor on_value:

That would be unexpected to me.
All sensors on esphome have filters, you can multiply, make calibrations or create custom ones.

The number of pulses per volume is nearly always shown on the meter face or the model specifications on the manufacturer website.
You didnt volunteer either. Best of luck in looking it up.
Even AI is challenged to do this without valid input.

Sounds good. What’s the syntax of state in that construction?

on_value:
  - sensor.template.publish:
      id: watermeter_verbruik_per_minuut
      state: ?

You’re overestimating the meter :slight_smile:

I’ll just look at the numbers on the meter, and at the pulse count. I can’t imagine that will raise much problems?

You can simply get rid of the lambda in the template sensor:

sensor:
  - platform: template
    name: watermeter_verbruik_per_minuut
    id: watermeter_verbruik_per_minuut
    entity_category: ''
    device_class: volume_flow_rate
    unit_of_measurement: L/min

And add it to the source sensor:

    on_value:
      - sensor.template.publish:
        id: watermeter_verbruik_per_minuut
        state: !lambda 'return id(watermeter_pulsering_per_minuut).state/4;'
1 Like

Turns out there is an indentation missing below - sensor.template.publish:.

I then thought I’d expand on this thinking, and coded this:

sensor:
  - platform: pulse_counter
    pin:
      number: 14
      inverted: true
      mode:
        input: true
        pullup: true
    name: watermeter_pulsering_per_minuut
    id: watermeter_pulsering_per_minuut
    update_interval: 1s
    total:
      name: watermeter_totaal_aantal_pulseringen
      id: watermeter_totaal_aantal_pulseringen
    on_value:
    - sensor.template.publish:
        id: watermeter_verbruik_per_minuut
        state: !lambda 'return id(watermeter_pulsering_per_minuut).state/4;'
    - sensor.template.publish:
        id: watermeter_totaal_verbruik
        state: !lambda 'return id(watermeter_totaal_aantal_pulseringen).state/4;'

  - platform: template
    name: watermeter_verbruik_per_minuut
    id: watermeter_verbruik_per_minuut
    entity_category: ''
    device_class: volume_flow_rate
    unit_of_measurement: L/min
  - platform: template
    name: watermeter_totaal_verbruik
    id: watermeter_totaal_verbruik
    entity_category: ''
    device_class: water
    unit_of_measurement: L

But that threw an error:

(venv) PS C:\PortbleApps\ESPHome> esphome compile watermeter.yaml
INFO ESPHome 2025.10.3
INFO Reading configuration watermeter.yaml...
INFO Detected timezone 'Europe/Paris'
INFO Generating C++ source...
ERROR Circular dependency detected! Please run with -v option to see what functions failed to complete.

I then ran the verbose option, and after a number of lines that started infinitely looping:

DEBUG Adding: sensor_sensorpublishaction_id_2 = new sensor::SensorPublishAction<float>(watermeter_totaal_verbruik);
DEBUG Registered variable sensor_sensorpublishaction_id_2 of type sensor::SensorPublishAction<float>
DEBUG Waiting for variable watermeter_totaal_aantal_pulseringen
DEBUG Running to_code in esphome.components.pulse_counter.sensor (num 15)
DEBUG Waiting for variable watermeter_totaal_aantal_pulseringen
DEBUG Running to_code in esphome.components.pulse_counter.sensor (num 15)
DEBUG Waiting for variable watermeter_totaal_aantal_pulseringen
DEBUG Running to_code in esphome.components.pulse_counter.sensor (num 15)
DEBUG Waiting for variable watermeter_totaal_aantal_pulseringen
DEBUG Running to_code in esphome.components.pulse_counter.sensor (num 15)

I don’t really understand what the error in the logic is. I suppose total is some sort of on_value in disquise?

So I did some more searching, and found the Integration Sensor. Maybe this is something:

sensor:
  - platform: pulse_counter
    pin:
      number: 14
      inverted: true
      mode:
        input: true
        pullup: true
    name: watermeter_pulsering_per_minuut
    id: watermeter_pulsering_per_minuut
    update_interval: 1s
    total:
      name: watermeter_totaal_aantal_pulseringen
      id: watermeter_totaal_aantal_pulseringen
    on_value:
    - sensor.template.publish:
        id: watermeter_verbruik_per_minuut
        state: !lambda 'return id(watermeter_pulsering_per_minuut).state/4;'
    #- sensor.template.publish:
    #    id: watermeter_totaal_verbruik
    #    state: !lambda 'return id(watermeter_totaal_aantal_pulseringen).state/4;'

  - platform: integration
    name: watermeter_totaal_verbruik
    id: watermeter_totaal_verbruik
    sensor: watermeter_pulsering_per_minuut
    entity_category: ''
    device_class: water
    unit_of_measurement: L
    time_unit: min
    filters:
    - multiply: .25
  
  - platform: template
    name: watermeter_verbruik_per_minuut
    id: watermeter_verbruik_per_minuut
    entity_category: ''
    device_class: volume_flow_rate
    unit_of_measurement: L/min
  #- platform: template
  #  name: watermeter_totaal_verbruik
  #  id: watermeter_totaal_verbruik
  #  entity_category: ''
  #  device_class: water
  #  unit_of_measurement: L

But this is a shot in the dark again by me…

It gives one pulse every 5 liters (or every 0.5 liters for old smaller ones).

I think you are over-complicating the thing.
Just default setup should be enough (maybe some internal_filter tuning is needed if the signal is not clean).

sensor:
  - platform: pulse_counter
    pin:
      number: 14
      inverted: true
      mode:
        input: true
        pullup: true
    name: 'L/min'
    filters:
      - multiply: 5  # (or 0.5 for small meter)

    total:
      unit_of_measurement: 'L'
      name: 'L Total'
      filters:
        - multiply: 5  # (or 0.5 for small meter)

And so it seems. :smile:

Your suggestion works; thanks!

You’re welcome!