Track a gas meter with ESPHome

I did, at least for my setup.

The issue is that the counter is susceptible to false counts as an effect of other electrical devices kicking in, in your house, and causing some effect on the ESP power supply.

I’ve tested 4 different 5V power supplies, all connected to the same socket in my kitchen, then to another socket also in the kitchen. Checked 3 different ESP-WROOM-32 boards as well. The external reed switch was disconnected, but when my oven, dishwasher pump or kettle was switched on, the counter would increment a few times.

When I switched to a power bank for the 5V, the issue went away completely and after 48h my gas usage was exactly correct down to 0.001m3. Ergo, the power supply is to blame for all the issue I and probably at least some of you are having.

Not sure how, how to fix it for AC powered setups, but since I am moving to a solar array for this one, I am not investing more time, which I already did an insane amount to get it resolved.

Hello!
I don’t know if this is still helpful. I had a similar problem with the inaccurate values. I have now found a solution that is sufficient for my needs. My goal is to track the gas consumption as a whole, i.e. total gas.
My setup:
ESP D1 mini, reed contact with AD converter (like this : Reed Switch).
The solution with pulse_counter and pulse_meter did not work satisfactorily.
My approach with binary_sensor and a template:

binary_sensor:
  - platform: gpio
    id: internal_pulse_counter
    pin: D5
    internal: true
    filters:
      - delayed_on: 100ms
    on_press:
      then:
        - lambda: id(total_pulses) += 1;
sensor:
  - platform: template
    name: "total_gas"
    device_class: gas
    unit_of_measurement: "m³"
    state_class: "total_increasing"
    icon: "mdi:fire"
    accuracy_decimals: 2
    lambda: |-
        return id(total_pulses) * 0.01;
globals:
  - id: total_pulses
    type: int
    initial_value: '0'

3 Likes

@navierstokes could it be that you missed the “sensor” component entry for this?
(as a binary sensor it doies not compile)

1 Like

Exactly, sorry for the confusion. I have corrected this.

This is my yaml, works great for the total amount consumed.
But I’m still struggling with the flow rate

sensor:
  - platform: pulse_meter
    name: "${device_friendly_name} Gasverbrauch"
    id: ${device_entity_id}_gasverbrauch
    unit_of_measurement: 'm³/min'
    internal_filter: 3 min # the flow rate would never get so fast that the pulse meter would get more than 1 pulse in 3 minutes (in my case)
    timeout: 10 min # after 10 min, the pulse meter assumes 0 m³/min
    state_class: measurement
    device_class: gas
    icon: mdi:meter-gas
    #accuracy_decimals: 3
    pin:
      number: $gas_read_pin # GPIO5 on my D1 mini pro
      mode: INPUT_PULLUP # reed contact connects pin to ground
    filters: # I'm still struggling to get the flow rate right 😬
       - lambda: return x * (1.0 / ${gas_imp_value});
    total: # this sensor tracks the total gas consumed in m³
      name: "${device_friendly_name} Gasverbrauch Total"
      id: ${device_entity_id}_gasverbrauch_total
      unit_of_measurement: 'm³'
      icon: mdi:meter-gas-outline
      state_class: total_increasing
      device_class: gas
      accuracy_decimals: 1
      filters: # 1 imp / 0.1 m³ = 10 imp / m³ (in my case); gas_imp_value = 10;
        - lambda: return x * (1.0 / ${gas_imp_value});
      on_value: # I have a led that blinks for every impulse 
        then:
          - script.execute: gas_led_blink

Not working for me.

At the end i adjusted the code again, so it works perfectly for me now, to be honest I don’t know if the flow rate is correct, because it is generally very low with gas.

I’ve added the parameter “inverted” to the pin-configuration and changed how the code keeps track of the impulses.

substitutions:
  # Device informations
  device_friendly_name: "Keller Zähler Gas"
  device_name: "keller-zaehler-gas"
  device_entity_id: "keller_zaehler_gas"
  # Pinout
  gas_read_pin: GPIO5
  # Gas Meter Settings
  gas_imp_value: '10' # impulses / m³ 
  gas_imp_debounce_on: '0.01 s'
  gas_imp_debounce_off: '0.1 s'

globals:
  - id: gas_impulses
    type: int
    restore_value: true # if set to false, the value will be 0 at reboot
    initial_value: '0'

binary_sensor:
  - platform: gpio
    id: gas_impulse
    internal: true
    pin:
      number: $gas_read_pin
      mode: INPUT_PULLUP
      inverted: true
    filters:
      - delayed_off: $gas_imp_debounce_off
    on_press:
      then:
        - lambda: id(gas_impulses) += 1;

sensor:
  - platform: pulse_meter
    name: "${device_friendly_name} Gasverbrauch"
    id: ${device_entity_id}_gasverbrauch
    unit_of_measurement: 'm³/min'
    internal_filter: $gas_imp_debounce_on
    timeout: 1 min
    state_class: measurement
    device_class: gas
    icon: mdi:meter-gas
    #accuracy_decimals: 3
    pin:
      number: $gas_read_pin
      mode: INPUT_PULLUP
      inverted: true
    filters: # 1 imp / 0.1 m³ = 10 imp / m³ (in my case); gas_imp_value = 10;
      - lambda: return x * (1.0 / ${gas_imp_value});
  - platform: template
    name: "${device_friendly_name} Gasverbrauch Total"
    id: ${device_entity_id}_gasverbrauch_total
    unit_of_measurement: 'm³'
    icon: mdi:meter-gas-outline
    state_class: total_increasing
    device_class: gas
    accuracy_decimals: 2
    lambda: return id(gas_impulses) * (1.0 / ${gas_imp_value});
    update_interval: 10 s

1 Like

Hallo Callen!

A little help is appreciated :slight_smile: Where can I set the initial total verbrauch as a starting point.
(In grafana I can group verbrauch in Weekly, monthly, year aspect → it’s clear)

Thanks, Stefan

hey,
you can put an global with an initial value.
I did this with my gas meter for 327,63 m³

globals:Preformatted text
  - id: total_pulses
    type: int
    restore_value: false
    initial_value: '32763'
...
  - platform: template
    name: "total_gas_1"
    device_class: gas
    unit_of_measurement: "m³"
    state_class: total_increasing
    icon: "mdi:fire"
    accuracy_decimals: 2
    lambda: |-
        return id(total_pulses) * 0.01;

Hi, when I try to compile, i get the following error. It doesnt seem to like using the same GPIO for the sensor and the binary sensor. Any ideas

INFO ESPHome 2024.6.4
INFO Reading configuration /config/esphome/esphome-web-9949a4.yaml...
WARNING GPIO5 is a strapping PIN and should only be used for I/O with care.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
WARNING GPIO5 is a strapping PIN and should only be used for I/O with care.
Attaching external pullup/down resistors to strapping pins can cause unexpected failures.
See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins
Failed config

binary_sensor.gpio: [source /config/esphome/esphome-web-9949a4.yaml:69]
  
  Pin 5 is used in multiple places.
  platform: gpio
  id: gas_impulse
  internal: True
  pin: 
    number: 5
    mode: 
      input: True
      pullup: True
      output: False
      open_drain: False
      pulldown: False
    inverted: True
sensor.pulse_meter: [source /config/esphome/esphome-web-9949a4.yaml:83]
  
  Pin 5 is used in multiple places.
  platform: pulse_meter
  name: Gas Meter Home
  id: gas_meter_home
  unit_of_measurement: m³/min
  internal_filter: 10ms
  timeout: 1min
  state_class: measurement
  device_class: gas
  icon: mdi:meter-gas
  pin: 
    number: 5
    mode: 

managed to get the reed switch and calculations working with this yaml code for ESPhome, but for some reason they sensors are not showing up in home assistant?

esphome:
  name: gas-heater
  friendly_name: gas-heater
  min_version: 2024.6.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: '1.0'

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
  platform: esphome


wifi:
  networks:
    - ssid: removed
      password: removed
      priority: 1

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: removed
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:

globals:
  - id: gas_impulses
    type: int
    restore_value: true
    initial_value: '0'

sensor:
  - platform: pulse_meter
    name: "Gas Usage"
    id: "gas_usage"
    unit_of_measurement: 'm³/min'
    internal_filter: '0.01 s'
    timeout: 1 min
    state_class: measurement
    device_class: gas
    icon: mdi:meter-gas
    pin:
      number: GPIO05
      mode: INPUT_PULLUP
      inverted: true
    filters:
      # Since each pulse is 0.01 m³, we multiply the pulse count (x) by 0.01.
      - lambda: return x * 0.01;
    total:
      unit_of_measurement: 'm³'
      name: "Gas Total Consumption"
      id: "gas_total_consumption"
      state_class: total_increasing
      icon: mdi:meter-gas-outline
      accuracy_decimals: 2
      filters:
        # Here we also multiply the total pulse count by 0.01 to get the total volume.
        - multiply: 0.01
    on_value:
      then:
        - lambda: id(gas_impulses) += 1;

In the end, I used this

GitHub - svenwal/gaszaehler-home-assistant-esphome: Adding automated reading of your gas consumption to your Home Assistent setup with minimal hardware setup

Doesnt give you gas flow rate though

thanks svenwal