Modbus读写Csharp兼容西门子施耐德
Posted 厦门德仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Modbus读写Csharp兼容西门子施耐德相关的知识,希望对你有一定的参考价值。
以下为C#读写Modbus,兼容西门子和施耐德。有详细的Modbus协议报文说明,以及代码注释
一、新建类:ModbusTcpUtil.cs 代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
/*
-
Modbus报文协议格式【只考虑读写保持寄存器 0x03:读多个保持寄存器 0x06:写单个保持寄存器 0x10:写多个保持寄存器】
-
PLC中,每个寄存器将数据分成两个字节,即一个寄存器【一个字Word】地址占用两个字节Byte,寄存器地址范围:【0x0000~0xFFFF】
-
注意:PLC存储数据是高位在前,如十进制的1000,在PLC中存储字节顺序是:0x03 0xE8。而在C#中存储字节顺序是低位在前:如 BitConverter.GetBytes((short)1000),是 0xE8 0x03
①发送的请求【读多个保持寄存器 0x03】Modbus字节流说明:
* byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
* byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
* byte[4] byte[5] 排在byte[5]后面所有字节的个数,也就是总长度6
* byte[6] 站号,随便指定,00–FF都可以,PLC返回的保持一致
* byte[7] 功能码,读保持寄存器 0x03:
* byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
* byte[10] byte[11] 寄存器个数【Word】,读取的数据长度【以字Word为单位】,读取Int32或Float就是两个字,读取byte或short就是一个字 范围【1~125 即 0x0001~0x007D】
*
* 接收的内容解析:【读多个保持寄存器 0x03】【Modbus响应】
* byte[0] byte[1] 与发送的一致
* byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
* byte[4] byte[5] 排在byte[5]后面所有字节的个数
* byte[6] 站号,与发送的一致
* byte[7] 功能码,与发送的一致
* byte[8] 表示byte[8]后面跟随的字节数【发送的寄存器个数 * 2】
* byte[9] byte[10] byte[11] byte[12] byte[…] 真实数据的字节流,字节流的总个数就是byte[8]②发送的请求【写单个保持寄存器 0x06】Modbus字节流说明: 【0x06只能写入一个16位【short】数据,无法写入Int32或Float】
* byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
* byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
* byte[4] byte[5] 排在byte[5]后面所有字节的个数
* byte[6] 站号,随便指定,00–FF都可以,PLC返回的保持一致
* byte[7] 功能码,0x06:写单个保持寄存器
* byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
* byte[10] byte[11] 写入的值【只能写入一个字Word】
*
* 接收的内容【写单个保持寄存器的响应内容 0x06】与发送的内容完全一致③发送的请求【写多个保持寄存器 0x10】Modbus字节流说明:【写Int32,UInt32,Float需要两个寄存器 写Int64,UInt64,Double需要四个寄存器】
* byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
* byte[2]=0 byte[3]=0 固定为0 代表Modbus标识 随便指定也可以
* byte[4] byte[5] 排在byte[5]后面所有字节的个数
* byte[6] 站号,随便指定,00–FF都可以,PLC返回的保持一致
* byte[7] 功能码,0x10:写多个保持寄存器
* byte[8] byte[9] 起始地址,如起始地址为整数20 则为 0x00 0x14,再如起始地址为整数1000,则为 0x03 0xE8
* byte[10] byte[11] 寄存器数量【设置长度】,范围1~120【0x78】,因此byte[10]=0, byte[11]为寄存器数量
* byte[12] 字节个数 也就是【寄存器数量*2】,范围【2~240】
* byte[13] byte[14] byte[15] byte[16] byte[…] 具体的数据内容 对应 数据一高位 数据一低位 数据二高位 数据二低位
*
* 【写多个保持寄存器 0x10】Modbus响应结果
* byte[0] byte[1] 随便指定,PLC返回的前两个字节完全一致
* byte[2]=0 byte[3]=0 固定为0 代表Modbus标识
* byte[4] byte[5] 排在byte[5]后面所有字节的个数
* byte[6] 站号,随便指定,00–FF都可以,PLC返回的保持一致
* byte[7] 功能码,0x10:写多个保持寄存器
* byte[8] byte[9] 起始地址
* byte[10] byte[11] 寄存器数量【设置长度】
*/
namespace TestProj
/// <summary>
/// 通过Modbus协议与PLC进行通信,读写指定起始地址的PLC寄存器数据
/// 兼容西门子PLC、施耐德PLC
/// </summary>
public class ModbusTcpUtil
/// <summary>
/// 与PLC通信的socket客户端
/// </summary>
public Socket socket;
/// <summary>
/// 是否已连接上PLC,true:已连接上PLC false:未连接
/// </summary>
public bool isConnectPLC = false;
/// <summary>
/// 连接PLC
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool ConnectPLC(string ip, int port)
isConnectPLC = false;
try
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult asyncResult = socket.BeginConnect(ip, port, CallbackConnect, socket);
asyncResult.AsyncWaitHandle.WaitOne(2000);
socket.ReceiveTimeout = 2000;//2000ms无数据接收则超时
return isConnectPLC;
catch (Exception ex)
//MessageBox.Show("连接Modbus_TCP失败:" + ex.Message);
System.Diagnostics.Debug.WriteLine("连接Modbus_TCP失败:" + ex.Message);
isConnectPLC = false;
return isConnectPLC;
/// <summary>
/// 异步连接PLC
/// </summary>
/// <param name="ar"></param>
private void CallbackConnect(IAsyncResult ar)
isConnectPLC = false;
try
Socket skt = ar.AsyncState as Socket;
skt.EndConnect(ar);
isConnectPLC = true;
catch (Exception ex)
//MessageBox.Show("连接Modbus_TCP失败:" + ex.Message);
System.Diagnostics.Debug.WriteLine("连接Modbus_TCP失败:" + ex.Message);
/// <summary>
/// 关闭套接字连接
/// </summary>
public void CloseConnect()
if (this.socket != null)
try
this.socket.Close(1000);
catch
/// <summary>
/// 写一个指定起始地址的Modbus值。写入【sbyte,byte,short,ushort】使用【0x06指令码:写单个寄存器】。
/// 写入【int,uint,long,ulong,float,double】使用【0x10指令码:写多个寄存器】
/// </summary>
/// <typeparam name="T">基本的数据类型,如short,int,double等</typeparam>
/// <param name="startAddress">寄存器起始地址,范围:【0x0000~0xFFFF】</param>
/// <param name="value">写入的值</param>
/// <returns>true:写入成功 false:写入失败</returns>
public bool WriteValue<T>(int startAddress, T value)
if (socket == null || !socket.Connected)
System.Diagnostics.Debug.WriteLine("socket为空或者尚未建立与PLC_Modbus的连接...");
return false;
if (startAddress < 0 || startAddress > 65535)
System.Diagnostics.Debug.WriteLine("Modbus的起始地址必须在0~65535之间");
return false;
byte[] addrArray = BitConverter.GetBytes((ushort)startAddress);
//sbyte,byte,short,ushort 占用一个寄存器(Word)范围的可以使用功能码0x06:写单个寄存器
//int,long,float,double 需要使用两个或两个以上寄存器,因此只能使用功能码0x10:写多个寄存器 其中int,uint,float占用两个寄存器 long,ulong,double占用四个寄存器
byte[] buffer = new byte[12] 0x02, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x06, addrArray[1], addrArray[0], 0x00, 0x00 ;
if (typeof(T) == typeof(sbyte))
sbyte sb = Convert.ToSByte(value);
byte b = (byte)sb;
buffer[11] = b;
else if (typeof(T) == typeof(byte))
byte b = Convert.ToByte(value);
buffer[11] = b;
else if (typeof(T) == typeof(short))
short s = Convert.ToInt16(value);
byte[] writeValueArray = BitConverter.GetBytes(s);
buffer[10] = writeValueArray[1];
buffer[11] = writeValueArray[0];
else if (typeof(T) == typeof(ushort))
ushort us = Convert.ToUInt16(value);
byte[] writeValueArray = BitConverter.GetBytes(us);
buffer[10] = writeValueArray[1];
buffer[11] = writeValueArray[0];
else if (typeof(T) == typeof(int))
int i = Convert.ToInt32(value);
byte[] writeValueArray = BitConverter.GetBytes(i);
buffer = new byte[17] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[3];
buffer[14] = writeValueArray[2];
buffer[15] = writeValueArray[1];
buffer[16] = writeValueArray[0];
else if (typeof(T) == typeof(uint))
uint ui = Convert.ToUInt32(value);
byte[] writeValueArray = BitConverter.GetBytes(ui);
buffer = new byte[17] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[3];
buffer[14] = writeValueArray[2];
buffer[15] = writeValueArray[1];
buffer[16] = writeValueArray[0];
else if (typeof(T) == typeof(long))
long l = Convert.ToInt64(value);
byte[] writeValueArray = BitConverter.GetBytes(l);
buffer = new byte[21] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[7];
buffer[14] = writeValueArray[6];
buffer[15] = writeValueArray[5];
buffer[16] = writeValueArray[4];
buffer[17] = writeValueArray[3];
buffer[18] = writeValueArray[2];
buffer[19] = writeValueArray[1];
buffer[20] = writeValueArray[0];
else if (typeof(T) == typeof(ulong))
ulong ul = Convert.ToUInt64(value);
byte[] writeValueArray = BitConverter.GetBytes(ul);
buffer = new byte[21] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[7];
buffer[14] = writeValueArray[6];
buffer[15] = writeValueArray[5];
buffer[16] = writeValueArray[4];
buffer[17] = writeValueArray[3];
buffer[18] = writeValueArray[2];
buffer[19] = writeValueArray[1];
buffer[20] = writeValueArray[0];
else if (typeof(T) == typeof(float))
float f = Convert.ToSingle(value);
byte[] writeValueArray = BitConverter.GetBytes(f);
buffer = new byte[17] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[3];
buffer[14] = writeValueArray[2];
buffer[15] = writeValueArray[1];
buffer[16] = writeValueArray[0];
else if (typeof(T) == typeof(double))
double d = Convert.ToDouble(value);
byte[] writeValueArray = BitConverter.GetBytes(d);
buffer = new byte[21] 0x02, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x10, addrArray[1], addrArray[0], 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;
buffer[13] = writeValueArray[7];
buffer[14] = writeValueArray[6];
buffer[15] = writeValueArray[5];
buffer[16] = writeValueArray[4];
buffer[17] = writeValueArray[3];
buffer[18] = writeValueArray[2];
buffer[19] = writeValueArray[1];
buffer[20] = writeValueArray[0];
else
//暂不考虑 char(就是ushort,两个字节),decimal(十六个字节)等类型
System.Diagnostics.Debug.WriteLine("写Modbus数据暂不支持其他类型:" + value.GetType());
return false;
try
socket.Send(buffer);
DisplayBuffer(buffer, buffer.Length, truemodbus网关 串口采集传输