C# Modbus从机探测:核心报文+极简实现
在工控场景中,快速找出总线上可用的Modbus从机地址(1-247)是高频需求。本文摒弃复杂理论,直接给出探测所需的核心报文和极简可运行C#代码,让你快速落地从机探测功能。
一、核心探测报文(直接用!)
Modbus探测的核心是发送功能码03最小开销请求,以下是两种主流协议的报文详情,直接复制即可用于报文构造或抓包验证。
1. Modbus TCP(最常用)
(1)探测请求报文(必发格式)
- 核心格式(十六进制,按字节排序):
事务ID 协议ID 长度 从站地址 功能码 起始寄存器高位 起始寄存器低位 寄存器数量高位 寄存器数量低位 - 固定配置(最小开销):
- 协议ID:
00 00(Modbus TCP固定标识) - 长度:
00 06(后续6个字节的长度) - 功能码:
03(读保持寄存器,兼容性最强) - 起始寄存器:
00 00(地址0,十进制) - 寄存器数量:
00 01(读取1个,最小开销)
- 协议ID:
- 可直接复用的示例(从站地址1):
00 01 00 00 00 06 01 03 00 00 00 01 - 批量替换:只需修改第7个字节(
01)为1-247对应的十六进制(如地址2对应02,地址10对应0A),即可遍历所有从站。
(2)响应判断报文(快速识别从站存在)
- 正常响应(从站存在):功能码仍为
03,示例:00 01 00 00 00 05 01 03 02 00 00 - 无效响应(从站不存在):功能码为
83(异常标识),或无任何响应(超时)
2. Modbus RTU(串口总线)
(1)探测请求报文(必发格式)
- 核心格式(十六进制):
从站地址 功能码 起始寄存器高位 起始寄存器低位 寄存器数量高位 寄存器数量低位 CRC校验 - 固定配置:功能码
03、起始寄存器00 00、寄存器数量00 01 - 可直接复用的示例(从站地址1):
01 03 00 00 00 01 84 0A(CRC校验已计算完成) - 批量替换:只需修改第1个字节(
01)为1-247对应的十六进制,即可遍历所有从站。
(2)响应判断报文(快速识别从站存在)
- 正常响应(从站存在):功能码仍为
03,示例:01 03 02 00 00 D5 CA - 无效响应(从站不存在):功能码为
83,或无任何响应(超时)
二、极简C#代码实现(直接运行)
基于NModbus4类库,实现快速探测,无冗余逻辑,聚焦报文发送和结果判断。
1. 前期准备
安装NuGet包:NModbus4(Visual Studio右键项目→管理NuGet程序包→搜索安装)
2. Modbus TCP探测代码
csharp
using System;
using System.Net.Sockets;
using NModbus;
namespace ModbusSlaveDetect
{
class Program
{
// Modbus TCP服务器IP
static string tcpServerIp = "192.168.1.100";
// Modbus TCP端口(默认502)
static int tcpPort = 502;
static void Main(string[] args)
{
Console.WriteLine("开始探测Modbus TCP从机地址(1-247)...");
// 遍历1-247从站地址
for (int slaveId = 1; slaveId <= 247; slaveId++)
{
try
{
// 建立TCP连接
using (TcpClient tcpClient = new TcpClient(tcpServerIp, tcpPort))
{
var factory = new ModbusFactory();
IModbusMaster master = factory.CreateMaster(tcpClient);
// 设置超时时间(避免阻塞过久,建议100ms)
master.Transport.ReadTimeout = 100;
// 发送核心探测报文(功能码03,起始地址0,读取1个寄存器)
ushort[] registers = master.ReadHoldingRegisters(
slaveAddress: slaveId, // 遍历的从站地址
startAddress: 0, // 起始寄存器0
numberOfPoints: 1); // 读取1个寄存器
// 能正常返回,说明从站存在
Console.WriteLine($"✅ 从站地址{slaveId}:存在");
}
}
catch
{
// 异常/超时,说明从站不存在
Console.WriteLine($"❌ 从站地址{slaveId}:不存在");
}
}
Console.WriteLine("探测完成!");
Console.ReadKey();
}
}
}
3. Modbus RTU探测代码
csharp
using System;
using System.IO.Ports;
using NModbus;
namespace ModbusSlaveDetect
{
class Program
{
// 串口名称(如COM1、COM3)
static string comPortName = "COM1";
// 波特率(根据设备配置,常见9600)
static int baudRate = 9600;
static void Main(string[] args)
{
Console.WriteLine("开始探测Modbus RTU从机地址(1-247)...");
// 初始化串口
using (SerialPort serialPort = new SerialPort(comPortName, baudRate))
{
serialPort.Open();
var factory = new ModbusFactory();
IModbusSerialMaster master = factory.CreateRtuMaster(serialPort);
// 设置超时时间
master.Transport.ReadTimeout = 100;
// 遍历1-247从站地址
for (int slaveId = 1; slaveId <= 247; slaveId++)
{
try
{
// 发送核心探测报文(功能码03,起始地址0,读取1个寄存器)
ushort[] registers = master.ReadHoldingRegisters(
slaveAddress: slaveId,
startAddress: 0,
numberOfPoints: 1);
// 正常返回=从站存在
Console.WriteLine($"✅ 从站地址{slaveId}:存在");
}
catch
{
// 异常/超时=从站不存在
Console.WriteLine($"❌ 从站地址{slaveId}:不存在");
}
}
}
Console.WriteLine("探测完成!");
Console.ReadKey();
}
}
}
三、关键注意事项(极简提示)
- 报文修改:只需替换从站地址对应的字节,其他字段固定不变,无需额外配置。
- 超时设置:建议设置100-300ms,平衡探测速度和准确性。
- 兼容性:功能码03是Modbus设备标配,无需担心设备不支持。
- 地址范围:仅遍历1-247,0为广播地址、248-255为保留地址,无需探测。