【从零搭建物联网智能充电桩系统】2、自定义二进制协议:设备为什么不用 JSON?

物联网设备为什么不用 JSON?本文详解本项目迭代了 3 版的 24 字节定长帧协议:字段如何布局、字节序如何处理、校验和怎么算,以及协议演进过程中的经验教训。


一、为什么用二进制协议(核心要点)

维度 JSON 方案 二进制方案(本项目)
传输体积 每个字段名+括号+逗号冗余 定长 24 字节,压缩 ~60%
解析成本 字符串解析 + 类型转换 按偏移量位运算直读
弱网适配 4G 流量按 KB 计费 适合 NB-IoT/4G 低频上报
可读性 人类可读 需 hex 工具,调试困难
协议演进 天然兼容加字段 需版本控制 + 两端同步升级

结论话术:物联网设备内存小、带宽贵、嵌入式开发简单化诉求强,二进制定长帧是行业主流做法(类比 OCPP、国标 GB/T 27930 均含二进制扩展帧)。


二、24 字节帧结构(v3.0,大端序)★必须背

偏移 长度 字段 Java 类型 说明
0 1 startTag byte 起始符,固定 0x76
1 2 chargeStationId short 充电桩 ID
3 8 chargeRecordId long 充电记录 ID(雪花算法)
11 4 chargingTime int 已充电时长(秒)
15 2 chargedEnergy short 已充电量(×100 存储)
17 2 currentPower short 当前功率
19 2 currentCost short 当前费用
21 1 chargingProgress byte 充电进度(%)
22 1 chargingState byte 充电状态
23 1 checksum byte 校验和(累加和取低 8 位)

协议演进史(v1.0 → v3.0):

  • v1.0:18 字节(初始版本,ChargeStatData 原始结构)
  • v2.0:chargeRecordId 从 4 字节扩展为 8 字节,22 字节
  • v3.0:chargingTime 从 2 字节扩展为 4 字节 int → 24 字节定长帧(当前版本)

类注释中保留了 v1.0/v2.0/v3.0 演进说明,可以主动讲这段演进,展现"协议是迭代出来的"。


三、核心代码(charge-protocol 模块)

复制代码
charge-protocol/src/main/java/com/yeseesion/SmartChargeStation/protocol/
├── mqtt/message/ChargeStatData.java      (228 行) ★真·二进制编解码
├── mqtt/message/ChargeCmdPayload.java    ( 38 行) 仅字段,无编解码
└── protobuf/ChargingCmdProtobuf.java     (579 行) protoc 生成,已废弃

ChargeStatData 关键方法

方法 作用
toBytes() 对象 → 24 字节数组(序列化)
fromBytes(byte[]) 字节数组 → 对象(反序列化)
fromHexString(String) MQTT 收到的 hex 字符串 → 对象
校验和计算 前 23 字节累加取低 8 位,与第 24 字节比对

位运算要点(动手实现时重点掌握):

java 复制代码
// short → 2 字节(大端序)
bytes[i]   = (byte) (value >> 8);
bytes[i+1] = (byte) (value & 0xFF);

// 2 字节 → short
short v = (short) ((bytes[i] & 0xFF) << 8 | (bytes[i+1] & 0xFF));

⚠️ 协议设计说明:上下行两套方案(值得关注)

  1. 下行指令为什么不用真·二进制? 这是有意的设计取舍 :下行指令由小程序发起 ,若用二进制帧,前端开发者无法直观看到发出的请求参数(如目标桩 ID、充电时长);改用「对象 JSON 序列化 → hex」后,抓包/日志里能直接看清字段内容,调试体验大幅提升 。代价是报文冗余、字段名(start_tag 等 snake_case)也进了 payload------但下行指令频率极低(用户手动点击),对带宽不敏感,用可读性换传输效率在这个方向是划算的
  2. chargingTime 截断不一致 :协议是 4 字节 int,但下游某处用 & 0xFFFF 截断成 2 字节 ------ 充电超过 65535 秒(约 18.2 小时)会溢出(这是真实 bug)。
  3. Protobuf 已退役ChargingCmdProtobuf.java 是 protoc 生成的死代码,前端链路已改 JSON 文本帧。

四、常见问题与解答

  1. 为什么起始符用 0x76 而不是 0x00?(避免与空字节混淆,便于抓包识别帧起点)
  2. 校验和用累加和,能发现哪些错误?(单比特翻转、数据错位能检测;不能防篡改,需要 HMAC/CRC16)
  3. 定长帧 vs 变长帧怎么选?(定长:解析简单、无需长度字段,适合固定采集项;变长:灵活但需 length 字段 + 粘包处理)
  4. 协议升级如何兼容老设备?(帧头加 version 字段,服务端按版本分发解析------当前未实现)
  5. chargedEnergy 为什么 ×100 存储?(浮点数不适合传输比较,转整型避免精度丢失------但要约定小数位)

小结

协议的 24 字节布局、大端序编解码与校验和逻辑已经讲完。这套协议是迭代了 3 版才稳定的,核心经验就一句:版本号从第一天就要留 。下一篇 【从零搭建物联网智能充电桩系统】3、MQTT 客户端 讲解设备数据接入云端的消息处理链路。

相关推荐
小叮当爱咖啡1 小时前
Day3.参数+Prompt三板斧
开发语言·python·prompt
菜冻鱼1 小时前
Python-pandas-索引与筛选
开发语言·笔记·python·numpy·pandas·学习方法
青 春 记 忆2 小时前
LeetCode 53. 最大子数组和|Python 解法详解
python·算法·leetcode
鹿角片ljp2 小时前
Java面试复盘(二):String不可变、字符串常量池与字符串拼接
开发语言·python
讲温控就好了2 小时前
光刻温控的产业链价值:从精度指标到半导体制造竞争力
python·制造
南极星10053 小时前
2026电赛E题有感
python·opencv·电赛
看浪的路人3 小时前
第3讲:代码补全引擎
开发语言·windows·python
想会飞的蒲公英3 小时前
PyTorch 学习率实战:从零理解衰减策略与调度器
人工智能·pytorch·python·深度学习·机器学习
灵析表格3 小时前
灵析表格手机号处理函数深度分析报告
前端·网络·json·wps·灵析表格·excel公式盒子