263ca4d7-cb7b-8060-a669-e9846a21bf#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
// 创建对象
WebServer server(80);
DNSServer dnsServer;
Preferences preferences;
// 配置参数
const char* ap_ssid = "ESP32-Config"; // 配网热点名称
const char* ap_password = ""; // 热点密码,留空为开放网络
const byte DNS_PORT = 53;
// 存储WiFi信息
String saved_ssid = "";
String saved_password = "";
bool wifi_configured = false;
// HTML页面
const char* config_html = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>ESP32 WiFi配置</title>
<style>
body { font-family: Arial; margin: 0; padding: 20px; background: #f0f0f0; }
.container { max-width: 400px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #333; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; color: #555; }
input, select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; box-sizing: border-box; }
button { width: 100%; padding: 12px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; }
button:hover { background: #0056b3; }
.scan-btn { background: #28a745; margin-bottom: 10px; }
.scan-btn:hover { background: #218838; }
.wifi-list { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; border-radius: 5px; }
.wifi-item { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; }
.wifi-item:hover { background: #f8f9fa; }
.wifi-item:last-child { border-bottom: none; }
.signal-strength { float: right; color: #666; }
.status { text-align: center; margin: 10px 0; padding: 10px; border-radius: 5px; }
.success { background: #d4edda; color: #155724; }
.error { background: #f8d7da; color: #721c24; }
.info { background: #d1ecf1; color: #0c5460; }
</style>
</head>
<body>
<div class='container'>
<h1>ESP32 WiFi配置</h1>
<div id='status' class='status info'>
点击扫描按钮搜索附近的WiFi网络
</div>
<button class='scan-btn' onclick='scanWiFi()'>扫描WiFi网络</button>
<form id='wifiForm' onsubmit='return submitForm()'>
<div class='form-group'>
<label>WiFi网络:</label>
<select id='ssid' name='ssid' required>
<option value=''>请选择WiFi网络...</option>
</select>
</div>
<div class='form-group'>
<label>密码:</label>
<input type='password' id='password' name='password' placeholder='输入WiFi密码'>
</div>
<button type='submit'>连接WiFi</button>
</form>
<div id='wifiList' class='wifi-list' style='display:none;'></div>
</div>
<script>
function scanWiFi() {
document.getElementById('status').innerHTML = '正在扫描WiFi网络...';
document.getElementById('status').className = 'status info';
fetch('/scan')
.then(response => response.json())
.then(data => {
if(data.networks && data.networks.length > 0) {
updateWiFiList(data.networks);
document.getElementById('status').innerHTML = '找到 ' + data.networks.length + ' 个WiFi网络';
document.getElementById('status').className = 'status success';
} else {
document.getElementById('status').innerHTML = '未找到WiFi网络';
document.getElementById('status').className = 'status error';
}
})
.catch(error => {
document.getElementById('status').innerHTML = '扫描失败: ' + error;
document.getElementById('status').className = 'status error';
});
}
function updateWiFiList(networks) {
const select = document.getElementById('ssid');
select.innerHTML = '<option value="">请选择WiFi网络...</option>';
networks.forEach(network => {
const option = document.createElement('option');
option.value = network.ssid;
option.textContent = network.ssid + ' (' + network.rssi + 'dBm)' + (network.encryption ? ' 🔒' : '');
select.appendChild(option);
});
}
function submitForm() {
const ssid = document.getElementById('ssid').value;
const password = document.getElementById('password').value;
if(!ssid) {
alert('请选择WiFi网络');
return false;
}
document.getElementById('status').innerHTML = '正在连接到 ' + ssid + '...';
document.getElementById('status').className = 'status info';
const formData = new FormData();
formData.append('ssid', ssid);
formData.append('password', password);
fetch('/connect', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
if(data === 'OK') {
document.getElementById('status').innerHTML = '连接成功!设备将重启...';
document.getElementById('status').className = 'status success';
setTimeout(() => {
alert('WiFi配置成功!设备正在重启连接到新网络。');
}, 2000);
} else {
document.getElementById('status').innerHTML = '连接失败: ' + data;
document.getElementById('status').className = 'status error';
}
})
.catch(error => {
document.getElementById('status').innerHTML = '请求失败: ' + error;
document.getElementById('status').className = 'status error';
});
return false;
}
// 页面加载时自动扫描
window.onload = function() {
scanWiFi();
};
</script>
</body>
</html>
)";
void setup() {
Serial.begin(115200);
Serial.println("启动ESP32 WiFi配网程序...");
// 初始化Preferences
preferences.begin("wifi-config", false);
// 读取保存的WiFi信息
saved_ssid = preferences.getString("ssid", "");
saved_password = preferences.getString("password", "");
if(saved_ssid.length() > 0) {
Serial.println("找到保存的WiFi配置,尝试连接: " + saved_ssid);
if(connectToWiFi(saved_ssid, saved_password)) {
wifi_configured = true;
Serial.println("WiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
return;
} else {
Serial.println("保存的WiFi连接失败,启动配网模式");
}
}
// 启动配网模式
startConfigMode();
}
bool connectToWiFi(String ssid, String password) {
WiFi.begin(ssid.c_str(), password.c_str());
int attempts = 0;
while(WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
Serial.println();
return WiFi.status() == WL_CONNECTED;
}
void startConfigMode() {
Serial.println("启动配网模式...");
// 设置为AP模式
WiFi.mode(WIFI_AP);
WiFi.softAP(ap_ssid, ap_password);
Serial.print("配网热点已启动: ");
Serial.println(ap_ssid);
Serial.print("IP地址: ");
Serial.println(WiFi.softAPIP());
// 启动DNS服务器(强制门户)
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
// 设置Web服务器路由
server.on("/", HTTP_GET, handleRoot);
server.on("/scan", HTTP_GET, handleScan);
server.on("/connect", HTTP_POST, handleConnect);
server.onNotFound(handleRoot); // 所有未找到的请求都重定向到根页面
server.begin();
Serial.println("配网服务器已启动");
Serial.println("请连接WiFi热点 '" + String(ap_ssid) + "' 并访问 192.168.4.1");
}
void handleRoot() {
server.send(200, "text/html", config_html);
}
void handleScan() {
Serial.println("开始扫描WiFi网络...");
int networkCount = WiFi.scanNetworks();
String json = "{\"networks\":[";
if(networkCount > 0) {
for(int i = 0; i < networkCount; i++) {
if(i > 0) json += ",";
json += "{";
json += "\"ssid\":\"" + WiFi.SSID(i) + "\",";
json += "\"rssi\":" + String(WiFi.RSSI(i)) + ",";
json += "\"encryption\":" + String(WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
json += "}";
}
}
json += "]}";
Serial.println("找到 " + String(networkCount) + " 个网络");
server.send(200, "application/json", json);
}
void handleConnect() {
String ssid = server.arg("ssid");
String password = server.arg("password");
Serial.println("尝试连接WiFi: " + ssid);
if(ssid.length() == 0) {
server.send(400, "text/plain", "SSID不能为空");
return;
}
// 尝试连接WiFi
WiFi.mode(WIFI_STA);
if(connectToWiFi(ssid, password)) {
// 连接成功,保存配置
preferences.putString("ssid", ssid);
preferences.putString("password", password);
Serial.println("WiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
server.send(200, "text/plain", "OK");
// 延迟后重启,让浏览器接收到响应
delay(2000);
ESP.restart();
} else {
Serial.println("WiFi连接失败");
server.send(400, "text/plain", "连接失败,请检查密码");
// 重新启动AP模式
delay(1000);
startConfigMode();
}
}
void loop() {
if(!wifi_configured) {
dnsServer.processNextRequest();
server.handleClient();
} else {
// WiFi已配置,执行您的主要程序逻辑
// 这里可以添加您的其他功能代码
delay(1000);
// 检查WiFi连接状态
if(WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi连接断开,重新启动配网模式");
wifi_configured = false;
startConfigMode();
}
}
}
主要修改和新增功能:
2. 新增功能
3. 使用说明
重启设备:配网页面中点击"清除配置"按钮,或配网时长按BOOT键3秒
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
// 创建对象
WebServer server(80);
DNSServer dnsServer;
Preferences preferences;
// 配置参数
const char* ap_ssid = "ESP32-Config"; // 配网热点名称
const char* ap_password = ""; // 热点密码,留空为开放网络
const byte DNS_PORT = 53;
const int BOOT_PIN = 0; // BOOT按键引脚
// 存储WiFi信息
String saved_ssid = "";
String saved_password = "";
bool wifi_configured = false;
bool force_config_mode = false; // 强制配网模式标志
// HTML页面
const char* config_html = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>ESP32 WiFi配置</title>
<style>
body { font-family: Arial; margin: 0; padding: 20px; background: #f0f0f0; }
.container { max-width: 400px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #333; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; color: #555; }
input, select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; box-sizing: border-box; }
button { width: 100%; padding: 12px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; font-size: 16px; }
button:hover { background: #0056b3; }
.scan-btn { background: #28a745; margin-bottom: 10px; }
.scan-btn:hover { background: #218838; }
.clear-btn { background: #dc3545; margin-top: 10px; }
.clear-btn:hover { background: #c82333; }
.wifi-list { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; border-radius: 5px; }
.wifi-item { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; }
.wifi-item:hover { background: #f8f9fa; }
.wifi-item:last-child { border-bottom: none; }
.signal-strength { float: right; color: #666; }
.status { text-align: center; margin: 10px 0; padding: 10px; border-radius: 5px; }
.success { background: #d4edda; color: #155724; }
.error { background: #f8d7da; color: #721c24; }
.info { background: #d1ecf1; color: #0c5460; }
.warning { background: #fff3cd; color: #856404; }
</style>
</head>
<body>
<div class='container'>
<h1>ESP32 WiFi配置</h1>
<div id='status' class='status info'>
点击扫描按钮搜索附近的WiFi网络
</div>
<button class='scan-btn' onclick='scanWiFi()'>扫描WiFi网络</button>
<form id='wifiForm' onsubmit='return submitForm()'>
<div class='form-group'>
<label>WiFi网络:</label>
<select id='ssid' name='ssid' required>
<option value=''>请选择WiFi网络...</option>
</select>
</div>
<div class='form-group'>
<label>密码:</label>
<input type='password' id='password' name='password' placeholder='输入WiFi密码'>
</div>
<button type='submit'>连接WiFi</button>
</form>
<button class='clear-btn' onclick='clearConfig()'>清除保存的WiFi配置</button>
<div id='wifiList' class='wifi-list' style='display:none;'></div>
</div>
<script>
function scanWiFi() {
document.getElementById('status').innerHTML = '正在扫描WiFi网络...';
document.getElementById('status').className = 'status info';
fetch('/scan')
.then(response => response.json())
.then(data => {
if(data.networks && data.networks.length > 0) {
updateWiFiList(data.networks);
document.getElementById('status').innerHTML = '找到 ' + data.networks.length + ' 个WiFi网络';
document.getElementById('status').className = 'status success';
} else {
document.getElementById('status').innerHTML = '未找到WiFi网络';
document.getElementById('status').className = 'status error';
}
})
.catch(error => {
document.getElementById('status').innerHTML = '扫描失败: ' + error;
document.getElementById('status').className = 'status error';
});
}
function updateWiFiList(networks) {
const select = document.getElementById('ssid');
select.innerHTML = '<option value="">请选择WiFi网络...</option>';
networks.forEach(network => {
const option = document.createElement('option');
option.value = network.ssid;
option.textContent = network.ssid + ' (' + network.rssi + 'dBm)' + (network.encryption ? ' 🔒' : '');
select.appendChild(option);
});
}
function submitForm() {
const ssid = document.getElementById('ssid').value;
const password = document.getElementById('password').value;
if(!ssid) {
alert('请选择WiFi网络');
return false;
}
document.getElementById('status').innerHTML = '正在连接到 ' + ssid + '...';
document.getElementById('status').className = 'status info';
const formData = new FormData();
formData.append('ssid', ssid);
formData.append('password', password);
fetch('/connect', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
if(data === 'OK') {
document.getElementById('status').innerHTML = '连接成功!设备将重启...';
document.getElementById('status').className = 'status success';
setTimeout(() => {
alert('WiFi配置成功!设备正在重启连接到新网络。');
}, 2000);
} else {
document.getElementById('status').innerHTML = '连接失败: ' + data;
document.getElementById('status').className = 'status error';
}
})
.catch(error => {
document.getElementById('status').innerHTML = '请求失败: ' + error;
document.getElementById('status').className = 'status error';
});
return false;
}
function clearConfig() {
if(confirm('确定要清除所有保存的WiFi配置吗?设备将重启。')) {
document.getElementById('status').innerHTML = '正在清除配置...';
document.getElementById('status').className = 'status warning';
fetch('/clear', {
method: 'POST'
})
.then(response => response.text())
.then(data => {
if(data === 'OK') {
document.getElementById('status').innerHTML = '配置已清除!设备将重启...';
document.getElementById('status').className = 'status success';
setTimeout(() => {
alert('WiFi配置已清除!设备正在重启。');
}, 2000);
} else {
document.getElementById('status').innerHTML = '清除失败: ' + data;
document.getElementById('status').className = 'status error';
}
})
.catch(error => {
document.getElementById('status').innerHTML = '请求失败: ' + error;
document.getElementById('status').className = 'status error';
});
}
}
// 页面加载时自动扫描
window.onload = function() {
scanWiFi();
};
</script>
</body>
</html>
)";
void setup() {
Serial.begin(115200);
Serial.println("启动ESP32 WiFi配网程序...");
// 初始化BOOT按键引脚
pinMode(BOOT_PIN, INPUT_PULLUP);
// 检测BOOT键是否被按下(上电时检测)
delay(100); // 等待电路稳定
if (digitalRead(BOOT_PIN) == LOW) {
Serial.println("检测到BOOT键按下,强制进入配网模式");
// 消抖处理,确保真的是按下
delay(50);
if (digitalRead(BOOT_PIN) == LOW) {
Serial.println("确认BOOT键按下,清除WiFi配置并进入配网模式");
// 清除保存的WiFi配置
preferences.begin("wifi-config", false);
preferences.clear();
preferences.end();
force_config_mode = true;
startConfigMode();
return; // 直接返回,不执行后续的正常启动流程
}
}
// 正常启动流程
Serial.println("正常启动模式");
// 初始化Preferences
preferences.begin("wifi-config", false);
// 读取保存的WiFi信息
saved_ssid = preferences.getString("ssid", "");
saved_password = preferences.getString("password", "");
if(saved_ssid.length() > 0) {
Serial.println("找到保存的WiFi配置,尝试连接: " + saved_ssid);
if(connectToWiFi(saved_ssid, saved_password)) {
wifi_configured = true;
Serial.println("WiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
// 显示当前状态信息
Serial.println("=====================================");
Serial.println("设备已联网,主程序正在运行中...");
Serial.println("如需重新配网,请重启时按住BOOT键");
Serial.println("=====================================");
return;
} else {
Serial.println("保存的WiFi连接失败,启动配网模式");
}
} else {
Serial.println("未找到保存的WiFi配置");
}
// 启动配网模式
startConfigMode();
}
bool connectToWiFi(String ssid, String password) {
Serial.println("正在连接WiFi: " + ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
int attempts = 0;
while(WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
// 如果在连接过程中检测到BOOT键,立即停止连接
if(digitalRead(BOOT_PIN) == LOW) {
Serial.println("\n检测到BOOT键按下,停止连接");
return false;
}
}
Serial.println();
return WiFi.status() == WL_CONNECTED;
}
void startConfigMode() {
Serial.println("启动配网模式...");
force_config_mode = true;
// 设置为AP模式
WiFi.mode(WIFI_AP);
WiFi.softAP(ap_ssid, ap_password);
Serial.println("=====================================");
Serial.print("配网热点已启动: ");
Serial.println(ap_ssid);
Serial.print("请连接此WiFi热点,然后访问: ");
Serial.println(WiFi.softAPIP());
Serial.println("在网页中选择要连接的WiFi并输入密码");
Serial.println("=====================================");
// 启动DNS服务器(强制门户)
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
// 设置Web服务器路由
server.on("/", HTTP_GET, handleRoot);
server.on("/scan", HTTP_GET, handleScan);
server.on("/connect", HTTP_POST, handleConnect);
server.on("/clear", HTTP_POST, handleClear); // 新增清除配置接口
server.onNotFound(handleRoot); // 所有未找到的请求都重定向到根页面
server.begin();
Serial.println("配网服务器已启动");
}
void handleRoot() {
server.send(200, "text/html", config_html);
}
void handleScan() {
Serial.println("开始扫描WiFi网络...");
int networkCount = WiFi.scanNetworks();
String json = "{\"networks\":[";
if(networkCount > 0) {
for(int i = 0; i < networkCount; i++) {
if(i > 0) json += ",";
json += "{";
json += "\"ssid\":\"" + WiFi.SSID(i) + "\",";
json += "\"rssi\":" + String(WiFi.RSSI(i)) + ",";
json += "\"encryption\":" + String(WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
json += "}";
}
}
json += "]}";
Serial.println("找到 " + String(networkCount) + " 个网络");
server.send(200, "application/json", json);
}
void handleConnect() {
String ssid = server.arg("ssid");
String password = server.arg("password");
Serial.println("尝试连接WiFi: " + ssid);
if(ssid.length() == 0) {
server.send(400, "text/plain", "SSID不能为空");
return;
}
// 尝试连接WiFi
WiFi.mode(WIFI_STA);
if(connectToWiFi(ssid, password)) {
// 连接成功,保存配置
preferences.putString("ssid", ssid);
preferences.putString("password", password);
Serial.println("WiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
server.send(200, "text/plain", "OK");
// 延迟后重启,让浏览器接收到响应
delay(2000);
Serial.println("重启设备...");
ESP.restart();
} else {
Serial.println("WiFi连接失败");
server.send(400, "text/plain", "连接失败,请检查密码");
// 重新启动AP模式
delay(1000);
startConfigMode();
}
}
// 新增:处理清除配置请求
void handleClear() {
Serial.println("收到清除配置请求");
preferences.clear();
Serial.println("WiFi配置已清除");
server.send(200, "text/plain", "OK");
// 延迟后重启
delay(2000);
Serial.println("重启设备...");
ESP.restart();
}
void loop() {
if(force_config_mode || !wifi_configured) {
// 配网模式
dnsServer.processNextRequest();
server.handleClient();
// 在配网模式下也可以检测BOOT键长按来重启
static unsigned long boot_press_start = 0;
if(digitalRead(BOOT_PIN) == LOW) {
if(boot_press_start == 0) {
boot_press_start = millis();
} else if(millis() - boot_press_start > 3000) { // 长按3秒
Serial.println("检测到BOOT键长按,重启设备...");
ESP.restart();
}
} else {
boot_press_start = 0;
}
} else {
// WiFi已配置,执行主要程序逻辑
// 检查WiFi连接状态
if(WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi连接断开,尝试重连...");
if(!connectToWiFi(saved_ssid, saved_password)) {
Serial.println("重连失败,启动配网模式");
wifi_configured = false;
force_config_mode = false;
startConfigMode();
}
}
// 检测BOOT键按下(运行时重新配网)
static unsigned long boot_press_start = 0;
if(digitalRead(BOOT_PIN) == LOW) {
if(boot_press_start == 0) {
boot_press_start = millis();
Serial.println("检测到BOOT键按下...");
} else if(millis() - boot_press_start > 2000) { // 长按2秒
Serial.println("BOOT键长按2秒,进入重新配网模式");
wifi_configured = false;
force_config_mode = false;
// 可选:清除保存的配置
// preferences.clear();
startConfigMode();
boot_press_start = 0;
return;
}
} else {
if(boot_press_start > 0 && millis() - boot_press_start > 100) {
Serial.println("BOOT键释放");
}
boot_press_start = 0;
}
// 这里添加您的主要程序逻辑
delay(100);
}
}