跳到主要内容

自定义帧

自定义帧用于处理厂家私有协议。常见格式是:帧头、长度、业务载荷、校验。

AA 55 02 10 34
帧头 长度 载荷

上面这帧里,真正的业务载荷只有:

10 34

Zeus 的 LengthHeaderFrameCodec 负责自动补帧头、写长度、算校验、处理半包和粘包。你的业务代码只需要关心载荷。

什么时候用它

设备手册里的格式是否适合自定义帧
AA 55 + 长度 + 数据 + 校验适合
7E + 长度 + 命令 + 数据 + CRC适合,配置帧头和校验
Modbus RTU / TCP不建议,直接用 Modbus
纯文本协议,例如一行一个命令不一定需要,直接按文本收发也可以

最小请求-响应

await using var app = ZeusHost.Create(builder => builder.AddVirtualChannel("bus"));

await using var session = app.CreateFrameSession(
"bus",
new FrameLayout([0xAA, 0x55], FrameLengthKind.UInt8, FrameChecksumKind.None));

await app.StartAsync();

var reply = await session.RequestAsync(new byte[] { 0x10, 0x34 });

这段代码的意思是:

代码含义
AddVirtualChannel("bus")先用虚拟通道联调,不接真实设备
CreateFrameSession("bus", layout)bus 通道上创建一个帧会话
FrameLayout([0xAA, 0x55], UInt8, None)帧头是 AA 55,长度 1 字节,没有校验
RequestAsync(new byte[] { 0x10, 0x34 })发送业务载荷 10 34,等待下一帧完整应答

如果使用默认布局 AA 55 + 1 字节长度 + 无校验,可以简写:

await using var session = app.CreateFrameSession("bus");

载荷和完整帧的区别

你传给 RequestAsync 的是载荷:

10 34

Zeus 写到线上的是完整帧:

AA 55 02 10 34

如果开启 CRC,线上还会多出校验字节。收到数据时反过来:Zeus 先验证完整帧,再只把载荷返回给你。

等待匹配应答

简单设备通常“发一帧,下一帧就是响应”。但真实现场可能会遇到:

  • 设备主动上报状态。
  • 上一次请求的响应迟到了。
  • 同一总线上有其它设备帧。
  • 响应里带序号、命令字或地址。

这时不要直接等下一帧,而是给 RequestAsync 一个匹配器:

var sequence = (byte)0x34;

var reply = await session.RequestAsync(
new byte[] { 0x10, sequence },
response => response.Length >= 2
&& response.Span[0] == 0x90
&& response.Span[1] == sequence);

这段代码逐行看:

代码含义
sequence = 0x34本次请求序号
new byte[] { 0x10, sequence }发送载荷:命令 10 + 序号 34
response.Length >= 2响应至少要有“命令 + 序号”两个字节
response.Span[0] == 0x90响应命令必须是 90,例如协议规定请求 10 对应响应 90
response.Span[1] == sequence响应序号必须等于本次请求序号 34

只有这种响应会被返回:

90 34 01
响应命令 序号 数据

这些完整帧会被跳过,继续等待:

90 33 01 // 命令对,但序号不对,可能是旧响应
91 34 01 // 序号对,但命令不对,可能是别的命令响应
20 01 // 主动上报帧,不是本次请求响应

被跳过的完整帧不会丢掉,会先留在会话收件箱里。

只发送不等应答

广播、心跳或不需要响应的命令使用 SendAsync

await session.SendAsync(new byte[] { 0x20, 0x01 });

帧选项

类型含义
FrameLengthKind.UInt8长度字段 1 字节,载荷最长 255 字节
UInt16LittleEndian长度字段 2 字节,小端
UInt16BigEndian长度字段 2 字节,大端
FrameChecksumKind.None无校验
FrameChecksumKind.Xor81 字节异或校验
FrameChecksumKind.Sum81 字节累加和校验
FrameChecksumKind.Crc16ModbusModbus CRC16,小端输出

校验覆盖长度域和载荷,不覆盖帧头。

常见问题

现象原因处理
超时未收到完整应答帧头、长度、校验或设备没有回包用串口工具抓原始字节,对照设备手册
超时未收到匹配应答收到了帧,但匹配器一直返回 false打印响应载荷,核对序号、命令字或地址
载荷超过 255 字节使用了 1 字节长度字段改用 UInt16LittleEndianUInt16BigEndian
一次收到多帧正常粘包LengthHeaderFrameCodec 会连续拆出完整帧
一帧分多次收到正常半包编解码器会缓存,直到完整帧到齐

下一步:如果你的设备是标准 Modbus,直接看 Modbus