ГлавнаяАкадемияCOURSE-16: Основы Интернета Вещей и практическое применение → Сбор, обработка и передача данных с Modbus-сенсора влажности

Сбор, обработка и передача данных с Modbus-сенсора влажности

Урок 7 · COURSE-16: Основы Интернета Вещей и практическое применение · theory

COURSE-16-M03-LAB01 — Сбор, обработка и передача данных с Modbus-сенсора влажности

1. Цели лабораторной работы

По завершении данной лабораторной работы инженер сможет:

2. Оборудование и программное обеспечение

3. Предварительная подготовка

📋 Чек-лист готовности к работе:

  • Физическое подключение: Датчик подключен к клеммам RS-485 контроллера HI. Схема подключения соответствует стандарту `WIRING-SENS-004`.
  • * Клемма `A` датчика -> Клемма `A` порта RS-485 контроллера.

    * Клемма `B` датчика -> Клемма `B` порта RS-485 контроллера.

    * Подано питание на датчик (например, 24V DC).

  • Настройка Modbus-устройства: На датчике выставлен уникальный Slave ID (например, `25`) и стандартные параметры шины (`9600 8N1`). Эти данные должны быть известны и задокументированы.
  • Доступ к Node-RED: У вас есть доступ к веб-интерфейсу Node-RED на контроллере.
  • 💡 Карта регистров используемого датчика (пример):

    Для данной лабораторной работы мы будем использовать следующие допущения о карте регистров нашего датчика:

    4. Сценарий выполнения

    Мы создадим поток Node-RED, который каждые 15 секунд опрашивает датчик влажности, проверяет корректность данных, приводит их к стандартному формату и отправляет в MQTT.

    Архитектура потока (ASCII Flow Diagram):
                                   +---------------------------+    +----------------------+
    

    [Inject: 15s] -> [Modbus-Getter] -> | Function: Validate/Format | -> | mqtt out |

    (Опрос) (Чтение RH) | (Основная логика) | | (Публикация данных) |

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

    | (on error)

    v

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

    [Catch: All Nodes]-----------------> | Function: LogErr | -> [Debug: Ошибки]

    (Перехват ошибок) +------------------+

    Шаг 1: Создание и настройка узла опроса

  • Перетащите на поле узел `inject`.
  • Дважды кликните на него и настройте:
  • * `Payload`: `timestamp`

    * `Repeat`: `interval`

    * `every`: `15` `seconds`

    * `Name`: `Опрос каждые 15 сек`

    * Нажмите Done.

    Шаг 2: Настройка узла чтения Modbus

  • Перетащите узел `Modbus-Getter` из палитры `modbus`.
  • Дважды кликните на него для настройки:
  • * `Name`: `Читать влажность (RH)`

    * `Server`: Нажмите на иконку карандаша для создания нового Modbus-клиента.

    * `Type`: `RTU-BUFFERED`

    * `Serial Port`: Укажите порт вашего контроллера, например, `/dev/ttyRS485-1`.

    * `Baud Rate`: `9600`

    * `Data Bits`: `8`

    * `Parity`: `None`

    * `Stop Bits`: `1`

    * Нажмите Add, а затем выберите созданный сервер.

    * `Unit-ID`: `25` (Slave ID вашего датчика).

    * `FC`: `FC 4: Read Input Registers`.

    * `Address`: `105` (адрес регистра влажности).

    * `Quantity`: `1`.

    * Нажмите Done.

  • Соедините выход узла `inject` со входом узла `Modbus-Getter`.
  • Шаг 3: Разработка узла логики (валидация и форматирование)

    Это самый важный узел, реализующий паттерны "Контракт сообщения" и "Визуальный статус".

  • Перетащите узел `function` и соедините его с выходом `Modbus-Getter`.
  • Дважды кликните и настройте:
  • * `Name`: `Validate & Format RH`

    * `Outputs`: `1`

    * Вставьте следующий код в редактор:

    // ID этого сценария для логирования и отладки
    

    const SENSOR_SOURCE_ID = "humidity-sensor-room101";

    // 1. Получение сырого значения из ответа Modbus

    // Узел Modbus-Getter возвращает массив в msg.payload

    let rawValue = msg.payload[0];

    // 2. Валидация данных

    // Проверяем, что значение находится в допустимом диапазоне (0-1000, т.е. 0.0-100.0%)

    if (rawValue === undefined || rawValue < 0 || rawValue > 1000) {

    // Паттерн "Визуальный статус": отображаем ошибку на узле

    node.status({ fill: "red", shape: "dot", text: "Invalid data: " + rawValue });

    // Паттерн "Обработка ошибок": генерируем ошибку, которая будет поймана узлом Catch

    node.error("Некорректное значение влажности: " + rawValue, msg);

    return null; // Прерываем выполнение потока для этого сообщения

    }

    // 3. Преобразование данных

    // Делим на 10, чтобы получить реальное значение влажности

    let humidity = rawValue / 10.0;

    // 4. Паттерн "Контракт сообщения": формируем стандартный объект msg.payload

    msg.payload = {

    value: humidity,

    unit: "%RH",

    source: SENSOR_SOURCE_ID,

    ts: Date.now()

    };

    // 5. Установка топика для MQTT

    msg.topic = "hi/telemetry/room101/humidity";

    // 6. Паттерн "Визуальный статус": отображаем успешный результат

    node.status({ fill: "green", shape: "dot", text: "OK: " + humidity + " %RH" });

    return msg;

  • Нажмите Done.
  • Шаг 4: Настройка публикации в MQTT

  • Перетащите узел `mqtt out` и соедините его с выходом узла `function`.
  • Дважды кликните и настройте:
  • * `Server`: Выберите или настройте ваш MQTT-брокер.

    * `Topic`: Оставьте пустым, так как топик устанавливается в `msg.topic`.

    * `QoS`: `1`

    * `Retain`: `false`

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

    * Нажмите Done.

    Шаг 5: Настройка централизованной обработки ошибок

  • Перетащите узел `catch` на поле.
  • Дважды кликните и настройте:
  • * `Scope`: `all nodes on current flow` (ловить ошибки со всех узлов на этой вкладке).

    * `Name`: `Перехват ошибок`

    * Нажмите Done.

  • Перетащите узел `debug` и настройте его на вывод `complete msg object`.
  • Соедините выход узла `catch` со входом узла `debug`.
  • ⚠️ Важно: В реальном проекте вместо узла `debug` здесь будет субпоток-логгер (`FLOW-SYSTEM-LOG-001`), который записывает ошибку в MySQL, отправляет уведомление администратору и т.д. Для данной лабораторной работы достаточно вывода в панель отладки.

    Шаг 6: Развертывание и проверка

  • Нажмите кнопку Deploy.
  • Перейдите в панель отладки (справа, иконка жука).
  • Каждые 15 секунд вы должны видеть сообщения от узла `Modbus-Getter`.
  • Если все настроено верно, узел `function` будет отображать зеленый статус с текущей влажностью.
  • С помощью любого MQTT-клиента (например, MQTT Explorer) подпишитесь на топик `hi/telemetry/room101/humidity` и убедитесь, что данные приходят в правильном JSON-формате.
  • 5. Скелет потока (Flow Skeleton)

    Вы можете импортировать этот JSON-код через меню Node-RED (`Import`), чтобы получить готовую структуру потока.

    [
    

    {

    "id": "a1b2c3d4.e5f6g7",

    "type": "tab",

    "label": "COURSE-16-M03-LAB01",

    "disabled": false,

    "info": ""

    },

    {

    "id": "1a2b3c4d.5e6f7g",

    "type": "inject",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Опрос каждые 15 сек",

    "props": [

    {

    "p": "payload"

    }

    ],

    "repeat": "15",

    "crontab": "",

    "once": true,

    "onceDelay": 0.1,

    "topic": "",

    "payload": "",

    "payloadType": "date",

    "x": 150,

    "y": 100,

    "wires": [

    [

    "8h9i0j1k.2l3m4n"

    ]

    ]

    },

    {

    "id": "8h9i0j1k.2l3m4n",

    "type": "modbus-getter",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Читать влажность (RH)",

    "showStatusActivities": true,

    "showErrors": true,

    "logIOActivities": false,

    "unitid": "25",

    "dataType": "InputRegister",

    "adr": "105",

    "quantity": "1",

    "server": "YOUR_MODBUS_SERVER_ID",

    "x": 380,

    "y": 100,

    "wires": [

    [

    "5p6q7r8s.9t0u1v"

    ],

    []

    ]

    },

    {

    "id": "5p6q7r8s.9t0u1v",

    "type": "function",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Validate & Format RH",

    "func": "const SENSOR_SOURCE_ID = \"humidity-sensor-room101\";\nlet rawValue = msg.payload[0];\nif (rawValue === undefined || rawValue < 0 || rawValue > 1000) {\n node.status({ fill: \"red\", shape: \"dot\", text: \"Invalid data: \" + rawValue });\n node.error(\"Некорректное значение влажности: \" + rawValue, msg);\n return null;\n}\nlet humidity = rawValue / 10.0;\nmsg.payload = {\n value: humidity,\n unit: \"%RH\",\n source: SENSOR_SOURCE_ID,\n ts: Date.now()\n};\nmsg.topic = \"hi/telemetry/room101/humidity\";\nnode.status({ fill: \"green\", shape: \"dot\", text: \"OK: \" + humidity + \" %RH\" });\nreturn msg;",

    "outputs": 1,

    "noerr": 0,

    "initialize": "",

    "finalize": "",

    "x": 630,

    "y": 100,

    "wires": [

    [

    "2w3x4y5z.6a7b8c"

    ]

    ]

    },

    {

    "id": "2w3x4y5z.6a7b8c",

    "type": "mqtt out",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Публикация в MQTT",

    "topic": "",

    "qos": "1",

    "retain": "false",

    "broker": "YOUR_MQTT_BROKER_ID",

    "x": 870,

    "y": 100,

    "wires": []

    },

    {

    "id": "d9e8f7g6.h5i4j3",

    "type": "catch",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Перехват ошибок",

    "scope": null,

    "uncaught": false,

    "x": 370,

    "y": 200,

    "wires": [

    [

    "k2l1m0n9.o8p7q6"

    ]

    ]

    },

    {

    "id": "k2l1m0n9.o8p7q6",

    "type": "debug",

    "z": "a1b2c3d4.e5f6g7",

    "name": "Лог ошибок",

    "active": true,

    "tosidebar": true,

    "console": false,

    "tostatus": false,

    "complete": "true",

    "targetType": "full",

    "statusVal": "",

    "statusType": "auto",

    "x": 570,

    "y": 200,

    "wires": []

    }

    ]

    6. Чек-лист для самопроверки

    7. Возможные усложнения (Advanced Practice)

    8. Рубрика оценивания

    | Критерий | Макс. баллов | Описание |

    | :--- | :--- | :--- |

    | Функциональность | 40 | Поток успешно считывает данные с Modbus-устройства и публикует их в MQTT с заданной периодичностью. |

    | Применение паттернов | 30 | Корректно реализованы: 1) Валидация и "Контракт сообщения" в узле `function`. 2) Использование `node.status` для визуальной обратной связи. 3) Наличие узла `catch` для перехвата ошибок. |

    | Качество кода и конфигурации | 20 | Узлы имеют осмысленные имена. Код в узле `function` читаем и содержит комментарии. Конфигурация узлов (Modbus, MQTT) выполнена корректно. |

    | Диагностика | 10 | Инженер может продемонстрировать работу системы обработки ошибок, искусственно создав сбой (например, отключив датчик). |

    | Итого | 100 | |

    9. Runbook: "Что делать, если не работает"

    | Проблема | Возможная причина | Действия по устранению |

    | :--- | :--- | :--- |

    | Узел `Modbus-Getter` показывает ошибку `timeout` или `Port not open` | 1. Неправильно указан COM-порт.
    2. Физический обрыв линии RS-485.
    3. Перепутаны провода A/B.
    4. Несовпадение параметров шины (скорость, четность).
    5. Неправильный Slave ID. | 1. Проверьте имя порта в ОС Linux контроллера (`ls /dev/tty*`).
    2. "Прозвоните" кабель. Проверьте надежность зажимов в клеммах.
    3. Поменяйте местами провода A и B на клеммах контроллера.
    4. Убедитесь, что настройки в узле Node-RED и на датчике идентичны (`9600 8N1`).
    5. Проверьте Slave ID, установленный на датчике. |

    | Данные приходят, но они некорректны (например, `0` или `65535`) | 1. Неправильный адрес регистра.
    2. Неправильный тип регистра (Input vs Holding).
    3. Ошибка "off-by-one" (адрес `105` вместо `104`). | 1. Внимательно перечитайте карту регистров устройства.
    2. Убедитесь, что вы используете правильную функцию Modbus (FC4 для Input Registers).
    3. Попробуйте указать адрес на единицу меньше. |

    | Узел `mqtt out` показывает статус `disconnected` | 1. Неправильный адрес или порт MQTT-брокера.
    2. Брокер недоступен по сети.
    3. Неверные учетные данные (логин/пароль). | 1. Проверьте настройки MQTT-сервера в Node-RED.
    2. С контроллера выполните команду `ping <адрес_брокера>`, чтобы проверить сетевую доступность.
    3. Проверьте логин и пароль в конфигурации узла. |

    | Поток работает, но узел `function` показывает красный статус `Invalid data` | 1. Датчик неисправен и отдает некорректные значения.
    2. Логика валидации в коде слишком строгая. | 1. Проверьте сырые данные, приходящие от `Modbus-Getter` в панели отладки.
    2. Временно ослабьте условия в `if (rawValue < 0 || rawValue > 1000)` для диагностики. |