2026年7月7日 星期二

Modbus FC=1 (Coils read) & FC=15 (Coils Write)

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 的節點處理:

JavaScript
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 節點要求的物件格式:

JavaScript
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"}}]

沒有留言:

張貼留言

Modbus FC=1 (Coils read) & FC=15 (Coils Write)

Modbus FC=1  (Coils read)  & FC=15 (Coils Write)   「從 Modbus 設備讀取多個線圈(Coils)狀態,並立刻將這些狀態原封不動地轉寫到另一個記憶體位址(起始位址 16)」 。 整個流程使用了知名套件 node-red...