Modbus FC=1 (Coils read) & FC=15 (Coils Write)
「從 Modbus 設備讀取多個線圈(Coils)狀態,並立刻將這些狀態原封不動地轉寫到另一個記憶體位址(起始位址 16)」。
整個流程使用了知名套件 node-red-contrib-modbus,主要分為兩個階段:讀取暫存器(Read FC=1) 與 批次寫入暫存器(Write FC=15)。
以下為您詳細拆解各個節點的功能與資料流向:
1. 流程圖架構與資料流
這個流程的運作路徑如下:
Modbus Read (讀取線圈) $\rightarrow$ Function (提取資料) $\rightarrow$ Function (打包 FC15 格式) $\rightarrow$ Modbus Write (寫入新位址)
🟢 第一階段:定時讀取線圈狀態 (Modbus Read)
節點名稱:
modbus-read通訊參數:
DataType:
Coil(代表功能碼 FC=1, Read Coil Status)。Address (adr):
0(從記憶體位址 0 開始讀)。Quantity:
8(連續讀取 8 個線圈的狀態,通常代表 1 個位元組 Byte 的 8 個位元)。Rate:
10000 ms(每 10 秒自動循環讀取一次)。Unit ID:
1(通訊設備站號為 1)。
🟡 第二階段:資料提取與清洗 (Function 節點)
讀取成功後,Modbus Read 節點輸出的是一個複雜的物件(Object),接著交給名稱為 function 的節點處理:
var data = msg.payload.data; // 從回傳物件中提取出真正的線圈陣列(例如 [true, false, true...])
msg.payload = data; // 將 payload 取代為這個純陣列
return msg;
Debug 4: 在這裡接了一個除錯節點,用來在側邊欄監看提取出來的 8 個線圈布林值陣列。
🔵 第三階段:重新打包寫入格式 (Read FC=15 節點)
這是最關鍵的邏輯轉換區。名稱為 Read FC=15 的 Function 節點將剛才讀到的 8 個線圈值,重新打包成符合 modbus-write 節點要求的物件格式:
const coilValues = msg.payload; // 取得剛才的 8 個布林值陣列
msg.payload = {
"value": coilValues, // 要寫入的布林值陣列
"fc": 15, // 強制指定功能碼 FC=15 (Write Multiple Coils,強行寫入多個線圈)
"unitid": 1, // 設備站號 1
"address": 16, // 變更寫入的目的地起始位址為 16
"quantity": coilValues.length // 寫入長度(自動取得陣列長度,即 8)
};
return msg;
🟠 第四階段:寫入新記憶體位址 (Modbus Write)
節點名稱:
modbus-write通訊參數:
DataType:
MCoils(Multiple Coils,複數線圈)。Address (adr):
16(寫入位址 16 ~ 23)。功能: 接收上一個節點打包好的物件,透過 Modbus TCP 網路,將原本從位址
0~7讀到的開關狀態,複製寫入到位址16~23。
Debug 5 & 6: 用來監控寫入前與寫入後的系統回傳狀態(判斷是否成功寫入)。
2. 底層通訊設定 (Modbus Client)
流程最下方配置了一個名為 local 的通訊客戶端物件(modbus-client):
連線方式: TCP-RTU-BUFFERED(這是一種很常見的架構,代表實體設備是 RS-485 序列埠走 RTU 協定,但前端透過了一個 TCP 轉 RS-485 伺服器/閘道器 連線,或者是在本機透過虛擬串列埠轉 TCP 的方式運作)。
IP 與 Port: 連線到本機端
127.0.0.1,通訊埠502(標準 Modbus TCP Port)。
💡 總結這個程式的目的
這段程式的目的就是「記憶體狀態同步(Data Mirroring/Mapping)」。
在工廠實務上,這通常用於資料橋接。例如:PLC 的位址 0~7 是現場實體按鈕或感測器的輸入狀態(Inputs),而系統需要把這些狀態即時複製到位址 16~23(內部旗標位元 Internal Bits 或輸出暫存器),以便讓工廠的 HMI 人機介面、SCADA 中控軟體,或是您上一張圖提到的 ModbusSim 模擬器 能夠在特定區塊讀取到集中的環境狀態。
Node-red程式
[{"id":"de273b1b8bd36b17","type":"modbus-read","z":"56c70b10b2e55b6e","name":"","topic":"","showStatusActivities":false,"logIOActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"Coil","adr":"0","quantity":"8","rate":"10000","rateUnit":"ms","delayOnStart":false,"enableDeformedMessages":true,"startDelayTime":"","server":"54dde46e.53267c","useIOFile":false,"ioFile":"","useIOForPayload":false,"emptyMsgOnFail":false,"x":190,"y":220,"wires":[[],["eb0999703abe22e8"]]},{"id":"563895aaeb2074e8","type":"debug","z":"56c70b10b2e55b6e","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":220,"wires":[]},{"id":"eb0999703abe22e8","type":"function","z":"56c70b10b2e55b6e","name":"function ","func":"var data=msg.payload.data;\nmsg.payload=data;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":220,"wires":[["563895aaeb2074e8","cceab653f17ba013"]]},{"id":"cceab653f17ba013","type":"function","z":"56c70b10b2e55b6e","name":"Read FC=15","func":"// Define your coil array values\nconst coilValues = msg.payload;\n\nmsg.payload = {\n \"value\": coilValues, // Array of booleans to write\n \"fc\": 15, // Function Code 15 (Write Multiple Coils)\n \"unitid\": 1, // Modbus Slave/Unit ID\n \"address\": 16, // Starting coil memory address\n \"quantity\": coilValues.length // Number of coils to write\n};\n\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":340,"wires":[["baf9113b483f99d0","d135dcbf561bc6db"]]},{"id":"f237104758129409","type":"debug","z":"56c70b10b2e55b6e","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":560,"y":340,"wires":[]},{"id":"baf9113b483f99d0","type":"debug","z":"56c70b10b2e55b6e","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":360,"y":300,"wires":[]},{"id":"d135dcbf561bc6db","type":"modbus-write","z":"56c70b10b2e55b6e","name":"","showStatusActivities":false,"showErrors":false,"showWarnings":true,"unitid":"1","dataType":"MCoils","adr":"16","quantity":"8","server":"54dde46e.53267c","emptyMsgOnFail":false,"keepMsgProperties":false,"delayOnStart":false,"startDelayTime":"","x":380,"y":340,"wires":[[],["f237104758129409"]]},{"id":"54dde46e.53267c","type":"modbus-client","name":"local","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":true,"queueLogEnabled":false,"failureLogEnabled":false,"tcpHost":"127.0.0.1","tcpPort":"502","tcpType":"TCP-RTU-BUFFERED","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":false,"reconnectTimeout":2000,"parallelUnitIdsAllowed":false,"showErrors":false,"showWarnings":true,"showLogs":true},{"id":"007e6f266989a5ed","type":"global-config","env":[],"modules":{"node-red-contrib-modbus":"5.45.2"}}]







沒有留言:
張貼留言