消费电子蓝牙与WiFi协同编程:实现智能设备的快速配网与数据传输
消费电子里“蓝牙+WiFi”不是两个无线功能堆一起,而是角色分工:BLE干配网和近距离控制,WiFi干长连接和云端数据。 常见翻车是把BLE当永久数据通道,结果配网完还一直广播、手机一切后台就掉线,WiFi大数据时BLE也跟着抖。正确模型是“BLE进场、WiFi接班、BLE退备”。
一、职责切分
BLE:设备发现、GATT配网、下发SSID/密码、状态回执、断网重配
WiFi:STA连路由器、MQTT/WebSocket/TCP业务数据、OTA、云端同步
切换点:WiFi拿到IP后,BLE停止广播或降为通知通道
配网阶段BLE是主通道,运行阶段BLE是备通道,别反着来。
二、BLE配网GATT结构
最简三组特征即可:
Service UUID: 0xFFA0
├─ 0xFFA1 Command (Write) 配网指令/触发
├─ 0xFFA2 Cred (Write) SSID+密码(加密载荷)
└─ 0xFFA3 Status (Notify) 连接结果/错误码
协议别用明文JSON裸跑,至少做LE Secure Connections+Bonding,密码字段AES加密后再写。
enum { PROV_IDLE, PROV_RECV, PROV_CONNECTING, PROV_OK, PROV_FAIL };
typedef struct {
uint8_t ssid[32];
uint8_t pwd[64];
uint8_t state;
uint8_t got_ssid, got_pwd;
} prov_t;
prov_t g_prov;
void gatt_write_cb(uint16_t handle, uint8_t*data, int len){
if(handle == CRED_SSID){
memcpy(g_prov.ssid, data, len);
g_prov.got_ssid = 1;
}else if(handle == CRED_PWD){
aes_dec(data, len, g_prov.pwd); // 解密后存
g_prov.got_pwd = 1;
}
if(g_prov.got_ssid && g_prov.got_pwd){
g_prov.state = PROV_RECV;
xTaskNotify(prov_task, 0, eNoAction); // 交主任务处理
}
}
⚠️ GATT回调里别直接调WiFi.begin,蓝牙协议栈ISR上下文里跑TCP栈容易死锁,写缓存+通知任务处理最稳。
三、配网状态机
void prov_task(void*p){
for(;;){
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(g_prov.state != PROV_RECV) continue;
g_prov.state = PROV_CONNECTING;
notify_status(0x01); // connecting
wifi_sta_connect(g_prov.ssid, g_prov.pwd);
int ret = wait_ip(15000);
if(ret == 0){
g_prov.state = PROV_OK;
notify_status(0x02); // ok
nvs_save_wifi(g_prov.ssid, g_prov.pwd);
ble_stop_adv(); // 配网完退场
mqtt_start();
}else{
g_prov.state = PROV_FAIL;
notify_status(0xFF); // fail
}
}
}
失败别一直重试不返回,设超时→Notify错误码→APP提示密码错/信号弱。配网成功存NVS,下次上电直接连,不再开BLE广播。
四、WiFi接管数据传输
BLE只发凭证,业务走WiFi:
周期上报:MQTT publish 到 dev/<sn>/telemetry
实时控制:WebSocket订阅指令通道
大文件/日志:HTTP分块上传
OTA:HTTPS拉固件,BLE只发“开始OTA”触发
void wifi_data_task(void*p){
while(wifi_connected()){
if(mqtt_need_pub()){
mqtt_pub(tele_topic, sensor_json(), QOS1);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
/* WiFi掉了:标记离线,BLE重配网模式可重新打开 */
ble_enter_recover_mode();
}
BLE重连只做“重新配网/诊断”,不抢业务数据通道。
五、双协议共存:单射频必须开coex
ESP32/C3/S3是单射频时分复用,BLE和WiFi不能同时发。menuconfig必须开:
Component config → Wireless Coexistence → Software controls WiFi/BT coexistence
调优经验:
配网阶段BLE优先,连上后改WiFi优先
BLE连接间隔配网期15~30ms,运行期可拉到100~200ms
别开持续扫描,scan window别拉满
WiFi省电模式开DTIM休眠,给BLE让时隙
双核芯片把WiFi栈和BLE栈绑不同核,减少互堵
硬件上天线区禁布金属件、电源纹波压住,否则共存参数再对也救不回来。
六、安全边界
不绑定的BLE别暴露写特征,任何人都能连
PoP密码/二维码校验,防附近误配
凭证AES-GCM或至少AES-128,别明文写特征
配网超时断开(如60s未完成自动断连)
WiFi密码存NVS但加密,别串口print
配网完关广播,别长期可被发现
七、落坑清单
配网成功还一直BLE广播→被附近手机反复连
GATT回调里直接WiFi.begin→协议栈死锁
密码明文写特征→抓包即泄
不绑Bonding→陌生人秒连设备
BLE持续扫描+WiFi大吞吐→两边都卡
coex没开→蓝牙断连WiFi正常
失败不Notify→APP干等无提示
NVS不存凭证→每次上电重配网
业务数据走BLE→吞吐撑不住
配网态不超时→设备卡在等待
八、落地顺序
定GATT服务:Command/Cred/Status
BLE只收SSID+密码,AES解密存缓存
单独prov任务调WiFi.begin,别放回调
拿到IP→Notify成功→停广播→起MQTT
凭证存NVS,后续自连
menuconfig开软件共存,配网后切WiFi优先
BLE降备通道:断网恢复/重配网用
Bonding+PoP+超时断开补齐安全
蓝牙WiFi协同的本质是“各管一段”:BLE负责把设备请上网,WiFi负责之后所有重活;切换点就是“拿到IP”这一刻,之前BLE主、之后BLE退。共存不是写两行代码就行,单射频芯片必须开coex、调连接间隔、别让两条协议同时满载。消费电子产品体验差,十有八九不是连不上WiFi,而是配完网BLE没退场、或者WiFi掉了BLE也不会救——这两件事在架构里先定死,后面业务代码才好写。





