Water / Gas Meter Monitoring Via Magnetometer - Sine Wave to Pulse Issue

Hi there,

I’m trying to integrate my home water and gas usage into HA. I bought a cheap water flow meter with built in hall sensor from ali, but it wasn’t accurate at low flow rates. The home’s water utility meter is very accurate. For $10 an easp32 and a QMC5883L Magnetometer can sufficiently read magnetic changes inside the meters!

I’m just struggling to convert the sine wave reported over i2c into pulse count data. I have tried using lambda as a trigger when sensor value reaches a high point in the curve, but i’m lost in then feeding that to a pulse counter.

sensor:
  - platform: qmc5883l
    address: 0x0D
    field_strength_y:
      name: "QMC5883L Field Strength Y"
      id: "field_strength_y"
    range: 200uT
    oversampling: 64x
    update_interval: 1s
    
binary_sensor:
  - platform: template
    id: "ytrigger"
    lambda: |-
      if (id(field_strength_y).state > 70) {
        return true;
      } else {
        return false;
      }

I thought about trying to output the signal, using a GPIO to another pin on the board setup as a pulse counter, but it seems like there must be an easier way i’m missing.

Any help or direction is appreciated.

2 Likes

Hey did you find a solution?

I wouldn’t think you would be able to feed into a pulse counter from a sensor, the pulse counters are hardware in the chip that count pulses at the pin.

I would think you would need to use a custom sensor that would count the pulse internally.

I’m looking to do the same thing, but had hoped that someone has already done the work. :slight_smile:

I havn’t spent much time on it. I did find a solution by using mysensors that I’ll probably use eventually. I’m sure ESPhome can provide the same functionality, but I lack the understanding to make it work right now.

https://forum.mysensors.org/topic/6125/gas-meter-reading-using-a-magnetometer

How about a hardware hack?

Every time your binary sensor turns on, turn on a GPIO output (using on_press and on_release). Short this to another GPIO input that you can feed to your pulse counter.

2 Likes

I will try this out this week. My current current setup does measure the field strength peaks accurately. I just need this counter solution to make is useable. Thanks Tom.

So I have something that is mostly working and if your cycles are as long as they look it may work for you. It’s not for me because the qmc5883l is only sampling once a second and my meter spins faster than that.


globals:
   - id: water_counter_total
     type: long
     restore_value: no
     initial_value: '0'
   - id: water_counter
     type: long
     restore_value: no
     initial_value: '0'
   - id: water_high
     type: bool
     restore_value: no
     initial_value: 'false'

interval:
  - interval: 5000ms
    then:
    - lambda: |-
       if (id(qmc5883lx).state >= 7 && !id(water_high)) {
          id(water_counter_total) += 1;
          id(water_counter) += 1;	
          id(water_high) = true;
        } else if (id(qmc5883lx).state <= -2 && id(water_high)) {
          id(water_high) = false;
        }   

sensor:
  - platform: qmc5883l
    address: 0x0D
    field_strength_x:
      name: "QMC5883L Field Strength X"
      id: qmc5883lx
    range: 200uT
    oversampling: 64
    update_interval: 1s

  - platform: template    
    name: "Flow Sensor"
    lambda: |-
      int temp = id(water_counter);
      id(water_counter) -= temp;
      return temp;
    update_interval: 60s

  - platform: template    
    name: "Total Sensor"
    lambda: |-
      return id(water_counter_total);
    update_interval: 5s
2 Likes

Thanks for this. I am able to monitor my water at my water softener turbine sensor using your code with some modifications. The turbine can spin about 30rps so setting the interval to 5ms works perfectly.

esphome:
  name: qmc5883l
  platform: ESP8266
  board: d1_mini


wifi:
  ssid: "XXXXXXXX"
  password: "XXXXXXXXXX"

logger:
  level: INFO
  logs:
    mqtt.component: INFO
    mqtt.client: ERROR

ota:
  password: "XXXXXXXX"

mqtt:
  broker: mqtt.lan
  username: XXXXXXXX
  password: XXXXXXXX

web_server:
  port: 80

i2c:
  sda: D2
  scl: D1

globals:
  - id: water_counter_total
    type: long
    restore_value: yes
    initial_value: '0'
  - id: water_counter
    type: long
    restore_value: no
    initial_value: '0'
  - id: water_increasing
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: water_last_value
    type: long
    restore_value: no
    initial_value: '0'
  - id: water_change
    type: long
    restore_value: no
    initial_value: '0'    

interval:
  - interval: 5ms
    then:
    - lambda: |-
       id(water_change) = id(qmc5883lx).state - id(water_last_value);
       if (abs(id(water_change)) > 0.0) {
        if (id(water_change) > 0.0 && !id(water_increasing)) {
            id(water_increasing) = true;
            id(water_counter_total) += 1;
            id(water_counter) += 1;	
          } else if (id(water_change) < 0.0 && id(water_increasing)) {
            id(water_increasing) = false;
          } 
        id(water_last_value) = id(qmc5883lx).state;
        }

sensor:
  - platform: qmc5883l
    address: 0x0D
    range: 200uT
    oversampling: 512x
    update_interval: 5ms
    heading:
      id: qmc5883lh
      filters:
        delta: 1.0
    field_strength_x:
      id: qmc5883lx
      filters:
        delta: 1.0
    field_strength_y:
      id: qmc5883ly
      filters:
        delta: 1.0
    field_strength_z:
      id: qmc5883lz
      filters:
        delta: 1.0

  - platform: template    
    name: "Flow Sensor"
    lambda: |-
      int temp = id(water_counter);
      id(water_counter) -= temp;
      return temp;
    update_interval: 10s

  - platform: template    
    name: "Total Sensor"
    lambda: |-
      return id(water_counter_total);
    update_interval: 10s
1 Like

Keep track of the max and min values ever reported, so in a matter of few cycles you’ll know the range of the sensor.

Then compare each reading with the previous one, and if it’s increasing, once it crosses the mid point you count one tick. Every 30 seconds you publish the daily count (see the daily energy example).

If you need more help than this you can try with chatgpt, it gave me a basically complete sketch with a request similar to what I just told you.

You know, you can learn from the sketches produced from chatgpt.

At least I did.

I recommend using GitHub - tronikos/esphome-magnetometer-water-gas-meter: Using ESP8266 or ESP32 and QMC5883L, a triple-axis magnetometer, to read your water meter or gas meter that supports easy calibration. You just need to press a button and the proper axis and thresholds will be picked.

1 Like

Thanks for sharing this project. Got water meter sensor going and in process of getting gas meter sensor up and running. Still too early to determine accuracy but water seems fairly close at least.

Glad you found it useful. If you want you can use the same ESP32 (recommended) or ESP8266 for both meters. Instead of including esphome-water-meter.yaml, include esphome-two-meters.yaml. See the file for the available substitutions.

1 Like

Thanks @tronikos. Amazing project and your instructions are detailed. I followed along and set this up on first try. I was wondering why my calculations were reported in gallons then I read more and figured I had to change the “Volume” to litres. Now that I have water fairly close to accurate (off by 1.7L). Going to start with the natural gas meter. Hopefully you have the time to do one on electricity monitoring :slight_smile: Then I will everything monitored

That means you successfully get triggers on your template sensor?
If so, you could use a template number and number.increment action on your sensor code.