Modernize your legacy industrial data. Part 2. • FlowFuse

In part 1 of this series for the introduction and overview.) published recently, I introduced the topic of working with legacy industrial data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world. We looked at some of the challenges and how Node-RED with node-red-contrib-buffer-parser node can help.


This is a companion discussion topic for the original entry at https://flowfuse.com/blog/2023/09/modernize-your-legacy-industrial-data-part2

As promised - the accompanying flows for the article.

NOTE: These flows require the following nodes be installed:

The overall demonstration was created without any Modbus device to PLC. Instead, a Modbus server was created in Node-RED to permit these demos to be developed. This flow is shared across all of the demos. Here is the flow for Modbus Server

[{"id":"9fa221f6a7f188f9","type":"modbus-write","z":"0906658a180c94fa","name":"","showStatusActivities":false,"showErrors":false,"showWarnings":true,"unitid":"","dataType":"MHoldingRegisters","adr":"1","quantity":"10","server":"bfc6a4331e29e6a4","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":300,"y":920,"wires":[["40b053612bcbbf26"],["b9df3d61c8fb3731"]]},{"id":"2938e06bd26c0753","type":"inject","z":"0906658a180c94fa","name":"Check data","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":980,"wires":[["279a72ce4467a04d"]]},{"id":"279a72ce4467a04d","type":"modbus-getter","z":"0906658a180c94fa","name":"","showStatusActivities":false,"showErrors":true,"showWarnings":true,"logIOActivities":false,"unitid":"","dataType":"HoldingRegister","adr":"1","quantity":"10","server":"bfc6a4331e29e6a4","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":320,"y":980,"wires":[["6e36b592b6bd32da"],["3f5ac6718d28740e"]]},{"id":"6e36b592b6bd32da","type":"debug","z":"0906658a180c94fa","name":"debug 4","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":980,"wires":[]},{"id":"40b053612bcbbf26","type":"debug","z":"0906658a180c94fa","name":"debug 5","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":900,"wires":[]},{"id":"b9df3d61c8fb3731","type":"debug","z":"0906658a180c94fa","name":"debug 6","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":940,"wires":[]},{"id":"3f5ac6718d28740e","type":"debug","z":"0906658a180c94fa","name":"debug 7","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":1040,"wires":[]},{"id":"3abce8e1a7bb122d","type":"debug","z":"0906658a180c94fa","name":"debug 14","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":700,"wires":[]},{"id":"1f5dd831b3206de1","type":"modbus-flex-server","z":"0906658a180c94fa","name":"","logEnabled":false,"serverAddress":"0.0.0.0","serverPort":"11503","responseDelay":"2","unitId":1,"delayUnit":"ms","coilsBufferSize":20000,"registersBufferSize":20000,"minAddress":0,"splitAddress":10000,"funcGetCoil":"function getFlexCoil(addr, unitID) {\n\tif (unitID === node.unitId && \n\t\taddr >= node.minAddress && \n\t\taddr <= node.splitAddress) { \n\n\t\treturn node.coils.readUInt8(addr * node.bufferFactor) \n\t}  \n}","funcGetDiscreteInput":"function getFlexDiscreteInput(addr, unitID) {\n\taddr += node.splitAddress\n\tif (unitID === node.unitId && \n\t\taddr >= node.splitAddress && \n\t\taddr <= node.splitAddress * 2) { \n\n\t\treturn node.coils.readUInt8(addr * node.bufferFactor) \n\t}  \n}","funcGetInputRegister":"function getFlexInputRegister(addr, unitID) { \n\tif (unitID === node.unitId && \n\t\taddr >= node.minAddress && \n\t\taddr <= node.splitAddress) { \n\n\t\treturn node.registers.readUInt16BE(addr * node.bufferFactor)  \n\t} \n}","funcGetHoldingRegister":"function getFlexHoldingRegsiter(addr, unitID) { \n\taddr += node.splitAddress\n\n\tlet a = 0\n\tfor (let index = 0; index < 1600; index++) {\n\t\ta = Math.sin(index+1)\n\t\ta /= 2\n\t}\n\n\tif (unitID === node.unitId && \n\t\taddr >= node.splitAddress && \n\t\taddr <= node.splitAddress * 2) { \n\t\treturn node.registers.readUInt16BE(addr * node.bufferFactor)  \n\t} \n}","funcSetCoil":"function setFlexCoil(addr, value, unitID) { \n\tif (unitID === node.unitId && \n\t\taddr >= node.minAddress && \n\t\taddr <= node.splitAddress) { \n\n\t\tnode.coils.writeUInt8(value, addr * node.bufferFactor)  \n\t} \n}","funcSetRegister":"function setFlexRegister(addr, value, unitID) { \n\taddr += node.splitAddress\n\tif (unitID === node.unitId && \n\t\taddr >= node.splitAddress && \n\t\taddr <= node.splitAddress * 2) { \n\n\t\tnode.registers.writeUInt16BE(value, addr * node.bufferFactor)  \n\t} \n}","showErrors":true,"x":160,"y":660,"wires":[[],[],[],[],["3abce8e1a7bb122d"]]},{"id":"23a4e1eede2fe719","type":"inject","z":"0906658a180c94fa","name":"Write data","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":"3","topic":"","payload":"","payloadType":"date","x":130,"y":800,"wires":[["b23ec3f5430f7b90"]]},{"id":"b23ec3f5430f7b90","type":"buffer-maker","z":"0906658a180c94fa","name":"","specification":"spec","specificationType":"ui","items":[{"name":"item1","type":"uint32be","length":1,"dataType":"num","data":"10"},{"name":"item2","type":"uint32be","length":1,"dataType":"num","data":"250"},{"name":"item3","type":"uint32be","length":1,"dataType":"num","data":"3000"},{"name":"item4","type":"uint32be","length":1,"dataType":"num","data":"2000"},{"name":"item5","type":"uint32be","length":1,"dataType":"num","data":"1000"}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","x":310,"y":800,"wires":[["3a5d01a34d4bc1d8","7391110d7befd297"]]},{"id":"3a5d01a34d4bc1d8","type":"buffer-parser","z":"0906658a180c94fa","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"uint16be","name":"item1","offset":0,"length":-1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"value","resultTypeType":"return","multipleResult":true,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":310,"y":840,"wires":[["a32453c03faaff35","9fa221f6a7f188f9"]]},{"id":"a32453c03faaff35","type":"debug","z":"0906658a180c94fa","name":"debug 16","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":840,"wires":[]},{"id":"7391110d7befd297","type":"debug","z":"0906658a180c94fa","name":"debug 17","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":800,"wires":[]},{"id":"e61cb03ea476ae51","type":"comment","z":"0906658a180c94fa","name":"Setup data for PT2 article","info":"","x":170,"y":760,"wires":[]},{"id":"77abefe9e93f3467","type":"comment","z":"0906658a180c94fa","name":"Modbus server for legacy Data to IIoT articles","info":"","x":230,"y":600,"wires":[]},{"id":"bfc6a4331e29e6a4","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"11503","tcpType":"DEFAULT","serialPort":"COM6","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}]

Here is the flow that accompanies Image 6: block reads, smart processing, no-code solution

[{"id":"55a33491f8c590bb","type":"mqtt out","z":"0906658a180c94fa","name":"machine/1/metrics/#","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"214ab9a6ce4e03b0","x":1840,"y":3440,"wires":[]},{"id":"9198c15383890f7c","type":"buffer-parser","z":"0906658a180c94fa","name":"","data":"payload.data","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"uint32be","name":"machine/1/metrics/count","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"uint32be","name":"machine/1/metrics/cycletime","offset":4,"length":1,"offsetbit":0,"scale":"/100","mask":""},{"type":"uint32be","name":"machine/1/metrics/productiontime","offset":8,"length":1,"offsetbit":0,"scale":"/100","mask":""},{"type":"uint32be","name":"machine/1/metrics/runtime","offset":12,"length":1,"offsetbit":0,"scale":"/100","mask":""},{"type":"uint32be","name":"machine/1/metrics/stoptime","offset":16,"length":1,"offsetbit":0,"scale":"/100","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"value","resultTypeType":"return","multipleResult":true,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":1610,"y":3400,"wires":[["148714bf5ea9b7b8","55a33491f8c590bb"]]},{"id":"61ecd7fee0a430cc","type":"modbus-getter","z":"0906658a180c94fa","name":"READ: FC3, Address 1 ~ 10","showStatusActivities":false,"showErrors":false,"showWarnings":true,"logIOActivities":false,"unitid":"","dataType":"HoldingRegister","adr":"1","quantity":"10","server":"bfc6a4331e29e6a4","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":true,"delayOnStart":false,"startDelayTime":"","x":1560,"y":3320,"wires":[[],["3add880577989644","9198c15383890f7c"]]},{"id":"148714bf5ea9b7b8","type":"debug","z":"0906658a180c94fa","name":"Processed Registers","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1840,"y":3380,"wires":[]},{"id":"21d015dabb389cf7","type":"inject","z":"0906658a180c94fa","name":"Go","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1510,"y":3240,"wires":[["61ecd7fee0a430cc"]]},{"id":"3add880577989644","type":"debug","z":"0906658a180c94fa","name":"Raw Data","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.data","targetType":"msg","statusVal":"","statusType":"auto","x":1800,"y":3320,"wires":[]},{"id":"214ab9a6ce4e03b0","type":"mqtt-broker","name":"","broker":"test.mosquitto.org","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"5","keepalive":"45","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"bfc6a4331e29e6a4","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"127.0.0.1","tcpPort":"11503","tcpType":"DEFAULT","serialPort":"COM6","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}]

image showing 1 modbus poll with smart processing

Let me know if you want other flows :slight_smile:

1 Like