#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", ""); }