ESPHome pulse_meter vs pulse_counter for Utility Meters

ESPHome pulse_meter vs pulse_counter for electricity, water and gas meters: low-flow accuracy, debounce, pulse totals, conversion formulas, ESP32 PCNT limits and working YAML examples.

For most electricity, water and gas pulse meters, ESPHome’s pulse_meter is now the better default than pulse_counter. Both components can count pulses and convert them into watts, litres per minute or cubic metres, but they calculate the live rate differently.

pulse_counter counts how many pulses occurred inside a fixed reporting interval. pulse_meter measures the time between pulses. That difference becomes very important at low power or low flow, where pulses may be many seconds or even minutes apart.

ESPHome describes pulse_meter as a drop-in replacement for pulse_counter and specifically notes its higher resolution at low pulse rates. For utility meters, that usually makes it the more natural component.

pulse_meter vs pulse_counter at a Glance

Featurepulse_meterpulse_counter
Measurement methodMeasures time between pulsesCounts pulses during an update interval
Low pulse-rate resolutionExcellentMore quantised
Default outputPulses/minutePulses/minute
UpdatesOn detected pulses, plus timeout handlingAt fixed update_interval
Total pulse sensorYesYes
ESP32 hardware PCNTNo fixed-window PCNT requirementUses PCNT by default
Long debounce/filter timesEasyLimited to 13 µs with ESP32 PCNT enabled
Best useUtility meters, slow flow, low power, RPMFast pulse counting, fixed-window frequency measurement

If you are building a new utility-meter project in 2026, start with pulse_meter unless you have a specific reason to use the hardware pulse counter.

Why pulse_meter Is Better at Low Rates

Imagine an electricity meter that produces 1000 pulses per kWh. At a constant 60 W load, one pulse represents 1 Wh and therefore arrives roughly once per minute.

If pulse_counter is using a 10-second update interval, most reporting windows contain zero pulses. A window that happens to contain one pulse suddenly appears much higher. The average is correct over time, but the live value becomes stepped and noisy.

pulse_meter instead measures the interval between valid pulses. If the interval is close to 60 seconds, ESPHome can infer the corresponding pulse frequency directly. That produces a much smoother and more meaningful low-load power value.

The same problem appears in water and gas monitoring. At a dripping tap or very low gas flow, the physical meter may produce one pulse only occasionally. A fixed-window counter has very little information to work with, whereas pulse timing still gives a usable rate once the next pulse arrives.

The Main Trade-Off: Low-Rate Values Are Inherently Delayed

No software component can calculate a new rate before the physical meter produces information. If a meter generates one pulse every three minutes, you cannot know the new rate immediately after a load changes; you have to wait for another pulse.

This is why utility-meter pulse monitoring can have excellent accumulated totals while still feeling slow at very low flow or power. The limitation comes from the meter’s pulse resolution, not from ESPHome.

pulse_meter has a timeout option. If no new pulse arrives within that period, the reported rate becomes zero. The current default is five minutes. Choose a timeout longer than the longest legitimate pulse interval you expect, otherwise low but valid consumption may be shown as zero between pulses.

Electricity Meter Pulse Math

Electricity meters usually print an impulse constant such as:

1000 imp/kWh
2000 imp/kWh
10000 imp/kWh

Because ESPHome reports pulses per minute, live electrical power is:

Power (W) =
pulses/min × 60000 / impulses_per_kWh

So the common multipliers are:

Meter constantMultiply pulses/min byTotal kWh per pulse
1000 imp/kWh600.001
2000 imp/kWh300.0005
10000 imp/kWh60.0001

For example, a 1000 imp/kWh meter producing 10 pulses per minute corresponds to:

10 × 60 = 600 W

For a wider comparison of pulse monitoring against CT clamps, PZEM and Modbus meters, see our ESP32 Energy Monitoring Methods comparison.

Recommended Electricity YAML: pulse_meter

This example assumes a 1000 imp/kWh electricity meter and an optical sensor connected to GPIO27:

sensor:
  - platform: pulse_meter
    pin: GPIO27
    name: "Grid Power"
    id: grid_power
    unit_of_measurement: "W"
    device_class: power
    state_class: measurement
    accuracy_decimals: 0

    internal_filter: 20ms
    timeout: 5min

    filters:
      - multiply: 60

    total:
      name: "Grid Energy Total"
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing
      accuracy_decimals: 3
      filters:
        - multiply: 0.001

For 10000 imp/kWh, change the live multiplier to 6 and the total multiplier to 0.0001.

This is also the architecture used in our ESP32 electricity meter pulse reader guide.

Water Meter Pulse Math

Water meters often specify a physical volume per pulse rather than pulses per kWh. Examples might be 1 L/pulse, 0.5 L/pulse or 0.1 L/pulse. Always use the value printed on your own meter or pulse-output documentation.

If one pulse represents L litres:

Flow (L/min) = pulses/min × litres_per_pulse

Total litres = total_pulses × litres_per_pulse

Total m³ = total_pulses × litres_per_pulse / 1000

Water Meter Example

Assume one pulse represents 0.1 litres:

sensor:
  - platform: pulse_meter
    pin:
      number: GPIO27
      mode:
        input: true
        pullup: true

    name: "Water Flow"
    id: water_flow
    unit_of_measurement: "L/min"
    device_class: volume_flow_rate
    state_class: measurement
    accuracy_decimals: 2

    internal_filter: 50ms
    timeout: 5min

    filters:
      - multiply: 0.1

    total:
      name: "Water Total"
      unit_of_measurement: "m³"
      device_class: water
      state_class: total_increasing
      accuracy_decimals: 3
      filters:
        - multiply: 0.0001

The total multiplier is 0.0001 because each pulse represents 0.1 L, which is 0.0001 m³.

For a complete Home Assistant implementation, see our ESP32 Water Meter for Home Assistant guide.

Gas Meter Pulse Math

Gas pulse interfaces frequently represent a fixed volume per pulse, such as 0.01 m³/pulse. The exact value depends on the meter and sensor arrangement.

If the meter generates 0.01 m³ per pulse:

Flow (m³/min) = pulses/min × 0.01

Total (m³) = total_pulses × 0.01

For domestic gas monitoring, the total is usually more important than a rapidly changing live flow value. Pulses can be widely spaced at low consumption, so pulse_meter‘s interval-based measurement is particularly useful.

See our ESP32 Gas Meter Pulse Sensor guide for the complete gas-specific setup.

Why pulse_counter Can Look Stepped

pulse_counter checks how many pulses occurred during its update window. Its default update interval is 60 seconds.

Imagine a 10-second update interval and a source averaging one pulse every 30 seconds. Individual windows can contain:

0 pulses
0 pulses
1 pulse
0 pulses
0 pulses
1 pulse

The average over a long period is correct, but the short-term rate jumps between zero and a much larger discrete value. You can make the window longer to smooth the reading, but that also makes the sensor slower to react.

pulse_meter avoids that particular quantisation by using the pulse-to-pulse interval directly.

When pulse_counter Is Still Useful

pulse_counter is not obsolete. On ESP32 it can use the hardware PCNT peripheral, which provides very accurate pulse counting with low CPU involvement. ESPHome currently allows up to eight hardware pulse-counter channels.

It remains a strong choice when:

  • The pulse rate is relatively high and stable.
  • You want a fixed-window frequency or RPM measurement.
  • You need hardware-assisted counting with minimal software overhead.
  • Your input does not need long software debounce times.

For a domestic utility meter with slow or intermittent pulses, those advantages are usually less important than the improved low-rate resolution of pulse_meter.

The ESP32 PCNT Filter Limitation

This is one of the most important differences if you are reading an S0 output, reed switch or other slow pulse source.

On ESP32, pulse_counter uses the hardware PCNT peripheral by default. ESPHome documents that the PCNT internal filter cannot be set higher than approximately 13 µs. That is far too short to debounce a mechanical contact or reject a 20–100 ms glitch/bounce pattern.

ESPHome’s documentation notes that 50–100 ms is a reasonable filtering range for some S0-style utility pulses, but on ESP32 you only get those longer filter values with use_pcnt: false.

sensor:
  - platform: pulse_counter
    pin: GPIO27
    name: "Utility Pulse Counter"

    use_pcnt: false
    internal_filter: 50ms
    update_interval: 10s

Once you disable PCNT to gain a long software filter, one of the biggest reasons for choosing pulse_counter has disappeared. For this type of slow utility input, pulse_meter is usually the cleaner solution.

pulse_meter internal_filter: EDGE vs PULSE

pulse_meter provides two filtering modes.

EDGE mode

This is the default. Rising edges that occur too quickly after the previous accepted edge are discarded. It is useful for a basically clean pulse source with some contact bounce or narrow false edges.

internal_filter: 20ms
internal_filter_mode: EDGE

PULSE mode

In PULSE mode, ESPHome checks the pulse itself: if additional transitions occur before the filter time has passed, the rising edge is rejected. In practical terms, the HIGH pulse has to remain valid for at least the filter duration.

internal_filter: 50ms
internal_filter_mode: PULSE

PULSE mode can be useful when the pulse has noise around both edges. EDGE mode is often the better starting point for a clean optical comparator or open-collector output.

How to Choose the Filter Time

Do not simply copy 20ms or 50ms from another project. The filter must be shorter than the shortest real pulse you need to detect.

For an electricity meter with 10000 imp/kWh and a maximum expected load of 16 kW, ESPHome’s own example shows why approximately 20 ms is sensible: a physically valid pulse interval at the maximum load is about 22.5 ms, so filtering anything shorter than around 20 ms rejects implausibly fast noise without clipping normal operation.

For a dry-contact water meter that closes for 100 ms, a 30–50 ms filter may be reasonable. For a fast flow sensor producing millisecond pulses, the same filter would throw away real data.

The rule is:

filter time
< shortest legitimate pulse width or interval
> typical noise/bounce duration

S0 Outputs, Reed Switches and Optical Sensors

The ESPHome component choice is only half the project. Utility meters expose pulses in several electrical forms.

Pulse sourceTypical interfaceDesign point
Electricity meter LEDPhototransistor / photodiode / comparatorShield from ambient light
S0 electricity outputIsolated transistor / open collectorUse correct pull-up and isolation arrangement
Water meter reed contactDry contactPull-up + debounce/filtering
Hall-effect flow sensorDigital pulse outputCheck voltage and maximum frequency
Gas meter pulse sensorReed/Hall/external magnetic pickupCheck exact volume per pulse

For an electricity meter, the safest DIY approach is normally an isolated S0 output or a non-invasive optical reader. Do not open a sealed utility meter or connect ESP32 wiring to unknown internal mains circuitry.

Pull-Ups and Input Logic

Many S0, Hall and reed interfaces are open-collector or simply pull the ESP32 input to ground. In that case you need a pull-up.

pin:
  number: GPIO27
  mode:
    input: true
    pullup: true

The ESP32’s internal pull-up can be enough for a short, clean connection. For longer field wiring or electrically noisy environments, an external resistor gives a more controlled input impedance and is often preferable.

Do not apply a 5 V pulse directly to an ESP32 GPIO. Check the meter output specification, use a suitable pull-up voltage, divider, optocoupler or level-shifting interface as required.

Count Rising or Falling Edges?

With pulse_counter, ESPHome lets you choose what happens on rising and falling edges through count_mode. If the source creates one clean LOW-going pulse, count only one edge—not both—or your total will double.

count_mode:
  rising_edge: DISABLE
  falling_edge: INCREMENT

ESPHome also notes that when using the pulse-counter internal filter, increasing on the falling edge is the intended configuration. With pulse_meter, the filtering model is different and the component handles the pulse timing directly.

Total Pulses Are Better Than Integrating the Live Rate

If the physical meter already tells you that each pulse represents a precise quantity, use the pulse total directly for lifetime energy, water or gas.

For example, a 1000 imp/kWh meter physically defines:

1 pulse = 0.001 kWh

Multiplying the total number of valid pulses by 0.001 preserves that direct relationship. Integrating a calculated watts value over time introduces unnecessary numerical approximation when the source already provides an accumulated quantity.

total_daily_energy is still useful when you specifically want a daily energy helper from a power sensor. For a pulse-output utility meter, though, the total: pulse sensor is normally the strongest source for the lifetime quantity.

Important: The ESPHome Pulse Total Is Not the Physical Meter Register

The total: sensor is the number of pulses ESPHome has counted during its running session. Current ESPHome source initialises that internal count when the component starts. Do not assume it is a permanently stored copy of the utility meter’s lifetime register.

ESPHome provides pulse_meter.set_total_pulses and pulse_counter.set_total_pulses actions so you can seed the raw count. That lets you align the ESPHome value with the physical meter when commissioning or after a reset.

If exact on-device continuity across reboots matters, deliberately implement a restoration strategy rather than relying on the basic pulse component alone. Be cautious about writing flash on every pulse; frequent persistent writes can create unnecessary flash wear.

Setting pulse_meter Total to Match the Utility Meter

Suppose a 1000 imp/kWh electricity meter reads 12,345.678 kWh. The equivalent raw pulse count is:

12,345.678 × 1000
= 12,345,678 pulses

You can expose an ESPHome API action:

api:
  actions:
    - action: set_grid_total
      variables:
        raw_pulses: int
      then:
        - pulse_meter.set_total_pulses:
            id: grid_power
            value: !lambda "return raw_pulses;"

ESPHome’s documentation specifically notes that this action takes the raw pulse count, not the filtered engineering value such as kWh.

pulse_counter Electricity Example

If you deliberately want pulse_counter, a 1000 imp/kWh configuration might be:

sensor:
  - platform: pulse_counter
    pin: GPIO27
    name: "Grid Power Counter"
    id: grid_power_counter
    unit_of_measurement: "W"
    device_class: power
    state_class: measurement

    update_interval: 60s

    filters:
      - multiply: 60

    total:
      name: "Grid Energy Counter"
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing
      filters:
        - multiply: 0.001

At high enough pulse rates, this works very well. At low load, expect the rate to change in visible steps because the component only knows how many whole pulses occurred during each update interval.

How update_interval Changes pulse_counter Behaviour

A shorter update interval gives faster response but more quantisation. A longer interval gives a smoother average but reacts more slowly.

Update intervalAdvantageDisadvantage
5 sFast reactionVery stepped at low pulse rates
10 sReasonable responsivenessStill quantised for slow utility pulses
60 sMuch smoother averageSlow response to changes
Several minutesGood for very low-rate totalsPoor live-rate display

This is the fundamental trade-off that pulse_meter avoids for most slow sensors.

Should You Smooth pulse_meter?

pulse_meter can react strongly to one unusual pulse interval. That is sometimes real—for example a kettle switching on—and sometimes noise.

Do not immediately add heavy averaging. First make the pulse detection physically reliable. Fix sensor alignment, shielding, pull-ups and false edges before filtering the resulting measurement.

If you want a calmer Home Assistant graph, a modest ESPHome filter such as throttle_average can reduce update frequency while retaining the interval-based measurement:

filters:
  - multiply: 60
  - throttle_average: 30s

For billing totals, keep the raw pulse count independent of any display smoothing.

Home Assistant Energy and Utility Meter

For Home Assistant, expose the accumulated utility quantity with the correct device class and a monotonically increasing total state.

UtilityTypical total unitDevice class
ElectricitykWhenergy
Waterm³ or Lwater
Gasgas

Use Home Assistant’s utility-meter helpers when you want daily, monthly or tariff-based counters derived from the lifetime source. Keep the ESPHome pulse conversion simple and let Home Assistant handle billing periods and dashboards.

Which Component Should You Use?

ApplicationRecommended componentReason
Electricity meter LEDpulse_meterExcellent low-load resolution
S0 electricity outputpulse_meterLong debounce/filter values are easy
Water meter reed contactpulse_meterSlow pulses + contact bounce
Gas meter pulse pickuppulse_meterVery low pulse rates
Hall flow sensor at moderate rateUsually pulse_meterGood live flow response
High-frequency clean pulse trainpulse_counterESP32 hardware PCNT is efficient
Fixed-window frequency measurementpulse_counterDirect counting over known interval

Common Problems

SymptomLikely cause / first check
Live value jumps between zero and a large numberFixed-window pulse_counter quantisation at low pulse rates; use pulse_meter.
Total is double the physical meterCounting both edges or receiving a double pulse for each event.
Huge random power spikesFalse optical pulses, contact bounce or insufficient internal_filter.
Valid high-load pulses disappearFilter time is too long.
Water flow remains non-zero after tap closespulse_meter timeout is longer than desired.
Low flow repeatedly becomes zeroTimeout is too short for legitimate pulse spacing.
Long debounce rejected by ESP32 pulse_counterHardware PCNT filter limit; use use_pcnt: false or switch to pulse_meter.
Lifetime total returns to a lower value after device restartDo not assume the raw ESPHome pulse total is a persistent physical meter register; re-seed or implement persistence deliberately.

Recommended Setup for Utility Meters

For most ESP32 + ESPHome utility-meter projects, I would use this approach:

  • Use pulse_meter.
  • Use one valid edge per physical pulse.
  • Choose internal_filter from the real pulse width and maximum expected rate.
  • Set timeout based on the slowest legitimate consumption you want to display.
  • Convert pulses/min directly into W, L/min or m³/min.
  • Use the component’s raw pulse total for accumulated quantity rather than integrating the live rate.
  • Send the cumulative quantity to Home Assistant and derive daily/monthly counters there.
  • Use an isolated S0 output or optical sensor for electricity meters rather than interfering with meter internals.

pulse_counter remains valuable for faster clean pulse trains, but the moment a project involves slow pulses, low-flow resolution or tens of milliseconds of debounce, pulse_meter is usually the simpler and better component.

Related ESP32 Utility Meter Guides

External Resources

Share your love