Умягчитель воды в доме: считаем остаток соли в баке | ESP8266
В загородных домах часто используются умягчители воды, внутри которых находится фильтрующий материал (в моём случае — это ионообменная
смола), и который необходимо периодически промывать раствором
поваренной соли.
Основной процедурой обслуживания умягчителя является — пополнение солевого бака. Сейчас я расскажу о том, как я сделал отображение в Home Assistant примерного остатка соли и настроил уведомление о необходимости пополнения бака.
Мой умягчитель настроен на промывку смолы через каждые 5 кубов потреблённой воды. И 50-ти кг соли, которые я засыпаю в бак, хватает примерно на 5-6 циклов промывки. Поэтому, первым делом, я думал подключиться к контроллеру для подсчета этих циклов, но в нём не нашлось никакой штатной возможности для этого.
Дальше я захотел применить ультразвуковой датчик для измерения расстояния. Но опыт использования во влажной среде показал, что работают они не дольше
нескольких месяцев, после чего выходят из строя из-за коррозии. А бак с солью – это очень даже влажная среда.
Поэтому я остановился на варианте с косвенным подсчетом уровня соли через мониторинг потребления воды. Благо, у счетчика оказался в наличии импульсных выход, и можно вести подсчёт с точностью до 1 литра. Опытным путём я выяснил, что соль нужно досыпать в бак примерно через каждые 30 кубов потреблённой воды.
Идея следующая: когда я открываю крышку для пополнения бака, показания счетчика воды фиксируются, и начинается отсчёт 30-ти кубов, по достижении
которых я получаю уведомление. При следующем открывании крышки цикл повторяется вновь.
Для реализации задуманного я задействовал устройство на базе ESP8266, которое спаял несколько лет назад для подключения двух счётчиков
воды. Ранее на нём была прошивка NodeMCU с написанным мною кодом. Теперь я загрузил в него прошивку ESPHome, которая изначально была создана для тесного взаимодействия с Home Assistant через API.
Схема подключения
Один из проводов счетчика и датчика открытия вместе подключаются к пину Ground. Каждый оставшийся провод подключается к свободному цифровому пину платы, кроме GPIO0 и 2, чтобы не возникла проблема с загрузкой. Также добавляется подтягивающий резистор по схеме. Для дополнительной защиты от дребезга контактов можно добавить конденсатор.
Вообще для упрощения подключения питания и прошивки модуля я бы рекомендовал использовать готовую плату вроде Wemos D1 mini.
Прошивка
ESPHome — это система для настройки модулей ESP8266 и ESP32 с помощью простых и мощных конфигурационных файлов, и удаленного управления ими с помощью систем домашней автоматизации.
Код конфигурации модуля ESP представлен ниже:
esphome:
name: esp-watercounter
platform: ESP8266
board: d1_mini
on_boot:
- logger.log: "Wait for MQTT connected"
- wait_until:
mqtt.connected:
- delay: 3s
- if:
condition:
mqtt.connected:
then:
- globals.set:
id: water_count_var
value: !lambda |-
return id(water_counter).state;
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
manual_ip:
static_ip: 172.16.1.120
subnet: 255.255.255.0
gateway: 172.16.1.1
dns1: 172.16.1.1
logger:
api:
ota:
mqtt:
broker: !secret mqtt_host
username: !secret mqtt_user
password: !secret mqtt_pass
discovery: false
globals:
- id: water_count_var
type: int
restore_value: no
time:
- platform: sntp
on_time:
- seconds: 0
minutes: /5
then:
- if:
condition:
lambda: |-
return id(water_count_var) > 0;
then:
- mqtt.publish:
topic: /pantry/watercounter
retain: true
payload: !lambda |-
return to_string(id(water_count_var));
binary_sensor:
- platform: gpio
name: "Pantry Tank cap"
device_class: window
pin:
number: 5
mode: INPUT_PULLUP
filters:
- delayed_on: 150ms
- delayed_off: 150ms
- platform: gpio
name: "Pantry Water usage"
id: water_usage
internal: true
pin:
number: 4
mode: INPUT_PULLUP
filters:
- delayed_on: 100ms
- delayed_off: 100ms
on_press:
then:
- if:
condition:
lambda: |-
return id(water_count_var) > 0;
then:
- lambda: 'id(water_count_var) += 1;'
sensor:
- platform: mqtt_subscribe
name: "Pantry Water count (L)"
icon: "mdi:water-pump"
id: water_counter
accuracy_decimals: 0
unit_of_measurement: 'L'
topic: /pantry/watercounterВ секции wifi указываем статический IP-адрес, который будет назначен устройству после загрузки в него прошивки, либо позволяем DHCP-серверу назначить IP-адрес автоматом. Главное, не забыть затем зарезервировать его за конкретным устройством, во избежание возможной потери связи с Home Assistant.
Имя точки доступа и пароль к ней записываю в виде секретов в отдельном окне редактора.
Далее следуют параметры подключения к MQTT-брокеру. И основная часть — описание подключенных к модулю датчиков.
Геркон на крышке бака подключен к пятому цифровому пину, device_class: window будет передавать статус Открыто/Закрыто, вместо on/off.
Счётчик воды подключен к 4-му пину, у него также есть защита от дребезга,
а сам компонент помечен как внутренний, т. к. его отображение в Home Assistant не требуется, и его использование не выходит за пределы модуля ESP.
Суть конфигурации сводится к следующему:
- создаётся глобальная переменная, в которую записываются текущие показания счетчика, хранимые в mqtt-брокере.
- для счетчика прописана автоматизация, которая при замыкании контактов
после каждого потреблённого литра воды, увеличивает значение глобальной переменной на этот самый 1 литр, а затем публикует обновлённое значение в mqtt-топике. - дополнительно создан сенсор, задача которого — отображать полученное из mqtt-топика значение показаний счетчика, т. к. этот сенсор уже будет виден в Home Assistant.
После сборки и загрузки прошивки в модуль, его можно добавить в Home Assistant через меню интеграций:
Смотрим, что появились два новых устройства. И можно написать первый
небольшой сценарий, в котором будут фиксироваться текущие показания счётчика воды для дальнейших расчётов:
Проверяем, что крышка открыта, сбрасываем флаг, и считываем текущее значение показаний счетчика, которое публикуем в отдельный MQTT-топик. Сам счётчик добавлен как сенсор в Home Assistant:
- platform: template
sensors:
pantry_water_count:
friendly_name: "Pantry Water count"
unit_of_measurement: 'm³'
icon_template: "mdi:water-pump"
value_template: >-
{% if states('sensor.pantry_water_count_l') in ['unavailable', 'unknown', 'none'] %}
unavailable
{% else %}
{{ states('sensor.pantry_water_count_l')|float / 1000 }}
{% endif %}Для отображения зафиксированных показаний счётчика, при которых была открыта крышка бака, я создал другой сенсор:
- platform: mqtt name: Pantry Tank last refill icon: mdi:basket-fill state_topic: "/pantry/watercounter/salt_refill" unit_of_measurement: "m³"
Непосредственно для расчёта остатка соли создал еще один сенсор на основе шаблона:
- platform: template
sensors:
pantry_tank_salt_left:
friendly_name: "Pantry Tank salt left"
unit_of_measurement: "%"
value_template: >-
{% if 0 < ((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0) <= 100 %}
{{ ((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0) }}
{% elif ((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0) <= 0 %}
0
{% endif %}
icon_template: >-
{% if 60 < (((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0)) < 100 %}
mdi:delete
{% elif 30 < (((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0)) < 60 %}
mdi:delete-outline
{% elif 10 < (((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0)) < 30 %}
mdi:delete-alert-outline
{% elif (((30 - (states('sensor.pantry_water_count')|float - states('sensor.pantry_tank_last_refill')|float))/30*100) | round(0)) < 10 %}
mdi:delete-forever-outline
{% endif %}В строке value_template происходит следующее:
30 — полученный опытным путём примерный объем потреблённой воды, на который хватает одной засыпки соли. Сначала из этого объема вычитается разница между текущим значением показаний счётчика воды (sensor.pantry_water_count) и фиксированным значением (sensor.pantry_tank_last_refill), при котором засыпалась новая порция соли, а затем полученное округлённое значение преобразуется в проценты.
Т.к. в MQTT-топике значения хранятся в виде строк, то в этих местах они преобразуются в числа с плавающей точкой (float).
В icon_template описаны условия для отображения разных иконок в зависимости от текущего уровня соли.
Сценарий для отправки уведомления делает следующее:
Каждые 12 часов проверяется текущее значение уровня соли, которое записывается в отдельную переменную (msg.salt_level), проверяется состояние
флага, который предотвращает повторную отправку уведомлений при следующей проверке, а сам является обычным виртуальным переключателем input_boolean:
pantry_water_tank_refill_flag: name: Pantry Tank refill flag icon: mdi:delete-alert-outline
Следом в Function- ноде происходит проверка условий, что флаг (input_boolean) ещё не был включен, а остаток соли менее 10%, и далее включается флаг и формируется уведомление в Telegram, в текст которого подставляется переменная со значением остатка:
Вот так всё это выглядит в Home Assistant:
Эту систему я тестировал и дорабатывал в течение нескольких месяцев, и сейчас она прекрасно справляется со своей задачей.
Все конфигурации, описанные в статье, доступны на GitHub.