ГлавнаяАкадемияИсполнительные устройства: интерлоки, таймауты → Шаблон 'Импульс' (Pulse)

Шаблон 'Импульс' (Pulse)

Урок 1 · Исполнительные устройства: интерлоки, таймауты · 30 мин · theory

Введение в шаблон 'Импульс'

Шаблон проектирования 'Импульс' (Pulse) — это фундаментальная техника в автоматизации, предназначенная для кратковременной подачи управляющего сигнала на устройство с целью его активации. В отличие от шаблона 'Включено/Выключено' (On/Off), который мы детально разобрали в предыдущем уроке (LESSON-05-M02-L01), 'Импульс' не подразумевает сохранение состояния со стороны контроллера. Контроллер лишь инициирует действие, а дальнейшая логика работы (например, открытие ворот до концевого выключателя и последующее закрытие по второму импульсу) реализуется на стороне самого исполнительного устройства.

Основное отличие от шаблона 'On/Off' можно представить в следующей таблице:

| Характеристика | Шаблон 'On/Off' (Включено/Выключено) | Шаблон 'Pulse' (Импульс) |

| ----------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------- |

| Управление состоянием | Контроллер хранит и отслеживает состояние (Stateful). | Контроллер не хранит состояние (Stateless, "fire and forget"). |

| Длительность сигнала | Сигнал подается постоянно до получения команды на отключение. | Сигнал подается кратковременно (например, 200-500 мс). |

| Логика устройства | Устройство пассивно (например, обычное реле, которое замыкает цепь). | Устройство имеет встроенную логику (контроллер ворот, диммер). |

| Аналогия | Выключатель света с фиксацией. | Кнопка дверного звонка или кнопка на пульте от ворот (без фиксации). |

> 💡 Подсказка: Импульсный режим идеален для устройств, которые имеют собственную логику управления и требуют лишь сигнала для старта (например, контроллер ворот, который сам обрабатывает открытие и закрытие).

Типичные сценарии применения

Шаблон 'Импульс' незаменим во множестве практических задач на объектах автоматизации:

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

---

Базовая реализация в Node-RED: узел 'Trigger'

Основным и наиболее удобным инструментом для реализации шаблона 'Импульс' в среде Node-RED является узел `Trigger`. Этот узел позволяет создать последовательность из двух сообщений, разделенных заданным интервалом времени, что в точности имитирует нажатие и отпускание физической кнопки.

Настройка узла 'Trigger'

При добавлении узла `Trigger` на рабочую область его настройки выглядят предельно просто и логично:

  • Send (Отправить): Здесь указывается полезная нагрузка (`payload`) первого сообщения, которое будет отправлено немедленно после получения узлом входящего сигнала. Для управления реле это обычно значение `1` или `true` (команда "Включить").
  • then wait for (затем подождать): Здесь выставляется длительность импульса в миллисекундах, секундах, минутах или часах. Для большинства задач, таких как управление воротами, достаточно `250` - `500` миллисекунд.
  • then send (затем отправить): В этом поле определяется `payload` второго сообщения, которое будет отправлено по истечении указанной задержки. Как правило, это значение `0` или `false` (команда "Выключить").
  • Дополнительно, узел имеет опцию "Extend delay if new message arrives", которая позволяет сбрасывать таймер при получении нового сообщения, но в базовом сценарии импульса она обычно не используется.

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

    Создадим простейший поток для демонстрации работы узла `Trigger`. Он будет состоять всего из трех узлов: `Inject` -> `Trigger` -> `Debug`.

  • Узел `Inject`: Оставьте его настройки по умолчанию. Он будет служить ручным триггером для запуска нашего импульса.
  • Узел `Trigger`: Настройте его следующим образом:
  • * Send: `Number` `1`

    * then wait for: `250` `milliseconds`

    * then send: `Number` `0`

  • Узел `Debug`: Подключите его к выходу узла `Trigger` для отображения исходящих сообщений.
  • ASCII-схема потока:
    +----------+      +-------------------+      +---------+
    

    | Inject |----->| Trigger (250ms) |----->| Debug |

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

    После развертывания потока (Deploy) и однократного нажатия на кнопку узла `Inject`, вы увидите в панели отладки (Debug) следующую картину:

  • Немедленно появится сообщение с `payload: 1`.
  • Через 250 миллисекунд появится второе сообщение с `payload: 0`.
  • // Сообщение 1 (t = 0ms)
    

    {

    "payload": 1,

    "_msgid": "a1b2c3d4.e5f6g7"

    }

    // Сообщение 2 (t = 250ms)

    {

    "payload": 0,

    "_msgid": "a1b2c3d4.e5f6g7"

    }

    > ℹ️ Информация: Обратите внимание, что `_msgid` у обоих сообщений одинаковый. Узел `Trigger` генерирует второе сообщение на основе того, которое его активировало, изменяя только `payload`. Это полезно для сохранения `topic` и других свойств исходного сообщения.

    Этот простой пример является ядром шаблона 'Импульс'. Мы научились генерировать последовательность "включить, подождать, выключить", которая является цифровым эквивалентом нажатия кнопки.

    ---

    Практический пример: управление реле для открытия ворот

    Теперь применим полученные знания для решения реальной задачи — управления автоматическими воротами с помощью реле на контроллере HI. Предположим, что контроллер ворот имеет вход "сухого контакта" (Dry Contact), замыкание которого инициирует цикл работы ворот. Мы будем замыкать этот контакт с помощью одного из реле контроллера.

    > ⚠️ Внимание: Перед подключением к реальному оборудованию убедитесь, что его управляющий вход рассчитан на тот тип сигнала, который вы подаете с реле. Неправильное подключение может повредить контроллер ворот или сам модуль реле. В данном случае мы имитируем "сухой контакт", поэтому на клеммы реле контроллера мы заводим не силовое напряжение, а управляющие провода от входа контроллера ворот.

    Построение потока в Node-RED

    Для управления реле, подключенным к нашему контроллеру HI, мы будем использовать протокол MQTT. Стандартная архитектура платформы HI предполагает, что каждое реле представлено в виде MQTT-топика.

    Предположим, нам нужно управлять первым реле (`K1`) на модуле расширения `wb-mr6c` с MQTT-идентификатором `wb-mr6c_3`. Согласно стандарту топиков платформы, для этого используется топик `.../on`.

    Наш поток будет выглядеть так:

  • `MQTT In`: Узел, подписанный на топик `hi/gates/main/set`. Он будет принимать команды из пользовательского интерфейса (например, `"OPEN"`).
  • `Trigger`: Узел, генерирующий импульс `1` -> `0` с задержкой `300ms`.
  • `MQTT Out`: Узел, который публикует сообщения от `Trigger` в топик реле.
  • ASCII-схема потока:
    +-----------+      +-------------------+      +-------------------------------+
    

    | MQTT In |----->| Trigger (300ms) |----->| MQTT Out |

    | gates/set | | Send 1, then 0 | | Topic: .../controls/K1/on |

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

    Детальная настройка: * Topic: `hi/gates/main/set`

    * QoS: `1`

    * Output: `a string`

    * Send: `Number` `1`

    * then wait for: `300` `milliseconds`

    * then send: `Number` `0`

    * Handle multiple messages: `ignore subsequent messages` (Важно, чтобы несколько быстрых нажатий не создавали очередь импульсов).

    * Topic: `hi/devices/wb-mr6c_3/controls/K1/on`

    * QoS: `1`

    * Retain: `false`

    Логика работы потока

  • Пользователь в мобильном приложении нажимает кнопку "Открыть ворота". Приложение публикует сообщение `"OPEN"` в топик `hi/gates/main/set`.
  • Узел `MQTT In` получает это сообщение и передает его дальше. Содержимое `payload` (`"OPEN"`) в данном случае не имеет значения, важен сам факт прихода сообщения.
  • Узел `Trigger` немедленно активируется и отправляет на свой выход первое сообщение:
  •     { "payload": 1 }

  • Узел `MQTT Out` получает это сообщение и публикует значение `1` в топик `hi/devices/wb-mr6c_3/controls/K1/on`. Физическое реле `K1` на контроллере HI замыкается, имитируя нажатие кнопки. Контроллер ворот регистрирует замыкание и начинает движение створки.
  • Проходит 300 миллисекунд.
  • Узел `Trigger` автоматически отправляет второе сообщение:
  •     { "payload": 0 }

  • Узел `MQTT Out` получает его и публикует значение `0` в тот же топик. Физическое реле `K1` размыкается, имитируя отпускание кнопки.
  • Для контроллера ворот импульс завершен. Он продолжает выполнять свою программу (открытие до концевика).
  • Таким образом, мы создали надежный и простой механизм управления сложным устройством, не вдаваясь в его внутреннюю логику, а лишь подавая стандартный для него управляющий сигнал.

    ---

    Продвинутые сценарии: импульсы переменной длительности

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

    Узел `Trigger` элегантно решает эту задачу. Его длительность задержки можно задавать не статически, а динамически, через свойство `delay` входящего сообщения.

    Динамическое управление узлом 'Trigger'

    Если в сообщении, приходящем на вход узла `Trigger`, присутствует свойство `msg.delay`, содержащее числовое значение в миллисекундах, узел будет использовать именно это значение для задержки, игнорируя то, что указано в его статической конфигурации.

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

    Пример потока с узлом `Function`:

    Предположим, из интерфейса приходят команды в виде JSON:

    ASCII-схема:
    [MQTT In] ---> [JSON Parser] ---> [Function: Set Delay] ---> [Trigger] ---> [MQTT Out]
    
    Код для узла `Function: Set Delay`:
    // Получаем объект из msg.payload
    

    const command = msg.payload;

    // Устанавливаем длительность импульса по умолчанию

    let pulseDuration = 250; // ms, для шагового режима

    if (command && command.action === 'full') {

    // Для полного открытия установим более длинный импульс

    pulseDuration = 1000; // ms

    }

    // Устанавливаем динамическую задержку для узла Trigger

    msg.delay = pulseDuration;

    // Устанавливаем payload для первого сигнала триггера

    // Мы можем переопределить здесь и payload, если нужно

    msg.payload = 1;

    // Устанавливаем статус для визуальной отладки

    node.status({

    fill: "blue",

    shape: "dot",

    text: "Pulse: " + pulseDuration + "ms"

    });

    return msg;

    В настройках `Trigger` теперь можно оставить задержку по умолчанию, но он будет переопределяться значением из `msg.delay`. Это мощный механизм для создания контекстно-зависимого поведения.

    Альтернативный подход: узел `Delay`

    Хотя `Trigger` является предпочтительным, создать импульс можно и с помощью связки из двух узлов `Change` и одного узла `Delay`:

  • `Change` #1: Устанавливает `msg.payload` в `1`.
  • `Delay`: Вносит задержку на `N` мс.
  • `Change` #2: Устанавливает `msg.payload` в `0`.
  • Этот поток требует разветвления, чтобы отправить и первое, и второе сообщение.

    ASCII-схема:
               +--> [Change: set 1] ----------------------------------+
    

    | |

    [Inject] --+--> [Delay: 250ms] --> [Change: set 0] --> [Join] --> [Debug]

    ^

    |

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

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

    ---

    Практический пример: пошаговое управление приводом шторы (DALI/RS-485)

    Рассмотрим сложный, но очень распространенный сценарий: управление шторой или жалюзи, подключенными по шине DALI или Modbus (RS-485). Такие приводы часто поддерживают несколько команд:

    Задача: реализовать управление с помощью кнопок в интерфейсе "Вверх", "Вниз", "Стоп". При коротком нажатии на "Вверх"/"Вниз" отправлять команду `STEP_UP`/`STEP_DOWN`. При удержании — `UP`/`DOWN`, а при отпускании — `STOP`.

    Наш контроллер HI инкапсулирует сложность работы с шинами DALI/RS-485. Мы будем отправлять простые JSON-команды в MQTT-топик, а системный сервис контроллера уже сам преобразует их в команды для физической шины.

    Командный топик: `hi/blinds/living_room/set` Формат `msg.payload`:
    { "command": "UP" }
    

    { "command": "STEP_UP" }

    { "command": "STOP" }

    Для реализации нам понадобятся узлы, отслеживающие "нажатие" и "отпускание" кнопки в UI. В Node-RED Dashboard это можно сделать с помощью узла `ui_button`, который может отправлять разные сообщения при нажатии (`mousedown`) и отпускании (`mouseup`).

    Логика потока:
  • При событии `mousedown` от кнопки "Вверх" мы засекаем время.
  • При событии `mouseup` мы проверяем, сколько времени прошло.
  • Если прошло меньше, например, 400 мс — это короткое нажатие. Отправляем импульсную команду `STEP_UP`.
  • Если `mouseup` не пришло за 400 мс (т.е. кнопка удерживается), мы отправляем команду `UP`. А когда `mouseup` наконец придет, мы отправим команду `STOP`.
  • Для предотвращения отправки множества команд `UP` во время удержания, мы используем узел `rbe` (report-by-exception), который блокирует все сообщения, пока их `payload` не изменится.

    Ниже представлен полный JSON-код потока для импорта в Node-RED, который реализует эту логику для одной кнопки "Вверх". Аналогичный поток создается и для кнопки "Вниз".

    JSON для импорта в Node-RED:
    [
    

    {

    "id": "e2a3b4c5.d6e7f8",

    "type": "comment",

    "z": "...",

    "name": "FLOW-BLINDS-STEP-001: Stepped Blind Control",

    "info": "Реализует логику короткого/длинного нажатия для управления шторой.\nКороткое нажатие (<400мс): STEP_UP\nДлинное нажатие (>400мс): UP, затем STOP при отпускании."

    },

    {

    "id": "a1b2c3d4.e5f6g7",

    "type": "ui_button",

    "z": "...",

    "name": "Штора Вверх",

    "group": "...",

    "order": 1,

    "width": 0,

    "height": 0,

    "passthru": false,

    "label": "Вверх",

    "tooltip": "",

    "color": "",

    "bgcolor": "",

    "icon": "arrow_upward",

    "payload": "press",

    "payloadType": "str",

    "topic": "up",

    "topicType": "str",

    "x": 150,

    "y": 200,

    "wires": [

    [

    "f9e8d7c6.b5a4b3"

    ]

    ]

    },

    {

    "id": "f9e8d7c6.b5a4b3",

    "type": "trigger",

    "z": "...",

    "name": "Таймер 400мс",

    "op1": "long_press",

    "op2": "release",

    "op1type": "str",

    "op2type": "str",

    "duration": "400",

    "extend": true,

    "override": "op2",

    "units": "ms",

    "reset": "release",

    "bytopic": "all",

    "topic": "topic",

    "outputs": 2,

    "x": 330,

    "y": 200,

    "wires": [

    [

    "c3d4e5f6.a7b8c9"

    ],

    [

    "g7h8i9j0.k1l2m3"

    ]

    ]

    },

    {

    "id": "c3d4e5f6.a7b8c9",

    "type": "change",

    "z": "...",

    "name": "Команда 'UP'",

    "rules": [

    {

    "t": "set",

    "p": "payload",

    "pt": "msg",

    "to": "{\"command\":\"UP\"}",

    "tot": "json"

    }

    ],

    "action": "",

    "property": "",

    "from": "",

    "to": "",

    "reg": false,

    "x": 510,

    "y": 180,

    "wires": [

    [

    "n4o5p6q7.r8s9t0"

    ]

    ]

    },

    {

    "id": "g7h8i9j0.k1l2m3",

    "type": "switch",

    "z": "...",

    "name": "Проверка таймера",

    "property": "payload",

    "propertyType": "msg",

    "rules": [

    {

    "t": "eq",

    "v": "release",

    "vt": "str"

    },

    {

    "t": "else"

    }

    ],

    "checkall": "true",

    "repair": false,

    "outputs": 2,

    "x": 520,

    "y": 260,

    "wires": [

    [

    "u1v2w3x4.y5z6a7"

    ],

    []

    ]

    },

    {

    "id": "u1v2w3x4.y5z6a7",

    "type": "change",

    "z": "...",

    "name": "Команда 'STEP_UP'",

    "rules": [

    {

    "t": "set",

    "p": "payload",

    "pt": "msg",

    "to": "{\"command\":\"STEP_UP\"}",

    "tot": "json"

    }

    ],

    "action": "",

    "property": "",

    "from": "",

    "to": "",

    "reg": false,

    "x": 720,

    "y": 240,

    "wires": [

    [

    "b8c9d0e1.f2g3h4"

    ]

    ]

    },

    {

    "id": "b8c9d0e1.f2g3h4",

    "type": "mqtt out",

    "z": "...",

    "name": "Отправить команду на штору",

    "topic": "hi/blinds/living_room/set",

    "qos": "1",

    "retain": "false",

    "broker": "...",

    "x": 950,

    "y": 220,

    "wires": []

    },

    {

    "id": "n4o5p6q7.r8s9t0",

    "type": "trigger",

    "z": "...",

    "name": "STOP при отпускании",

    "op1": "",

    "op2": "{\"command\":\"STOP\"}",

    "op1type": "pay",

    "op2type": "json",

    "duration": "-1",

    "extend": false,

    "override": "op2",

    "units": "s",

    "reset": "release",

    "bytopic": "all",

    "topic": "topic",

    "outputs": 2,

    "x": 730,

    "y": 180,

    "wires": [

    [

    "i5j6k7l8.m9n0o1",

    "b8c9d0e1.f2g3h4"

    ],

    [

    "b8c9d0e1.f2g3h4"

    ]

    ]

    },

    {

    "id": "i5j6k7l8.m9n0o1",

    "type": "rbe",

    "z": "...",

    "name": "Блокировать дубликаты",

    "func": "rbe",

    "gap": "",

    "start": "",

    "inout": "out",

    "septopics": false,

    "property": "payload.command",

    "x": 930,

    "y": 140,

    "wires": [[]]

    }

    ]

    Этот пример демонстрирует, как комбинация простых узлов (`Trigger`, `Change`, `Switch`) и правильная логика позволяют реализовать сложный и интуитивно понятный для пользователя интерфейс управления.

    ---

    Итоги и лучшие практики

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

    Краткие выводы:

    Советы по отладке и документированию

  • Всегда используйте `Debug`: При настройке потока с узлом `Trigger`, всегда подключайте к его выходу узел `Debug`. Это позволит вам визуально убедиться, что генерируются оба сообщения (`1` и `0`) и что временной интервал между ними соответствует вашим ожиданиям.
  • Комментируйте логику: Потоки с импульсами, особенно с динамической длительностью, могут быть неочевидны для другого инженера. Используйте узел `Comment` для описания назначения импульса и логики его работы. Например: `"Импульс 250мс для контроллера ворот Came. Имитация кнопки F2"`.
  • Проверяйте физическое устройство: Прежде чем винить Node-RED, убедитесь, что само исполнительное устройство (контроллер ворот, замок) настроено на работу в импульсном режиме. Часто в них есть физические переключатели или программные настройки режима работы входа.
  • Шаблон 'Импульс', несмотря на свою простоту, является мощным инструментом, который отделяет профессиональную систему автоматизации от любительской. Его правильное применение обеспечивает предсказуемость, надежность и удобство для конечного пользователя.

    Что дальше

    > 🔗 Связанный материал: В следующем уроке (LESSON-05-M02-L03) мы рассмотрим шаблон 'Timed' (Работа по таймеру), который расширяет концепцию управления по времени, но применяется для других задач, нежели 'Импульс'. Мы научимся создавать сценарии, где нагрузка должна быть включена на строго определенный, часто длительный промежуток времени, например, управление поливом, вентиляцией или подогревом.