How to count pulse frequency accurately with ESPHome?

Nice! Looking forward to get your pr merged in the next release which should be in around 4 weeks (first week of march).

From what it looks like there is only some tests left to get it merged actually :rocket:

If some one here uses the component it would be nice to comment that directly in the github issue pr (best as a review and approve it) :+1:

Yeah, would like to get that merged. I’d like to add tests, but I’ve not found any examples of tests on this sort of component to follow, any suggestions?

Not really as I’m no programmer at all. I already start sweating when I can’t implement by stuff with pure yaml in esphome but need to go down the road and fight with lambda :facepunch:.

I could only suggest to join the discord channel from esphome and ask for help in the #dev channel there. Most likely there is someone who can help you with that :+1:

1 Like

This is working for me, updated config:

sensor:
  - platform: pulse_meter
    name: "Electricity"
    pin:
      number: D5
      mode: INPUT_PULLUP
    unit_of_measurement: "kW"
    internal_filter: 100ms
    accuracy_decimals: 3
    timeout: 2 min
    filters:
      # Filter outliers
      - median:
          window_size: 3
          send_every: 1
          send_first_at: 1
      # Convert pulses/min (Wh/min) to kW
      - multiply: 0.06
    total:
      name: "Electricity Meter"
      unit_of_measurement: "kWh"
      accuracy_decimals: 3
      filters:
        - multiply: 0.001

  - platform: pulse_meter
    name: "Gas"
    pin:
      number: D6
      mode: INPUT_PULLUP
    unit_of_measurement: "kW"
    internal_filter: 4s
    accuracy_decimals: 3
    timeout: 2 min
    filters:
      # Convert 0.01 m³ (one pulse) to kWH/min using:
      #
      # 1.022640 * 39.2 / 3.6 / 100 = 0.1113541333
      #
      # Then kWH/min to kW by multiplying by 60 = 6.6812
      - multiply: 6.6812
    total:
      name: "Gas Meter"
      unit_of_measurement: "m³"
      accuracy_decimals: 2
      filters:
        # One revolution of the small dial is 10l
        - multiply: 0.01

Looks like the config is being honoured:

16:28:43][C][pulse_meter:052]: Pulse Meter 'Electricity'
[16:28:43][C][pulse_meter:052]:   Unit of Measurement: 'kW'
[16:28:43][C][pulse_meter:052]:   Accuracy Decimals: 3
[16:28:43][C][pulse_meter:052]:   Icon: 'mdi:pulse'
[16:28:43][C][pulse_meter:053]:   Pin: GPIO14 (Mode: INPUT_PULLUP)
[16:28:43][C][pulse_meter:054]:   Filtering pulses shorter than 100000 µs
[16:28:43][C][pulse_meter:055]:   Assuming 0 pulses/min after not receiving a pulse for 120s
[16:28:43][C][pulse_meter:052]: Pulse Meter 'Gas'
[16:28:43][C][pulse_meter:052]:   Unit of Measurement: 'kW'
[16:28:43][C][pulse_meter:052]:   Accuracy Decimals: 3
[16:28:43][C][pulse_meter:052]:   Icon: 'mdi:pulse'
[16:28:43][C][pulse_meter:053]:   Pin: GPIO12 (Mode: INPUT_PULLUP)
[16:28:43][C][pulse_meter:054]:   Filtering pulses shorter than 4000000 µs
[16:28:43][C][pulse_meter:055]:   Assuming 0 pulses/min after not receiving a pulse for 120s

I have just updated to the latest ESPHome which might make a difference?

I’ve just changed this again to take out the median filter, I suspect the lack of pull-up might be the original cause of the noise (so hopefully the median filter will no longer be required).

This seems to be exactly what I’m looking for to improve my water meter sensor. Thank you very much!

Just a dumb question, as for the installation, what exactly should I unpack and see in the /config/esphome/ directory?

  • the custom_components folder,
  • the custom_components folder from the (higher level) custom_components folder,
  • the pulse_meter folder
  • or the files from the pulse_meter folder?

Hi, it should look something like this:

So in config/esphome you should see your devices, alongside those there should be a custom_components directory with the pulse_meter folder inside it.

1 Like

Thank you it works. Used file editor to create the ‘custom_components’ director and the ‘pulse_meter’ directory in that and then copied the files. Excellent.

The sensor works great. I use it for my watermeter with an indictive sensor giving 1 pulse per liter.

1 Like

You’re right, it’s OK for me too !!!
Now everthing works fine, thanks you very much !! :+1:

Hi and thanks for pulse_meter!

3 questions:

1: Do you have any idea why I have something weird with 0 watts in my graph
-esp211 is pulse_meter with S0-pulsemeter. Sensor config is quite same with stevebaxter
-esp240 is sonoff powr2 with esphome for reference
-They measure same load (freezer+fridge) and shows quite same load (~75w/140w)

And esp211 log shows that it detects timeout but after that does not send 0w:

[20:06:58][D][sensor:092]: 'esp211-powermeter-1': Sending state 74.20997 W with 0 decimals of accuracy
[20:06:58][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24200 kWh with 3 decimals of accuracy
[20:07:47][D][sensor:092]: 'esp211-powermeter-1': Sending state 74.16258 W with 0 decimals of accuracy
[20:07:47][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24400 kWh with 3 decimals of accuracy
[20:08:37][D][sensor:092]: 'esp211-powermeter-1': Sending state 73.18560 W with 0 decimals of accuracy
[20:08:37][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24500 kWh with 3 decimals of accuracy
[20:09:26][D][sensor:092]: 'esp211-powermeter-1': Sending state 73.05936 W with 0 decimals of accuracy
[20:09:26][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24600 kWh with 3 decimals of accuracy
[20:10:15][D][sensor:092]: 'esp211-powermeter-1': Sending state 73.05936 W with 0 decimals of accuracy
[20:10:15][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24700 kWh with 3 decimals of accuracy
[20:11:05][D][sensor:092]: 'esp211-powermeter-1': Sending state 72.81112 W with 0 decimals of accuracy
[20:11:05][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.24900 kWh with 3 decimals of accuracy
[20:11:54][D][sensor:092]: 'esp211-powermeter-1': Sending state 72.81112 W with 0 decimals of accuracy
[20:11:54][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.25000 kWh with 3 decimals of accuracy
[20:12:43][D][sensor:092]: 'esp211-powermeter-1': Sending state 73.01639 W with 0 decimals of accuracy
[20:12:43][D][sensor:092]: 'esp211-totaldailyenergy-1': Sending state 1.25100 kWh with 3 decimals of accuracy
[20:14:43][D][pulse_meter:025]: No pulse detected for 120s, assuming 0 pulses/min
[20:14:43][D][sensor:092]: 'esp211-powermeter-1': Sending state 73.01639 W with 0 decimals of accuracy

2.: With influxdb and grafana, do we need force_update, seems that it is not working yet?

3.: Do we need some kind of “limiter” for energy messages (like combination of update_interval and force_update)? Now it sends quite lot of messages with heavy load, 1/sec with 6kw.


Petri

hi Steve,

this is great work, I’m only using HA for a couple of weeks now because i just moved from Domoticz to Home Assistant.

Within Domoticz i used the pluse counter to monitor my Solar panels and water meter but i cannot get i working like i would in HA.

I downloaded your pulse_counter and uploaded it in the ESPhome directory but what to do next?
Should i manually add an pulse_counter to ESPH? Is this the correct way?

I did that but when i tried to add the timeout option i recieve an error.

I use a KWH meter with 0,5 wh every puls, so 2000 pulses every 1 KWh.
I think you have a different KWH meter because you need to multiply to convert Wh/min to kw?

I have just set this up for a water meter that pulses every 10 litres. No other sensor was going to cut it. Thanks, stevebaxter, for the great work.

good you mention it.

i have the same issue with my Solar counter. in the evening it gets always stuck at exactly 32W (definitely no power going into the grid)
image

my code

  - platform: pulse_meter
    name: "Solareinspeisung"
    id: solareinspeisung
    pin:
      number: D2
      mode: INPUT_PULLUP
    unit_of_measurement: "kW"
    internal_filter: 100ms
    accuracy_decimals: 3
    timeout: 2 min
    filters:
      # Convert pulses/min (Wh/min) to kW
      - multiply: 0.06

what am I doing wrong? can this be solved?

thanks!

That is strange. When I set the timeout it does get to 0 my problem is the spikes I get. For that I wanted to use a median but when I do that it doesn’t reach 0
This is my current code

sensor:
  - platform: pulse_meter
    name: "Solar Panels"
    pin: 
      number: D1
     # mode: INPUT_PULLUP
    unit_of_measurement: "kW"
    internal_filter: 500ms
    accuracy_decimals: 3
    timeout: 1 min
    filters:
      # Filter outliers
      #- median:
      #    window_size: 5
      #    send_every: 2
      #    send_first_at: 1
      # Convert pulses/min (Wh/min) to kW
      - multiply: 0.06

INPUT_PULLUP also gives me a lot of spikes.
Above code does give me some spikes but with the internal filter set to 500 it’s okish.

Hi Petri,

Sorry for the slow replies, just catching up with this!

  1. That’s a bit odd. What we should see is this (this is from my gas meter, the electricity never goes to zero):
[20:03:25][D][sensor:092]: 'Gas': Sending state 34.42439 kW with 3 decimals of accuracy
[20:03:25][D][sensor:092]: 'Gas Meter': Sending state 5.28000 m³ with 2 decimals of accuracy
[20:03:37][D][sensor:092]: 'Gas': Sending state 34.35947 kW with 3 decimals of accuracy
[20:03:37][D][sensor:092]: 'Gas Meter': Sending state 5.29000 m³ with 2 decimals of accuracy
[20:05:37][D][pulse_meter:025]: No pulse detected for 120s, assuming 0 pulses/min
[20:05:37][D][sensor:092]: 'Gas': Sending state 0.00000 kW with 3 decimals of accuracy

So it should send the new sensor state after a pulse hasn’t been received for the timeout interval. Let me look at the code to see if I can spot a bug.

  1. I’m using Influx and Grafana, it seems to be working fine for me. What is the problem?

  2. You can do this using a throttle filter (see https://esphome.io/components/sensor/index.html#throttle-heartbeat-debounce-delta), though this may kind of defeat the purpose of the better temporal resolution this sensor provides. In practice I haven’t seen a problem with the frequency of messages. In Influx I may need to set a shorter lifetime for the real time data and a longer one for daily data (I have my system set up to calculate daily gas/electric and record this once a day as well as real-time data).

Update

Right, I see what is happening with it not going to zero - it’s the median filter. Let’s say the last 5 real values detected were 5, 6, 3, 10, 8. With a median filter of 3, we will actually output 5, 6, 8. When we get the 0 value, the median of 10, 8, 0 is still 8, so it will never go to zero. Unfortunately this means that for meters that may go to 0, using a median filter isn’t going to work.

The best solution is probably to work out where the spikes are coming from and eliminate them. As I say, setting the right pull up/down value on the pin solved this for me, I no longer have a median filter in my config and I don’t see any noise. A longer internal_filter value will help eliminate switching noise, but if you’re getting random signals that won’t help.

Cheers,

Steve

Hi Bas,

It needs to go in a “custom_components” directory, there’s some good info further up the thread:

Cheers,

Steve

On the spikes, I think there are two things to look at:

  1. My spikes went away when I set the pull-up correctly. The photodiode and hall sensors I’m using are active-low, changing the pin to pull-up made all the spikes go away. It’s possible that you might need pull-down depending on how your sensor is wired exactly (if it’s a switch wired between +5V and an input, I think you will need pull-down to avoid spikes). This is definitely not my area of expertise, but the info here was useful https://www.seeedstudio.com/blog/2020/02/21/pull-up-resistor-vs-pull-down-differences-arduino-guide/.
  2. You can also filter these out digitally using the internal_filter value which should help to ignore noise from photodiodes as they switch, or using a median filter to ignore random spike values.

Basically the meter will see sudden spikes in power that only last for one reading if we see two rising edges close together (but not so close that internal_filter rejects the second one.

i ve problem to read the correct values. mostly are the counters to much.
my board: NodeMCU ESP8266

my actually code:

sensor:
  - platform: pulse_meter
    pin: 
      number: D4
      mode: INPUT_PULLUP
    name: "ESPhome_Gaszaehler"
    internal_filter: 2000ms
    filters:
      - lambda: |-
          static float total_value = 43989.37;
          total_value += x * 0.01;
          return total_value;
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    timeout: 2 min
    icon: 'mdi:fire'

Values now:
Display Homeassistent: 44018.23
Display Gasmeter: 43994.20

you see, too many counters were recorded.

i played with+
mode: INPUT_PULLUP -> #mode: INPUT_PULLUP
internal_filter: 2000ms -> 4s -> 50ms -> 100ms
timeout: 2 min -> #timeout: 2 min

i use a reed sensor like this:
image

EDIT 1:
i try now this code, perhaps this will goes better

sensor:
  - platform: pulse_counter
    pin: 
      number: D4
      mode: INPUT_PULLUP
    name: "ESPhome_Gaszaehler"
    internal_filter: 1000ms
    count_mode:
     rising_edge: DISABLE
     falling_edge: INCREMENT
    filters:
      - lambda: |-
          static float total_value = 43995.75;
          total_value += x * 0.01;
          return total_value;
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    icon: 'mdi:fire

no success with both solutions (_meter and _pulse).
with both cases esphome shows me to many pulses…
internal_filter is now at 2500ms

what dows this setting?
-> timeout: 5min

EDIT:

latest attempt, also not working correct. to many counts in esphome.

sensor:
  - platform: pulse_meter
    pin: 
      number: D4
      mode: INPUT_PULLUP
    name: "ESPhome_Gaszaehler"
    internal_filter: 5000ms
    filters:
      - lambda: |-
          static float total_value = 44020.52;
          total_value += x * 0.01;
          return total_value;
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    timeout: 5min
    icon: 'mdi:fire

@stevebaxter Thanks for the hint on the pull-down.
I changed my config to INPUT_PULLDOWN_16 (I am using an ESP8266) and that seems to be working.
I will monitor it for a couple of days but the last 3 days no spikes :confetti_ball: