Problems with gas meter reed esp32

Hello everyone,

I need your help, as I’ve been searching various forums for several days and simply can’t find a solution.

I have mounted a reed switch with external pull-up on my gas meter using an ESP32. When gas is consumed, a 3.1-second pulse is generated approximately every 14.5 seconds.

This pulse is also visualized with an LED over GPIO33.

The current meter reading is also calculated.

Unfortunately, a few pulses are not counted, so the calculated meter reading deviates downward from the actual meter reading on the gas meter.

Here is my yaml file:

esphome:
  name: logomatic
  friendly_name: Logomatic

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "1234..."

ota:
  - platform: esphome
    password: "1234..."

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Logomatic Fallback Hotspot"
    password: "1234..."

captive_portal:

uart:
  id: uart_bus
  tx_pin: GPIO2
  rx_pin: GPIO4
  baud_rate: 2400

external_components:
  - source: github://the78mole/esphome_components@main
    components: [ km271_wifi ]
      
km271_wifi:
  - id: budoil
    uart_id: uart_bus

switch:
# -------------------------------
# LED für Reedkontakt Impulse
# -------------------------------
  - platform: gpio
    id: peak_output
    name: "Peak Output GPIO33"
    pin:
      number: GPIO33
      mode: OUTPUT
    restore_mode: ALWAYS_OFF
# -------------------------------

binary_sensor:
  - platform: gpio
    id: gas_impuls
    name: "Gas Impuls"
    pin:
      number: GPIO34
      mode: INPUT   # externes Pull-Up
      inverted: True     # logisch invertieren
    filters:
      - delayed_on: 20ms
      - delayed_off: 20ms

    on_press:
      then:

        - lambda: |-
            id(gas_total_m3).publish_state(id(gas_total_m3).state + 0.01);    

        # GPIO33 LOW
        - switch.turn_on: peak_output

    on_release:
      then:
        # GPIO33 HIGH
        - switch.turn_off: peak_output

  - platform: template
    id: gas_peak_active
    name: "Gas Peak aktiv"
    device_class: running
    lambda: |-
      // GPIO33 ist LOW während Peak → invertieren
      return id(peak_output).state;
   
number:
# -------------------------------
# Persistenter Gesamtzähler
# -------------------------------
  - platform: template
    id: gas_total_m3
    name: "Gaszähler Gesamt"
    min_value: 0
    max_value: 100000
    step: 0.01
    restore_value: true
    optimistic: true
    initial_value: 24179.87  # optional Startwert
# -------------------------------    
  
sensor:
# -------------------------------
# Gesamtverbrauch Sensor (Energy Dashboard kompatibel)
# -------------------------------
  - platform: template
    name: "Gaszähler Gesamt"
    unit_of_measurement: "m³"
    icon: "mdi:fire"
    device_class: gas
    state_class: total_increasing
    accuracy_decimals: 2
    lambda: |-
      return id(gas_total_m3).state;

  # -------------------------------
  # Aktueller Verbrauch m³/h
  # -------------------------------
  - platform: template
    name: "Gasverbrauch m³/h"
    unit_of_measurement: "m³/h"
    icon: "mdi:fire"
    accuracy_decimals: 2
    update_interval: 60s
    lambda: |-
      static float last_value = 0;
      static unsigned long last_time = millis();
      float current_value = id(gas_total_m3).state;
      unsigned long current_time = millis();
      float delta_m3 = current_value - last_value;
      float delta_hours = (current_time - last_time) / 3600000.0; // ms → h
      last_value = current_value;
      last_time = current_time;
      if (delta_hours > 0) {
        return delta_m3 / delta_hours;
      } else {
        return 0.0;
      }
  # -------------------------------

Does anyone have any ideas on what I could optimize?

What does it mean? That randomly sometimes pulse is not counted? Is your only evidence for that the fact that it doesn’t match to the meter reading?

Anyway, template number is not best approach for this because it can be out of synch with HA and HA can update it. Use Globals instead.

globals:
  - id: gas_pulses
    type: uint32_t
    restore_value: true
    initial_value: '0'

binary_sensor:
  - platform: gpio
    ....
    on_press:
      then:
        - lambda: id(gas_pulses)++;

And use a template sensor to expose the counter on HA (and to convert the units).

Not sure what you are following or why it doesn’t work, but you might want to look here.

spoiler alert, this is the solution to the long thread

This is a long thread, but worth reading

@Karosm:
Yes, my only evidence that some pulses were not counted was based solely on the fact that there was a discrepancy between the actual hardware meter reading and the meter reading in HA.
Following your comment regarding synchronization problems with HA and the template number, I took the trouble to count the pulses recorded by HA using a history chart and manually calculated the meter reading.
This matched the value actually read on the gas meter. However, the meter reading calculated in HA was missing two pulses.
Therefore, I will try to implement your approach tomorrow.

@neel-m:
Thank you for your reply as well.
I will try Karosm’s approach first and then work through the post you quoted.
I will let you know how it goes!

My gas meter has a switch/pulser in it too. I started with that. The standard pulse counters in both esphome and Tasmota did not do so well with it. I ended up writing some custom code to check every second or so and use that to generate counts, which worked better.

I started using the magnetometer method a few months ago and discovered it wasn’t working too well. I also discovered that the pulsar seems to be messed up somehow.

My meter doesn’t have a strong signal for the magnetometer and the signal drifts with temperature so it was impossible to get values that worked across the temperature range of a day. I was going to write some adaptive code to deal with it, but @tronikos already did it. So far it seems to be working well.

Interestingly, my pulse counter registers false counts (with nothing attached, other than the wires to the switch) once or twice a day. Since the signal changes slowly, I am not convinced that interrupts are the best way to handle this. I think an optocoupler might be more noise tolerant.

@neel-m:
Yes, I had already considered an optocoupler, but I was put off by the installation effort.
The magnet in my gas meter seems to be strong enough. At least the connected indicator LED always lights up clearly for a fairly constant period of time when passing through zero.

@Karosm:
I actually implemented your approach last night. And lo and behold, this morning the meter reading is exactly the same.
I hope this wasn’t just a fluke and will observe it for a few more days before continuing with my further evaluation of costs, dashboard, etc.

Nevertheless, many thanks to both of you for your efforts!

Do that, but I’m sure esphome doesn’t miss 3s pulses on 15s cycle.