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

Влияние IoT на окружающую среду: от теории к практике на платформе HI

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

COURSE-16-M05-L07 — Влияние IoT на окружающую среду: от теории к практике на платформе HI

Введение

Интернет вещей (IoT) является мощным инструментом, способным оказывать как положительное, так и отрицательное воздействие на окружающую среду. С одной стороны, IoT-системы позволяют осуществлять точный мониторинг, рационально использовать ресурсы и снижать выбросы парниковых газов. С другой — массовое внедрение электроники порождает вызовы, связанные с энергопотреблением и утилизацией электронных отходов.

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

Положительное воздействие: Практическая реализация на платформе HI

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

1. Мониторинг качества воздуха и управление вентиляцией

Задача: Автоматически поддерживать здоровый микроклимат в офисном помещении, управляя приточной вентиляцией на основе концентрации CO₂. Это позволяет не только улучшить самочувствие сотрудников, но и экономить электроэнергию, избегая избыточной работы вентиляционной установки. Оборудование: Схема подключения: WIRING-ENV-AIR-001

📋 Чек-лист подключения:

  • Подключить датчик к шине RS-485 контроллера (клеммы A, B, GND).
  • Подать на датчик питание 24V DC.
  • Установить на датчике уникальный Modbus ID (например, `15`).
  • Если датчик — последнее устройство на шине, активировать на нем терминальный резистор 120 Ом.
  • Подключить управляющую цепь вентилятора к реле контроллера (например, `RL-10`).
  • //============ WIRING-ENV-AIR-001: Office Air Quality Control ==============
    

    // Используется кабель "витая пара" UTP Cat5e с экраном

    [CTRL:HI-Core] (SENS:CO2-01: Modbus ID 15)

    (RS485-1)

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

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

    A ---A------ (Зеленый) ---- A

    B ---B------ (Белый) ----- B

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

    |

    (Экран) -------- GND (только со стороны контроллера)

    // Управление вентилятором (230V AC)

    [CTRL:HI-Core]

    ~L~ (из щита) --- C (RL-10)

    NO (RL-10) --- ~L~ -------- L (Вентилятор)

    ~N~ (из щита) ------------------------------ N (Вентилятор)

    Логика в Node-RED: FLOW-ENV-AIR-001

    Поток реализует опрос датчика, анализ данных и управление реле.

    // Flow: Опрос датчика CO2 и управление вентиляцией
    

    //

    // [Inject: 1 min] -> [Modbus-Read] -> [Function: Validate & Format] -> [Switch: Check CO2] --+

    // |

    // [Catch] -> [Function: Log Error] -> [mqtt out: errors] |

    // |

    // +--------------------------------< (CO2 > 1000 ppm) >-- [Change: ON] --> [rpi gpio out: RL-10]

    // |

    // +--------------------------------< (CO2 < 800 ppm) >--- [Change: OFF] --> [rpi gpio out: RL-10]

    Реализация в Node-RED:
  • Опрос датчика: Узел `Modbus-Read` настраивается на периодический (раз в минуту) опрос регистра, содержащего значение CO₂ (например, Holding Register `40101`, адрес `100`).
  • * Конфигурация Modbus-Client: Убедитесь, что настроен Modbus RTU клиент на соответствующий COM-порт контроллера HI (`/dev/ttyS1` или `/dev/ttyUSB0`) с правильными параметрами скорости, четности и стоп-битов (например, 9600, 8N1).

    * Конфигурация Modbus-Read:

    * `Server`: Выбрать настроенный Modbus RTU клиент.

    * `Unit-ID`: `15` (или тот, что установлен на датчике).

    * `FC`: `FC 3: Read Holding Registers`.

    * `Address`: `100` (или соответствующий адрес CO₂ по карте регистров датчика).

    * `Quantity`: `1`.

    * `Polling interval`: `60` seconds.

  • Валидация и контракт сообщения: Узел `Function` принимает данные от Modbus-узла и приводит их к стандартному контракту.
  •     // Узел: "Validate & Format CO2"

    // Вход: msg.payload от Modbus-узла, например { data: [1150], buffer: }

    // Выход: msg с payload в стандартном контракте

    // Контракт сообщения: { "value": 1150, "unit": "ppm", "source": "modbus-sensor-office-1", "ts": 1678886400000 }

    if (!msg.payload || !Array.isArray(msg.payload.data) || msg.payload.data.length === 0) {

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

    node.error("Некорректный формат данных от Modbus-датчика", msg);

    return null; // Прервать поток

    }

    const co2_level = msg.payload.data[0];

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

    if (isNaN(co2_level) || co2_level < 400 || co2_level > 5000) { // Типичные диапазоны CO2

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

    node.error("Некорректное значение CO2: " + co2_level, msg);

    return null; // Прервать поток

    }

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

    msg.payload = {

    value: co2_level,

    unit: "ppm",

    source: "modbus-sensor-office-1",

    ts: Date.now()

    };

    msg.topic = "telemetry/office/air_quality/co2"; // Для маршрутизации

    // 3. Визуальный статус

    node.status({fill:"green", shape:"dot", text: co2_level + " ppm"});

    return msg;

  • Логика управления: Узел `Switch` проверяет `msg.payload.value`:
  • * `Property`: `msg.payload.value`

    * `Rule 1`: `is greater than` `1000` (выход 1)

    * `Rule 2`: `is less than` `800` (выход 2)

    * Узел `Change` (для включения вентиляции):

    * `Set`: `msg.payload`

    * `to the value`: `true` (boolean)

    * `Set`: `msg.topic`

    * `to the value`: `commands/fan/office/set`

    * Узел `Change` (для выключения вентиляции):

    * `Set`: `msg.payload`

    * `to the value`: `false` (boolean)

    * `Set`: `msg.topic`

    * `to the value`: `commands/fan/office/set`

    * Узел `rpi gpio out` (для управления реле):

    * `Pin`: `RL-10` (или соответствующий номер реле).

    * `Type`: `Digital output`.

    * `Initial state`: `Low`.

    * `msg.payload` должен быть `true`/`false`.

  • Обработка ошибок: Узел `Catch` перехватывает ошибки связи с Modbus-датчиком и отправляет уведомление в системный журнал (например, в MySQL или MQTT-топик `hi/system/logs/error`).
  • * Узел `Catch`: Настроен на перехват ошибок от всех узлов на вкладке.

    * Узел `Function: Log Error`:

            // Форматирование ошибки для логирования по контракту

    // Контракт сообщения об ошибке: { "error_message": "...", "node_id": "...", "node_name": "...", "original_msg": {...}, "timestamp": ... }

    msg.payload = {

    error_message: msg.error.message,

    node_id: msg.error.node.id,

    node_name: msg.error.node.name,

    original_msg: msg.error, // Сохраняем оригинальное сообщение об ошибке для полной диагностики

    timestamp: Date.now()

    };

    msg.topic = "hi/system/logs/error";

    return msg;

    * Узел `mqtt out`: Публикует отформатированное сообщение об ошибке в топик `hi/system/logs/error`.

    2. Рациональное управление водными ресурсами

    Задача: Создать систему автоматического полива газона, которая учитывает не только влажность почвы, но и прогноз погоды, чтобы избежать бесполезного расхода воды перед дождем. Оборудование: Схема подключения: WIRING-WATER-003
    //============ WIRING-WATER-003: Smart Irrigation System ==============
    
    

    // Датчик влажности почвы (аналоговый 0-10V)

    [CTRL:HI-Core] (SENS:SOIL-01)

    +24V (от PSU) ---- (Красный) ----------------- V+

    GND (от PSU) ----- (Черный) ----------------- GND

    (UI-05) --- (Оранжевый) ---- OUT (0-10V)

    (GND) ----- (Черный) ------- GND (общий с сигналом)

    // Электромагнитный клапан (24V DC)

    [PSU:24VDC] [CTRL:HI-Core]

    +24V --(+)------- C (RL-12)

    NO (RL-12) ---------------- + (Клапан)

    GND --(-)----------------------------------- - (Клапан)

    Логика в Node-RED: FLOW-WATER-IRRIG-002 (Паттерн "Конечный автомат")

    Этот поток использует `flow context` для хранения состояния системы (`IDLE`, `WATERING`, `WAITING_DUE_TO_RAIN`).

    // Flow: Умный полив газона с учетом прогноза погоды
    

    //

    // [Inject: 1 hour] -> [Function: Prepare Data] --+--> [HTTP Request: Weather API] --+

    // | |

    // +--> [rpi gpio in: UI-05] --------+--> [Join: Weather & Soil] --> [Function: FSM Irrigation Logic] --> [rpi gpio out: RL-12]

    // |

    // +--> [Debug]

    //

    // [Catch] -> [Function: Log Error] -> [mqtt out: errors]

  • Сбор данных:
  • * Узел `Inject` раз в час запускает проверку.

    * Узел `Function: Prepare Data`:

            // Подготавливает msg для запроса погоды и чтения датчика

    // Контракт сообщения: { "url": "...", "payload": {} }

    msg.url = "http://api.openweathermap.org/data/2.5/weather?lat=55.75&lon=37.61&appid=YOUR_API_KEY&units=metric"; // Замените на реальные координаты и ключ API

    msg.payload = {}; // Инициализируем payload

    return msg;

    * Узел `HTTP Request`: Отправляет запрос к бесплатному погодному API (например, OpenWeatherMap) для получения прогноза на ближайшие 3 часа.

    * `Method`: `GET`

    * `URL`: `msg.url`

    * `Return`: `a parsed JSON object`

    * `Name`: "Get Weather Forecast"

    * Выход `msg.payload` будет содержать JSON-объект с данными о погоде.

    * Узел `rpi gpio in` (для датчика влажности):

    * `Pin`: `UI-05` (универсальный вход).

    * `Type`: `Analog input (0-10V)`.

    * `Name`: "Датчик влажности почвы".

    * Выход `msg.payload` будет содержать значение от 0 до 1023 (для 10-битного АЦП) или 0-4095 (для 12-битного). Необходимо будет масштабировать в проценты.

    * Узел `Join: Weather & Soil`: Объединяет сообщения от погодного API и датчика влажности.

    * `Mode`: `manual`

    * `Property`: `msg.topic`

    * `Output`: `a single message`

    * `Join`: `msg.payload`

    * `Timeout`: `5` seconds (если одно из сообщений не пришло).

    * `Name`: "Join Weather & Soil"

    * Важно: Для корректной работы, `msg.topic` от `HTTP Request` и `rpi gpio in` должны быть разными, чтобы `Join` мог их различить. Например, `msg.topic = "weather"` и `msg.topic = "soil"`.

  • Конечный автомат (FSM) в узле `Function`:
  •     // Узел: "FSM: Irrigation Logic"

    // Хранит состояние в flow.irrigation_state

    // Вход: msg.payload = { weather: {...}, soil_raw: 512 } (после Join)

    // Контракт исходящего сообщения: { "value": true/false, "source": "irrigation_fsm", "ts": ... }

    const DRY_THRESHOLD_PERCENT = 30; // % влажности, ниже которого нужно поливать

    const RAIN_FORECAST_THRESHOLD_MM = 0.5; // мм осадков, при которых отменяем полив

    const ANALOG_MAX_VALUE = 1023; // Максимальное значение АЦП для UI-05 (зависит от контроллера)

    let state = flow.get("irrigation_state") || "IDLE";

    let soil_raw_value = msg.payload.soil_raw; // Предполагаем, что Join объединил как { "soil_raw": ..., "weather": ... }

    let weather_data = msg.payload.weather;

    // Масштабирование аналогового значения в проценты влажности (пример)

    // Предполагаем, что 0V = 0% влажности, 10V = 100% влажности

    let soil_moisture_percent = (soil_raw_value / ANALOG_MAX_VALUE) * 100;

    // Проверка прогноза дождя

    let is_rain_forecasted = false;

    if (weather_data && weather_data.rain && weather_data.rain['1h'] > RAIN_FORECAST_THRESHOLD_MM) {

    is_rain_forecasted = true;

    }

    let output_command = "OFF"; // По умолчанию выключено

    switch (state) {

    case "IDLE":

    if (soil_moisture_percent < DRY_THRESHOLD_PERCENT && !is_rain_forecasted) {

    state = "WATERING";

    output_command = "ON";

    node.warn("Почва сухая, дождя нет. Начинаем полив.");

    } else if (is_rain_forecasted) {

    state = "WAITING_DUE_TO_RAIN";

    node.warn("Прогнозируется дождь. Откладываем полив.");

    }

    break;

    case "WATERING":

    // Логика остановки полива:

    // 1. Если влажность достигла нужного уровня (например, 70%)

    // 2. Или если полив длится слишком долго (например, 30 минут - нужен таймер, реализуется отдельным узлом)

    // 3. Или если вдруг пошел дождь

    if (soil_moisture_percent >= (DRY_THRESHOLD_PERCENT + 10) || is_rain_forecasted) { // Гистерезис

    state = "IDLE";

    output_command = "OFF";

    node.warn("Полив завершен или отменен из-за дождя.");

    } else {

    output_command = "ON"; // Продолжаем полив

    }

    break;

    case "WAITING_DUE_TO_RAIN":

    if (!is_rain_forecasted) {

    state = "IDLE"; // Дождя больше не ожидается, возвращаемся к проверке

    node.warn("Дождь не прогнозируется. Возвращаемся в IDLE.");

    }

    break;

    }

    flow.set("irrigation_state", state); // Сохраняем текущее состояние

    node.status({text: `State: ${state}, Soil: ${soil_moisture_percent.toFixed(1)}%, Rain: ${is_rain_forecasted ? 'Yes' : 'No'}`});

    // Формируем сообщение для управления клапаном по контракту

    msg.payload = {

    value: (output_command === "ON"), // true/false

    source: "irrigation_fsm",

    ts: Date.now()

    };

    msg.topic = "commands/irrigation/valve/set";

    return msg;

    * Важно: Для корректной работы FSM, узел `Function` должен получать оба значения (влажность и прогноз погоды) в одном `msg`. Используйте узел `Join` для объединения сообщений от датчика и HTTP-запроса.

  • Исполнение: В зависимости от `msg.payload.value` (`true`/`false`) срабатывает узел управления реле `RL-12`.
  • * Узел `rpi gpio out` (для управления клапаном):

    * `Pin`: `RL-12` (или соответствующий номер реле).

    * `Type`: `Digital output`.

    * `Initial state`: `Low`.

    * `msg.payload` должен быть `true`/`false`.

    Экологические вызовы и методы их минимизации на платформе HI

    Инженерное проектирование должно учитывать и минимизировать негативное влияние IoT.

    Увеличение количества электронных отходов (E-waste)

    ⚠️ Проблема: Короткий жизненный цикл многих IoT-устройств приводит к их быстрому устареванию и выбросу.

    💡 Решение на платформе HI:

    Повышенное энергопотребление

    ⚠️ Проблема: Миллиарды подключенных устройств потребляют электроэнергию, увеличивая углеродный след.

    💡 Решение на платформе HI:

    Заключение

    Ответственный подход к проектированию IoT-систем позволяет превратить их в эффективный инструмент для создания устойчивой и экологичной среды. Инженер, использующий платформу HI, обладает всеми необходимыми средствами для реализации "зеленых" сценариев: от гибкой логики в Node-RED для экономии ресурсов до поддержки стандартных протоколов, обеспечивающих долговечность и ремонтопригодность системы.

    ---

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

    COURSE-16-M05-LAB13: Создание системы мониторинга CO₂ и журналирование данных

    Задача: Настроить поток Node-RED, который считывает данные с (симулированного) Modbus-датчика CO₂, валидирует их, отображает статус и записывает каждое измерение в базу данных MySQL на контроллере. Скелет потока: `[Inject]` -> `[Modbus Getter]` -> `[Function: Validate]` -> `[MySQL Out]`. Дополнительно подключить `[Catch]` для логирования ошибок. Flow Diagram (ASCII):
    [Inject: 1 min] --> [Modbus-Getter: Simulate CO2] --> [Function: Validate & Format] --> [MySQL Out: telemetry_log]
    

    |

    +--> [Debug]

    |

    +--> [Catch: All Nodes] --> [Function: Format Error] --> [MySQL Out: error_log]

    Описание узлов:
  • `Inject`: Настроен на повторение каждые 60 секунд.
  • `Modbus-Getter: Simulate CO2`:
  • * Используйте узел `modbus-flex-getter` или `modbus-read` с настроенным Modbus-клиентом.

    * Для симуляции можно использовать узел `Function` перед `Validate & Format`, который генерирует случайное число в диапазоне CO₂.

        // Function: Simulate Modbus CO2

    // Контракт исходящего сообщения: { "payload": { "data": [1234] } }

    msg.payload = { data: [Math.floor(Math.random() * (1500 - 400 + 1)) + 400] }; // CO2 от 400 до 1500 ppm

    return msg;

  • `Function: Validate & Format`: Аналогичен узлу из примера выше, но с добавлением `msg.topic` для MySQL.
  •     // Узел: "Validate & Format CO2 for DB"

    // Вход: msg.payload от Modbus-узла, например { data: [1150], buffer: }

    // Контракт исходящего сообщения: { "payload": { "value": 1150, "unit": "ppm", "source": "modbus-sensor-office-1", "timestamp": "..." }, "topic": "INSERT INTO ..." }

    if (!msg.payload || !Array.isArray(msg.payload.data) || msg.payload.data.length === 0) {

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

    node.error("Некорректный формат данных от Modbus-датчика", msg);

    return null;

    }

    const co2_level = msg.payload.data[0];

    if (isNaN(co2_level) || co2_level < 400 || co2_level > 5000) {

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

    node.error("Некорректное значение CO2: " + co2_level, msg);

    return null;

    }

    // Формирование по контракту сообщения для MySQL

    msg.payload = {

    value: co2_level,

    unit: "ppm",

    source: "modbus-sensor-office-1",

    timestamp: new Date().toISOString() // ISO формат для MySQL DATETIME

    };

    msg.topic = "INSERT INTO telemetry_log (value, unit, source, timestamp) VALUES (?, ?, ?, ?)"; // SQL-запрос

    node.status({fill:"green", shape:"dot", text: co2_level + " ppm"});

    return msg;

  • `MySQL Out: telemetry_log`:
  • * Настройте подключение к локальной базе данных MySQL на контроллере HI.

    * `Name`: `telemetry_log`

    * `Query`: `msg.topic`

    * `Parameters`: `msg.payload.value, msg.payload.unit, msg.payload.source, msg.payload.timestamp`

    * Предварительно создайте таблицу:

            CREATE TABLE IF NOT EXISTS telemetry_log (

    id INT AUTO_INCREMENT PRIMARY KEY,

    value FLOAT,

    unit VARCHAR(10),

    source VARCHAR(50),

    timestamp DATETIME

    );

  • `Catch: All Nodes`: Перехватывает ошибки со всех узлов на вкладке.
  • `Function: Format Error`:
  •     // Форматирование ошибки для записи в MySQL по контракту

    // Контракт исходящего сообщения: { "payload": { "error_message": "...", "node_id": "...", "node_name": "...", "original_msg_payload": "...", "timestamp": "..." }, "topic": "INSERT INTO ..." }

    msg.payload = {

    error_message: msg.error.message,

    node_id: msg.error.node.id,

    node_name: msg.error.node.name,

    original_msg_payload: JSON.stringify(msg.error.message), // Сохраняем оригинальное сообщение

    timestamp: new Date().toISOString()

    };

    msg.topic = "INSERT INTO error_log (error_message, node_id, node_name, original_msg_payload, timestamp) VALUES (?, ?, ?, ?, ?)";

    return msg;

  • `MySQL Out: error_log`:
  • * Настройте подключение к локальной базе данных MySQL.

    * `Name`: `error_log`

    * `Query`: `msg.topic`

    * `Parameters`: `msg.payload.error_message, msg.payload.node_id, msg.payload.node_name, msg.payload.original_msg_payload, msg.payload.timestamp`

    * Предварительно создайте таблицу:

            CREATE TABLE IF NOT EXISTS error_log (

    id INT AUTO_INCREMENT PRIMARY KEY,

    error_message TEXT,

    node_id VARCHAR(50),

    node_name VARCHAR(100),

    original_msg_payload TEXT,

    timestamp DATETIME

    );

    Критерии оценки:
  • Поток корректно считывает данные (или симулирует их).
  • Применяется "контракт сообщения" с полями `value`, `unit`, `source`, `timestamp`.
  • Данные успешно записываются в таблицу `telemetry_log` в MySQL.
  • Ошибки (например, симулированные некорректные значения CO₂) перехватываются и логируются в `error_log`.
  • Узел `Function` отображает актуальное значение CO₂ через `node.status`.
  • COURSE-16-M05-LAB14: Разработка сценария умного отключения розеток

    Задача: Создать субпоток (subflow) для управления группой розеток. Субпоток должен принимать на вход команду `ON`/`OFF`. Дополнительно, реализовать логику: если в течение 2 часов не было зафиксировано движение ни от одного из трех (симулированных) датчиков движения, все розетки автоматически отключаются для экономии энергии. Flow Diagram (ASCII):
    // Main Flow:
    

    [MQTT In: motion/sensor1] --+

    [MQTT In: motion/sensor2] ---+--> [Join: 3 inputs] --> [Trigger: 2 hours, reset on msg] --> [Change: Set OFF] --> [Link Out: To Subflow Control]

    [MQTT In: motion/sensor3] --+

    [Inject: Manual ON] --> [Change: Set ON] --> [Link Out: To Subflow Control]

    [Inject: Manual OFF] -> [Change: Set OFF] -> [Link Out: To Subflow Control]

    // Subflow: "Manage Power Sockets" (FLOW-AUTO-POWER-001)

    [Link In: From Subflow Control] --> [Function: Validate Command] --> [rpi gpio out: RL-01]

    [rpi gpio out: RL-02]

    [rpi gpio out: RL-03]

    |

    +--> [Status]

    Описание узлов:
  • `MQTT In: motion/sensorX` (3 узла):
  • * `Topic`: `telemetry/motion/sensor1`, `telemetry/motion/sensor2`, `telemetry/motion/sensor3`.

    * `Payload`: `true` (движение обнаружено).

    * Эти узлы симулируют датчики движения.

  • `Join: 3 inputs`:
  • * `Mode`: `manual`

    * `Count`: `3` (или `wait for a specified time` с коротким таймаутом).

    * `Output`: `a single message`

    * `Join`: `msg.payload`

    * Цель: объединить сообщения от датчиков движения, чтобы любое из них сбрасывало таймер.

  • `Trigger: 2 hours, reset on msg`:
  • * `Send nothing, then send`: `msg.payload` `false` (для отключения розеток).

    * `After`: `2` `hours`.

    * `Extend delay if new message arrives`: `checked`.

    * Этот узел будет ждать 2 часа, прежде чем отправить `false`. Любое входящее сообщение от датчиков движения сбросит таймер.

  • `Change: Set OFF` (после Trigger):
  • * `Set`: `msg.payload`

    * `to the value`: `false` (boolean)

    * `Set`: `msg.topic`

    * `to the value`: `commands/sockets/all/set`

  • `Inject: Manual ON/OFF` (2 узла): Для ручного тестирования.
  • * `Payload`: `true` или `false`.

    * `Topic`: `commands/sockets/all/set`.

  • `Link Out: To Subflow Control`: Отправляет команду в субпоток.
  • Субпоток: "Manage Power Sockets" (FLOW-AUTO-POWER-001)
  • `Link In: From Subflow Control`: Принимает команды.
  • `Function: Validate Command`:
  •     // Узел: "Validate Command for Sockets"

    // Вход: msg.payload = true/false, msg.topic = commands/sockets/all/set

    // Контракт исходящего сообщения: { "payload": true/false, "topic": "commands/sockets/all/set", "source": "socket_control" }

    if (typeof msg.payload !== 'boolean' || msg.topic !== 'commands/sockets/all/set') {

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

    node.error("Получена некорректная команда для управления розетками", msg);

    return null;

    }

    // Устанавливаем статус для субпотока

    node.status({fill: msg.payload ? "green" : "red", shape:"dot", text: msg.payload ? "ON" : "OFF"});

    // Формируем исходящее сообщение по контракту

    msg.payload = {

    value: msg.payload,

    source: "socket_control",

    ts: Date.now()

    };

    // msg.topic остается commands/sockets/all/set

    return msg;

  • `rpi gpio out: RL-01`, `RL-02`, `RL-03` (3 узла):
  • * `Pin`: `RL-01`, `RL-02`, `RL-03` (или другие реле, к которым подключены розетки).

    * `Type`: `Digital output`.

    * `Initial state`: `Low`.

    * `msg.payload.value` должен быть `true`/`false`.

    * Подключите все три узла к выходу `Function: Validate Command`.

  • `Status`: Отображает текущее состояние розеток.
  • Критерии оценки:
  • Логика управления реле вынесена в переиспользуемый субпоток `FLOW-AUTO-POWER-001`.
  • Система корректно реагирует на команды `ON`/`OFF` (ручные и автоматические).
  • Таймер `trigger` сбрасывается при получении сигнала от любого датчика движения.
  • При отсутствии движения в течение 2 часов реле розеток отключается.
  • Состояние "Энергосбережение активно" (или "ON"/"OFF") отображается через узел `Status` в субпотоке.
  • Тест для самопроверки

    COURSE-16-M05-QUIZ

  • Какая основная цель применения гистерезиса (например, 800/1000 ppm) в логике управления климатом?
  • a) Для экономии памяти контроллера.

    b) Для предотвращения частых переключений оборудования.

    c) Для более точного измерения.

    d) Для соответствия протоколу Modbus.

  • При подключении экранированной витой пары для шины RS-485, экран кабеля следует подключать:
  • a) К клемме GND на обоих концах шины.

    b) К клемме PE (заземление) на обоих концах.

    c) К клемме GND только со стороны контроллера.

    d) Не подключать вообще.

  • Какой паттерн Node-RED наиболее подходит для управления системой с несколькими сложными состояниями (например, "Полив", "Ожидание", "Сон")?
  • a) Контракт сообщения.

    b) Конечный автомат (FSM).

    c) Визуальный статус.

    d) Централизованный обработчик ошибок.

  • Что является лучшей практикой для минимизации E-waste (электронных отходов) в проекте автоматизации?
  • a) Использовать самые дешевые датчики и менять их каждый год.

    b) Использовать проприетарные протоколы от одного производителя.

    c) Выбирать оборудование с длительным сроком службы и поддержкой стандартных протоколов (Modbus, CAN).

    d) Программировать всю логику на низком уровне (C++).

  • В узле `Function` для валидации данных с датчика, какое действие нужно предпринять при получении некорректного значения (например, температура -100°C)?
  • a) Проигнорировать и передать дальше.

    b) Заменить на последнее известное корректное значение.

    c) Прервать поток (вернуть `null`) и сгенерировать ошибку через `node.error()`.

    d) Перезагрузить контроллер.

  • В схеме `WIRING-WATER-003`, для чего используется реле `RL-12`?
  • a) Для измерения уровня воды.

    b) Для подачи питания 24V DC на электромагнитный клапан.

    c) Для чтения данных с датчика влажности.

    d) Для подключения к шине RS-485.

  • Какой узел Node-RED используется для перехвата ошибок от других узлов на вкладке?
  • a) `Status`

    b) `Link In`

    c) `Catch`

    d) `Switch`

  • Оптимальная частота опроса датчика температуры в жилой комнате по Modbus:
  • a) 10 раз в секунду.

    b) 1 раз в секунду.

    c) 1 раз в 30-60 секунд.

    d) 1 раз в час.

  • Согласно стандарту "Контракт сообщения", какое из полей НЕ является обязательным, но рекомендованным в `msg.payload`?
  • a) `value`

    b) `source`

    c) `ts` (timestamp)

    d) `unit` (единица измерения)

  • Что произойдет, если в системе умного полива не учесть прогноз погоды?
  • a) Система не сможет включить клапан.

    b) Система может потратить воду впустую, включив полив перед дождем.

    c) Датчик влажности выйдет из строя.

    d) Контроллер перегреется.

    (Ответы: 1-b, 2-c, 3-b, 4-c, 5-c, 6-b, 7-c, 8-c, 9-d, 10-b)

    Мини-Runbook: "Если не работает"

    Проблема: Датчик CO₂ (Modbus) не отдает данные или показывает ошибку "timeout".

  • Физическая проверка:
  • * Питание: Убедитесь, что на датчик подается питание 24V DC. Проверьте индикаторы на его корпусе.

    * Шина RS-485: Проверьте правильность подключения проводов A, B, GND согласно схеме `WIRING-ENV-AIR-001`. Попробуйте поменять A и B местами — это частая ошибка.

    * Терминатор: Убедитесь, что терминальный резистор 120 Ом включен только на крайних устройствах шины.

  • Проверка в Node-RED:
  • * Статус узла: Посмотрите на статус узла `Modbus-Read`. Он показывает "active", "connecting" или "error"?

    * Конфигурация клиента: Откройте настройки Modbus-клиента. Убедитесь, что выбран правильный COM-порт (`/dev/ttyS1`, `/dev/ttyUSB0` и т.д.), скорость, четность и стоп-биты соответствуют настройкам датчика.

    * Настройки узла: Проверьте, что в узле `Modbus-Read` указан правильный `Unit-ID` (Slave ID) датчика (например, `15`). Проверьте адрес регистра (например, `100`) и функцию чтения (например, `FC 3: Read Holding Registers`).

  • Диагностика:
  • * Подключите узел `Debug` к выходу узла `Catch`. Какие сообщения об ошибках он показывает? `CRC Error` указывает на проблемы с качеством линии, `Timeout` — на неверный адрес или отсутствие ответа.

    * Используйте внешний Modbus-сканер (например, Modbus Poll на ПК с USB-RS485 адаптером) для проверки связи с датчиком напрямую, минуя Node-RED. Это поможет локализовать проблему: в проводке/датчике или в конфигурации Node-RED.

    Проблема: Вентилятор/клапан не включается, хотя логика в Node-RED отрабатывает.

  • Проверка в Node-RED:
  • * Подключите узел `Debug` к выходу узла `rpi gpio out`, который должен управлять реле. Убедитесь, что он отправляет правильную команду (`true`/`false` для `msg.payload.value`).

    * Проверьте, что узел `rpi gpio out` настроен на правильный номер реле (например, `RL-10` для вентилятора, `RL-12` для клапана).

  • Физическая проверка:
  • * Индикация на контроллере: Посмотрите на светодиодный индикатор соответствующего реле на корпусе контроллера HI. Загорается ли он в момент отправки команды?

    * Если индикатор горит: Проблема во внешней цепи.

    * Проверьте подключение проводов к клеммам реле (C и NO) согласно схемам `WIRING-ENV-AIR-001` или `WIRING-WATER-003`.

    * Проверьте целостность цепи до самого вентилятора/клапана.

    * Убедитесь в наличии питания 230V AC (для вентилятора) или 24V DC (для клапана) на соответствующих клеммах.

    * Проверьте работоспособность самого вентилятора/клапана, подав на него питание напрямую (если это безопасно).

    * Если индикатор не горит: Проблема в логике или конфигурации Node-RED.

    * Убедитесь, что узел `rpi gpio out` настроен на правильный номер выхода (`RL-10`, `RL-12` и т.д.).

    * Проверьте, что `msg.payload.value` приходит в виде булевого значения (`true`/`false`), а не строки или числа.