加入星計劃,您可以享受以下權益:

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

python實現(xiàn)ModBusRTU服務端

12/06 13:52
1573
閱讀需 16 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

python實現(xiàn)基于串口通信的ModBusRTU服務端是一件簡單的事情,只要通過pymodbus模塊就可以實現(xiàn)。

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

1、一個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ù)存儲區(qū)
    # data_block = ModbusSequentialDataBlock(0, [0] * 100)  # 100個保持寄存器?
    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)

    # 啟動Modbus RTU服務器
    # 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()
    # 設置保持寄存器的0地址的值為s
    # 定義函數(shù)參數(shù)
    write_address = 0  # 起始地址
    read_address = 10
    read_count = 10
    values = [115]  # 觸發(fā)指令s 要設置的多個值列表,如[10, 20, 30]

    # 調(diào)用函數(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())  # 停止服務器

在這里簡單說明上述程序涉及到的對象:

(1)ModbusRtuFramer的作用

在 Modbus 通信協(xié)議中,數(shù)據(jù)以幀的形式進行傳輸。ModbusRtuFramer 是 pymodbus 庫中的一個類,它的作用是負責處理 Modbus RTU 幀的編碼和解碼。

具體來說,ModbusRtuFramer 完成了以下幾個主要任務:

①編碼(封裝)Modbus RTU 幀:當你需要發(fā)送 Modbus RTU 請求或響應時,ModbusRtuFramer 負責將請求或響應的數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進行封裝,生成符合 Modbus RTU 規(guī)范的幀,以便發(fā)送到 Modbus 設備。

②解碼(解析)Modbus RTU 幀:當你從 Modbus 設備接收到數(shù)據(jù)時,ModbusRtuFramer 負責將接收到的二進制數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進行解析,提取出請求或響應的數(shù)據(jù),以便進行后續(xù)的處理和分析。

③錯誤檢測和糾正:ModbusRtuFramer 也負責檢測 Modbus RTU 幀中的錯誤,比如奇偶校驗錯誤、幀起始符和結束符錯誤等。如果幀中存在錯誤,ModbusRtuFramer 會幫助你識別錯誤的位置和類型,便于進行糾正或錯誤處理。

總之,ModbusRtuFramer 是一個處理 Modbus RTU 幀的工具,它確保了在 Modbus RTU 通信中,數(shù)據(jù)的正確封裝和解析,以及錯誤的檢測和處理。

(2)ModbusSequentialDataBlock、ModbusSlaveContext與ModbusServerContext的作用

在 pymodbus 庫中,ModbusSequentialDataBlock、ModbusSlaveContext 和 ModbusServerContext 是用來處理 Modbus 數(shù)據(jù)存儲和上下文的類,它們的作用如下:

①ModbusSequentialDataBlock:

ModbusSequentialDataBlock 是一個用于創(chuàng)建順序排列的 Modbus 寄存器塊的類。
它通常用于模擬設備的保持寄存器(Holding Registers)或輸入寄存器(Input Registers)。
這個類允許你定義寄存器的起始地址和初始化寄存器的值。
例如:ModbusSequentialDataBlock(0, [0] * 100) 創(chuàng)建了一個從地址 0 開始,包含 100 個初始值為 0 的保持寄存器的塊。

②ModbusSlaveContext:

ModbusSlaveContext 是一個用于表示 Modbus 從設備的類。
它包含一個或多個數(shù)據(jù)存儲塊(比如保持寄存器塊、輸入寄存器塊等)。
這個類可以用來創(chuàng)建一個模擬的 Modbus 從設備上下文。
你可以在這個上下文中添加多個不同類型的數(shù)據(jù)塊,模擬一個完整的 Modbus 從設備。

③ModbusServerContext:

ModbusServerContext 是一個用于表示整個 Modbus 服務器的類。
它包含一個或多個 Modbus 從設備的上下文(ModbusSlaveContext 實例)。
這個類可以用來創(chuàng)建一個完整的 Modbus 服務器環(huán)境,包含多個模擬的 Modbus 從設備。

在搭建 Modbus 通信環(huán)境時,你通常會創(chuàng)建 ModbusSequentialDataBlock 實例作為寄存器的存儲,然后將它們添加到 ModbusSlaveContext 中。最后,將多個 ModbusSlaveContext 實例添加到 ModbusServerContext 中,以構建一個包含多個從設備的 Modbus 服務器環(huán)境。這樣的架構可以讓你模擬多個不同類型的 Modbus 從設備。

(3)context = ModbusServerContext(slaves={1: data_block}, single=False)的解釋

在這個代碼行中,你創(chuàng)建了一個 ModbusServerContext 對象,該對象用于模擬一個 Modbus 服務器的上下文。ModbusServerContextpymodbus 模塊中的一個類,它用于存儲和管理 Modbus 服務器的數(shù)據(jù)。在這個特定的代碼行中,你傳遞了一些參數(shù)給 ModbusServerContext 構造函數(shù):

  • slaves={1: data_block}: 這個參數(shù)是一個字典,表示 Modbus 服務器的從設備。在這里,你創(chuàng)建了一個從設備,其 Modbus 地址為 1,并且將這個從設備關聯(lián)到了一個 data_block 對象上。data_block 可能是一個 ModbusSequentialDataBlock 對象,用于存儲 Modbus 寄存器的數(shù)據(jù)。
  • single=False: 這個參數(shù)用于確定是否將所有的從設備視為一個整體。當 single=True 時,所有的從設備共享相同的 Modbus 地址空間,它們沒有獨立的地址范圍。而當 single=False 時,每個從設備都有獨立的 Modbus 地址空間,它們的地址范圍是相互獨立的。

所以,這一行代碼的意義是創(chuàng)建了一個 ModbusServerContext 對象,該對象包含一個從設備(Modbus 地址為 1),并且這個從設備擁有獨立的 Modbus 地址空間。這個上下文可以在模擬多個獨立的 Modbus 設備時使用。

(4)關于StartSerialServer啟動服務器時必須的參數(shù)

必須傳入以下參數(shù)才能正常啟動

  • "context": context 表示傳入的 Modbus 上下文。
  • "framer": framer 表示 Modbus 使用的幀格式。
  • "port": port 表示串口號,例如 "COM46"。
  • "baudrate": baudrate表示波特率,即每秒傳輸?shù)奈粩?shù)。

這些參數(shù)將被傳遞給 StartSerialServer 函數(shù),用于啟動 Modbus RTU 服務器。

這需要根據(jù)客戶端的設置來對應設置。

2、權限問題

建議使用管理員權限運行腳本。

3、端口占用問題

確保端口沒有被其他軟件占用。

4、pymodbus版本問題

網(wǎng)上的示例代碼,StartSerialServer要求傳入serial模塊的Serial(port=COM, baudrate=Baudrate, timeout=2, bytesize=Databits, parity=Parity, stopbits=Stopbits)實例,我不確定是哪個版本,你可以試一下,目前在新版本只要傳入波特率和端口即可。

二、pymodbus相關文檔

1、官方文檔地址

Datastore — PyModbus 3.6.0dev documentation

2、Datastore對象

Datastore is responsible for managing registers for a server.

(1)pymodbus.datastore.ModbusSparseDataBlock

pymodbus.datastore.ModbusSparseDataBlock 是 PyModbus 庫中用于創(chuàng)建稀疏數(shù)據(jù)塊(sparse data block)的類。在 Modbus 協(xié)議中,數(shù)據(jù)通常被組織成多個數(shù)據(jù)塊,而每個數(shù)據(jù)塊包含一定數(shù)量的數(shù)據(jù)寄存器或者線圈。

ModbusSparseDataBlock 允許您創(chuàng)建包含不連續(xù)地址的數(shù)據(jù)塊。具體來說,您可以在數(shù)據(jù)塊中指定特定地址的數(shù)據(jù),而無需為數(shù)據(jù)塊的每個地址都分配內(nèi)存。這種方式可以有效地節(jié)省內(nèi)存空間,尤其是在處理大量數(shù)據(jù)時。

以下是 ModbusSparseDataBlock 的初始化參數(shù):

  • values:一個字典,包含要存儲的數(shù)據(jù)。字典的鍵是地址,值是相應地址的數(shù)據(jù)值。
  • address:數(shù)據(jù)塊的起始地址。
  • size:數(shù)據(jù)塊的大小,即包含的地址數(shù)量。

相關推薦

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