Умягчитель воды в доме: считаем остаток соли в баке | 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.