Добрый день.
Пытаюсь подружить 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)
Теперь ответ от устройства получен и осталось дело техники: описать виртуальное устройство, разобрать полученные данные и как-то циклически всё это опрашивать.