加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 一、ModBusTCP協(xié)議
    • 二、一個(gè)Demo及其引發(fā)的問(wèn)題
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

python實(shí)現(xiàn)ModBusTCP協(xié)議的client

12/06 13:53
1351
閱讀需 20 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

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

一、ModBusTCP協(xié)議

1、了解ModBusTCP協(xié)議

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

以下是 Modbus TCP 的基本特點(diǎn):

(1)基于 TCP/IP 協(xié)議:Modbus TCP 使用 TCP/IP 網(wǎng)絡(luò)進(jìn)行通信,可以通過(guò)以太網(wǎng)互聯(lián)網(wǎng)等方式進(jìn)行遠(yuǎn)程通信。

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

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

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

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

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

(7)安全性:由于 Modbus TCP 通信是基于 TCP/IP 的,因此可以通過(guò)網(wǎng)絡(luò)安全措施(例如 VPN、防火墻等)提供數(shù)據(jù)傳輸?shù)陌踩浴?/p>

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

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

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

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

區(qū)別主要在于:

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

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

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

(4)適用場(chǎng)景不同:Modbus TCP 主要用于工業(yè)自動(dòng)化控制系統(tǒng)中,用于實(shí)時(shí)數(shù)據(jù)交換;TCP client 可以用于各種通信場(chǎng)景,包括 Web 客戶端、數(shù)據(jù)庫(kù)客戶端、文件傳輸?shù)取?/p>

綜上所述,Modbus TCP 是一種特定協(xié)議的 TCP client,用于在工業(yè)自動(dòng)化領(lǐng)域?qū)崿F(xiàn)設(shè)備之間的數(shù)據(jù)交換。TCP client 則是一個(gè)更通用的概念,可以與各種服務(wù)器進(jìn)行通信,不受特定協(xié)議限制。

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

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

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

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

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

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

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

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

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

總體來(lái)說(shuō),Modbus TCP 協(xié)議的數(shù)據(jù)幀格式是固定的,但是具體的數(shù)據(jù)內(nèi)容和長(zhǎng)度會(huì)根據(jù)功能碼的不同而有所變化。詳細(xì)的數(shù)據(jù)幀格式需要根據(jù)具體的功能碼和操作類型來(lái)確定。

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

1、一個(gè)Demo

from pymodbus.client import ModbusTcpClient


if __name__ == "__main__":
    # Modbus TCP服務(wù)器的IP地址和端口號(hào)
    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   # 要讀取的寄存器數(shù)量
        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 都實(shí)現(xiàn)了哪些功能碼

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

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

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

  • address: 起始線圈的地址
  • count: 要讀取的線圈數(shù)量
  • slave: Modbus 單元標(biāo)識(shí)符(站號(hào))

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

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

  • address: 起始輸入線圈的地址
  • count: 要讀取的輸入線圈數(shù)量
  • slave: Modbus 單元標(biāo)識(shí)符

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

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

  • address: 起始保持寄存器的地址
  • count: 要讀取的保持寄存器數(shù)量
  • slave: Modbus 單元標(biāo)識(shí)符

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

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

  • address: 起始輸入寄存器的地址
  • count: 要讀取的輸入寄存器數(shù)量
  • slave: Modbus 單元標(biāo)識(shí)符

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

write_coil(address, value, slave=0)

  • address: 線圈的地址
  • value: 要寫入的值,True 表示 ON,F(xiàn)alse 表示 OFF
  • slave: Modbus 單元標(biāo)識(shí)符

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

write_register(address, value, slave=0)

  • address: 寄存器的地址
  • value: 要寫入的值
  • slave: Modbus 單元標(biāo)識(shí)符

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

write_coils(address, values, slave=0)

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

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

write_registers(address, values, slave=0)

  • address: 起始寄存器的地址
  • values: 要寫入的寄存器值,是一個(gè)整數(shù)列表
  • slave: Modbus 單元標(biāo)識(shí)符

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

只能說(shuō)有可能是反的。

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

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

這里為什么說(shuō)可能呢?原因很簡(jiǎn)單,通信的兩端如果內(nèi)存存儲(chǔ)方式不一樣,讀到的數(shù)據(jù)就是反的。比如PC是小端序,嵌入式設(shè)備是大端序,存儲(chǔ)在嵌入式設(shè)備的值為AC,高位是A,低位是C,那么PC在以小端序存儲(chǔ)時(shí),會(huì)把對(duì)方高位的A存儲(chǔ)低位,把對(duì)方低位的C存儲(chǔ)到高位,就變成了CA。

如果二者都是大端序或小端序的存儲(chǔ)方式,則不會(huì)有該問(wèn)題,因?yàn)樗麄兊淖x取和存儲(chǔ)方式都是一樣的。

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

當(dāng)數(shù)據(jù)在大端字節(jié)序(big-endian)和小端字節(jié)序(little-endian)之間傳送時(shí),通常需要進(jìn)行大小端轉(zhuǎn)換。這是因?yàn)椴煌?a class="article-link" target="_blank" href="/tag/%E5%A4%84%E7%90%86%E5%99%A8/">處理器和計(jì)算機(jī)體系結(jié)構(gòu)可能使用不同的字節(jié)序,如果發(fā)送方和接收方的字節(jié)序不同,就需要進(jìn)行轉(zhuǎn)換。

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

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

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

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

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

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

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

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

總結(jié):

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

請(qǐng)注意,這些大小是Modbus協(xié)議規(guī)定的標(biāo)準(zhǔn)大小,不同的設(shè)備可能有不同的實(shí)現(xiàn),因此在實(shí)際應(yīng)用中,你應(yīng)該查閱設(shè)備的文檔以確認(rèn)具體的數(shù)據(jù)大小。

6、一個(gè)保持寄存器能存2個(gè)字母,用兩個(gè)保持寄存器存儲(chǔ)ACK1,第一個(gè)寄存器存儲(chǔ)AC,第二個(gè)存儲(chǔ)K1,大端序會(huì)怎么存儲(chǔ)?

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

在大端序下,存儲(chǔ)順序如下:

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

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

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

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

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

import sys

print(sys.byteorder)

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

相關(guān)推薦

電子產(chǎn)業(yè)圖譜