Modbus協議和應用開發介紹

来源:https://www.cnblogs.com/wuhuacong/archive/2020/04/09/12666022.html
-Advertisement-
Play Games

因業務需要瞭解Modbus協議的使用,因此對Modbus的協議,以及相應的C#處理應用進行瞭解,針對協議的幾種方式(RTU、ASCII、TCPIP)進行了封裝,以及對Modbus的各種功能碼的特點進行了詳細的瞭解,本篇隨筆基於這些知識進行了一定的梳理和介紹,主要內容包括Modbus協議簡要介紹、Mo... ...


因業務需要瞭解Modbus協議的使用,因此對Modbus的協議,以及相應的C#處理應用進行瞭解,針對協議的幾種方式(RTU、ASCII、TCPIP)進行了封裝,以及對Modbus的各種功能碼的特點進行了詳細的瞭解,本篇隨筆基於這些知識進行了一定的梳理和介紹,主要內容包括Modbus協議簡要介紹、Modbus模擬工具使用和Modbus應用開發幾個部分。

1)Modbus協議簡要介紹

Modbus 協議是應用於電子控制器上的一種通用語言。通過此協議,控制器相互之間、控制器經由網路(例如乙太網)和其它設備之間可以通信。它已經成為一通用工業標準。有了它,不同廠商生產的控制設備可以連成工業網路,進行集中監控。

此協議定義了一個控制器能認識使用的消息結構,而不管它們是經過何種網路進行通信的。它描述了一控制器請求訪問其它設備的過程,如果回應來自其它設備的請求,以及怎樣偵測錯誤並記錄。它制定了消息域格局和內容的公共格式。 

當在一Modbus網路上通信時,此協議決定了每個控制器須要知道它們的設備地址,識別按地址發來的消息,決定要產生何種行動。如果需要回應,控制器將生成反饋信息並用Modbus協議發出。在其它網路上,包含了Modbus協議的消息轉換為在此網路上使用的幀或包結構。這種轉換也擴展了根據具體的網路解決節地址、路由路徑及錯誤檢測的方法。

Modbus由MODICON公司於1979年開發,是一種工業現場匯流排協議標準。1996年施耐德公司推出基於乙太網TCP/IP的modbus協議:modbusTCP。

Modbus協議是一項應用層報文傳輸協議,包括ASCII、RTU、TCP三種報文類型。

標準的Modbus協議物理層介面有RS232、RS422、RS485和乙太網介面,採用master/slave方式通信。

對於串列連接,存在兩個變種,它們在數值數據表示不同和協議細節上略有不同。Modbus RTU是一種緊湊的,採用二進位表示數據的方式,Modbus ASCII是一種人類可讀的,冗長的表示方式。這兩個變種都使用串列通信(serial communication)方式。RTU格式後續的命令/數據帶有迴圈冗餘校驗的校驗和,而ASCII格式採用縱向冗餘校驗的校驗和。被配置為RTU變種的節點不會和設置為ASCII變種的節點通信,反之亦然。
對於通過TCP/IP(例如乙太網)的連接,存在多個Modbus/TCP變種,這種方式不需要校驗和計算。
對於所有的這三種通信協議在數據模型和功能調用上都是相同的,只有封裝方式是不同的。
Modbus有一個擴展版本Modbus Plus(Modbus+或者MB+),不過此協議是Modicon專有的,和Modbus不同。它需要一個專門的協處理器來處理類似HDLC的高速令牌旋轉。它使用1Mbit/s的雙絞線,並且每個節點都有轉換隔離裝置,是一種採用轉換/邊緣觸發而不是電壓/水平觸發的裝置。連接Modbus Plus到電腦需要特別的介面,通常是支持ISA(SA85),PCI或者PMCIA匯流排的板卡。

MODBUS 協議定義了一個與基礎通信層無關的簡單協議數據單元(PDU)。

Modbus串列連路上的的PDU如下所示。

錯誤檢驗域是對報文內容執行"冗餘校驗" 的計算結果。根據不同的傳輸模式(RTU or ASCII) 使用兩種不同的計算方法。

RTU的報文格式如下所示。

ASCII碼的報文格式如下所示。

 

 在 ASCII 模式, 報文用特殊的字元區分幀起始和幀結束。一個報文必須以一個‘冒號’ ( : ) (ASCII 十六進位3A )起始,以‘回車-換行’ (CR LF) 對(ASCII 十六進位0D 和0A) 結束。

 

而Modbus TCP數據幀包含報文頭、功能代碼和數據3部分。

MBAP Header長度共7個位元組,分別為Transaction identifier(事務標識符),Protocol identifier(協議標識符),Length(長度),

 Unitidentifier(單元標識符)組成,具體如下表所示:

請求和響應帶有六個位元組的首碼,如下:
      byte 0:     事務處理標識符 –由伺服器複製 –通常為 0
      byte 1:     事務處理標識符 –由伺服器複製 –通常為 0
      byte 2:     協議標識符= 0
      byte 3:     協議標識符= 0
      byte 4:     長度欄位 (上半部分位元組) = 0 (所有的消息長度小於256)
      byte 5:     長度欄位  (下半部分位元組)   = 後面位元組的數量
      byte 6:     單元標識符 (原“從站地址”)
      byte 7:     MODBUS 功能代碼
      byte 8 on:   所需的數據

數據區:數據區是根據不同的功能碼而不同。數據區可以是實際數值、設置點、主機發送給從機或從機發送給主機的地址。

標準的Modicon控制器使用RS232C實現串列的Modbus。Modbus的ASCII、RTU協議規定了消息、數據的結構、命令和就答的方式,數據通訊採用Maser/Slave方式。 
Modbus協議需要對數據進行校驗,串列協議中除有奇偶校驗外,ASCII模式採用LRC校驗,RTU模式採用16位CRC校驗.
ModbusTCP模式沒有額外規定校驗,因為TCP協議是一個面向連接的可靠協議。

對於常規的Modbus串口協議,我們來看看03功能碼的讀取寄存器的操作請求和響應代碼瞭解下。

請求PDU格式如下所示。

 響應的PDU格式如下所示。

 一個請求讀寄存器108-110 的實例:

可以註意到,很多數據的處理,需要拆分高位低位,高位在前,低位在後的模式。 

根據這些RTU、ASCII、TCPIP的Modbus協議的不同,我們可以構建一個通用的處理程式來處理這些操作,在後面的應用開發部分繼續介紹。

 

2)Modbus模擬工具使用

一般在做Mobus前期的開發的時候,一般不是針對具體的Modbus設備進行寄存器的處理,而是使用Modbus模擬工具來進行調試,一般我們需要配合Modbus Slave、Modbus Poll、Virtual Serial Port Driver這幾個模擬軟體來進行開發的。

Modbus Poll :Modbus主機模擬器,用於測試和調試Modbus從設備。該軟體支持Modbus的RTU、ASCII、TCP/IP。用來幫助開發人員測試Modbus從設備,或者其它Modbus協議的測試和模擬。

 Modbus Slave: Modbus從設備模擬器,可以模擬32個從設備/地址域。每個介面都提供了對EXCEL報表的OLE自動化支持。主要用來模擬Modbus從站設備,接收主站的命令包,回送數據包。幫助Modbus通訊設備開發人員進行Modbus通訊協議的模擬和測試,用於模擬、測試、調試Modbus通訊設備。 

 Virtual Serial Port Driver:虛擬串口工具,不需要串口接線,提供虛擬的串口,適合學習和調試使用。

配合這幾款軟體,我們就可以實現串口Modbus協議的模擬測試了,如果我們使用Modbus的TCPIP協議,那麼我們不需要VSPD也可以。

如果我們使用Modbus協議的串口通訊方式,那麼我們先要使用VSPD進行串口的配對模擬,模擬出兩個通訊的串口埠,埠配對模擬成功後,我們可以看到設備管理器中增加了兩個埠了。

 接著使用從機模擬器,模擬一個Modbus從機供測試,通過菜單【Connection】【Connect】啟動,我們選擇連接方式為串口,埠則選擇我們配對的其中一個埠即可,如下圖所示。

 其中模式選擇RTU或者ASCII都可以,這兩個模式協議有所不同,一旦從機選擇RTU模式,那麼Modbus主機也需要選擇對應的RTU模式,反之亦然。 

其他串口設置,如波特率、數據位、奇偶位、停止位等預設配置即可。

如果我們選擇TCPIP模式,那麼對應Modbus主機也需要選擇TCPIP方式。

 一旦Modbus從機啟動,就會處理來自Modbus主機的指令請求(如果有的話),並做相應處理,我們可以通過【Display】【Communication】菜單彈出的對話框,瞭解到對應請求和應答的協議詳細信息。

 Modbus主機的啟動和ModBus從機類似,我們根據ModBus從機的配置,選擇對應的主機配置,Modbus模擬主機啟動和查看通訊記錄界面如下所示。

另外我們可以通過【Display】裡面選擇內容顯示的進位格式。

在從機的設置裡面,我們可以修改從機的定義信息,以便設置對應的從機ID,功能碼,其實地址,長度或者數量的信息,如下界面所示。

 我們可以根據實際的寄存器地址和數量,設置對應的數值,如下是顯示4個數據的內容設置和顯示內容。

設置後正常的內容顯示如下。

 同時我們也需要設置對應Modbus主機模擬器的地址和數量,正確設置後可以正常顯示。

通過更深一步的設置或者調整,我們可以極大程度的進行模擬Modbus實際設備的處理方式,從而在沒有實際Modbus硬體設備的情況下儘可能通過前期的模擬完成常規功能的測試和準備。

在我們開發Modbus應用的時候,我們對照相應的主從機Modbus協議請求和應答,能夠檢查我們程式的輸出是否正常,從而可以快速的開發Modbus的應用處理功能。

3)Modbus應用開發

為了模擬對接Modbus的RTU、ASCII、TCP/IP協議處理,我根據不同協議的處理方式定義了一個輔助函數,然後統一進行處理,以便達到統一調用的處理便利。

首先我們來看看使用串口模式下(RTU、ASCII)的處理界面效果,這個直接獲取模擬器Modbus Slave從機的數值進行顯示的。

TCPIP網路方式對接Modbus界面處理效果如下所示。

兩者數據均來源於Modbus Slave從機的數值,只是它們對接的方式不同。

 

串口的處理,我通過SerialPortUtil類來使用Windows的串口類,處理對應的串口操作,通過定義事件的方式,使得串口收到數據的時候,及時通知調用者進行界面更新處理即可。

//使用字元串參數構造
serial = new SerialPortUtil(portname, this.txtBaudRate.Text, this.txtParity.Text, this.txtDataBits.Text, this.txtStopBit.Text);
//收到數據處理的事件
serial.DataReceived += Serial_DataReceived;
serial.RTUMode = this.radRTU.Checked;//預設RTU模式為True,否則使用ASCII模式

收到數據後,及時通過委托方式,通知UI進行界面的更新顯示。

/// <summary>
/// 收到串口響應事件後,及時進行處理(更新在界面上)
/// </summary>
/// <param name="e"></param>
private void Serial_DataReceived(DataReceivedEventArgs e)
{
    //記錄在日誌,方便複製
    LogTextHelper.Info(e.DataReceived);

    //使用委托進行處理界面控制項的數據更新
    this.txtResponse.Invoke(new MethodInvoker(()=> 
    {
        //顯示在界面上
        this.txtResponse.AppendText(e.DataReceived);
        this.txtResponse.AppendText(Environment.NewLine);

        var dataBytes = e.BytesReceived;
        if(dataBytes != null && dataBytes.Length > 2)
        {
            var function = dataBytes[1];
            if(function > 0x80)//128
            {
                //Modbus的異常代碼大於128,如果是異常,則可以解析錯誤
                var newFunction = function - 0x80;
                lblTips.Text = "響應有異常,功能代碼:" + newFunction.ToString("D2");
                lblTips.Text += ",錯誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
            }
            else
            {
                lblTips.Text = "響應正常";//小於128的為正常響應
            }
        }
    }));
}

而對於網路方式,我們先要定義一個Socket通訊的基類,封裝相關的通訊處理操作。

 

 

然後簡單構建一個子類進行使用,如下所示。

    /// <summary>
    /// 通信類子類
    /// </summary>
    public class ModbusClient : BaseSocketClient
    {
        public ModbusClient()
        {
            this.Name = "ModbusClient";
        }
    }

界面處理的時候,我們只需要初始化一個ModbusClient類來使用即可,如下代碼所示。

client = new ModbusClient();
//收到數據處理的事件
client.DataReceived += Client_DataReceived;

收到數據通知界面進行更新的操作如下所示。

private void Client_DataReceived(DataReceivedEventArgs e)
{
    //記錄在日誌,方便複製
    LogTextHelper.Info(e.DataReceived);

    //使用委托進行處理界面控制項的數據更新
    this.txtResponse.Invoke(new MethodInvoker(() =>
    {
        this.txtResponse.AppendText(e.DataReceived);
        this.txtResponse.AppendText(Environment.NewLine);

        var dataBytes = e.BytesReceived;
        if (dataBytes != null && dataBytes.Length > 2)
        {
            //串口功能碼為第二個位元組,TCP/IP功能碼為第8個
            var function = dataBytes[7];
            if (function > 0x80)//128
            {
                //Modbus的異常代碼大於128,如果是異常,則可以解析錯誤
                var newFunction = function - 0x80;
                lblTips.Text = "響應有異常,功能代碼:" + newFunction.ToString("D2");
                lblTips.Text += ",錯誤描述:" + ((ModBusExceptionCode)newFunction).ToString();
            }
            else
            {
                lblTips.Text = "響應正常";//小於128的為正常響應
            }
        }
    }));
}

不管是串口的RTU或者ASCII,又或者是TCPIP的協議,我們可以通過定義一個協議封裝的輔助類ModbusQueryHelper來處理協議的具體細節。

    /// <summary>
    /// Modbus查詢消息生成輔助類,可以用於串口RTU/ASCII協議,也可以用於TCPIP協議。
    /// 用於生成各種功能代碼的消息內容。
    /// </summary>
    public class ModbusQueryHelper
    {
        /// <summary>
        /// 是否為RTU模式,預設為True,否則為ASCII方式
        /// </summary>
        public ModbusProtocol Protocol { get; set; } = ModbusProtocol.RTU;

        /// <summary>
        /// 預設函數
        /// </summary>
        public ModbusQueryHelper()
        {
        }

        /// <summary>
        /// 參數化構造,指定RTU模式
        /// </summary>
        /// <param name="protocal">Modbus協議:ASCII,RTU, TCP,預設為RTU</param>
        public ModbusQueryHelper(ModbusProtocol protocal)
        {
            this.Protocol = protocal;
        }

而其中ModbusProtocol是一個枚舉,定義如下所示。

    /// <summary>
    /// 幾種常用的Modbus協議
    /// </summary>
    public enum ModbusProtocol
    {
        /// <summary>
        /// 串口的ASCII模式
        /// </summary>
        ASCII,
        /// <summary>
        /// 串口的RTU模式
        /// </summary>
        RTU, 
        /// <summary>
        /// 網路TCPIP模式
        /// </summary>
        TCP
    }

我們通過ModbusQueryHelper 類,可以處理不同協議之間的封裝細節,並可以對各種功能碼的協議進行封裝處理。

 

 

 

 以上就是 相關Modbus的應用處理和封裝,對於常規的Modbus協議可以極大簡化對接處理,在實際對接Modbus設備的時候,我們只需要根據對應的說明書,獲取對應的內容,就可以把例如溫度、濕度、轉速等一些設備或者機器人的參數獲得,並記錄在資料庫裡面,然後在應用模塊中整合一些圖表展示就可以很好的實現看板功能了。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在編程語言中有兩個很基礎的概念,即方法(method)和函數(function)。如果達到了編程初級/入門級水平,那麼你肯定在心中已有了初步的答案。 也許在你心中已有答案了 除去入參、返回值、匿名函數之類的正確的形式內容之外,你也許會說“函數就是定義在類外面的,而方法就是定義在類裡面的,跟類綁定的” ...
  • 為什麼要閱讀源碼?這是一個有趣的問題,類似的問題還有,為什麼要看書?為什麼要爬山? 這也是一個哲學問題,我想每個人都有不同的答案,下麵我是對閱讀源碼好處的一些思考。 (PS:也歡迎你在評論區留言補充) 閱讀源碼的好處 1.知其然知其所以然 這是一句 IT 人都很熟悉的話,在開源軟體越來越多,並且升級 ...
  • Java 菜鳥,會把可變的配置信息寫死在代碼里;Java 老鳥,會把可變的配置信息提取到配置文件中。坊間流傳這麼一句非科學的衡量標準,來評判程式員的級別。 那麼,項目中的配置信息,你平時都是怎樣來實現的呢?你項目中用過哪些配置文件呢? 近期將結合實際項目或源碼,說說這些年用過的那些有關配置的奇技淫巧 ...
  • 2020年九大java開發框架,小編主要介紹Spring框架、Hibernate框架、Struts框架、Play框架、GoogleWebToolkit框架、Grails框架、Blade框架、JavaServerFaces框架、Vaadin框架九大框架,希望您可以多java框架有更多的認識。 一、Sp ...
  • 接上2篇案例: "Python_批量下載1000個apk" , "Python_批量驗證1000個apk" , 都下載完了, 也驗證了下載文件的版本號和MD5後,就要開始批量安裝了, 然後開始嘗試運行打開這個app, 做最基本的功能檢點,最後就要考慮卸載App了。 本篇內容主要介紹如何用Python ...
  • 之前困擾了很久的Strcpy()和Strcpy_s(),終於知道解決方法了~~ 之前用的時候,Strcpy不安全,Strcpy又不會用。 1.解決方法 官方給的方法是:用strcpy_s 格式:strcpy_s(st1 , lenth+1 , st2) //strcpy_s(變數,常量,變數/常量) ...
  • @2020.4.9 作業一:自己試驗一下菱形問題下的屬性查找順序(經典類用Python2試驗) 作業二: 在昨天作業的基礎之上——周三的作業—類與對象2+封裝 https://www.cnblogs.com/bigorangecc/p/12663017.html (本周周二作業—類與對象—https ...
  • 出問題的代碼如下: public class DBBookChaptersService : IBookChaptersService { private readonly BooksContext _booksContext; public DBBookChaptersService(BooksC ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...