开始前适用开发板:ESP32-S3 Korvo · 开发框架:ESP-IDF
适用开发板:Korvo S3 · 工程
02.beginner.http_request· targetesp32s3
前置:Wi-Fi Station
学习时长:约 20 分钟 · 难度:★★☆☆☆
学习目标
完成本节后,你将能够:
- 理解 ESP32 作为 HTTP 客户端主动发请求的完整流程
- 掌握 lwIP Socket 层面的 DNS → TCP → HTTP 全过程
- 区分 HTTP 请求(主动访问)与 HTTP Server(被动等待)
- 了解
protocol_examples_common公共连网组件的用法
1. 这个例子在干嘛
连接 Wi-Fi 后,用 原生 lwIP Socket API(不是高级封装)向 example.com 发送 HTTP GET 请求,把服务器返回的 HTML 打印到串口。每 10 秒重复一次。
生活类比:
http_server是开饭店等客人来(被动)http_request是自己去别人饭店吃饭(主动)
┌──────────┐ Wi-Fi ┌──────────┐ TCP ┌──────────────┐
│ ESP32-S3 │────────→│ 路由器 │────────→│ example.com │
│ Socket │ DNS │ │ HTTP │ 端口 80 │
│ 客户端 │ 解析 │ │ GET │ │
└──────────┘ └──────────┘ └──────────────┘
↓
串口打印 HTML 响应
2. 编译烧录
cd Korvo_Firmware\02.beginner.http_request
idf.py set-target esp32s3
idf.py menuconfig # Example Connection Configuration → Wi-Fi SSID/密码
idf.py build flash monitor
menuconfig 选项
使用 IDF 官方 protocol_examples_common 组件的连网配置:
| 菜单 | 选项 | 说明 |
|---|---|---|
| Example Connection Configuration | WiFi SSID | 路由器名 |
| WiFi Password | 密码 | |
| Max retry | 重试次数 |
3. 运行现象
| 阶段 | 屏幕 | 串口 |
|---|---|---|
| Wi-Fi 连接 | WiFi connecting... | wifi: connected |
| DNS 解析 | DNS resolving... | DNS lookup: 93.184.216.34 |
| TCP 连接 | Connecting... | socket connected |
| 发送请求 | Sending GET... | GET / HTTP/1.0 |
| 收到响应 | Recv: N bytes | HTML 内容打印 |
| 等待重试 | Wait 10s... | 倒计时 |
4. 关键文件与代码解析
| 文件 | 作用 | 重点 |
|---|---|---|
main/http_request_example_main.c | 核心:Wi-Fi 连接 + Socket HTTP | 全部逻辑 |
main/idf_component.yml | 引用 protocol_examples_common | 自动连网 |
核心流程
app_main()
├── ksdiy_example_display_bootstrap()
├── nvs_flash_init()
├── esp_netif_init() + esp_event_loop_create_default()
├── example_connect() // 官方组件:自动连 Wi-Fi 并等 IP
└── xTaskCreate(http_get_task)
http_get_task() — 无限循环:
├── getaddrinfo("example.com", "80", &hints, &res) // DNS 解析
├── socket(AF_INET, SOCK_STREAM, 0) // 创建 TCP socket
├── connect(sock, res->ai_addr, ...) // TCP 三次握手
├── write(sock, REQUEST, strlen(REQUEST)) // 发 HTTP 请求
├── setsockopt(SO_RCVTIMEO, 5s) // 设 5 秒接收超时
├── while (1) {
│ r = read(sock, buf, sizeof(buf)-1) // 读响应
│ if (r == 0) break // 对端关闭
│ putchar(buf[i]) // 串口输出
│ }
├── close(sock)
└── 倒计时 10 秒 → 重来
HTTP 请求报文
GET / HTTP/1.0\r\n
Host: example.com:80\r\n
User-Agent: esp-idf/1.0 esp32\r\n
\r\n
注意用的是 HTTP/1.0(短连接),服务器发完响应会主动关闭连接。
5. 原生 Socket vs esp_http_client
| 对比 | 原生 Socket(本例) | esp_http_client |
|---|---|---|
| 抽象级别 | 最底层 | 高级封装 |
| DNS 解析 | 手动 getaddrinfo() | 自动 |
| HTTPS | 需手动配 mbedTLS | 一行参数开启 |
| 重定向 | 需手动处理 | 自动跟随 |
| Chunked 编码 | 需手动解析 | 自动 |
| 学习价值 | 高:理解底层 | 生产推荐 |
6. 常见问题与排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| DNS 解析失败 | 路由器无互联网 | ping 测试路由器联网 |
| DNS 解析失败 | DNS 服务器问题 | 手动设 DNS(8.8.8.8) |
| 连接超时 | example.com 被墙 | 换国内可访问的 URL |
| 响应截断 | 单次 read buffer 小 | 循环读取直到返回 0 |
| 乱码 | 收到 gzip 压缩内容 | 添加 Accept-Encoding: identity |
| Wi-Fi 连不上 | SSID/密码错误 | menuconfig 确认 |
7. 动手改造建议
- 改目标 URL:访问自己的服务器或 API
- JSON 解析:请求天气 API → cJSON 解析温度显示在屏幕
- 升级 HTTPS:改用
esp_http_client+ CA 证书访问 https:// - POST 请求:修改请求方法为 POST + JSON body 上报数据
- 对比 HTTP Server:同时跑
http_server,ESP32 既是客户端又是服务端
8. 知识延伸
- getaddrinfo():POSIX 标准 DNS 解析函数,把域名转成 IP 地址结构体
- Socket 编程三部曲:
socket()创建 →connect()连接 →read()/write()通信 - HTTP/1.0 vs 1.1:1.0 默认短连接(用完关),1.1 默认长连接(复用 TCP)
- 为什么用原生 Socket:教学目的——理解 HTTP 底层就是 TCP 上发文本
9. 下一步
| 方向 | 推荐例程 | 说明 |
|---|---|---|
| HTTP Server | HTTP Server | 被动提供服务 |
| MQTT | MQTT | IoT 长连接通信 |
| OTA | OTA | HTTP 下载固件 |
| P4C5 对比 | P4C5 HTTP Request | C5 联网版 |
酷世DIY · Kevincoooool