C# modbus TCP协议应用
协议简介
Modbus由MODICON公司(现施耐德公司)于1979年开发,是一种工业现场总线协议标准。主要分为了RTU,ASCII,TCP三种协议类型。本文我们只探讨C#
modbusTCP的应用。 modbusTcp协议采用master/slave模型。在modbus总线中是以"一主多从"关系存在的。通讯方式是主站发出请求(广播或者单播),从站收到请求后应答。
报文分析
请求 :19 15 00 00 00 06 01 03 05 4A 00 01
应答:19 15 00 00 00 05 01 03 02 11 22
请求:
19 15 为报文标识符,一般每次通信加1来表示区别不同报文
00 00 表示modbusTCP协议
00 06 表示后面的数据长度
01 从站号
03 功能码。
05 4A 读取数据的起始地址
00 01 读几位地址(读连续的地址)
应答:
19 15 为报文标识符
00 00 表示modbusTCP协议
00 05 表示后面的数据长度
01 从站号
03 功能码
02 表示读到的数据长度
11 22 表示读到的数据
不同于RTU协议,不需要进行CRC校验
功能码 | 含义 |
---|---|
01 | 读线圈 |
05 | 写单个线圈 |
0F | 写多个线圈 |
02 | 读离散量输入 |
04 | 读输入寄存器 |
03 | 读保持寄存器 |
06 | 写单个保持寄存器 |
10 | 写多个保持寄存器 |
modbusTCP master实现
一般来说上位机作为客户端和主站进行连接
- 字符集
using System.Net;
using System.Net.Sockets;
- tcp连接
string ip = "192.168.20.79";
int port = 502;
// 创建socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 建立连接
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
- 读保持寄存器和线圈
//拼接报文
public byte[] getReadCommand(ushort address, byte station, byte funtion, ushort length) //寄存器地址 站号 功能码 寄存器长度
{
byte[] buffer = new byte[12];
buffer[0] = 0x19;
buffer[1] = 0x12; //client发出的验证信息
buffer[2] = 0x00;
buffer[3] = 0x00; //表示tcp/ip的协议的modbus的协议
buffer[4] = 0x00;
buffer[5] = 0x06; //表示的是该字节以后的字节长度
buffer[6] = station; //站号
buffer[7] = funtion; //功能码
buffer[8] = BitConverter.GetBytes(address)[1];
buffer[9] = BitConverter.GetBytes(address)[0]; //寄存器地址
buffer[10] = BitConverter.GetBytes(length)[1];
buffer[11] = BitConverter.GetBytes(length)[0]; //寄存器的长度
return buffer;
}
//发送报文
socket.Send(buffer);
//接收数据
byte[] write_done_recvBuffer = new byte[11];
socket.Receive(write_done_recvBuffer, 0, write_done_recvBuffer.Length, SocketFlags.None);
5.写线圈和保持寄存器
public byte[] writeCommand(ushort address, byte station, byte funtion, byte[] values) //寄存器地址 站号 功能码 寄存器长度
{
byte[] buffer = new byte[10+ values.Length];
buffer[0] = 0x19;
buffer[1] = 0xB2; //client发出的验证信息
buffer[2] = 0x00;
buffer[3] = 0x00; //表示tcp/ip的协议的modbus的协议
buffer[4] = BitConverter.GetBytes(4 + values.Length)[1];
buffer[5] = BitConverter.GetBytes(4 + values.Length)[0]; //表示的是该字节以后的字节长度
buffer[6] = station; //站号
buffer[7] = funtion; //功能码
buffer[8] = BitConverter.GetBytes(address)[1];
buffer[9] = BitConverter.GetBytes(address)[0]; //寄存器地址
values.CopyTo(buffer, 10);
return buffer;
}
//接收数据
byte[] write_done_recvBuffer = new byte[20];//
socket.Receive(write_done_recvBuffer, 0, write_done_recvBuffer.Length, SocketFlags.None);