#include "WiFiConfigurator.h"
// 静态实例指针初始化
WiFiConfigurator* WiFiConfigurator::_instance = nullptr;
// HTML页面内容
const char captive_html[] PROGMEM = R"rawliteral(
)rawliteral";
const char index_html[] PROGMEM = R"rawliteral(
WiFi配置
WiFi网络配置
%MESSAGE_BOX%
设备热点: %AP_SSID%
)rawliteral";
const char success_html[] PROGMEM = R"rawliteral(
连接成功
连接成功!
已成功连接到 %SSID%
IP地址: %IP_ADDRESS%
配置热点将在 5 秒后关闭
)rawliteral";
const char error_html[] PROGMEM = R"rawliteral(
连接失败
连接失败
无法连接到 %SSID%
%ERROR_MESSAGE%
)rawliteral";
// 构造函数
WiFiConfigurator::WiFiConfigurator(int slowLedPin, int fastLedPin, int configButtonPin,
const char* ap_ssid, const char* ap_password,
IPAddress apIP, IPAddress gateway, IPAddress subnet)
: _slowLedPin(slowLedPin),
_fastLedPin(fastLedPin),
_configButtonPin(configButtonPin),
_ap_ssid(ap_ssid),
_ap_password(ap_password),
_apIP(apIP),
_gateway(gateway),
_subnet(subnet),
_server(80),
_captive_html(captive_html),
_index_html(index_html),
_success_html(success_html),
_error_html(error_html) {
_instance = this;
}
// 初始化函数
void WiFiConfigurator::begin() {
// 初始化LED
pinMode(_slowLedPin, OUTPUT);
pinMode(_fastLedPin, OUTPUT);
// 初始化按键,使用内部上拉电阻
pinMode(_configButtonPin, INPUT_PULLUP);
Serial.println("WiFi配置器初始化...");
_preferences.begin("wifi-config", false);
// 启动时立即扫描WiFi
scanWiFiNetworks();
// 尝试连接已保存的WiFi
String savedSsid = _preferences.getString("ssid", "");
String savedPassword = _preferences.getString("password", "");
if (savedSsid.length() > 0) {
Serial.println("尝试连接已保存的WiFi...");
connectToWiFi(savedSsid.c_str(), savedPassword.c_str());
} else {
startAPMode();
}
}
// 主循环处理函数
void WiFiConfigurator::loop() {
// 处理按键输入
handleConfigButton();
// LED控制
unsigned long currentMillis = millis();
// 慢闪LED
if (currentMillis - _previousSlowMillis >= _slowInterval) {
_previousSlowMillis = currentMillis;
_slowLedState = !_slowLedState;
digitalWrite(_slowLedPin, _slowLedState);
}
// 快闪LED - 连接过程中加速闪烁
if (_connectionInProgress) {
if (currentMillis - _previousFastMillis >= 100) { // 加速闪烁
_previousFastMillis = currentMillis;
_fastLedState = !_fastLedState;
digitalWrite(_fastLedPin, _fastLedState);
}
} else if (WiFi.getMode() == WIFI_AP || WiFi.status() != WL_CONNECTED) {
if (currentMillis - _previousFastMillis >= _fastInterval) {
_previousFastMillis = currentMillis;
_fastLedState = !_fastLedState;
digitalWrite(_fastLedPin, _fastLedState);
}
} else {
digitalWrite(_fastLedPin, HIGH);
}
// 持续处理网络请求
if (WiFi.getMode() & WIFI_AP) { // 只要AP模式开启就处理请求
_dnsServer.processNextRequest();
_server.handleClient();
// 检查AP模式超时(仅在连接未进行时)
if (!_connectionInProgress && _apTimeout > 0 && millis() - _apTimeout > _apTimeoutDuration) {
Serial.println("AP模式超时,关闭热点");
WiFi.softAPdisconnect(true);
_server.stop();
_dnsServer.stop();
_apTimeout = 0;
}
}
// 连接成功后保持AP模式一段时间
if (_connectionSuccess && (WiFi.getMode() & WIFI_AP) &&
millis() - _successNotificationTime > _successNotificationDelay) {
Serial.println("成功通知时间已到,关闭AP模式");
WiFi.softAPdisconnect(true);
_server.stop();
_dnsServer.stop();
_apTimeout = 0;
}
}
// 检查是否已连接WiFi
bool WiFiConfigurator::isConnected() {
return WiFi.status() == WL_CONNECTED;
}
// 获取当前连接的SSID
String WiFiConfigurator::getCurrentSSID() {
return WiFi.status() == WL_CONNECTED ? WiFi.SSID() : "";
}
// 获取IP地址
String WiFiConfigurator::getIPAddress() {
return WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "";
}
// 处理配置按键
void WiFiConfigurator::handleConfigButton() {
// 读取按键状态(LOW表示按下,因为使用了内部上拉)
int buttonState = digitalRead(_configButtonPin);
// static bool slowLedAlwaysOn = false; // 标记慢闪灯是否已常亮
// 按键按下
if (buttonState == LOW && !_buttonPressed) {
_buttonPressed = true;
_buttonPressStartTime = millis();
// slowLedAlwaysOn = false; // 重置状态
Serial.println("配置按键被按下...");
// 开始快速闪烁LED,表示正在计时
_fastLedState = HIGH;
digitalWrite(_fastLedPin, _fastLedState);
}
// 按键释放
else if (buttonState == HIGH && _buttonPressed) {
unsigned long pressDuration = millis() - _buttonPressStartTime;
_buttonPressed = false;
// slowLedAlwaysOn = false; // 重置状态
// 检查按下时间是否达到要求
if (pressDuration >= _configButtonPressDuration) {
Serial.println("按键长按5秒以上,进入配网模式...");
enterConfigMode();
} else {
Serial.printf("按键释放,按下时间: %d毫秒(不足5秒)\n", pressDuration);
}
}
// 按键持续按下中
else if (buttonState == LOW && _buttonPressed) {
unsigned long pressDuration = millis() - _buttonPressStartTime;
// 每100ms检查一次(提高响应速度)
if (pressDuration % 100 < 10) {
// 计算已按下的秒数
int secondsPressed = pressDuration / 1000;
Serial.printf("按键持续按下中: %d秒/%d秒\n", secondsPressed, _configButtonPressDuration / 1000);
// 快闪LED提示用户还在计时
_fastLedState = !_fastLedState;
digitalWrite(_fastLedPin, _fastLedState);
// 关键修改:按住期间达到5秒且尚未设置常亮
if (pressDuration >= _configButtonPressDuration) {
// if (pressDuration >= _configButtonPressDuration && !slowLedAlwaysOn) {
Serial.println("已按住5秒,慢闪灯常亮");
_slowLedState = HIGH; // 设置慢闪LED状态为常亮
digitalWrite(_slowLedPin, _slowLedState);
// slowLedAlwaysOn = true; // 标记已常亮,避免重复设置
}
}
}
}
// 进入配网模式
void WiFiConfigurator::enterConfigMode() {
// 清除已保存的WiFi配置
_preferences.putString("ssid", "");
_preferences.putString("password", "");
// 停止当前的WiFi连接
WiFi.disconnect();
// 重新启动AP模式和Web服务
startAPMode();
// 闪烁LED提示进入配网模式
for (int i = 0; i < 5; i++) {
digitalWrite(_slowLedPin, HIGH);
digitalWrite(_fastLedPin, HIGH);
delay(200);
digitalWrite(_slowLedPin, LOW);
digitalWrite(_fastLedPin, LOW);
delay(200);
}
Serial.println("已进入配网模式,请连接设备热点进行配置");
}
// 扫描WiFi网络并缓存结果
void WiFiConfigurator::scanWiFiNetworks() {
Serial.println("开始扫描WiFi网络...");
int n = WiFi.scanNetworks(false, false, false, 100);
Serial.printf("发现 %d 个WiFi网络\n", n);
_wifiScanResults = "";
for (int i = 0; i < n; i++) {
int signalStrength = map(WiFi.RSSI(i), -100, -50, 0, 4);
String signalBar = "";
for(int j = 0; j < signalStrength; j++) signalBar += "●";
for(int j = signalStrength; j < 4; j++) signalBar += "○";
_wifiScanResults += "";
// 串口打印扫描结果
Serial.printf("网络: %s, 信号强度: %d, 加密方式: %d\n",
WiFi.SSID(i).c_str(), WiFi.RSSI(i), WiFi.encryptionType(i));
}
WiFi.scanDelete();
}
void WiFiConfigurator::startAPMode() {
Serial.println("启动AP模式...");
// 重置连接状态
_connectionInProgress = false;
_connectionCompleted = false;
_connectionSuccess = false;
_connectionStatus = "";
_connectionProgress = 0;
// 配置为AP模式
WiFi.mode(WIFI_AP);
delay(200); // 增加延迟确保模式切换完成
bool configSuccess = WiFi.softAPConfig(_apIP, _gateway, _subnet);
Serial.print("AP配置: ");
Serial.println(configSuccess ? "成功" : "失败");
// 启动AP
bool apStarted = WiFi.softAP(_ap_ssid, _ap_password, 1, false, 4);
if (!apStarted) {
Serial.println("AP启动失败,尝试默认配置...");
apStarted = WiFi.softAP(_ap_ssid, _ap_password);
}
Serial.print("AP启动: ");
Serial.println(apStarted ? "成功" : "失败");
// 配置DNS
_dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
bool dnsStarted = _dnsServer.start(_dnsPort, "*", _apIP);
Serial.print("DNS服务器启动: ");
Serial.println(dnsStarted ? "成功" : "失败");
// 苹果Captive Portal支持
_server.on("/hotspot-detect.html", [](){
_instance->_server.send(200, "text/html", _instance->_captive_html);
});
_server.on("/library/test/success.html", [](){
_instance->_server.send(200, "text/html", _instance->_captive_html);
});
// 主要路由
_server.on("/", handleRoot);
_server.on("/refresh", handleRefresh);
_server.on("/connect", HTTP_POST, handleConnect);
_server.on("/status", handleStatus);
_server.on("/success", handleSuccess);
_server.on("/error", handleError);
_server.onNotFound(handleNotFound);
_server.begin();
Serial.print("AP IP地址: ");
Serial.println(WiFi.softAPIP());
// 重置AP超时计时器
_apTimeout = millis();
}
void WiFiConfigurator::connectToWiFi(const char* ssid, const char* password) {
_currentSsid = ssid;
_connectionInProgress = true;
_connectionCompleted = false;
_connectionSuccess = false;
_connectionProgress = 0;
Serial.println("\n===== [DEBUG] 开始连接WiFi流程 =====\n");
// 完全断开所有WiFi连接
Serial.println("[DEBUG] 1. 正在清除之前的WiFi连接信息...");
WiFi.disconnect(true);
delay(500); // 增加一点延迟,确保清除操作完成
// 配置为AP+STA模式
Serial.println("[DEBUG] 2. 设置WiFi模式为 AP+STA...");
WiFi.mode(WIFI_AP_STA);
delay(200);
// 打印将要使用的SSID和密码
Serial.print("[DEBUG] 3. 准备连接到网络...");
Serial.print(" SSID: '");
Serial.print(ssid);
Serial.print("' ");
Serial.print(" Password: '");
Serial.print(password);
Serial.println("'");
// 开始连接
Serial.println("[DEBUG] 4. 调用 WiFi.begin()...");
WiFi.begin(ssid, password);
// 等待连接结果(最多20秒)
Serial.println("[DEBUG] 5. 开始等待连接结果...");
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 20000) {
// 打印当前状态码
Serial.printf("[DEBUG] 状态码: %d (等待连接中...)\n", WiFi.status());
int elapsedSeconds = (millis() - startAttemptTime) / 1000;
_connectionProgress = 20 + (elapsedSeconds * 70) / 20;
if (_connectionProgress > 90) _connectionProgress = 90;
_dnsServer.processNextRequest();
_server.handleClient();
delay(1000);
}
// 检查最终连接结果
Serial.println("\n[DEBUG] 6. 连接尝试结束,检查最终结果...");
if (WiFi.status() == WL_CONNECTED) {
_connectionSuccess = true;
String ipAddress = WiFi.localIP().toString();
_connectionStatus = "成功连接到 " + _currentSsid + ",IP地址: " + ipAddress;
Serial.println("[DEBUG] => 连接成功!");
Serial.printf("===== [DEBUG] 成功连接到 '%s',IP: %s =====\n", ssid, ipAddress.c_str());
_preferences.putString("ssid", ssid);
_preferences.putString("password", password);
_connectionCompleted = true;
_connectionInProgress = false;
_connectionProgress = 100; // 标记进度为100%
// 不立即关闭AP,而是记录成功时间,稍后在loop中关闭
_successNotificationTime = millis();
Serial.println("连接成功,保持AP模式5秒以确保客户端收到通知");
} else {
_connectionSuccess = false;
int errorCode = WiFi.status();
Serial.println("[DEBUG] => 连接失败!");
Serial.printf("[DEBUG] 最终状态码: %d\n", errorCode);
String errorMsg;
switch(errorCode) {
case WL_CONNECT_FAILED: errorMsg = "连接失败(密码错误或网络拒绝接入)"; break;
case WL_CONNECTION_LOST: errorMsg = "连接丢失"; break;
case WL_DISCONNECTED: errorMsg = "已断开连接"; break;
default: errorMsg = "未知错误(代码: " + String(errorCode) + ")";
}
_connectionStatus = "连接失败: " + errorMsg;
Serial.println(_connectionStatus);
Serial.printf("===== [DEBUG] 连接 '%s' 失败 =====\n", ssid);
_connectionCompleted = true;
_connectionInProgress = false;
_apTimeout = millis();
if (!(WiFi.getMode() & WIFI_AP)) {
Serial.println("[DEBUG] 意外退出AP模式,重新启动...");
WiFi.mode(WIFI_AP_STA);
delay(200);
_server.begin();
_dnsServer.start(_dnsPort, "*", _apIP);
}
_server.handleClient();
}
}
// HTTP请求处理函数实现
void WiFiConfigurator::handleRoot() {
// 每次访问主页都重置AP超时
_instance->_apTimeout = millis();
String html = _instance->_index_html;
String messageBox = "";
// 显示最后一次连接结果
if (_instance->_connectionCompleted) {
if (_instance->_connectionSuccess) {
messageBox = "" + _instance->_connectionStatus + "
";
} else if (_instance->_connectionStatus.length() > 0) {
messageBox = "" + _instance->_connectionStatus + "
";
}
// 重置连接完成状态,只显示一次
_instance->_connectionCompleted = false;
}
// 使用缓存的WiFi扫描结果
html.replace("%MESSAGE_BOX%", messageBox);
html.replace("%WIFI_LIST%", _instance->_wifiScanResults);
html.replace("%AP_SSID%", _instance->_ap_ssid);
// 发送响应
_instance->_server.send(200, "text/html; charset=UTF-8", html);
}
void WiFiConfigurator::handleRefresh() {
Serial.println("用户请求刷新WiFi列表");
_instance->scanWiFiNetworks(); // 重新扫描WiFi
// 重置AP超时
_instance->_apTimeout = millis();
handleRoot(); // 刷新后显示主页
}
void WiFiConfigurator::handleConnect() {
// 检查是否有必要的参数
if (!_instance->_server.hasArg("ssid") || !_instance->_server.hasArg("password")) {
_instance->_connectionStatus = "参数错误:请填写WiFi名称和密码";
Serial.println(_instance->_connectionStatus);
_instance->_server.sendHeader("Location", "/error");
_instance->_server.send(302, "text/plain", "");
return;
}
String ssid = _instance->_server.arg("ssid");
String password = _instance->_server.arg("password");
Serial.printf("收到连接请求: SSID=%s, 密码长度=%d\n", ssid.c_str(), password.length());
// 重置AP超时计时器
_instance->_apTimeout = millis();
Serial.println("用户点击连接,重置AP超时计时器");
// 立即响应,告知客户端开始连接
_instance->_server.sendHeader("Connection", "close");
_instance->_server.send(200, "text/plain", "连接开始");
// 在后台进行连接操作
_instance->connectToWiFi(ssid.c_str(), password.c_str());
}
void WiFiConfigurator::handleStatus() {
// 每次状态查询都重置AP超时
_instance->_apTimeout = millis();
// 构建JSON响应
String json = "{";
json += "\"connecting\":" + String(_instance->_connectionInProgress ? "true" : "false") + ",";
json += "\"success\":" + String(_instance->_connectionSuccess ? "true" : "false") + ",";
json += "\"progress\":" + String(_instance->_connectionProgress) + ",";
json += "\"status\":\"" + _instance->_connectionStatus + "\"";
json += "}";
_instance->_server.send(200, "application/json", json);
}
void WiFiConfigurator::handleSuccess() {
String html = _instance->_success_html;
html.replace("%SSID%", _instance->_currentSsid);
html.replace("%IP_ADDRESS%", WiFi.localIP().toString());
_instance->_server.send(200, "text/html; charset=UTF-8", html);
}
void WiFiConfigurator::handleError() {
String html = _instance->_error_html;
html.replace("%SSID%", _instance->_currentSsid);
// 生成更具体的错误信息
String errorMsg = "请检查:";
errorMsg += "
- WiFi密码是否正确";
errorMsg += "
- 路由器信号是否稳定";
errorMsg += "
- 网络是否允许新设备接入";
html.replace("%ERROR_MESSAGE%", errorMsg);
_instance->_server.send(200, "text/html; charset=UTF-8", html);
}
void WiFiConfigurator::handleNotFound() {
_instance->_server.sendHeader("Location", "/");
_instance->_server.send(302, "text/plain", "");
}