使用C#和NModbus库构建Modbus从站
using System;
using System.IO.Ports;
using NModbus;
using NModbus.Serial;
class ConfigurableModbusSlave
{
private static SerialPort port;
private static IModbusSlaveNetwork network;
private static ushort addrMin = 0; // 寄存器起始地址
private static ushort addrMax = 34; // 寄存器结束地址
static void Main()
{
// 初始化串口
port = new SerialPort
{
PortName = "COM3",
BaudRate = 9600,
Parity = Parity.None,
DataBits = 8,
StopBits = StopBits.One
};
// 创建Modbus组件
var factory = new ModbusFactory();
var adapter = new SerialPortAdapter(port);
network = factory.CreateSlaveNetwork(adapter);
// 设置从站实例
var slave = factory.CreateSlave(unitId: 1);
SetupDataStorage(slave);
network.AddSlave(slave);
network.ListenAsync();
Console.WriteLine($"Modbus从站已启动 (ID=1, 地址 {addrMin}-{addrMax}),按Enter退出");
Console.ReadLine();
// 资源清理
port.Close();
network.Dispose();
}
static void SetupDataStorage(IModbusSlave slave)
{
// 配置保持寄存器
slave.DataStore.HoldingRegisters.StorageOperationOccurred += (s, e) =>
Console.WriteLine($"[{DateTime.Now}] 寄存器 {e.StartingAddress} 操作: {e.Operation}");
InitRegisters(slave.DataStore.HoldingRegisters);
// 配置线圈
slave.DataStore.CoilDiscretes.StorageOperationOccurred += (s, e) =>
Console.WriteLine($"[{DateTime.Now}] 线圈 {e.StartingAddress} 操作: {e.Operation}");
InitCoils(slave.DataStore.CoilDiscretes);
}
static void InitRegisters(ISlaveDataStore<ushort> regs)
{
regs[0] = 2500; // 温度初始值
regs[1] = 1200; // 压力初始值
for (ushort i = 2; i <= addrMax; i++) regs[i] = 0;
}
static void InitCoils(ISlaveDataStore<bool> coils)
{
coils[0] = true; // 设备运行状态
for (ushort i = 1; i <= addrMax; i++) coils[i] = false;
}
}
核心功能说明
地址范围配置
通过修改addrMin和addrMax变量调整寄存器范围(例如设置为0-100)。支持通过外部参数动态加载。
数据存储管理
| 存储类型 | 地址范围 | 数据类型 | 功能码 |
|---|---|---|---|
| 保持寄存器 | 0x0000-0x0022 | 16位整数 | 0x03/0x06 |
| 离散输出线圈 | 0x0000-0x0022 | 布尔值 | 0x01/0x05 |
操作事件监控
通过StorageOperationOccurred事件记录数据变更。自动处理地址越界错误(如返回错误码03 03 02 00 0A)。
扩展功能实现
动态地址配置
// 从配置文件读取
var configStart = int.Parse(System.Configuration.ConfigurationManager.AppSettings["StartAddr"]);
var configEnd = int.Parse(System.Configuration.ConfigurationManager.AppSettings["EndAddr"]);
实时数据模拟
// 定时更新温度值
var timer = new System.Threading.Timer(_ =>
{
ushort temperature = (ushort)(2000 + new Random().Next(100));
slave.DataStore.HoldingRegisters[0] = temperature;
}, null, 0, 1000);
访问安全控制
// 验证从站ID
if (request.UnitId != 1)
{
SendErrorResponse(request, 0x07); // 返回ID不匹配错误
return;
}