欢迎光临
我们一直在努力

ESP32 初次开机自动配置 WiFi

1. 简介

很多物联网设备需要连接家庭 WiFi,但无法预知用户的网络名称和密码。本教程将教你如何编写一个 ESP32 程序,实现:

  • 首次开机:自动进入配网模式,创建一个名为 ESP32_Config 的热点,用户通过手机/电脑连接后打开网页输入 WiFi 信息。

  • 保存信息:将 WiFi 信息保存到 ESP32 的 NVS(非易失性存储)中,断电不丢失。

  • 后续开机:自动读取保存的 WiFi 信息并连接,如果连接失败则重新进入配网模式。

通过这种方式,用户只需配置一次,之后设备就能自动联网。

2. 准备材料

  • 硬件:ESP32 开发板、USB 数据线。

  • 软件:Arduino IDE,浏览器(用于配置页面)。

  • 可选:LED (验证连接状态,ESP32 板载 LED 通常已集成)。

3. 代码解析

以下是完整的代码(稍后解释各部分功能):

#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>

// 配置参数
const char* AP_SSID = "ESP32_Config"; // 配置模式下的热点名称
const char* AP_PASSWORD = ""; // 热点密码(空表示开放)
const int DNS_PORT = 53; // DNS 服务器端口
const int WEB_PORT = 80; // Web 服务器端口
const unsigned long CONNECT_TIMEOUT = 30000; // WiFi 连接超时时间(毫秒)
const unsigned long AP_TIMEOUT = 300000; // AP 模式超时时间(毫秒,5分钟),超过无配置则重启

// 全局对象
WebServer server(WEB_PORT);
DNSServer dnsServer;
Preferences preferences;

// 标志位:是否处于配网模式
bool configMode = false;
unsigned long apStartTime = 0;

// 用户 LED 引脚(可根据需要修改)
const int ledPin = 2; // 大多数 ESP32 开发板板载 LED 在 GPIO2

// 读取保存的 WiFi 凭据
bool loadWiFiCredentials(String &ssid, String &password) {
preferences.begin("wifi", false); // 命名空间 "wifi",读写模式
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
preferences.end();
return (ssid.length() > 0);
}

// 保存 WiFi 凭据
void saveWiFiCredentials(const String &ssid, const String &password) {
preferences.begin("wifi", false);
preferences.putString("ssid", ssid);
preferences.putString("password", password);
preferences.end();
}

// 尝试连接 WiFi
bool connectToWiFi(const String &ssid, const String &password) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);

unsigned long startAttempt = millis();
while (WiFi.status() != WL_CONNECTED && millis() – startAttempt < CONNECT_TIMEOUT) {
delay(500);
Serial.print(".");
}
Serial.println();

if (WiFi.status() == WL_CONNECTED) {
Serial.println("Connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
return true;
} else {
Serial.println("Connection failed.");
return false;
}
}

// 启动 AP 模式并开启 DNS + Web 服务器
void startConfigMode() {
configMode = true;
apStartTime = millis();

WiFi.mode(WIFI_AP);
WiFi.softAP(AP_SSID, AP_PASSWORD);
IPAddress apIP = WiFi.softAPIP();
Serial.println("AP mode started");
Serial.print("AP IP address: ");
Serial.println(apIP);

// 启动 DNS 服务器,将所有域名请求指向 AP 的 IP
dnsServer.start(DNS_PORT, "*", apIP);

// 配置 Web 服务器路由
server.on("/", handleRoot);
server.on("/configure", HTTP_POST, handleConfigure);
server.onNotFound(handleNotFound);

server.begin();
Serial.println("HTTP server started");
}

// 处理根路径:显示配置页面
void handleRoot() {
String html = "<!DOCTYPE html><html>";
html += "<head><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\">";
html += "<title>WiFi Config</title></head><body>";
html += "<h2>ESP32 WiFi Configuration</h2>";
html += "<form method=\\"POST\\" action=\\"/configure\\">";
html += "SSID:<br><input type=\\"text\\" name=\\"ssid\\" placeholder=\\"WiFi SSID\\"><br>";
html += "Password:<br><input type=\\"password\\" name=\\"password\\" placeholder=\\"Password\\"><br><br>";
html += "<input type=\\"submit\\" value=\\"Save and Restart\\">";
html += "</form></body></html>";
server.send(200, "text/html", html);
}

// 处理配置表单提交
void handleConfigure() {
if (server.hasArg("ssid") && server.hasArg("password")) {
String ssid = server.arg("ssid");
String password = server.arg("password");

if (ssid.length() > 0) {
saveWiFiCredentials(ssid, password);
server.send(200, "text/html", "<h2>Configuration saved. Restarting…</h2>");
delay(1000);
ESP.restart();
} else {
server.send(400, "text/html", "<h2>SSID cannot be empty</h2>");
}
} else {
server.send(400, "text/html", "<h2>Missing parameters</h2>");
}
}

// 处理未找到的路径(用于 Android/iPhone 的浏览器探测)
void handleNotFound() {
server.send(204, "text/plain", ""); // 204 No Content 让手机不弹出错误
}

void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\\nESP32 WiFi Config Example");

pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

// 尝试加载已保存的 WiFi 信息
String savedSSID, savedPassword;
bool hasCredentials = loadWiFiCredentials(savedSSID, savedPassword);

if (hasCredentials) {
Serial.println("Found saved WiFi credentials.");
if (connectToWiFi(savedSSID, savedPassword)) {
// 连接成功,执行用户主程序(此处为 LED 闪烁示例)
Serial.println("Starting user program…");
// 在主循环中执行用户逻辑(见 loop)
return; // 直接退出 setup,进入 loop
} else {
Serial.println("Saved WiFi connection failed.");
}
} else {
Serial.println("No saved WiFi credentials.");
}

// 没有凭据或连接失败,启动配网模式
startConfigMode();
}

void loop() {
if (configMode) {
// 处理 DNS 和 Web 请求
dnsServer.processNextRequest();
server.handleClient();

// 超时检查:若 AP 模式运行超过指定时间且无配置,则重启尝试重新连接
if (millis() – apStartTime > AP_TIMEOUT) {
Serial.println("AP timeout, restarting…");
ESP.restart();
}
} else {
// 正常模式:用户程序
// 示例:LED 闪烁,表示已连接 WiFi
digitalWrite(ledPin, !digitalRead(ledPin));
delay(1000);
Serial.println("Wifi连接成功,进入用户程序.");
}
}

关键代码解释

头文件与全局变量

  • WiFi.h:提供 WiFi 连接功能。

  • WebServer.h:用于建立 HTTP 服务器,处理配置页面。

  • DNSServer.h:实现 DNS 劫持,使所有域名请求指向 ESP32 的 IP。

  • Preferences.h:用于在 NVS 中保存 WiFi 信息(类似键值对存储)。

参数配置

  • AP_SSID 和 AP_PASSWORD:配置模式下的热点名称和密码(留空表示开放)。

  • CONNECT_TIMEOUT:连接 WiFi 的超时时间。

  • AP_TIMEOUT:AP 模式最长运行时间,避免一直占用。

Preferences 存储

  • preferences.begin("wifi", false):打开命名空间 wifi(可自定义),第二个参数 false 表示读写。

  • putString / getString:存储和读取字符串。

连接逻辑

  • setup() 中先尝试读取保存的 WiFi,如果能读到则尝试连接。

  • 连接成功则进入 loop() 的用户程序;失败则启动 AP 模式。

  • AP 模式下启动 DNS 和 Web 服务器,用户通过浏览器提交配置后保存并重启。

DNS 劫持

  • dnsServer.start(DNS_PORT, "*", apIP):将所有域名请求解析到 ESP32 的 AP IP(192.168.4.1)。

  • 这样用户连接热点后,无论访问什么网址(如 www.baidu.com)都会跳转到 ESP32 的配置页面,提升体验。

超时重启

  • AP 模式下记录开始时间 apStartTime,若超过 AP_TIMEOUT 仍未收到配置,则自动重启,再次尝试连接已保存的 WiFi(如果有)。

用户程序占位

  • 在 loop() 的 else 分支中写你自己的代码,示例中是一个 LED 闪烁,代表 WiFi 已连接。

注意:不同 ESP32 开发板的板载 LED 可能连接在不同 GPIO,常见的是 GPIO2 或 GPIO5。若不确定,可查阅开发板原理图或先测试。

6. 操作步骤

6.1 上传程序

  • 用 USB 线连接 ESP32 和电脑。

  • 在 Arduino IDE 中选择正确的开发板(如 ESP32 Dev Module)和端口(如 COM3)。

  • 将上述代码复制到 IDE 中,点击上传按钮(→)。

  • 等待编译和上传完成,打开串口监视器(波特率 115200),观察输出。

  • 6.2 首次配置

  • 如果 ESP32 没有保存过 WiFi 信息,它会在串口打印 未找到保存的 WiFi 凭据。 并启动 AP 模式,创建名为 ESP32_Config 的开放热点。

  • 用手机或电脑连接该热点(无需密码)。

  • 连接成功后,通常会自动弹出配置页面(若未弹出,使用浏览器访问 http://192.168.4.1)。

  • 在页面中输入你家 WiFi 的 SSID 和密码,点击“保存并重启”。

  • 保存成功界面如下:

  • ESP32 保存信息后自动重启,尝试连接你输入的 WiFi。

  • 6.3 正常使用

    • 连接成功后,串口会显示获得的 IP 地址,LED 开始每秒闪烁一次。

      且串口输出信息如下:

    • 此后每次开机,ESP32 都会自动连接上次保存的 WiFi,无需再次配置。

    • 如果 WiFi 连接失败(如密码更改、路由器关闭),程序会在超时后重新进入 AP 模式,等待新配置。

    7. 常见问题与解决

    Q1: 连接 ESP32 热点后,没有自动弹出配置页面怎么办?

    • 确保手机/电脑关闭了移动数据,并且已经连接到 ESP32_Config 热点。

    • 手动在浏览器输入 http://192.168.4.1 访问。

    Q2: 配置页面提交后,ESP32 没有重启或连接不上?

    • 检查输入的 WiFi 密码是否正确。

    • 查看串口监视器输出:如果保存成功,会显示 配置已保存,设备即将重启…,然后重启。如果 WiFi 名称或密码错误,重启后仍会进入 AP 模式,可再次配置。

    Q3: 如何修改配置热点的名称或密码?

    • 修改代码开头的 AP_SSID 和 AP_PASSWORD 即可。

    • 若希望 AP 模式有密码,可设置 AP_PASSWORD 为字符串,如 "12345678"(注意 WiFi 密码至少 8 位)。

    Q4: 如何清除保存的 WiFi 信息?

    • 可以通过串口发送命令(需自己扩展)或重新上传一个空白程序来清除 NVS。

    • 快速方法:在 Arduino IDE 中,选择 工具 → ESP32 Sketch Data Upload 下方的 Erase Flash,擦除整个 Flash 后再上传程序。

    Q5: 我想在用户程序中添加更多功能,比如读取传感器、控制继电器,应该怎么改?

    • 在 loop() 的 else 分支中(即 configMode == false 时)编写你的主逻辑即可。

    • 注意:不要使用 delay() 过长,以免影响 WiFi 维持连接。建议用 millis() 定时或改用多任务(FreeRTOS)。

    8. 扩展思考

    • 多网络支持:可以修改程序,保存多个 WiFi 信息,按优先级尝试连接。

    • Web 页面美化:可添加 CSS 样式,使配置界面更美观。

    • 安全考虑:如果 AP 模式需要密码,务必设置复杂密码,防止他人误连。

    • 远程配置:若需要远程修改 WiFi,可配合 MQTT 或 BLE 实现。

    赞(0)
    未经允许不得转载:171主机测评 » ESP32 初次开机自动配置 WiFi
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址