3、通信协议设计:物理层选型(Wi-Fi/BLE/Zigbee)、应用层协议设计(JSON/Protobuf)、心跳与重连机制
通信协议设计,说白了就是给窗帘电机定规矩——怎么说话、说什么话、说错了怎么办。我做了这么多年嵌入式,见过太多项目在通信上栽跟头。有的选错物理层,有的协议太啰嗦,有的断线了再也连不上。今天咱们就把这三个坑一个一个填平。
3.1 物理层选型:Wi-Fi / BLE / Zigbee
先聊物理层。你想想看,窗帘电机装在窗户边上,离路由器可能隔着两堵墙,旁边还有微波炉、蓝牙音箱在干扰。选什么无线技术,直接决定了产品能不能稳定工作。
核心原则:没有最好的协议,只有最合适的场景。Wi-Fi适合直连云端,BLE适合手机近场控制,Zigbee适合组网联动。
3.1.1 Wi-Fi:适合智能家居单品
Wi-Fi的好处是直接连路由器,用户不用买网关。我做过一个项目,客户要求「手机App直接控制窗帘,不要额外设备」。那没得选,只能用Wi-Fi。
但Wi-Fi有个大问题——功耗高。窗帘电机如果是电池供电的,Wi-Fi模块待机电流动辄几十毫安,几天就没电了。所以Wi-Fi方案一般只用于有220V供电的电动窗帘。
另外,Wi-Fi的穿墙能力其实一般。2.4GHz频段穿一堵墙还行,两堵墙就开始丢包。我建议如果家里面积大,最好用Mesh Wi-Fi或者加个中继。
选型建议:如果产品是插电的、需要远程控制、用户不想买网关,选Wi-Fi。推荐乐鑫ESP32或瑞昱RTL8710,生态成熟,SDK好使。
3.1.2 BLE:适合电池供电、手机直连
BLE(低功耗蓝牙)是另一个热门选择。它的功耗极低,一颗纽扣电池能撑一年。而且现在手机都自带蓝牙,用户不需要额外硬件。
我在一个智能窗帘项目中用过Nordic的nRF52832,待机电流只有几微安。用户靠近窗帘时,手机App自动弹出控制界面,体验很流畅。
但BLE的缺点是距离短(一般10米以内),而且不能直接连互联网。如果你想远程控制,还得加一个蓝牙网关,或者让手机做中转。嗯,这里要注意:BLE的广播包容易被干扰,尤其是在商场、写字楼这种蓝牙设备密集的地方。
避坑指南:我曾经在一个项目中用BLE做窗帘控制,结果用户反映「有时候拉一半就停了」。查了半天,发现是手机蓝牙和窗帘电机之间的连接被其他蓝牙设备干扰了。后来我们加了重传机制和信道跳频,问题才解决。
3.1.3 Zigbee:适合全屋智能组网
Zigbee是专门为物联网设计的协议,支持自组网、多跳路由。一个Zigbee网络可以挂几百个设备,而且每个设备都能当中继,信号覆盖范围很广。
如果你在做全屋智能方案,比如窗帘、灯光、传感器联动,Zigbee是首选。它的功耗比Wi-Fi低,但比BLE高一点。不过Zigbee需要网关,用户得多花一笔钱。
我个人的习惯是:如果客户要求「稳定、低功耗、多设备联动」,我会推荐Zigbee。比如涂鸦的Zigbee模组,价格已经降到十几块钱,性价比很高。
| 参数 | Wi-Fi | BLE | Zigbee |
|---|---|---|---|
| 功耗 | 高(~100mA) | 极低(~10μA) | 低(~30μA) |
| 传输距离 | 30-100米 | 10米 | 10-100米(可中继) |
| 是否需要网关 | 不需要 | 不需要(手机直连) | 需要 |
| 组网能力 | 弱(星型) | 弱(点对点) | 强(Mesh) |
| 典型场景 | 远程控制 | 近场控制 | 全屋联动 |
3.2 应用层协议设计:JSON vs Protobuf
物理层选好了,接下来要定「说话的内容和格式」。说白了,就是电机和服务器之间传什么数据、怎么编码。
3.2.1 JSON:简单直观,适合调试
JSON是文本协议,人类可读,调试起来非常方便。我刚开始做物联网时,所有协议都用JSON。比如控制窗帘打开50%:
{
"cmd": "set_position",
"params": {
"target": 50,
"speed": 30
},
"seq": 1234
}
JSON的好处是灵活,字段可以随意增减。但坏处也很明显——体积大。上面这条消息有80多个字节,而实际有效数据只有几个数字。对于Wi-Fi来说还好,但如果是BLE或者NB-IoT这种窄带宽场景,JSON就太浪费了。
我的经验:在产品原型阶段,我建议先用JSON。调试方便,改字段不用重新编译。等产品稳定了,再考虑换成更高效的协议。
3.2.2 Protobuf:高效紧凑,适合量产
Protobuf是Google出的二进制序列化协议。同样的控制命令,用Protobuf编码后只有十几个字节:
message MotorCommand {
int32 cmd = 1; // 1: set_position
int32 target = 2; // 0-100
int32 speed = 3; // 0-100
int32 seq = 4; // 序列号
}
编码后的二进制数据:08 01 10 32 18 1E 20 D2 09(仅9个字节)。
Protobuf的优点是体积小、解析快。但缺点是需要定义.proto文件,两端都要生成代码,改字段要重新编译。而且调试时看不到明文,得用工具转码。
我个人习惯是:如果产品已经量产,或者设备数量超过1000台,我会用Protobuf。省流量、省带宽、省电。
注意:Protobuf的字段编号不能随意改。一旦发布,旧字段编号就废弃了,否则会导致解析错误。我曾经在升级协议时忘了这茬,结果旧设备全部离线,被运维骂了一下午。
3.3 心跳与重连机制
通信协议设计好了,但网络是不稳定的。Wi-Fi会断、蓝牙会掉、服务器会重启。如果没有心跳和重连机制,窗帘电机可能「失联」好几天。
3.3.1 心跳:确认对方还活着
心跳就是设备定期给服务器发一个「我还活着」的消息。服务器收到后回复一个ACK。如果连续几次没收到回复,就认为连接断了。
心跳间隔怎么定?太频繁浪费流量,太稀疏反应迟钝。我一般这样设置:
- Wi-Fi设备:30秒一次心跳。Wi-Fi功耗高,但带宽大,30秒一次可以接受。
- BLE设备:5秒一次心跳。BLE连接容易断开,需要快速检测。
- Zigbee设备:60秒一次心跳。Zigbee网络稳定,心跳可以慢一点。
心跳消息的内容很简单:
{
"type": "heartbeat",
"device_id": "curtain_001",
"timestamp": 1700000000
}
服务器收到后,回复一个空ACK即可。
优化技巧:心跳消息可以携带一些状态信息,比如电池电量、信号强度。这样既保活,又顺便上报了状态,一举两得。
3.3.2 重连:断线后自动恢复
心跳检测到断线后,设备要自动重连。重连不是简单的「再连一次」,而是一套策略:
- 立即重连:第一次断线后,马上尝试重连。因为可能是临时抖动。
- 指数退避:如果连续重连失败,间隔时间逐渐增加。比如第一次等1秒,第二次等2秒,第三次等4秒...最多等60秒。
- 最大重试次数:如果重试了10次还是连不上,就进入深度休眠,等用户手动唤醒。
我曾经在一个项目中,设备断线后疯狂重连,把服务器打挂了。后来加了指数退避,服务器压力瞬间降下来。
// 伪代码:重连策略
int retry_count = 0;
int max_retry = 10;
int base_delay = 1000; // 1秒
while (retry_count < max_retry) {
if (connect() == SUCCESS) {
break;
}
retry_count++;
int delay = base_delay * (1 << retry_count); // 指数退避
if (delay > 60000) delay = 60000; // 最多60秒
sleep(delay);
}
// 如果还是连不上,进入休眠
if (retry_count >= max_retry) {
enter_deep_sleep();
}
避坑指南:重连时一定要清理旧的连接资源。我见过一个案例,设备每次重连都新建一个socket,旧的socket没关闭,最后内存泄漏导致死机。重连前先close(),再connect(),这个顺序不能乱。
3.3.3 状态同步:重连后恢复现场
重连成功后,设备要主动和服务器同步状态。比如窗帘当前的位置、电机的工作模式。服务器也要把离线期间下发的命令补发给设备。
我建议在重连后做一次「全量同步」:设备上报所有状态,服务器下发所有待执行的命令。这样双方状态就一致了。
嗯,这里有个细节:如果设备在离线期间被人手动拉过窗帘,位置变了,但服务器不知道。重连后设备上报的位置才是真实的。所以一定要以设备上报为准,服务器不能覆盖。
好了,通信协议设计这块就聊到这儿。物理层选型决定了产品的「骨架」,应用层协议决定了「血肉」,心跳重连则是「免疫系统」。三者缺一不可。下一章咱们聊聊电机控制的底层驱动,那才是真正让窗帘动起来的关键。