python實(shí)現(xiàn)基于串口通信的ModBusRTU服務(wù)端是一件簡(jiǎn)單的事情,只要通過(guò)pymodbus模塊就可以實(shí)現(xiàn)。
一、一個(gè)Demo及其引發(fā)的問(wèn)題
1、一個(gè)Demo
import asyncio
import json
import threading
import time
from pymodbus.server import StartSerialServer, ServerAsyncStop
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusServerContext, ModbusSlaveContext
from pymodbus.transaction import ModbusRtuFramer
from Application.common.private_socket import Request
if __name__ == "__main__":
# 定義串口配置
port = "COM46"
# Serial(port="COM46", baudrate=115200, timeout=2, bytesize=8, parity="N", stopbits=1)
framer = ModbusRtuFramer
# 創(chuàng)建數(shù)據(jù)存儲(chǔ)區(qū)
# data_block = ModbusSequentialDataBlock(0, [0] * 100) # 100個(gè)保持寄存器?
data_block = ModbusSlaveContext(
hr=ModbusSequentialDataBlock(0, [0] * 100)
)
# store = ModbusSlaveContext(hr=data_block)
# context = ModbusServerContext(slaves=store)
context = ModbusServerContext(slaves={1: data_block}, single=False)
# 啟動(dòng)Modbus RTU服務(wù)器
# StartSerialServer(context=context, framer=framer, port="COM46")
# thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600, "timeout":2, "bytesize":8, "parity":"N", "stopbits":1})
thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600})
thread.start()
# 設(shè)置保持寄存器的0地址的值為s
# 定義函數(shù)參數(shù)
write_address = 0 # 起始地址
read_address = 10
read_count = 10
values = [115] # 觸發(fā)指令s 要設(shè)置的多個(gè)值列表,如[10, 20, 30]
# 調(diào)用函數(shù),設(shè)置0地址為觸發(fā)指令s
data_block.setValues(16, write_address, values)
while True:
time.sleep(3)
# 獲取保持寄存器的值并打印
hr_values = data_block.getValues(3, 0, count=20)
print("Hold Register Values:", hr_values)
asyncio.run(ServerAsyncStop()) # 停止服務(wù)器
在這里簡(jiǎn)單說(shuō)明上述程序涉及到的對(duì)象:
(1)ModbusRtuFramer的作用
在 Modbus 通信協(xié)議中,數(shù)據(jù)以幀的形式進(jìn)行傳輸。ModbusRtuFramer 是 pymodbus 庫(kù)中的一個(gè)類(lèi),它的作用是負(fù)責(zé)處理 Modbus RTU 幀的編碼和解碼。
具體來(lái)說(shuō),ModbusRtuFramer 完成了以下幾個(gè)主要任務(wù):
①編碼(封裝)Modbus RTU 幀:當(dāng)你需要發(fā)送 Modbus RTU 請(qǐng)求或響應(yīng)時(shí),ModbusRtuFramer 負(fù)責(zé)將請(qǐng)求或響應(yīng)的數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進(jìn)行封裝,生成符合 Modbus RTU 規(guī)范的幀,以便發(fā)送到 Modbus 設(shè)備。
②解碼(解析)Modbus RTU 幀:當(dāng)你從 Modbus 設(shè)備接收到數(shù)據(jù)時(shí),ModbusRtuFramer 負(fù)責(zé)將接收到的二進(jìn)制數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進(jìn)行解析,提取出請(qǐng)求或響應(yīng)的數(shù)據(jù),以便進(jìn)行后續(xù)的處理和分析。
③錯(cuò)誤檢測(cè)和糾正:ModbusRtuFramer 也負(fù)責(zé)檢測(cè) Modbus RTU 幀中的錯(cuò)誤,比如奇偶校驗(yàn)錯(cuò)誤、幀起始符和結(jié)束符錯(cuò)誤等。如果幀中存在錯(cuò)誤,ModbusRtuFramer 會(huì)幫助你識(shí)別錯(cuò)誤的位置和類(lèi)型,便于進(jìn)行糾正或錯(cuò)誤處理。
總之,ModbusRtuFramer 是一個(gè)處理 Modbus RTU 幀的工具,它確保了在 Modbus RTU 通信中,數(shù)據(jù)的正確封裝和解析,以及錯(cuò)誤的檢測(cè)和處理。
(2)ModbusSequentialDataBlock、ModbusSlaveContext與ModbusServerContext的作用
在 pymodbus 庫(kù)中,ModbusSequentialDataBlock、ModbusSlaveContext 和 ModbusServerContext 是用來(lái)處理 Modbus 數(shù)據(jù)存儲(chǔ)和上下文的類(lèi),它們的作用如下:
①M(fèi)odbusSequentialDataBlock:
ModbusSequentialDataBlock 是一個(gè)用于創(chuàng)建順序排列的 Modbus 寄存器塊的類(lèi)。
它通常用于模擬設(shè)備的保持寄存器(Holding Registers)或輸入寄存器(Input Registers)。
這個(gè)類(lèi)允許你定義寄存器的起始地址和初始化寄存器的值。
例如:ModbusSequentialDataBlock(0, [0] * 100) 創(chuàng)建了一個(gè)從地址 0 開(kāi)始,包含 100 個(gè)初始值為 0 的保持寄存器的塊。
②ModbusSlaveContext:
ModbusSlaveContext 是一個(gè)用于表示 Modbus 從設(shè)備的類(lèi)。
它包含一個(gè)或多個(gè)數(shù)據(jù)存儲(chǔ)塊(比如保持寄存器塊、輸入寄存器塊等)。
這個(gè)類(lèi)可以用來(lái)創(chuàng)建一個(gè)模擬的 Modbus 從設(shè)備上下文。
你可以在這個(gè)上下文中添加多個(gè)不同類(lèi)型的數(shù)據(jù)塊,模擬一個(gè)完整的 Modbus 從設(shè)備。
③ModbusServerContext:
ModbusServerContext 是一個(gè)用于表示整個(gè) Modbus 服務(wù)器的類(lèi)。
它包含一個(gè)或多個(gè) Modbus 從設(shè)備的上下文(ModbusSlaveContext 實(shí)例)。
這個(gè)類(lèi)可以用來(lái)創(chuàng)建一個(gè)完整的 Modbus 服務(wù)器環(huán)境,包含多個(gè)模擬的 Modbus 從設(shè)備。
在搭建 Modbus 通信環(huán)境時(shí),你通常會(huì)創(chuàng)建 ModbusSequentialDataBlock 實(shí)例作為寄存器的存儲(chǔ),然后將它們添加到 ModbusSlaveContext 中。最后,將多個(gè) ModbusSlaveContext 實(shí)例添加到 ModbusServerContext 中,以構(gòu)建一個(gè)包含多個(gè)從設(shè)備的 Modbus 服務(wù)器環(huán)境。這樣的架構(gòu)可以讓你模擬多個(gè)不同類(lèi)型的 Modbus 從設(shè)備。
(3)context = ModbusServerContext(slaves={1: data_block}, single=False)的解釋
在這個(gè)代碼行中,你創(chuàng)建了一個(gè) ModbusServerContext
對(duì)象,該對(duì)象用于模擬一個(gè) Modbus 服務(wù)器的上下文。ModbusServerContext
是 pymodbus
模塊中的一個(gè)類(lèi),它用于存儲(chǔ)和管理 Modbus 服務(wù)器的數(shù)據(jù)。在這個(gè)特定的代碼行中,你傳遞了一些參數(shù)給 ModbusServerContext
構(gòu)造函數(shù):
slaves={1: data_block}
: 這個(gè)參數(shù)是一個(gè)字典,表示 Modbus 服務(wù)器的從設(shè)備。在這里,你創(chuàng)建了一個(gè)從設(shè)備,其 Modbus 地址為1
,并且將這個(gè)從設(shè)備關(guān)聯(lián)到了一個(gè)data_block
對(duì)象上。data_block
可能是一個(gè)ModbusSequentialDataBlock
對(duì)象,用于存儲(chǔ) Modbus 寄存器的數(shù)據(jù)。single=False
: 這個(gè)參數(shù)用于確定是否將所有的從設(shè)備視為一個(gè)整體。當(dāng)single=True
時(shí),所有的從設(shè)備共享相同的 Modbus 地址空間,它們沒(méi)有獨(dú)立的地址范圍。而當(dāng)single=False
時(shí),每個(gè)從設(shè)備都有獨(dú)立的 Modbus 地址空間,它們的地址范圍是相互獨(dú)立的。
所以,這一行代碼的意義是創(chuàng)建了一個(gè) ModbusServerContext
對(duì)象,該對(duì)象包含一個(gè)從設(shè)備(Modbus 地址為 1
),并且這個(gè)從設(shè)備擁有獨(dú)立的 Modbus 地址空間。這個(gè)上下文可以在模擬多個(gè)獨(dú)立的 Modbus 設(shè)備時(shí)使用。
(4)關(guān)于StartSerialServer啟動(dòng)服務(wù)器時(shí)必須的參數(shù)
必須傳入以下參數(shù)才能正常啟動(dòng)
"context": context
表示傳入的 Modbus 上下文。"framer": framer
表示 Modbus 使用的幀格式。"port": port
表示串口號(hào),例如"COM46"
。"baudrate": baudrate
表示波特率,即每秒傳輸?shù)奈粩?shù)。
這些參數(shù)將被傳遞給 StartSerialServer
函數(shù),用于啟動(dòng) Modbus RTU 服務(wù)器。
這需要根據(jù)客戶(hù)端的設(shè)置來(lái)對(duì)應(yīng)設(shè)置。
2、權(quán)限問(wèn)題
建議使用管理員權(quán)限運(yùn)行腳本。
3、端口占用問(wèn)題
確保端口沒(méi)有被其他軟件占用。
4、pymodbus版本問(wèn)題
網(wǎng)上的示例代碼,StartSerialServer要求傳入serial模塊的Serial(port=COM, baudrate=Baudrate, timeout=2, bytesize=Databits, parity=Parity, stopbits=Stopbits)實(shí)例,我不確定是哪個(gè)版本,你可以試一下,目前在新版本只要傳入波特率和端口即可。
二、pymodbus相關(guān)文檔
1、官方文檔地址
Datastore — PyModbus 3.6.0dev documentation
2、Datastore對(duì)象
Datastore is responsible for managing registers for a server.
(1)pymodbus.datastore.ModbusSparseDataBlock
pymodbus.datastore.ModbusSparseDataBlock
是 PyModbus 庫(kù)中用于創(chuàng)建稀疏數(shù)據(jù)塊(sparse data block)的類(lèi)。在 Modbus 協(xié)議中,數(shù)據(jù)通常被組織成多個(gè)數(shù)據(jù)塊,而每個(gè)數(shù)據(jù)塊包含一定數(shù)量的數(shù)據(jù)寄存器或者線圈。
ModbusSparseDataBlock
允許您創(chuàng)建包含不連續(xù)地址的數(shù)據(jù)塊。具體來(lái)說(shuō),您可以在數(shù)據(jù)塊中指定特定地址的數(shù)據(jù),而無(wú)需為數(shù)據(jù)塊的每個(gè)地址都分配內(nèi)存。這種方式可以有效地節(jié)省內(nèi)存空間,尤其是在處理大量數(shù)據(jù)時(shí)。
以下是 ModbusSparseDataBlock
的初始化參數(shù):
values
:一個(gè)字典,包含要存儲(chǔ)的數(shù)據(jù)。字典的鍵是地址,值是相應(yīng)地址的數(shù)據(jù)值。address
:數(shù)據(jù)塊的起始地址。size
:數(shù)據(jù)塊的大小,即包含的地址數(shù)量。