跳转至

WiFi 网络

WiFi 是 ESP32 最核心的特性之一。它支持 802.11 b/g/n,可以作为客户端(STA)连接路由器,也可以作为热点(AP)让其他设备连接,还能两种模式同时工作。


一、WiFi 模式概览

graph TB
    ESP[ESP32] --> STA[STA 模式<br>连接路由器]
    ESP --> AP[AP 模式<br>作为热点]
    ESP --> APSTA[AP+STA 模式<br>同时工作]

    STA --> S1[连接家庭/办公 WiFi]
    STA --> S2[访问互联网]
    STA --> S3[上传数据到云平台]

    AP --> A1[手机直连 ESP32]
    AP --> A2[无需路由器]
    AP --> A3[局域网配置页面]

    APSTA --> AS1[中继/网关]
    APSTA --> AS2[配网模式]
模式 函数 用途
STA WiFi.mode(WIFI_STA) 连接现有 WiFi 网络
AP WiFi.mode(WIFI_AP) 创建 WiFi 热点
AP+STA WiFi.mode(WIFI_AP_STA) 同时做热点和客户端

二、STA 模式(连接 WiFi)

基本连接

#include <WiFi.h>

const char* WIFI_SSID = "YourWiFi";
const char* WIFI_PASS = "YourPassword";

void setup() {
    Serial.begin(115200);

    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASS);

    Serial.print("连接 WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("\n连接成功!");
    Serial.print("IP 地址: ");
    Serial.println(WiFi.localIP());
    Serial.print("信号强度: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
}

WiFi 连接状态

状态码 宏定义 说明
0 WL_IDLE_STATUS 空闲
1 WL_NO_SSID_AVAIL 找不到网络
3 WL_CONNECTED 已连接
4 WL_CONNECT_FAILED 连接失败(密码错误等)
5 WL_CONNECTION_LOST 连接丢失
6 WL_DISCONNECTED 已断开

带超时和重连的连接

#include <WiFi.h>

const char* WIFI_SSID = "YourWiFi";
const char* WIFI_PASS = "YourPassword";
const int CONNECT_TIMEOUT = 10000;  // 10 秒超时

void connectWiFi() {
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASS);

    unsigned long startTime = millis();
    while (WiFi.status() != WL_CONNECTED) {
        if (millis() - startTime > CONNECT_TIMEOUT) {
            Serial.println("\n连接超时!");
            return;
        }
        delay(500);
        Serial.print(".");
    }
    Serial.printf("\n已连接,IP: %s\n", WiFi.localIP().toString().c_str());
}

void setup() {
    Serial.begin(115200);
    connectWiFi();
}

void loop() {
    // 自动重连
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("WiFi 断开,正在重连...");
        connectWiFi();
    }
    delay(1000);
}

WiFi 事件回调

void WiFiEvent(WiFiEvent_t event) {
    switch (event) {
        case ARDUINO_EVENT_WIFI_STA_CONNECTED:
            Serial.println("已连接到 AP");
            break;
        case ARDUINO_EVENT_WIFI_STA_GOT_IP:
            Serial.printf("获得 IP: %s\n", WiFi.localIP().toString().c_str());
            break;
        case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
            Serial.println("WiFi 断开连接");
            WiFi.reconnect();  // 自动重连
            break;
    }
}

void setup() {
    Serial.begin(115200);
    WiFi.onEvent(WiFiEvent);
    WiFi.begin("SSID", "password");
}

三、AP 模式(创建热点)

#include <WiFi.h>

const char* AP_SSID = "ESP32-Config";
const char* AP_PASS = "12345678";   // 至少 8 个字符

void setup() {
    Serial.begin(115200);

    WiFi.mode(WIFI_AP);
    WiFi.softAP(AP_SSID, AP_PASS, 1, 0, 4);
    // 参数: SSID, 密码, 通道, 隐藏SSID, 最大连接数

    Serial.print("AP IP 地址: ");
    Serial.println(WiFi.softAPIP());  // 默认 192.168.4.1
}

void loop() {
    Serial.printf("已连接设备数: %d\n", WiFi.softAPgetStationNum());
    delay(5000);
}

AP 模式设置

  • 密码至少 8 个字符,设为空字符串 "" 则创建开放网络
  • 默认 IP 是 192.168.4.1,可通过 WiFi.softAPConfig() 修改
  • 最多支持 ==4 个==同时连接的设备(可配置到 10 个)

四、WiFi 扫描

#include <WiFi.h>

void setup() {
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);

    Serial.println("开始扫描 WiFi...");
    int n = WiFi.scanNetworks();

    Serial.printf("发现 %d 个网络:\n", n);
    for (int i = 0; i < n; i++) {
        Serial.printf("%2d | %-32s | CH:%2d | RSSI:%4d | %s\n",
            i + 1,
            WiFi.SSID(i).c_str(),
            WiFi.channel(i),
            WiFi.RSSI(i),
            (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "Open" : "Encrypted"
        );
    }
}

五、HTTP 客户端

发送 GET 请求

#include <WiFi.h>
#include <HTTPClient.h>

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) delay(500);
}

void loop() {
    if (WiFi.status() == WL_CONNECTED) {
        HTTPClient http;

        http.begin("http://api.example.com/data");
        int httpCode = http.GET();

        if (httpCode > 0) {
            Serial.printf("HTTP 状态码: %d\n", httpCode);
            if (httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                Serial.println(payload);
            }
        } else {
            Serial.printf("请求失败: %s\n", http.errorToString(httpCode).c_str());
        }

        http.end();
    }
    delay(10000);
}

发送 POST 请求

#include <WiFi.h>
#include <HTTPClient.h>

void sendData(float temperature, float humidity) {
    if (WiFi.status() != WL_CONNECTED) return;

    HTTPClient http;
    http.begin("http://api.example.com/sensor");
    http.addHeader("Content-Type", "application/json");

    // 构建 JSON 数据
    char jsonBuffer[128];
    snprintf(jsonBuffer, sizeof(jsonBuffer),
        "{\"temp\":%.1f,\"hum\":%.1f,\"device\":\"ESP32-01\"}",
        temperature, humidity);

    int httpCode = http.POST(jsonBuffer);

    if (httpCode > 0) {
        Serial.printf("POST 成功: %d\n", httpCode);
    } else {
        Serial.printf("POST 失败: %s\n", http.errorToString(httpCode).c_str());
    }

    http.end();
}

HTTPS 注意

访问 HTTPS 网站需要配置 CA 证书:

#include <WiFiClientSecure.h>
WiFiClientSecure client;
client.setCACert(root_ca);  // 设置根证书
// 或跳过验证(不推荐用于生产环境)
client.setInsecure();


六、HTTP 服务器(WebServer)

简单 Web 服务器

#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);  // 端口 80

float temperature = 25.5;
bool ledState = false;

void handleRoot() {
    String html = "<!DOCTYPE html><html><head>";
    html += "<meta charset='utf-8'>";
    html += "<meta name='viewport' content='width=device-width'>";
    html += "<title>ESP32 控制面板</title>";
    html += "<style>body{font-family:sans-serif;text-align:center;padding:20px;}";
    html += ".btn{padding:15px 30px;font-size:18px;margin:10px;border:none;";
    html += "border-radius:5px;cursor:pointer;}";
    html += ".on{background:#4CAF50;color:white;}";
    html += ".off{background:#f44336;color:white;}</style></head><body>";
    html += "<h1>ESP32 控制面板</h1>";
    html += "<p>温度: " + String(temperature) + " °C</p>";
    html += "<p>LED 状态: " + String(ledState ? "开" : "关") + "</p>";
    html += "<a href='/led/on'><button class='btn on'>开灯</button></a>";
    html += "<a href='/led/off'><button class='btn off'>关灯</button></a>";
    html += "</body></html>";

    server.send(200, "text/html", html);
}

void handleLedOn() {
    ledState = true;
    digitalWrite(2, HIGH);
    server.sendHeader("Location", "/");
    server.send(303);
}

void handleLedOff() {
    ledState = false;
    digitalWrite(2, LOW);
    server.sendHeader("Location", "/");
    server.send(303);
}

void setup() {
    Serial.begin(115200);
    pinMode(2, OUTPUT);

    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) delay(500);
    Serial.printf("Web 服务器: http://%s\n", WiFi.localIP().toString().c_str());

    server.on("/", handleRoot);
    server.on("/led/on", handleLedOn);
    server.on("/led/off", handleLedOff);
    server.begin();
}

void loop() {
    server.handleClient();
}

JSON API 服务器

#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

void handleGetData() {
    char json[256];
    snprintf(json, sizeof(json),
        "{\"temperature\":%.1f,\"humidity\":%.1f,\"uptime\":%lu}",
        25.5, 60.0, millis() / 1000);

    server.send(200, "application/json", json);
}

void setup() {
    // ... WiFi 连接代码 ...

    server.on("/api/data", HTTP_GET, handleGetData);

    server.on("/api/led", HTTP_POST, []() {
        if (server.hasArg("state")) {
            String state = server.arg("state");
            digitalWrite(2, state == "on" ? HIGH : LOW);
            server.send(200, "application/json", "{\"success\":true}");
        } else {
            server.send(400, "application/json", "{\"error\":\"missing state\"}");
        }
    });

    server.begin();
}

七、mDNS(域名发现)

使用 mDNS 后可以通过 http://esp32.local 访问设备,不用记 IP 地址:

#include <ESPmDNS.h>

void setup() {
    // ... WiFi 连接 ...

    if (MDNS.begin("esp32")) {  // 设备名
        Serial.println("mDNS 已启动: http://esp32.local");
        MDNS.addService("http", "tcp", 80);
    }
}

mDNS 兼容性

  • macOS / iOS:原生支持
  • Linux:需要安装 avahi-daemon
  • Windows:需安装 Bonjour 服务(安装 iTunes 会自动安装)

八、OTA 空中升级

OTA(Over-The-Air)允许通过 WiFi 上传新固件,无需 USB 线:

ArduinoOTA

#include <WiFi.h>
#include <ArduinoOTA.h>

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) delay(500);

    ArduinoOTA.setHostname("esp32-device");
    // ArduinoOTA.setPassword("admin");  // 可选密码保护

    ArduinoOTA.onStart([]() {
        Serial.println("开始 OTA 更新...");
    });

    ArduinoOTA.onEnd([]() {
        Serial.println("\nOTA 更新完成!");
    });

    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("进度: %u%%\r", (progress / (total / 100)));
    });

    ArduinoOTA.onError([](ota_error_t error) {
        Serial.printf("OTA 错误[%u]: ", error);
        if (error == OTA_AUTH_ERROR) Serial.println("认证失败");
        else if (error == OTA_CONNECT_ERROR) Serial.println("连接失败");
        else if (error == OTA_RECEIVE_ERROR) Serial.println("接收失败");
    });

    ArduinoOTA.begin();
    Serial.println("OTA 就绪");
}

void loop() {
    ArduinoOTA.handle();  // 必须在 loop 中调用
    // ... 其他代码 ...
}

OTA 使用方法

  1. 首次通过 USB 上传带 OTA 功能的固件
  2. Arduino IDE → Tools → Port → 选择网络端口(显示设备 IP)
  3. 之后就可以通过 WiFi 上传新固件

PlatformIO 中使用:

upload_protocol = espota
upload_port = 192.168.1.100


九、NTP 时间同步

#include <WiFi.h>
#include "time.h"

const char* ntpServer = "ntp.aliyun.com";           // 阿里云 NTP
const long gmtOffset_sec = 8 * 3600;                 // UTC+8 北京时间
const int daylightOffset_sec = 0;

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "password");
    while (WiFi.status() != WL_CONNECTED) delay(500);

    configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}

void loop() {
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
        Serial.printf("%04d-%02d-%02d %02d:%02d:%02d\n",
            timeinfo.tm_year + 1900,
            timeinfo.tm_mon + 1,
            timeinfo.tm_mday,
            timeinfo.tm_hour,
            timeinfo.tm_min,
            timeinfo.tm_sec);
    }
    delay(1000);
}

十、常用函数速查

函数 功能
WiFi.begin(ssid, pass) 连接 WiFi
WiFi.disconnect() 断开连接
WiFi.reconnect() 重新连接
WiFi.status() 获取连接状态
WiFi.localIP() 获取本机 IP
WiFi.macAddress() 获取 MAC 地址
WiFi.RSSI() 获取信号强度
WiFi.scanNetworks() 扫描可用网络
WiFi.softAP(ssid, pass) 创建热点
WiFi.softAPIP() 获取 AP IP
WiFi.softAPgetStationNum() 已连接设备数
WiFi.mode(mode) 设置 WiFi 模式
WiFi.setHostname(name) 设置主机名

十一、常见问题

WiFi 连接不上怎么排查?

  1. 确认 SSID 和密码完全正确(注意大小写和空格)
  2. ESP32 只支持 2.4GHz,不支持 5GHz WiFi
  3. 检查路由器是否开启了 MAC 地址过滤
  4. 尝试重启路由器
  5. 使用 WiFi.scanNetworks() 确认能扫描到目标网络
  6. 信号太弱(RSSI < -80 dBm)可能连接不稳定

WiFi 频繁断线怎么办?

  • 实现==自动重连==机制(事件回调中调用 WiFi.reconnect()
  • 检查电源稳定性(WiFi 发射时电流可达 300mA
  • 减少 WiFi 发射功率:WiFi.setTxPower(WIFI_POWER_8_5dBm)
  • 避免 WiFi 天线附近有金属遮挡

怎么让 ESP32 在没有互联网的环境中工作?

使用 AP 模式,ESP32 自己创建 WiFi 热点。手机/电脑连接 ESP32 的热点,直接访问 ESP32 的 Web 服务器页面进行控制。完全不需要路由器和互联网。

如何实现配网(SmartConfig)?

乐鑫提供 SmartConfig 协议,通过手机 APP 将 WiFi 信息发送给 ESP32:

WiFi.beginSmartConfig();
while (!WiFi.smartConfigDone()) {
    delay(500);
}
// 连接成功,WiFi 信息自动保存
需要配合乐鑫 ESP-TOUCH APP 使用。