Почему канал реле горит красным

Подключил такое двухканальное реле к порту RS485-2 WirenBoard 8.4: https://aliexpress.ru/item/1005006546630262.html?sku_id=12000037624457919. Добавил устройство через стандартный шаблон “Устройство с протоколом modbus”. Добавил два канала с адресами coil 0 и 1 соответственно. Оба канала реле работают при включении и отключении. Однако второй канал горит красным в Устройствах. Почему так?

Добрый день!

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

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

Добрый день!

Судя по ошибкам:

Jan 11 15:35:08 wirenboard-AON5RX2F wb-mqtt-serial[719947]: WARNING: [modbus] failed to read 1 coil(s) @ 1 of device modbus:200: Serial protocol error: request timed out  
Jan 11 15:35:09 wirenboard-AON5RX2F wb-mqtt-serial[719947]: WARNING: [modbus] failed to read 1 coil(s) @ 1 of device modbus:200: Serial protocol error: request timed out  
Jan 11 15:35:10 wirenboard-AON5RX2F wb-mqtt-serial[719947]: WARNING: [modbus] failed to read 1 coil(s) @ 1 of device modbus:200: Serial protocol error: request timed out  

Контроллер не может считать регистры устройства. Рекомендую:

  1. Свериться с настройками устройства по документации на реле:
  • Проверьте правильность адреса Modbus.
  • Убедитесь, что скорость, биты данных, чётность и стоп-биты совпадают.
  1. Проверить работу через Modbus-client:
  • Используйте инструкцию для считывания данных напрямую через Modbus-client.

Предположу что проблема может быть вызвана следующим обстоятельством (ранее как то приходилось работать с реле через modbus client): считывание coil регистров ведется побайтно, для получения значения второго регистра необходимо прочитать регистры (команда 01) начиная с 0 регистра 8 бит (1 байт), полученное значение будет соответстсвовать максимальному возможному кол-ву реле - 8. Как реализовать такую логику чтения в wb?

Добрый день!

Не совсем понимаю цель ваших действий. Пожалуйста, опишите подробнее:

  1. Что именно вы делаете?
  2. Какого результата ожидаете?
  3. Что получаете в итоге?

Поясню подробнее. Я пытаюсь добавить в WB двухканальное реле (ссылку на устройство присылал в первом письме, там же по ссылке на алиэкспресс в описании указаны команды для работы с реле по modbus).
При этом хочу чтобы я мог а) управлять обоими каналами реле из веб-интерфейса б) постоянно получать актуальное состояние реле.
Отдельно отмечу, что проверил работу реле через usb стик и программу CAS Modbus Scanner. То есть параметры соединения (9600, 8, none, 1) и адрес - правильные. Коды команд мне понятны, и ответные сообщения - тоже.

Я попробовал добавить реле в WB двумя способами:

1 вариант. Добавить новое устройство через веб-интерфейс WB: настройки драйверов serial-устройств → порт /dev/ttyRS485-2 (настроен с указанными выше параметрами) → Добавить устройства вручную → Устройство с протоколом modbus → Добавляем два канала с адресами 0 и 1, тип регистра coil. Данные каналы оба работают на включение, однако второй канал подсвечивается в “Устройствах” красным. То есть явно возникает ошибка чтения второго канала. В этом и был первоначальный вопрос.

2 вариант. Долго пытался добавить реле через виртуальное устройство. С помощью разных команд: /usr/bin/printf, mbpoll, modbus_client. Но выходила похожая ошибка: реле включались, т.е. команда на запись работает. Но ответа не приходило. И вот только сегодня вычитал в документации wirenboard, что для корректной работы с modbus_client требуется отключить службу wb-mqtt-serial. Отключаешь - все ок, ответ возвращается как при подаче команды на запись состояния реле, так и при чтении состояния реле. При этом я хочу считывать состояние реле скажем каждые 0,2 сек, чтобы оно корректно отображалось на веб-интерфейсе. Но нельзя ведь так часто отключать и включать службу wb-mqtt-serial.

И еще такую неприятную вещь заметил: как только начинаешь обращаться через командную строку SSH с помощью команды modbus к реле, а затем запускаешь службу wb-mqtt-serial контроллер автоматически создает двухканальное реле с двумя каналами - такое же как и при ручном создании. Несмотря на то, что я его удалил. И это точно не виртуальное устройство. В созданном устройстве с двумя каналами второй канал также продолжает подсвечиваться красным.

Получается что оба способа нерабочие: 1 способ подсвечивает второй канал красным, 2 способ предполагает отключение wb-mqtt-serial на время выполнения запроса.

Добрый день!

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

Добрый день, удалось решить вопрос?

Пока что не получилось. Попробовал при этом предложенные в статье способы изменения параметров guard_interval_us, response_timeout_ms, max_bit_hole. Не помогло.

Добрый день!

Прошу прислать карту регистров данного реле и общую документацию. Это поможет более детально рассмотреть ваш вопрос и предложить решение.

полной карты регистров на реле не нашел, но команды для управления приведены в описании по ссылке в первом письме, чуть больше информации есть здесь Релейный модуль 2 канала, RS485 Modbus | купить, адреса ключевых койлов, отвечающих за работу двух каналов - 0х0000 и 0х0001, при этом указанные в описании команды исправно работают если их вводить из консоли ssh (важно! при отключенном wb-mqtt-serial), исправно - значит приходят ответы! вообще реле стоит 300-400 руб, проще его купить ) при включенном wb-mqtt-serial поведение реле не меняется: второй канал горит красным, вне зависимости от того как его добавлять: типовой или кастомный шаблон, виртуальное устройство также не подходит т.к. не получается считывать состояние реле, только записывать

Добрый день!

Потребуется некоторое время для ознакомления с документацией. Как только будут результаты, я свяжусь с вами.

Добрый день!

Мы не компетентны помогать со сторонними неподдерживаемыми устройствами. Можем упустить какие-то из их особенностей.

Услуг настройки стороннего оборудования не оказываем.

Шаблон и modbus_client могут корректно работать только со стандартным Modbus - на странице по вашей ссылке указано, что модбас у данного устройства нестандартный.

Не могу понять по описанию, что вы делали с виртуальным устройством и modbus_client? Как это реализоввывали? Можете, пожалуйста, рассказать поподробнее?

Такое может быть от слишком частых запросов. Можно попробовать реже опрашивать канал.

Вместо modbus_client при включенном wb-mqtt-serial можно использовать modbus_client_rpc.

Также для с нестандартными протоколами можно работать через RPC.

Тестировать работу по нестандартному протоколу можно с помощью serial_tool.

2 лайка

Про утилиту modbus_client_rpc когда читал, не увидел. С ней все работает исправно. Спасибо. Единственное что приходится учитывать - команде требуется более 1 сек чтобы считать значение. Поэтому из-за ассинхронности runShellCommand приходится добавлять задержку с setTimeout. В коде ниже есть пара “лишних” функций, но в целом все функции рабочие:

var is_active_Example_Modbus_relay = true;

if (is_active_Example_Modbus_relay) {
    defineVirtualDevice("modbus_relay", {
        title: {
            "en": "Two-Channel Modbus Relay",
            "ru": "Двухканальное реле"
        },
        cells: {
            relay1: {
                type: "switch",
                value: false,
                title: {
                    "en": "Relay 1",
                    "ru": "Реле 1"
                }
            },
            relay2: {
                type: "switch",
                value: false,
                title: {
                    "en": "Relay 2",
                    "ru": "Реле 2"
                }
            }
        }
    });

    var DEVICE_ADDRESS = 200; // Адрес устройства
    var BAUD_RATE = 9600; // Скорость обмена
    var PARITY = "none"; // Четность
    var DATA_BITS = 8; // Биты данных
    var STOP_BITS = 1; // Стоповые биты
    var POLL_INTERVAL = 5000; // Интервал опроса в мс (между чтением состояния реле)
    var POLL_DELAY = 2000; // Ожидаем результата выполнения запроса 
    var MODBUS_PORT = "/dev/ttyRS485-2"; // Порт устройства
    var RelStatus = []; // по умолчанию пустой массив
    var Rel1Pushed = false;
    var Rel2Pushed = false;

    // CRC вычисление
    function calculateCRC(command) {
        var crc = 0xFFFF;
        for (var i = 0; i < command.length; i++) {
            crc ^= command[i];
            for (var j = 0; j < 8; j++) {
                if (crc & 1) {
                    crc = (crc >> 1) ^ 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        return [(crc & 0xFF), (crc >> 8)];
    }

    // Настройка порта
    function setupPort() {
        runShellCommand("stty -F " + MODBUS_PORT + " ospeed " + BAUD_RATE + " ispeed " + BAUD_RATE + " raw clocal -crtscts -parenb -echo cs8");
    }

    // Добавление CRC в команду и получение команды для запуска runShellCommand
    function CommandCRC(command) {
        var crc = calculateCRC(command);
        var fullCommand = command.concat(crc);
        var commandString = "";

        for (var i = 0; i < fullCommand.length; i++) {
            var hexValue = fullCommand[i].toString(16).toUpperCase();
            // Убедимся, что hexValue всегда будет длиной 2 символа
            if (hexValue.length === 1) {
                hexValue = "0" + hexValue; // добавляем ведущий ноль
            }
            commandString += "\\\\x" + hexValue;
        }
        return commandString;
    }

    // Отправка команды в порт
    function sendCommand(command) {
        //runShellCommand('/usr/bin/printf "' + CommandCRC(command) + '" > ' + MODBUS_PORT);
        var cmd = 'modbus_client_rpc --debug -mrtu -b' + BAUD_RATE + ' -d' + DATA_BITS + ' -s' + STOP_BITS + ' -p' + PARITY + ' ' + MODBUS_PORT + ' -a' + DEVICE_ADDRESS + ' -t' + command[1] + ' -r' + ((command[2] << 8) | command[3]) + ' ' + ((command[4] << 8) | command[5]);
        runShellCommand(cmd);
    }

    // Чтение койла (один регистр 0 или 1)
    function getModbusRegisterValue(command) {
        var cmd = 'modbus_client_rpc --debug -mrtu -b' + BAUD_RATE + ' -d' + DATA_BITS + ' -s' + STOP_BITS + ' -p' + PARITY + ' ' + MODBUS_PORT + ' -a' + DEVICE_ADDRESS + ' -t' + command[1] + ' -r' + ((command[2] << 8) | command[3]) + ' -c' + ((command[4] << 8) | command[5]);
        RelStatus = []; // по умолчанию пустой массив
        runShellCommand(cmd, {
            captureOutput: true,
            exitCallback: function(exitCode, capturedOutput) {
                if (exitCode === 0 && capturedOutput.length > 0) {
                    RelStatus = capturedOutput.split("Data:")[1].trim().split(" ").map(function(byte) {
                        return parseInt(byte, 16);
                    });
                }
            }
        });
    }
  
    // Чтение состояния реле
    function readRelayStatus() {
        getModbusRegisterValue([DEVICE_ADDRESS, 0x01, 0x00, 0x00, 0x00, 0x08]);
        setTimeout(function() {
          if (RelStatus.length>0) {
            if (!Rel1Pushed) {
              dev["modbus_relay/relay1"] = RelStatus[0] ? true : false;
            }
            if (!Rel2Pushed) {
              dev["modbus_relay/relay2"] = RelStatus[1] ? true : false;
            }
          }  
        }, POLL_DELAY);          
    }

    // Обработка изменений состояния реле
    defineRule("relay1_control", {
        whenChanged: "modbus_relay/relay1",
        then: function (newValue) {
          Rel1Pushed = true;
          sendCommand([DEVICE_ADDRESS, 0x05, 0x00, 0x00, newValue ? 0xFF : 0x00, 0x00]);
          setTimeout(function() {
            Rel1Pushed = false;
          }, POLL_DELAY);
        }
    });

    defineRule("relay2_control", {
        whenChanged: "modbus_relay/relay2",
        then: function (newValue) {
          Rel2Pushed = true;
          sendCommand([DEVICE_ADDRESS, 0x05, 0x00, 0x01, newValue ? 0xFF : 0x00, 0x00]);
          setTimeout(function() {
            Rel2Pushed = false;
          }, POLL_DELAY);
        }
    });
  
    // Периодический опрос состояния реле
    setInterval(readRelayStatus, POLL_INTERVAL);

    // Инициализация
    setTimeout(setupPort, 1000);
}

Рада, что хоть немного удалось помочь :slight_smile: