C# Modbus TCP/IP学习记录
Modbus协议中,脚色分为主站(Mater)、从站(Slave);数据范例分为线圈(Coil)、离散输入(Input)、保持寄存器(HoldingRegister)、输入寄存器(InputRegister)。主站作为哀求发起方,负责发送命令给一个或多个从站;从站作为哀求响应方,负责接收主站命令并执行响应的操纵。
线圈为可读写布尔值,离散输入为只读布尔值,保持寄存器为可读写16位比特数值,输入寄存器为只读16为比特数值。
实操步骤:
1.利用Visual Studio创建C# Framework 控制台应用程序,作为TCP Client(客户端)、Modbus Master(主站)。
2.利用Visual Studio安装Nuget包NModbus:
https://i-blog.csdnimg.cn/direct/f4f5ba38651c4c32894e5eb5fa46ac65.png
3.下载Modbus Slave应用程序,作为TCP Server(服务端)、Modbus Slave(从站)。
下载地址:Download
https://i-blog.csdnimg.cn/direct/47735eab327642c7a78a55669b54c8a1.png
4.打开Modbus Slave,单击工具栏“New”,创建四个Slave:
https://i-blog.csdnimg.cn/direct/0f63dbdf063e4f8e8a442db4c5ef3612.png
https://i-blog.csdnimg.cn/direct/2c2a4228d7cd4793b33dba5900925ec0.png
5.创建的Slave默认内容为10行,Slave ID为1,数据范例为保持寄存器,如需修改利用工具栏Slave Defintion。
https://i-blog.csdnimg.cn/direct/dbe92f3fdb02402eac06d3760ff48ddc.png
运用SlaveDefintion把Mbslave1.mbs的Function改为01 Coil Status(0x),为线圈;
把Mbslave2.mbs的Function改为02 Input Status(1x),为离散输入;
把Mbslave3.mbs的Function改为03 Holding Register(4x),为保持寄存器;
把Mbslave3.mbs的Function改为04 Input Register(4x),为输入寄存器。
https://i-blog.csdnimg.cn/direct/159626f0bb414f959fcacbf0fd400f1c.png
6.利用工具栏Connection项,单击Connect,弹窗Connection Setup,修改Connection下拉框为Modbus TCP/IP,下列配置按实际需求修改:
https://i-blog.csdnimg.cn/direct/607be45e727b4812aa8df44734e841a8.png
7.回到Visual Studio的控制台应用程序,输入以下代码,代码含有4种数据范例的读写(离散输入和输入寄存器为只读,所以只有读)方法,按需修改:
using System;
using System.Diagnostics.SymbolStore;
using System.Net.Sockets;
using NModbus;
using NModbus.Message;
class ModbusTcpClient
{
private static string ipAddress = "127.0.0.1"; // 替换为实际 IP 地址
private static int port = 502; // Modbus TCP 默认端口号
private static byte slaveId = 1; // 从设备 ID
static void Main(string[] args)
{
try
{
using (TcpClient client = new TcpClient(ipAddress, port))
{
// 创建 Modbus TCP 主站
var factory = new ModbusFactory();
IModbusMaster master = factory.CreateMaster(client);
HoldingRegisterTest(master);
InputRegisterTest(master);
ColiTest(master);
InputTest(master);
}
}
catch (Exception ex)
{
Console.WriteLine($"发生错误: {ex.Message}");
}
Console.ReadLine();
}
private static void HoldingRegisterTest(IModbusMaster master)
{
//保持寄存器是可读写的16位字(16位bits)
ReadHoldingRegisters(master, slaveId, 0, 10);//从地址0寄存器起,读10个保持寄存器数值
WriteSingleRegister(master, slaveId, 0, 21);//往地址0寄存器写入值21
ReadSingleHoldingReister(master, slaveId, 0);//读取地址0寄存器数值
WriteHoldingRegister(master, slaveId, 0, new ushort[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });//从地址0寄存器起,按数组顺序写入10个保持寄存器数值
ReadHoldingRegisters(master, slaveId, 0, 10);
}
private static void InputRegisterTest(IModbusMaster master)
{
//输入寄存器是只读的16位字(16位bits)
ReadInputRegisters(master, slaveId, 0, 10);
ReadSingleInputRegister(master, slaveId, 0);
}
private static void ColiTest(IModbusMaster master)
{
//线圈是可读写的布尔值
ReadCoils(master, slaveId, 0, 10);//从地址0寄存器起,读10个数值线圈
WriteSingleCoils(master, slaveId, 0, true);//往地址0寄存器写入值21
ReadCoils(master, slaveId, 0);//读取地址0寄存器数值
WriteCoils(master, slaveId, 0, new bool[] { true, false, true, false, true, false, true, false, true, false});//从地址0寄存器起,按数组顺序写入10个线圈数值
ReadCoils(master, slaveId, 0, 10);
}
private static void InputTest(IModbusMaster master)
{
//离散输入是只读的布尔值
ReadInputs(master, slaveId, 0, 10);
ReadSingleInput(master, slaveId, 0);
}
#region 保持寄存器
public static ushort[] ReadHoldingRegisters(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
{
ushort[] readValues = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
Console.WriteLine("读取到的保持寄存器数据:");
for (int i = 0; i < readValues.Length; i++)
{
Console.WriteLine($"保持寄存器 {startAddress + i}: {readValues}");
}
return readValues;
}
public static ushort ReadSingleHoldingReister(IModbusMaster master, byte slaveId, ushort startAddress)
{
ushort[] readValues = ReadHoldingRegisters(master, slaveId, startAddress, 1);//没有读单一保持寄存器的方法
return readValues;
}
public static void WriteHoldingRegister(IModbusMaster master, byte slaveId, ushort startAddress, ushort[] value)
{
master.WriteMultipleRegisters(slaveId, startAddress, value);
Console.WriteLine($"写入的保持寄存器数据:");
for (int i = 0; i < value.Length; i++)
{
Console.WriteLine($"保持寄存器 {startAddress + i}: {value}");
}
}
public static void WriteSingleRegister(IModbusMaster master, byte slaveId, ushort address, ushort value)
{
master.WriteSingleRegister(slaveId, address, value);
Console.WriteLine($"写入的保持寄存器数据:");
Console.WriteLine($"保持寄存器 {address + 1}: {value}");
}
#endregion
#region 输入寄存器
public static ushort[] ReadInputRegisters(IModbusMaster master ,byte slaveId, ushort startAddress, ushort numRegisters)
{
ushort[] readValues = master.ReadInputRegisters (slaveId, startAddress, numRegisters);
Console.WriteLine("读取到的输入寄存器数据:");
for (int i = 0; i < readValues.Length; i++)
{
Console.WriteLine($"输入寄存器 {startAddress + i}: {readValues}");
}
return readValues;
}
public static ushort ReadSingleInputRegister(IModbusMaster master, byte slaveId, ushort startAddress)
{
ushort[] readValues = ReadInputRegisters(master, slaveId, startAddress, 1);//没有读单一输入寄存器的方法
return readValues;
}
#endregion
#region 线圈
public static bool[] ReadCoils(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
{
bool[] readValues = master.ReadCoils(slaveId, startAddress, numRegisters);
Console.WriteLine("读取到的线圈数据:");
for (int i = 0; i < readValues.Length; i++)
{
Console.WriteLine($"线圈 {startAddress + i}: {readValues}");
}
return readValues;
}
public static bool ReadCoils(IModbusMaster master, byte slaveId, ushort startAddress)
{
bool[] readValues = ReadCoils(master, slaveId, startAddress, 1);//没有读单一保持寄存器的方法
return readValues;
}
public static void WriteCoils(IModbusMaster master, byte slaveId, ushort startAddress, bool[] value)
{
master.WriteMultipleCoils(slaveId, startAddress, value);
Console.WriteLine($"写入的线圈数据:");
for (int i = 0; i < value.Length; i++)
{
Console.WriteLine($"线圈 {startAddress + i}: {value}");
}
}
public static void WriteSingleCoils(IModbusMaster master, byte slaveId, ushort address, bool value)
{
master.WriteSingleCoil(slaveId, address, value);
Console.WriteLine($"写入的线圈数据:");
Console.WriteLine($"线圈{address + 1}: {value}");
}
#endregion
#region 离散输入
public static bool[] ReadInputs(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
{
bool[] readValues = master.ReadInputs(slaveId, startAddress, numRegisters);
Console.WriteLine("读取到的离散输入数据:");
for (int i = 0; i < readValues.Length; i++)
{
Console.WriteLine($"离散输入 {startAddress + i}: {readValues}");
}
return readValues;
}
public static bool ReadSingleInput(IModbusMaster master, byte slaveId, ushort startAddress)
{
bool[] readValues = ReadInputs(master, slaveId, startAddress, 1);//没有读单一离散输入的方法
return readValues;
}
#endregion
} 8.执行结果:
https://i-blog.csdnimg.cn/direct/532ccd4784444714a6523957882d54b3.png
https://i-blog.csdnimg.cn/direct/b859cfb83f1048959a93c3631027b44d.png
https://i-blog.csdnimg.cn/direct/ebb733c9413a44879148b69064889d84.png
https://i-blog.csdnimg.cn/direct/a404d5deefcf4896aed69cb3ab52f331.png
https://i-blog.csdnimg.cn/direct/24401f3a61a34c72b87717ed7262117f.png
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]