National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

RS-485 與 Modbus RTU

主從架構、訊框、站號、功能碼與 CRC
Interaction 30 minute(s)

Learning Objectives

  • 說明 RS-485 的接線特性:差動、兩線式半雙工、多點匯流排與終端電阻
  • 拆解 Modbus RTU 訊框:站號、功能碼、資料、CRC,以及訊框之間的靜默間隔
  • 自己用 Python 算出 CRC-16,並說明低位元組先送
  • 用 pymodbus 的 ModbusSerialClient 讀取 PLC,處理參數不一致、沒有回應與例外回應

一對線,串起整條產線的儀表

溫控器、電力錶、變頻器、流量計……很多工業設備背後都有兩個標著 A、B(或 D+、D−)的端子:那是 RS-485 介面, 上面跑的通訊協定最常見的就是 Modbus RTU。一條雙絞線可以串起幾十台設備,讓 PLC 或電腦一台一台去問數值。

這一課要回答:

  1. RS-485 為什麼適合工廠?接線要注意什麼?
  2. Modbus RTU 一筆訊息長什麼樣子?每個位元組代表什麼?
  3. 接收端怎麼知道訊息在傳輸途中有沒有被干擾?
  4. 用 Python 讀取時,常見的錯誤有哪些?

RS-485 的特性

特性 說明
差動訊號 用兩條線的電壓差表示 0 與 1;雜訊通常同時加在兩條線上,相減之後就抵消了,所以抗雜訊、能傳遠
兩線式、半雙工 同一對線上,同一時間只能有一台設備在送(四線式是選用的做法)
多點匯流排 設備依序串在同一條主幹線上(菊鏈),不要接成星狀的分岔
終端電阻 主幹線的兩端各接一顆終端電阻,減少訊號在線尾反射
共同地線 除了 A、B,還要一條共同地線(Common)把各設備的參考電位接在一起

Modbus 序列線路規格(Modbus over Serial Line V1.02)寫明:不加中繼器時一條 RS-485 匯流排至少允許 32 台設備, 主幹線最長可到 1000 m(實際依鮑率與線材而定)。RS-485 只規定電氣特性(TIA/EIA-485),上面跑什麼協定是另一回事。

安全:RS-485 的設備常裝在控制盤裡,旁邊可能有市電或 24 V 電源。接線前先斷電並依工廠規定上鎖掛牌,盤內的市電部分由合格人員處理。

Modbus RTU 的訊框

Modbus 是請求與回應的協定:一台 client(發問的一方,通常是 PLC、電腦或 HMI)送出請求,被問到的 server(儀表、變頻器、PLC)回應。 舊文件稱為 master/slave(主站/從站),Modbus Organization 在 2020 年改用 client/server,兩種說法現在都還很常見。

序列線路上的每台 server 有一個站號(1~247),0 是廣播:所有 server 都會執行,但不回應(只能用在寫入)。

一個 RTU 訊框:

 站號   功能碼   資料(位址、數量、值…)   CRC 低   CRC 高
 1 位元組 1 位元組        N 位元組           1 位元組  1 位元組

例如「讀取站號 1 的保持暫存器,從位址 0 開始讀 2 個」:

 01     03     00 00      00 02      C4 0B
 站號   功能碼  起始位址    數量       CRC
  • 位址、數量這類 16 位元的數字,高位元組在前(00 02)。
  • CRC 卻是低位元組先送(C4 0B 代表 CRC 值 0x0BC4)。這是規格規定的,最容易搞混。
  • RTU 沒有「開始」「結束」字元,接收端靠靜默間隔分辨訊框:訊框之間至少要安靜 3.5 個字元的時間。一個 RTU 訊框最多 256 個位元組。

常用的功能碼:01 讀線圈、02 讀離散輸入、03 讀保持暫存器、04 讀輸入暫存器、05 寫單一線圈、06 寫單一暫存器、 15 寫多個線圈、16 寫多個暫存器(下一課詳細說明這四種資料)。

逐步看:自己算 CRC-16

CRC(循環冗餘檢查)是把整個訊框「濃縮」成 16 個位元的檢查碼。送方算好附在最後;收方用同樣的方法再算一次, 不一樣就表示資料在路上被改了。注意每個位元組都要做 8 次「右移,必要時和 A001 做 XOR」;可以拖動進度條跳過中間的步驟,看每個位元組算完後 crc 的值。

互動練習:組訊框、看 CRC、解讀回應

上半部組一個請求,每個位元組都標出意思,下方的表格列出 CRC 算到每個位元組時的值。下半部可以貼上通訊紀錄裡的 16 進位解讀。依序完成任務:

  1. 組出「讀取站號 1 的保持暫存器,從位址 0 開始讀 2 個」的請求。
  2. 改成「讀取站號 2 的線圈,從位址 0 開始讀 8 個」,看 CRC 整個變了。
  3. 按「貼上範例」解讀下一個範例會收到的回應,看出兩個暫存器的值。
  4. 把解讀區的任何一個位元組改掉再按「解讀」,看 CRC 檢查失敗。
  5. 回答一個問題。

用 pymodbus 經 RS-485 讀資料

電腦接 RS-485 通常用 USB 轉 RS-485 的轉接器,在 Windows 上會出現一個 COM 埠(Linux 是 /dev/ttyUSB0 這類名稱)。 Python 用 pymodbus 套件:

from pymodbus.client import ModbusSerialClient

client = ModbusSerialClient("COM4", baudrate=9600, parity="N", stopbits=2, timeout=1)
client.connect()
rr = client.read_holding_registers(0, count=2, device_id=1)
print(rr.registers)
client.close()

序列參數兩端必須完全一致:鮑率、資料位元、同位檢查(N 無/E 偶/O 奇)、停止位元。規格的預設是偶同位; 使用無同位時規格要求 2 個停止位元(很多設備也接受 1 個,以設備手冊為準)。參數不一致時,對方收到的是亂碼,不會回應。

pymodbus 的版本差異(本課用 3.x 的寫法,撰寫時最新版是 3.16):

版本 指定站號的參數 其他
3.10 以後 device_id=1 ModbusSlaveContext 改名 ModbusDeviceContext
3.3~3.9 slave=1 3.8 起 count 等參數必須用關鍵字寫(count=2,不能只寫 2)
更舊的版本 unit=1 網路上很多舊範例是這種寫法

站號省略時預設是 1。照網路範例寫卻出現「unexpected keyword argument」,多半就是版本不同;先用 pip show pymodbus 看版本,再對照官方的 API 變更說明。 本課的模擬器 slave= 和 device_id= 都接受。

逐步看:讀保持暫存器與線圈

看畫面下方的通訊紀錄:每一次讀取都有一筆「→」請求和一筆「←」回應,都是真的 RTU 訊框,最後兩個位元組是 CRC。拿回應的 16 進位到上面的小工具解讀看看。

逐步看:沒有回應與例外回應

兩種錯誤長得不一樣:沒有回應(站號不存在、參數不一致、線斷了、CRC 錯)只能等到逾時; 例外回應表示 server 收到了、但做不到,會回傳功能碼加 0x80(03 變成 83)與例外碼。注意第二次請求之後虛擬時間跳了 1 秒,那是在等逾時。

互動練習:經 RS-485 讀取 PLC

模擬 PLC 接在 COM4,序列參數是 9600 bps、無同位,站號 1;保持暫存器 0 是設定值 250,線圈 0 是馬達。 起始程式沒有設定鮑率(pymodbus 預設 19200)。依序完成任務:

  1. 直接執行起始程式,看通訊紀錄:請求送出了,卻沒有回應。
  2. 把鮑率改成 9600,讀到保持暫存器 0、1 並印出來(要看到 250)。
  3. 再讀線圈 0~7,印出馬達(線圈 0)的狀態。
  4. 再問一次站號 7(不存在),用 try/except 接住 ModbusIOException,印出「站號 7 沒有回應」,程式要正常結束。
  5. 回答一個問題。

小測驗:RS-485 與訊框

1.

RS-485 匯流排的終端電阻要接在哪裡?

2.

CRC 值是 0x0BC4,RTU 訊框最後兩個位元組依序是?下面的程式會印出什麼?

c = 0x0BC4
print(format(c & 0xFF, "02X"), format(c >> 8, "02X"))
3.

client 送出一個站號 0 的寫入請求,會發生什麼事?

第 1 次作答

小測驗:錯誤處理

1.

讀取時收到回應 01 83 02 C0 F1,代表什麼?

2.

下列哪一種情況,client 會收到「例外回應」而不是「沒有回應」?

第 1 次作答

重點整理

  • RS-485:差動、兩線式半雙工、多點菊鏈,主幹線兩端接終端電阻,另接共同地線。
  • Modbus 是請求與回應:client 發問、server 回答;站號 1~247,0 是不回應的廣播。
  • RTU 訊框=站號+功能碼+資料+CRC;16 位元的數字高位元組在前,CRC 低位元組在前;訊框之間靠 3.5 字元的靜默間隔分開。
  • CRC-16:初值 FFFF,每個位元組做 8 次「右移,最低位元是 1 就和 A001 做 XOR」。
  • 鮑率、同位、停止位元兩端必須一致;不一致、站號不存在、CRC 錯誤都是「沒有回應」,只能等逾時。
  • 位址超出範圍等「收到了但做不到」的情況,server 回例外回應(功能碼+0x80、例外碼)。
  • pymodbus 3.10 起用 device_id=,之前是 slave=;照舊範例寫出錯時先查版本。

下一課整理 Modbus 的四種資料:線圈、離散輸入、輸入暫存器、保持暫存器,以及最容易出錯的位址編號。

這一課完成了!
Next lesson:Modbus 資料模型
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。