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
| Feature | pulse_meter | pulse_counter |
|---|---|---|
| Measurement method | Measures time between pulses | Counts pulses during an update interval |
| Low pulse-rate resolution | Excellent | More quantised |
| Default output | Pulses/minute | Pulses/minute |
| Updates | On detected pulses, plus timeout handling | At fixed update_interval |
| Total pulse sensor | Yes | Yes |
| ESP32 hardware PCNT | No fixed-window PCNT requirement | Uses PCNT by default |
| Long debounce/filter times | Easy | Limited to 13 µs with ESP32 PCNT enabled |
| Best use | Utility meters, slow flow, low power, RPM | Fast 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 constant | Multiply pulses/min by | Total kWh per pulse |
|---|---|---|
| 1000 imp/kWh | 60 | 0.001 |
| 2000 imp/kWh | 30 | 0.0005 |
| 10000 imp/kWh | 6 | 0.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 source | Typical interface | Design point |
|---|---|---|
| Electricity meter LED | Phototransistor / photodiode / comparator | Shield from ambient light |
| S0 electricity output | Isolated transistor / open collector | Use correct pull-up and isolation arrangement |
| Water meter reed contact | Dry contact | Pull-up + debounce/filtering |
| Hall-effect flow sensor | Digital pulse output | Check voltage and maximum frequency |
| Gas meter pulse sensor | Reed/Hall/external magnetic pickup | Check 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 interval | Advantage | Disadvantage |
|---|---|---|
| 5 s | Fast reaction | Very stepped at low pulse rates |
| 10 s | Reasonable responsiveness | Still quantised for slow utility pulses |
| 60 s | Much smoother average | Slow response to changes |
| Several minutes | Good for very low-rate totals | Poor 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.
| Utility | Typical total unit | Device class |
|---|---|---|
| Electricity | kWh | energy |
| Water | m³ or L | water |
| Gas | m³ | gas |
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?
| Application | Recommended component | Reason |
|---|---|---|
| Electricity meter LED | pulse_meter | Excellent low-load resolution |
| S0 electricity output | pulse_meter | Long debounce/filter values are easy |
| Water meter reed contact | pulse_meter | Slow pulses + contact bounce |
| Gas meter pulse pickup | pulse_meter | Very low pulse rates |
| Hall flow sensor at moderate rate | Usually pulse_meter | Good live flow response |
| High-frequency clean pulse train | pulse_counter | ESP32 hardware PCNT is efficient |
| Fixed-window frequency measurement | pulse_counter | Direct counting over known interval |
Common Problems
| Symptom | Likely cause / first check |
|---|---|
| Live value jumps between zero and a large number | Fixed-window pulse_counter quantisation at low pulse rates; use pulse_meter. |
| Total is double the physical meter | Counting both edges or receiving a double pulse for each event. |
| Huge random power spikes | False optical pulses, contact bounce or insufficient internal_filter. |
| Valid high-load pulses disappear | Filter time is too long. |
| Water flow remains non-zero after tap closes | pulse_meter timeout is longer than desired. |
| Low flow repeatedly becomes zero | Timeout is too short for legitimate pulse spacing. |
| Long debounce rejected by ESP32 pulse_counter | Hardware PCNT filter limit; use use_pcnt: false or switch to pulse_meter. |
| Lifetime total returns to a lower value after device restart | Do 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_filterfrom the real pulse width and maximum expected rate. - Set
timeoutbased 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
- ESP32 Electricity Meter Pulse Reader for Home Assistant — optical LED reading, power and kWh.
- ESP32 Water Meter for Home Assistant — flow rate, total water and leak detection.
- ESP32 Gas Meter Pulse Sensor — slow pulse monitoring and gas totals.
- ESP32 Energy Monitoring Methods Compared — pulse meter vs CT clamp vs PZEM vs Modbus.
- ESP32 PZEM-004T Energy Monitor — an alternative when you need voltage, current, watts and power factor.
External Resources
- ESPHome Pulse Meter Sensor — timing method, filters, timeout, total sensor and conversion examples.
- ESPHome Pulse Counter Sensor — ESP32 PCNT behaviour, edge counting and filter limits.
- ESPHome Non-Invasive Power Meter cookbook — official pulse-meter electricity example.
- ESPHome Total Daily Energy — daily integration of a power sensor where required.