Добрый день коллеги, поговорим о прозрачном конверторе Modbus TCP в Modbus RTU.
Казалось бы, что выбор ограничен несколькими проприетарными продуктами, которые не всегда удовлетворяют нашим потребностям.
Мы предложим свободно программируемый контроллер AntexGate с помощью которого можно получить конвертер Modbus tcp – Modbus rtu – can – profitnet – bacnet – mqtt – http – opc UA – другой протокол.
Итак, рассмотрим первую связку Modbus TCP – Modbus RTU, нам понадобится сам контроллер AntexGate либо другой компьютер с linux/windows на борту и периферией RS485.
Первое, что необходимо – это установить программный продукт Node-Red, это делается одним скриптом:
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
Сделать Node-red после установки сервисом:
sudo systemctl enable nodered.service
Перезагружаем железяку:
sudo reboot
Теперь попадаем в среду нашего программного конвертора Node-red через web браузер по ссылке ip шлюза:1880
Далее настроим скрипт, который конвертирует посылку Modbus TCP в RTU, посылает в RS485 порт, ждет ответ и тут же преобразует ответ обратно в Modbus TCP.
Заходим в "меню" > "импорт"
Проект конвертора Modbus TCP <> RTU
[
{
"id": "22ff3060.54da6",
"type": "function",
"z": "de082ac2.4b8bf8",
"name": "Convert TCP to RTU",
"func": "var MBAPHeader = [5];\nvar crc = 0xFFFF;\n\nMBAPHeader[0]= msg.payload[0];\nMBAPHeader[1]= msg.payload[1];\nMBAPHeader[2]= msg.payload[2];\nMBAPHeader[3]= msg.payload[3];\nMBAPHeader[4]= msg.payload[4];\nflow.set('MBAP_HEADER', MBAPHeader);\n\nvar ReqLen = msg.payload[5];\nvar MBLen = (msg.payload.length)-ReqLen;\nvar MBReq = [MBLen];\n\nfor (let x=0; x < MBLen;x++){\n MBReq[x] = msg.payload[x+ReqLen];\n}\n\nfor (var req = 0; req < ReqLen; req++) {\n crc ^= MBReq[req]; // XOR byte into least sig. byte of crc\n \n for (var i = 8; i !== 0; i--) { // Loop over each bit\n if ((crc & 0x0001) !== 0) { // If the LSB is set\n crc >>= 1; // Shift right and XOR 0xA001\n crc ^= 0xA001;\n } else { // Else LSB is not set\n crc >>= 1; // Just shift right\n }\n }\n}\n\nvar MyCRC = crc.toString(16);\nMBReq[MBLen+1] = parseInt(MyCRC.substring(0,2),16);\nMBReq[MBLen] = parseInt(MyCRC.substring(2,4),16);\n\nvar buf = new Buffer(MBReq);\nmsg.payload = buf;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 300,
"y": 100,
"wires": [
[
"7e62bbbc.bd8ea4"
]
],
"outputLabels": [
"XO"
]
},
{
"id": "7e62bbbc.bd8ea4",
"type": "serial request",
"z": "de082ac2.4b8bf8",
"name": "",
"serial": "27dabad398437b7e",
"x": 490,
"y": 100,
"wires": [
[
"db79ee80.1d792",
"9e53f02d0a4e04fb"
]
]
},
{
"id": "db79ee80.1d792",
"type": "function",
"z": "de082ac2.4b8bf8",
"name": "Convert RTU to TCP",
"func": "if (msg.payload.length > 0) {\n var resLen = msg.payload.length - 2;\n var respond = [];\n respond[0] = flow.get('MBAP_HEADER')[0];\n respond[1] = flow.get('MBAP_HEADER')[1];\n respond[2] = flow.get('MBAP_HEADER')[2];\n respond[3] = flow.get('MBAP_HEADER')[3];\n respond[4] = flow.get('MBAP_HEADER')[4];\n respond[5] = resLen;\n\n for (let req = 6; req < resLen+6; req++) {\n respond[req]= msg.payload[req-6];\n }\n msg.payload = new Buffer(respond);\n return msg;\n} else {\n return null;\n}\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 640,
"y": 60,
"wires": [
[
"2622a1e893f52bed",
"140a62f5366490d9"
]
]
},
{
"id": "55423964225fa869",
"type": "comment",
"z": "de082ac2.4b8bf8",
"name": "Convert Modbus TCP to Modbus RTU",
"info": "",
"x": 170,
"y": 40,
"wires": []
},
{
"id": "cbd6e86861af643d",
"type": "tcp in",
"z": "de082ac2.4b8bf8",
"name": "Modbus TCP - IN",
"server": "server",
"host": "",
"port": "5002",
"datamode": "stream",
"datatype": "buffer",
"newline": "",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 100,
"y": 100,
"wires": [
[
"22ff3060.54da6",
"48471bdeae3a1d30"
]
]
},
{
"id": "2622a1e893f52bed",
"type": "tcp out",
"z": "de082ac2.4b8bf8",
"name": "Modbus TCP - OUT",
"host": "127.0.0.1",
"port": "502",
"beserver": "reply",
"base64": false,
"end": false,
"x": 850,
"y": 100,
"wires": []
},
{
"id": "9e53f02d0a4e04fb",
"type": "debug",
"z": "de082ac2.4b8bf8",
"name": "debug 17",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 640,
"y": 220,
"wires": []
},
{
"id": "140a62f5366490d9",
"type": "debug",
"z": "de082ac2.4b8bf8",
"name": "debug 18",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 820,
"y": 200,
"wires": []
},
{
"id": "48471bdeae3a1d30",
"type": "debug",
"z": "de082ac2.4b8bf8",
"name": "debug 19",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 260,
"y": 200,
"wires": []
},
{
"id": "27dabad398437b7e",
"type": "serial-port",
"serialport": "/dev/ttyUSB0",
"serialbaud": "115200",
"databits": "8",
"parity": "none",
"stopbits": "1",
"waitfor": "",
"dtr": "none",
"rts": "none",
"cts": "none",
"dsr": "none",
"newline": "5",
"bin": "bin",
"out": "time",
"addchar": "",
"responsetimeout": "1000"
}
]
Копируем скрипт, нажимаем "Импорт"
Настраиваем порт для просушки в узле Modbus TCP IN, рекомендуем не использовать стандартный 502, а использовать нестандартный порт более 1024, для них не нужны дополнительные разрешения в Linux.
Настраиваем последовательный порт RS485/232 на скорость конечного железа, поддерживающего протокол Modbus RTU.
Жмем "Развернуть" и спустя 20 минут наш конвертер готов!
Настраиваем Ваш инструмент верхнего уровня, например Modbus TCP OPC сервер стандартно: ip_адре_ шлюза:5002:id_RTU_устройства.
Все работает стабильно - чтение и запись! Посылки как видим между запросом и ответом 100-200мс, что равно пингу. Удаленный шлюз AntexGate работает на встроенном LTE модеме.
Бонусом, для тех, у кого софт верхнего уровня поддерживает функцию Modbus RTU поверх TCP включаем это правило.
В Node-Red упрощаем поток и исключаем функции RTU TCP обработки, и наш проект в Node-Red становится без единой строчки кода.
Контроллер AntexGate в связке с Node-red является отличным универсальным решением для обработки, пересылки и хранения данных. Прошу прощения за рекламу, однако выше представленный проект Вы можете реализовать на любом железе и любой ОС.
Присоединяйтесь к нашему сообществу в Telegram
Железо можно получить на тесты, пишите на почту: info@antexcloud.ru (на три месяца для ЮР лиц и ИП)
Комментарии (8)
Yak52
11.06.2024 08:20Жмем "Развернуть" и спустя 20 минут наш конвертер готов!
Если не секрет - это слабость железа или ... ?
antex_dom Автор
11.06.2024 08:20+1Установить софт, и настроить его просто рука быстрее мышку не двигает. Наверное не правильно выразится 20 минут это вся настройка в целом начиная от включения контроллера, поток разворачивается примерно 500мс.
AlloySteel
11.06.2024 08:20Молодцы! Какое замечательное решение в одной коробочке! За Node Red отдельный плюс!
antex_dom Автор
11.06.2024 08:20Спасибо Вам, за такой коммент, после таких слов хочется еще лучше коробочки делать!
hooperer
Достаточно странно что на странице контроллера нет его цены и она выдаётся только по запросу.
antex_dom Автор
Огромная кнопка Прайс-лист Вас не смущает? Давайте продублирую https://antexcloud.ru/price/
hooperer
простите,
вот сразу и не нашёл. нажал на кнопку " купить" которая левее прайс-листа и попал на какую то анкету обратной формы с запросами.
почему-то ожидал увидеть цену/диапазон на странице прибора, и прайс лист не идентифицировался как возможность узнать цену, а кнопка " купить" идентифицировалась как возможность узнать цену.
ну это у меня)
antex_dom Автор