You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS无法连接ESP8266 WiFi软AP的问题排查与解决方案请求

ESP8266软AP无法被iOS 18.6.2设备连接的问题解决

问题描述

使用ESP8266WiFiHW-628 V0.0.1模块创建WiFi软AP,Windows设备可正常连接,但iOS 18.6.2系统的iPhone Xs、iPhone SE连接时提示“无法加入网络”。

开发环境及硬件配置

  • ESP8266WiFiHW-628 V0.0.1
  • VSCode + PlatformIO
  • 5V/3A电源(实际最大电流0.8A)
  • iOS 18.6.2系统的iPhone Xs与iPhone SE

已尝试的排查手段

  • 更换4A电源,实际电流需求仍低于1A
  • 尝试信道1、6、11,并关闭其他WiFi AP
  • 将iOS设备置于ESP8266模块10cm范围内,排除信号弱问题
  • 使用高强度密码(密码错误时提示信息不同)
  • 更换AP IP地址(如10.0.0.1、118.118.4.1等),排除IP冲突
  • 启动DNS服务器并在iPhone上手动设置DNS
  • 关闭iPhone的“私有WiFi地址”功能

当前使用代码

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <DNSServer.h>

// 创建AsyncWebServer实例,端口80
AsyncWebServer server(80);
DNSServer dnsServer;

// iOS兼容的IP配置
IPAddress LocalIP(192, 168, 4, 1);
IPAddress Gateway(192, 168, 4, 1);
IPAddress Subnet(255, 255, 255, 0);

void setup() {
  // 启动串口调试
  Serial.begin(115200);

  Serial.println("\n--- 开始初始化 ---");

  // 重置AP配置
  WiFi.softAPdisconnect(true);
  
  if (WiFi.softAPConfig(LocalIP, Gateway, Subnet)) {
    Serial.println("AP IP配置成功");
    // 创建AP:强密码、信道6、不隐藏、最大4个客户端
    if (WiFi.softAP("CircularClockAP", "Clock123!@#", 6, false, 4)) {
      Serial.println("AP创建成功");
      // 关闭WiFi休眠提升iOS连接稳定性
      WiFi.setSleepMode(WIFI_NONE_SLEEP);
      // 打印AP IP地址
      Serial.print("AP IP地址: ");
      Serial.println(WiFi.softAPIP());
      Serial.printf("当前连接设备数: %d\n", WiFi.softAPgetStationNum());
    } else {
      Serial.println("AP创建失败");
    }
  } else {
    Serial.println("AP IP配置失败");
  }

  // 启动DNS服务器用于 captive portal
  dnsServer.start(53, "*", LocalIP);
  Serial.println("DNS服务器启动");

  // iOS captive portal检测处理
  server.on("/hotspot-detect.html", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS热点检测请求");
    request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
  });
  
  server.on("/library/test/success.html", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS成功页检测请求");
    request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
  });
  
  // 安卓captive portal检测处理
  server.on("/generate_204", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("安卓连接检测请求");
    request->send(204); // 返回无内容响应
  });
  
  // Windows连接检测处理
  server.on("/connecttest.txt", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("Windows连接检测请求");
    request->send(200, "text/plain", "Microsoft Connect Test");
  });

  // 主页请求处理
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String userAgent = request->getHeader("User-Agent") ? request->getHeader("User-Agent")->value() : "";
    
    // 处理Apple的CaptiveNetworkSupport验证请求
    if (userAgent.indexOf("CaptiveNetworkSupport") >= 0) {
      Serial.println("Apple连接验证请求");
      request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
      return;
    }
    
    Serial.println("主页访问请求");
    String html = "<!DOCTYPE html><html><head>";
    html += "<meta charset='UTF-8'>";
    html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
    html += "<title>Circular Clock 设置</title>";
    html += "<style>body{font-family:Arial,sans-serif;margin:40px;background:#f5f5f5;}";
    html += ".container{background:white;padding:30px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}";
    html += "h1{color:#333;text-align:center;}";
    html += "p{color:#666;line-height:1.6;}";
    html += ".status{background:#e8f5e8;padding:15px;border-radius:5px;margin:20px 0;}";
    html += "</style></head><body>";
    html += "<div class='container'>";
    html += "<h1>🕐 Circular Clock 设置</h1>";
    html += "<div class='status'>✅ 已成功连接到Circular Clock!</div>";
    html += "<p>你的iOS设备已完成网络验证,可以开始配置时钟设置或关闭此页面。</p>";
    html += "<p><strong>网络名称:</strong> CircularClockAP<br>";
    html += "<strong>连接状态:</strong> 已验证并连接</p>";
    html += "</div></body></html>";
    request->send(200, "text/html", html);
  });
  
  // 未匹配路径重定向到主页
  server.onNotFound([](AsyncWebServerRequest *request){
    Serial.println("重定向请求: " + request->url());
    request->redirect("http://192.168.4.1/");
  });
  
  // 启动Web服务器
  server.begin();
  Serial.println("Web服务器启动");
  Serial.println("--- 初始化完成 ---");
}

void loop() {
  // 处理DNS请求
  dnsServer.processNextRequest();
  
  // 每10秒打印连接设备数
  static unsigned long lastCheck = 0;
  if (millis() - lastCheck > 10000) {
    uint8_t stationCount = WiFi.softAPgetStationNum();
    Serial.printf("AP状态 - 连接设备数: %d\n", stationCount);
    if (stationCount > 0) {
      Serial.println("iOS设备已成功连接!");
    }
    lastCheck = millis();
  }
}

适配iOS 18.6.2的优化代码及配置说明

针对iOS 18.6.2的兼容性问题,对代码进行以下关键调整:

  1. 强制使用WPA2-PSK加密:iOS 18对WPA3或自动加密模式兼容性不佳,明确指定WPA2加密
  2. 锁定WiFi模式为802.11n:避免ESP8266自动切换到802.11b/g导致iOS连接失败
  3. 补充iOS 18新增检测路径:处理苹果新增的图标检测请求,避免设备判定网络不可用
  4. 优化WiFi参数稳定性:明确指定WiFi工作模式,减少自动切换带来的兼容性问题

优化后的代码:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <DNSServer.h>

AsyncWebServer server(80);
DNSServer dnsServer;

// 固定IP配置
IPAddress LocalIP(192, 168, 4, 1);
IPAddress Gateway(192, 168, 4, 1);
IPAddress Subnet(255, 255, 255, 0);

void setup() {
  Serial.begin(115200);
  Serial.println("\n--- 开始初始化 ---");

  // 重置并设置WiFi为纯AP模式
  WiFi.softAPdisconnect(true);
  WiFi.mode(WIFI_AP);

  // 配置AP IP
  if (WiFi.softAPConfig(LocalIP, Gateway, Subnet)) {
    Serial.println("AP IP配置成功");
    
    // 强制WPA2-PSK加密,锁定802.11n,信道6
    // 参数说明:SSID, 密码, 信道, 是否隐藏, 最大连接数, 加密模式
    if (WiFi.softAP("CircularClockAP", "Clock123!@#", 6, false, 4, WIFI_AUTH_WPA2_PSK)) {
      Serial.println("AP创建成功");
      
      // 关闭WiFi休眠,锁定为802.11n模式
      WiFi.setSleepMode(WIFI_NONE_SLEEP);
      WiFi.setPhyMode(WIFI_PHY_MODE_11N);
      
      Serial.print("AP IP地址: ");
      Serial.println(WiFi.softAPIP());
      Serial.printf("当前连接设备数: %d\n", WiFi.softAPgetStationNum());
    } else {
      Serial.println("AP创建失败");
    }
  } else {
    Serial.println("AP IP配置失败");
  }

  // 启动DNS服务器,捕获所有域名请求
  dnsServer.start(53, "*", LocalIP);
  Serial.println("DNS服务器启动");

  // iOS 18 captive portal检测路径处理
  server.on("/hotspot-detect.html", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS热点检测请求");
    request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
  });
  
  server.on("/library/test/success.html", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS成功页检测请求");
    request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
  });
  
  // Apple新增的连接检测路径
  server.on("/apple-touch-icon-precomposed.png", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS图标检测请求");
    request->send(404);
  });
  
  server.on("/apple-touch-icon.png", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("iOS图标检测请求");
    request->send(404);
  });
  
  // 安卓和Windows检测处理
  server.on("/generate_204", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("安卓连接检测请求");
    request->send(204);
  });
  
  server.on("/connecttest.txt", HTTP_GET, [](AsyncWebServerRequest *request){
    Serial.println("Windows连接检测请求");
    request->send(200, "text/plain", "Microsoft Connect Test");
  });

  // 主页处理,兼容Apple的CaptiveNetworkSupport检测
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String userAgent = request->getHeader("User-Agent") ? request->getHeader("User-Agent")->value() : "";
    
    if (userAgent.indexOf("CaptiveNetworkSupport") >= 0) {
      Serial.println("Apple连接验证请求");
      request->send(200, "text/html", "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
      return;
    }
    
    Serial.println("主页访问请求");
    String html = "<!DOCTYPE html><html><head>";
    html += "<meta charset='UTF-8'>";
    html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
    html += "<title>Circular Clock 设置</title>";
    html += "<style>body{font-family:Arial,sans-serif;margin:40px;background:#f5f5f5;}";
    html += ".container{background:white;padding:30px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}";
    html += "h1{color:#333;text-align:center;}";
    html += "p{color:#666;line-height:1.6;}";
    html += ".status{background:#e8f5e8;padding:15px;border-radius:5px;margin:20px 0;}";
    html += "</style></head><body>";
    html += "<div class='container'>";
    html += "<h1>🕐 Circular Clock 设置</h1>";
    html += "<div class='status'>✅ 已成功连接到Circular Clock!</div>";
    html += "<p>你的iOS设备已完成网络验证,可以开始配置时钟设置或关闭此页面。</p>";
    html += "<p><strong>网络名称:</strong> CircularClockAP<br>";
    html += "<strong>连接状态:</strong> 已验证并连接</p>";
    html += "</div></body></html>";
    request->send(200, "text/html", html);
  });
  
  // 未匹配路径重定向到主页
  server.onNotFound([](AsyncWebServerRequest *request){
    Serial.println("重定向请求: " + request->url());
    request->redirect("http://192.168.4.1/");
  });
  
  server.begin();
  Serial.println("Web服务器启动");
  Serial.println("--- 初始化完成 ---");
}

void loop() {
  dnsServer.processNextRequest();
  
  // 每10秒打印连接设备数
  static unsigned long lastCheck = 0;
  if (millis() - lastCheck > 10000) {
    uint8_t stationCount = WiFi.softAPgetStationNum();
    Serial.printf("AP状态 - 连接设备数: %d\n", stationCount);
    if (stationCount > 0) {
      Serial.println("iOS设备已成功连接!");
    }
    lastCheck = millis();
  }
}

额外注意事项

  • 确保ESP8266的核心固件版本更新到最新(PlatformIO中选择esp8266:esp8266@3.2.0及以上)
  • 若仍无法连接,尝试将密码改为纯字母数字组合(避免特殊符号)
  • 重启iOS设备后再尝试连接,清除之前的网络缓存

内容的提问来源于stack exchange,提问作者Erez

火山引擎 最新活动