Windowsで動くModbus Slaveのアプリの値を取得するフローです。

[{"id":"d9fe244914115dd4","type":"modbus-flex-getter","z":"2e3bfa49201b1a94","name":"","showStatusActivities":false,"showErrors":false,"showWarnings":true,"logIOActivities":false,"server":"ea6a6fe883e135ac","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"enableDeformedMessages":false,"startDelayTime":"","x":570,"y":360,"wires":[["3d0d3fe722b68443"],[]]},{"id":"cb9b8b903530e531","type":"function","z":"2e3bfa49201b1a94","name":"Modbus取得情報","func":"msg.payload = { \n 'fc': 3,\n 'unitid': 100,\n 'address': 1 ,\n 'quantity': 10\n };\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":240,"wires":[["d9fe244914115dd4"]]},{"id":"3d0d3fe722b68443","type":"debug","z":"2e3bfa49201b1a94","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":460,"wires":[]},{"id":"65d656521acf51ac","type":"inject","z":"2e3bfa49201b1a94","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":260,"y":240,"wires":[["cb9b8b903530e531"]]},{"id":"ea6a6fe883e135ac","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"192.168.1.103","tcpPort":502,"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,"showErrors":false,"showWarnings":true,"showLogs":true},{"id":"779cf83f1c86d698","type":"global-config","env":[],"modules":{"node-red-contrib-modbus":"5.45.1"}}]

ModbusSlaveから取得する情報としては上記のようにしました。

こうすることで、ModbusSlaveのアドレス1から10までの値を配列で取得します。
値に変化があるかチェック
ModbusSlaveのInput(FC4)の値を1秒ごとに10アドレスづつ読み込み、変化があったもののみを出力するフロー

[{"id":"86c8834a380f5716","type":"function","z":"2e3bfa49201b1a94","name":"Modbus Read設定","func":"msg.payload = { \n 'fc': 4, // Holding Register を読み取る(必要に応じて4に変更可能)\n 'unitid': 100,\n 'address': 1,\n 'quantity': 10\n };\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":360,"wires":[["eb22b5ccdf75ba64"]]},{"id":"eb22b5ccdf75ba64","type":"modbus-flex-getter","z":"2e3bfa49201b1a94","name":"Modbus Read","showStatusActivities":false,"showErrors":false,"showWarnings":true,"logIOActivities":false,"server":"ea6a6fe883e135ac","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"enableDeformedMessages":false,"startDelayTime":"","x":600,"y":460,"wires":[["1c0901749f6388aa"],[]]},{"id":"1c0901749f6388aa","type":"function","z":"2e3bfa49201b1a94","name":"値の変化チェック","func":"// Modbus データ(配列)\nlet current = msg.payload;\n\n// 配列チェック\nif (!Array.isArray(current)) {\n node.error(\"Modbusデータが配列ではありません: \" + JSON.stringify(current));\n return null;\n}\n\n// 前回値を取得\nlet before = flow.get('before_value');\n\n// -------------------------\n// 初回:全ての値を changes として出力\n// -------------------------\nif (!before) {\n let changes = [];\n\n for (let i = 0; i < current.length; i++) {\n changes.push({\n index: i,\n old: null, // 初回は old が存在しないので null\n new: current[i],\n address: i + 1\n });\n }\n\n // 初回の before を保存(deep copy)\n flow.set('before_value', JSON.parse(JSON.stringify(current)));\n\n msg.payload = {\n message: \"初回データを読み込みました\",\n changes: changes,\n timestamp: new Date().toISOString()\n };\n return msg;\n}\n\n// -------------------------\n// 2回目以降:変化チェック\n// -------------------------\nlet changes = [];\n\nfor (let i = 0; i < current.length; i++) {\n if (current[i] !== before[i]) {\n changes.push({\n index: i,\n old: before[i],\n new: current[i],\n address: i + 1\n });\n }\n}\n\n// 変化なし → 出力しない\nif (changes.length === 0) {\n return null;\n}\n\n// before を更新(deep copy)\nflow.set('before_value', JSON.parse(JSON.stringify(current)));\n\n// 出力\nmsg.payload = {\n message: \"Modbus値が変化しました\",\n changes: changes,\n timestamp: new Date().toISOString()\n};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":540,"wires":[["a77fda590a76fba4","7e4ac6863a6ed4f3"]]},{"id":"53cc11ac5a41b09d","type":"debug","z":"2e3bfa49201b1a94","name":"変化通知","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":860,"wires":[]},{"id":"30ae643c57ae22d9","type":"ui-table","z":"2e3bfa49201b1a94","group":"5d9040f02211696f","name":"ChangedValues","label":"text","order":1,"width":0,"height":0,"maxrows":0,"passthru":false,"autocols":true,"showSearch":true,"deselect":true,"selectionType":"none","columns":[],"mobileBreakpoint":"sm","mobileBreakpointType":"defaults","action":"replace","x":1300,"y":460,"wires":[[]]},{"id":"3873d7b6cc170533","type":"inject","z":"2e3bfa49201b1a94","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":360,"y":260,"wires":[["86c8834a380f5716"]]},{"id":"a77fda590a76fba4","type":"function","z":"2e3bfa49201b1a94","name":"変化した値を出力","func":"// 受信した payload\nlet changes = msg.payload.changes;\n\n// 配列チェック\nif (!Array.isArray(changes)) {\n node.error(\"changes が配列ではありません\");\n return null;\n}\n\n// 出力メッセージの配列を作成\nlet msgs = [];\n\nchanges.forEach(change => {\n msgs.push({\n payload: change\n });\n});\n\n// changes の要素数分、別々のメッセージとして出力\nreturn [msgs];\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":640,"wires":[["53cc11ac5a41b09d","0a2c3d8c9df8f52e"]]},{"id":"7e4ac6863a6ed4f3","type":"change","z":"2e3bfa49201b1a94","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.changes","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":460,"wires":[["30ae643c57ae22d9"]]},{"id":"0a2c3d8c9df8f52e","type":"switch","z":"2e3bfa49201b1a94","name":"","property":"payload.address","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"2","vt":"num"},{"t":"eq","v":"3","vt":"num"},{"t":"eq","v":"4","vt":"num"},{"t":"eq","v":"5","vt":"num"}],"checkall":"true","repair":false,"outputs":5,"x":1030,"y":680,"wires":[["a7addda30e2296f9"],["9b74b6526ed6d0ab"],["393b6d4ab6ca166d"],[],[]]},{"id":"a7addda30e2296f9","type":"debug","z":"2e3bfa49201b1a94","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1320,"y":640,"wires":[]},{"id":"9b74b6526ed6d0ab","type":"debug","z":"2e3bfa49201b1a94","name":"debug 18","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1320,"y":700,"wires":[]},{"id":"393b6d4ab6ca166d","type":"debug","z":"2e3bfa49201b1a94","name":"debug 19","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1320,"y":740,"wires":[]},{"id":"f601297aabce0473","type":"comment","z":"2e3bfa49201b1a94","name":"tableで変化のあった値を表示","info":"","x":1080,"y":400,"wires":[]},{"id":"6a67b90fdfa21dda","type":"comment","z":"2e3bfa49201b1a94","name":"ModbusAddressで分岐","info":"","x":1060,"y":600,"wires":[]},{"id":"ea6a6fe883e135ac","type":"modbus-client","name":"","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"queueLogEnabled":false,"failureLogEnabled":true,"tcpHost":"192.168.1.103","tcpPort":502,"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,"showErrors":false,"showWarnings":true,"showLogs":true},{"id":"5d9040f02211696f","type":"ui-group","name":"ModbusAddressValues","page":"9d4bc1caffe751ab","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"9d4bc1caffe751ab","type":"ui-page","name":"変化した値","ui":"f881b08788106a79","path":"/ui-button-example","icon":"button-pointer","layout":"notebook","theme":"9d8bfd7e0d216779","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":2,"className":"","visible":"true","disabled":"false"},{"id":"f881b08788106a79","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":1,"showDisconnectNotification":true},{"id":"9d8bfd7e0d216779","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#15617e","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"b78d417789ce3ab5","type":"global-config","env":[],"modules":{"node-red-contrib-modbus":"5.45.1","@flowfuse/node-red-dashboard":"1.29.0"}}]
これで初回はすべて出力、その後は値に変化があったアドレスの値だけを出力します。
なので、アドレスをスイッチノードで切り分けて、操作したいデバイスの制御内容を追加することで、Inputの値によって制御することができます。
