UPDATE ON ORIGINAL POST
After having a bit of a play about over the Christmas holidays and thinking about the best way to get this working I changed the YAML code from that previously posted, or more accurately, changed the C++ code in the Lambda function within the YAML code
I’ve now had this running for over 48 hours and it seems to be logging the needle as it passes under the IR sensor quite accurately now, as can be seen from the screenshots below.
IR Sensor ADC Values
This screen grab shows the values being logged from the ESP32’s ADC using the analog output from the IR sensor. The red vertical sketch line is the last time the ESP32 was reset. The spikes on the graph are the needle passing below the IR sensors emitter and receiver. For clarity, the narrow spikes are when the burner is turned on (needle rotating fast) and the wider spikes are when the burner is off and just the pilot light running (needle running slowly). The darker blue numbers scribbled near the spikes are the number of rotations of the meter dial which equate to 1 ft
3 of gas since the last reset (red vertical line). This screenshot shows 11 ft
3 used since the last reset of the ESP32.
Actual Logged Consumption
This is a screen grab from HA itself, and the gas consumption figure correlates to the spikes shown on the previous graph, 11 ft3
YAML Code
name: gas-meter-monitor
esp32:
board: nodemcu-32s
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "HNNVUMR69RNNJBK9q+LCtVwenVKZpL1en2+jzl0VaHE="
ota:
password: "a306ebc5fe5e8fb5c286227999570932"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Gas-Meter-Monitor"
password: "kCTrfHhD3FcG"
captive_portal:
globals:
- id: total_pulses
type: float
restore_value: false
initial_value: '0.0'
- id: max_pulse_value
type: float
restore_value: false
initial_value: '0.1730'
# This sensor exposes the 'total_pulses' variable to Home Assistant and
# tells HA it is to ft³
sensor:
- platform: template
name: "Gas used"
device_class: gas
unit_of_measurement: "ft³"
state_class: "total_increasing"
icon: "mdi:fire"
accuracy_decimals: 0
lambda: |-
return id(total_pulses);
# This is the reading of the voltage on the analog input pin Take a measurement every 50ms but only report the average
# every 2 seconds. This is to overcome the noisy ADC on the ESP32, and also to see if the sensor is 'seeing' the rotating needle
- platform: adc
id: adc_value
pin: GPIO32
accuracy_decimals: 4
name: "Gas Meter Sensor Analog Value"
update_interval: 50ms
attenuation: 11dB
filters:
- throttle_average: 2sec
# When a new voltage measurement is received, calculate the
# difference with the previous voltage. If it's larger than
# a certain threshold, increase the pulse counter and also reset the maximum value
# to the current value for the next needle rotation
on_value:
then:
- lambda: |-
float current_pulse_value = id(adc_value).state;
float last_pulse_value = current_pulse_value;
float diff = id(max_pulse_value) - current_pulse_value;
if(current_pulse_value > id(max_pulse_value)){
id(max_pulse_value) = current_pulse_value;
}
if(diff > 0.008){
id(total_pulses) += 1;
}
if(diff > 0.008){
id(max_pulse_value) = current_pulse_value;
}
# Uptime sensor
- platform: uptime
name: Gas Meter Uptime
# WiFi Signal sensor
- platform: wifi_signal
name: Gas Meter WiFi Signal
update_interval: 60s
# Switch to restart
switch:
- platform: restart
name: gas meter monitor restart
Brief Explanation of How It Works
When there is no needle under the sensor the ESP 32 reports back a pretty steady voltage level. The ADC is quite noisy but the voltage is read every 50ms and averaged before reporting this every two seconds - smooths it out a bit.
As the needle starts to pass under the IR sensor, the voltage output on the sensor rises and reaches a maximum (in my case) of 0.183 V and after this peak starts to fall back to around 0.172 to 0.174 V - these are the peaks and troughs on the above graph.
As the needle starts to pass under the sensor, the ESP32 checks the current value of the IR sensor and compares it to the max_pulse_value of the IR sensor since the last rotation, and if it is higher, it will log that newer value as max_pulse_value. This means that we can obtain a maximum value from the IR sensor as the needle rotates under it, and we then know that the needle has passed under the sensor.
There is a variable named ‘diff’ which is the difference between the max_pulse_value and the current_pulse_value - this number is changing all the time as the needle rotates and passes under the IR sensor; it will increase and decrease in size as the needle rotates.
What we want to know is this “When has the needle definitely passed the IR sensor”. (and also when it has passed the IR Sensor, then increment the gas consumption figure by 1 ft3). This is done in the Lambda function which is C++ code, not YAML. The IF statements within the Lambda function are used to determine the difference in voltage between high and steady state, that ‘difference’ being 0.008V in my case. If this difference is exceeded, then the needle has passed the IR head as the difference between the peak sensor value and the lower ‘steady state’ value is at it’s maximum level. When this large difference occurs, the total_consumption variable is increased by 1, and max_pulse_value variable is reset to the current level, to enable the next rotation of the dial to be ascertained - and the process starts again.
I hope the above explanation is clear. If anybody wants to use this method the ‘0.008’ figure within the Lambda function will need to be changed to suit your own purposes. It would be best to set up a dashboard where you can monitor the value of the ESP32’s ADC value from the sensor, or you could sit and watch the ESPHome Log scroll past to monitor voltages/ADC values. You will need to find the maximum values and “steady-state” values of your own installation. When the dial passes under the IR Sensor this will provide the maximum value, and when the dial is away from the sensor (180 degrees opposite, it will be the lowest value). It is these ‘max’ & ‘current’ values which determine the ‘diff’ threshold in the Lambda function - and thus the success of the total_consumption variable being increased every time the needle passes the IR Sensor.
I do hope this helps somebody, and please do feel free to respond if you feel I can be of any help at all.
Thanks to all who contributed, and offered suggestions. Your input was much appreciated and valued.
Cheers,
Mike.