はじめに
・今回はNode-REDを使ってModbusのMasterとSlaveの検証を行った
・また、今回の検証は以下の動画、ライブラリを参考に作成した。
・https://youtu.be/nN9SJfK0iIo
・https://flows.nodered.org/node/node-red-contrib-modbus
フロー作成
全体像

フロー作成手順
- ・Node-REDの右上のメニューから読み込みを選択

・下記のJSON形式のフローデータを貼り付け、読み込みボタンを押す
[{"id":"a93effa614da2604","type":"modbus-server","z":"d78b8f871190815d","name":"","logEnabled":false,"hostname":"0.0.0.0","serverPort":"502","responseDelay":100,"delayUnit":"ms","coilsBufferSize":10000,"holdingBufferSize":10000,"inputBufferSize":10000,"discreteBufferSize":10000,"showErrors":false,"x":460,"y":240,"wires":[["f8ed3667788a4fd0"],["f8ed3667788a4fd0"],["f8ed3667788a4fd0"],["f8ed3667788a4fd0"],["f8ed3667788a4fd0"]]},{"id":"ae1b6df3bb9c4b5a","type":"inject","z":"d78b8f871190815d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":250,"y":220,"wires":[["a93effa614da2604"]]},{"id":"f8ed3667788a4fd0","type":"debug","z":"d78b8f871190815d","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":710,"y":260,"wires":[]},{"id":"edc21ae6596480ce","type":"modbus-flex-getter","z":"d78b8f871190815d","name":"FlexGetter","showStatusActivities":false,"showErrors":false,"logIOActivities":false,"server":"b62d434859b22bc4","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"keepMsgProperties":false,"x":550,"y":420,"wires":[[],["a4e6a3afceb67fea"]]},{"id":"a4e6a3afceb67fea","type":"modbus-response","z":"d78b8f871190815d","name":"","registerShowMax":20,"x":750,"y":420,"wires":[]},{"id":"922ed923301accb7","type":"inject","z":"d78b8f871190815d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":250,"y":320,"wires":[["9c6490e8241964b4"]]},{"id":"9c6490e8241964b4","type":"function","z":"d78b8f871190815d","name":"FC3 0
","func":"msg.payload = { \n 'fc': 3,\n 'unitid': 0,\n 'address': 0 ,\n 'quantity': 4\n };\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":360,"wires":[["edc21ae6596480ce"]]},{"id":"826b7a0cae32b0c2","type":"function","z":"d78b8f871190815d","name":"FC16 0
","func":"msg.payload = {\n value : msg.payload,\n 'fc': 16,\n 'unitid': 0,\n 'address': 0 ,\n 'quantity': 2\n };\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":520,"wires":[["98fa71a376265232"]]},{"id":"98fa71a376265232","type":"modbus-flex-write","z":"d78b8f871190815d","name":"FlexWrite","showStatusActivities":false,"showErrors":false,"server":"b62d434859b22bc4","emptyMsgOnFail":false,"keepMsgProperties":false,"x":720,"y":520,"wires":[[],[]]},{"id":"602494bf34f00e32","type":"inject","z":"d78b8f871190815d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":230,"y":460,"wires":[["5c419a49d91c4cba"]]},{"id":"5c419a49d91c4cba","type":"join","z":"d78b8f871190815d","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":370,"y":520,"wires":[["826b7a0cae32b0c2"]]},{"id":"ecb171afe55be84e","type":"inject","z":"d78b8f871190815d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":230,"y":520,"wires":[["5c419a49d91c4cba"]]},{"id":"b62d434859b22bc4","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","serialAsciiResponseStartDelimiter":"0x3A","unit_id":"1","commandDelay":"1","clientTimeout":"1000","reconnectOnTimeout":true,"reconnectTimeout":"2000","parallelUnitIdsAllowed":true}]

・「node-red-contrib-modbus」を追加する

フロー解説
1. 全体の流れ (各 Injectノード)

- Injectノードを押した時の流れ:
- ① 最上部のInjectノードを押すことでModbus Serverが起動する
- ➁ 0か100のInjectノードを2つ選択して押す
- ➂ 中部にあるInjectノードを押すことでサーバーにデータを書き込む
「Modbus Server」が 「Modbus Slave」
「Flex Getter / Modbus Response」が 「Modbus Slave」のような役割になっている
2. Modbus-Serverノード
- 役割: Modbus Slaveのような役割を担う、Modbus TCPプロトコルを介してModbus Masterからのリクエストを受信する
- 設定: 初期設定からPortを502に設定するのみ

3. Flex-Getterノード
- 役割: Modbusリクエストを生成し、Modbus Serverからデータを取得する
- 設定: Serverは同じNode-REDの物を使うので以下の設定にする
Host:127.0.0.1
Port :502


4. Modbus-Responseノード
- 役割: Modbusリクエストに対するレスポンスを処理し、表示するためにデータの整形を行う
- 設定: 特に設定変更はなし

5. Functionノード
- 役割: 読み取りリクエストのペイロードを成形し、次に処理するための適切な形式に設定する
- 設定: 以下の内容を入力
msg.payload = {
'fc': 3, // 関数コード 3: ホールディングレジスタの読み取り
'unitid': 0, // ユニットID 0: ターゲットデバイスを指定
'address': 0, // アドレス 0: 読み取りを開始するアドレス
'quantity': 4 // 数量 4: 読み取るレジスタの数
};
// 修正されたメッセージオブジェクトを返す
return msg;

6. Joinノード
- 役割: 2つのメッセージを1つの配列にまとめる
- 設定:
動作:手動
出力:配列
指定数のメッセージパーツを受信後:2

7. Functionノード
- 役割: 書き込みリクエストのペイロードを成形し、次に処理するための適切な形式に設定する
- 設定: 以下の内容を入力
msg.payload = {
value: msg.payload, // 書き込むデータ(前のノードから提供される)
'fc': 16, // 関数コード16:複数のホールディングレジスタに書き込み
'unitid': 0, // ユニットID0:ターゲットデバイス
'address': 0, // アドレス0:書き込みを開始するメモリ位置
'quantity': 2 // 数量2:書き込むレジスタの数
};
return msg;

8. Flex-Writeノード
- 役割: Modbus Server(Modbus Slave)にデータを書き込む
- 設定: Server(Slave)を選択する

実行結果
・Flex-Getterにdebugノードをつけることで配列で値が取得可能になる
・Modbus Serverを起動した後に、FlexGetter(読み込み)のInjectノードを押すと、Serverに対してRead Multiple Holding Registersを行い結果がResponseに表示される

・次にFlexWriteのInjectノードを1回づつ押した後、FlexGetterのInjectノードを押すと、書き込んだ内容がResponseとして取得できる
