Давно мучал вопрос передачи показаний давления системы отопления со штатного манометра газового котла. Для этого несколько лет назад была приобретена камера ESP32-CAM и интегрирована в Home Assistant посредством ESPHome.

Камеру я направил прямо на манометр, что позволило мне периодически вручную удаленно контролировать давление в системе отопления, и, при необходимости, открывать кран подачи воды в систему (также удалённо).

Штатный светодиод ESP32-CAM помогает разглядеть манометр в закрытом шкафу
Штатный светодиод ESP32-CAM помогает разглядеть манометр в закрытом шкафу

Для того, чтобы хоть как-то облагородить этот "аналоговый" процесс в Node-RED была создана автоматизация, завязанная на контроле значений энергопотребления котла и насосов:

Работало это так:

  1. В Телеграм приходит уведомление об ошибке котла;

  2. Я лезу в интерфейс HA и в камере смотрю какое давление;

  3. Если давление низкое - открываю кран подпитки.

Уже неплохо, но руки-то чешутся, поэтому я решил, что изображение надо распознать и вывести давление в HA в нормальном виде.

Выбор способа оцифровки

Помимо Raspberry Pi 4B, на котором у меня крутится HA у меня есть самосборный NAS c Xpenology на борту. Поэтому я решил задействовать в этом деле его.

Установка контейнера Python в Xpenology

Первым делом в Container Manager загружаем образ Python 3-10-slim

Создание контейнера:

  1. Открываем вкладку Образы, выбираем python и нажимаем Запустить;

  2. Как-нибудь называем контейнер;

  3. На этапе Настройка томов:

    • Нажимаем Добавить папку

    • Выбираем папку /volume1/docker/gauge

    • В контейнере указываем путь: /app

  4. Нажимаем Далее, потом Применить

Получаем такой результат
Получаем такой результат

Далее временно включаем доступ к NAS по SSH в панели управления:

И подключаемся к с серверу с помощью Putty

Вводим логин, пароль, переключаемся на root, получаем ID контейнера:

docker ps

Заходим в контейнер:

docker exec -it ID_контейнера bash

Устанавливаем необходимые библиотеки:

apt update && apt install -y python3-pip libgl1 libglib2.0-0
apt-get install -y python3-pip python3-dev
apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev
apt-get install -y qt5-qmake qtbase5-dev-tools qtchooser
apt-get install -y libx11-dev libgl1-mesa-glx libfontconfig1 libxkbcommon0
apt-get install -y libxcb-xinerama0
pip install opencv-python numpy
pip3 install opencv-python matplotlib

Создаем скрипт gauge_reader.py и кладем его в папку docker\gauge на NAS. К нему мы вернемся чуть позже.

Настройка Home Assistant

Теперь мне нужно положить в эту же папку, где лежит скрипт само изображение, которое нужно распознать. Для этого делаем следующее:

  1. В HA переходим в Настройки, Хранилище, Добавить сетевое хранилище, указываем путь к папке docker на NAS:

После создаем автоматизацию, которая будет каждый час включать светодиод на ESP32-CAM, делать снимок, выкладывать его в папку gauge, которую мы подключили шагом выше, и выключать светодиод:

alias: ESP Cam snapshot каждый час
description: ""
triggers:
  - minutes: 0
    trigger: time_pattern
actions:
  - target:
      entity_id: switch.pressure_boiler_flash_led
    action: switch.turn_on
    data: {}
  - delay:
      hours: 0
      minutes: 0
      seconds: 10
      milliseconds: 0
  - data:
      entity_id: camera.pressure_boiler_my_camera
      filename: /media/gauge/sample.jpg
    action: camera.snapshot
  - delay:
      hours: 0
      minutes: 0
      seconds: 10
      milliseconds: 0
  - target:
      entity_id: switch.pressure_boiler_flash_led
    action: switch.turn_off
    data: {}
mode: single

И сразу создадим сенсор, в который будет присылать NAS результат распознавания давления:

sensor:
    - name: "Boiler Pressure"
      state_topic: "sensors/boiler/pressure"
      unit_of_measurement: "bar"
      device_class: pressure
      unique_id: pressure_boiler

Подготовка фото

Теперь надо немного поколдовать с самим объектом - манометром. Спустя пару десятков итераций со скриптом я пришел к тому, что несмотря на все ухищрения, качество изображения не позволяет со 100% результатом распознать стрелку манометра. Поэтому я наклеил вокруг манометра квадрат для возможности правильной обрезки и коррекции перспективы. Получилось так:

ESP32-CAM в лего-кронштейне. Над манометром временно прилепил белый лист бумаги, т.к. рельеф корпуса котла иногда вносил небольшую неразбериху в определении угла квадрата.
ESP32-CAM в лего-кронштейне. Над манометром временно прилепил белый лист бумаги, т.к. рельеф корпуса котла иногда вносил небольшую неразбериху в определении угла квадрата.

Написание скрипта (хвала нейросетям!)

Теперь переходим к скрипту. Задачу по определению давления разбиваем на следующие шаги:

  1. Обнаруживаем на фото четырехугольник;

  2. Корректируем перспективу - превращаем четырехугольник в квадрат и обрезаем изображение;

  3. Определяем центр и стрелку манометра;

  4. Замеряем угол отклонения стрелки и по таблице калибровки переводим угол-градусы в давление-бары;

  5. Отрисовываем некоторые этапы для контроля выполнения кода скрипта;

  6. Отправляем полученные данные в mqtt HA.

import cv2
import numpy as np
import math
import logging
import paho.mqtt.client as mqtt
from typing import Optional, Tuple, List

# Настройка логгирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Конфигурация
class Config:
    MQTT_BROKER = "192.168.1.****"
    MQTT_PORT = ****
    MQTT_TOPIC = "sensors/boiler/pressure"
    MQTT_USER = "****"
    MQTT_PASS = "****"
    MQTT_TIMEOUT = 5
    
    CALIBRATION = {
        215: 0.0,
        155: 1.0,
        90: 2.0,
        29: 3.0,
        327: 4.0
    }
    
    OUTPUT_DIR = '/app/'
    OUTPUT_SIZE = 600
    BLUR_SIZE = (5, 5)
    ADAPTIVE_THRESH_BLOCK = 11
    ADAPTIVE_THRESH_C = 2
    HOUGH_CIRCLES_PARAMS = {
        'dp': 1,
        'minDist': 100,
        'param1': 50,
        'param2': 30,
        'minRadius': 100,
        'maxRadius': 200
    }
    HOUGH_LINES_PARAMS = {
        'rho': 1,
        'theta': np.pi/180,
        'threshold': 50,
        'minLineLength': 150,
        'maxLineGap': 10
    }

def detect_quadrilateral(image: np.ndarray) -> Tuple[Optional[List[Tuple[int, int]]], np.ndarray]:
    """Определение углов четырехугольника вокруг манометра"""
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY_INV)
    
    kernel = np.ones((5,5), np.uint8)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    if not contours:
        return None, image.copy()
    
    largest_contour = max(contours, key=cv2.contourArea)
    epsilon = 0.03 * cv2.arcLength(largest_contour, True)
    approx = cv2.approxPolyDP(largest_contour, epsilon, True)
    
    if len(approx) != 4:
        for eps in [0.02, 0.04, 0.05]:
            epsilon = eps * cv2.arcLength(largest_contour, True)
            approx = cv2.approxPolyDP(largest_contour, epsilon, True)
            if len(approx) == 4:
                break
        else:
            return None, image.copy()
    
    points = np.array([point[0] for point in approx], dtype=np.float32)
    center = np.mean(points, axis=0)
    
    def sort_key(point):
        return math.atan2(point[1] - center[1], point[0] - center[0])
    
    sorted_points = sorted(points, key=sort_key, reverse=True)
    bottom_idx = np.argmax([p[1] for p in sorted_points])
    sorted_points = np.roll(sorted_points, -bottom_idx, axis=0)
    
    if bottom_idx == 1:
        sorted_points = np.roll(sorted_points, -1, axis=0)
    
    corners = [tuple(map(int, p)) for p in sorted_points]
    
    vis = image.copy()
    for i, corner in enumerate(corners):
        cv2.circle(vis, corner, 10, (0, 255, 0), -1)
        cv2.putText(vis, f"{i+1}", (corner[0]+15, corner[1]+15), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    
    for i in range(4):
        cv2.line(vis, corners[i], corners[(i+1)%4], (255, 0, 0), 2)
    
    return corners, vis

def correct_perspective(image: np.ndarray, corners: List[Tuple[int, int]]) -> np.ndarray:
    """Коррекция перспективы на основе найденных углов"""
    size = Config.OUTPUT_SIZE
    dst_points = np.array([
        [0, size-1],
        [size-1, size-1],
        [size-1, 0],
        [0, 0]
    ], dtype=np.float32)
    
    src_points = np.array(corners, dtype=np.float32)
    matrix = cv2.getPerspectiveTransform(src_points, dst_points)
    corrected = cv2.warpPerspective(image, matrix, (size, size))
    
    return corrected

def enhance_image(image: np.ndarray, roi: Tuple[int, int, int, int]) -> Tuple[np.ndarray, np.ndarray]:
    """Улучшение изображения и выделение стрелки"""
    x, y, w, h = roi
    cropped = image[y:y+h, x:x+w]
    gray = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, Config.BLUR_SIZE, 0)
    thresh = cv2.adaptiveThreshold(
        blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV, Config.ADAPTIVE_THRESH_BLOCK, Config.ADAPTIVE_THRESH_C
    )
    return thresh, cropped

def find_dial_center(image: np.ndarray, roi: Tuple[int, int, int, int]) -> Tuple[Tuple[int, int, int], np.ndarray]:
    """Определение центра циферблата"""
    x, y, w, h = roi
    cropped = image[y:y+h, x:x+w]
    gray = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY)
    vis = cropped.copy()
    
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, **Config.HOUGH_CIRCLES_PARAMS)
    
    if circles is not None:
        circles = np.uint16(np.around(circles))
        best = circles[0][0]
        cv2.circle(vis, (best[0], best[1]), best[2], (0, 255, 0), 2)
        cv2.circle(vis, (best[0], best[1]), 2, (0, 0, 255), 3)
        center_vis_path = f"{Config.OUTPUT_DIR}center_detection.jpg"
        cv2.imwrite(center_vis_path, vis)
        return (x + best[0], y + best[1], best[2]), vis
    
    center = (x + w//2, y + h//2, min(w, h)//2)
    cv2.circle(vis, (w//2, h//2), min(w, h)//2, (0, 255, 0), 2)
    center_vis_path = f"{Config.OUTPUT_DIR}center_detection.jpg"
    cv2.imwrite(center_vis_path, vis)
    return center, vis

def find_arrow_angle(thresh: np.ndarray, original: np.ndarray, 
                    roi: Tuple[int, int, int, int], 
                    center_info: Tuple[int, int, int]) -> Tuple[Optional[float], Optional[Tuple[int, int]], np.ndarray]:
    """Поиск угла стрелки"""
    x, y, w, h = roi
    cX, cY, radius = center_info
    cropped = original[y:y+h, x:x+w]
    cX_roi, cY_roi = cX - x, cY - y
    
    params = Config.HOUGH_LINES_PARAMS.copy()
    params['minLineLength'] = radius * 0.5
    
    edges = cv2.Canny(thresh, 50, 150)
    lines = cv2.HoughLinesP(edges, **params)
    
    if lines is not None:
        filtered = []
        for line in lines:
            x1, y1, x2, y2 = line[0]
            dist1 = math.hypot(x1 - cX_roi, y1 - cY_roi)
            dist2 = math.hypot(x2 - cX_roi, y2 - cY_roi)
            if min(dist1, dist2) < radius * 0.3:
                filtered.append(line)
        
        if filtered:
            longest = max(filtered, key=lambda l: math.hypot(l[0][0]-l[0][2], l[0][1]-l[0][3]))
            x1, y1, x2, y2 = longest[0]
            
            tip = (x2, y2) if math.hypot(x1-cX_roi, y1-cY_roi) < math.hypot(x2-cX_roi, y2-cY_roi) else (x1, y1)
            angle = math.degrees(math.atan2(cY_roi - tip[1], tip[0] - cX_roi)) % 360
            
            cv2.line(cropped, (cX_roi, cY_roi), tip, (0, 0, 255), 3)
            return angle, (cX, cY), cropped
    
    return None, None, cropped

def setup_mqtt() -> mqtt.Client:
    """Настройка MQTT клиента"""
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    client.username_pw_set(Config.MQTT_USER, Config.MQTT_PASS)
    client.connect(Config.MQTT_BROKER, Config.MQTT_PORT, Config.MQTT_TIMEOUT)
    return client

def calibrate_pressure(angle: float) -> float:
    """Калибровка давления"""
    angles = np.array(sorted(Config.CALIBRATION.keys()))
    pressures = np.array([Config.CALIBRATION[a] for a in angles])
    return float(np.interp(angle, angles, pressures))

def send_mqtt(pressure: float) -> bool:
    """Отправка данных через MQTT"""
    try:
        client = setup_mqtt()
        client.publish(Config.MQTT_TOPIC, f"{pressure:.2f}")
        client.disconnect()
        return True
    except Exception as e:
        logger.error(f"Ошибка MQTT: {str(e)}")
        return False

def process_image(image_path: str) -> Optional[float]:
    """Основная функция обработки изображения"""
    try:
        image = cv2.imread(image_path)
        if image is None:
            raise FileNotFoundError(f"Не удалось загрузить изображение: {image_path}")
        
        quad_corners, quad_vis = detect_quadrilateral(image)
        if quad_corners is None:
            logger.error("Не удалось обнаружить четырехугольник")
            return None
            
        cv2.imwrite(f"{Config.OUTPUT_DIR}quadrilateral_detection.jpg", quad_vis)
        corrected_image = correct_perspective(image, quad_corners)
        cv2.imwrite(f"{Config.OUTPUT_DIR}corrected_perspective.jpg", corrected_image)
        
        corrected_roi = (0, 0, Config.OUTPUT_SIZE, Config.OUTPUT_SIZE)
        thresh, cropped = enhance_image(corrected_image, corrected_roi)
        center, _ = find_dial_center(corrected_image, corrected_roi)
        angle, _, processed = find_arrow_angle(thresh, corrected_image, corrected_roi, center)
        
        if angle is None:
            logger.error("Не удалось определить угол стрелки")
            return None
        
        pressure = calibrate_pressure(angle)
        cv2.putText(processed, f"Angle: {angle:.1f}°", (20, 40), 
                   cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        cv2.putText(processed, f"Pressure: {pressure:.2f} bar", (20, 80),
                   cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        
        cv2.imwrite(f"{Config.OUTPUT_DIR}processed.jpg", processed)
        return pressure
        
    except Exception as e:
        logger.error(f"Ошибка обработки: {str(e)}")
        return None

def main():
    image_path = '/app/sample.jpg'
    pressure = process_image(image_path)
    if pressure is not None:
        send_mqtt(pressure)

if __name__ == "__main__":
    main()

Код скрипта писал трудяга DeepSeek под моим чутким руководством. Сначала пытался это сделать в ChatGPT, но после достижения бесплатного лимита запросов он резко "тупел" и дело шло уже не так хорошо:)

Определение углов
Определение углов
Коррекция перспективы и обрезка изображения
Коррекция перспективы и обрезка изображения
Определение центра и окружности манометра
Определение центра и окружности манометра
Поиск стрелки и определение давления по углу отклонения стрелки
Поиск стрелки и определение давления по углу отклонения стрелки

Настройка автозапуска скрипта в Xpenology

Осталось дело за малым - настраиваем на NAS автозапуск скрипта. Переходим в Панель управления, Планировщик задач, Создать, Запланированная задача, Скрипт заданный пользователем.

Во вкладке Общие указываем, что задача должна выполняться от пользователя root. Расписание - ежедневно каждый час. В Настройки задач в поле Выполнить команду прописываем docker exec ID_контейнера python3 /app/gauge_reader.py.

Результат

Теперь раз в час HA делает фотографию манометра и кладет ее в папку NAS. А NAS спустя пять минут распознает это дело шлёт в mqtt фактическое давление в системе отопления.

Да, я в курсе, что сейчас уже есть DIY zigbee манометры. Может быть я соберусь с мыслями, возьму паяльник и врежусь в систему отопления и в систему водоснабжения, чтобы можно было автоматизировать включение крана подпитки системы отопления только в случае наличия холодной воды. Но пока пусть поработает так.

Почему не реализовал распознавание изображения на Raspberry Pi? Просто так захотелось))

Сама идея взаимодействия двух устройств в домашней сети показалась интересной.

Комментарии (37)


  1. greed-314
    10.05.2025 14:58

    Спасибо Вам, интересная статья


    1. dre40
      10.05.2025 14:58

      Очень интересно. Предлагаю пойти дальше и автоматизировать регулировку температуры на этом котле, готовых решений для этого я не нашёл.


  1. BigBeerman
    10.05.2025 14:58

    Как КИПовец, негодую! Датчик давления для этого есть!


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Как энергетик - промолчу...)))

      Разумеется, что есть датчик давления. И уважаемый Джон Гудман Efekta для таких как я их продает.
      Тут больше процесс. Поднапрячь мозги, получить новый опыт.


      1. aborouhin
        10.05.2025 14:58

        И уважаемый Джон Гудман Efekta для таких как я их продает.

        Скорее Овен их продаёт, если самые распространённые у нас брать. 4..20 мА, без всяких беспроводных заморочек.


    1. aborouhin
      10.05.2025 14:58

      А ещё котлы часто умеют в OpenTherm, по которому сами это давление отдают. Но если на котле манометр аналоговый - то вряд ли, конечно.


      1. kuzhakhmetov Автор
        10.05.2025 14:58

        Да, мой Аристон не поддерживает OpenTherm, к сожалению.


        1. Xius
          10.05.2025 14:58

          Думаю, своей цели Вы достигли - сэкономили время на обработку фото глазами )) А сам манометр разборный? Можно ещё было крышку снять и на герконах сделать (нанести магнитное напыление на стрелку). Допустим целевое давление между 1,5 и 2,5 и на них будут замыкаться датчики. И там уже обрабатывать. Чуть-чуть паять придётся, но зато нагрузки меньше на систему (обработка фото и пр).


          1. kuzhakhmetov Автор
            10.05.2025 14:58

            Своей цели я однозначно достиг)

            Манометр разобрать можно, но это точно не для меня)


      1. electedfx
        10.05.2025 14:58

        Никак не связанные вещи, у меня котел с opentherm, но аналоговый манометр присутствует. Имхо нужен для оперативного визуального контроля, чтобы не искать в куче параметров


        1. kuzhakhmetov Автор
          10.05.2025 14:58

          Разумеется, это разные вещи. Тут вопрос больше в том, что если бы котёл поддерживал возможность прямой интеграции куда-либо - не нужно было вот так колхозить, чтобы показания снять.


    1. Agalexx
      10.05.2025 14:58

      КИПовец знал бы, что это называется преобразователь давления. И такие есть в продаже даже на Озоне. ИМХО удобнее всего на 1wire через esp'шку прокинуть в HA


  1. fquantum
    10.05.2025 14:58

    Так себе идея - открывать кран для подпитки при падении давления. При повышении температуры в системе может резко подскочить давление и сработает сброс (если есть). Вообще, падение давления в закрытой системе - повод сделать ее ревизию. Проверить утечку жидкости, проверить расширительный бак.


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Конечно нет! Хоть система и замкнутая, но я периодически провожу её ТО. Сливаю теплоноситель, чищу фильтры и заполняю вновь. При заполнении система не сразу развоздушивается - воздух может в течении нескольких дней удаляться через воздухоотводчики.
      Ну и по поводу "подскочить давление" - в котле стоит штатный расширительный бак. Также установлен дополнительный на 15 литров т.к. система большая.


      1. fquantum
        10.05.2025 14:58

        Я вот что имел ввиду - если при наличии расширительного бака в системе упало давление, значит система либо потеряла много жидкости (именно много, не каплю, т.е. где то протечка, которая не компенсировалась расширительным баком), либо ушел воздух из расширительного бака.


      1. Kitsok
        10.05.2025 14:58

        Сценарий аварии другой, если кран подпитки застрянет в открытом состоянии, не важно, по какой причине.


        1. kuzhakhmetov Автор
          10.05.2025 14:58

          Ну тут тоже ничего страшного. На вводе холодной воды стоит редуктор давления, который настроен на 2.8 бар. Ну ещё группа безопасности установлена со сбросным клапаном, который по умолчанию на 3 бара настроен.

          А вот если кран застрянет в открытом состоянии и выключат холодную воду - тогда я останусь без котла - уйдёт в аварию по низкому давлению системы.


          1. kavc
            10.05.2025 14:58

            Разве после крана подпитки у вас не установлен обратный клапан? Непорядок.


            1. kuzhakhmetov Автор
              10.05.2025 14:58

              Согласен!


  1. Lukovkin_a
    10.05.2025 14:58

    Интересно, спасибо. В процессе чтения подумалось, на что только люди не идут, лишь бы расширительный бачок в порядок не привести и утечки не устранить ))

    А серьезно- на понимаю, зачем часто сливать теплоноситель? Фильтр почистить- по хорошему он кранами должен перекрываться.


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Утечек в системе нет.

      Расширительный бак в порядке. Раз в год немного подбиваю в него давление.

      Теплоноситель сливаю только с обвязки котла, потому что в нём перед насосом установлен фильтр. Как правило делаю это раз в год.


  1. Daddy_Cool
    10.05.2025 14:58

    Очень интересно! А с какой скоростью это все работает, можно сказать? Это скорее вопрос о библиотеке. Если стрелка будет крутиться со скоростью 1 оборот в 10 секунд получится данные считать?


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      У меня все работает очень быстро. Esp32-CAM может фотографировать каждую секунду точно. Скрипт, который крутится на NAS тоже отрабатывает почти мгновенно.

      Но, для стрелки, которая вращается с какой-то постоянной скоростью, думаю надо немного другое решение подбирать. Опять же, надо глубже вникнуть в задачу)


  1. StelthRoman
    10.05.2025 14:58

    Сделал по-другому недавно. От холодной воды сделал врезку в обратку системы отопления через регулятор давления, настроенный на 0.9 psi. Он поддерживает постоянное давление не ниже заданного. Плюс, этой зимой было очень холодно, а я часто на вахте пропадаю - удалённо только за температурой системы по opentherm управляю котлом. Бывало что необходимо было держать температуру котла под 80 градусов (он у меня по pid Home Assistant работает, а целевая температура у меня погодозависимой прописана) как итог система нагревается и давление к 2.0 psi подбиралось-супруга сбрасывала давление в системе периодически. Для сброса давления поставил соленоид с РД настроенный на 1.7 psi с дифференциалом 0.6. Еще есть место под датчик давления. Схему уже собрал, осталось через esp его в home assistant прописать. Надеюсь успею за неделю до отъезда.


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Респект! Уважаю такие решения)

      Тоже стараюсь по максимуму жену от инженерки домашней освободить, пока по командировкам мотаюсь.

      Для датчика давления на esp тоже все подготовил, но так руки не дошли.

      Сейчас хорошее готовое решение на ZigBee есть, не смотрели? https://habr.com/ru/articles/828330/

      P.s. что-то я с величинах давления ваших запутался) если это бары, а не psi, то 2.0 не так много для системы отопления. Смело до 3-х можно нагонять. Или у котла другие ограничения?


    1. nvor
      10.05.2025 14:58

      Получается у вас при протечке системы отопления затопит дом? Авто подпитка спорное решение, у тепло-вода был ролик про затопленый таким образом дом. Системе отопления не должна требовать постоянной подпитки, надо опрессовать и найти утечку. При исправном и правильно подобранном расширительном бачке при нагреве давление сильно не растёт, может он сдулся у вас?


  1. theult
    10.05.2025 14:58

    Спасибо, интересная статья при очень нестандартном подходе. Интересен как один датчик и для человека "мне проще код написать, чем спаять трубу". А так пачку датчиков 4-20мА куда надо, что на отопление, что на скважину, что на фильтрацию воды


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Проще не равно интереснее)


  1. leon0399
    10.05.2025 14:58

    Не рассматривал относительно готовое решение, работающее прямо на железе ESP32?

    https://github.com/jomjol/AI-on-the-edge-device


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      С этим проектном я знаком. Навешивал на счетчик газа ровно так, как в там описано. Под мой манометр эту тему тяжело было бы адаптировать.


      1. positroid
        10.05.2025 14:58

        Тоже такая мысль была всю статью - ИИ и под Си на ESP32 код может писать, кажется что распознавание угла стрелки вполне бы заработало непосредственно на контроллере без дополнительного железа.

        Тем более что картинка статична, можно вручную задать все необходимые координаты и искажения. Но за реализацию все равно плюс)


        1. kuzhakhmetov Автор
          10.05.2025 14:58

          Надо эту мысль переварить)


  1. kiberpcix
    10.05.2025 14:58

    Кажется, решить вопрос герметичности контура проще..


    1. kuzhakhmetov Автор
      10.05.2025 14:58

      Поясните?


      1. kiberpcix
        10.05.2025 14:58

        Если давление в замкнутом контуре отопления упало, значит течет где-то. Ладно трубы, теплообменники часто лопаются/окисляются и текут на электронику. Я считаю индивидуальную систему отопления неисправной если требуется подпитка настолько часто, что приходится не штатную автоматику изобретать. Решение автора классное, но пахнет пожаром :) особенно если не знаешь как работает твоя система. Есть много людей, которые что то доделывают, особо не разбираясь что может быть при нарушении штатной работы. Удобство в ущерб безопасности в данном случае


        1. kuzhakhmetov Автор
          10.05.2025 14:58

          Насколько часто, по вашему мнению, мне требуется пользоваться подпиткой?

          Касательно неисправностей - любое оборудование имеет свой ресурс. Любое оборудование может выходить из строя. Хорошо, когда что-то ломается, а я дома и могу оперативно диагностировать и устранить неисправность. Но, нештатные ситуации на происходят по расписанию. На то они и нештатные.

          Например, как часто, по вашему мнению, надо прокачивать мембрану а расширительном баке? И как часто эти мемтраны рвутся?

          А если выключили свет/газ и минус 30 за окном? А включили через день и температура теплоносителя упала и давление ниже 1 бара? А дома никого нет и кран подпитки открыть некому? Да что кран подпитки открыть, никто и не в курсе что котел не работает.

          Разумеется, такое бывает нечасто. И разумеется, то что я теперь снимаю показания с манометра таким способом - это больше игрушки. Но это те мелочи, которые в итоге делают жизнь проще при небольших временных вложениях.


          1. randomsimplenumber
            10.05.2025 14:58

            Насколько часто, по вашему мнению, мне требуется пользоваться подпиткой?

            Если чаще чем раз в год - значит что-то где-то течет. Правильно было бы ликвидировать утечку. Но можно и подливать воду.

            включили через день и температура теплоносителя упала и давление ниже 1 бара?

            точно течет.

            Например, как часто, по вашему мнению, надо прокачивать мембрану а расширительном баке?

            Раз в год, когда делаете профилактику.