The modbus devices I'm testing use function 0x64. Like many SCADA software it does not work with this function, node-red is essential to send data at 0x64 and process that data.
With that in mind, I created a routine that requests the status of 10 devices and saves it in a database.
The devices response is an array of 105. I don't need to handle this data initially.
Now I wanted a way to be able to extract the data from the table using FC=03.
Example: Scada client via TCP/IP, requests with FC = 03 the status data of ID = 4. With node-red I receive this request and return the data saved in the column "response" and ID = 4 of the database.
[
{
"id": "3b8b13e72e2f1d82",
"type": "function",
"z": "e1463c22.f5c5f8",
"name": "Serial / TCP-IP",
"func": "\nfunction tamanho_array(_payload) {\n if (_payload.length === 105) {\n return Buffer.from(_payload);\n } else {\n return null;\n }\n}\n\nif (msg.payload.length === 105 && msg.payload[12]) {\n var primeirasPosicoes = msg.payload.slice(0, 12);\n msg.payload = [...msg.payload.slice(12), ...primeirasPosicoes];\n msg.payload = tamanho_array(msg.payload);\n return msg;\n }\n\nreturn null;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 480,
"y": 1140,
"wires": [
[
"668a8a26163a1406"
]
]
},
{
"id": "668a8a26163a1406",
"type": "function",
"z": "e1463c22.f5c5f8",
"name": "MySql",
"func": "var id = msg.payload[0];\nvar array = msg.payload;\nvar arrayString = JSON.stringify(array);\n\n\nvar query = \"INSERT INTO Status (Resposta) VALUES ('\" + arrayString + \"')\";\n// Correção na consulta SQL abaixo\nquery = \"UPDATE Status SET `Resposta` = ? WHERE `ID` = ?\";\nmsg.payload = [arrayString, id];\nmsg.topic = query;\nreturn [msg];\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 650,
"y": 1140,
"wires": [
[
"98b39ef9c86ef9ba"
]
]
},
{
"id": "de85e7fc088fc4ba",
"type": "inject",
"z": "e1463c22.f5c5f8",
"name": "Test - Read SQL",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "SELECT * FROM status_skc.status;",
"payload": "",
"payloadType": "date",
"x": 640,
"y": 1200,
"wires": [
[
"98b39ef9c86ef9ba"
]
]
},
{
"id": "98b39ef9c86ef9ba",
"type": "mysql",
"z": "e1463c22.f5c5f8",
"mydb": "0e6b7172991e5b22",
"name": "",
"x": 830,
"y": 1140,
"wires": [
[
"c6363ebab6aced3e"
]
]
},
{
"id": "0532d7a424334cc0",
"type": "tcp in",
"z": "e1463c22.f5c5f8",
"name": "TCP/IP",
"server": "client",
"host": "192.168.108.45",
"port": "5300",
"datamode": "stream",
"datatype": "buffer",
"newline": "",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 190,
"y": 1140,
"wires": [
[
"3b8b13e72e2f1d82"
]
]
},
{
"id": "c6363ebab6aced3e",
"type": "debug",
"z": "e1463c22.f5c5f8",
"name": "Respose - SQL",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1060,
"y": 1140,
"wires": []
},
{
"id": "9124c23b0577246d",
"type": "comment",
"z": "e1463c22.f5c5f8",
"name": " Communication Response - Modbus",
"info": "",
"x": 200,
"y": 1100,
"wires": []
},
{
"id": "a82b0d538c826d92",
"type": "comment",
"z": "e1463c22.f5c5f8",
"name": "Request status - FC = 0x64 - ID_modbus = 1 to 10 ",
"info": "",
"x": 610,
"y": 1320,
"wires": []
},
{
"id": "5f623a38400dd534",
"type": "group",
"z": "e1463c22.f5c5f8",
"name": "Loop SKC",
"style": {
"stroke": "#000000",
"label": true,
"fill": "#ff7f7f",
"color": "#000000"
},
"nodes": [
"b5422b8155a0c8d2",
"326badf4c93713e8",
"67e33382de04d3c1",
"8500c51d37773f41",
"59278ea82b9a43dd",
"06fa7d14694b4def",
"4ae6228ee5c53caf",
"07e435f289f85dd3",
"d64b537daf6d7ddf"
],
"x": 134,
"y": 1359,
"w": 952,
"h": 162
},
{
"id": "b5422b8155a0c8d2",
"type": "ui_text_input",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "Qtd SKC:",
"label": " <font size =\"5\"><b> Qnt de SKC: </b> </font> ",
"tooltip": "",
"group": "e666424d8e8a2464",
"order": 4,
"width": 4,
"height": 1,
"passthru": true,
"mode": "number",
"delay": "100",
"topic": "",
"sendOnBlur": true,
"className": "",
"topicType": "str",
"x": 240,
"y": 1440,
"wires": [
[
"8500c51d37773f41"
]
]
},
{
"id": "326badf4c93713e8",
"type": "debug",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "Loop For",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 980,
"y": 1400,
"wires": []
},
{
"id": "67e33382de04d3c1",
"type": "function",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "CRC",
"func": "var n_skc = msg.payload;\nvar crc_final;\nvar h;\nvar l;\n\nfunction crc16(str) {\n const buf = Buffer.from(str, 'hex');\n let crc = 0xFFFF;\n for (let i = 0; i < buf.length; i++) {\n crc = crc ^ buf[i];\n for (let j = 0; j < 8; j++) {\n const temp = crc & 0x01;\n crc >>= 0x01;\n if (temp == 0x01) {\n crc ^= 0xA001;\n }\n }\n }\n return crc;\n}\n\nconst crc = crc16(msg.payload);\nconst arr = crc.toString(16).toUpperCase().substring(-4).split(\"\");\n\ncrc_final = msg.payload + arr.splice(-2).concat(arr).join(\"\");\n\ncrc_final = crc_final.slice(\"-4\").split(\"\");\n\nvar h_s = crc_final[0] + crc_final[1];\n\nh_s = parseInt(h_s, 16).toString();\nvar l_s = crc_final[2] + crc_final[3];\n\nl_s = parseInt(l_s, 16).toString();\n\n\nmsg.payload = Buffer.from([n_skc[0], 0x64, 0x00, 0x00, 0x00, h_s, l_s]);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 730,
"y": 1400,
"wires": [
[
"326badf4c93713e8",
"4ae6228ee5c53caf"
]
]
},
{
"id": "8500c51d37773f41",
"type": "function",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "Loop - Solicitar Status",
"func": "var n_skc = msg.payload;\nvar currentID = 1;\n\nfunction loop() {\n var buffer = Buffer.from([currentID, 0x64, 0x00, 0x00, 0x00]);\n node.send({payload: buffer});\n node.send([null,null]);\n\n if (currentID === n_skc) {\n msg.payload = n_skc;\n node.send([null, msg]); // Envia [null, msg] para reiniciar automaticamente\n }\n\n currentID++;\n if (currentID <= n_skc) {\n setTimeout(loop, 2500);\n }\n}\nloop();",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 470,
"y": 1400,
"wires": [
[
"67e33382de04d3c1"
],
[
"06fa7d14694b4def"
]
]
},
{
"id": "59278ea82b9a43dd",
"type": "link in",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "",
"links": [
"8500c51d37773f41"
],
"x": 395,
"y": 1460,
"wires": [
[
"8500c51d37773f41"
]
]
},
{
"id": "06fa7d14694b4def",
"type": "delay",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "",
"pauseType": "delay",
"timeout": "3",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"allowrate": false,
"outputs": 1,
"x": 580,
"y": 1460,
"wires": [
[
"59278ea82b9a43dd"
]
]
},
{
"id": "4ae6228ee5c53caf",
"type": "tcp out",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "TCP/IP",
"host": "192.168.108.45",
"port": "5300",
"beserver": "client",
"base64": false,
"end": false,
"tls": "",
"x": 1000,
"y": 1460,
"wires": []
},
{
"id": "07e435f289f85dd3",
"type": "comment",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "Qtd = 10",
"info": "",
"x": 240,
"y": 1400,
"wires": []
},
{
"id": "d64b537daf6d7ddf",
"type": "inject",
"z": "e1463c22.f5c5f8",
"g": "5f623a38400dd534",
"name": "10",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "10",
"payloadType": "num",
"x": 230,
"y": 1480,
"wires": [
[
"8500c51d37773f41"
]
]
},
{
"id": "e666424d8e8a2464",
"type": "ui_group",
"name": "Teste",
"tab": "0be9643108e709d3",
"order": 1,
"disp": false,
"width": 20,
"collapse": false,
"className": ""
},
{
"id": "0be9643108e709d3",
"type": "ui_tab",
"name": "Tabela",
"icon": "dashboard",
"order": 1,
"disabled": false,
"hidden": false
},
{
"id": "0e6b7172991e5b22",
"type": "MySQLdatabase",
"name": "",
"host": "127.0.0.1",
"port": "3306",
"db": "Status_SKC",
"tz": "",
"charset": "UTF8"
}
]