ESP32 Electricity Meter Pulse Reader for Home Assistant (ESPHome 2026 Guide)

Read your home electricity meter’s pulse LED with an ESP32 and ESPHome, then send real-time power and total kWh to Home Assistant. This guide covers optical sensor setup, pulse_meter configuration, conversion from imp/kWh to watts and kWh, Energy dashboard integration, calibration, and common fixes for false pulses and spikes.

Home Assistant has made this kind of project more useful than it used to be. The Energy dashboard now supports real-time power alongside energy data, and the Now view shows live power badges at a glance. Home Assistant also officially documents Home Assistant Glow, an open-source ESPHome-based way to read an electricity meter’s flashing activity LED.

Why this project is worth doing

A simple optical pulse reader gives you:

  • live power in watts
  • total imported energy in kWh
  • proper Energy dashboard support
  • daily and monthly usage tracking
  • a fully local setup with no cloud dependency

ESPHome’s pulse_meter is a strong fit because it measures the time between pulses instead of only counting pulses in a fixed interval, which gives better resolution at low pulse rates.

Which electricity meters this works with

This guide is for meters that have a small flashing LED or other visible pulse indicator. Home Assistant’s electricity-grid docs explicitly note that many meters flash an LED whenever energy passes through them, and that monitoring the time between flashes lets you determine consumption.

This is the best fit when you want a non-invasive external reader stuck over the pulse LED, basically the same idea as Home Assistant Glow.

If your meter exposes a smarter interface such as P1, IEC62056-21, or SML, those are often better than pulse-reading because they can provide richer direct data. Home Assistant documents all of those options too.

What you need

  • an ESP32 development board
  • a small optical sensor positioned over the meter LED
  • a short cable and jumper wires
  • black heat-shrink, foam, or a small printed hood to block ambient light
  • Home Assistant
  • ESPHome

The most important value is the meter constant, usually printed on the front as something like:

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

That tells you how many LED flashes equal 1 kWh, and it controls all the math. ESPHome’s own examples use this impulse constant directly for the conversion.

How the pulse math works

pulse_meter reports pulses per minute by default. ESPHome’s documentation shows the power conversion formula using the meter’s impulse constant. For a 10000 pulses/kWh meter, the example multiplier is 6, which comes from (60 / impulse constant) × 1000.

Using that same formula:

  • Power in W = pulses/min × 60000 / imp_per_kWh
  • Total in kWh = total pulses × 1 / imp_per_kWh

So:

  • for 1000 imp/kWh, multiply live pulses/min by 60
  • for 2000 imp/kWh, multiply by 30
  • for 10000 imp/kWh, multiply by 6

And for totals:

  • 1000 imp/kWh → total multiplier 0.001
  • 2000 imp/kWh → total multiplier 0.0005
  • 10000 imp/kWh → total multiplier 0.0001

Wiring and placement

The critical part is not the ESP32. It is the sensor placement.

You want the optical sensor to see only the meter’s pulse LED and as little room light as possible. In practice, that means:

  • place the sensor directly over the flashing LED
  • shield it from daylight and reflections
  • keep the wiring tidy and short
  • test with the utility cupboard open and closed

Home Assistant Glow follows exactly this “stick it on top of the meter LED” approach.

ESPHome YAML example for 1000 imp/kWh

This is the easiest common example. It assumes:

  • your meter flashes 1000 impulses per kWh
  • you are reading the meter’s pulse LED optically
  • the sensor output is presented to GPIO27
esphome:
name: esp32-electricity-meter
friendly_name: ESP32 Electricity Meteresp32:
board: esp32dev
framework:
type: arduinologger:api:ota:wifi:
ssid: !secret wifi_ssid
password: !secret wifi_passwordsensor:
- platform: pulse_meter
pin: GPIO27
name: "Grid Power"
id: grid_power
unit_of_measurement: "W"
device_class: power
state_class: measurement
accuracy_decimals: 0 # Start conservative and tune later
internal_filter: 20ms
timeout: 30s # 1000 imp/kWh:
# W = pulses/min × (60000 / 1000) = pulses/min × 60
filters:
- multiply: 60 total:
name: "Grid Energy Total"
id: grid_energy_total
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
accuracy_decimals: 3 # kWh = total pulses × (1 / 1000)
filters:
- multiply: 0.001

That matches Home Assistant’s requirements for Energy dashboard entities: power sensors should use device_class: power with state_class: measurement, while energy totals should use device_class: energy with state_class: total or total_increasing and an accepted unit such as kWh.

ESPHome YAML example for 10000 imp/kWh

Many digital meters use a much higher pulse rate such as 10000 imp/kWh. ESPHome’s own docs use that example and show a live-power multiplier of 6.

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: 30s
filters:
- multiply: 6 total:
name: "Grid Energy Total"
id: grid_energy_total
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
accuracy_decimals: 3
filters:
- multiply: 0.0001

Choosing the debounce filter

ESPHome documents internal_filter as a debounce/noise filter: if a pulse shorter than that time is detected, it is discarded.

For electricity meters, a good starting point is often around 20 ms, and ESPHome’s example uses that exact value for a 10000 imp/kWh meter at up to 16 kW. Their cookbook walks through the reasoning: at that pulse rate, anything faster than about 22.5 ms would imply a higher load than expected, so 20 ms is a sensible rejection threshold.

In real life:

  • if you get false spikes, increase the filter a bit
  • if you miss pulses at high load, the filter may be too aggressive
  • if the reading jumps around in daylight, improve the light shielding first

Adding it to Home Assistant Energy

Once the ESPHome node is online:

  • confirm Grid Power changes when the house load changes
  • confirm Grid Energy Total only ever goes upward
  • let Home Assistant collect statistics
  • add the total kWh sensor to the Energy dashboard
  • optionally add the live power sensor where real-time power is supported

If a sensor does not appear in Energy, Home Assistant says to check four things first:

  • correct device_class
  • correct state_class
  • correct unit
  • no statistics errors in Developer Tools

Calibrating the total to match the real meter

ESPHome supports pulse_meter.set_total_pulses, which lets you set the raw pulse count so your ESPHome total matches the actual utility meter. ESPHome also notes an important detail: this value is the raw pulse count, not the filtered engineering value you see after conversion.

So if your physical electricity meter reads 12345.678 kWh:

  • at 1000 imp/kWh, raw pulses = 12345.678 × 1000 = 12,345,678
  • at 10000 imp/kWh, raw pulses = 12345.678 × 10000 = 123,456,780

Example API action:

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

That is useful after first installation, after testing, or if you ever want the ESPHome total to line up exactly with the utility meter again.

Daily and monthly tracking with Utility Meter

The Energy dashboard is useful, but for billing-style views you will usually also want daily and monthly helper sensors.

Home Assistant’s utility_meter integration is built for exactly that. It tracks a source sensor in billing-style cycles, supports tariffs, and keeps values persistent across restarts. The docs also explain that if your source sensor is a domestic utility meter that does not reset during normal use, periodically_resetting should be disabled.

utility_meter:
electricity_daily:
source: sensor.grid_energy_total
cycle: daily
periodically_resetting: false electricity_monthly:
source: sensor.grid_energy_total
cycle: monthly
periodically_resetting: false electricity_yearly:
source: sensor.grid_energy_total
cycle: yearly
periodically_resetting: false

If you use time-of-use tariffs, utility_meter can also split the same source into peak and off-peak counters.

Common problems

Power is stuck at zero

Usually one of these:

  • wrong sensor position
  • LED is too dim for your optical setup
  • wrong GPIO
  • wrong logic level from the sensor
  • timeout is fine but pulses are simply not being seen

Power is absurdly high

Usually:

  • wrong imp/kWh constant
  • false double-counting
  • ambient light causing phantom pulses
  • internal_filter too low

This is the classic “looks impressive but is nonsense” problem.

Total energy drifts from the real meter

Usually:

  • a few missed pulses from bad alignment
  • occasional false pulses from reflections
  • wrong multiplier
  • total not calibrated at the start

The sensor will not appear in Energy

Check the entity attributes first:

  • power sensor: device_class: power, state_class: measurement
  • total sensor: device_class: energy, state_class: total or total_increasing
  • energy unit should be kWh or another accepted energy unit
  • fix any statistics warnings before trying again

Good dashboard ideas

Once it works, add:

  • a live watts tile for current demand
  • daily and monthly utility-meter cards
  • a history graph for evening peaks
  • an automation that notifies you if base load never drops overnight
  • a card comparing today vs yesterday

This kind of project gets even more useful now that Home Assistant’s Energy dashboard includes live power views and clearer utility sections.

This is one of the better ESP32 + Home Assistant projects because it solves a real problem. It is cheap, local, non-invasive, and it plugs neatly into the official Home Assistant energy workflow. For a lot of homes, an optical pulse reader is also the easiest route because you do not need to interfere with the meter internals at all.

Share your love

Leave a Reply

Your email address will not be published. Required fields are marked *