Questions about Modbus - UI controls and forwarding signals?

I am using node-red-contrib-modbus and node-red-dashboard.

I need to implement:

  • dashboard controls for modbus devices (e.g. ON/OFF signals through Modbus via UI switch)
  • a modbus server that redirects received modbus signals to another device

I was wondering if anyone with more experience could advise me on how best to do this?

Which bits don't you know how to do?

I believe that the modbus package does have a server capability.

1 Like

The switch block has an input and an output, but don't know:

  • what goes on the input of the switch? I want to emit a signal to activate a physical relay switch, not interrupt a signal.
  • How to properly emit a control signal using Modbus. My reference code has a fair few Modbus reads, but the only Modbus writes are complicated.
  • How to re-broadcast from a Modbus server.

I presume you are talking about the dashboard ui_switch node. There is also a Switch node that is used to route input messages to different outputs. The purpose of the input on the ui_switch node is to allow the flow to control the switch position, if you want to do that. Feed in a value in msg.payload equal to the values that you have configured for On and Off inside the node and the switch will change state.
If you manually click the switch on the dashboard then it sends the On or Off payloads that you have configured.

I don't understand what you mean by that. You can only write or read values. If you want a change of state in the modbus device to trigger something in the flow then you will have to poll the value using modbus read.

Have you looked at the help text for the server node to see what it can do?

Thank you for clarifying that!

So the switch input is optional?

What I meant was, the Modbus server block needs to read a modbus signal (I can bodge that together), but I have no idea how to write that signal to anywhere; it needs to go to an external device as a Modbus signal.

Would it note be quicker to test it, just with inject and debug nodes, rather than ask here.

I don't know what you mean by that. I assume that the modbus server emulates a modbus device, so you can write and read to/from it. I don't think the node (they are nodes not blocks) reads stuff itself.

I would say that initially you should work on your modbus communications first. Working out the UI part to start / stop comms is fairly simple. So for the modbus comms. Modbus is NOT a client / server architecture. I know many of the nodes say server and what not but the protocol is a Master / Slave poll / response system. I assume you are using modbus TCP? or modbus RTU with TCP encapsulation? meaning that you intend to communicate from your NodeRed instance to your modbus device using the network connection, Not a serial protocol such as RS485 or 232? Assuming you are using modbus TCP of some sort. You can configure the modbus flex getter node to point from your NodeRed instance to the IP of the device you want to read from, On the port you want to use for Modbus. Usually this is port 502 but your device may be configured differently. Once you have a Getter Node configured and pointing to your modbus device you can set up a function node or flex node to poll your device for the registers the documentation tells you should contain data. here is an example of how to use the flex getter node

[{"id":"e14e3869.eb3a5","type":"modbus-flex-getter","z":"652f4e57.e3d538","name":"Modbus Flexible Read","showStatusActivities":false,"showErrors":true,"logIOActivities":false,"server":"fa873ff5.42afa","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":520,"y":400,"wires":[["ff88bff3.9f8ff","6f54a366.20bebc","ade16e02.cb6d18"],["f3f1b052.baf858","fa9a0149.27dc7"]]},{"id":"2eea6853.20c25","type":"inject","z":"652f4e57.e3d538","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":340,"wires":[["e38a7a4f.8c2f48"]]},{"id":"e38a7a4f.8c2f48","type":"function","z":"652f4e57.e3d538","name":"FC1","func":"msg.payload = { 'fc': 1, 'unitid': 1, 'address': 0 , 'quantity': 16 }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":340,"wires":[["e14e3869.eb3a5"]]},{"id":"ff88bff3.9f8ff","type":"debug","z":"652f4e57.e3d538","name":"","active":false,"console":"false","complete":"payload","x":770,"y":300,"wires":[]},{"id":"f3f1b052.baf858","type":"debug","z":"652f4e57.e3d538","name":"","active":false,"console":"false","complete":"true","x":750,"y":440,"wires":[]},{"id":"fa9a0149.27dc7","type":"modbus-response","z":"652f4e57.e3d538","name":"","registerShowMax":20,"x":790,"y":480,"wires":[]},{"id":"ade16e02.cb6d18","type":"debug","z":"652f4e57.e3d538","name":"","active":true,"console":"false","complete":"true","x":750,"y":380,"wires":[]},{"id":"6f54a366.20bebc","type":"debug","z":"652f4e57.e3d538","name":"","active":false,"console":"false","complete":"responseBuffer","x":800,"y":340,"wires":[]},{"id":"8ed3e78.4db9a98","type":"inject","z":"652f4e57.e3d538","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":380,"wires":[["de331f5c.d08768"]]},{"id":"de331f5c.d08768","type":"function","z":"652f4e57.e3d538","name":"FC2","func":"msg.payload = { 'fc': 2, 'unitid': 1, 'address': 0 , 'quantity': 8 }\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":380,"wires":[["e14e3869.eb3a5"]]},{"id":"3c4efee.7c84982","type":"inject","z":"652f4e57.e3d538","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":420,"wires":[["ef723150.a5224"]]},{"id":"ef723150.a5224","type":"function","z":"652f4e57.e3d538","name":"FC3","func":"msg.payload = { 'fc': 3, 'unitid': 1, 'address': 0 , 'quantity': 8 }\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":420,"wires":[["e14e3869.eb3a5"]]},{"id":"f370e948.f19418","type":"inject","z":"652f4e57.e3d538","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":460,"wires":[["ac2f1711.01ace"]]},{"id":"ac2f1711.01ace","type":"function","z":"652f4e57.e3d538","name":"FC4","func":"msg.payload = { 'fc': 4, 'unitid': 1, 'address': 0 , 'quantity': 10 }\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":460,"wires":[["e14e3869.eb3a5"]]},{"id":"fa873ff5.42afa","type":"modbus-client","z":"652f4e57.e3d538","name":"Modbus Local Read Client","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"tcpHost":"127.0.0.1","tcpPort":"15502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":1,"commandDelay":1,"clientTimeout":1000,"reconnectTimeout":2000}]
1 Like

In what way is modbus not a client/server architecture?

@Colin

Because the protocol is a master slave. The only way to get a modbus slave device is if the modbus master specifically sends a message saying "hey device 2, give me the values in your holding registers 30 for the next 204 registers" ... then the slave device sends back "Im device 2, and the values you want are [bunch of values in a row]. If the master doesnt poll the slave devices.. they dont communicate. The slave devices dont even respond with anything but the values in the registers.. If the master did not specifically initiate the poll request, anything coming from a slave device is garbage because there is no way to know what registers its sending. Its not a Server / Client. The modbus specs call it master slave.. because the master MUST initiate the response

What is the difference between that and an HTTP GET request to a web server, or an SQL query to a MYSQL server?

Its a reasonable question and there is some overlap. But the question that you are asking is comparing a network communication Master, and a Client. And viewing the Slave and Server as essentially the same. A server in most cases can serve the requests of many clients. i.e. Many users sql queries, or many http GET hits on an api url from many clients. In a master slave network there is only a single master, sometimes the master is a group of devices in an elevated hierarchy (so called Multi-Master) but virtually all master slave networks can only have a single master with many slaves. Another difference in master slave configuration is that the master controls all network traffic, makes all requests and controls the timing of communications. This can substantially influence the behavior of the slave nodes and affect the data each slave is using. There is a decently concise explanation here Industrial Ethernet Guide - Client/Server Vs. Master/Slave - Copperhill

Can one not have multiple modbus clients fetching data from one modbus device when using modbus TCP?

@Colin
excellent question,
So the technical answer per the modbus standard is No.. but but.. I know I have done it plenty of times myself. The reason that modbus TCP will allow the use of more than one master is essentially an exploit of a system that was built on serial networking and that has been adapted to TCP by taking modbus commands and wrapping them in a TCP packet. The slave has no idea of master identification. Just that it has to respond to a valid command. A serial network wont allow more than one master because two of them on the same connection will just step all over each other and none of the slaves will get a complete command. It works in TCP because an entire modbus command can be sent in a packet or two. and the response simply goes back to the address that sent the command. So Yes you can have more than one master in modbus TCP. Not because it is actually supported.. its just because modbus is so dumb that its fine with being exploited.

1 Like

In the guide you linked to above, it says
"Ethernet/IP and Modbus/TCP are based on a Client/Server architecture"
so the author there appears to agree with my view.

Yes.. and the modbus committee also directed that all references of master / slave must be removed from documentation and participating member companies are directed to replace any modbus references of master slave with client server. You arent wrong to think of modbus as a client / server. Especially when using TCP or RTU with TCP Encapsulation. I am just old and have spent many a day away from home troubleshooting modbus and mostly over RS485. If you ever find yourself doing modbus serial. I would emplore you.. dont run two clients on a serial sensor daisy chain. the master slave features of such a configuration will have great difficulty.

Absolutely right.
Serial modbus is a pain in the proverbial.

It is a Modbus TCP master/slave config, and I've tried to approximate what I want using @PLCMercenary's example, but I keep getting "Is Not A Valid Memory Write Message To Server" EDIT: That's about as far as I've got; it mostly outputs arrays of zeroes; sometimes the server outputs packets like the following:

{"type":"holding","message":{"topic":"gflow_temperature","messageId":"644834900671dce7f468ad80","payload":[196,80,125,26,0,0,0,64759],"queueLengthByUnitId":{"unitId":1,"queueLength":0},"queueUnitId":1,"unitId":1,"modbusRequest":{"unitid":1,"fc":3,"address":0,"quantity":8,"emptyMsgOnFail":false,"keepMsgProperties":false,"messageId":"644834900671dce7f468ad80"},"responseBuffer":{"data":[196,80,125,26,0,0,0,64759],"buffer":[0,196,0,80,0,125,0,26,0,0,0,0,0,0,252,247]},"_msgid":"e0f1b52a9042359b"},"payload":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"_msgid":"88044c57d11bd5d8"}

Code is included in the next post.

[
    {
        "id": "b5f1325c1c7bc513",
        "type": "inject",
        "z": "37f1be47da815ae1",
        "name": "Get Silo Level for server",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "gsilo_level_0_1000",
        "payload": "",
        "payloadType": "date",
        "x": 150,
        "y": 620,
        "wires": [
            [
                "9387d09127bac253"
            ]
        ]
    },
    {
        "id": "9387d09127bac253",
        "type": "function",
        "z": "37f1be47da815ae1",
        "name": "FC3 - Read Holding Registers",
        "func": "msg.payload = { 'fc': 3, 'unitid': 1, 'address': 0 , 'quantity': 8 }\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 450,
        "y": 620,
        "wires": [
            [
                "4d8e9765b1d3c587"
            ]
        ]
    },
    {
        "id": "4d8e9765b1d3c587",
        "type": "modbus-flex-getter",
        "z": "37f1be47da815ae1",
        "name": "Modbus Flexible Read",
        "showStatusActivities": false,
        "showErrors": true,
        "showWarnings": true,
        "logIOActivities": false,
        "server": "d8b07ddb.c44d4",
        "useIOFile": false,
        "ioFile": "",
        "useIOForPayload": false,
        "emptyMsgOnFail": false,
        "keepMsgProperties": false,
        "delayOnStart": false,
        "startDelayTime": "",
        "x": 760,
        "y": 640,
        "wires": [
            [
                "5d59c82badda5237"
            ],
            []
        ]
    },
    {
        "id": "5d59c82badda5237",
        "type": "modbus-server",
        "z": "37f1be47da815ae1",
        "name": "",
        "logEnabled": true,
        "hostname": "0.0.0.0",
        "serverPort": 10502,
        "responseDelay": 100,
        "delayUnit": "ms",
        "coilsBufferSize": 10000,
        "holdingBufferSize": 10000,
        "inputBufferSize": 10000,
        "discreteBufferSize": 10000,
        "showErrors": true,
        "x": 980,
        "y": 640,
        "wires": [
            [
                "d04855631aecc823"
            ],
            [],
            [],
            [],
            []
        ]
    },
    {
        "id": "d04855631aecc823",
        "type": "debug",
        "z": "37f1be47da815ae1",
        "name": "Server output",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1260,
        "y": 640,
        "wires": []
    },
    {
        "id": "e275a6f7585e24b2",
        "type": "function",
        "z": "37f1be47da815ae1",
        "name": "FC3 - Read Holding Registers",
        "func": "msg.payload = { 'fc': 3, 'unitid': 1, 'address': 0 , 'quantity': 8 }\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 430,
        "y": 740,
        "wires": [
            [
                "4d8e9765b1d3c587"
            ]
        ]
    },
    {
        "id": "f3ae13770ac423ca",
        "type": "inject",
        "z": "37f1be47da815ae1",
        "name": "Get Flow Temp for server",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "gflow_temperature",
        "payload": "",
        "payloadType": "date",
        "x": 140,
        "y": 740,
        "wires": [
            [
                "e275a6f7585e24b2"
            ]
        ]
    },
    {
        "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
    }
]

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.