当前位置:首页 > 物联网 > 智能应用
[导读]消费电子里“蓝牙+WiFi”不是两个无线功能堆一起,而是角色分工:BLE干配网和近距离控制,WiFi干长连接和云端数据。 常见翻车是把BLE当永久数据通道,结果配网完还一直广播、手机一切后台就掉线,WiFi大数据时BLE也跟着抖。正确模型是“BLE进场、WiFi接班、BLE退备”。


消费电子里“蓝牙+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也不会救——这两件事在架构里先定死,后面业务代码才好写。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭