Last active
February 6, 2024 09:48
-
-
Save baudneo/f529bcffa0b876e0c61f5f7189b92af3 to your computer and use it in GitHub Desktop.
Esp8266 and qmc58xxx magnetometer - meter reader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
esphome: | |
name: watermeter-magnet | |
friendly_name: watermeter | |
esp8266: | |
board: nodemcuv2 | |
logger: | |
level: INFO | |
web_server: | |
port: 80 | |
api: | |
encryption: | |
key: "xxxxxxxxxxxxxxxC3lOnN/+JAmQH7ls=" | |
ota: | |
password: "xxxxxxcccc5bb3757ab72da5" | |
wifi: | |
ssid: !secret wifi_ssid | |
password: !secret wifi_password | |
# Enable fallback hotspot (captive portal) in case wifi connection fails | |
ap: | |
ssid: "Watermeter-ESP8266" | |
password: "xxxxxxxxxx5flyu" | |
i2c: | |
sda: D2 | |
scl: D1 | |
scan: False | |
substitutions: | |
friendly_name: "watermeter" | |
switch: | |
- platform: restart | |
name: $friendly_name restart | |
binary_sensor: | |
- platform: status | |
name: "Status" | |
device_class: connectivity | |
icon: mdi:power | |
- name: "Magnet High" | |
platform: template | |
icon: mdi:magnet | |
lambda: |- | |
return id(water_magnet_high); | |
button: | |
- platform: template | |
name: "Reset pulse counter" | |
id: reset_pulse_button | |
on_press: | |
- lambda: id(magnet_rotations) = 0; | |
- platform: template | |
name: "Reset total consumption" | |
id: reset_consumption_button | |
on_press: | |
- lambda: id(estimated_consumption) = 0; | |
- platform: template | |
name: "Toggle Magnet High" | |
id: toggle_mag_high_button | |
on_press: | |
- lambda: |- | |
if (id(water_magnet_high) == true) { | |
id(water_magnet_high) = false; | |
} else if (id(water_magnet_high) == false) { | |
id(water_magnet_high) = true; | |
} else{ | |
id (water_magnet_high) = false; | |
} | |
text_sensor: | |
- platform: wifi_info | |
ip_address: | |
name: "IP Address" | |
icon: mdi:ip | |
mac_address: | |
name: "MAC Address" | |
icon: mdi:network | |
- platform: version | |
name: "Version" | |
icon: mdi:cube-outline | |
interval: | |
# 1ms less than sensor read to make sure it always gets read | |
- interval: 9ms | |
then: | |
- lambda: |- | |
long ts = id(legit_ts); | |
long diff_ = 0; | |
long curr_ = millis(); | |
if (id(mag_z).state >= 25 && !id(water_magnet_high)) { | |
if (ts > 0) { | |
diff_ = curr_ - id(legit_ts); | |
ESP_LOGI("", "Magnet: HIGH >>> LOW to HIGH duration: %d ms", diff_); | |
} | |
id(legit_ts) = curr_; | |
id(water_magnet_high) = true; | |
id(magnet_rotations) += .5; | |
id(rotation_counter) += .5; | |
id(rotation2L_counter) += .5; | |
} else if (id(mag_z).state <= -25) { | |
if (id(water_magnet_high)) { | |
id(water_magnet_high) = false; | |
if (ts > 0) { | |
diff_ = curr_ - id(legit_ts); | |
ESP_LOGI("", "Magnet: LOW >>> HIGH to LOW duration: %d ms", diff_); | |
} | |
id(legit_ts) = curr_; | |
id(magnet_rotations) += .5; | |
id(rotation_counter) += .5; | |
id(rotation2L_counter) += .5; | |
id(estimated_consumption) += id(ml_per_rotation) / 1000; | |
} | |
} | |
sensor: | |
- platform: uptime | |
name: $friendly_name Uptime | |
- platform: qmc5883l | |
address: 0x0D | |
field_strength_z: | |
name: "Field Z" | |
id: mag_z | |
internal: yes | |
range: 200uT | |
oversampling: 64 | |
# lowest ive seena rotation take is 30-ish ms. | |
update_interval: 10ms | |
- name: "Flow L/min" | |
platform: template | |
id: flow_L_min | |
# C -= A is equivalent to C = C - A. | |
lambda: |- | |
int temp = id(rotation2L_counter); | |
id(rotation2L_counter) -= temp; | |
return (temp * id(ml_per_rotation)) / 1000; | |
update_interval: 60s | |
unit_of_measurement: "L/min" | |
- name: "Flow rotations/min" | |
platform: template | |
id: flow_rotate_min | |
lambda: |- | |
int temp = id(rotation_counter); | |
id(rotation_counter) -= temp; | |
return temp; | |
update_interval: 60s | |
unit_of_measurement: "rotations/min" | |
- name: "Estimated Consumed" | |
platform: template | |
id: total_consumed | |
lambda: |- | |
return id(estimated_consumption); | |
update_interval: 1s | |
unit_of_measurement: "L" | |
accuracy_decimals: 3 | |
icon: mdi:water | |
device_class: water | |
state_class: total_increasing | |
# Update the estimated meter reading | |
on_value: | |
then: | |
- lambda: |- | |
if (id(estimated_consumption > 0)) { | |
id(meter_read_estimate) = (id(meter_read_start) + (id(estimated_consumption) / 1000)) + id(meter_offset); | |
} else { | |
id(meter_read_estimate) = id(meter_read_start) + id(meter_offset); | |
} | |
- name: "Magnet Rotations" | |
platform: template | |
id: show_magnet_rotations | |
lambda: |- | |
return id(magnet_rotations); | |
update_interval: 1s | |
unit_of_measurement: "rotation(s)" | |
state_class: total_increasing | |
- name: "Meter Should Read" | |
platform: template | |
id: meter_should_read | |
# meter base + consumed | |
state_class: measurement | |
update_interval: 1s | |
lambda: |- | |
return id(meter_read_estimate); | |
unit_of_measurement: 'm³' | |
accuracy_decimals: 3 | |
device_class: water | |
icon: mdi:water | |
on_value: | |
then: | |
lambda: |- | |
id(meter_read_diff) = id(meter_read_actual) - id(meter_read_estimate); | |
- name: "Meter Read - Difference" | |
platform: template | |
id: meter_should_read_difference | |
update_interval: 1s | |
# meter actual - estimated | |
lambda: |- | |
return id(meter_read_diff); | |
state_class: measurement | |
unit_of_measurement: 'm³' | |
accuracy_decimals: 3 | |
device_class: water | |
icon: mdi:water | |
number: | |
- platform: template | |
name: "Estimate Offset - Input" | |
id: meter_offset_input | |
step: 0.001 | |
min_value: 0 | |
max_value: 100000 | |
set_action: | |
lambda: |- | |
id(meter_offset) = x; | |
id(meter_read_estimate) = id(meter_read_estimate) + x; | |
lambda: |- | |
return id(meter_offset); | |
unit_of_measurement: 'm³' | |
- platform: template | |
# This is what the meter reads when you start recording data to see if estimated = actual. | |
# Read your meter, input the number to this var and esphome will take care of the rest | |
name: "Base Meter Read - Input" | |
id: meter_base_input | |
step: 0.001 | |
min_value: 0 | |
max_value: 100000 | |
set_action: | |
lambda: |- | |
id(meter_read_start) = x; | |
if (id(estimated_consumption > 0)) { | |
id(meter_read_estimate) = (id(meter_read_start) + (id(estimated_consumption) / 1000)) + id(meter_offset); | |
} else { | |
id(meter_read_estimate) = id(meter_read_start) + id(meter_offset); | |
} | |
lambda: |- | |
return id(meter_read_start); | |
unit_of_measurement: 'm³' | |
device_class: water | |
icon: mdi:water | |
# mL per full magnet rotation. | |
- name: "mL per pulse input" | |
platform: template | |
id: m_per_pulse_input | |
step: 0.01 | |
min_value: 0 | |
max_value: 10000 | |
unit_of_measurement: 'mL' | |
device_class: water | |
icon: mdi:water-percent | |
set_action: | |
lambda: |- | |
id(ml_per_rotation) = x; | |
lambda: |- | |
return id(ml_per_rotation); | |
# Every once in awhile enter what the meter reads so esphome can report | |
# The difference between estimated vs actual. This helps when calibrating | |
# what 1 full magnet rotation consumes. | |
- platform: template | |
name: "meter actual input" | |
step: 0.001 | |
min_value: 0 | |
max_value: 100000 | |
set_action: | |
lambda: |- | |
id(meter_read_actual) = x; | |
id(meter_read_diff) = id(meter_read_actual) - id(meter_read_estimate); | |
lambda: |- | |
return id(meter_read_actual); | |
unit_of_measurement: 'm³' | |
device_class: water | |
icon: mdi:water | |
# 75.7082 | |
# Maximum liters per minute flow | |
- platform: template | |
name: "meter max flow input" | |
step: 0.001 | |
min_value: 0 | |
max_value: 1000000 | |
set_action: | |
lambda: |- | |
id(meter_flow_max) = x; | |
lambda: |- | |
return id(meter_flow_max); | |
unit_of_measurement: 'L/min' | |
device_class: water | |
icon: mdi:water | |
globals: | |
- id: meter_flow_max | |
type: float | |
restore_value: yes | |
initial_value: "0.000" | |
- id: meter_read_actual | |
type: float | |
restore_value: yes | |
initial_value: '0.0000' | |
- id: meter_read_diff | |
type: float | |
restore_value: yes | |
initial_value: '0.0000' | |
- id: estimated_consumption | |
type: float | |
restore_value: yes | |
initial_value: '0.000' | |
- id: magnet_rotations | |
type: float | |
restore_value: yes | |
initial_value: '0' | |
# rotations to L per min | |
- id: rotation2L_counter | |
type: float | |
restore_value: no | |
initial_value: '0' | |
# rotations per min | |
- id: rotation_counter | |
type: float | |
restore_value: no | |
initial_value: '0' | |
- id: water_magnet_high | |
type: bool | |
restore_value: yes | |
initial_value: 'false' | |
- id: ml_per_rotation | |
type: float | |
restore_value: yes | |
initial_value: "65.71" | |
- id: meter_read_start | |
type: float | |
restore_value: yes | |
initial_value: "0.0000" | |
- id: legit_ts | |
type: long | |
restore_value: no | |
initial_value: "0" | |
- id: meter_read_estimate | |
type: float | |
restore_value: no | |
initial_value: '0.0000' | |
- id: rotation_time_ms | |
type: long | |
restore_value: yes | |
initial_value: "0" | |
- id: meter_offset | |
type: float | |
restore_value: yes | |
initial_value: "0" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment