ГлавнаяАкадемияДатчики и входы: нормализация сигналов → Практика: Мониторинг температуры в нескольких зонах с помощью DS18B20

Практика: Мониторинг температуры в нескольких зонах с помощью DS18B20

Урок 6 · Датчики и входы: нормализация сигналов · 30 мин · theory

Введение и постановка задачи

> ℹ️ Информация: Этот урок объединяет навыки, полученные ранее. Убедитесь, что вы изучили материалы по физическому подключению датчиков (урок COURSE-04-M06-L03: Подключение датчиков температуры DS18B20) и базовому опросу в Node-RED (урок COURSE-04-M06-L05: Опрос датчиков в Node-RED).

Цель данного практического занятия — создать полноценную, готовую к эксплуатации систему мониторинга температуры для нескольких критически важных точек на объекте. Эта задача является одной из самых распространенных в автоматизации зданий и малых промышленных объектов. В качестве примера мы рассмотрим систему отопления коттеджа, где необходимо контролировать три ключевых параметра:

  • Температура на подающей линии котла: Позволяет судить об эффективности работы котла и соответствии его мощности текущим потребностям.
  • Температура на обратной линии ("обратка"): Разница между подачей и обраткой (дельта T) является важнейшим показателем работы системы отопления в целом.
  • Температура в котельной: Позволяет контролировать микроклимат в техническом помещении и косвенно судить о наличии утечек тепла или нештатной работе оборудования.
  • Хотя пример сфокусирован на системе отопления, представленная архитектура является универсальным шаблоном, который легко адаптируется для мониторинга любых других объектов: температуры в серверной стойке, в холодильных камерах, в помещениях гостиницы и т.д.

    Архитектура нашего решения будет состоять из следующих логических блоков, реализуемых в среде Node-RED на контроллере HI:

    Этот урок является кульминацией модуля, объединяя ранее изученные теоретические концепции и практические навыки в единое, работающее и масштабируемое решение.

    ---

    Проектирование потока: от опроса до публикации

    > 💡 Подсказка: Прежде чем собирать поток в Node-RED, нарисуйте его схему на бумаге или в любом диаграммном редакторе. Это помогает структурировать мысли, заранее выявить потенциальные проблемы в логике и делает процесс разработки более предсказуемым.

    Правильное проектирование — залог создания читаемого, поддерживаемого и надежного потока автоматизации. Вместо хаотичного соединения узлов мы будем придерживаться четкой и линейной логической структуры.

    Визуализация логики потока

    Наш поток будет состоять из пяти основных этапов, каждому из которых соответствует определенный узел или группа узлов в Node-RED:

  • `Inject` (Триггер): Узел, который запускает всю цепочку с заданной периодичностью. Он выполняет роль системного "пульса" или heartbeat для нашего мониторинга.
  • `w1-in` (Чтение данных): Для каждого датчика DS18B20 на шине мы создадим отдельный узел `w1-in`. Такой параллельный подход, в отличие от последовательного перебора, более эффективен и позволяет легко идентифицировать, какой именно датчик не ответил в случае сбоя.
  • `Join` (Объединение/Агрегация): Этот узел является ключевым для нашей задачи. Он будет "слушать" сообщения от всех узлов `w1-in` и, когда получит показания от каждого из них (или по истечении таймаута), объединит их в один-единственный объект `msg`.
  • `Function` (Нормализация): Получив агрегированные, но все еще "сырые" данные от узла `Join`, этот узел преобразует их в финальный, чистый формат. Здесь мы присвоим данным понятные имена, добавим служебную информацию и проверим значения на корректность.
  • `mqtt out` (Публикация): Финальный этап, на котором обработанные и готовые к использованию данные отправляются в MQTT-брокер, становясь доступными для всей экосистемы автоматизации.
  • Визуально эта последовательность выглядит так:

                      +------------+   +-------------------+
    

    | w1-in |-->| |

    | (Sensor 1) | | |

    +--------+ +------------+ | | +----------+ +----------+

    | Inject |------->+------------+-->| Join |-->| Function |-->| mqtt out |

    | (30s) | | w1-in | | (aggregate data) | | (format) | | (publish)|

    +--------+ | (Sensor 2) | | | +----------+ +----------+

    +------------+ | |

    | w1-in | | |

    | (Sensor 3) |-->| |

    +------------+ +-------------------+

    Проектирование структуры данных

    Ключевой аспект проектирования — заранее определить, в каком виде мы хотим получить данные на выходе. Неструктурированные данные (`23.5`) или данные с техническими идентификаторами (`{"28-01201e8a0e3a": 65.1}`) усложняют дальнейшую разработку и поддержку.

    Наша цель — получить на выходе из узла `Function` строго структурированный JSON-объект.

    Пример целевой структуры `msg.payload`:
    {
    

    "values": {

    "supply_pipe": 65.1,

    "return_pipe": 58.7,

    "boiler_room": 24.3

    },

    "meta": {

    "status": "OK",

    "timestamp": 1678886400000,

    "source": "heating-controller-flow"

    }

    }

    Такая структура обладает несколькими преимуществами:

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

    ---

    Практика: Агрегация данных с датчиков DS18B20

    Перейдем к сборке потока в редакторе Node-RED. На этом этапе мы настроим узлы `Inject`, `w1-in` и `Join` для сбора данных со всех датчиков в один объект.

    1. Настройка триггера `Inject`

  • Перетащите узел `Inject` на поле редактора.
  • Дважды кликните по нему, чтобы открыть окно настроек.
  • В поле `Payload` оставьте `timestamp` (это значение по умолчанию, оно нам пригодится).
  • В разделе `Repeat` выберите `interval` и установите значение `every 30 seconds`. Это оптимальная частота для мониторинга инертных процессов, таких как нагрев воды.
  • Задайте узлу осмысленное имя, например, "Опрос каждые 30 сек".
  • 2. Конфигурация узлов `w1-in` для каждого датчика

    Теперь необходимо создать узел для каждого из трех наших датчиков. Предположим, система обнаружила следующие ID на шине 1-Wire:

    Создайте три узла `w1-in`:

  • Перетащите на поле узел `w1-in` из палитры `rpi 1-wire`.
  • Откройте его настройки.
  • В поле `1-Wire Device` выберите из выпадающего списка первый ID: `28-01201e8a0e3a`.
  • Это ключевой шаг: В поле `Topic` впишите человеко-читаемый алиас для этого датчика: `supply_pipe`.
  • В поле `Name` укажите: "Температура подачи".
  • Нажмите `Done`.
  • Повторите шаги 1-6 для двух других датчиков, задавая им соответствующие `Device ID`, `Topic` (`return_pipe`, `boiler_room`) и `Name`.
  • Соедините выход узла `Inject` со входами всех трех узлов `w1-in`. Теперь каждые 30 секунд они будут одновременно пытаться считать температуру со своих датчиков. После успешного чтения каждый узел сгенерирует сообщение, где `msg.payload` будет содержать температуру, а `msg.topic` — заданный нами алиас.

    3. Настройка узла `Join` для объединения сообщений

    Узел `Join` — это наш агрегатор. Его задача — собрать три отдельных сообщения в одно.

  • Перетащите узел `Join` на поле редактора.
  • Соедините выходы всех трех узлов `w1-in` со входом узла `Join`.
  • Откройте настройки узла `Join`. Нам нужно настроить его на работу в "ручном" режиме объединения.
  • * Mode: Установите `manual`.

    * Combine to create: Выберите `a key/value object`. Это скажет узлу `Join` создавать объект, используя `msg.topic` в качестве ключа и `msg.payload` в качестве значения.

    * Number of message parts: Установите `3`. Узел будет ждать ровно 3 сообщения, прежде чем сформировать и отправить итоговый объект.

    * And every: Установите таймаут, например, `5` секунд. Если за 5 секунд узел не получит все 3 сообщения (например, один из датчиков "повис" или оборвалась линия), он отправит дальше то, что успел собрать. Это защищает систему от полной остановки из-за сбоя одного элемента.

    * `Topic` of joined message: Оставьте пустым.

    После этих настроек, если все датчики работают штатно, узел `Join` будет генерировать следующее сообщение `msg`:

    {
    

    "payload": {

    "supply_pipe": 65.1,

    "return_pipe": 58.7,

    "boiler_room": 24.3

    },

    "topic": "",

    "_msgid": "..."

    }

    Мы получили агрегированные данные. Теперь их нужно привести к нашему целевому формату.

    ---

    Практика: Нормализация и форматирование данных

    > ⚠️ Внимание: Всегда реализуйте проверку на наличие и корректность данных. Если необработанное значение `null` или некорректное число (например, `85` градусов для DS18B20 — это признак ошибки инициализации) попадет в вашу логику автоматизации, это может привести к непредсказуемым последствиям, от ложных тревог до полной остановки работы системы.

    На этом этапе мы используем узел `Function` для финальной обработки данных, полученных от `Join`. Эта "швейцарский нож" Node-RED, позволяющий реализовать практически любую логику на JavaScript.

  • Перетащите узел `Function` на поле и соедините его со выходом узла `Join`.
  • Откройте настройки и вставьте следующий код в редактор:
  • // Получаем объект с температурами из входящего сообщения
    

    const temps = msg.payload;

    // Список ожидаемых датчиков (алиасов). Это поможет нам определить, все ли на месте.

    const expectedSensors = ["supply_pipe", "return_pipe", "boiler_room"];

    let finalValues = {};

    let failedSensors = [];

    let status = "OK";

    // 1. Итерация по ожидаемым датчикам и валидация значений

    expectedSensors.forEach(sensorAlias => {

    // Проверяем, пришло ли значение от датчика и является ли оно числом

    if (temps.hasOwnProperty(sensorAlias) && typeof temps[sensorAlias] === 'number') {

    let tempValue = temps[sensorAlias];

    // Дополнительная валидация: DS18B20 работает в диапазоне -55 to +125.

    // Значение 85 - частый признак ошибки при включении.

    if (tempValue > -55 && tempValue < 125 && tempValue !== 85.0) {

    finalValues[sensorAlias] = tempValue;

    } else {

    // Значение вышло за допустимые пределы

    finalValues[sensorAlias] = null; // Помечаем как невалидное

    failedSensors.push(sensorAlias);

    node.warn(`Некорректное значение от датчика ${sensorAlias}: ${tempValue}`);

    }

    } else {

    // Датчик не ответил (не пришел в объекте от узла Join)

    finalValues[sensorAlias] = null; // Помечаем как отсутствующее

    failedSensors.push(sensorAlias);

    node.warn(`Нет данных от датчика: ${sensorAlias}`);

    }

    });

    // 2. Определение общего статуса

    if (failedSensors.length > 0) {

    if (failedSensors.length === expectedSensors.length) {

    status = "TOTAL_FAILURE"; // Все датчики отказали

    } else {

    status = "PARTIAL_FAILURE"; // Часть датчиков отказала

    }

    }

    // 3. Формирование финального msg.payload по нашему целевому контракту

    msg.payload = {

    values: finalValues,

    meta: {

    status: status,

    failed_count: failedSensors.length,

    failed_list: failedSensors, // Список отказавших для быстрой диагностики

    timestamp: Date.now(), // Добавляем точную временную метку

    source: "flow-heating-monitor-01"

    }

    };

    // 4. Обновление статуса узла для визуальной диагностики в редакторе

    if (status === "OK") {

    node.status({ fill: "green", shape: "dot", text: `OK: Подача ${finalValues.supply_pipe}°C` });

    } else {

    node.status({ fill: "red", shape: "ring", text: `Ошибка: ${status}, отказало ${failedSensors.length}` });

    }

    // Возвращаем измененное сообщение для передачи следующему узлу

    return msg;

    Разбор кода:

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

    ---

    Интеграция с MQTT: Публикация данных для системы

    > 🔗 Связанный материал: Подробно о принципах работы с MQTT и настройке брокера на контроллере HI рассказано в модуле по протоколам и интеграциям (COURSE-02-M03: Протокол MQTT для распределенных систем).

    Последний шаг — сделать наши данные доступными для всей системы. MQTT — это идеальный инструмент для этой задачи, выступающий в роли центральной "шины данных" для умного объекта.

  • Перетащите узел `mqtt out` на поле редактора и соедините его с выходом узла `Function`.
  • Откройте настройки узла:
  • * Server: Выберите из выпадающего списка ваш MQTT-брокер. На контроллерах HI он обычно предустановлен и доступен по адресу `localhost:1883`.

    * Topic: Укажите topic, соответствующий принятой в вашем проекте иерархии. Хорошей практикой является структура `platform/site/system/measurement`. Для нашего примера это будет:

    `hi/cottage-1/heating/temperatures`

    Такая структура позволяет гибко подписываться на данные. Например, подписка на `hi/cottage-1/heating/#` даст все данные по системе отопления, а `hi/cottage-1/#` — вообще все данные по объекту "Коттедж 1".

    * QoS: Установите `1` (At least once). Это обеспечивает гарантированную доставку сообщения, но допускает дубликаты (что для данных мониторинга не критично).

    * Retain: Установите `true` (галочка `Retain`). Это означает, что MQTT-брокер сохранит последнее сообщение в этом топике. Любой новый клиент (например, панель визуализации), подписавшись на этот топик, немедленно получит последнее актуальное состояние температур, не дожидаясь следующего цикла опроса.

    * Name: "Публикация в MQTT".

    После развертывания потока (`Deploy`) каждые 30 секунд в топик `hi/cottage-1/heating/temperatures` будет публиковаться наш JSON-объект.

    Для проверки корректности работы можно использовать любой внешний MQTT-клиент, например, программу MQTT Explorer. Подключившись к брокеру, вы должны увидеть ваш топик и поступающие в него сообщения в реальном времени, что подтверждает успешное завершение нашей работы.

    ---

    Заключение: Итоги и дальнейшие шаги

    В рамках этого урока мы прошли полный цикл создания практического решения: от постановки задачи и проектирования до реализации, отладки и интеграции. Мы создали надежный и эффективный поток Node-RED, который решает одну из самых фундаментальных задач автоматизации — мониторинг температуры в нескольких зонах.

    Ключевые результаты: Анализ решения и потенциальные улучшения: * Динамический поиск: Текущая реализация статична. При добавлении нового датчика на шину требуется ручное редактирование потока. На уровне `Advanced` рассматриваются методы автоматического сканирования шины 1-Wire и динамической генерации потоков.

    * Продвинутая обработка ошибок: Можно добавить логику повторных попыток чтения для "зависшего" датчика или отправку push-уведомлений администратору при полном отказе системы мониторинга.

    Что дальше?

    Собранные и опубликованные в MQTT данные являются не конечной целью, а ценным ресурсом для создания более сложной автоматизации:

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