- ModBusTCP简介
MODBUS TCP是结合了MODBUS协议和TCP/IP网络标准,它是MODBUS协议在TCP/IP上的具体实现,数据传输时在TCP报文中插入MODBUS 应用数据单元,Modbus协议赋予TCP端口号为502,这是目前在仪表与自动化行业中分配到的端口号,所使用的硬件接口为以太网接口。
MODBUS TCP信息帧包含了报文头、功能码、数据三部分内容,其中消息报文头由四部分构成,共7个字节,与MODBUS RTU相比,MODBUS TCP中不存在CRC校验,校验部分由TCP/IP协议和以太网的链路层来完成。
- 信息帧内容结构:
MBAP报文头 | 数据单元(ADU) | ||||
传输标识 | 协议标识 | 长度 | 单元标识符 | 功能码 | 数据 |
2 byte | 2 byte | 2 byte | 1 byte | 1 byte | n byte |
- 信息帧内容解读:
传输标识 | 2个字节长度,标志Modbus询问/应答的传输,一般默认是00 00 |
协议标识 | 2个字节长度,00 00 表示Modbus协议 |
长度 | 2个字节长度,即从单元标识符以后的数据字节个数 |
单元标识符 | 1个字节长度,相当于MODBUS RTU通信中的从站站号(slave id) |
功能码 | 1个字节长度,可用于读取设备数据或者往设备中写入数据 |
数据 | 根据读取数据长度的不同,由n个字节组成。 |
- 功能码(决定是写数据还是读取数据):
功能码 | 描述 | 访问类型 | PLC地址 | 数据类型 | 操作数量 |
01H | 线圈寄存器 | 读 | 00001-09999 | 位 | 单/多 |
02H | 离散输入寄存器 | 只读 | 10001-19999 | 位 | 单/多 |
03H | 保持寄存器 | 读 | 40001-49999 | 字 | 单/多 |
04H | 输入寄存器 | 读 | 30001-39999 | 字 | 单/多 |
05H | 线圈寄存器 | 写 | 00001-09999 | 位 | 单个 |
06H | 保持寄存器 | 写 | 40001-49999 | 位 | 多个 |
0FH | 线圈寄存器 | 写 | 00001-09999 | 位 | 多个 |
10H | 保持寄存器 | 写 | 40001-49999 | 字 | 多个 |
- Modbus Tcp通信格式举例
03H功能码(读取一个或多个保持寄存器的数值)使用举例:
发送(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 首个寄存器地址 | 读取的寄存器个数 |
00 00 | 00 00 | 00 06 | 01 | 03 | 00 00 | 00 03 |
接收(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 读取的字节数 | 第1、2、3个寄存器的数值 | ||
00 00 | 00 00 | 00 09 | 01 | 03 | 06 | 03 E8 | 13 88 | 02 8A |
06H功能码(写一个保持寄存器的值)使用举例:
发送(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 寄存器地址 | 写入寄存器的数值 |
00 00 | 00 00 | 00 06 | 01 | 06 | 00 00 | 00 0A |
接收(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 寄存器地址 | 写入寄存器的数值 |
00 00 | 00 00 | 00 06 | 01 | 06 | 00 00 | 00 0A |
10H功能码(写多个保持寄存器的值)使用举例:
接收(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 首个寄存器地址 | 写入寄存器个数 | 写入字节数 | 写入寄存器的值 |
00 00 | 00 00 | 00 0D | 01 | 10 | 00 00 | 00 03 | 06 00 | 00 0A 00 0B 00 0F |
接收(hex): | 传输标识符 | 协议标识符 | 长度标识符 | 站号 | 功能码 | 首个寄存器地址 | 写入寄存器个数 |
00 00 | 00 00 | 00 06 | 01 | 10 | 00 00 | 00 03 |
- ABB机器人ModBusTCP通讯实现案例
ABB机器人并没有提供标准的MODBUS相关处理函数,要使用ModBusTCP,可以使用普通socket收发,按照ModBusTCP格式进行数据传输。由于ABB机器人没有专用的通讯指令,因此只能作为客户端。客户端连接程序可以放在初始化程序中,每次运行连接一次即可。
- ABB机器人配置需求:
- MODBUS/TCP基于以太网,故ABB机器人在使用时需要有616-1PCINTERFACE选项。
- MODBUS/TCP通讯介质使用普通网线连接控制柜网口即可。
- ABB机器人端程序编写:
- 定义所需变量及初始化socket通讯:
VAR socketdev socket_modbus; ! 定义一个套接字数据类型
VAR byte send_byte{13}; ! 定义一个字节数据类型数组
VAR rawbytes send_rawbyte; ! 定义一个原始数据字节容器
VAR rawbytes recv_rawbyte; ! 定义一个原始数据字节容器
VAR num recv_val;
VAR num reg_val;
PROC Init_ModbusTcp()
SocketClose socket_modbus; ! 关闭套接字
SocketCreate socket_modbus; ! 创建套接字
SocketConnect socket_modbus,"127.0.0.1", 50000; ! 指定连接IP和端口号
ENDPROC
PROC Close_ModbusTcp()
SocketClose socket_modbus; ! 关闭套接字
ENDPROC
- 读取一个16位无符号整型数值:
FUNC num Read_HoldingReg_Uint(num addr)
send_byte{1}:= 0X00; ! 传输标志符
send_byte{2}:= 0X00; ! 传输标志符
send_byte{3}:= 0X00; ! 协议标志符
send_byte{4}:= 0X00; ! 协议标志符
send_byte{5}:= 0X00; ! 数据长度高8位
send_byte{6}:= 0X06; ! 数据长度低8位
send_byte{7}:= 0X01; ! 站号
send_byte{8}:= 0X03; ! 功能码: 03H 读取一个保持寄存器数值 send_byte{9}:= addr DIV 256; ! 数据地址高8位
send_byte{10}:= addr MOD 256; ! 数据地址低8位
send_byte{11}:= 0X00; ! 数据个数高8位
send_byte{12}:= 0X01; ! 数据个数低8位 (以word计数)
!\Data 表示数组的格式发送字节数据类型
!\NoOfBytes表示指定发送的字节数
SocketSend socket_modbus\Data:=send_byte\NoOfBytes:=12;
TPWrite "read_uint addr:" + ValToStr(addr);
SocketReceive socket_modbus\rawdata:=recv_rawbyte;
UnpackRawBytes recv_rawbyte\Network,10,recv_val\IntX:=UINT;
TPWrite "read_uint recv:" + ValToStr(recv_val);
RETURN recv_val;
ENDFUNC
- 读取一个32位浮点型数值:
FUNC num Read_HoldingReg_Real(num addr)
send_byte{1}:= 0X00; ! 传输标志符
send_byte{2}:= 0X00; ! 传输标志符
send_byte{3}:= 0X00; ! 协议标志符
send_byte{4}:= 0X00; ! 协议标志符
send_byte{5}:= 0X00; ! 数据长度高8位
send_byte{6}:= 0X06; ! 数据长度低8位
send_byte{7}:= 0X01; ! 站号
send_byte{8}:= 0X03; ! 功能码: 03H 读取多个保持寄存器数值
send_byte{9}:= addr DIV 256; ! 数据地址高8位
send_byte{10}:= addr MOD 256; ! 数据地址低8位
send_byte{11}:= 0X00; ! 数据个数高8位
send_byte{12}:= 0X02; ! 数据个数低8位 (以word计数)
!\Data 表示数组的格式发送字节数据类型
!\NoOfBytes表示指定发送的字节数
SocketSend socket_modbus\Data:=send_byte\NoOfBytes:=12;
TPWrite "read_real addr:" + ValToStr(addr);
SocketReceive socket_modbus\rawdata:=recv_rawbyte;
UnpackRawBytes recv_rawbyte\Network,10,recv_val\float4;
TPWrite "read_real recv:" + ValToStr(recv_val);
RETURN recv_val;
ENDFUNC
- 写入一个16位无符号整型数值:
PROC Write_HoldingReg_Uint(num addr, num val)
send_byte{1}:= 0X00; ! 传输标志符
send_byte{2}:= 0X00; ! 传输标志符
send_byte{3}:= 0X00; ! 协议标志符
send_byte{4}:= 0X00; ! 协议标志符
send_byte{5}:= 0X00; ! 数据长度高8位
send_byte{6}:= 0X06; ! 数据长度低8位
send_byte{7}:= 0X01; ! 站号
send_byte{8}:= 0X06; ! 功能码:06H 写入一个保持寄存器数值
send_byte{9}:= addr DIV 256; ! 数据地址高8位
send_byte{10}:= addr MOD 256; ! 数据地址低8位
send_byte{11}:= val DIV 256; ! 数值高8位
send_byte{12}:= val MOD 256; ! 数值低8位
!\Data 表示数组的格式发送字节数据类型
!\NoOfBytes表示指定发送的字节数
SocketSend socket_modbus\Data:=send_byte\NoOfBytes:=12;
TPWrite "write_uint send:" + ValToStr(val);
SocketReceive socket_modbus\rawdata:=recv_rawbyte;
UnpackRawBytes recv_rawbyte\Network,11,recv_val\IntX:=UINT;
TPWrite "write_uint recv:" + ValToStr(recv_val);
ENDPROC
- 写入一个32位浮点型数值:
PROC Write_HoldingReg_Real(num addr, num val)
send_byte{1}:= 0X00; ! 传输标志符
send_byte{2}:= 0X00; ! 传输标志符
send_byte{3}:= 0X00; ! 协议标志符
send_byte{4}:= 0X00; ! 协议标志符
send_byte{5}:= 0X00; ! 数据长度高8位
send_byte{6}:= 0X06; ! 数据长度低8位
send_byte{7}:= 0X01; ! 站号
send_byte{8}:= 0X10; ! 功能码:10H 写入一个保持寄存器数值
send_byte{9}:= addr DIV 256; ! 数据地址高8位
send_byte{10}:= addr MOD 256; ! 数据地址低8位
send_byte{11}:= 0X00; ! 寄存器个数高8位
send_byte{12}:= 0X02; ! 寄存器个数低8位
send_byte{13}:= 0X04; ! 字节数量
FOR i FROM 1 TO 13 DO
PackRawBytes send_byte{i}, send_rawbyte\Network,i,\Hex1;
ENDFOR
PackRawBytes val, send_rawbyte\Network,14,\float4;
SocketSend socket_modbus\rawdata:=send_rawbyte;
TPWrite "write_real send:" + ValToStr(val);
UnpackRawBytes send_rawbyte\Network,14,recv_val\float4;
TPWrite "write_real val:" + ValToStr(recv_val);
SocketReceive socket_modbus\rawdata:=recv_rawbyte;
UnpackRawBytes recv_rawbyte\Network,9,recv_val\IntX:=UINT;
TPWrite "write_real recv:" + ValToStr(recv_val);
ENDPROC
- 调用函数实现数值读写操作:
Init_ModbusTcp;
Write_HoldingReg_Uint 100, 1; ! 在地址100处写入数值1
Write_HoldingReg_Real 200, 1.1; ! 在地址200处写入数值1.1
reg_val = Read_HoldingReg_Uint(100); ! 读取地址100处的整型数值
reg_val = Read_HoldingReg_Real(200); ! 读取地址200处的浮点型数值
Close_ModbusTcp;