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 证书:
六、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 使用方法
- 首次通过 USB 上传带 OTA 功能的固件
- Arduino IDE → Tools → Port → 选择网络端口(显示设备 IP)
- 之后就可以通过 WiFi 上传新固件
PlatformIO 中使用:
九、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 连接不上怎么排查?
- 确认 SSID 和密码完全正确(注意大小写和空格)
- ESP32 只支持 2.4GHz,不支持 5GHz WiFi
- 检查路由器是否开启了 MAC 地址过滤
- 尝试重启路由器
- 使用
WiFi.scanNetworks()确认能扫描到目标网络 - 信号太弱(RSSI < -80 dBm)可能连接不稳定
WiFi 频繁断线怎么办?
- 实现==自动重连==机制(事件回调中调用
WiFi.reconnect()) - 检查电源稳定性(WiFi 发射时电流可达 300mA)
- 减少 WiFi 发射功率:
WiFi.setTxPower(WIFI_POWER_8_5dBm) - 避免 WiFi 天线附近有金属遮挡
怎么让 ESP32 在没有互联网的环境中工作?
使用 AP 模式,ESP32 自己创建 WiFi 热点。手机/电脑连接 ESP32 的热点,直接访问 ESP32 的 Web 服务器页面进行控制。完全不需要路由器和互联网。