ESP32 Wi-Fi 联网第一课:STA/AP 双模式、连接管理与 HTTP 请求实战
里程碑:到这里,你已经会用 GPIO、ADC、PWM、总线与本地外设对话。接通 Wi-Fi 之后,ESP32 从「单片机」升级为「物联网设备」——数据能上云、手机能控制。
一、三种工作模式
| 模式 | 角色 | 场景 |
|---|---|---|
| STA(Station) | 连接路由器的「客户端」 | 设备联网拿数据、上报传感器 |
| AP(Access Point) | 自己发出热点 | 无路由器时的配置入口(手机直连设备) |
| AP+STA | 同时两者 | 配网模式经典架构:AP 收配置 → STA 连路由 |
WiFi.mode(WIFI_STA); // 客户端模式(最常用)
WiFi.mode(WIFI_AP); // 热点模式
WiFi.mode(WIFI_AP_STA); // 双模式
经典 ESP32 仅支持 2.4GHz——公司/家里的 5GHz 热点搜不到属正常,不是故障。
二、连接:阻塞版与事件版
1. 阻塞版(原型验证够用)
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.begin("你的SSID", "你的密码");
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.printf("\n已连接! IP: %s\n", WiFi.localIP().toString().c_str());
}
问题:连不上就死等(还记得定时器篇的看门狗吗?)。
2. 事件驱动版(生产姿势)
WiFiEventHandler onGotIP, onDisconnect;
void setupWiFi() {
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true); // 断线自动重连
WiFi.persistent(false); // 不把密码写 Flash(省 NVS 磨损)
onGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &e) {
Serial.printf("已连接, IP: %s\n", e.ip.toString().c_str());
});
onDisconnect = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected &e) {
Serial.println("断线,自动重连中...");
});
WiFi.begin("你的SSID", "你的密码");
}
要点:别在 loop 里反复
WiFi.begin()——每次调用都重启射频,反而更连不上。begin 一次 + 自动重连 + 事件回调,是标准架构。
三、HTTP 请求:GET 与 POST
#include <WiFi.h>
#include <HTTPClient.h>
void fetchWeather() {
HTTPClient http;
http.begin("http://api.example.com/weather?city=shenzhen");
int code = http.GET(); // 发起请求
if (code == 200) {
String payload = http.getString(); // 响应体
Serial.println(payload);
}
http.end(); // 释放连接(必写!)
}
void postData() {
HTTPClient http;
http.begin("http://api.example.com/log");
http.addHeader("Content-Type", "application/json");
int code = http.POST("{\"temp\":25.5}");
Serial.printf("POST 状态: %d\n", code);
http.end();
}
HTTPS:URL 换
https://即可(esp32 内置根证书校验,个别自签服务需配置)。生产代码建议一律 HTTPS。
四、ArduinoJson:解析响应
#include <ArduinoJson.h>
void parseWeather(String json) {
JsonDocument doc; // v7 自动容量
DeserializationError err = deserializeJson(doc, json);
if (err) { Serial.println("JSON 解析失败"); return; }
const char* city = doc["city"];
float temp = doc["temperature"];
int humidity = doc["humidity"];
Serial.printf("%s: %.1f°C, 湿度 %d%%\n", city, temp, humidity);
}
v6 与 v7 API 不同(
StaticJsonDocument/DynamicJsonDocumentvsJsonDocument),报「未定义」时先查库版本——与 LEDC 的新旧 API 分水岭同款剧情。
五、实战:温湿度上报完整闭环
把本专栏已学全部串起来(L2 验收项目之一):
// 架构:定时器(采集) → I2C(读AHT20) → Wi-Fi(HTTP上报) → 非阻塞主循环
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
uint32_t tReport = 0;
void loop() {
if (WiFi.status() == WL_CONNECTED && millis() - tReport > 60000) {
tReport = millis();
float temp = readAht20Temp(); // I2C 总线篇的实现
JsonDocument doc;
doc["temp"] = temp;
String body;
serializeJson(doc, body);
HTTPClient http;
http.begin("http://api.example.com/report");
http.addHeader("Content-Type", "application/json");
http.POST(body);
http.end();
}
}
六、避坑速查表(收藏级)
| 症状 | 根因与对策 |
|---|---|
| 搜不到 5GHz 热点 | 仅支持 2.4GHz,正常现象 |
| 连接 15 秒超时 | 路由器 5G 频段/中文 SSID/密码错;用手机热点先验证 |
| 反复 begin 反而连不上 | begin 只调一次,交给自动重连 |
| HTTP 返回 -1 | 未连网就请求;或域名 DNS 失败,检查 WiFi.status() |
http.end() 忘写 | 连接泄漏,几次后 new 连接失败 |
| 偶发 Brownout 重启 | 射频瞬态电流 ~450mA,见选型指南电源陷阱 |
| JSON 「out of memory」 | v6 手动容量给小了;升 v7 或加大 |
| 上电卡 30 秒才连上 | NVS 存了旧配置 + 阻塞等待;改事件驱动 |
七、延伸阅读
- ⏭️ 下一篇:WebServer 与 mDNS——让手机浏览器直接控制 ESP32
- 🧭 地基:定时器——非阻塞周期上报
- 📚 深入方向:MQTT(L3)、ESP-NOW(L3)、深度睡眠低功耗上报(L3)
版权声明:本文采用 CC BY-SA 4.0 协议授权。转载与二创请注明出处:ESPDIY 创客工坊 - espdiy.com
ESP32 Wi-Fi First Steps: STA/AP Modes, Connection Management and HTTP
Milestone: at this point you command GPIO, ADC, PWM and the buses for local peripherals. Bring Wi-Fi up and the ESP32 graduates from “microcontroller” to “IoT device” — data reaches the cloud, phones reach the device.
1. The three operating modes
| Mode | Role | Use case |
|---|---|---|
| STA (Station) | Client that joins a router | Devices fetching/reporting data |
| AP (Access Point) | Emits its own hotspot | Config portal without a router (phone connects to the device) |
| AP+STA | Both at once | The classic provisioning architecture: AP collects config → STA joins the router |
WiFi.mode(WIFI_STA); // client mode (most common)
WiFi.mode(WIFI_AP); // hotspot mode
WiFi.mode(WIFI_AP_STA); // dual mode
The classic ESP32 is 2.4GHz-only — not finding your 5GHz network is normal, not a fault.
2. Connecting: blocking vs event-driven
1. Blocking version (fine for prototypes)
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.printf("\nConnected! IP: %s\n", WiFi.localIP().toString().c_str());
}
Problem: if it can’t connect, it waits forever (remember the watchdog from timers?).
2. Event-driven version (production posture)
WiFiEventHandler onGotIP, onDisconnect;
void setupWiFi() {
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true); // automatic reconnection
WiFi.persistent(false); // don't wear the flash with credentials
onGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &e) {
Serial.printf("Connected, IP: %s\n", e.ip.toString().c_str());
});
onDisconnect = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected &e) {
Serial.println("Disconnected, auto-reconnecting...");
});
WiFi.begin("YOUR_SSID", "YOUR_PASSWORD");
}
Key point: never call
WiFi.begin()repeatedly in loop — every call restarts the radio and makes things worse. begin once + auto-reconnect + event callbacks is the standard architecture.
3. HTTP: GET and POST
#include <WiFi.h>
#include <HTTPClient.h>
void fetchWeather() {
HTTPClient http;
http.begin("http://api.example.com/weather?city=shenzhen");
int code = http.GET();
if (code == 200) {
String payload = http.getString();
Serial.println(payload);
}
http.end(); // release the connection (mandatory!)
}
void postData() {
HTTPClient http;
http.begin("http://api.example.com/log");
http.addHeader("Content-Type", "application/json");
int code = http.POST("{\"temp\":25.5}");
Serial.printf("POST status: %d\n", code);
http.end();
}
HTTPS: just switch the URL to
https://(root certificates are built in; some self-signed services need config). Production code should be HTTPS-only.
4. ArduinoJson: parsing responses
#include <ArduinoJson.h>
void parseWeather(String json) {
JsonDocument doc; // v7 auto-capacity
DeserializationError err = deserializeJson(doc, json);
if (err) { Serial.println("JSON parse failed"); return; }
const char* city = doc["city"];
float temp = doc["temperature"];
int humidity = doc["humidity"];
Serial.printf("%s: %.1f°C, humidity %d%%\n", city, temp, humidity);
}
v6 and v7 differ (
StaticJsonDocument/DynamicJsonDocumentvsJsonDocument) — “not declared” errors mean you hit the version watershed, same story as LEDC.
5. Project: the full temperature reporting loop
Wiring together everything from this column (an L2 acceptance project):
// Architecture: timer(collect) → I2C(read AHT20) → Wi-Fi(HTTP report) → non-blocking loop
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
uint32_t tReport = 0;
void loop() {
if (WiFi.status() == WL_CONNECTED && millis() - tReport > 60000) {
tReport = millis();
float temp = readAht20Temp(); // from the buses guide
JsonDocument doc;
doc["temp"] = temp;
String body;
serializeJson(doc, body);
HTTPClient http;
http.begin("http://api.example.com/report");
http.addHeader("Content-Type", "application/json");
http.POST(body);
http.end();
}
}
6. Pitfall cheat sheet (bookmark this)
| Symptom | Cause and fix |
|---|---|
| Can’t see the 5GHz network | 2.4GHz-only — normal |
| 15-second connect timeout | 5GHz SSID / non-ASCII SSID / wrong password; verify with a phone hotspot |
| Repeated begin makes it worse | begin once; leave reconnecting to auto-reconnect |
| HTTP returns -1 | Requested while offline; or DNS failure — check WiFi.status() |
Forgot http.end() | Connection leak — later requests fail |
| Random Brownout reboots | Radio transients ~450mA — power traps in the Chip Selection Guide |
| JSON “out of memory” | v6 manual capacity too small; upgrade to v7 or enlarge |
| 30s stall on boot | Stale NVS config + blocking wait; go event-driven |
7. Further reading
- ⏭️ Next: WebServer & mDNS — control the ESP32 from a phone browser
- 🧭 Foundation: Timers — non-blocking periodic reporting
- 📚 Going deeper: MQTT (L3), ESP-NOW (L3), deep-sleep low-power reporting (L3)
License: CC BY-SA 4.0. Repost & remix with attribution: ESPDIY Maker Lab - espdiy.com