| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824 |
- #include "WiFiConfigurator.h"
- // 静态实例指针初始化
- WiFiConfigurator* WiFiConfigurator::_instance = nullptr;
- // HTML页面内容
- const char captive_html[] PROGMEM = R"rawliteral(
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="refresh" content="0;url=/">
- </head>
- <body>
- </body>
- </html>
- )rawliteral";
- const char index_html[] PROGMEM = R"rawliteral(
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>WiFi配置</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <style>
- * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
- body { background-color: #f5f5f7; color: #333; padding: 20px; }
- .container { max-width: 500px; margin: 50px auto; background: white; padding: 30px; border-radius: 18px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); }
- h1 { color: #1d1d1f; text-align: center; margin-bottom: 25px; font-weight: 600; }
- .form-group { margin-bottom: 20px; }
- .wifi-selector { display: flex; gap: 10px; align-items: flex-end; }
- label { display: block; margin-bottom: 8px; font-weight: 500; color: #6e6e73; }
- select, input { width: 100%; padding: 14px; border: 1px solid #d2d2d7; border-radius: 10px; font-size: 16px; }
- select:focus, input:focus { outline: none; border-color: #0071e3; box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.2); }
- button { background-color: #0071e3; color: white; border: none; cursor: pointer; font-weight: 500; padding: 14px; border-radius: 10px; margin-top: 10px; }
- button:hover { background-color: #0077ed; }
- button:disabled { background-color: #94bfff; cursor: not-allowed; }
- .refresh-btn { padding: 14px; width: auto; white-space: nowrap; }
- .connect-btn { width: 100%; }
- .signal { display: inline-block; width: 80px; text-align: right; color: #86868b; }
- .status-bar { text-align: center; margin-top: 20px; color: #6e6e73; font-size: 14px; }
- .message { padding: 15px; margin: 15px 0; border-radius: 8px; text-align: center; }
- .success { background-color: #eaf8ed; color: #147d39; }
- .error { background-color: #fee; color: #c51e24; }
- .loading { background-color: #f0f7ff; color: #0066cc; }
- .password-hint { font-size: 12px; color: #86868b; margin-top: 5px; }
- .spinner { display: inline-block; width: 20px; height: 20px; border: 3px solid rgba(0,113,227,.3); border-radius: 50%; border-top-color: #0071e3; animation: spin 1s ease-in-out infinite; margin-right: 8px; }
- @keyframes spin { to { transform: rotate(360deg); } }
- .connection-status { margin-top: 15px; padding: 10px; border-radius: 8px; background-color: #f5f5f7; text-align: center; font-size: 14px; }
- .progress-container { margin-top: 10px; background-color: #eaeaea; border-radius: 10px; overflow: hidden; }
- .progress-bar { height: 8px; background-color: #0071e3; width: 0%; transition: width 0.3s ease; }
- .timeout-warning { color: #ff9500; font-size: 12px; text-align: center; margin-top: 10px; }
- </style>
- <script>
- let checkStatusInterval;
- let timeoutTimer;
- let remainingTime = 120; // 120秒超时
-
- // 更新超时提示
- function updateTimeoutDisplay() {
- const element = document.getElementById('timeoutWarning');
- if (element) {
- element.textContent = `配置热点将在 ${remainingTime} 秒后自动关闭`;
- }
-
- if (remainingTime <= 0) {
- clearInterval(timeoutTimer);
- return;
- }
-
- remainingTime--;
- }
-
- function startCheckingStatus() {
- // 禁用表单防止重复提交
- document.getElementById('connectBtn').disabled = true;
- document.getElementById('ssid').disabled = true;
- document.getElementById('password').disabled = true;
- document.querySelector('.refresh-btn').disabled = true;
-
- // 重置超时时间
- resetTimeout();
-
- // 显示初始状态
- updateStatus({
- connecting: true,
- status: "准备连接...",
- progress: 0
- });
-
- // 每200ms检查一次状态
- checkStatusInterval = setInterval(() => {
- fetch('/status')
- .then(response => {
- if (!response.ok) throw new Error('网络错误');
- return response.json();
- })
- .then(data => {
- updateStatus(data);
-
- // 如果连接完成,停止检查
- if (!data.connecting) {
- clearInterval(checkStatusInterval);
-
- // 连接成功时显示提示并跳转
- if (data.success) {
- setTimeout(() => {
- window.location.href = '/success';
- }, 1000);
- } else {
- // 连接失败时重新启用表单,允许重试
- enableForm();
- }
- }
- })
- .catch(err => {
- console.error('获取状态失败:', err);
- updateStatus({
- connecting: false,
- status: "通信错误,请重试",
- progress: 0
- });
- clearInterval(checkStatusInterval);
- enableForm();
- });
- }, 200);
- }
-
- function updateStatus(data) {
- const statusElement = document.getElementById('connectionStatus');
- const progressBar = document.getElementById('progressBar');
-
- statusElement.innerHTML = data.connecting ?
- '<span class="spinner"></span>' + data.status : data.status;
-
- statusElement.className = 'connection-status ' +
- (data.connecting ? 'loading' : (data.success ? 'success' : 'error'));
-
- progressBar.style.width = data.progress + '%';
- }
-
- function enableForm() {
- // 确保表单元素可操作
- document.getElementById('connectBtn').disabled = false;
- document.getElementById('ssid').disabled = false;
- document.getElementById('password').disabled = false;
- document.querySelector('.refresh-btn').disabled = false;
-
- // 聚焦到密码输入框
- document.getElementById('password').focus();
- }
-
- // 重置超时计时器
- function resetTimeout() {
- remainingTime = 120;
- clearInterval(timeoutTimer);
- timeoutTimer = setInterval(updateTimeoutDisplay, 1000);
- updateTimeoutDisplay();
- }
-
- // 密码验证
- function validatePassword() {
- const password = document.getElementById('password').value;
- const hintElement = document.getElementById('passwordHint');
-
- if (password.length === 0) {
- hintElement.textContent = '请输入WiFi密码';
- hintElement.style.color = '#86868b';
- return true;
- } else if (password.length < 8 && password.length > 0) {
- hintElement.textContent = '警告: 大多数WiFi密码至少需要8个字符';
- hintElement.style.color = '#ff9500';
- return false;
- } else {
- hintElement.textContent = '';
- return true;
- }
- }
-
- window.onload = function() {
- // 初始化超时显示
- const timeoutDiv = document.createElement('div');
- timeoutDiv.id = 'timeoutWarning';
- timeoutDiv.className = 'timeout-warning';
- document.querySelector('.container').appendChild(timeoutDiv);
- resetTimeout();
-
- const passwordInput = document.getElementById('password');
- passwordInput.addEventListener('input', validatePassword);
-
- const connectForm = document.getElementById('connectForm');
- connectForm.addEventListener('submit', function(e) {
- e.preventDefault();
- if (validatePassword()) {
- // 提交表单数据到后端
- const formData = new FormData(connectForm);
- fetch('/connect', {
- method: 'POST',
- body: formData
- }).then(() => {
- startCheckingStatus();
- });
- }
- });
-
- // 为刷新按钮添加超时重置
- document.querySelector('.refresh-btn').addEventListener('click', function() {
- resetTimeout();
- });
- }
- </script>
- </head>
- <body>
- <div class="container">
- <h1>WiFi网络配置</h1>
-
- %MESSAGE_BOX%
-
- <form id="connectForm">
- <div class="form-group">
- <label for="ssid">选择WiFi网络:</label>
- <div class="wifi-selector">
- <select id="ssid" name="ssid" required style="flex: 1;">
- %WIFI_LIST%
- </select>
- <button type="button" class="refresh-btn" onclick="window.location.href='/refresh'">
- 刷新
- </button>
- </div>
- </div>
- <div class="form-group">
- <label for="password">WiFi密码:</label>
- <input type="password" id="password" name="password" placeholder="请输入WiFi密码">
- <div id="passwordHint" class="password-hint"></div>
- </div>
- <button type="submit" id="connectBtn" class="connect-btn">
- 连接网络
- </button>
- <div id="connectionStatus" class="connection-status">
- 等待操作...
- </div>
- <div class="progress-container">
- <div id="progressBar" class="progress-bar"></div>
- </div>
- </form>
- <div class="status-bar">
- 设备热点: %AP_SSID%
- </div>
- </div>
- </body>
- </html>
- )rawliteral";
- const char success_html[] PROGMEM = R"rawliteral(
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>连接成功</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <style>
- * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
- body { background-color: #f5f5f7; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; color: #333; }
- .container { text-align: center; max-width: 400px; width: 100%; padding: 30px; background: white; border-radius: 18px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); }
- h1 { margin-bottom: 20px; font-weight: 600; color: #147d39; }
- .message { margin: 20px 0; line-height: 1.6; color: #6e6e73; }
- .countdown { color: #0071e3; font-weight: 600; margin: 15px 0; }
- </style>
- <script>
- // 5秒后提示用户切换网络
- let countdown = 5;
- function updateCountdown() {
- document.getElementById('countdown').textContent = countdown;
- if (countdown <= 0) {
- document.getElementById('countdownMessage').textContent =
- '请切换到已配置的WiFi网络';
- return;
- }
- countdown--;
- setTimeout(updateCountdown, 1000);
- }
- window.onload = updateCountdown;
- </script>
- </head>
- <body>
- <div class="container">
- <h1>连接成功!</h1>
-
- <div class="message">
- <p>已成功连接到 %SSID%</p>
- <p>IP地址: %IP_ADDRESS%</p>
- <div id="countdownMessage" class="countdown">
- 配置热点将在 <span id="countdown">5</span> 秒后关闭
- </div>
- </div>
- </div>
- </body>
- </html>
- )rawliteral";
- const char error_html[] PROGMEM = R"rawliteral(
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>连接失败</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <style>
- * { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
- body { background-color: #f5f5f7; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; color: #333; }
- .container { text-align: center; max-width: 400px; width: 100%; padding: 30px; background: white; border-radius: 18px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); }
- h1 { margin-bottom: 20px; font-weight: 600; color: #c51e24; }
- .message { margin: 20px 0; line-height: 1.6; color: #6e6e73; }
- button { background-color: #0071e3; color: white; border: none; cursor: pointer; font-weight: 500; padding: 14px 24px; border-radius: 10px; margin-top: 20px; font-size: 16px; }
- button:hover { background-color: #0077ed; }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>连接失败</h1>
-
- <div class="message">
- <p>无法连接到 %SSID%</p>
- <p>%ERROR_MESSAGE%</p>
- </div>
-
- <button onclick="window.location.href='/'">返回重试</button>
- </div>
- </body>
- </html>
- )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 += "<option value=\"" + WiFi.SSID(i) + "\">" +
- WiFi.SSID(i) + " <span class=\"signal\">" + signalBar + "</span></option>";
-
- // 串口打印扫描结果
- 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 = "<div class='message success'>" + _instance->_connectionStatus + "</div>";
- } else if (_instance->_connectionStatus.length() > 0) {
- messageBox = "<div class='message error'>" + _instance->_connectionStatus + "</div>";
- }
- // 重置连接完成状态,只显示一次
- _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 += "<br>- WiFi密码是否正确";
- errorMsg += "<br>- 路由器信号是否稳定";
- errorMsg += "<br>- 网络是否允许新设备接入";
-
- 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", "");
- }
|