MODBUS/TCP是简单的、中立厂商的用于管理和控制自动化设备的MODBUS系列通讯协议的派生产品,显而易见,它覆盖了使用TCP/IP协议的“Intranet”和“Internet”环境中MODBUS报文的用途。协议的最通用用途是为诸如PLC,I/O模块,以及连接其它简单域总线或I/O模块的网关服务的。
Modbus TCP协议是在RTU协议前面添加MBAP报文头,由于TCP是基于可靠连接的服务,RTU协议中的CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码。(使用上的主要区别)。MBAP报文头: 识( 2字节 ) 长度( 2字节 ) 单元标识符(1字节 )
目前Modbus TCP/IP协议主要应用领域Internet或Intranet中,而以太网传输距离远、传输速度快,使得应用范围广泛传输距离远、传输速度快,使得应用范围广泛。
modbus的操作对象有四种:线圈、离散输入、输入寄存器、保持寄存器
线圈:PLC的输出位,开关量,在MODBUS中可读可写
离散量:PLC的输入位,开关量,在MODBUS中只读
输入寄存器:PLC中只能从模拟量输入端改变的寄存器,在MODBUS中只读
保持寄存器:PLC中用于输出模拟量信号的寄存器,在MODBUS中可读可写
根据对象的不同,modbus的功能码有:
0x01:读线圈
0x02:读离散量输入
0x03:读保持寄存器
0x04:读输入寄存器
0x05:写单个线圈
0x06:写单个保持寄存器
0x10:写多个保持寄存器
0x0F:写多个线圈
NModbus4是一个基于C#的Modbus协议库,可用于与Modbus RTU、ASCII、TCP和UDP设备进行通信。NModbus4中文版相当于对原版进行了翻译,使得不懂英文的人能够更方便地使用这个开源库进行编程。NModbus4是用C#编写的Modbus通信协议库,它支持的Modbus协议包括Modbus RTU、ASCII、TCP和UDP,可用于编程读写Modbus设备的寄存器和线圈。它完全符合Modbus协议规范,同时通过使用的事件调用机制,能够实现断线重连的功能。
NModbus4是一个完全开源的库,可以在GitHub上免费下载和使用
搭建西门子仿真环境,需要先前掌握这些,看本人这些博客
windows10企业版安装西门子博途V15---01准备环境
windows10企业版安装西门子博途V15---02安装软件
windows10企业版安装西门子博途V15---03安装仿真软件
windows10企业版安装西门子博途V15---04连接测试
需要先前掌握这些,看本人这些博客
本文最后会提供这个项目,只要打开即可
请注意,这里为什么是BYTE 20,是因为变量mf1到mf5共10寄存器,每个寄存器占2个字节,所以是20个字节,编译完成后,下载到Plc中
以上8个步骤就完成了modbustcp服务器,接下来搞程序,来读写Plc中的浮点数
打开VS2019,创建窗体项目,布局很简单,4个button按钮
///
/// nmodbus4读取一个float
///
///
///
private void button4_Click(object sender, EventArgs e)
{
//nmodbus4读取到的数据都是ushort类型
tcpClient = new TcpClient();
tcpClient.Connect("192.168.1.199", 6800);//连接到主机
master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
byte slaveAddr = byte.Parse("1");//从站地址
//ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示读保持寄存器
//slaveAddress从站地址(默认为1,通常也是1)
//startAddress寄存器开始地址(这个地址是modbus的地址,不是Plc变量地址)
//numberOfPoints寄存器数量(real类型占2个寄存器数量)
ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));
byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);//ushort数组转byte数组
float[] floats = FloatLib.GetFloatArrayFromByteArray(t);//byte数组转float数组
string fw1a = string.Join(",", floats);
float fw1b = FloatLib.GetFloatFromByteArray(t, 0);//byte数组转float
MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("2"), ushort.Parse("2"));
t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
floats = FloatLib.GetFloatArrayFromByteArray(t);
fw1a = string.Join(",", floats);
fw1b = FloatLib.GetFloatFromByteArray(t, 0);
MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("4"), ushort.Parse("2"));
t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
floats = FloatLib.GetFloatArrayFromByteArray(t);
fw1a = string.Join(",", floats);
fw1b = FloatLib.GetFloatFromByteArray(t, 0);
MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("6"), ushort.Parse("2"));
t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
floats = FloatLib.GetFloatArrayFromByteArray(t);
fw1a = string.Join(",", floats);
fw1b = FloatLib.GetFloatFromByteArray(t, 0);
MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("8"), ushort.Parse("2"));
t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
floats = FloatLib.GetFloatArrayFromByteArray(t);
fw1a = string.Join(",", floats);
fw1b = FloatLib.GetFloatFromByteArray(t, 0);
MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
master.Dispose();
tcpClient.Dispose();
}
运行效果
全部读取到了PLC中的数据
nmodbus4读取到的数据都是ushort类型
ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示读保持寄存器
slaveAddress从站地址(默认为1,通常也是1)
startAddress寄存器开始地址(这个地址是modbus的地址,不是Plc变量地址)
numberOfPoints寄存器数量(real类型占2个寄存器数量)
ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));
这个意思是读取从站地址1中的从0开始的2个寄存器数据,即%DB4.DBD0中的数据,结果是1.1
很多人搞不清楚,这个为何是开始地址0,数量是2,这就需要明白PLC中的地址与MODBUS地址的关系,另外nmodbus4读取到的数据都是ushort类型,因此需要进行类型转换,将ushort数组转byte数组,再将byte数组转float数组
///
/// nmodbus4读取全部
///
///
///
private void button5_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();
tcpClient.Connect("192.168.1.188", 6800);//连接到主机
master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
byte slaveAddr = byte.Parse("1");
ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("5"), ushort.Parse("10"));
byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
float[] floats = FloatLib.GetFloatArrayFromByteArray(t);
string fw1a = string.Join(",", floats);
MessageBox.Show(fw1a.ToString());
master.Dispose();
tcpClient.Dispose();
}
运行效果
///
/// nmodbus4写入单个浮点
///
///
///
private void button7_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();
tcpClient.Connect("192.168.1.199", 6800);//连接到主机
master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
//从站地址
byte slaveAddr = byte.Parse("1");
开始地址
ushort startAddr = ushort.Parse("0");
数据的值
string vals = ("12.625");
float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
//void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data);//写入多保持寄存器,意思是指向多个寄存器地址写入数据,也就是指同时向多个寄存器写入数据
//slaveAddress表示从站地址,通常为1,默认也为1
//startAddress表示寄存器开始地址,必须是ushort类型
//data表示写入的具体数值,必须是ushort数组
master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);//向第一个寄存器(地址是0)写入数据12.625
MessageBox.Show("【 通过多保持寄存器】写入正数成功!");
float floatValue = 12.625f;
startAddr = ushort.Parse("2");
byte[] byteArray = ByteArrayLib.GetByteArrayFromFloat(floatValue);
//将字节数组中第0个开始的2个字节转换成ushort类型,即0,1
ushort ua = UShortLib.GetUShortFromByteArray(byteArray, 0, DataFormat.ABCD);
master.WriteSingleRegister(slaveAddr, startAddr, ua);//向从站地址1中的第3个寄存器(地址为2)写入数据ua
//将字节数组中第2个开始的2个字节转换成ushort类型,即2,3
ushort ub = UShortLib.GetUShortFromByteArray(byteArray, 2, DataFormat.ABCD);
startAddr = ushort.Parse("3");
master.WriteSingleRegister(slaveAddr, startAddr, ub);//向从站地址1中的第4个寄存器(地址为3)写入数据ub
MessageBox.Show("【 通过单保持寄存器】写入正数成功!");
vals = ("-18.326");
startAddr = ushort.Parse("8");
uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
ushorts = UShortLib.GetUShortArrayFromByteArray(y);
master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);
MessageBox.Show("【 通过多保持寄存器】写入负数成功!");
master.Dispose();
tcpClient.Dispose();
}
运行效果
///
/// nmodbus4写入多个浮点
///
///
///
private void button6_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();
tcpClient.Connect("192.168.1.199", 6800);//连接到主机
master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
//从站地址
byte slaveAddr = byte.Parse("1");
//开始地址
ushort startAddr = ushort.Parse("0");
//数据的值
string vals = ("4.9635,6.9635,-1.28,67,-902");
float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);
MessageBox.Show("【 多保持寄存器】写入成功!");
master.Dispose();
tcpClient.Dispose();
}
运行效果
浮点数包括整数,小数,也包括正数或负数,所以正整数,负整数,正小数,负小数都可以写入
MODBUS TCP 结合了以太网物理网络和网络标准 TCP/IP 以及以 MODBUS 作为应用协议标准的数据表示方法。MODBUS TCP 通信报文被封装于以太网 TCP/IP 数据包中,MODBUS 协议规范一帧数据的最大长度为 256 个字节。
MODBUS TCP/IP 的通信系统中有两种类型的设备:MODBUS TCP/IP 客户端和服务器设备。
客户端(TCP Client)主动向服务器(TCP Server)发起连接请求,连接建立成功,仅允许客户端主动发起通讯请求。
以太网机型作为 MODBUS TCP 客户端时,通过 S_OPEN 指令建立 TCP 连接,通过 M_TCP 指令发起 MODBUS 请求。
服务器主动监听 502 端口,等待客户端连接请求,连接建立成功,响应符合 Modbus TCP 协议规范的数据通讯请求。
优势: 免费、简单、容易使用,Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备,
链接:https://pan.baidu.com/s/1mARLDATOBphLKbecj4sW8g
提取码:lggv