• 正文
    • 一、ModBusTCP協(xié)議
    • 二、一個Demo及其引發(fā)的問題
  • 相關推薦
申請入駐 產業(yè)圖譜

python實現ModBusTCP協(xié)議的client

2024/12/06
3771
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

python實現ModBusTCP協(xié)議的client是一件簡單的事情,只要通過pymodbus、pyModbusTCP等模塊都可以實現,本文采用pymodbus。

一、ModBusTCP協(xié)議

1、了解ModBusTCP協(xié)議

Modbus TCP 是一種基于 TCP/IP 協(xié)議棧的 Modbus 通信協(xié)議,它用于在工業(yè)自動化系統(tǒng)中進行設備之間的通信。Modbus TCP 將 Modbus 協(xié)議封裝在 TCP/IP 協(xié)議之上,通過網絡連接設備,實現數據的讀取和寫入。

以下是 Modbus TCP 的基本特點:

(1)基于 TCP/IP 協(xié)議:Modbus TCP 使用 TCP/IP 網絡進行通信,可以通過以太網、互聯網等方式進行遠程通信。

(2)實時性:Modbus TCP 具有較高的實時性,適用于需要快速響應的控制系統(tǒng)。

(3)異步通信:Modbus TCP 支持異步通信,允許設備之間的非同步數據交換。

(4)客戶端-服務器模型:Modbus TCP 通信采用客戶端-服務器模型??蛻舳耍ㄍǔJ强刂葡到y(tǒng)或監(jiān)控系統(tǒng))向服務器(設備或傳感器)發(fā)出請求,服務器返回響應數據。

(5)支持多種數據類型:Modbus TCP 支持不同數據類型的讀寫操作,包括線圈(Coil)、離散輸入(Discrete Input)、保持寄存器(Holding Register)和輸入寄存器(Input Register)等。

(6)數據傳輸格式:Modbus TCP 使用 Modbus 協(xié)議的格式進行數據傳輸,包括設備地址、功能碼、數據域等。

(7)安全性:由于 Modbus TCP 通信是基于 TCP/IP 的,因此可以通過網絡安全措施(例如 VPN、防火墻等)提供數據傳輸的安全性。

總的來說,Modbus TCP 提供了一種可靠的、靈活的工業(yè)通信解決方案,廣泛用于自動化領域中的各種設備之間的數據交換。

2、ModBusTCP協(xié)議的client與TCPclient的區(qū)別?

Modbus TCP 是一種特定的應用層通信協(xié)議,用于在工業(yè)自動化系統(tǒng)中設備之間進行數據交換。它是在 TCP/IP 協(xié)議棧上運行的 Modbus 協(xié)議的變種。Modbus TCP 協(xié)議的數據包是通過 TCP/IP 協(xié)議進行傳輸的。

TCP client 是一種通用的網絡通信模式,它指的是通過 TCP/IP 協(xié)議與遠程服務器建立連接,并向服務器發(fā)送請求并接收響應的程序。TCP client 可以用于與任何支持 TCP/IP 協(xié)議的服務器進行通信,不限于 Modbus 協(xié)議。

區(qū)別主要在于:

(1)用途不同:Modbus TCP 是一種特定的工業(yè)自動化通信協(xié)議,用于工業(yè)設備之間的數據交換;而 TCP client 是一種通用的網絡通信模式,可以與各種服務器進行通信,不限于 Modbus 協(xié)議。

(2)協(xié)議不同:Modbus TCP 使用 Modbus 協(xié)議進行數據傳輸,而 TCP client 沒有固定的協(xié)議限制,可以與各種應用層協(xié)議進行通信。

(3)功能不同:Modbus TCP 協(xié)議定義了特定的功能碼和數據格式,用于讀寫線圈、離散輸入、保持寄存器等;TCP client 則沒有固定的功能碼和數據格式,可以根據具體需求自定義通信內容。

(4)適用場景不同:Modbus TCP 主要用于工業(yè)自動化控制系統(tǒng)中,用于實時數據交換;TCP client 可以用于各種通信場景,包括 Web 客戶端、數據庫客戶端、文件傳輸等。

綜上所述,Modbus TCP 是一種特定協(xié)議的 TCP client,用于在工業(yè)自動化領域實現設備之間的數據交換。TCP client 則是一個更通用的概念,可以與各種服務器進行通信,不受特定協(xié)議限制。

3、ModBusTCP協(xié)議的數據幀格式是怎樣的?

大體如上圖紅色部分所描述,Modbus TCP 協(xié)議的數據幀格式如下:

(1)MBAP 頭部(Modbus Application Protocol Header):

Transaction Identifier(事務標識符):2 字節(jié),用于標識事務,通常是遞增的序號。
Protocol Identifier(協(xié)議標識符):2 字節(jié),固定為0,表示 Modbus 協(xié)議。
Length(數據長度):2 字節(jié),表示 MBAP 后面數據的長度,包括單元標識符(Unit Identifier)和數據字段。
Unit Identifier(單元標識符):1 字節(jié),用于標識 Modbus 設備,通常為 1。
(2)PDU(Protocol Data Unit):

Function Code(功能碼):1 字節(jié),表示 Modbus 操作的類型,如讀取保持寄存器、寫入線圈等。
Data(數據):根據功能碼的不同,數據的格式和長度會有所變化。

數據幀的格式可以根據不同的功能碼和操作類型而變化,例如:

(1)對于讀取保持寄存器(Function Code 0x03):

起始地址:2 字節(jié),表示要讀取的寄存器的起始地址。
寄存器數量:2 字節(jié),表示要讀取的寄存器的數量。
(2)對于寫入單個保持寄存器(Function Code 0x06):

寄存器地址:2 字節(jié),表示要寫入的寄存器的地址。
寄存器值:2 字節(jié),表示要寫入的寄存器的值。

總體來說,Modbus TCP 協(xié)議的數據幀格式是固定的,但是具體的數據內容和長度會根據功能碼的不同而有所變化。詳細的數據幀格式需要根據具體的功能碼和操作類型來確定。

二、一個Demo及其引發(fā)的問題

1、一個Demo

from pymodbus.client import ModbusTcpClient


if __name__ == "__main__":
    # Modbus TCP服務器的IP地址和端口號
    server_ip = "192.168.1.189"
    port = 502
    station = 1

    # 創(chuàng)建Modbus TCP客戶端
    MDclient = ModbusTcpClient(server_ip, port)
    if MDclient.connect():
        # 讀取保持寄存器的示例
        address = 0  # 起始寄存器地址
        count = 10   # 要讀取的寄存器數量
        MDclient.write_registers(address, 115, slave=1)
        time.sleep(2)
        response = MDclient.read_holding_registers(10, 10, slave=station)
        print(response.registers[0])
        MDclient.close()

2、pymodbus.client.ModbusTcpClient 都實現了哪些功能碼

pymodbus.client.ModbusTcpClient 類是 PyModbus 庫中用于 Modbus TCP 客戶端通信的類。它支持以下常用的 Modbus 功能碼:

(1)讀取線圈狀態(tài)(Read Coils):功能碼 0x01,用于讀取輸出線圈的狀態(tài)。

read_coils(address, count=1, slave=0)

  • address: 起始線圈的地址
  • count: 要讀取的線圈數量
  • slave: Modbus 單元標識符(站號)

(2)讀取離散輸入狀態(tài)(Read Discrete Inputs):功能碼 0x02,用于讀取輸入線圈的狀態(tài)。

read_discrete_inputs(address, count=1, slave=0)

  • address: 起始輸入線圈的地址
  • count: 要讀取的輸入線圈數量
  • slave: Modbus 單元標識符

(3)讀取保持寄存器(Read Holding Registers):功能碼 0x03,用于讀取保持寄存器的值。

read_holding_registers(address, count=1, slave=0)

  • address: 起始保持寄存器的地址
  • count: 要讀取的保持寄存器數量
  • slave: Modbus 單元標識符

(4)讀取輸入寄存器(Read Input Registers):功能碼 0x04,用于讀取輸入寄存器的值。

read_input_registers(address, count=1, slave=0)

  • address: 起始輸入寄存器的地址
  • count: 要讀取的輸入寄存器數量
  • slave: Modbus 單元標識符

(5)寫單個線圈(Write Single Coil):功能碼 0x05,用于寫入一個輸出線圈的狀態(tài)。

write_coil(address, value, slave=0)

  • address: 線圈的地址
  • value: 要寫入的值,True 表示 ON,False 表示 OFF
  • slave: Modbus 單元標識符

(6)寫單個寄存器(Write Single Register):功能碼 0x06,用于寫入一個保持寄存器的值。

write_register(address, value, slave=0)

  • address: 寄存器的地址
  • value: 要寫入的值
  • slave: Modbus 單元標識符

(7)寫多個線圈(Write Multiple Coils):功能碼 0x0F,用于寫入多個輸出線圈的狀態(tài)。

write_coils(address, values, slave=0)

  • address: 起始線圈的地址
  • values: 要寫入的線圈狀態(tài),是一個布爾值列表
  • slave: Modbus 單元標識符

(8)寫多個寄存器(Write Multiple Registers):功能碼 0x10,用于寫入多個保持寄存器的值。

write_registers(address, values, slave=0)

  • address: 起始寄存器的地址
  • values: 要寫入的寄存器值,是一個整數列表
  • slave: Modbus 單元標識符

3、讀到的結果與寫入的值為什么是反的

只能說有可能是反的。

比如我用 response = MDclient.read_holding_registers(10, 10, slave=station) print(response.registers[0])打印了一個寄存器,能讀到數值為17217,轉化成16進制再轉化成字符串為CA,但實際的字符串應該為AC,這是為什么呢?

當你期望的結果是AC而不是CA,說明你讀取的數值可能被解釋為了大端字節(jié)序(big-endian)而不是你期望的小端字節(jié)序(little-endian)。在大端字節(jié)序中,高位字節(jié)保存在低地址,而在小端字節(jié)序中,高位字節(jié)保存在高地址。

這里為什么說可能呢?原因很簡單,通信的兩端如果內存存儲方式不一樣,讀到的數據就是反的。比如PC是小端序,嵌入式設備是大端序,存儲在嵌入式設備的值為AC,高位是A,低位是C,那么PC在以小端序存儲時,會把對方高位的A存儲低位,把對方低位的C存儲到高位,就變成了CA。

如果二者都是大端序或小端序的存儲方式,則不會有該問題,因為他們的讀取和存儲方式都是一樣的。

4、大端序與小端序之間的傳輸

當數據在大端字節(jié)序(big-endian)和小端字節(jié)序(little-endian)之間傳送時,通常需要進行大小端轉換。這是因為不同的處理器計算機體系結構可能使用不同的字節(jié)序,如果發(fā)送方和接收方的字節(jié)序不同,就需要進行轉換。

  • 大端字節(jié)序:在大端字節(jié)序中,高位字節(jié)保存在低地址,低位字節(jié)保存在高地址。例如,整數0x12345678在大端字節(jié)序中存儲為12 34 56 78。
  • 小端字節(jié)序:在小端字節(jié)序中,低位字節(jié)保存在低地址,高位字節(jié)保存在高地址。同樣的整數0x12345678在小端字節(jié)序中存儲為78 56 34 12

如果數據在不同字節(jié)序的系統(tǒng)之間傳遞,發(fā)送方需要將數據按照目標系統(tǒng)的字節(jié)序進行轉換,接收方再將接收到的數據進行反向轉換,以確保數據的正確傳遞。在很多網絡通信和文件傳輸的場景下,大小端轉換是非常常見的操作。

5、線圈、輸入寄存器、保持寄存器、離散寄存器分別占用多少bit位

在Modbus協(xié)議中,不同類型的數據(線圈、輸入寄存器、保持寄存器、離散寄存器)占用的位數如下:

(1)線圈(Coils):線圈是只能讀寫的二進制輸出,每個線圈占用1位。這意味著一個線圈的狀態(tài)只能是開(1)或者關(0)。

(2)輸入寄存器(Input Registers):輸入寄存器是只讀的,每個輸入寄存器占用16位(2個字節(jié))。

(3)保持寄存器(Holding Registers):保持寄存器是讀寫的,每個保持寄存器也占用16位(2個字節(jié))。

(4)離散輸入(Discrete Inputs):離散輸入是只讀的二進制輸入,每個離散輸入占用1位。類似于線圈,一個離散輸入的狀態(tài)只能是開(1)或者關(0)。

總結:

  • 線圈:1位
  • 輸入寄存器:16位(2字節(jié))
  • 保持寄存器:16位(2字節(jié))
  • 離散輸入:1位

請注意,這些大小是Modbus協(xié)議規(guī)定的標準大小,不同的設備可能有不同的實現,因此在實際應用中,你應該查閱設備的文檔以確認具體的數據大小。

6、一個保持寄存器能存2個字母,用兩個保持寄存器存儲ACK1,第一個寄存器存儲AC,第二個存儲K1,大端序會怎么存儲?

在大端序(Little Endian)下,字節(jié)的存儲順序是低位字節(jié)在前,高位字節(jié)在后。如果每個保持寄存器能存儲兩個字母,要存儲字符串“ACK1”,它將被拆分為兩個部分:'AC' 和 'K1'。以AC為例,A為高位,C為低位。

在大端序下,存儲順序如下:

  • 第一個保持寄存器(低位字節(jié)在前(高位),高位字節(jié)在后(低位)):存儲67(0x43)和65(0x41),即存儲為0x4341。
  • 第二個保持寄存器(低位字節(jié)在前(高位),高位字節(jié)在后(低位)):存儲49(0x31)和75(0x4B),即存儲為0x314B。

所以,在小端序下,字符串“ACK1”會按照上述方式存儲到兩個保持寄存器中。

7、如何判斷win10存儲是大端序還是小端序?

在通用的個人計算機(包括Windows 10系統(tǒng))中,主流處理器(如x86和x86-64架構的處理器)使用的是小端序(Little Endian)存儲。這意味著在存儲多字節(jié)數據時,低位字節(jié)存儲在內存的低地址處,高位字節(jié)存儲在內存的高地址處。

如果你想要確認你的Windows 10系統(tǒng)是使用小端序還是大端序,可以使用Python來進行測試。以下是一個簡單的Python代碼片段,它可以幫助你判斷系統(tǒng)的字節(jié)序:

import sys

print(sys.byteorder)

運行這段代碼,如果輸出結果是'little',說明你的系統(tǒng)是小端序;如果輸出結果是'big',則表示系統(tǒng)是大端序。在大部分個人計算機上,特別是使用x86和x86-64架構的系統(tǒng),輸出應該是'little'。

相關推薦