Slave (server) flows:
[
{
"id": "3decb5f518882686",
"type": "subflow",
"name": "live debug",
"info": "",
"category": "",
"in": [
{
"x": 240,
"y": 160,
"wires": [
{
"id": "45dbf990.ba2408"
}
]
}
],
"out": [
{
"x": 560,
"y": 160,
"wires": [
{
"id": "45dbf990.ba2408",
"port": 1
}
]
}
],
"env": [
{
"name": "debug_name",
"type": "str",
"value": "Debug",
"ui": {
"label": {
"en-US": "Name"
},
"type": "input",
"opts": {
"types": [
"str"
]
}
}
},
{
"name": "debug_property",
"type": "str",
"value": "msg",
"ui": {
"label": {
"en-US": "Property Path"
},
"type": "input",
"opts": {
"types": [
"str"
]
}
}
},
{
"name": "pass_debug_property",
"type": "bool",
"value": "false",
"ui": {
"label": {
"en-US": "Pass Property Path"
},
"type": "input",
"opts": {
"types": [
"bool"
]
}
}
}
],
"meta": {},
"color": "#DDAA99"
},
{
"id": "45dbf990.ba2408",
"type": "function",
"z": "3decb5f518882686",
"name": "format time nicely",
"func": "let msg1 = {payload: RED.util.cloneMessage(msg)};\nmsg1.payload.debug_property = msg.debug_property || env.get(\"debug_property\") || \"msg\"\ntry {\n msg1.payload.debug_property_array = RED.util.normalisePropertyExpression(msg1.payload.debug_property)\n}\ncatch(error){\n msg.payload = error;\n msg1.payload.error = error;\n}\nmsg1.payload.debug_name = env.get(\"debug_name\") || msg._msgid || \"name error\"\nmsg1.payload.pass_debug_property = env.get(\"pass_debug_property\") || false\nreturn [msg1,msg];",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 390,
"y": 120,
"wires": [
[
"50da04b3.af25fc"
],
[]
]
},
{
"id": "50da04b3.af25fc",
"type": "websocket out",
"z": "3decb5f518882686",
"name": "",
"server": "985ecbc7.67a138",
"client": "",
"x": 680,
"y": 120,
"wires": []
},
{
"id": "1787be40.e87842",
"type": "http in",
"z": "3decb5f518882686",
"name": "",
"url": "/debug",
"method": "get",
"upload": false,
"swaggerDoc": "",
"x": 330,
"y": 40,
"wires": [
[
"1857548e.e7a8ab"
]
]
},
{
"id": "1857548e.e7a8ab",
"type": "template",
"z": "3decb5f518882686",
"name": "Simple Web Page",
"field": "payload",
"fieldType": "msg",
"format": "html",
"syntax": "mustache",
"template": "<!DOCTYPE HTML>\n<html>\n <head>\n <style>\n span.debug_name {\n color: black;\n }\n span.debug_error{\n color: red;\n }\n span.debug_date{\n color:blue;\n }\n pre[id^='json_data_']{\n font-size:20px;\n background-color: ghostwhite;\n border: 1px solid silver;\n padding: 10px 20px;\n margin: 20px; \n white-space: pre-wrap;\n }\n .json-key {\n color: olive;\n }\n .json-value {\n color: navy;\n }\n .json-string {\n color: brown;\n }\n\n </style>\n <title>Debug Messages</title>\n <script type=\"text/javascript\">\n var ws;\n var wsUri = \"ws:\";\n var loc = window.location;\n console.log(loc);\n if (loc.protocol === \"https:\") { wsUri = \"wss:\"; }\n // This needs to point to the web socket in the Node-RED flow\n wsUri += \"//\" + loc.host + loc.pathname.replace(\"debug\",\"ws/debug\");\n\n function wsConnect() {\n console.log(\"connect\",wsUri);\n ws = new WebSocket(wsUri);\n ws.onmessage = function(msg) {\n segments = document.getElementById('json_data').innerHTML;\n var paused = document.getElementById('paused').innerText === \"Pause\";\n var data;\n var line = \"\";\n var error = \"\";\n var date = new Date().toISOString();\n // parse the incoming message as a JSON object\n try { \n data = JSON.parse(msg.data);\n } catch (json_error) {\n data = {error: [\"json parse error\", json_error]};\n }; \n if(!data.error){\n var debug_name = data.debug_name ;\n var debug_property = data.debug_property_array;\n var original_property = data.debug_property;\n if(!data.pass_debug_property) delete data.debug_property;\n delete data.pass_debug_property;\n delete data.debug_property_array;\n delete data.debug_name;\n debug_property.slice(1).forEach((prop, index) => {\n if(data[prop]) { \n data = data[prop];\n }else{\n error = \"Property path Error on or after - \" + debug_property[index];\n }\n });\n\n line = `<div id=\"element${date}\">\n <hr/>\n <span class=\"debug_name\">${debug_name}</span> <span class=\"debug_date\">${date}</span> \n <span class=\"debug_orginal\">${original_property}</span> <span class=\"debug_error\">${error}</span> \n <button title=\"copy\" alt=\"copy\" onclick=\"copy_text('json_data_${date}')\"> ❒ </button>\n <button title=\"delete\" alt=\"delete\" onclick=\"delete_text('element${date}')\"> ❌ </button> \n <span class=\"json_data\">\n <pre id=\"json_data_${date}\"><code id=data>${library.json.prettyPrint(data)}</code></pre></span> \n </div>`;\n }else{\n line = `<div id=\"element${date}\">\n <hr/>\n <span class=\"debug_error\">Error: ${JSON.stringify(data.error)}</span> <span class=\"debug_date\">${date}</span> \n <button title=\"delete\" alt=\"delete\" onclick=\"delete_text('element${date}')\"> ❌ </button> \n </div>`;\n }\n // append line to segment\n if(paused){\n document.getElementById('json_data').innerHTML = line + segments;\n }\n }\n ws.onopen = function() {\n // update the status div with the connection status\n document.getElementById('status').innerHTML = \"connected\";\n //ws.send(\"Open for data\");\n console.log(\"connected\");\n }\n ws.onclose = function() {\n // update the status div with the connection status\n document.getElementById('status').innerHTML = \"not connected\";\n // in case of lost connection tries to reconnect every 3 secs\n setTimeout(wsConnect,3000);\n }\n }\n function copy_text(input) {\n // Get the text field\n var copyText = document.getElementById(input).innerText;\n var dummy = document.createElement(\"textarea\");\n document.body.appendChild(dummy);\n dummy.value = copyText;\n dummy.select();\n document.execCommand(\"copy\");\n document.body.removeChild(dummy);\n alert(\"Copied Data to Clipboard\\n\\n\" + copyText)\n }\n function delete_text(input) {\n // Get the text field\n document.getElementById(input).innerHTML = \"\";\n }\n if (!library) var library = {};\n library.json = {\n replacer: function(match, pIndent, pKey, pVal, pEnd) {\n var key = '<span class=json-key>\"';\n var val = '<span class=json-value>';\n var str = '<span class=json-string>';\n var r = pIndent || '';\n if (pKey)\n r = r + key + pKey.replace(/[\": ]/g, '') + '\"</span>: ';\n if (pVal)\n r = r + (pVal[0] == '\"' ? str : val) + pVal + '</span>';\n return r + (pEnd || '');\n },\n prettyPrint: function(obj) {\n var jsonLine = /^( *)(\"[\\w]+\": )?(\"[^\"]*\"|[\\w.+-]*)?([,[{])?$/mg;\n return JSON.stringify(obj, null, 4)\n .replace(/&/g, '&').replace(/\\\\\"/g, '"')\n .replace(/</g, '<').replace(/>/g, '>')\n .replace(jsonLine, library.json.replacer);\n }\n \n };\n function pause() {\n var element_paused = document.getElementById(\"paused\");\n paused = (element_paused.innerText === \"Resume\") ? \"Pause\" : \"Resume\";\n element_paused.innerText = paused;\n }\n \n </script>\n </head>\n <body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n <font face=\"Arial\">\n <h1>Debug Messages <button id=\"paused\" value=\"pause\" onclick=\"pause()\">Pause</button> </h1>\n <div id=\"json_data\"></div>\n <hr/>\n <div id=\"status\">unknown</div>\n </font>\n </body>\n</html>\n",
"x": 550,
"y": 40,
"wires": [
[
"42a28745.bd5d78"
]
]
},
{
"id": "42a28745.bd5d78",
"type": "http response",
"z": "3decb5f518882686",
"name": "",
"x": 730,
"y": 40,
"wires": []
},
{
"id": "985ecbc7.67a138",
"type": "websocket-listener",
"z": "37f1be47da815ae1",
"path": "/ws/debug",
"wholemsg": "false"
},
{
"id": "37f1be47da815ae1",
"type": "tab",
"label": "Modbus Server",
"disabled": false,
"info": "",
"env": []
},
{
"id": "49c18e3ad19acbda",
"type": "group",
"z": "37f1be47da815ae1",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"c5a0fdf6b8bb9850",
"ed36434616147015",
"248c46681d071246",
"62340306b6eaf6f0",
"f5457920917eb0f0",
"8512686ef7c21c3e",
"4cefa7be49623107",
"c396898e828dee2a",
"85ebfdbffa8d984b",
"8916057c5b4e9270",
"5d59c82badda5237",
"d04855631aecc823",
"3acabffc83517f90",
"134c7774ebcf3011",
"4486d6e85a7d3e1b",
"889f040436167658",
"99eb4635c1919a85",
"b3fc5514304b5242",
"a28bbddf98fa5939",
"290109b199a881e3",
"4f9540eaeb13509a",
"ad73abc4d0bad61d",
"f4d3a0e92cc61648",
"509b5b5744b44bea"
],
"x": 14,
"y": 159,
"w": 1932,
"h": 444.5
},
{
"id": "5d59c82badda5237",
"type": "modbus-server",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "",
"logEnabled": true,
"hostname": "0.0.0.0",
"serverPort": 10502,
"responseDelay": 100,
"delayUnit": "ms",
"coilsBufferSize": "6",
"holdingBufferSize": "6",
"inputBufferSize": "6",
"discreteBufferSize": "6",
"showErrors": true,
"x": 1560,
"y": 540,
"wires": [
[
"d04855631aecc823",
"3acabffc83517f90"
],
[],
[],
[],
[]
]
},
{
"id": "d04855631aecc823",
"type": "debug",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Server output",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1820,
"y": 440,
"wires": []
},
{
"id": "f5457920917eb0f0",
"type": "switch",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "gboiler_running_condition",
"vt": "str"
},
{
"t": "eq",
"v": "gsilo_level_0_1000",
"vt": "str"
},
{
"t": "eq",
"v": "gmax_stoker_speed",
"vt": "str"
},
{
"t": "eq",
"v": "gpid_stoker_output",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 4,
"x": 1030,
"y": 340,
"wires": [
[
"c396898e828dee2a"
],
[
"4cefa7be49623107"
],
[
"8512686ef7c21c3e"
],
[
"62340306b6eaf6f0"
]
]
},
{
"id": "b3fc5514304b5242",
"type": "comment",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Boiler Heat Meter kW & mWh [req. Serial RS485]",
"info": "",
"x": 240,
"y": 260,
"wires": []
},
{
"id": "290109b199a881e3",
"type": "inject",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Get Silo Level",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "5",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "gsilo_level_0_1000",
"payload": "",
"payloadType": "date",
"x": 180,
"y": 320,
"wires": [
[
"889f040436167658"
]
]
},
{
"id": "889f040436167658",
"type": "link out",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Sensors out",
"mode": "link",
"links": [
"37db5198429b7396"
],
"x": 445,
"y": 340,
"wires": []
},
{
"id": "99eb4635c1919a85",
"type": "link in",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Processor in",
"links": [
"da25e4e66d04561c"
],
"x": 745,
"y": 340,
"wires": [
[
"134c7774ebcf3011"
]
]
},
{
"id": "134c7774ebcf3011",
"type": "modbus-flex-getter",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Boiler_getter",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"server": "d8b07ddb.c44d4",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 870,
"y": 340,
"wires": [
[
"f5457920917eb0f0"
],
[]
]
},
{
"id": "4486d6e85a7d3e1b",
"type": "comment",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "<-- I/O through lvc_labels -->",
"info": "",
"x": 600,
"y": 340,
"wires": []
},
{
"id": "85ebfdbffa8d984b",
"type": "debug",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "debug 10",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1520,
"y": 280,
"wires": []
},
{
"id": "a28bbddf98fa5939",
"type": "inject",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Shutdown/Fireout",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "5",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "gboiler_running_condition",
"payload": "",
"payloadType": "date",
"x": 190,
"y": 200,
"wires": [
[
"889f040436167658"
]
]
},
{
"id": "4f9540eaeb13509a",
"type": "inject",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Stoker at maximum speed",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "5",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "gmax_stoker_speed",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 440,
"wires": [
[
"889f040436167658"
]
]
},
{
"id": "ad73abc4d0bad61d",
"type": "inject",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Averaged stoker speed",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "5",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "gpid_stoker_output",
"payload": "",
"payloadType": "date",
"x": 190,
"y": 520,
"wires": [
[
"889f040436167658"
]
]
},
{
"id": "c396898e828dee2a",
"type": "function",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Shutdown/Fireout scale",
"func": "// This node caused \"TypeError: Cannot read properties of undefined (reading 'length')\"\n// Fixed: https://discourse.nodered.org/t/is-not-a-valid-memory-write-message-to-server/77977/7?u=darren\n\nmsg.payload = { \n 'value': Number(msg.payload[0]),\n 'register': 'holding',\n 'address': 1,\n 'disableMsgOutput': 0 };\n \nreturn msg;\n\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1250,
"y": 280,
"wires": [
[
"85ebfdbffa8d984b",
"5d59c82badda5237"
]
]
},
{
"id": "4cefa7be49623107",
"type": "function",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Silo Level scale",
"func": "msg.payload = {\n 'value': msg.payload,\n 'register': 'holding',\n 'address': 2,\n 'disableMsgOutput': 0\n};\n\n// debug\n//msg.payload = {\n// 'value': 1337, 'register': 'holding',\n// 'address': 2, 'disableMsgOutput': 0\n//};\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1220,
"y": 320,
"wires": [
[
"8916057c5b4e9270",
"5d59c82badda5237"
]
]
},
{
"id": "8512686ef7c21c3e",
"type": "function",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Maximum stoker speed scale",
"func": "msg.payload = {\n 'value': msg.payload,\n 'register': 'holding',\n 'address': 3,\n 'disableMsgOutput': 0\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1260,
"y": 360,
"wires": [
[
"ed36434616147015",
"5d59c82badda5237"
]
]
},
{
"id": "62340306b6eaf6f0",
"type": "function",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "Averaged stoker speed scale",
"func": "msg.payload = {\n 'value': msg.payload,\n 'register': 'holding',\n 'address': 4,\n 'disableMsgOutput': 0\n};\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1260,
"y": 400,
"wires": [
[
"248c46681d071246",
"5d59c82badda5237"
]
]
},
{
"id": "c5a0fdf6b8bb9850",
"type": "comment",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "[Unavailable] Stoker at minimum speed",
"info": "",
"x": 190,
"y": 380,
"wires": []
},
{
"id": "8916057c5b4e9270",
"type": "debug",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "debug 11",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1520,
"y": 320,
"wires": []
},
{
"id": "ed36434616147015",
"type": "debug",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "debug 12",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1500,
"y": 360,
"wires": []
},
{
"id": "248c46681d071246",
"type": "debug",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "debug 13",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1500,
"y": 400,
"wires": []
},
{
"id": "3acabffc83517f90",
"type": "subflow:3decb5f518882686",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "",
"x": 1830,
"y": 540,
"wires": [
[]
]
},
{
"id": "c1f1279b6bad34ee",
"type": "comment",
"z": "37f1be47da815ae1",
"name": "Brown & Carroll Modbus re-server",
"info": "",
"x": 920,
"y": 100,
"wires": []
},
{
"id": "f4d3a0e92cc61648",
"type": "modbus-write",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"unitid": "1",
"dataType": "HoldingRegister",
"adr": "60",
"quantity": "1",
"server": "06fb380adab7a3ac",
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 1160,
"y": 520,
"wires": [
[],
[]
]
},
{
"id": "509b5b5744b44bea",
"type": "inject",
"z": "37f1be47da815ae1",
"g": "49c18e3ad19acbda",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "69",
"payloadType": "num",
"x": 970,
"y": 480,
"wires": [
[
"f4d3a0e92cc61648"
]
]
},
{
"id": "d8b07ddb.c44d4",
"type": "modbus-client",
"name": "Boiler_PLC",
"clienttype": "tcp",
"bufferCommands": true,
"stateLogEnabled": false,
"queueLogEnabled": false,
"failureLogEnabled": false,
"tcpHost": "192.168.127.2",
"tcpPort": "502",
"tcpType": "DEFAULT",
"serialPort": "/dev/ttyUSB",
"serialType": "RTU-BUFFERD",
"serialBaudrate": "9600",
"serialDatabits": "8",
"serialStopbits": "1",
"serialParity": "none",
"serialConnectionDelay": "100",
"serialAsciiResponseStartDelimiter": "",
"unit_id": "1",
"commandDelay": "1",
"clientTimeout": "1000",
"reconnectOnTimeout": true,
"reconnectTimeout": "2000",
"parallelUnitIdsAllowed": true
},
{
"id": "06fb380adab7a3ac",
"type": "modbus-client",
"name": "nodered_local_server",
"clienttype": "tcp",
"bufferCommands": true,
"stateLogEnabled": false,
"queueLogEnabled": false,
"failureLogEnabled": true,
"tcpHost": "127.0.0.1",
"tcpPort": "10502",
"tcpType": "DEFAULT",
"serialPort": "/dev/ttyUSB",
"serialType": "RTU-BUFFERD",
"serialBaudrate": "9600",
"serialDatabits": "8",
"serialStopbits": "1",
"serialParity": "none",
"serialConnectionDelay": "100",
"serialAsciiResponseStartDelimiter": "0x3A",
"unit_id": "1",
"commandDelay": "1",
"clientTimeout": "1000",
"reconnectOnTimeout": true,
"reconnectTimeout": "2000",
"parallelUnitIdsAllowed": true,
"showWarnings": true,
"showLogs": true
}
]