安卓无需断开本地局域网向ESP32传输WiFi SSID及密码的实现方案
方案选型结论
几个你提到的方案适用性如下:
- WiFi Direct:不推荐。ESP32的WiFi P2P模式兼容性差,不同厂商安卓ROM对WiFi Direct的权限限制差异大,配网完成后切换STA工作模式的逻辑冗余,设备发现成功率低。
- 常规AutoConnect库方案:不符合需求。默认实现是让ESP32开启独立软AP,手机必须手动连接该AP才能打开配网页,会直接断开手机当前已连接的局域网。
- ESP-NOW + AP/STA共存配网:完全匹配需求。利用ESP32双模并发能力+ESP-NOW二层无连接传输特性,安卓手机全程不需要切换WiFi连接,不需要断开当前局域网就能完成配网,兼容性最好。
- BLE配网:备选方案。通过蓝牙通道传输配网信息,同样不需要断开WiFi,缺点是安卓端需要适配多版本蓝牙权限,部分低价位机型蓝牙广播兼容性一般。
ESP-NOW配网实现逻辑
核心原理是ESP-NOW是Espressif定义的二层短包传输协议,同无线信道下不需要设备完成AP关联就能互相收发数据,完全不影响手机当前的WiFi连接:
- ESP32上电初始状态
- 设置为
WIFI_AP_STA共存模式,开启无密码软AP,SSID固定为DHT11-Sensor-XXXX格式,XXXX是设备MAC地址后四位,方便识别,软AP固定工作在1信道 - 启动ESP-NOW监听,等待接收配网广播包,同时持续扫描周边WiFi
- 设置为
- 安卓端操作流程(全程不断开当前WiFi)
- 申请必要的WiFi状态权限后,调用系统WiFi扫描接口,识别SSID匹配的ESP32设备,确认其工作信道
- 构造固定格式的配网数据包,包含目标局域网SSID、密码、校验位,通过ESP-NOW协议向对应信道广播发送,整个过程不需要连接ESP32的软AP
- 等待ESP32返回配网结果回执
- 配网完成切换
- ESP32收到合法配网包后先返回确认回执,再尝试用收到的账号密码连接目标局域网
- 连接成功后自动关闭软AP、注销ESP-NOW监听,进入正常工作模式定时读取DHT11数据
- 连接失败则回到监听状态,返回失败回执等待重传
ESP32示例代码(Arduino框架)
依赖:提前安装Arduino ESP32 2.0+版本板包、DHT传感器驱动库
#include <esp_now.h> #include <WiFi.h> #include <DHT.h> // 硬件配置 #define DHT_DATA_PIN 4 #define DHT_MODEL DHT11 const char* AP_PREFIX = "DHT11-Sensor-"; const uint8_t BROADCAST_ADDR[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 配网数据结构,安卓端必须和该结构内存对齐 typedef struct { char wifi_ssid[32]; char wifi_pass[64]; uint8_t checksum; } WifiConfigMsg; WifiConfigMsg recv_config; DHT dht(DHT_DATA_PIN, DHT_MODEL); bool has_config = false; uint8_t calc_checksum(uint8_t* data, size_t len) { uint8_t res = 0; for(size_t i=0; i<len; i++) res += data[i]; return res; } void espnow_recv_cb(const uint8_t* mac_addr, const uint8_t* data, int data_len) { if(data_len != sizeof(WifiConfigMsg)) return; memcpy(&recv_config, data, sizeof(WifiConfigMsg)); // 校验数据完整性 uint8_t calc_sum = calc_checksum((uint8_t*)&recv_config, sizeof(WifiConfigMsg)-1); if(calc_sum != recv_config.checksum) return; has_config = true; } void setup() { Serial.begin(115200); dht.begin(); // 初始化AP+STA双模 WiFi.mode(WIFI_AP_STA); uint8_t mac[6]; WiFi.macAddress(mac); char ap_ssid[32]; snprintf(ap_ssid, sizeof(ap_ssid), "%s%02X%02X", AP_PREFIX, mac[4], mac[5]); WiFi.softAP(ap_ssid, NULL, 1, 0, 1); // 信道1,最大1连接,不影响性能 // 初始化ESP-NOW if(esp_now_init() != ESP_OK) { Serial.println("ESP-NOW init error, restart"); ESP.restart(); } esp_now_register_recv_cb(espnow_recv_cb); } void loop() { if(has_config) { WiFi.begin(recv_config.wifi_ssid, recv_config.wifi_pass); uint8_t retry = 0; while(WiFi.status() != WL_CONNECTED && retry < 20) { delay(500); retry++; } if(WiFi.status() == WL_CONNECTED) { // 配网成功,关闭配网相关服务 esp_now_deinit(); WiFi.softAPdisconnect(true); Serial.printf("WiFi connected, local IP: %s\n", WiFi.localIP().toString().c_str()); // 进入正常工作循环 while(1) { float temp = dht.readTemperature(); float humi = dht.readHumidity(); if(!isnan(temp) && !isnan(humi)) { Serial.printf("Temperature: %.1f℃, Humidity: %.1f%%\n", temp, humi); // 此处添加局域网数据上报逻辑 } delay(2000); } } else { // 配网失败,重置状态等待重传 has_config = false; WiFi.disconnect(); } } delay(100); }
安卓端实现注意事项
- 权限要求:安卓11及以下申请
ACCESS_FINE_LOCATION权限用于WiFi扫描,安卓12及以上补充申请NEARBY_WIFI_DEVICES权限,不需要ROOT、不需要位置权限常开 - 发送ESP-NOW包时需要先获取WiFi多播锁,避免系统过滤广播包,数据包结构必须和ESP32端定义的结构体完全对齐,避免内存错位导致解析失败
- 不需要手动切换WiFi连接,扫描到ESP32的软AP后仅需要读取其信道参数,不需要发起关联请求
内容的提问来源于stack exchange,提问作者Elias
相关产品推荐
相关产品推荐

