BiancoRoyal / node-red-contrib-modbus

maintained by PLUS for Node-RED - https://plus4nodered.com
https://www.npmjs.com/package/node-red-contrib-modbus
BSD 3-Clause "New" or "Revised" License
291 stars 108 forks source link

[Q] Reccomended way to query multiple devices Addresses #263

Closed Sineos closed 2 years ago

Sineos commented 3 years ago

Simple question to get some hint

I'm using a Modbus RTU USB stick to read around 30+ addresses on multiple IDs. The help file recommends to use the flex getter when querying more than 10 adresses. Being my first real contact with Modbus over serial and more than 5 addresses, I'd like to ask for some advice regarding the recommended approach:

  1. One Felx-Getter with multiple inputs grafik

  2. Multiple Flex-Getters reading only one value at dedicated register address

  3. Flex-Getter reading huge Quantity of addresses in one go, splitting the response into single values later

Any advice on the best approach is highly appreciated. Many thanks in advance

Your Environment

Please tell us about your environment:

biancode commented 3 years ago

Hi @Sineos, please check the Imports to learn more ;)

image image
Sineos commented 3 years ago

Many thanks for your kind reply @biancode. I have looked at these these flows before posting. This was what me led to my two approaches above:

  1. One Felx-Getter with multiple inputs
  2. Flex-Getter reading huge Quantity of addresses in one go, splitting the response into single values later

Is my understanding correct that a combination of

is the recommended approach?

  1. As there are 9999 registers within one FC, how many should typically be read in one go? 8 - 16?
  2. Should the inject-nodes be "staggered" to avoid stalling the bus or will it take care of such internally?
  3. Do multiple connections also make sense for a serial Modbus device?

Many thanks.

biancode commented 3 years ago
  • As there are 9999 registers within one FC, how many should typically be read in one go? 8 - 16? This depends on the device (sometimes just 50) and the Modbus Specs (I mean 125 per read/write max) Please, have a look to the free Modbus Specs to learn more!

  • Should the inject-nodes be "staggered" to avoid stalling the bus or will it take care of such internally? Depending on your client settings in Node-RED for Modbus, the client should take care about that. Especially the queues in our client will handle this. (parallel mode, communication per unitid, etc.)

  • Do multiple connections also make sense for a serial Modbus device? A serial device will not allow multiple connections. Just TCP will give you that option. On serial you should get "Port Not Open" on the second one. ;)

If you need to handle a lot registers, then it helps to use the Node-RED context (flow, global) and to build an information model inside Node-RED to store all states of the registers in there. With that, you could have a process to read/poll data from Modbus and to consume them from the structured context model. Split of concerns in other words.

A function node can include a loop of 100 to query 100 registers each from 0...10.000 and if it is done go ahead from the beginning and so on. But, think about if you really need all registers. Mostly you just need groups of registers like 10..110, 220..270 and so on. For that set up a sequence with the new sequence node and give it a try!

Happy wiring!

biancode commented 3 years ago

also #233

Sineos commented 3 years ago

Many thanks for coming back to this.

Actually I found that querying multiple registers is much less problematic with my adapter than querying multiple Unit-IDs. I ended up having a 1s delay between switching the Unit-ID, otherwise the adapter would error out.

Is there no internal queuing in the sense of FIFO and sending the next query once the previous has been acknowledged / returned?

biancode commented 3 years ago

There is a queue, but maybe you have the parallel mode on to send per UnitId. For serial that option of parallel UnitIds has to be switched to off.

biancode commented 3 years ago

"UnitId' in parallel" works with a queue on each UnitId and sends them in parallel per cycle if possible.

The order of UnitIds depends on msgs.

Option off sending: 1 2 3 2 1 3

Option on sending:

1,2,3 2,1,3

Modbus Gateways can handle that for example or TCP can do that - serial can normally not work in parallel.

dhruvjhalani95 commented 3 years ago

Hi!

I am looking to poll data from a PLC and a few energy meters over modbus RS485.

Problem statement: Reading group of registers from a PLC having unit id 1: 100 to 112, 200 to 212, 300 to 312. Reading another group of registers from energy meters (Schneider) having unit id 2, 3, 4, 5: 3061, 3062

How should I do that if I use a single modbus flex getter. How would I know as to which data is coming from which device if I use a single flex getter. Also can you suggest an example flow which I can refer to for this problem statement.

biancode commented 3 years ago

@dhruvjhalani95 have a look to the whole msg object in the debug, there you should find all what you need. Send the requests to the Flex-Getter with the right UnitId and take care about the sequential work of the client node by switching the parallel unitid to off for RS485.

Sineos commented 3 years ago

I'm using a serial device and the option of parallel UnitIDs is turned off. Found that I need to do kind of queening regarding the UnitIDs manually or the adapter would error out and go into a reconnect cycle.

@dhruvjhalani95 This is how I did something similar. Works for me but no guarantee that this is the best solution.

[{"id":"8b47c09e.dcd158","type":"modbus-flex-getter","z":"30780075.230cb","name":"","showStatusActivities":true,"showErrors":true,"logIOActivities":false,"server":"9aa67be1.3e8b3","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":true,"x":630,"y":360,"wires":[[],["32aeee9.83e9892"]]},{"id":"db8b574a.5a0a8","type":"function","z":"30780075.230cb","name":"PowerDemand","func":"// Define ModBus parameters\nconst Description = 'PowerDemand';\nconst FunctionCode= 4;\nconst UnitID = 1;\nconst StartRegister = 84;\nconst Addresses = 4;\n\n// Create message for Flex Getter\nmsg = {\n 'topic': Description,\n 'payload': {\n 'value': msg.payload,\n 'fc': FunctionCode,\n 'unitid': UnitID,\n 'address': StartRegister,\n 'quantity': Addresses\n }\n};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":300,"wires":[["8b47c09e.dcd158"]]},{"id":"32aeee9.83e9892","type":"switch","z":"30780075.230cb","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"PowerDemand","vt":"str"},{"t":"eq","v":"TotalActiveEnergy","vt":"str"},{"t":"eq","v":"PHMeter","vt":"str"},{"t":"eq","v":"ORPMeter","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":810,"y":360,"wires":[["54c617.ac19e9e8"],["913c8cbb.6dca88"],["2b0ff702.672e2"],["b82ca13a.b18328"]]},{"id":"54c617.ac19e9e8","type":"buffer-parser","z":"30780075.230cb","name":"","data":"payload.buffer","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"floatbe","name":"TotalPower","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"floatbe","name":"MaximumPower","offset":4,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"value","msgPropertyType":"str","resultType":"value","resultTypeType":"output","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":990,"y":300,"wires":[["99fcf9ca8d65ab94"]]},{"id":"eba9eca0.2a73e8","type":"inject","z":"30780075.230cb","name":"Inject after 5s / repeat 5s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":"5","topic":"","payloadType":"date","x":150,"y":300,"wires":[["db8b574a.5a0a8"]]},{"id":"2afe2c99.f1e26c","type":"function","z":"30780075.230cb","name":"PHMeter","func":"const Description = 'PHMeter';\nconst FunctionCode = 3;\nconst UnitID = 8;\nconst StartRegister = 5;\nconst Addresses = 4;\n\n// Create message for Flex Getter\nmsg = {\n 'topic': Description,\n 'payload': {\n 'value': msg.payload,\n 'fc': FunctionCode,\n 'unitid': UnitID,\n 'address': StartRegister,\n 'quantity': Addresses\n }\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":380,"wires":[["8b47c09e.dcd158"]]},{"id":"9558f410.528b88","type":"inject","z":"30780075.230cb","name":"Inject after 7s / repeat 5s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":"7","topic":"","payloadType":"date","x":150,"y":380,"wires":[["2afe2c99.f1e26c"]]},{"id":"2b0ff702.672e2","type":"buffer-parser","z":"30780075.230cb","name":"","data":"payload.buffer","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"int16be","name":"Temperature","offset":0,"length":1,"offsetbit":0,"scale":"/10","mask":""},{"type":"int16be","name":"PH","offset":4,"length":1,"offsetbit":0,"scale":"/100","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"value","msgPropertyType":"str","resultType":"value","resultTypeType":"output","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":990,"y":380,"wires":[["c6779af6e1443446","0876a15a8d9692e7"]]},{"id":"f1015254.fd10e8","type":"function","z":"30780075.230cb","name":"ORPMeter","func":"// Define ModBus parameters\nconst Description = 'ORPMeter';\nconst FunctionCode= 3;\nconst UnitID = 9;\nconst StartRegister = 8;\nconst Addresses = 1;\n\n// Create message for Flex Getter\nmsg ={\n 'topic': Description,\n 'payload': {\n 'value': msg.payload,\n 'fc': FunctionCode,\n 'unitid': UnitID,\n 'address': StartRegister,\n 'quantity': Addresses\n }\n};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":420,"wires":[["8b47c09e.dcd158"]]},{"id":"e68cc094.df6988","type":"inject","z":"30780075.230cb","name":"Inject after 8s / repeat 5s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":"8","topic":"","payloadType":"date","x":150,"y":420,"wires":[["f1015254.fd10e8"]]},{"id":"b82ca13a.b18328","type":"buffer-parser","z":"30780075.230cb","name":"","data":"payload.buffer","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"int16be","name":"ORP","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"value","msgPropertyType":"str","resultType":"value","resultTypeType":"output","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":990,"y":460,"wires":[["9f6873e81a45af8b"]]},{"id":"a1fe8177.7513b8","type":"function","z":"30780075.230cb","name":"TotalActiveEnergy","func":"// Define ModBus parameters\nconst Description = 'TotalActiveEnergy';\nconst FunctionCode= 4;\nconst UnitID = 1;\nconst StartRegister = 342;\nconst Addresses = 4;\n\n// Create message for Flex Getter\nmsg ={\n 'topic': Description,\n 'payload': {\n 'value': msg.payload,\n 'fc': FunctionCode,\n 'unitid': UnitID,\n 'address': StartRegister,\n 'quantity': Addresses\n }\n};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":340,"wires":[["8b47c09e.dcd158"]]},{"id":"dd14a043.23a38","type":"inject","z":"30780075.230cb","name":"Inject after 6s / repeat 5s","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":"6","topic":"","payloadType":"date","x":150,"y":340,"wires":[["a1fe8177.7513b8"]]},{"id":"913c8cbb.6dca88","type":"buffer-parser","z":"30780075.230cb","name":"","data":"payload.buffer","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"floatbe","name":"TotalActiveEnergy","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"value","msgPropertyType":"str","resultType":"value","resultTypeType":"output","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":990,"y":340,"wires":[["77ceee9df93b9b4c"]]},{"id":"9aa67be1.3e8b3","type":"modbus-client","name":"ModBus USB Gateway","clienttype":"serial","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB0","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"10","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":false}]

For parsing the buffer this node is used: https://flows.nodered.org/node/node-red-contrib-buffer-parser

dhruvjhalani95 commented 3 years ago

@dhruvjhalani95 have a look to the whole msg object in the debug, there you should find all what you need. Send the requests to the Flex-Getter with the right UnitId and take care about the sequential work of the client node by switching the parallel unitid to off for RS485.

This works for me. Thank you so much for your help.

The system is working fine since a few days now. Hoping its a stable solution to my problem

@biancode

anversoft commented 3 years ago

In addition, you can use the new flex-sequencer that automatically manages the flow of multiple address requests.

node-red-contrib-modbus = v5.14.1

dhruvjhalani95 commented 3 years ago

@dhruvjhalani95 have a look to the whole msg object in the debug, there you should find all what you need. Send the requests to the Flex-Getter with the right UnitId and take care about the sequential work of the client node by switching the parallel unitid to off for RS485.

This works for me. Thank you so much for your help.

The system is working fine since a few days now. Hoping its a stable solution to my problem

@biancode

Hi I noticed that after a few days the flex getter automatically goes into a reconnecting state with the approach that @biancode mentioned. I also want to know the relevence of Reconnect on timeout function in the modbus flex getter.

What if with multiple slave devices one of the device is turned off?

Modbus Flex Getter Function

Please refer to the screenshot I have attached herewith.

dhruvjhalani95 commented 3 years ago

Hi, It would be great if @biancode @Sineos @anversoft any of you could assist me with this. I am stuck due to this in the production environment.

anversoft commented 3 years ago

Hi,

the timeout box indicates after how many milliseconds the slave must reply to receive a valid reply. From the amount of devices you have attached and the baud rate I deduce that the distance from the master is quite long. I advise you to increase the Timeout (e.g. 3000 ms) and click on the Reconnect on Timeout box (It is used to reconnect the master after it has not received any response from a slave) and the Reconnect Timeout to 500 ms.

anversoft commented 3 years ago

Also I recommend you to use this approach, it drastically reduces memory consumption and guarantees that the buffer will never fill up beyond what you requested.

image

[{"id":"f0131f16.70b96","type":"modbus-flex-sequencer","z":"bf644d16.1fc6b","name":"","sequences":[{"name":"1","unitid":"1","fc":"FC1","address":"4","quantity":"5"},{"name":"2","unitid":"3","fc":"FC2","address":"5","quantity":"6"}],"server":"2e5e8f26.89653","showStatusActivities":false,"showErrors":false,"logIOActivities":false,"useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":540,"y":300,"wires":[["75f9b163.aadc7","e2789d05.83284"],[]]},{"id":"98d52420.baa598","type":"inject","z":"bf644d16.1fc6b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"","payloadType":"date","x":300,"y":300,"wires":[["f0131f16.70b96"]]},{"id":"75f9b163.aadc7","type":"join","z":"bf644d16.1fc6b","name":"","mode":"custom","build":"object","property":"","propertyType":"full","key":"name","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":780,"y":300,"wires":[["a199c840.31f288"]]},{"id":"a199c840.31f288","type":"function","z":"bf644d16.1fc6b","name":"Data Processing","func":"\nreturn [msg, msg, msg, msg, msg, msg];","outputs":6,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1240,"y":280,"wires":[["69f0d817.908a58"],["4238f86c.4b7948"],["1f1910f6.1c96ef"],["f425a752.149d08"],["ba980ea3.8f013"],["7dba0f55.78456"]]},{"id":"69f0d817.908a58","type":"function","z":"bf644d16.1fc6b","name":"Machine 1","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":260,"wires":[[]]},{"id":"4238f86c.4b7948","type":"function","z":"bf644d16.1fc6b","name":"Machine 2","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":300,"wires":[[]]},{"id":"1f1910f6.1c96ef","type":"function","z":"bf644d16.1fc6b","name":"Machine 3","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":340,"wires":[[]]},{"id":"f425a752.149d08","type":"function","z":"bf644d16.1fc6b","name":"Machine 4","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":380,"wires":[[]]},{"id":"ba980ea3.8f013","type":"function","z":"bf644d16.1fc6b","name":"Machine 5","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1480,"y":420,"wires":[[]]},{"id":"21e9f332.527c2c","type":"modbus-queue-info","z":"bf644d16.1fc6b","name":"All Queues","topic":"","unitid":1,"queueReadIntervalTime":1000,"lowLowLevel":25,"lowLevel":75,"highLevel":150,"highHighLevel":300,"server":"2e5e8f26.89653","errorOnHighLevel":false,"showStatusActivities":false,"updateOnAllQueueChanges":false,"updateOnAllUnitQueues":true,"x":490,"y":560,"wires":[[]]},{"id":"e2789d05.83284","type":"trigger","z":"bf644d16.1fc6b","name":"Watchdog","op1":"","op2":"0","op1type":"nul","op2type":"str","duration":"2","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":480,"y":440,"wires":[["3a5a6bd0.63ffa4","7dba0f55.78456"]]},{"id":"3a5a6bd0.63ffa4","type":"function","z":"bf644d16.1fc6b","name":"Send the msg.resetQueue = true","func":"msg.resetQueue = true;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":500,"wires":[["21e9f332.527c2c"]]},{"id":"846450b2.f81cb","type":"comment","z":"bf644d16.1fc6b","name":"If no message arrives for x seconds, reset the queue","info":"","x":510,"y":400,"wires":[]},{"id":"7dba0f55.78456","type":"delay","z":"bf644d16.1fc6b","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":860,"y":500,"wires":[["f0131f16.70b96"]]},{"id":"195ac5e9.c01f6a","type":"comment","z":"bf644d16.1fc6b","name":"Resend the messages after the data has been successfully processed","info":"","x":1050,"y":540,"wires":[]},{"id":"63c8e8fd.920da8","type":"comment","z":"bf644d16.1fc6b","name":"Send only once","info":"","x":300,"y":260,"wires":[]},{"id":"f879f1fa.a754d","type":"comment","z":"bf644d16.1fc6b","name":"Set the number of messages in the \"After a number of message parts\"","info":"","x":930,"y":260,"wires":[]},{"id":"2e5e8f26.89653","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"tcpHost":"127.0.0.1","tcpPort":"502","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","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true}]

biancode commented 2 years ago

In addition, you can use the new flex-sequencer that automatically manages the flow of multiple address requests.

node-red-contrib-modbus = v5.14.1

more help and docs from v5.15.+ inside the node