ESP32如何在Arduino IoT Cloud运行时通过AP修改WiFi SSID
基于Arduino IoT Cloud实现浏览器修改WiFi凭证的方案
问题背景
尝试结合WiFi.h、WebServer.h、EEPROM.h与ArduinoIoTCloud.h、Arduino_ConnectionHandler.h,实现无需重新上传固件即可修改WiFi凭证。但将WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS)移至setup()内后,无论是使用常量形式的凭证,还是从EEPROM读取的凭证(如WiFiConnectionHandler ArduinoIoTPreferredConnection(esid.c_str(), epass.c_str());)均无法正常工作,ESP会因IoT库无法找到SSID和密码而重启。此前在Blynk等库中通过AP配网+网页输入的方式实现过类似功能,现在需要在Arduino IoT Cloud环境下实现该需求。
错误原因
原代码中WiFiConnectionHandler是全局实例,生命周期覆盖整个程序运行过程;修改后将其放在setup()内声明为局部变量,setup()执行完毕后该实例会被销毁,后续ArduinoCloud.update()调用时依赖的连接对象已不存在,导致系统崩溃重启。
可行实现方案
核心思路
- 将
WiFiConnectionHandler声明为全局指针,在setup()内根据读取到的WiFi凭证动态初始化,保证其生命周期全局有效 - 实现SoftAP配网逻辑:当EEPROM中无有效WiFi凭证,或使用凭证连接WiFi失败时,启动ESP的SoftAP模式,搭建简易Web服务器,让用户通过浏览器输入新的WiFi SSID和密码,写入EEPROM后重启设备,加载新凭证连接Arduino IoT Cloud
完整代码示例
#include <ArduinoIoTCloud.h> #include <Arduino_ConnectionHandler.h> #include <WiFi.h> #include <WebServer.h> #include <EEPROM.h> // Arduino IoT Cloud 配置 const char DEVICE_LOGIN_NAME[] = "你的设备登录名"; const char DEVICE_KEY[] = "你的设备密钥"; // WiFi 凭证存储参数(EEPROM地址) #define EEPROM_SIZE 128 #define SSID_ADDR 0 #define PASS_ADDR 32 // 全局变量 float temperature; WiFiConnectionHandler *ArduinoIoTPreferredConnection = nullptr; WebServer apServer(80); // 函数声明 void initProperties(); void onTemperatureChange(); void readWiFiCredentials(String &ssid, String &pass); void writeWiFiCredentials(const String &ssid, const String &pass); void startSoftAP(); void handleRoot(); void handleSaveCredentials(); void initProperties() { ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME); ArduinoCloud.setSecretDeviceKey(DEVICE_KEY); ArduinoCloud.addProperty(temperature, READWRITE, ON_CHANGE, onTemperatureChange); } void onTemperatureChange() {} void setup() { Serial.begin(115200); delay(1000); // 初始化EEPROM EEPROM.begin(EEPROM_SIZE); // 读取存储的WiFi凭证 String storedSSID, storedPASS; readWiFiCredentials(storedSSID, storedPASS); // 尝试连接WiFi WiFi.begin(storedSSID.c_str(), storedPASS.c_str()); unsigned long startMillis = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startMillis < 10000) { delay(500); Serial.print("."); } if (WiFi.status() == WL_CONNECTED) { // WiFi连接成功,初始化IoT连接对象 ArduinoIoTPreferredConnection = new WiFiConnectionHandler(storedSSID.c_str(), storedPASS.c_str()); // 启动Arduino IoT Cloud连接 initProperties(); ArduinoCloud.begin(*ArduinoIoTPreferredConnection); setDebugMessageLevel(2); ArduinoCloud.printDebugInfo(); Serial.println("\nConnected to Arduino IoT Cloud"); } else { // WiFi连接失败,启动SoftAP配网 Serial.println("\nWiFi connection failed, starting SoftAP for configuration"); startSoftAP(); } } void loop() { if (WiFi.status() == WL_CONNECTED && ArduinoIoTPreferredConnection != nullptr) { ArduinoCloud.update(); } else { apServer.handleClient(); } } // 从EEPROM读取WiFi凭证 void readWiFiCredentials(String &ssid, String &pass) { ssid = ""; for (int i = SSID_ADDR; i < SSID_ADDR + 32; i++) { char c = EEPROM.read(i); if (c == '\0') break; ssid += c; } pass = ""; for (int i = PASS_ADDR; i < PASS_ADDR + 32; i++) { char c = EEPROM.read(i); if (c == '\0') break; pass += c; } } // 将WiFi凭证写入EEPROM void writeWiFiCredentials(const String &ssid, const String &pass) { // 清空SSID存储区 for (int i = SSID_ADDR; i < SSID_ADDR + 32; i++) { EEPROM.write(i, '\0'); } // 写入SSID for (int i = 0; i < ssid.length() && i < 32; i++) { EEPROM.write(SSID_ADDR + i, ssid[i]); } // 清空密码存储区 for (int i = PASS_ADDR; i < PASS_ADDR + 32; i++) { EEPROM.write(i, '\0'); } // 写入密码 for (int i = 0; i < pass.length() && i < 32; i++) { EEPROM.write(PASS_ADDR + i, pass[i]); } EEPROM.commit(); } // 启动SoftAP并配置WebServer void startSoftAP() { const char *apSSID = "ESP-IoT-Config"; const char *apPASS = "12345678"; WiFi.softAP(apSSID, apPASS); IPAddress apIP = WiFi.softAPIP(); Serial.printf("SoftAP started: %s, IP: %s\n", apSSID, apIP.toString().c_str()); // 配置WebServer路由 apServer.on("/", handleRoot); apServer.on("/save", handleSaveCredentials); apServer.begin(); } // 配网首页 void handleRoot() { String html = "<!DOCTYPE html><html><head><title>WiFi Config</title></head><body>"; html += "<h1>Configure WiFi for Arduino IoT Cloud</h1>"; html += "<form action='/save' method='POST'>"; html += "SSID: <input type='text' name='ssid' required><br>"; html += "Password: <input type='password' name='pass'><br>"; html += "<input type='submit' value='Save & Connect'>"; html += "</form></body></html>"; apServer.send(200, "text/html", html); } // 处理凭证保存请求 void handleSaveCredentials() { String ssid = apServer.arg("ssid"); String pass = apServer.arg("pass"); if (!ssid.isEmpty()) { writeWiFiCredentials(ssid, pass); apServer.send(200, "text/html", "Credentials saved! Rebooting..."); Serial.println("WiFi credentials saved, rebooting..."); delay(2000); ESP.restart(); } else { apServer.send(400, "text/html", "Invalid SSID!"); } }
关键说明
- 全局连接对象:使用
WiFiConnectionHandler *全局指针,在WiFi连接成功后动态创建实例,确保其生命周期覆盖整个程序运行过程 - EEPROM存储:将WiFi凭证存储在EEPROM中,重启后可直接读取使用,无需重新配置
- SoftAP配网:当WiFi连接失败时自动启动AP,用户通过浏览器访问AP的IP地址即可输入新的WiFi凭证,保存后设备自动重启并尝试连接新WiFi及Arduino IoT Cloud
内容的提问来源于stack exchange,提问作者Hayder Ismael
相关产品推荐
相关产品推荐

