ГлавнаяАкадемияCOURSE-16: Основы Интернета Вещей и практическое применение → Основы программирования в экосистеме HI: от кода к потокам

Основы программирования в экосистеме HI: от кода к потокам

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

COURSE-16-M02-L04 — Основы программирования в экосистеме HI: от кода к потокам

Введение в программирование на платформе HI

Программирование является ядром любой системы автоматизации. Оно определяет логику, по которой контроллер реагирует на события, обрабатывает данные с датчиков и управляет исполнительными устройствами. В отличие от классических систем, требующих глубоких знаний языков вроде C++ или Python, платформа HI использует современный, визуальный подход к программированию — Node-RED.

Этот подход позволяет инженерам создавать сложные сценарии автоматизации, соединяя готовые функциональные блоки (узлы) в логические цепочки (потоки). Это не только на порядок ускоряет разработку, но и делает систему прозрачной, легко читаемой и простой в обслуживании. 95% всех задач на платформе HI решаются именно с помощью визуального программирования.

В этом уроке мы изучим фундаментальные принципы программирования на контроллере HI, освоим ключевые концепции Node-RED и создадим свой первый рабочий сценарий автоматизации.

Уровни программирования на платформе HI

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

Уровень 1: Визуальное программирование (Node-RED)

Это основной и наиболее предпочтительный метод разработки для инженеров всех уровней.

* Скорость: Создание сценариев в 5-10 раз быстрее, чем написание традиционного кода.

* Наглядность: Логика работы системы видна сразу на схеме потока. Отладка и поиск неисправностей значительно упрощаются.

* Надежность: Используются проверенные, отлаженные узлы из обширной библиотеки. Риск допустить синтаксическую ошибку сведен к минимуму.

Уровень 2: Скриптинг (JavaScript в узле `Function`)

Когда стандартных узлов недостаточно для реализации сложной логики, на помощь приходит узел `Function`.

* Валидация и преобразование данных от датчиков.

* Реализация сложных математических вычислений.

* Формирование сообщений сложной структуры для интеграции с внешними системами.

💡 Совет: Узел `Function` — мощный инструмент, но его следует использовать дозированно. Всегда сначала ищите готовый узел для вашей задачи. Если логика внутри узла `Function` становится слишком сложной (более 20-30 строк), это верный признак того, что ее нужно декомпозировать на несколько стандартных узлов или вынести в субпоток (Subflow).

Уровень 3: Системный уровень (Linux/Debian)

Для задач, выходящих за рамки стандартной автоматизации, инженеры уровня Architect могут использовать всю мощь операционной системы Linux (Debian), на которой работает контроллер.

* Интеграция со специфическим оборудованием, для которого нет узлов Node-RED.

* Задачи архивирования данных, ротации логов.

* Запуск сложных вычислительных моделей.

⚠️ Предупреждение: Работа на системном уровне требует глубоких знаний Linux и сопряжена с риском нарушения стабильности работы контроллера. Этот уровень не рассматривается в курсах Foundation и Installer.

Рабочая среда инженера: Редактор Node-RED

Ваша основная среда разработки — это веб-интерфейс Node-RED, доступный по IP-адресу контроллера. Он состоит из трех ключевых областей:

  • Палитра узлов (слева): Библиотека всех доступных функциональных блоков, сгруппированных по категориям (input, output, function, social, storage и т.д.). Вы перетаскиваете узлы из палитры в рабочую область.
  • Рабочая область (в центре): Бесконечное полотно, где вы соединяете узлы в потоки (flows). Потоки можно организовывать по вкладкам для логического разделения (например, "Освещение", "Климат", "Безопасность").
  • Боковая панель (справа): Содержит вкладки "Информация" (описание выбранного узла), "Помощь" и, что самое важное, "Отладка" (Debug). Вкладка отладки — ваш главный инструмент для просмотра сообщений, проходящих через поток, и поиска ошибок.
  • Ключевые концепции программирования в Node-RED

    Для создания надежных сценариев необходимо понимать четыре фундаментальные концепции.

    1. Событийно-ориентированное программирование

    Потоки в Node-RED не выполняются постоянно. Они "спят" и активируются только в ответ на какое-либо событие. Событием может быть:

    После активации поток выполняет последовательность действий и снова переходит в режим ожидания.

    2. Поток данных и "Контракт сообщения"

    Узлы обмениваются информацией с помощью объектов-сообщений (`msg`). Каждый `msg` — это JavaScript-объект, который имеет различные свойства. Самое важное свойство — `msg.payload`, в котором обычно передаются основные данные.

    Для создания предсказуемой и надежной системы в Академии HI принят строгий "Контракт сообщения". Все данные, передаваемые между узлами, должны соответствовать стандартному JSON-формату.

    Пример правильного `msg.payload` от датчика температуры:
    {
    

    "value": 24.5,

    "source": "temp-sensor-livingroom-01",

    "ts": 1678886400000,

    "unit": "°C"

    }

    ⚠️ Критически важно: Никогда не передавайте "голые" значения (например, просто число `24.5`). Всегда оборачивайте их в стандартизированный JSON-объект. Это упрощает отладку, логирование и дальнейшую обработку.

    3. Управление состоянием (Контекст)

    Часто сценарию нужно "помнить" предыдущее состояние. Например, чтобы переключить свет по одному нажатию кнопки, нужно знать, был ли он включен до этого. Для этого используется контекст (Context).

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

    4. Обработка ошибок и визуальный статус

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

    Практический пример: Считывание данных с датчика температуры

    Создадим сценарий, который каждые 10 секунд считывает показания с датчика температуры DS18B20 (подключен к универсальному входу контроллера) и отправляет их в MQTT в стандартизированном формате.

    ID Схемы подключения: `WIRING-SENS-001` ID Потока: `FLOW-SENS-TEMP-001`

    Схема подключения (ASCII)

    //========= WIRING-SENS-001: DS18B20 Temperature Sensor =========
    
    

    [CTRL:HI-Core] (SENS:Temp:DS18B20)

    (UI-01)

    Клемма Шина Цвет Клемма

    +5V --------- (Красный) ---- VDD

    DATA --------- (Желтый) ----- DQ

    GND --------- (Черный) ----- GND

    Поток в Node-RED (ASCII)

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

    [Inject] -> | ds18b20 (1-Wire In) | -> | Function: Validate/Format| -> | mqtt out |

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

    | (on error) ^

    v |

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

    | Catch Node | -> [Function: Log Error] -> [Debug/MySQL] |

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

    Конфигурация узлов:

  • `Inject` (Запуск опроса):
  • * Настроить на повторение (`repeat`) каждые `10 секунд`.

  • `ds18b20` (Чтение датчика):
  • * Установить узел из палитры `node-red-contrib-ds18b20-sensor`.

    * В настройках указать ID датчика 1-Wire (например, `28-01234567abcd`).

  • `Function` (Валидация и форматирование по контракту):
  • * Код:

        // Получаем сырое значение температуры из msg.payload

    let temp = parseFloat(msg.payload);

    // 1. Валидация

    if (isNaN(temp) || temp < -55 || temp > 125) {

    node.status({fill:"red", shape:"dot", text:"Invalid reading: " + msg.payload});

    // Генерируем ошибку, которую поймает узел Catch

    node.error("Некорректное значение температуры: " + msg.payload, msg);

    return null; // Останавливаем дальнейшее движение сообщения

    }

    // 2. Формирование сообщения по "Контракту"

    msg.payload = {

    value: temp,

    source: "28-01234567abcd", // ID датчика

    ts: Date.now(),

    unit: "°C"

    };

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

    msg.topic = "hi/office/room101/temperature";

    // 4. Обновление визуального статуса

    node.status({fill:"green", shape:"dot", text:"OK: " + temp + " °C"});

    return msg;

  • `mqtt out` (Публикация данных):
  • * Настроить подключение к MQTT-брокеру контроллера (обычно `localhost:1883`).

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

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

    ---

    Лабораторные работы

    COURSE-16-M02-LAB07: "Здравствуй, мир автоматизации!" — Управление реле по кнопке

    Цель: Создать базовый сценарий, в котором нажатие настенного выключателя (подключен как "сухой контакт") включает или выключает свет (подключен через реле контроллера). Оборудование: Поток (ASCII):

    `[rpi gpio in: UI-05]` -> `[Function: Toggle Logic]` -> `[rpi gpio out: RL-01]`

    Задание:
  • Создайте новый поток (вкладку) в Node-RED с именем "Lab 07 - Simple Light".
  • Добавьте узел `rpi gpio in` для чтения состояния входа `UI-05`. Настройте его на отслеживание нажатия (falling edge).
  • Добавьте узел `Function`. Внутри него реализуйте логику переключения. Используйте `flow context` для хранения текущего состояния света (`flow.get("light_state")`). При каждом нажатии инвертируйте состояние (`true` -> `false`, `false` -> `true`) и сохраняйте его обратно (`flow.set("light_state", ...)`).
  • Новое состояние (`true` или `false`) передавайте в `msg.payload`.
  • Добавьте узел `rpi gpio out` для управления реле `RL-01`.
  • Соедините узлы. Разверните (Deploy) поток.
  • Проверьте работу: каждое нажатие на выключатель должно менять состояние лампы.
  • Рубрика оценивания:

    COURSE-16-M02-LAB08: Обработка данных датчика и публикация в MQTT

    Цель: Реализовать поток из практического примера урока: опрос датчика температуры DS18B20, валидация, форматирование по контракту и отправка в MQTT. Оборудование: Задание:
  • Создайте поток `FLOW-SENS-TEMP-001` в точности как описано в уроке.
  • Настройте узел `ds18b20` на ID вашего реального датчика.
  • Скопируйте код для узла `Function`.
  • Настройте узел `mqtt out` для отправки данных на локальный MQTT-брокер.
  • Добавьте узел `Debug` после узла `Function`, чтобы видеть сформированное сообщение.
  • Разверните поток.
  • Проверьте результат:
  • * В панели отладки должны каждые 10 секунд появляться корректно сформированные JSON-объекты.

    * Узел `Function` должен показывать зеленый статус с текущей температурой.

    * С помощью MQTT-клиента (например, MQTT Explorer) подпишитесь на топик `hi/office/room101/temperature` и убедитесь, что данные поступают.

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

    ---

    Тест для самопроверки

    ID Квиза: `COURSE-16-M02-QUIZ`
  • Какой подход к программированию является основным на платформе HI?
  • * a) Объектно-ориентированное программирование на C++.

    * b) Визуальное программирование потоков в Node-RED.

    * c) Функциональное программирование на Rust.

    * d) Написание скриптов на Python.

  • Что такое "Контракт сообщения" в контексте Академии HI?
  • * a) Способ шифрования данных между узлами.

    * b) Стандартный формат JSON для `msg.payload` для обеспечения предсказуемости данных.

    * c) Лицензионное соглашение на использование узлов.

    * d) Документ, описывающий логику потока.

  • Для чего используется узел `Function` в Node-RED?
  • * a) Для написания сложной логики на JavaScript, когда стандартных узлов недостаточно.

    * b) Для определения математических функций.

    * c) Для запуска внешних программ.

    * d) Для объявления глобальных переменных.

  • Какой инструмент является главным для отладки потоков в Node-RED?
  • * a) Вкладка "Информация".

    * b) Внешний отладчик Visual Studio Code.

    * c) Панель отладки (Debug) в боковой панели.

    * d) Консоль браузера.

  • Где следует хранить состояние устройства (например, включен свет или выключен), чтобы оно сохранилось после перезагрузки контроллера?
  • * a) В `msg.payload`.

    * b) В `global context` без персистентности.

    * c) В `flow context`, настроенном на сохранение в файловую систему.

    * d) В переменной внутри узла `Function`.

  • Какой узел используется для перехвата ошибок от других узлов?
  • * a) `Try`.

    * b) `Error`.

    * c) `Status`.

    * d) `Catch`.

  • Что означает красный кружок в статусе узла?
  • * a) Узел неактивен.

    * b) Узел выполняет операцию.

    * c) В узле произошла ошибка.

    * d) Узел успешно завершил работу.

  • Что произойдет, если узел `Function` вернет `null`?
  • * a) Произойдет ошибка, которую поймает `Catch`.

    * b) Сообщение `msg` остановит свое движение по потоку.

    * c) Весь поток будет перезапущен.

    * d) Будет отправлено пустое сообщение.

  • В примере с датчиком температуры, зачем нужна валидация `if (isNaN(temp) || temp < -55 || temp > 125)`?
  • * a) Чтобы убедиться, что датчик не перегрелся.

    * b) Чтобы отсеять заведомо ложные или ошибочные показания (например, при обрыве связи).

    * c) Это требование протокола MQTT.

    * d) Для преобразования градусов Цельсия в Фаренгейты.

  • Какая команда используется для сохранения значения в контексте потока?
  • * a) `flow.save("key", value)`

    * b) `context.set("key", value)`

    * c) `flow.set("key", value)`

    * d) `msg.context.key = value`

    ---

    Мини-runbook "Если что-то не работает"

    📋 Чек-лист для быстрой диагностики:

  • Поток не запускается:
  • * Проверьте конфигурацию узла-триггера (`Inject`, `rpi gpio in`, `mqtt in`). Правильно ли указан пин, топик, интервал?

    * Нажмите кнопку "Deploy" (Развернуть). Возможно, вы забыли применить изменения.

  • Данные в `Debug` некорректны:
  • * Проверьте "Контракт сообщения". Убедитесь, что узел, который вы отлаживаете, получает `msg` в ожидаемом формате.

    * Если это узел `Function`, добавьте `node.warn(msg)` в начало кода, чтобы увидеть, что приходит на вход.

    * Проверьте физическое подключение датчика. Возможно, он неисправен или плохо подключен.

  • Узел показывает ошибку (красный статус):
  • * Подключите узел `Catch` к узлу `Debug` (настроенному на вывод полного объекта `msg`). Разверните поток и спровоцируйте ошибку. В панели отладки вы увидите подробное описание проблемы.

    * Частые причины: неверный IP-адрес в узле MQTT/Modbus, обрыв связи, некорректный ID датчика.

  • Реле не щелкает / Устройство не реагирует:
  • * Проверьте правильность пина, указанного в узле `rpi gpio out`.

    * Проверьте схему подключения (`WIRING-`...). Подается ли питание на исполнительное устройство? Не перепутаны ли клеммы?

    * Временно замените узел `rpi gpio out` на `Debug`. Убедитесь, что на его вход приходит правильное значение (`true`/`false` или `1`/`0`).

  • Состояние не сохраняется после перезагрузки:
  • * Убедитесь, что вы используете `flow context` или `global context`.

    * Проверьте файл `settings.js` в директории Node-RED. Убедитесь, что `contextStorage` настроен на использование файловой системы (`localfilesystem`) или MySQL.