Byte Message - Inter-chip 通信协议库

📋 协议概述

Inter-chip 协议是一个用于芯片间通信的二进制协议,支持:

数据包格式

短帧格式 (≤255 字节总长度):

Flag Len Cmd Payload Checksum
1B 1B 1B N B 1B

长帧格式 (>255 字节总长度):

Flag Len LenH Cmd Payload Checksum
1B 1B 1B 1B N B 1B

标志位定义

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
reserve Long Frame reserve Checksum reserve reserve reserve reserve

位功能说明:

  • Bit 4 (Checksum): 1=启用校验和, 0=禁用校验和
  • Bit 6 (Long Frame): 1=长帧格式, 0=短帧格式
  • 其他位: reserve,设为 0

支持的命令类型

  • 0xF8: 普通指令 - 常规设备间通信
  • 0x20: DFU 指令 - 设备固件升级

🚀 快速开始

安装

pubspec.yaml 中添加依赖:

dependencies:
  byte_message: ^1.2.0

然后运行:

dart pub get
# 或者对于 Flutter 项目
flutter pub get

基本使用(工厂函数)

使用工厂一次性完成第三层 → 第二层 → 第一层的组帧或解析。

import 'package:byte_message/byte_message.dart';

void main() {
  // Control Bus 示例:请求电量/充电状态
  final cbFactory = ControlBusFactory();
  final l1BatteryReq = cbFactory.encodeBatteryStatusReq();
  print('BatteryStatus Req L1 bytes: ${l1BatteryReq.map((b) => b.toRadixString(16).padLeft(2, '0')).toList()}');

  // 模拟设备返回 AckOK(payload 为第三层载荷,这里示意为 2 字节)
  final simulatedAck = InterChipEncoder().encode(
    InterChipPacket(cmd: InterChipCmds.ackOk, payload: const [0x64, 0x01]),
  );
  final batteryRes = cbFactory.decodeBatteryStatusRes(simulatedAck);
  print('BatteryStatus decoded: ${batteryRes.data}');

  // DFU 示例:开始升级
  final dfuFactory = DfuFactory();
  final l1StartReq = dfuFactory.encodeStartUpgradeReq();
  print('StartUpgrade Req L1 bytes: ${l1StartReq.map((b) => b.toRadixString(16).padLeft(2, '0')).toList()}');
  final simulatedDfuAck = InterChipEncoder().encode(
    InterChipPacket(cmd: InterChipCmds.ackOk, payload: const [0x01, 0x00]),
  );
  final startRes = dfuFactory.decodeStartUpgradeRes(simulatedDfuAck);
  print('StartUpgrade isOk: ${startRes.data?.isOk}');
}

byte_message

一个用于 inter-chip 协议编码和解码的 Dart 库,支持标准帧和长帧格式,提供完整的校验和处理功能。

特性

  • 完整的协议支持: 支持 inter-chip 协议的标准帧和长帧格式
  • 灵活的编码器: 支持自动和手动模式的标志位、长度和校验和设置
  • 强大的解码器: 支持单包和多包解码,包含完整的错误处理
  • 校验和验证: 支持 XOR 校验和的自动计算和验证
  • 类型安全: 使用强类型定义,避免运行时错误
  • 全面测试: 包含 45 个测试用例,覆盖所有功能场景

协议格式

标准帧格式

[Flag] [Len] [Cmd] [Payload...] [Checksum?]

长帧格式

[Flag] [LenL] [LenH] [Cmd] [Payload...] [Checksum?]

快速开始

安装

将以下内容添加到你的 pubspec.yaml 文件中:

dependencies:
  byte_message: ^1.2.0

基础使用

import 'package:byte_message/byte_message.dart';

void main() {
  // 创建编码器和解码器
  const encoder = InterChipEncoder();
  const decoder = InterChipDecoder();

  // 创建数据包
  final packet = InterChipPacket(
    flag: 0x00,
    len: 4, // 命令(1) + 负载(3)
    cmd: InterChipCmds.normal,
    payload: [0x01, 0x02, 0x03],
  );

  // 编码
  final encodedData = encoder.encode(packet);
  print('编码结果: ${encodedData}');

  // 解码
  final decodedPacket = decoder.decode(encodedData);
  if (decodedPacket != null) {
    print('解码成功: ${decodedPacket.payload}');
  }
}

📖 详细使用指南(严格分层,互不混用)

三层协议之间的关系与数据流:

  • 编码方向:Layer3(业务对象 → 业务字节) → 作为 Layer2 的 payload(子命令字节流) → 再作为 Layer1 的 payload(链路层帧)进行封装与发送。
  • 解码方向:Layer1(解帧得到载荷) → 交由 Layer2(按子命令解析二层字节) → 再交由 Layer3(还原为具体业务对象)。
  • 责任边界:各层互不依赖实现细节,彼此通过“字节载荷”衔接;你可以仅使用某一层的编解码,也可以组合三层形成完整链路。

示例总览:

  • 第一层协议 L1:包头

    L1.flag L1.en L1.lenH L1.cmd L1.payload L1.checksum
    u8 u8 u8 u8 u8n u8
  • 第二层协议:control bus

    L2.cmd L2.payload
    u8 u8n
  • 第二层协议:dfu

    L2.cmd L2.version L2.payload
    u8 u8 u8n
  • 第三层协议:业务内容(该层内容由具体业务定义,不固定长度)

    L3.content1 L3.content2 L3.content3
    u8[] u8[] u8[]

编码示意(两个独立示例):

  • ControlBus deviceStatusRequest:Layer3(无) → Layer2 输出 0x37 → Layer1 封装为帧(载荷含 0x37)。
  • DFU startUpgrade(ver=0x01):Layer3(无) → Layer2 输出 dfuCmd, 0x01 → Layer1 封装为帧(载荷含 dfu 二层字节)。

本库将协议按层次拆分为:

  • 第一层(Layer1,Inter-chip 帧):只负责帧头、指令码、长度、校验等链路层封装与解析,载荷视为“透明字节”。
  • 第二层(Layer2,Control Bus / DFU 子命令):只负责二层命令字与其二层载荷的编码与解析,不关心一层细节。
  • 第三层(Layer3,具体业务载荷):只负责具体业务的字段布局与字节编码,不关心一层或二层细节。

以下示例分别针对每一层进行“单独解耦”的编码与解码演示,不把层与层混在一起。

第一层(Layer1)Inter-chip 帧:只演示一层的编码与解码

import 'package:byte_message/byte_message.dart';

/// 示例仅演示一层:将一段“透明载荷字节”封装为 inter-chip 帧,并解码回透明载荷
void main() {
  final encoder = InterChipEncoder();
  final decoder = InterChipDecoder();

  // 一层载荷(透明字节,来源可以是任意上层):
  final transparentPayload = const [0xAA, 0xBB, 0xCC];

  // 一层编码:仅设置一层指令码和标志位,不涉及二层或三层对象
  final packet = InterChipPacket(
    flag: 0x10, // 启用校验和
    cmd: InterChipCmds.normal,
    payload: transparentPayload,
  );
  final encoded = encoder.encode(packet);
  print('Layer1 encoded bytes: ${encoded?.map((b) => b.toRadixString(16).padLeft(2, '0')).toList()}');

  // 一层解码:得到一层的指令与透明载荷
  final decoded = decoder.decode(encoded);
  if (decoded != null) {
    print('Layer1 decoded cmd: 0x${decoded.cmd.toRadixString(16)}');
    print('Layer1 decoded payload: ${decoded.payload}');
  }
}

第二层(Layer2)Control Bus 与 DFU:只演示二层的编码与解码

import 'package:byte_message/byte_message.dart';

/// 示例仅演示二层:将二层消息编码为“二层字节”,并从“二层字节”解码为消息对象
void main() {
  // Control Bus 二层编码
  final l2Cb = ControlBusMessage(cbCmd: CbCmd.deviceStatusRequest, cbPayload: const []);
  final cbBytes = ControlBusEncoder().encode(l2Cb); // 例如 [0x37]
  print('Layer2 ControlBus encoded: ${cbBytes.map((b) => b.toRadixString(16).padLeft(2, '0')).toList()}');

  // Control Bus 二层解码
  final cbMsg = ControlBusDecoder().decode(cbBytes);
  print('Layer2 ControlBus decoded cmd: 0x${cbMsg?.cbCmd.toRadixString(16)}, payload: ${cbMsg?.cbPayload}');

  // DFU 二层编码
  final l2Dfu = DfuMessage(dfuCmd: DfuCmd.startUpgrade, dfuVersion: 0x01, dfuPayload: const []);
  final dfuBytes = DfuEncoder().encode(l2Dfu); // 例如 [0x02, 0x01]
  print('Layer2 DFU encoded: ${dfuBytes.map((b) => b.toRadixString(16).padLeft(2, '0')).toList()}');

  // DFU 二层解码
  final dfuMsg = DfuDecoder().decode(dfuBytes);
  print('Layer2 DFU decoded cmd: 0x${dfuMsg?.dfuCmd.toRadixString(16)}, ver: ${dfuMsg?.dfuVersion}, payload: ${dfuMsg?.dfuPayload}');
}

第三层(Layer3)具体业务载荷:只演示三层的编码与解码

import 'package:byte_message/byte_message.dart';

/// 示例仅演示三层:将业务对象编码为“业务字节”,并从“业务字节”解码为业务对象
void main() {
  // Control Bus 三层业务:设备连接请求/应答
  final connReq = GetDeviceConnectionReq(protocolVersion: 0x02);
  final connReqBytes = connReq.encode(); // 例如 [0x02]
  print('Layer3 ConnectionReq encoded: ${connReqBytes}');

  // 设备连接应答三层解析(示例字节,实际长度与内容请参考协议文档)
  final connResBytes = List<int>.filled(28, 0x00);
  final connRes = GetDeviceConnectionRes.fromBytes(connResBytes);
  print('Layer3 ConnectionRes decoded -> model=${connRes.model}, fw=${connRes.firmwareVersion}, hw=${connRes.hardwareVersion}');

  // DFU 三层业务:写升级包
  final blob = DfuBlob(pageId: 1, blobId: 1, blobStart: 0, blobData: const [0xDE, 0xAD, 0xBE]);
  final writeReq = WriteUpgradeChunkReq(blob: blob);
  final writeReqBytes = writeReq.encode();
  print('Layer3 WriteUpgradeChunkReq encoded: ${writeReqBytes}');

  // DFU 三层业务:写升级包应答解析(u8 版本,u8 结果码)
  final writeRes = WriteUpgradeChunkRes.fromBytes(const [0x01, 0x00]);
  print('Layer3 WriteUpgradeChunkRes decoded -> dfuPkgVersion=${writeRes.dfuPkgVersion}, isOk=${writeRes.isOk}');
}

编码器配置

// 创建编码器
final encoder = InterChipEncoder();

// 编码短帧数据包
final shortPacket = InterChipPacket(
  flag: 0x00,  // 无校验和,短帧
  len: 4,
  cmd: InterChipCmds.normal,
  payload: [0x01, 0x02, 0x03],
);

final encodedShort = encoder.encode(shortPacket);

长帧处理

// 创建长帧数据包 (>255 字节)
final longPayload = List.generate(300, (i) => i % 256);
final longPacket = InterChipPacket(
  flag: 0x40,  // 长帧标志
  len: 0x2D,   // 低位字节 (301 & 0xFF)
  lenH: 0x01,  // 高位字节 (301 >> 8)
  cmd: InterChipCmds.dfu,
  payload: longPayload,
);

final encodedLong = encoder.encode(longPacket);

校验和处理

// 启用校验和的数据包
final packetWithChecksum = InterChipPacket(
  flag: 0x10,  // 启用校验和
  len: 4,
  cmd: InterChipCmds.normal,
  payload: [0x01, 0x02, 0x03],
);

// 编码时会自动计算并添加校验和
final encoded = encoder.encode(packetWithChecksum);

// 解码时会自动验证校验和
final decoded = decoder.decode(encoded!);
if (decoded != null) {
  print('校验和验证通过');
} else {
  print('校验和验证失败');
}

多数据包处理

// 批量处理多个数据包
final packets = <InterChipPacket>[
  InterChipPacket(flag: 0x00, len: 2, cmd: InterChipCmds.normal, payload: [0x01]),
  InterChipPacket(flag: 0x10, len: 3, cmd: InterChipCmds.dfu, payload: [0x02, 0x03]),
];

final encodedPackets = <List<int>>[];
for (final packet in packets) {
  final encoded = encoder.encode(packet);
  if (encoded != null) {
    encodedPackets.add(encoded);
  }
}

// 解码所有数据包
final decodedPackets = <InterChipPacket>[];
for (final encoded in encodedPackets) {
  final decoded = decoder.decode(encoded);
  if (decoded != null) {
    decodedPackets.add(decoded);
  }
}

错误处理

try {
  // 尝试编码无效数据包
  final invalidPacket = InterChipPacket(
    flag: 0x00,
    len: 1,
    cmd: InterChipCmds.normal,
    payload: List.generate(70000, (i) => i), // 超出最大长度
  );

  final encoded = encoder.encode(invalidPacket);
  if (encoded == null) {
    print('编码失败:数据包无效');
  }
} catch (e) {
  print('编码异常: $e');
}

// 解码损坏的数据
final corruptedData = [0x10, 0x04, 0xF8, 0x01]; // 数据不完整
final decoded = decoder.decode(corruptedData);
if (decoded == null) {
  print('解码失败:数据损坏或不完整');
}

API 参考

核心类

  • InterChipPacket: 数据包模型类
  • InterChipEncoder: 编码器类
  • InterChipDecoder: 解码器类
  • InterChipCmds: 命令类型枚举
  • PacketUtils: 工具类

配置类

  • InterChipFlags: 数据包标志位解析

示例

查看 example/usage_example.dart 文件获取完整的使用示例,包括:

  • 基础编码解码
  • 长帧处理
  • 校验和处理
  • 多包处理
  • 错误处理

更多 DFU 示例请参考以下文件:

  • example/dfu/get_device_info_factory_example.dart
  • example/dfu/start_upgrade_factory_example.dart
  • example/dfu/write_upgrade_chunk_factory_example.dart
  • example/dfu/finish_upgrade_factory_example.dart

DFU 使用示例

以下示例展示使用 DfuFactory 完成 DFU 相关流程的编码与解码。

获取设备信息(Get Device Info)

import 'package:byte_message/byte_message.dart';

void main() {
  final factory = DfuFactory();

  // 编码请求(DfuCmd=0x01)
  final req = factory.encodeGetDeviceInfoReq();

  // 模拟 AckOK 应答:第三层载荷应长度为 33 字节
  // 这里仅演示解码调用,实际应由设备返回真实字节序列
  final decoded = factory.decodeGetDeviceInfoRes(req); // 演示:通常传设备返回的 bytes
  if (decoded.data != null) {
    final info = decoded.data!;
    print('romVersion: ${info.romVersion}'); // 解析规则:忽略首字节,仅用后三字节为 MAJOR.MINOR.REVISION(REVISION 两位)
  }
}

开始升级(Start Upgrade)

import 'package:byte_message/byte_message.dart';

void main() {
  final factory = DfuFactory();

  // 编码请求(DfuCmd=0x02)
  final req = factory.encodeStartUpgradeReq();

  // 模拟 AckOK 应答(第三层载荷:u8 dfuPkgVersion, u8 dfuOpResult)
  final ackOk = InterChipEncoder().encode(
    InterChipPacket(cmd: InterChipCmds.ackOk, payload: const [0x01, 0x00]),
  );

  final decoded = factory.decodeStartUpgradeRes(ackOk);
  if (decoded.data != null) {
    print('StartUpgrade isOk: ${decoded.data!.isOk}');
  }
}

写升级包(Write Upgrade Chunk)

import 'package:byte_message/byte_message.dart';

void main() {
  final factory = DfuFactory();

  // 构造 DfuBlob(PageId/BlobId/BlobStart 均为 u16,大端;BlobData 为 u8[n])
  final blob = DfuBlob(
    pageId: 1,
    blobId: 1,
    blobStart: 0,
    blobData: const [0xDE, 0xAD, 0xBE, 0xEF],
  );

  // 编码请求(DfuCmd=0x05),第三层载荷为 DfuBlob 字节序列
  final req = factory.encodeWriteUpgradeChunkReq(blob: blob);

  // 模拟 AckOK 应答(第三层载荷:u8 dfuPkgVersion, u8 dfuOpResult)
  final ackOk = InterChipEncoder().encode(
    InterChipPacket(cmd: InterChipCmds.ackOk, payload: const [0x01, 0x00]),
  );

  final decoded = factory.decodeWriteUpgradeChunkRes(ackOk);
  if (decoded.data != null) {
    print('WriteUpgradeChunk isOk: ${decoded.data!.isOk}');
  }
}

完成升级(Finish Upgrade)

import 'package:byte_message/byte_message.dart';

void main() {
  final factory = DfuFactory();

  // 编码请求(DfuCmd=0x03)
  final req = factory.encodeFinishUpgradeReq();

  // 模拟 AckOK 应答(第三层载荷:u8 dfuPkgVersion, u8 dfuOpResult)
  final ackOk = InterChipEncoder().encode(
    InterChipPacket(cmd: InterChipCmds.ackOk, payload: const [0x01, 0x00]),
  );

  final decoded = factory.decodeFinishUpgradeRes(ackOk);
  if (decoded.data != null) {
    print('FinishUpgrade isOk: ${decoded.data!.isOk}');
  }
}

测试

运行所有测试:

dart test

运行特定测试:

dart test test/byte_message_test.dart
dart test test/encoder_test.dart
dart test test/decoder_test.dart
dart test test/integration_test.dart

贡献

欢迎提交问题和拉取请求!请确保:

  1. 所有测试通过
  2. 代码符合 Dart 风格指南
  3. 添加适当的文档注释

🧪 测试

本库包含完整的测试套件,确保所有功能的正确性和稳定性。

运行测试

# 运行所有测试
dart test

# 运行特定测试文件
dart test test/packet_models_test.dart
dart test test/encoder_test.dart
dart test test/decoder_test.dart

# 运行测试并生成覆盖率报告
dart test --coverage=coverage
dart run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib

测试覆盖范围

  • 单元测试: 所有核心类和方法
  • 集成测试: 编码器和解码器的完整流程
  • 边界测试: 最大/最小值和异常情况
  • 性能测试: 大数据量处理能力

🔍 调试工具

数据包分析

import 'package:byte_message/byte_message.dart';

// 分析原始数据包
final rawData = [0x10, 0x04, 0xF8, 0x01, 0x02, 0x03, 0x1E];
final analysis = PacketUtils.analyzePacket(rawData);

print('数据包分析结果:');
print('- 是否为长帧: ${analysis['isLongFrame']}');
print('- 校验和启用: ${analysis['checksumEnabled']}');
print('- 负载长度: ${analysis['payloadLength']}');
print('- 命令类型: ${analysis['command']}');

调试信息生成

// 生成详细的调试信息
final debugInfo = PacketUtils.generateDebugInfo(rawData);
print(debugInfo);

// 输出示例:
// Packet Debug Info:
// Raw Data: 10 04 F8 01 02 03 1E
// Flag: 0x10 (Checksum: ON, Long Frame: OFF)
// Length: 4 bytes
// Command: 0xF8 (NORMAL)
// Payload: 01 02 03
// Checksum: 0x1E (Valid)

格式化工具

// 格式化字节数组为十六进制字符串
final formatted = PacketUtils.formatBytes([0x10, 0x04, 0xF8], ':');
print(formatted); // "10:04:F8"

// 解析十六进制字符串为字节数组
final bytes = PacketUtils.parseHexString("10 04 F8");
print(bytes); // [16, 4, 248]

📊 性能特性

编码性能

  • 短帧编码: ~0.1ms (典型 10 字节负载)
  • 长帧编码: ~0.5ms (典型 1KB 负载)
  • 批量编码: 支持每秒处理 10,000+ 数据包

内存使用

  • 最小内存占用: 每个数据包 ~100 字节
  • 零拷贝优化: 大负载数据的高效处理
  • 垃圾回收友好: 最小化临时对象创建

兼容性

  • Dart SDK: >=3.0.0 <4.0.0
  • 平台支持: Flutter (iOS, Android, Web, Desktop), Dart VM
  • 依赖: 无外部依赖,纯 Dart 实现

👨‍💻 作者信息

作者: 蔡铨
创建日期: 2025 年 11 月 3 日
版本: 1.3.0


Made with ❤️ by 蔡铨

Libraries

byte_message
Inter-chip协议编码解码库