Подключение Daly BMS по RS485

Добрый день.
Пытаюсь подружить Daly BMS по RS485, но через Modbus-utils-RPC не могу послать нужную команду т.к. нужно не просто считать регистр, а сделать это по протоколу. Нашел на GitHub описание протокола Протокол Там же есть и спецификация протокола.
Вопрос. Можно ли как-то сделать такой запрос по RS485.
daly_uart_protocol_v1.2.pdf (164,7 КБ)

Здравствуйте.

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

Добрый день.
Схема подключения очень простая:

С вопросом уже разобрался и так как WirenBoard очень гибкая система, то нашлось целых 2 способа решить мою задачу.
Решение 1. Отправлять произвольную команду через runShellCommand, согласно протокола.

Спойлер

// Определение виртуального устройства “rs485_cmd” с одним параметром
defineVirtualDevice(“rs485_cmd”,
{
title: “Send custom command to RS-485 port”, // Название устройства в интерфейсе
cells:
{
// Определение ячейки “enabled” - переключатель
enabled:
{
type: “switch”, // Тип элемента управления - переключатель
value: false // Начальное значение - выключен (false)
},
}
});

// Функция настройки порта RS-485
function setup_port()
{
// Настройка параметров последовательного порта /dev/ttyRS485-2
runShellCommand(“stty -F /dev/ttyRS485-2 ospeed 9600 ispeed 9600 raw clocal -crtscts -parenb -echo cs8”);

// Параметры команды stty:
// -F /dev/ttyRS485-2 - указание устройства
// ospeed 9600 - скорость передачи данных (выходная) 9600 бод
// ispeed 9600 - скорость передачи данных (входная) 9600 бод
// raw - отключение обработки символов (сырой режим)
// clocal - игнорировать сигналы линии модема
// -crtscts - отключить управление потоком через RTS/CTS
// -parenb - отключить четность (без четности)
// -echo - не отображать принимаемые символы
// cs8 - 8 бит данных

}

// Определение правила для включения RS-485 команды
defineRule(“_rs485_switch_on”,
{
asSoonAs: function ()
{
// Условие срабатывания правила: когда переключатель enabled установлен в true
return dev.rs485_cmd.enabled;
},
then: function() {
// Действие при срабатывании правила: отправка команды в порт RS-485
runShellCommand(“/usr/bin/printf ‘\xa5\x40\x96\x08\x00\x00\x00\x00\x00\x00\x00\x00\x83’ > /dev/ttyRS485-2”);

// Отправляем специфичную команду в формате hex:
// \xa5\40\96\08\00\00\00\00\00\00\00\00\83
// Это бинарная команда для управления устройством по RS-485

}
});

// Определение правила для выключения RS-485 команды
defineRule(“_rs485_switch_off”,
{
asSoonAs: function ()
{
// Условие срабатывания правила: когда переключатель enabled установлен в false
return !dev.rs485_cmd.enabled;
},
then: function()
{
// Действие при срабатывании правила: отправка команды выключения в порт RS-485
runShellCommand(“/usr/bin/printf ‘\xa5\x40\xd8\x08\x00\x00\x00\x00\x00\x00\x00\x00\xc5’ > /dev/ttyRS485-2”);

// Отправляем другую специфичную команду выключения
// \xa5\40\d8\08\00\00\00\00\00\00\00\00\c5

}
});

// Запланировать выполнение функции setup_port() через 1 секунду после старта правил
setTimeout(setup_port, 1000);

Решение 2. Оказалось элегантнее и удобнее. Используя RPC MQTT.

Спойлер

// Определение пути RPC для отправки запросов к модбас-порту
var pathRPC = “/rpc/v1/wb-mqtt-serial/port/Load/”;

// Указание устройства модбас-порта (RS485 интерфейс)
var modbusPort = “/dev/ttyRS485-2”;

// Скорость передачи данных в модбас порту (9600 бод)
var modbusSpeed = 9600;

// Параметры четности (N - без четности)
var modbusParity = “N”;

// Количество стоп-битов (1 стоп-бит)
var modbusStopbit = 1;

// Строка данных в шестнадцатеричном формате для отправки
var message = “A5409608000000000000000083”;

// Идентификатор клиента для MQTT соединения
var clientID = “testRPC”;

/**

  • Функция отправки RPC запроса через MQTT

  • @param {string} modbusPort - Путь к модбас-порту

  • @param {number} modbusSpeed - Скорость передачи данных

  • @param {string} modbusParity - Тип четности

  • @param {number} modbusStopbit - Количество стоп-битов

  • @param {string} clientID - Идентификатор клиента

  • @param {number} requiestID - ID запроса

  • @param {string} messageType - Тип сообщения (HEX)

  • @param {string} message - Данные для отправки

  • @param {number} responseSize - Размер ожидаемого ответа
    */
    function requestRPC(modbusPort, modbusSpeed, modbusParity, modbusStopbit, clientID, requiestID, messageType, message, responseSize){
    // Формирование JSON строки с параметрами запроса
    var strJson = JSON.stringify({
    params: {
    // Размер ожидаемого ответа от устройства
    response_size: responseSize,
    // Тип формата данных (HEX - шестнадцатеричный)
    format: messageType,
    // Путь к модбас-порту
    path: modbusPort,
    // Скорость передачи данных
    baud_rate: modbusSpeed,
    // Тип четности
    parity: modbusParity,
    // Количество данных битов (8 бит)
    “data_bits” : 8,
    // Количество стоп-битов
    “stop_bits” : modbusStopbit,
    // Данные для отправки в формате HEX
    “msg”: message
    },
    // Уникальный ID запроса для отслеживания ответа
    “id” : requiestID
    });

    // Вывод отладочной информации о JSON строке
    log.info(“strJson =”, strJson);

    // Отправка JSON запроса через MQTT на указанный топик
    publish(pathRPC+clientID, strJson, 2, false)
    };

/**

  • Подписка на получение ответа от устройства
  • Слушает топик с ответами для указанного клиента
    */
    trackMqtt(pathRPC+clientID+“/reply”, function(message){
    // Вывод информации о полученных данных (топик и значение)
    log.info(“name: {}, value: {}”.format(message.topic, message.value))
    });

/**

  • Вызов функции отправки RPC запроса
  • Отправляет сообщение на модбас-порт с указанными параметрами
    */
    requestRPC(modbusPort, modbusSpeed, modbusParity, modbusStopbit, clientID, 1, “HEX”, message, 8)

Теперь ответ от устройства получен и осталось дело техники: описать виртуальное устройство, разобрать полученные данные и как-то циклически всё это опрашивать.