4、应用层协议设计:命令集定义、状态上报、心跳保活与地址分配
好,咱们进入应用层协议设计这一块。说实话,这是整个通信协议里最贴近业务逻辑的部分。你想想看,物理层和链路层搞定了数据怎么传,但传的是什么、怎么理解、怎么响应,全得靠应用层来定。我个人习惯把这一层叫做“窗帘听得懂的语言”。
4.1 命令集定义:开/关/停/调光/百分比
先说说命令集。窗帘电机最基础的动作就那几个:开、关、停。但现在的智能窗帘,基本都带调光功能——说白了就是控制开合程度,或者调节百叶的角度。
我一般把命令设计成固定长度的帧结构。举个例子:
帧头(1字节) + 地址(2字节) + 命令码(1字节) + 参数(1字节) + 校验(1字节) + 帧尾(1字节)
命令码我习惯这样定义:
| 命令码 | 含义 | 参数说明 |
|---|---|---|
| 0x01 | 开 | 无参数,或0x00 |
| 0x02 | 关 | 无参数,或0x00 |
| 0x03 | 停 | 无参数,或0x00 |
| 0x04 | 调光 | 0x00~0x64 对应 0%~100% |
| 0x05 | 百分比设置 | 0x00~0x64 对应 0%~100% |
这里有个细节我想提一下。调光和百分比设置,看起来很像对吧?但我在项目中遇到过一个问题:调光命令通常带渐变效果,电机慢慢走到目标位置;而百分比设置是直接跳转。所以我把它们分成了两个命令码,避免混淆。
4.2 状态上报机制
命令发出去了,电机执行了,但你怎么知道它执行到位了?这就得靠状态上报。
我习惯把状态上报分成两种:
- 主动上报:电机执行完命令后,自动回一条状态帧。比如“当前开度80%”、“电机正在运行中”。
- 查询上报:主机主动发查询命令,电机回复当前状态。这个在调试时特别有用。
状态帧的结构我一般这样设计:
帧头(1字节) + 地址(2字节) + 状态码(1字节) + 当前开度(1字节) + 运行状态(1字节) + 校验(1字节) + 帧尾(1字节)
运行状态字段我习惯用位标志:
| 位 | 含义 |
|---|---|
| bit0 | 0=停止,1=运行中 |
| bit1 | 0=正常,1=故障 |
| bit2 | 0=非堵转,1=堵转 |
| bit3~bit7 | 预留 |
嗯,这里要注意。状态上报的频率不能太高。我曾经见过一个项目,电机每100ms就上报一次状态,结果总线被撑爆了。我建议:主动上报只在状态变化时触发,或者至少间隔500ms以上。
4.3 心跳包与保活机制
这个机制说白了就是“你还活着吗?”和“我还活着”。
为什么需要心跳?因为无线通信不可靠。电机可能掉线、断电、或者被干扰。主机需要知道哪些设备在线,哪些失联了。
我一般这样设计:
- 心跳间隔: 建议5~30秒。太频繁浪费带宽,太慢响应不及时。
- 心跳内容: 一个简短的帧,只包含地址和心跳标志。不需要带状态数据。
- 超时判断: 主机连续3~5次没收到心跳,就判定设备离线。
举个例子,心跳帧可以简单到只有4个字节:
0xAA + 地址高字节 + 地址低字节 + 0x55
你想想看,如果每个心跳都带一大堆状态数据,那总线压力多大?心跳就是“报个到”,别搞复杂了。
4.4 多设备地址分配策略
一个窗帘电机系统,少则几个,多则几十上百个电机。怎么给它们分配地址?
我见过几种做法:
- 硬件拨码开关: 每个电机上有个小开关,手动拨地址。简单可靠,但安装维护麻烦。
- 软件自动分配: 主机广播一个“分配地址”命令,电机按顺序领取。方便,但需要保证唯一性。
- MAC地址绑定: 每个电机出厂带唯一ID,主机通过ID来映射逻辑地址。灵活,但需要数据库支持。
我个人比较推荐第二种,软件自动分配。具体流程是这样的:
1. 主机广播“地址分配开始”命令
2. 所有未分配地址的电机进入待分配状态
3. 主机发送“分配地址”命令,携带一个地址值
4. 第一个响应的电机领取该地址
5. 重复步骤3~4,直到所有电机分配完毕
6. 主机广播“地址分配结束”命令
这里有个坑。如果多个电机同时响应,会发生冲突。我一般会在响应前加一个随机延时,比如1~100ms,这样能大大降低冲突概率。
另外,地址分配完成后,最好让电机把地址保存到非易失性存储器里。这样断电重启后,地址不会丢失。我遇到过有人图省事,地址只存在RAM里,结果一断电全乱了,重新分配一遍,那叫一个折腾。
好了,应用层协议这块就聊到这儿。命令集、状态上报、心跳保活、地址分配,这四个东西搞清楚了,你的窗帘电机通信协议基本就成型了。下一章咱们聊聊具体的软件集成实现,到时候我会拿一个实际项目来拆解。