当前位置:   article > 正文

Modbus TCP协议

modbus tcp

Modbus介绍

1、起源

  1. Modbus 通信协议,是一种工业现场总线协议标准。
  2. Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,主要有以下三种:
  3. Modbus RTU、Modbus ASCII和Modbus TCP
  4. 优势: 免费、简单、容易使用

2、分类:

  1. 1)Modbus RTU
  2. 二进制 效率高 常用 运行在串口上的协议,采用二进制表现形式以及紧凑型数据结构,通信效率高,应用广泛
  3. 2)Modbus ASCII
  4. 运行在串口上的协议,采用ASCII码传输,并且利用特殊字符作为其字节的开始与结束标识,其传输效率要远远低于Modbus RTU协议,一般只有在通信数据量较小的情况下才考虑使用Modbus ASCII通信协议
  5. 3)Modbus TCP
  6. 运行在以太网上的协议

3、优势:

  免费、简单、容易使用

4、Modbus 应用场景:

     Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备

5、特点:

  1. 采用主从问答方式进行通信
  2. Modbus TCP是应用层协议,基于传输层TCP协议实现
  3. Modbus TCP端口号默认为502

Modbus TCP协议

    TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上50和一个6并通过TCP/IP网络协议发送出去即可 Modbus采用主从问答方式(master/slave)通信,有一个节点是master节点,其他使用Modbus协议参与通信的节点是slave节点(可以多个),每一个slave设备都有一个唯一的地址            ModbusTCP通常使用端口502作为接收报文端口

Modbus TCP协议格式

Modbus TCP数据帧包含报文头、功能代码和数据三部分

Modbus TCP/IP协议最大数据帧长度为260字节

1、 MBAP报文头:

Modbus TCP/IP协议包含一个7字节报文头

2、寄存器

Modbus TCP通过寄存器的方式存储数据。

寄存器分类:

离散量输入、线圈、输入寄存器、保持寄存器。

离散量输入和线圈其实就是位寄存器(每个寄存器数据占1个字节),工业上主要用于控制IO设备。

输入和保持寄存器是字寄存器(每个寄存器数据占2个字节),工业上主要用于存储工业设备的值。

寄存器PLC地址 开头表示状态:
   0开头:线圈   1开头:离散输入  3 开头:输入寄存器    4开头:保持寄存器
离散输入寄存器

离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。

所以功能码也简单就一个读的 0x02

线圈寄存器

类比为开关量,每一个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器可读可写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。

对应上面的功能码也就是:0x01 0x05 0x0f

保持寄存器

这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读可写的。比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写

所以功能码有对应的三个:0x03 0x06 0x10

输入寄存器

这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值

对应的功能码也就一个 0x04

3、功能码:

根据四种不同的寄存器设置了8种功能码,分别如下表所示

在实际使用时会根据要进行的操作选择不同的功能码

功能码:

  1. 读:01 ~04
  2. 01:读线圈
  3. 02:读离散输入
  4. 03:读保持寄存器
  5. 04:读输入寄存器
  1. 写: 05 06 15 16
  2. 05:写单个线圈
  3. 06:写单个保存计时器
  4. 15:写多个线圈
  5. 16:写多个保存寄存器

举例:(详细解)

读取保持寄存器 功能码 :03H

  1. 主站询问数据流:00 00 00 00 || 00 06 || 11 || 03 || 00 6B || 00 02
  2. 从左到右依次分析:
  3. 00 00:事务标识符,类似与TCP里的seq和ack,可以随意写,回复和发送的会一致
  4. 00 00:协议标识符,00 00表示ModbusTCP协议
  5. 00 06:表示接下来的数据长度,单位为字节
  6. 11:单元标识符,即新建寄存器表时候的从机ID,默认是1
  7. 03:功能码,读保持寄存器
  8. 00 6B:读取的寄存器起始地址,0x6B=107则对应寄存器PLC地址为40108
  9. 00 02:读取的寄存器个数为2个,则结束地址为40108+2-1=40109
  1. 从站应答数据流:00 00 00 00 || 00 07 || 11 || 03 || 04 || 02 2B || 01 06
  2. 前八字节不再重复,同上
  3. 04:计数的字节个数
  4. 02 2B:第一个寄存器40108的数据值
  5. 01 06:第二个寄存器40109的数据值
  1. 读线圈状态寄存器: 功能码 01H

函数:
  1. int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  2. 功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器起始地址
  6. nb :寄存器个数
  7. dest :得到的状态值
  1. 读离散输入寄存器 : 功能码02H

函数
  1. int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  2. 功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器起始地址
  6. nb :寄存器个数
  7. dest :得到的状态值
  8. 返回值:成功:返回nb的值
  1. 读取保持寄存器 : 功能码03H

函数
  1. int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  2. 功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03)
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器起始地址
  6. nb :寄存器个数
  7. dest :得到的寄存器的值
  8. 返回值:成功:读到寄存器的个数
  9. 失败:-1
  1. 读输入寄存器 : 功能码04H

函数
  1. int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  2. 功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器起始地址
  6. nb :寄存器个数
  7. dest :得到的寄存器的值
  8. 返回值:成功:读到寄存器的个数
  9. 失败:-1
  1. 写单个线圈寄存器 : 功能码05H

函数
  1. int modbus_write_bit(modbus_t *ctx, int addr, int status);
  2. 功能:写入单个线圈的状态(对应功能码为0x05
  3. 参数:
  4. ctx :Modbus实例
  5. addr :线圈地址
  6. status:线圈状态
  7. 返回值:成功:0
  8. 失败:-1
  1. 写单个保持寄存器 : 功能码06H

函数
  1. int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
  2. 功能:写入多个连续线圈的状态(对应功能码为15
  3. 参数:
  4. ctx :Modbus实例
  5. addr :线圈地址
  6. nb :线圈个数
  7. src :多个线圈状态
  8. 返回值:成功:0
  9. 失败:-1
  1. 写多个线圈状态 : 功能码0FH(15)

函数
  1. int modbus_write_register(modbus_t *ctx, int addr, int value);
  2. 功能: 写入单个寄存器(对应功能码为0x06
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器地址
  6. value :寄存器的值
  7. 返回值:成功:0
  8. 失败:-1
  1. 写(预置)多个保持寄存器 : 功能码10H(16)

函数
  1. int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
  2. 功能:写入多个连续寄存器(对应功能码为16
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器地址
  6. nb :寄存器的个数
  7. src :多个寄存器的值
  8. 返回值:成功:0
  9. 失败:-1

Modbus库

三方库的使用

1、gcc xx.c -I ./install/include/modbus -L./install/lib -lmodbus

./a.out

解释:

-I 后需要指定出头文件的路径(大写的i)

-L 后需要指定库的路径

-l 后需要指定库名

因为库为动态库,所有运行时会报错,

2.要想编译方便,可以将头文件和库文件放到系统路径下

sudo cp install/include/modbus/*.h /usr/include

sudo cp install/lib/* -r /lib -d

后期编译时,可以直接gcc xx.c -lmodbus

头文件默认搜索路径:/usr/include 、/usr/local/include

库文件默认搜索路径:/lib、/usr/lib

编程流程

1. 创建实例 modbus_new_tcp

  1. modbus_t* modbus_new_tcp(const char *ip, int port)
  2. 功能:以TCP方式创建Modbus实例,并初始化
  3. 参数:
  4. ip :ip地址
  5. port:端口号
  6. 返回值:成功:Modbus实例
  7. 失败:NULL

2. 设置从机ID modbus_set_slave

  1. int modbus_set_slave(modbus_t *ctx, int slave)
  2. 功能:设置从机ID
  3. 参数:
  4. ctx :Modbus实例
  5. slave:从机ID
  6. 返回值:成功:0
  7. 失败:-1

3. 和从机进行连接 modbus_connect

  1. int modbus_connect(modbus_t *ctx)
  2. 功能:和从机(slave)建立连接
  3. 参数:
  4. ctx:Modbus实例
  5. 返回值:成功:0
  6. 失败:-1

4. 寄存器进行操作 功能码对应函数

  1. int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  2. 功能:读取线圈状态,可读取多个连续线圈的状态(对应功能码为0x01)
  3. 参数:
  4. ctx :Modbus实例
  5. addr :寄存器起始地址
  6. nb :寄存器个数
  7. dest :得到的状态值
  8. int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest)
  9. 功能:读取输入状态,可读取多个连续输入的状态(对应功能码为0x02)
  10. 参数:
  11. ctx :Modbus实例
  12. addr :寄存器起始地址
  13. nb :寄存器个数
  14. dest :得到的状态值
  15. 返回值:成功:返回nb的值
  16. int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  17. 功能:读取保持寄存器的值,可读取多个连续保持寄存器的值(对应功能码为0x03)
  18. 参数:
  19. ctx :Modbus实例
  20. addr :寄存器起始地址
  21. nb :寄存器个数
  22. dest :得到的寄存器的值
  23. 返回值:成功:读到寄存器的个数
  24. 失败:-1
  25. int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest)
  26. 功能:读输入寄存器的值,可读取多个连续输入寄存器的值(对应功能码为0x04)
  27. 参数:
  28. ctx :Modbus实例
  29. addr :寄存器起始地址
  30. nb :寄存器个数
  31. dest :得到的寄存器的值
  32. 返回值:成功:读到寄存器的个数
  33. 失败:-1
  34. int modbus_write_bit(modbus_t *ctx, int addr, int status);
  35. 功能:写入单个线圈的状态(对应功能码为0x05
  36. 参数:
  37. ctx :Modbus实例
  38. addr :线圈地址
  39. status:线圈状态
  40. 返回值:成功:0
  41. 失败:-1
  42. int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *src);
  43. 功能:写入多个连续线圈的状态(对应功能码为15
  44. 参数:
  45. ctx :Modbus实例
  46. addr :线圈地址
  47. nb :线圈个数
  48. src :多个线圈状态
  49. 返回值:成功:0
  50. 失败:-1
  51. int modbus_write_register(modbus_t *ctx, int addr, int value);
  52. 功能: 写入单个寄存器(对应功能码为0x06
  53. 参数:
  54. ctx :Modbus实例
  55. addr :寄存器地址
  56. value :寄存器的值
  57. 返回值:成功:0
  58. 失败:-1
  59. int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src);
  60. 功能:写入多个连续寄存器(对应功能码为16
  61. 参数:
  62. ctx :Modbus实例
  63. addr :寄存器地址
  64. nb :寄存器的个数
  65. src :多个寄存器的值
  66. 返回值:成功:0
  67. 失败:-1

5. 关闭套接字 modbus_close

  1. void modbus_close(modbus_t *ctx)
  2. 功能:关闭套接字
  3. 参数:ctx:Modbus实例

6. 释放实例 modbus_free

  1. void modbus_free(modbus_t *ctx)
  2. 功能:释放Modbus实例
  3. 参数:ctx:Modbus实例

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/328140
推荐阅读
相关标签
  

闽ICP备14008679号