modbus tcp_modbus tcp教程 - CSDN
精华内容
参与话题
  • Modbus TCP 入门学习

    万次阅读 2019-04-13 11:29:27
    1. ModBus通讯协议简介  (摘抄:来自网络)Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用...

    记录下我入门学习的过程,供日后回看,文字部分多是转载他人blog,有注明来源地址;实验部分为真实测试结果。

    1. ModBus通讯协议简介

      (摘抄:来自网络)Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为Modbus Master,从设备方使用的协议称为Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。Modbus通讯物理接口可以选用串口(包括RS232和RS485),也可以选择以太网口。其通信遵循以下的过程:

      ● 主设备向从设备发送请求

      ● 从设备分析并处理主设备的请求,然后向主设备发送结果

      ● 如果出现任何差错,从设备将返回一个异常功能码

    2. Modbus TCP 的数据帧 

         由MBAP 头和PDU 构成, MBAP= Modbus Application Protocol Header(Modbus应用协议) 头部

         PDU = Protocol Data Unit (数据单元)

    ADU:Application Data Unit

    上面截图来源:http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf

    头部MBAP:

    例如:

    3:功能码

    来源:https://blog.csdn.net/iknow_nothing/article/details/84292914 

    modbus的操作对象有四种:线圈、离散输入、输入寄存器、保持寄存器

    线圈:PLC的输出位,开关量,在MODBUS中可读可写
    离散量:PLC的输入位,开关量,在MODBUS中只读
    输入寄存器:PLC中只能从模拟量输入端改变的寄存器,在MODBUS中只读
    保持寄存器:PLC中用于输出模拟量信号的寄存器,在MODBUS中可读可写
    根据对象的不同,modbus的功能码有:

    0x01:读线圈
    0x02:读离散量输入
    0x03:读保持寄存器 

    0x04:读输入寄存器

    0x05:写单个线圈
    0x06:写单个保持寄存器
    0x10:写多个保持寄存器
    0x0F:写多个线圈

    4:实验

    准备一个C# Socket的收发模型封装类,下载一个Modbus Slave工具 

    序列号:5455415451475662

    0x01:读线圈
    在从站中读1~2000个连续线圈状态,ON=1,OFF=0

    下面截图来源:https://blog.csdn.net/thebestleo/article/details/52269999#commentsedit

    请求:MBAP 功能码 + 起始地址H 起始地址L +数量H 数量L
    响应:MBAP 功能码 数据长度 数据(一个地址的数据为1位)
    :在从站0x01中,读取开始地址为0x0002的线圈数据,读16位

     请求:00 01 00 00 00 06 01 (Slave ID)01(功能码) 00 02 (起始地址)00 10(长度16转化16进制为10)

    byte[] data = new byte[] { 0x00,0x01,0x00,0x00,0x00,0x06, 0x01, 0x01, 0x00, 0x02, 0x00, 0x10 };

     

    验证:0x55 转化为二进制位: 01010101

               0x15转化为二进制位:  00010101

    把上面2个二进制按一定的方向组合起来就和上图配置的 开关量保持一致了。从C# 程序上来说:

    byte[] data = new byte[] { 0x55, 0x15 };

    data[0]是地位,data[1]是高位,深入到每个byte里面的二进制,高位在前,低位在后。ModBus使用Big-Endian表示地址和数据项。

    0x02:读离散量输入

    过程和0x01一致,略

    0x03:读保持寄存器

    从远程设备中读保持寄存器连续块的内容

    • 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)
    • 响应:MBAP 功能码 数据长度 寄存器数据(长度:9+寄存器数量×2)
     byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x4f, 0x00, 0x03 };

    响应:

    见下面0x04,过程一致;

    0x04:读输入寄存器

    从一个远程设备中读1~2000个连续输入寄存器

    • 请求:MBAP+功能码+起始地址H 起始地址L+ 寄存器数量H 寄存器数量L(共12字节)
    • 响应:MBAP + 功能码 + 数据长度 + 寄存器数据 (长度:9+寄存器数量×2)
    byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x4f, 0x00, 0x05 };

    得到响应如下图所示:

    注意:16位的寄存器存储的最大带符号2进制数是32767

    0x05:写单个线圈
    将从站中的一个输出写成ON或OFF,0xFF00请求输出为ON,0x000请求输出为OFF

    80的16进制为0x50 

     byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x50, 0x00, 0x00 };

    结果为:

     

    0x06:写单个保持寄存器

    • 请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
    • 响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
    byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,0x01, 0x06,  0x00, 0x4f, 0x00, 0xa8 };

     

     0x10:写多个保持寄存器

    • 请求:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L 字节长度 寄存器值(13+寄存器数量×2)
    • 响应:MBAP 功能码 起始地址H 起始地址L 寄存器数量H 寄存器数量L(共12字节)

    例如:从0x02开始,写入0x03个寄存器,字节数为:0x06, 值分别为:00 0A,01 02,00 A8

    byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x010, 0x00, 0x02, 0x00, 0x03, 0x06,0x00,0x0A,0x01,0x02,0x00,0xa8 };

     0x0F:写多个线圈

    • 请求:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L 字节长度 输出值H 输出值L
    • 响应:MBAP 功能码 起始地址H 起始地址L 输出数量H 输出数量L

    上图的字节数N = 输出数量/8 或不足整除+1

    这里说明下为何协议里还要有一个字节数的存在,很好理解:假如输出值都是一致的,起始地址为0,输出16位长度和输出15个长度的请求如何区分呢,需要告诉PLC 改变的线圈的个数就由字节数来表示。

    例如:从地址0开始写入11个线圈,值为0xcd: 11001101

    byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x01, 0x0f, 0x00, 0x00,0x00,0x0b,0x02, 0xcd, 0xcd };

     5:长连接心跳

     在实际测试过程中发现大概1到2分钟之间,再次发送数据包时提示连接已经断开。如果频繁的连接则一直会保持连接!

    所以这里加一个定时器处理:

      private void timer1_Tick(object sender, EventArgs e)
            {
                byte[] data = new byte[] { 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01 };
                client.SendAsync(data);
            }

    不知道这个模拟Modbus Slave的缘故还是内部有一些超时的机制在内面,通过测试发现有这个现象,还未拿到真正的PLC硬件测试,暂时做一个记录。下面贴图为一个参考: 可能说的是TCP Keep Alive 机制

    6:Modbus 错误码

    来源:https://blog.csdn.net/ouyangxin95/article/details/78174071

    这里贴过来,汇总整理,方便学习之用:

    功能码表

      数据类型 功能描述 功能码 功能码(十六进制) 异常功能码
    比特访问 物理离散量输入 读输入离散量 02 0x02 0x82
    内部比特或者物理线圈 读线圈 01 0x01 0x81
    写单个线圈 05 0x05 0x85
    写多个线圈 15 0x0F 0x8F
     
    16比特访问 输入存储器 读输入寄存器 04 0x04 0x84
    内部存储器或物理输出存储器(保持寄存器) 读多个寄存器 03 0x03 0x83
    写单个寄存器 06 0x06 0x86
    写多个寄存器 16 0x10 0x90
    读/写多个寄存器 23 0x17 0x97
    屏蔽写寄存器 22 0x16 0x96
     
    文件记录访问 读文件记录 20 0x14  
    写文件记录 21 0x15  

     

    其中物理离散量输入和输入寄存器只能有I/O系统提供的数据类型,即只能是由I/O系统改变离散量输入和输入寄存器的数值,而上位机程序不能改变的数据类型,在数据读写上表现为只读,而内部比特或者物理线圈和内部寄存器或物理输出寄存器(保持寄存器)则是上位机应用程序可以改变的数据类型,在数据读写上表现为可读可写。

    错误代码表

    代码 名称 含义
    01 非法功能 对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作,可能是因为功能码仅适用于新设备而被选单元中不可实现同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:它是未配置的,且要求返回寄存器值。
    02 非法数据地址 对于服务器(或从站)来说,询问中接收的数据地址是不可允许的地址,特别是参考号和传输长度的组合是无效的。对于带有100个寄存器的控制器来说,偏移量96和长度4的请求会成功,而偏移量96和长度5的请求将产生异常码02。
    03 非法数据值 对于服务器(或从站)来说,询问中包括的值是不可允许的值。该值指示了组合请求剩余结构中的故障。例如:隐含长度是不正确的。modbus协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。
    04 从站设备故障 当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。
    05 确认 与编程命令一起使用,服务器(或从站)已经接受请求,并且正在处理这个请求,但是需要长持续时间进行这些操作,返回这个响应防止在客户机(或主站)中发生超时错误,客户机(或主机)可以继续发送轮询程序完成报文来确认是否完成处理。
    07 从属设备忙 与编程命令一起使用,服务器(或从站)正在处理长持续时间的程序命令,当服务器(或从站)空闲时,客户机(或主站)应该稍后重新传输报文。
    08 存储奇偶性差错 与功能码20和21以及参考类型6一起使用,指示扩展文件区不能通过一致性校验。服务器(或从站)设备读取记录文件,但在存储器中发现一个奇偶校验错误。客户机(或主机)可重新发送请求,但可以在服务器(或从站)设备上要求服务。
    0A 不可用网关路径 与网关一起使用,指示网关不能为处理请求分配输入端口值输出端口的内部通信路径,通常意味着网关是错误配置的或过载的。
    0B 网关目标设备响应失败 与网关一起使用,指示没有从目标设备中获得响应,通常意味着设备未在网络中。

    7:如何读取float型数据

    通过上面的测试可以看到寄存器读到的是short型数据,float占两个寄存器,需要两个字节存储,p1、p2对应两个寄存器的值。

    https://www.cnblogs.com/derekhan/p/10041679.html

    public static float GetFloat(ushort P1, ushort P2)
            {
                int intSign, intSignRest, intExponent, intExponentRest;
                float faResult, faDigit;
                intSign = P1 / 32768;
                intSignRest = P1 % 32768;
                intExponent = intSignRest / 128;
                intExponentRest = intSignRest % 128;
                faDigit = (float)(intExponentRest * 65536 + P2) / 8388608;
                faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1);
                return faResult;
            }

    本文完!2019年4月12日15:08:25

    展开全文
  • ModBus/TCP协议分析

    万次阅读 2017-09-08 16:44:42
    ModBus/TCP协议分析 一、术语 1 word = 2 byte; 1 byte = 8 bit. 校验码:校验码是由前面的数据通过某种算法得出的,用以检验该组数据的正确性。代码作为数据在向计算机或其它设备进行输入时,容易产生输入错误,...

    ModBus/TCP协议分析

    一、术语

    1 word = 2 byte;

    1 byte = 8 bit.

    校验码:校验码是由前面的数据通过某种算法得出的,用以检验该组数据的正确性。代码作为数据在向计算机或其它设备进行输入时,容易产生输入错误,为了减少这种输入错误,编码专家发明了各种校验检错方法,并依据这些方法设置了校验码。

    常用的校验有:累加和校验SUM、字节异或校验XOR、纵向冗余校验LRC、循环冗余校验CRC……

    离散量输入:主要用来读取单个位的数据,如IO的状态;
    线圈:开关输出信号,主要用来写入单个位的数据,与离散量构成组成对位的操作;
    输入寄存器:主要用来读取16位,也就是两个字节的数据;
    保持寄存器:主要用来写入16位的数据。、

    PLC:可编程逻辑控制器,是一种采用一类可编程的存储器,用于其内部存储程序,执行逻辑运算、顺序控制、定时、计数与算术操作等面向用户的指令,并通过数字或模拟式输入/输出控制各种类型的机械或生产过程。

    二、协议简介

    Modbus通信协议,是Modicon PLC所制定的资料交换通信接口标准,于1979年首先制定串行通信标准(含Modbus异步及Modbus Plus同步),于1997年制定网络通信标准(Modbus/TCP)。是属于OSI所定义的通信层的第七层应用层(Application Layer)。是为Client/Server或称为 Master/Slave型式的通信协议。由于Modbus的通信协议简单容易设计,结果广被许多控制设备或外围信号设备所采用,因此无形中成为自控业界的标准。Modbus异步的硬件架构简单,被使用的比率最高。Modbus Plus同步的协议可以提供高速的通信速度,适合主控制设备间大量资料交换。Modbus/TCP则是因应Ethernet网络的架构,近年来被大量使用的通信协议,也因为其速度及资料传送量远比Modbus Plus更快更大,所以已渐渐取代其功能。

    Modbus通信协议基本上是遵循MasterandSlave的通信步骤,有一方扮演Master角色采取主动询问方式,送出QueryMessage给Slave方,然后由Slave方依据接到的QueryMessage内容准备ResponseMessage回传给Master。即使目前硬件通信已经可以达到双方互相主动通信的能力,但是于Modbus通信协议的规定,必须一方为Master,另一方为Slave不能互换角色。一般使用上,监控系统(HMI)都为Master,PLC、电表、仪表等都为Slave,HMI系统一直PollingSlave的各种relayandregister最新数值,然后做显示及各种逻辑计算及控制调整等处理。

     

     

    报文说明

    1. Modbus/TCP报文格式


    起始字符组:于前面再多加6个字符,以定义一些TCP/IP的需要 系数。说明如下: 

    Byte0:本次通信Message的编号以2 bytes整数(Byte 0、1)表示,此 byte为上字符,一般是由Master编号之,以区分每次Message。如果是Slave则将Master传来的Query Message照转至Response Message。 

    Byte1:本次通信Message的编号下字符。

    Byte2:通信协议识别号码以2 bytes整数(Byte 2、3)表示,此 byte 为上字符,于此处为零。 

    Byte3:通信协议识别号码下字符,于此处为零。 

    Byte4:Message长度以2 bytes整数(Byte 4、5)表示,此 byte 为上字符(由Device Address至Data为止),因为长度不能超过256位,所以此位永远为零。 

    Byte5:Message长度下字符(由Device Address至Data为止)。 

    由Device Address至Data等资料,都是将 8 bits原始值转换为两码的十六进制ASCII码,所以其实际传送的字符数约为RTU格式的两倍。Data:数个字符是表示每个Function Code有不同数目的详细资料规定。

    Modbus规定IP Port No. 为 502。

    2.功能码

    功能码作用

       Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网线路上的Modbus协议映射能够在应用数据单元(ADU)上引入一些附加域。

    启动Modbus事务处理的客户机创建Modbus应用数据单元。功能码向服务器指示将执行哪种操作。

    Modbus协议建立了客户机启动的请求格式。

    用一个字节编码Modbus数据单元的功能码域。有效范围是十制制1-255(128-255为异常响应保留)。当从客户机向服务器发送报文时,功能码域通过服务器执行哪种操作。

    从客户机向服务器发送的报文数据域包括附加信息,服务器使用这个信息执行功能码定义的操作。这个域还包括离散项目和寄存器地址、处理项目的数量以及域中的实际数据字节数。

    在某种请求中,数据域可以是不存在的,在此情况下服务器不需要任何附加信息。功能码仅说明操作。

    功能码的类型

        功能码主要分为有效功能码、异常功能码和错误功能码。

    如果在一个正确接收Modbus ADU中,不出现与请求Modbus功能有关的差错,那么服务器至客户机的响应数据会包含请求中的正常功能码。如果出现与请求Modbus功能有关的差错,那么响应数据会包含一个异常码和错误码。

    例如,客户机能够读一组离散量输出或输入的开/关状态,或者用户能够读/写一组寄存器数据内容。当服务器对客户机响应时,它使用功能码域来指示正常(无差错)响应或出现某种差错(称为异常响应)。对于一个正常响应来说,服务器仅对原始功能码响应,如下图:

    对于异常响应,服务器返回一个与客户机等同的码,设置该原始功能码的最高有效位为逻辑1,并加该异常码后增加错误码,以通知客户机异常原因。如下图:

    有效功能码

    有效功能码有二十几种,但是一般使用上都以1、2、3、4、5、6、15、16等八种最为常用,以及另外特殊使用的20、21两种,此为General Reference Register,绝大部份的Modbus设备并不会提供此Register。于PLC上主要的控制数据有下列四种型式。此八种功能码就是处理这些控制资料,详细说明如下各点:

     

    控制数据四种型式: 

    DI:DigitalInput(数字输入,离散输入),一个地址一个数据位,用户只能读取它的状态,不能修改。以一个 bit表示 On/Off,用来记录控制信号的状态输入,例如:开关,接触点,马达运转,超限switch…等等。于PLC上被称为Input relay、input coil等。 

     

    DO:DigitalOutput(数字输出,线圈输出),一个地址一个数据位,用户可以置位、复位,可以回读状态。以一个 bit表示 On/Off,用来输出控制信号,以激活或停止马达,警铃,灯光…等等。于PLC上被称为Output relay、Output coil等。 

    AI:Analog Input(模拟输入,输入寄存器),一个地址16位数据,用户只能读,不能修改,,以16 bits integer表示一个数值,用来记录控制信号的数值输入,例如:温度、流量、料量、速度、转速、文件板开度、液位、重量…等等。于PLC上被称为Input register。

     

    AO:AnalogOutput(模拟输出,保持寄存器),一个地址16位数据,用户可以写,也可以回读,以16 bits integer表示一个数值,用来输出控制信号的数值,例如:温度、流量、速度、转速、文件板开度、饲料量…等等设定值。于PLC上被称为Output register、Holding register。

     

      有效功能码说明(十进制)

    01

    读取线圈状态

    02

    读取输入状态

    03

    读取保持寄存器

    04

    读取输入寄存器

    05

    强置单线圈

    06

    预置单寄存器

    07

    读取异常状态

    08

    回送诊断校验

    09

    编程(只用于484)

    10

    控询

    11

    读取事件计数

    12

    读取通信事件记录

    13

    编程(184/384/484/584等)

    14

    探寻

    15

    强置多线圈

    16

    预置多线圈

    17

    报告多寄存器

    18

    可使主机模拟编程功能

    19

    重置通信链路

    20

    读取通用参数

    21

    写入通用参数

    22

    屏蔽写寄存器

    23

    读/写多个寄存器

    43

    读设备别识码

    22-42,44-64

    保留作为扩展功能

    65-72

    保留以备用功能所用

    73-119

    非法功能

    120-127

    保留,留作内部作用

    128-255

    保留,用于异常应答

    注:功能码的具体描述可参考如下地址第11页:

    http://wenku.baidu.com/link?url=cm-3qRyq2LAUZV3hb7fHucYD2NgacRy4bC-eJI4z4NBY03SZeGQqsISLACm_hwsbRKo7ChWsrAO5sXDXJyMJhnpyLZvVSXUOMt3iG7zgsGC

     

            

    异常码说明

    异常码(ExceptionCode)是用来表示,当有任何通信资料异常时,由Slave不回传正常资料,而回传错误码号(Error Code)以提供Master做异常处理。其Message格式以下表为例说明:

    由以上可知处理ExceptionCode,将原有的Function Code的最左边 Bit 设定为 1,然后将适合的Error code代入。比如:上图功能码为06(二进制为00000110),因为是地址超出范围,属于异常情况,所以将最左边bit设为1,即:10000110,转成10进制为86。

    Modbus错误码(10进制)

    01

    非法功能。对于服务器(或从站)来说,询问中接收到的功能码是不可允许的操作,可能是因为功能码仅适用于新设备而被选单元中不可实现同时,还指出服务器(或从站)在错误状态中处理这种请求,例如:它是未配置的,且要求返回寄存器值。

    02

    非法数据地址。对于服务器(或从站)来说,询问中接收的数据地址是不可允许的地址,特别是参考号和传输长度的组合是无效的。对于带有100个寄存器的控制器来说,偏移量96和长度4的请求会成功,而偏移量96和长度5的请求将产生异常码02。

    03

    非法数据值。对于服务器(或从站)来说,询问中包括的值是不可允许的值。该值指示了组合请求剩余结构中的故障。例如:隐含长度是不正确的。modbus协议不知道任何特殊寄存器的任何特殊值的重要意义,寄存器中被提交存储的数据项有一个应用程序期望之外的值。

    04

    从站设备故障。当服务器(或从站)正在设法执行请求的操作时,产生不可重新获得的差错。

    05

    确认。与编程命令一起使用,服务器(或从站)已经接受请求,并且正在处理这个请求,但是需要长持续时间进行这些操作,返回这个响应防止在客户机(或主站)中发生超时错误,客户机(或主机)可以继续发送轮询程序完成报文来确认是否完成处理。

    06

    从属设备忙。与编程命令一起使用。服务器(或从站)正在处理长持续时间的程序命令。张服务器(或从站)空闲时,用户(或主站)应该稍后重新传输报文。

    08

    存储奇偶差错。与功能码20和21以及参考类型6一起使用,指示扩展文件区不能通过一致性校验。服务器(或从站)设法读取记录文件,但是在存储器中发现一个奇偶校验错误。客户机(或主方)可以重新发送请求,但可以在服务器(或从站)设备上要求服务。

    10

    不可用网关路径。与网关一起使用,指示网关不能为处理请求分配输入端口至输出端口的内部通信路径。通常意味着网关是错误配置的或过载的。

    11

    网关目标设备响应失败。与网关一起使用,指示没有从目标设备中获得响应。通常意味着设备未在网络中。

     

    三、报文分析

    报文分两种,查询报文和响应报文

    1.正常报文形势

    此报文为仿真软件抓取

    1.1查询报文

    00000000 d05f[k1] 0000[k2] 0006[k3] 01[k4] 03[k5]  00 00[k6] 0064[k7] 

    此条报文的意思是:从寄存器起始地址0000开始,读取100个寄存器(每个寄存器2个字节)。

    [k1]起始字符组,用于区分查询与其响应报文是否是一组

     [k2]起始字符组,协议类型报文,0000代表是modbus/TCP类型

     [k3]起始字符组,包长,代表后面有6个字节的内容

     [k4]设备地址

     [k5]功能码03,表示读取保持寄存器

     [k6]寄存器起始位置

     [k7]要读取100个寄存器


    1.2查询响应报文

       00000000  d0 5f[k8] 0000[k9] 00cb[k10] 01[k11] 03[k12]  c8[k13] 00 00 00 00 00 00 00 ._...... ........

        00000010 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000020 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000030 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ........ ........

        00000050 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000060 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000070 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000080 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        00000090 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        000000A0 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        000000B0 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

        000000C0 00 00 00 00 00 00 00 00  00 00 0000 00 00 00 00 ........ ........

    000000D0  00                                               .[k14] 

    此条报文的意思是:读取100个寄存器内容,反馈给查询方

    [k8]起始字符组,与查询报文对应

     [k9]起始字符组,协议类型报文,0000代表是modbus/TCP类型

     [k10]起始字符组,包长,代表后面有203个字节的内容

     [k11]设备地址

     [k12]功能码03,表示读取保持寄存器

     [k13]代表后面有200个字节

     [k14]指读取的100个寄存器内容


    2.异常报文形势

    以下报文是shodan流量中的

    2.1异常报文类型一

    2.1.1查询报文

    00000000 0000[k15] 00 00[k16] 00 02[k17] 07[k18] 11[k19] 

    [k15]起始字符组,用于区分查询与其响应报文是否是一组

     [k16]起始字符组,协议类型报文,0000代表是modbus/TCP类型

     [k17]起始字符组,包长,代表后面有2个字节的内容

     [k18]设备地址

     [k19]功能码17,表示报告多寄存器


    2.1.2查询响应报文

    00000000 0000[k20] 00 00[k21] 00 03[k22] 07[k23] 91[k24]  0b[k25] 

    [k20]起始字符组,与查询报文对应

     [k21]起始字符组,协议类型报文,0000代表是modbus/TCP类型

     [k22]起始字符组,包长,代表后面有3个字节的内容

     [k23]设备地址

     [k24]异常码,原功能码17(十进制),二进制为00010001,在最高位加1后为10010001,转成16进制为91

     [k25]错误码11,表示网关目标设备响应失败。


    2.2异常报文类型一

    2.2.1查询报文

    00000000 00 00 00 00 00 05 07[k26] 2b[k27]  0e[k28] 01[k29] 00[k30] 

     [k26]设备地址

     [k27]功能码43,表示读取设备识别码

     [k28]基本设备识别码。所有此种对象都是必备的:厂商名称、产品代码和修订本号。

     [k29]正常设备识别码。提供附加的和可选择的识别码以及数据对象描述。按标准定义所有种类的对象,但这种对象的执行是可选的。

     [k30]扩展设备识别码。除正常数据对象外,提供附加的和可选择的识别码以及专用数据描述。所有这些数据都是与设备有关的。


    2.2.2查询响应报文

    00000000 0000[k31] 00 00[k32] 00 03[k33] 07[k34] ab[k35]  0b[k36] 

     [k31]起始字符组,与查询报文对应

     [k32]起始字符组,协议类型报文,0000代表是modbus/TCP类型

     [k33]起始字符组,包长,代表后面有3个字节的内容

     [k34]设备地址

     [k35]异常码171。16进制2B转二进制为00101011,最高位加1后为10101011,转16进制为ab

     [k36]错误码11,表示网关目标设备响应失败。


    四、参考资料

    主要是从网上查找资料

    1. Modbus通信通讯教程(详细)

    网址:http://wenku.baidu.com/view/7c737b5d915f804d2a16c122.html?re=view

    2. MODBUS通讯协议中文版

    网址:http://wenku.baidu.com/link?url=cm-3qRyq2LAUZV3hb7fHucYD2NgacRy4bC-eJI4z4NBY03SZeGQqsISLACm_hwsbRKo7ChWsrAO5sXDXJyMJhnpyLZvVSXUOMt3iG7zgsGC

    3.报文主要来源

    网址:http://www.pcapr.net/browse?q=Modbus




    展开全文
  • 文章目录1 ModbusTcp1.1 MBAP报文1.2 MODBUS请求的生成1.3 MODBUS响应的生成2 Modbus介绍2.1 MODBUS数据模型2.2 公共功能码定义3 功能码描述3.1 01(0x01)功能码---读线圈3.2 02(0x02)功能码---读离散量输入3.3 03(0x...

    1 ModbusTcp

    简单的理解一下Modbus TCP/IP协议的内容,就是去掉了modbus协议本身的CRC校验,增加了MBAP 报文头。TCP/IP上的MODBUS的请求/响应如下图所示:

    TCPIPmodbus请求和响应

    1.1 MBAP报文

    首先来看一下,MBAP 报文头都包括了哪些信息和内容

    长度 描述 客户机 服务器
    事务元标识符 2个字节 MODBUS请求/响应事务处理的识别码 客户机启动 服务器从接收的请求中重新复制
    协议标识符 2个字节 0=MODBUS协议 客户机启动 服务器从接收的请求中重新复制
    长度 2个字节 以下字节的数量 客户机启动(请求) 服务器(响应)启动
    单元标识符 1个字节 串行链路或其它总线上连接的远程从站的识别码 客户机启动 服务器从接收的请求中重新复制

    事务元标识符(2个字节):用于事务处理配对。在响应中,MODBUS服务器复制请求的事务处理标识符。这里在以太网传输中存在一个问题,就是先发后至,我们可以利用这个事务处理标识符做一个TCP序列号,来防止这种情况所造成的数据收发错乱(这里我们先不讨论这种情况,这个事务处理标识符我们统一使用0x00,0x01)
    协议标识符(2个字节):modbus协议标识符为0x00,0x00
    长度(2个字节):长度域是下一个域的字节数,包括单元标识符和数据域。
    单元标识符(1个字节):该设备的编号。(可以使用PLC的IP地址标识)。

    在MODBUS MODBUS+串行链路子网中对设备进行寻址时,这个域是标识设备地址。在这种情况下,“Unit Identifier”携带一个远端设备的MODBUS从站地址:

    • 如果MODBUS服务器连接到MODBUS+或MODBUS串行链路子网,并通过一个桥或网关配置地址这个服务器,MODBUS单元标识符对识别连接到网桥或网关后的子网的从站设备是必需的。目的IP地址识别了网桥本身的地址,而网桥则使用MODBUS单元标识符将请求转交给正确的从站设备。
    • 分配串行链路上MODBUS从站设备地址为1~247(10进制),地址0作为广播地址。
      对 TCP/IP 来说,利用IP地址寻址MODBUS服务器;因此,MODBUS单元标识符是无用的。必需使用值0xFF。
    • 当对直接连接到TCP/IP网络上的MODBUS服务器寻址时,建议不要在“单元标识符”域使用有效的MODBUS从站地址。在一个自动系统中重新分配IP地址的情况下,并且如果以前分配给MODBUS服务器的IP地址又被指配给网关,使用一个有效的从站地址可能会由于网关的路由不畅而引起麻烦。使用无效的从站地址,网关仅是简单地废弃MODBUD PDU,而不会有任何问题。建议:在采用0xFF作为“单元标识符”的无效值。

      注:0也可以用作与MODBUS/TCP设备直接通信。

    1.2 MODBUS请求的生成

    在收到来自用户应用的需求后,客户端必须生成一个MODBUS请求,并发送到TCP管理。下表显示MODBUS请求ADU编码:

    类型 描述 字节大小 实例
    MBAP报文头 事务处理标识符Hi 1 0x15
    事务处理标识符Lo 1 0x01
    协议标识符 2 0x0000
    长度 2 0x0006
    单元标识符 1 0xFF
    MODBUS请求 功能码 1 0x03
    起始地址 2 0x0005
    寄存器数量 2 0x0001

    1.3 MODBUS响应的生成

    一旦处理请求,MODBUS 服务器必须使用适当的MODBUS服务器事务处理生成一个响应,并且必须将响应发送到TCP管理组件。

    根据处理结果,可以生成两类响应:

    • 肯定的MODBUS响应:
      • 响应功能码 = 请求功能码
    • MODBUS异常响应:
      • 目的是为客户机提供与处理过程检测到的错误相关的信息
      • 响应功能码 = 请求功能码+0x80
      • 提供异常码来表明出错的原因。
    异常码 MODBUS名称 备注
    01 非法的功能码 服务器不了解功能码
    02 非法的数据地址 与请求有关
    03 非法的数据值 与请求有关
    04 服务器故障 在执行过程中,服务器故障
    05 确认 服务器接受服务调用,但是需要相对长的时间完成服务。因此,服务器仅返回一个服务调用接收的确认。
    06 服务器繁忙 服务器不能接受MODBUS请求PDU。客户应用由责任决定是否和何时重发请求。
    0A 网关故障 网关路经是无效的。
    0B 网关故障 目标设备没有响应。网关生成这个异常信息。

    通过上面的两步,一个Modbus TCP的客户端连接已经建立起来了,下面我们就来分析Modbus TCP协议的具体内容与实现方式了。

    2 Modbus介绍

    2.1 MODBUS数据模型

    基本表格 对象类型 访问类型 内容
    离散量输入 单个比特 只读 I/O系统提供这种类型数据
    线圈 单个比特 读写 通过应用程序改变这种类型数据
    输入寄存器 16比特字 只读 I/O系统提供这种类型数据
    保持寄存器 16比特字 读写 通过应用程序改变这种类型数据

    2.2 公共功能码定义

    功能码
    子码 十六进制
    比特访问
    物理离散量输入 读输入离散量 02 02
    内部比特或物理线圈 读线圈 01 01
    写单个线圈 05 05
    写多个线圈 15 0F
    16比特访问 输入寄存器 读输入寄存器 04 04
    内部存储器或物理输出存储器 读多个寄存器 03 03
    写单个寄存器 06 06
    写多个寄存器 16 10
    读/写多个寄存器 23 17
    屏蔽写寄存器 22 16
    文件记录访问 读文件记录 20 6 14
    写文件记录 21 6 15

    3 功能码描述

    我们直接用实例来说明。

    3.1 01(0x01)功能码—读线圈

    在一个远程设备中,使用该功能码读取线圈的1 至2000 连续状态。请求PDU 详细说明了起始地址,即指定的第一个线圈地址和线圈编号。从零开始寻址线圈。因此寻址线圈1-16 为0-15。

    根据数据域的每个比特将响应报文中的线圈分成为一个线圈。指示状态为1= ON 和0= OFF。

    第一个数据字节的LSB(最低有效位)包括在询问中寻址的输出。其它线圈依次类推,一直到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。

    如果返回的输出数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。

    请求与响应格式

    请求PDU

    功能码 1个字节 0x01
    起始地址 2个字节 0x0000至0xFFFF
    线圈数量 2个字节 1至2000(0x7D0)

    响应PDU

    功能码 1个字节 0x01
    字节数 1个字节 N*
    线圈状态 N个字节 n=N或N+1

    注:*N=输出数量/8,如果余数不等于0,那么N=N+1

    错误

    差错码 1个字节 功能码+0x80
    异常码 1个字节 01或02或03或04

    这是一个请求读离散量输出20-38 的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 01 功能 01
    起始地址Hi 00 字节数 03
    起始地址Lo 13 输出状态27-20 CD
    输出数量Hi 00 输出状态35-28 6B
    输出数量Lo 13 输出状态38-36 05

    将输出27-20 的状态表示为十六进制字节值CD,或二进制1100 1101。输出27 是这个字节的MSB,输出20 是LSB。
    通常,将一个字节内的比特表示为MSB 位于左侧,LSB 位于右侧。第一字节的输出从左至右为27至20。下一个字节的输出从左到右为35至28。当串行发射比特时,从LSB向MSB传输:20 . . .27、28 . . . 35 等等。
    在最后的数据字节中,将输出状态38-36表示为十六进制字节值05,或二进制0000 0101。输出38 是左侧第六个比特位置,输出36 是这个字节的LSB。用零填充五个剩余高位比特。

    注:用零填充五个剩余比特(一直到高位端)。

    3.2 02(0x02)功能码—读离散量输入

    在一个远程设备中,使用该功能码读取离散量输入的1 至2000 连续状态。请求PDU 详细说明了起始地址,即指定的第一个输入地址和输入编号。从零开始寻址输入。因此寻址输入1-16 为0-15。

    根据数据域的每个比特将响应报文中的离散量输入分成为一个输入。指示状态为1= ON 和0=OFF。第一个数据字节的LSB(最低有效位)包括在询问中寻址的输入。其它输入依次类推,一直
    到这个字节的高位端为止,并在后续字节中从低位到高位的顺序。

    如果返回的输入数量不是八的倍数,将用零填充最后数据字节中的剩余比特(一直到字节的高位端)。字节数量域说明了数据的完整字节数。

    请求PDU

    功能码 1个字节 0x02
    起始地址 2个字节 0x0000至0xFFFF
    线圈数量 2个字节 1至2000(0x7D0)

    响应PDU

    功能码 1个字节 0x82
    字节数 1个字节 N*
    线圈状态 N*x1个字节

    注:*N=输出数量/8,如果余数不等于0,那么N=N+1

    错误

    差错码 1个字节 功能码+0x82
    异常码 1个字节 01或02或03或04

    这是一个请求读离散量输入197-218 的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 02 功能 02
    起始地址Hi 00 字节数 03
    起始地址Lo C4 输出状态204-197 AC
    输出数量Hi 00 输出状态212-205 DB
    输出数量Lo 16 输出状态218-213 35

    将离散量输入状态204-197表示为十六进制字节值AC,或二进制1010 1100。输入204是这个字节的MSB,输入197 是这个字节的LSB。

    将离散量输入状态218-213表示为十六进制字节值35,或二进制0011 0101。输入218位于左侧第3 比特,输入213 是LSB。

    注:用零填充2 个剩余比特(一直到高位端)。

    3.3 03(0x03)功能码—读保持寄存器

    在一个远程设备中,使用该功能码读取保持寄存器连续块的内容。请求PDU说明了起始寄存器地址和寄存器数量。从零开始寻址寄存器。因此,寻址寄存器1-16 为0-15
    将响应报文中的寄存器数据分成每个寄存器有两字节,在每个字节中直接地调整二进制内容。
    对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。

    请求

    功能码 1个字节 0x03
    起始地址 2个字节 0x0000至0xFFFF
    寄存器数量 2个字节 1至125(0x7D)

    响应

    功能码 1个字节 0x03
    字节数 1个字节 2xN*
    寄存器值 N*x2个字节

    注:*N=输出数量/8,如果余数不等于0,那么N=N+1

    错误

    差错码 1个字节 0x83
    异常码 1个字节 01或02或03或04

    这是一个请求读保持寄存器108-110 的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 03 功能 03
    高起始地址 00 字节数 06
    低起始地址 6B 寄存器值Hi(108) 02
    高寄存器编号 00 寄存器值Lo(108) 2B
    低寄存器编号 03 寄存器值Hi(109) 00
    寄存器值Lo(109) 00
    寄存器值Hi(110) 00
    寄存器值Lo(110) 64

    将寄存器108的内容表示为两个十六进制字节值02 2B,或十进制555。将寄存器109-110的内容分别表示为十六进制00 00 和00 64,或十进制0 和100。

    3.4 04(0x04)功能码—读输入寄存器

    在一个远程设备中,使用该功能码读取1 至大约125 的连续输入寄存器。请求PDU 说明了起始地址和寄存器数量。从零开始寻址寄存器。因此,寻址输入寄存器1-16 为0-15
    将响应报文中的寄存器数据分成每个寄存器为两字节,在每个字节中直接地调整二进制内容。
    对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位比特。

    请求

    功能码 1个字节 0x04
    起始地址 2个字节 0x0000至0xFFFF
    寄存器数量 2个字节 0x0000至0x007D

    响应

    功能码 1个字节 0x04
    字节数 1个字节 2xN*
    寄存器值 N*x2个字节

    注:*N=输入寄存器的数量

    错误

    差错码 1个字节 0x84
    异常码 1个字节 01或02或03或04

    这是一个请求读输入寄存器9的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 04 功能 04
    起始地址Hi 00 字节数 02
    起始地址Lo 08 输入寄存器值Hi(9) 00
    输入寄存器数量Hi 00 输入寄存器值Lo(9) 0A
    输入寄存器Lo 01

    将输入寄存器9 的内容表示为两个十六进制字节值00 0A,或十进制10。

    3.5 05(0x05)功能码—写单个线圈

    在一个远程设备上,使用该功能码写单个输出为ON 或OFF。
    请求数据域中的常量说明请求的ON/OFF状态。十六进制值FF 00请求输出为ON。十六进制值00 00 请求输出为OFF。其它所有值均是非法的,并且对输出不起作用。
    请求PDU说明了强制的线圈地址。从零开始寻址线圈。因此,寻址线圈1 为0。线圈值域的常量说明请求的ON/OFF 状态。十六进制值0XFF00请求线圈为ON。十六进制值0X0000请求线圈为
    OFF。其它所有值均为非法的,并且对线圈不起作用。
    正常响应是请求的应答,在写入线圈状态之后返回这个正常响应。

    请求

    功能码 1个字节 0x05
    输出地址 2个字节 0x0000至0xFFFF
    输出值 2个字节 0x0000至0xFF00?

    响应

    功能码 1个字节 0x05
    输出地址 2个字节 0x0000至0xFFFF
    输出值 2个字节 0x0000至0xFF00

    错误

    差错码 1个字节 0x85
    异常码 1个字节 01或02或03或04

    这是一个请求写线圈173为ON的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 05 功能 05
    输出地址Hi 00 输出地址Hi 00
    输出地址Lo AC 输出地址Lo AC
    输出值Lo 00 输出值Lo 00

    3.6 06(0x06)功能码—写单个寄存器

    在一个远程设备中,使用该功能码写单个保持寄存器。
    请求PDU 说明了被写入寄存器的地址。从零开始寻址寄存器。因此,寻址寄存器1 为0。
    正常响应是请求的应答,在写入寄存器内容之后返回这个正常响应。

    请求

    功能码 1个字节 0x06
    寄存器地址 2个字节 0x0000至0xFFFF
    寄存器值 2个字节 0x0000至0xFFFF

    响应

    功能码 1个字节 0x06
    寄存器地址 2个字节 0x0000至0xFFFF
    寄存器值 N*x2个字节 0x0000至0xFFFF

    错误

    差错码 1个字节 0x86
    异常码 1个字节 01或02或03或04

    这是一个请求将十六进制 00 03 写入寄存器2的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 06 功能 06
    寄存器地址Hi 00 输出地址Hi 00
    寄存器地址Lo 01 输出地址Lo 01
    寄存器值Hi 00 输出值Hi 00
    寄存器值Lo 03 输出值Hi 03

    3.7 15(0x0F)功能码—写多个线圈

    在一个远程设备中,使用该功能码强制线圈序列中的每个线圈为ON 或OFF。请求PDU说明了强制的线圈参考。从零开始寻址线圈。因此,寻址线圈1 为0。
    请求数据域的内容说明了被请求的ON/OFF 状态。域比特位置中的逻辑“1”请求相应输出为ON。域比特位置中的逻辑“0”请求相应输出为OFF。
    正常响应返回功能码、起始地址和强制的线圈数量。

    请求PDU

    功能码 1个字节 0x0F
    起始地址 2个字节 0x0000至0xFFFF
    输出数量 2个字节 0x0001至0x07B0
    字节数 1个字节 N*
    输出值 N*X1个字节

    注:*N=输出数量/8,如果余数不等于0,那么N=N+1

    响应PDU

    功能码 1个字节 0x0F
    起始地址 2个字节 0x0000至0xFFFF
    输出数量 2个字节 0x0001至0x07B0

    错误

    差错码 1个字节 0x8F
    异常码 1个字节 01或02或03或04

    这是一个请求从线圈20开始写入10个线圈的实例:

    请求的数据内容为两个字节:十六进制CD 01 (二进制1100 1101 0000 0001)。使用下列方法,二进制比特对应输出。

    比特 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
    输出 27 26 25 24 23 22 21 20 29 28

    传输的第一字节(十六进制CD)寻址为输出27-20,在这种设置中,最低有效比特寻址为最低输出(20)。
    传输的下一字节(十六进制01)寻址为输出29-28,在这种设置中,最低有效比特寻址为最低输出(28)。
    应该用零填充最后数据字节中的未使用比特。

    请求 响应
    域名 十六进制 域名 十六进制
    功能 0F 功能 0F
    起始地址Hi 00 起始地址Hi 00
    起始地址Lo 13 起始地址Lo 13
    输出数量Hi 00 输出数量Hi 00
    输出数量Lo 0A 输出数量Lo 0A
    字节数 02
    输出值Hi CD
    输出数量Lo 01

    3.8 16(0x10)功能码—写多个寄存器

    在一个远程设备中,使用该功能码写连续寄存器块(1 至约120 个寄存器)。
    在请求数据域中说明了请求写入的值。每个寄存器将数据分成两字节。
    正常响应返回功能码、起始地址和被写入寄存器的数量。

    请求

    功能码 1个字节 0x10
    起始地址 2个字节 0x0000至0xFFFF
    寄存器数量 2个字节 0x0001至0x0078
    字节数 1个字节 2xN*
    寄存器值 N*x2个字节

    注:*N=寄存器的数量

    响应

    功能码 1个字节 0x04
    起始地址 2个字节 0x0000至0xFFFF
    寄存器数量 2个字节 1至123(0x7B)

    错误

    差错码 1个字节 0x90
    异常码 1个字节 01或02或03或04

    这是一个请求将十六进制00 0A 和01 02 写入以2 开始的两个寄存器的实例:

    请求 响应
    域名 十六进制 域名 十六进制
    功能 10 功能 10
    起始地址Hi 00 起始地址Hi 00
    起始地址Lo 01 起始地址Lo 01
    寄存器数量Hi 00 寄存器数量Hi 00
    寄存器数量Lo 02 寄存器数量Lo 02
    字节数 04
    寄存器值Hi 00
    寄存器值Lo 0A
    寄存器值Hi 01
    寄存器值Lo 02
    展开全文
  • Modbus RTU/TCP协议解析

    千次阅读 2018-08-08 16:18:45
    Modbus通信协议由Modicon公司(现已经为施耐德公司并购,成为其旗下的子品牌)于1979年发明的,是全球最早用于工业现场的总线规约。由于其免费公开发行,使用该协议的厂家无需缴纳任何费用,Modbus通信协议采用的是...

    Modbus通信协议由Modicon公司(现已经为施耐德公司并购,成为其旗下的子品牌)于1979年发明的,是全球最早用于工业现场的总线规约。由于其免费公开发行,使用该协议的厂家无需缴纳任何费用,Modbus通信协议采用的是主从通信模式(即Master/Slave通信模式),其在分散控制方面应用极其广泛,从而使得Modbus协议在全球得到了广泛的应用。

    Modbus通信协议具有多个变种,其具有支持串口(主要是RS-485总线),以太网多个版本,其中最著名的是Modbus RTU,Modbus ASCII和Modbus TCP三种。其中Modbus RTU与Modbus ASCII均为支持RS-485总线的通信协议,其中Modbus RTU由于其采用二进制表现形式以及紧凑数据结构,通信效率较高,应用比较广泛。而Modbus ASCII由于采用ASCII码传输,并且利用特殊字符作为其字节的开始与结束标识,其传输效率要远远低于Modbus RTU协议,一般只有在通信数据量较小的情况下才考虑使用Modbus ASCII通信协议,在工业现场一般都是采用Modbus RTU协议,一般而言,大家说的基于串口通信的Modbus通信协议都是指Modbus RTU通信协议。

    在此我们着重讨论Modbus RTU协议,而Modbus TCP协议则是在RTU协议上加一个MBAP报文头,由于TCP是基于可靠连接的服务,RTU协议中的CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码,用一句比较通俗的话说就是:Modbus TCP协议就是Modbus RTU协议在前面加上五个0以及一个6,然后去掉两个CRC校验码字节就OK.虽然这句话说得不是特别准确,但是也基本上把RTU与TCP之间的区别说得比较清楚了。

    RTU协议中的指令由地址码(一个字节),功能码(一个字节),起始地址(两个字节),数据(N个字节),校验码(两个字节)五个部分组成,其中数据又由数据长度(两个字节,表示的是寄存器个数,假定内容为M)和数据正文(M乘以2个字节)组成,而RTU协议是采用3.5个字节的空闲时间作为指令的起始和结束,一般而言,只有当从机返回数据或者主机写操作的时候,才会有数据正文,而其他时候比如主机读操作指令的时候,没有数据正文,只需要数据长度即可。(本章的讨论只涉及寄存器的读写,其他比如线圈的读写指令我们暂时不涉及)。在此我们通过两个指令(0x03H:读多个寄存器指令以及0x10H:写多个寄存器指令)来解释Modbus RTU协议。在此我们使用的RTU设备是深圳市国科伟业通信技术有限公司的ND-1084型485总线I/O模块

    ND-1084型485总线I/O模块采用RS-485总线通信,支持四路开关量输出以及八路开关量输入,我们只讲述读取开关量输出的状态以及通过写指令控制开关量的输出。其中储存开关量输出状态的四个寄存器分别:0x18E,0x18F,0x190,0x191。在此我们假设模块的地址为默认的0x01,当我们要去读取开关量输出对应的四个寄存器的状态的时候,我们下发的十六进制的指令为:“01 03 01 8E 00 04 25 DE”,其中“01”为模块的地址码,“03”为功能码,即表示读寄存器,“01 8E”为寄存器地址,即从该寄存器地址开始读取数据,“00 04”则表示读取4个寄存器,而“25 DE”则为前面“01 03 01 8E 00 04”的CRC校验码,该数值通过CRC16校验算法计算出来的,我们会在其他文章中阐述。该指令的完整解读就是,在地址码为“01”的模块中,从“01 8E”寄存器开始,读取4个寄存器的数据返回至主机。在此,我们可以看到,读取指令中并没有什么数据正文,因为它只是读取相应数量的寄存器,并不需要数据正文,而写操作指令则相反,我们会在后面讲到。

    模块返回的指令是:“01 03 08 00 01 00 01 00 01 00 01 28 D7”,返回的指令内容解读就是:“01”表示模块的地址码,“03”表示该指令是读操作返回的指令,“08”表示数据长度,在此表示的是8个字节数据正文(即4个寄存器,每个寄存器两个字节表示),“00 01 00 01 00 01 00 01”是数据正文,表示四个寄存器的状态,“28 D7”就是CRC16校验码。

    同样的当我们执行写操作的是,我们举例写第一个开关量输出,即寄存器“0x18E”,主机下发的指令为:“01 10 01 8e 00 01 02 00 00 A8 7E”,该指令的解读就是:“01”表示模块的地址,“10”表示该指令为写寄存器,“01 8E”表示从该寄存器地址开始执行写操作指令“00 01”表示写多少个寄存器,在此为写1个寄存器,“02”表示数据长度,表示数据长度为两个字节,“00 00”表示写入寄存器的数据,在此表示连通,“A8 7E”为CRC校验码。模块返回的指令和读取寄存器的返回的指令类似。

    前文所述,Modbus TCP协议是在RTU协议前面添加MBAP报文头,共七个字节长度,其分别的意义是:1.传输标志,两个字节长度,标志Modbus询问/应答的传输,一般默认是00 00。2.协议标志,两个字节长度,0表示是Modbus,1表示UNI-TE协议,一般默认也是00 00。3.后续字节计数,两个字节长度,其实际意义就是后面的字节长度,具体情况详见下文。4.单元标志,一个字节长度,一般默认为00,单元标志对应于Modbus RTU协议中的地址码,当RTU与TCP之间进行协议转换的时候,特别是Modbus网关转换协议的时候,在TCP协议中,该数据就是对应RTU协议中的地址码,具体情况详见下文。

    通过上面的描述我们差不多能够理解Modbus RTU协议,我们再说说Modbus TCP通信协议,前面就已经说过TCP协议就是在RTU协议的基础上去掉校验码以及加上五个0和一个6,当是读取相关寄存器的时候,该说法是没有错的,比如上文的“01 03 01 8E 00 04 25 DE”读取指令,用TCP协议来表述的话,指令是“00 00 00 00 00 06 00 03 01 8E 00 04”,由于TCP是基于TCP连接的,不存在所谓的地址码,所以06后面一般都是“00”(当其作为Modbus网关服务器挂接多个RTU设备的时候,数值从01-FF).即“00 03 01 8E 00 04”对应的是RTU中去掉校验码的指令,前面则是五个0以及一个6。其中6表示的是数据长度,即“00 03 01 8E 00 04”有6个字节长度。而当其为写操作指令的时候,其指令是“00 00 00 00 00 09 01 10 01 8e 00 01 02 00 00”,其中“00 09”表示后面有9个字节。

    Modbus RTU与Modbus TCP读指令对比

     

    MBAP报文头

    地址码

    功能码

    寄存器地址

    寄存器数量

    CRC校验

    Modbus RTU

    01

    03

    01 8E

    00 04

    25 DE

    Modbus TCP

    00 00 00 00 00 06 00

    03

    01 8E

    00 04

    指令的涵义:从地址码为01(TCP协议单元标志为00)的模块0x18E(01 8E)寄存器地址开始读(03)四个(00 04)寄存器。

    Modbus RTU与Modbus TCP写指令对比

     

    MBAP报文头

    地址码

    功能码

    寄存器地址

    寄存器数量

    数据长度

    正文

    CRC校验

    RTU

    01

    10

    01 8E

    00 01

    02

    00 00

    A8 7E

    TCP

    00 00 00 00 00 09 00

    10

    01 8E

    00 01

    02

    00 00

    指令的涵义:从地址码为01(TCP协议单元标志为00)的模块0x18E(01 8E)寄存器地址开始写(10)一个(00 01)寄存器,具体数据长度为2个字节(02),数据正文内容为00 00(00 00)。

    展开全文
  • freemodbus modbus TCP 学习笔记

    万次阅读 多人点赞 2016-10-01 22:00:30
    使用modbus有些时间了,期间使用过modbus RTU也使用过modbus TCP,通过博文和大家分享一些MODBUS TCP的东西。在嵌入式中实现TCP就需要借助一个以太网协议栈,在这里我选择最简单的uIP协议栈。uIP协议栈简单易用方便...
  • ModbusTCP协议学习笔记

    万次阅读 多人点赞 2018-11-20 12:00:10
    1996年施耐德公司推出基于以太网TCP/IP的modbus协议:modbusTCP。 Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。 标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口...
  • 废话少说,我们直接上干的,学习知识,第一个是收集和查阅资料,这个是必须的。
  • Modbus Tcp协议详解

    万次阅读 2017-08-02 17:36:01
    一、ModbusTcp简介 什么是ModbusTcp? /1、Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MODBUS寄存器地址来交换数据; /2、但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而...
  • Modbus Tcp

    千次阅读 2019-06-27 12:45:00
    Modbus数据帧 MBAP报文头 MBAP为报文头,长度为7字节 ...00 00表示ModbusTCP协议 长度 2字节 表示接下来的数据长度,单位为字节 单元表示符 1字节 串行链路或其它总线上连接的远程从站地址 帧结构PDU PD...
  • Modbus通信协议(tcp/ip)简述

    千次阅读 2019-01-07 11:43:48
    Modbus通信协议 Modbus协议是一种单主/多从的通信协议,其特点是在同一时间,总线上只能有一个主设备,但可以有一个或者多个(最多247个 ip地址1-247)从设备。每一个从设备一个ip地址 在请求的报文中请求的地址为0...
  • Modbus TCPModbus Rtu协议的区别

    万次阅读 2013-06-26 13:20:28
    Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MODBUS寄存器地址来交换数据;但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。现在市场上有很多...
  • 一、串口调试工具SSCOM 可以自动识别串口,hex格式发送和接收,字符格式发送和接收,可以自定义发送命令及自动发送、循环发送命令。
  • 西门子S7-1200Modbus TCP通讯

    万次阅读 2017-11-18 14:43:25
    接了个1200的项目,用到了Modbus-tcp,我modbus感觉还比较熟悉,因为串口通讯平时用的比较多,TCP的确实没用过,看网上的资料也是一头雾水,经半天时间才总结出modbus的数据结构,原来网口和串口其实都差不多,核心...
  • modbus tcp数据报文结构详解

    万次阅读 2014-04-20 11:48:51
    modbus tcp数据报文结构  一次modbus tcp读取保持寄存器的通信分析(省略了ip/tcp头):从左向右分析该数据报文:00 03为此次通信编号,一般每次通信之后将被要求加1以区别不同的通信数据报文;00 00表示协议...
  • Modbus Poll 设置Modbus TCP通信超时时间

    万次阅读 2015-12-30 21:06:31
    使用Modbus Poll可以轻松的用PC模拟Modbus主机,可以建立 Modbus RTU Modbus ASCII Modbus-TCP通信。 当使用Modbus TCP通信时,可以根据结点的多少设置一下超时时间,默认可能为:3000ms(3秒)有点长。  毕竟TCP...
  • 经过这几天的学习与调试,终于在STM32F103VCT6+W5500(SPI1)+Freemodbus 平台上,实现Modbus-TCP协议的功能。其实很简单,只要熟悉Modbus-RTU通讯,明白Modbus帧的结构等,Modbus-TCP只是在原来的帧结构上加个头,去...
  • ModbusTCP报文格式说明

    万次阅读 2018-07-04 17:39:04
    modbus tcp数据报文结构请求:00 00 00 00 00 06 09 03 00 00 00 01响应:00 00 00 00 00 05 09 03 02 12 34 一次modbus tcp读取保持寄存器的通信分析(省略了ip/tcp头):从左向右分析该数据报文:请求:00 00为...
  • 如题,C++编程:用socket TCP/IP 更改端口号 为502 可以实现Modbus TCP/IP 通讯吗? 1、我现在服务器端是用socket TCP/IP 创建的,客户端用的是Modsim32的从设备仿真工具。 2、基本配置 物理链路上是通的; 我要实现...
  • Modbus TCP指令格式说明

    万次阅读 2017-05-13 12:34:13
    这个是一条正确的发送指令指令 : 22 A3 00 00 00 06 FE 05 00 1F FF 00 ...00 00 :表示该条指令遵循ModbusTCP协议; 00 06 :表示后面数据的长度,可以自己数一下是否是6个Bit FE : 设备
  • C++ ModBUS TCP客户端

    千次阅读 2018-07-20 14:32:01
    https://sourceforge.net/projects/qmodmaster/   用Qt写的一个客户端。非常小巧,非常好用。平台通用。内部有一个文件夹libmodbus,可以借来用用。 在github上找了一下都没找到这么好的东西。... ...
1 2 3 4 5 ... 20
收藏数 4,873
精华内容 1,949
关键字:

modbus tcp