Gas meter reading with magnetometer HMC5883L GY-271 (Bonus: PoE powered)

Making my power and gas meters smart, I tried to read my gas meter (BK-G4) with a reed contact.
I wasn’t really happy with this approach with the reed sensor sometimes bouncing and I’m not experienced enough to build a hardware debouncer.
Another question I had is what happens when the gas meter stays for a couple of time with the magnet near the reed contact (no more warm water needed, counter stays at “0” - will the reed really read once or will it read more often?

So I decided to go with a magnetometer. I used the GY-271 which ships for around 3 €/$, maybe even cheaper from Ali.

After mounting (I used a 3d printed case fittet for relays) I first looked at the history graph on all 3 axis.



I decided to go with the X axis and set a global variable “high” to true whenever it reads more than 50 microtesla (look at the graphs) and to false whenever it goes below zero.
I increment the gas counter by 0.01 on every spike (gas meter says so, since the magnet is in the third decimal place).
I added another sensor to calculate from m³ to kWh (depends on your actual place and situation, you have to look up these variables in your gas invoices), and another sensor to show the gas flow in the last minute.

So far it works extremely reliable.

Bonus:
Since my gas meter is far away I used the olimex PoE board and used GPIO 2 and 3.
Another side note: The GY-271 is connected to the ESP with a 6 meter unshielded four pole cable which also works great.
Last side note: The ESP also reads out my power meter with OBIS protocol on the RX/TX with a 15 meter CAT5 cable which also works perfect.

esphome:
  name: olimex
  platform: ESP32
  board: esp32-poe

logger:
#  baud_rate: 0
#  hardware_uart: UART2
  level: DEBUG

api:

ota:

substitutions:
  devicename: "energiezaehler"
  upper_devicename: "Energiezaehler"
  friendly_name: "Energiezaehler"

i2c:
  sda: 2
  scl: 3
  scan: true
  id: bus_a

globals:
   - id: gas_counter_total
     type: float
     restore_value: yes
     initial_value: '0'
   - id: gas_counter
     type: long
     restore_value: no
     initial_value: '0'
   - id: gas_high
     type: bool
     restore_value: no
     initial_value: 'false'

interval:
  - interval: 5000ms
    then:
    - lambda: |-
       if (id(hmc5883lx).state >= 50 && !id(gas_high)) {
          id(gas_counter_total) += 0.01;
          id(gas_counter) += 1;	
          id(gas_high) = true;
        } else if (id(hmc5883lx).state <= -2 && id(gas_high)) {
          id(gas_high) = false;
        }   

ethernet:
  type: LAN8720
  mdc_pin: GPIO23
  mdio_pin: GPIO18
  clk_mode: GPIO17_OUT
  phy_addr: 0
  power_pin: GPIO12

time:
  - platform: homeassistant
    id: homeassistant_time

web_server:
  port: 80

binary_sensor:
  - platform: status
    name: "${friendly_name} - Status"
    device_class: connectivity

uart:
# (obis reader, not topic here)

sensor:
  - platform: hmc5883l
#    address: 0x68
    field_strength_x:
      name: "${friendly_name} HMC5883L X"
      id: hmc5883lx
    field_strength_y:
      name: "${friendly_name} HMC5883L Y"
      id: hmc5883ly
    field_strength_z:
      name: "${friendly_name} HMC5883L Z"
      id: hmc5883lz
    range: 250uT
    oversampling: 8
    update_interval: 1s
    heading:
      name: "${friendly_name} HMC5883L Heading"


  - platform: template
    name: "${friendly_name} Gas Durchfluss"
    lambda: |-
      int temp = id(gas_counter);
      id(gas_counter) -= temp;
      return temp;
    update_interval: 60s
    unit_of_measurement: "m³/min"

  - platform: template
    name: "${friendly_name} Gas Gesamt"
    lambda: |-
      return id(gas_counter_total);
    update_interval: 5s
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    icon: 'mdi:fire'
    device_class: gas
    state_class: total_increasing

  - platform: template
    name: "${friendly_name} Gas kWh"
# Gasvolumen in m³ x Zustandszahl x Brennwert = Gasverbrauch in kWh
# Zustandszahl: 0.8934
# Brennwert: 11.274
    filters:
    lambda: |-
      return (id(gas_counter_total) * (10.0721916));
    update_interval: 5s
    unit_of_measurement: "kWh"
    accuracy_decimals: 2
    icon: 'mdi:fire'
    device_class: energy
    state_class: total_increasing


text_sensor:
  - platform: version
    name: "${friendly_name} - Version"
    icon: mdi:cube-outline

Thanks to @haufes Water / Gas Meter Monitoring Via Magnetometer - Sine Wave to Pulse Issue - #6 by haufes

2 Likes

Hallo @cg089 cg09, i tried your yaml and have some questions:

interval:
  - interval: 5000ms
    then:
    - lambda: |-
       if (id(hmc5883lx).state >= 50 && !id(gas_high)) {
          id(gas_counter_total) += 0.01;
          id(gas_counter) += 1;	
          id(gas_high) = true;
        } else if (id(hmc5883lx).state <= -2 && id(gas_high)) {
          id(gas_high) = false;
        }   

why is id(gas_counter) += 1; not 0.01?

- platform: template
    name: "${friendly_name} Gas Durchfluss"
    lambda: |-
      int temp = id(gas_counter);
      id(gas_counter) -= temp;
      return temp;

this is always 0, doesnt it? Its like gas_counter - gas_counter.

I am new to esp. Can esp pulse counter be used with this sensor to count pulses?

For all having problems with the magnetometer/compass HMC5883L GY-271:
There are 2 different sensors, HMC5883L and QMC5883L.
If its named GY-271 and looks like HMC5883L that doesnt mean you have a HMC5883L!
If you get an error about could not read anything, look to your esp log.

[i2c.arduino:053]: Results from i2c bus scan:
[i2c.arduino:059]: Found i2c device at address 0x0D

if a device is found at address 0x0D, you actually have the QMC5883L and must use

  - platform: qmc5883l
    address: 0x0D
    oversampling: 64x      # 512x (default), 256x, 128x, 64x
    range: 800uT           # 200 uT, 800 uT

It would be good if HMC5883L Magnetometer — ESPHome had a warning, info or link to the QMC5883L Magnetometer — ESPHome.

3 Likes