CSoft.Protocol.Modbus
11.0.1
dotnet add package CSoft.Protocol.Modbus --version 11.0.1
NuGet\Install-Package CSoft.Protocol.Modbus -Version 11.0.1
<PackageReference Include="CSoft.Protocol.Modbus" Version="11.0.1" />
<PackageVersion Include="CSoft.Protocol.Modbus" Version="11.0.1" />
<PackageReference Include="CSoft.Protocol.Modbus" />
paket add CSoft.Protocol.Modbus --version 11.0.1
#r "nuget: CSoft.Protocol.Modbus, 11.0.1"
#:package CSoft.Protocol.Modbus@11.0.1
#addin nuget:?package=CSoft.Protocol.Modbus&version=11.0.1
#tool nuget:?package=CSoft.Protocol.Modbus&version=11.0.1
Modbus 协议库
CSoft.Protocol.Modbus 提供 Modbus 主站和从站,支持 RTU 和 Modbus TCP 报文、寄存器与线圈读写,以及基于 [CHProtocol] 的模型映射。
安装与兼容性
dotnet add package CSoft.Protocol.Modbus
11.0.0 新增 net11.0 程序集,同时提供 netstandard2.0、net8.0、net9.0 和 net10.0 程序集。新部署可使用 .NET 10 或 .NET 11,.NET 8 和 .NET 9 目标供现有应用兼容使用。 .NET 支持周期
主站快速开始
下面的示例连接到本机的 Modbus TCP 从站,读取单元标识 1 的第一个保持寄存器。可以使用后面的 TCP 从站示例配对运行。
using System;
using Modbus;
var master = ModBusMaster.UseTcp("127.0.0.1", 1502)
.Protocol(ModbusType.TCP)
.Timeout(3000)
.Build();
try
{
await master.OpenAsync();
var registers = await master.ReadHoldingRegistersAsync(0x01, start: 0, count: 1);
Console.WriteLine(registers[0]);
}
finally
{
await master.CloseAsync();
}
使用串口时,将主站构造替换为下面的写法。默认串口参数是 9600 波特、8 数据位、无校验、1 停止位。
var master = ModBusMaster.UseSerial("COM2", baudRate: 9600).Build();
UseTcp、UseSerial 和 UseNamedPipe 选择物理链路。报文格式默认是 RTU,即使使用 TCP 链路也是如此。设备使用带 MBAP 头的 Modbus TCP 报文时,指定 .Protocol(ModbusType.TCP)。也可以通过 new ModBusMaster(physicalPort, ModbusType.TCP) 使用已有的 IPhysicalPort。
主站的读写方法接受 byte 站号,也接受十进制字符串 "1"、"01" 和带 0x 前缀的十六进制字符串 "0x01"。start、register、coil 和 [CHProtocol] 中的地址是从 0 开始的协议地址。如果设备手册将首个保持寄存器标为 40001,通常传入 0,具体以设备地址表为准。
功能码
| 功能码 | 主站方法 | 数据区 |
|---|---|---|
| FC01 | ReadCoilsAsync |
读线圈,返回 bool[] |
| FC02 | ReadDiscreteInputsAsync |
读离散量输入,返回 bool[] |
| FC03 | ReadHoldingRegistersAsync |
读保持寄存器,返回 ushort[] |
| FC04 | ReadInputRegistersAsync |
读输入寄存器,返回 ushort[] |
| FC05 | WriteCoilAsync |
写单个线圈 |
| FC06 | WriteRegisterAsync |
写单个保持寄存器 |
| FC15 | WriteCoilsAsync |
写多个线圈 |
| FC16 | WriteRegistersAsync |
写多个保持寄存器 |
批量操作超出单帧上限时会自动拆分。单帧最多读取 125 个寄存器或 2000 个位,写入 123 个寄存器或 1968 个线圈。
模型映射
用 [CHProtocol] 标注属性的起始寄存器。GetAsync<T> 使用 FC03 读取保持寄存器,并将值填入模型。SetAsync<T> 使用 FC16 写入模型中所有非 null 的映射属性,数值类型的默认值也会写入。
using Modbus;
public class SensorData
{
[CHProtocol(1)]
public float Temperature { get; set; }
[CHProtocol(3)]
public ushort Humidity { get; set; }
[CHProtocol(5)]
public float Pressure { get; set; }
[CHProtocol(7, count: 4)]
public string DeviceName { get; set; } = string.Empty;
[CHProtocol(11)]
public bool Running { get; set; }
}
在主站连接打开后读写模型。
var data = await master.GetAsync<SensorData>(0x01);
await master.SetAsync(0x01, new SensorData
{
Temperature = 25.5f,
Humidity = 60,
Pressure = 101.3f,
DeviceName = "PUMP-A",
Running = true
});
读写时会合并连续的寄存器,并按单帧上限拆分。读取可用 master.MaxMergeGap 跨过小间隙,前提是设备允许读取间隙里的地址。默认值 0 只合并连续块。也可以用 BlockList.From<SensorData>(maxMergeGap: 2) 生成块列表,再传给 GetAsync(address, blocks)。无块参数的非泛型 GetAsync(address) 使用 master.BlockInfos。
数据类型
| C# 类型 | 寄存器数 | 说明 |
|---|---|---|
ushort、short |
1 | 16 位整数 |
uint、int、float |
2 | 32 位值 |
double |
4 | 64 位浮点数 |
bool |
1 | 写入 0 或 1,读取时非零为 true |
string |
N | 用 [CHProtocol(address, count: N)] 声明长度 |
字序与字符串
主站用 ReqWordOrder 设置写入值的字序,用 RspWordOrder 设置读取值的字序。从站通过 ModBusSlaveOptions.WordOrder 配置模型映射。默认均为 ABCD。
| WordOrder | 32 位值 0x12345678 的线上字节 |
|---|---|
ABCD |
12 34 56 78 |
DCBA |
78 56 34 12 |
CDAB |
56 78 12 34 |
BADC |
34 12 78 56 |
手动构建 BlockList 时,可通过 Channel.IsHighByteBefore 为单个通道覆盖读取字序。true 使用 ABCD,false 使用 DCBA,null 使用主站的 RspWordOrder。旧版主站配置 IsHighByteBefore_Req 和 IsHighByteBefore_Rsp 映射到对应字序。IsHighByteBefore_MBAP 控制 TCP 报文头字段的字节序,标准 Modbus TCP 使用默认值 true。
字符串默认用 ASCII,可通过主站的 StringEncoding 和从站的 ModBusSlaveOptions.StringEncoding 配置。N 个寄存器能存放 2 × N 个编码后的字节。写入时不足部分补 0x00,超长会抛出 InvalidOperationException。读取时去掉尾部的空字符。
从站快速开始
从站提供 HoldingRegisters、InputRegisters、Coils 和 DiscreteInputs 四个数据区。主站可以读写保持寄存器和线圈,读取输入寄存器和离散量输入。应用可以直接访问这四个数组。下面的示例在本机启动 TCP 从站,响应会发回请求来源客户端。
using System;
using CommBuilder;
using Modbus;
var server = Comm.TopServer()
.UseTcpServer("127.0.0.1", 1502)
.WithParserFactory(ModBusSlave.CreateParser)
.Build();
using var slave = new ModBusSlave(server, new ModBusSlaveOptions
{
Address = 0x01,
ModbusType = ModbusType.TCP
});
slave.HoldingRegisters[0] = 0x1234;
slave.WriteReceived += write =>
Console.WriteLine($"收到 FC{write.FunctionCode:X2} 写入,起始地址 {write.StartAddress}");
try
{
await slave.StartAsync();
Console.WriteLine("从站已启动,按 Enter 停止。");
Console.ReadLine();
}
finally
{
await slave.StopAsync();
}
使用串口时,可用 new ModBusSlave(new Communication.Bus.PhysicalPort.SerialPort("COM3", 9600)) 创建 RTU 从站,并使用同样的 StartAsync 和 StopAsync 管理连接。
对于进入请求处理阶段的单播帧,功能码不受支持、地址越界或数据非法时,从站会返回 Modbus 异常响应。RTU 地址 0 用于广播,有效的广播写请求会执行且不返回响应。WriteReceived 在成功写入后触发,包括广播写入。
绑定模型与自定义传输
BindModel 将模型属性映射到保持寄存器,并立即写入初始值。主站通过 FC06 或 FC16 更新保持寄存器后,从站会同步回模型。应用直接修改模型时,调用 SyncModelToRegisters() 发布新值。应用直接修改保持寄存器后,可调用 SyncRegistersToModel() 更新模型。
var model = new SensorData { Temperature = 25.5f, DeviceName = "PUMP-A" };
slave.BindModel(model);
model.Temperature = 30f;
slave.SyncModelToRegisters();
自定义传输可以用 new ModBusSlave(options) 创建从站核心,向 HandleRequest(frame) 传入完整报文。返回值是响应报文,或表示无需响应的 null。
分帧、超时与取消
RTU 分帧根据功能码和字节数推导长度,并校验 CRC。推导长度不匹配时,会尝试在候选长度中寻找 CRC 正确的帧。Modbus TCP 按 MBAP 长度域分帧。完整帧进入响应校验时,主站还会检查站号、功能码以及适用的事务 ID、数据长度或写回显。
| 异常 | 含义 |
|---|---|
ModbusSlaveException |
从站返回协议异常响应,可读取 ExceptionCode 和 DeviceAddress |
ModbusFrameException |
响应的结构或字段与请求不匹配 |
ModbusCrcException |
响应校验阶段检测到 RTU CRC 不符 |
ModbusTimeoutException |
请求超时,未收到可供处理的响应 |
OperationCanceledException |
调用者取消了等待 |
前四种异常位于 Modbus.Exceptions。损坏的 RTU 帧可能在分帧阶段被丢弃,此时主站可能收到超时异常。持续噪声超过分帧缓冲阈值时,分帧器会丢弃旧数据并保留一段尾部供下次解析。
CancellationToken 在提交请求前取消时可阻止提交。请求进入底层队列后,令牌只取消当前调用的等待,已经提交的请求仍可能发出并写入设备。
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
var data = await master.GetAsync<SensorData>(0x01, cts.Token);
主站提供 IsConnect 状态,以及 OnConnect、OnDisconnect、OnSentData、OnReceivedData 事件。
构建与测试
从源码构建需要 .NET 11 SDK。运行全部测试还需要安装 .NET 10 运行时。
dotnet build Modbus.sln -c Release
dotnet test Test/Test.csproj -c Release --no-build
Release 构建会在 Modbus/bin/Release 生成 NuGet 包。测试分别在 .NET 10 和 .NET 11 上运行。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- CSoft.Protocol (>= 11.0.3)
-
net10.0
- CSoft.Protocol (>= 11.0.3)
-
net11.0
- CSoft.Protocol (>= 11.0.3)
-
net8.0
- CSoft.Protocol (>= 11.0.3)
-
net9.0
- CSoft.Protocol (>= 11.0.3)
NuGet packages (7)
Showing the top 5 NuGet packages that depend on CSoft.Protocol.Modbus:
| Package | Downloads |
|---|---|
|
CSoft.Protocol.FlueGasAnalyzer
杭州泽天春来烟气分析仪 Modbus RTU v6.3 协议 |
|
|
CSoft.Protocol.TPF146D
泽天温压流146D通讯协议 |
|
|
CSoft.Protocol.TPF113B
泽天温压流113B通讯协议 |
|
|
CSoft.Protocol.DustAnalyzerSampling
泽天抽取粉尘仪通讯协议 |
|
|
CSoft.Protocol.DustAnalyzerSquare
泽天方型粉尘仪通讯协议 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 11.0.1 | 57 | 11/13/2026 | |
| 11.0.0 | 54 | 11/13/2026 | |
| 10.100.0 | 108 | 10/29/2026 | |
| 10.2.1 | 232 | 8/21/2026 | |
| 10.2.0 | 169 | 5/28/2026 | |
| 10.1.1 | 152 | 5/19/2026 | |
| 10.1.0 | 152 | 5/11/2026 | |
| 10.0.10 | 162 | 3/31/2026 | |
| 10.0.9 | 155 | 3/30/2026 | |
| 10.0.8 | 293 | 1/14/2026 | |
| 10.0.7 | 230 | 1/14/2026 | |
| 10.0.6 | 228 | 1/13/2026 | |
| 10.0.5 | 219 | 1/13/2026 | |
| 10.0.4 | 224 | 1/13/2026 | |
| 10.0.3 | 229 | 1/9/2026 | |
| 10.0.2 | 245 | 1/8/2026 | |
| 10.0.1 | 198 | 1/8/2026 | |
| 10.0.0 | 415 | 11/12/2025 | |
| 9.3.2 | 387 | 8/5/2025 | |
| 9.3.1 | 401 | 7/29/2025 |
升级 CSoft.Protocol 至 11.0.3,修复通信依赖的程序集版本不匹配。