Modbus协议和应用开发介绍

因业务须要了解Modbus协议的使用,所以对Modbus的协议,以及相应的C#处理应用进行了解,针对协议的几种方式(RTU、ASCII、TCPIP)进行了封装,以及对Modbus的各类功能码的特色进行了详细的了解,本篇随笔基于这些知识进行了必定的梳理和介绍,主要内容包括Modbus协议简要介绍、Modbus模拟工具使用和Modbus应用开发几个部分。数据库

1)Modbus协议简要介绍

Modbus 协议是应用于电子控制器上的一种通用语言。经过此协议,控制器相互之间、控制器经由网络(例如以太网)和其它设备之间能够通讯。它已经成为一通用工业标准。有了它,不一样厂商生产的控制设备能够连成工业网络,进行集中监控。服务器

此协议定义了一个控制器能认识使用的消息结构,而无论它们是通过何种网络进行通讯的。它描述了一控制器请求访问其它设备的过程,若是回应来自其它设备的请求,以及怎样侦测错误并记录。它制定了消息域格局和内容的公共格式。 网络

当在一Modbus网络上通讯时,此协议决定了每一个控制器需要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行动。若是须要回应,控制器将生成反馈信息并用Modbus协议发出。在其它网络上,包含了Modbus协议的消息转换为在此网络上使用的帧或包结构。这种转换也扩展了根据具体的网络解决节地址、路由路径及错误检测的方法。ide

Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太网TCP/IP的modbus协议:modbusTCP。函数

Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。工具

标准的Modbus协议物理层接口有RS23二、RS42二、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)。this

Modbus串行连路上的的PDU以下所示。spa

错误检验域是对报文内容执行"冗余校验" 的计算结果。根据不一样的传输模式(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设备的时候,咱们只须要根据对应的说明书,获取对应的内容,就能够把例如温度、湿度、转速等一些设备或者机器人的参数得到,并记录在数据库里面,而后在应用模块中整合一些图表展现就能够很好的实现看板功能了。

相关文章
相关标签/搜索