「一、引言」
-
「应用场景与价值」 -
物联网设备的数据上报(云平台交互) -
OTA固件升级、远程配置获取 -
与Web API交互(RESTful服务) -
「ESP-IDF的HTTP解决方案优势」 -
原生支持 esp_http_client组件(低内存占用) -
内置SSL/TLS加密(ESP-TLS) -
支持HTTP/HTTPS、重定向、分块传输
「二、环境准备」
-
「硬件要求」 -
ESP32系列开发板(需WiFi连接) -
「软件配置」 -
ESP-IDF v4.4+ 开发环境 -
启用组件: idf.py menuconfig
→ Component config → ESP HTTP Client → Enable
「三、HTTP客户端基础使用」
-
「关键API解析」 esp_http_client_config_t config = {
.url = "http://example.com/api/data",
.method = HTTP_METHOD_GET,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client); -
esp_http_client_init():配置请求参数(URL、方法、超时) -
esp_http_client_perform():执行请求(阻塞式) -
esp_http_client_cleanup():释放资源 -
「处理响应数据」 char buffer[128];
int read_len;
while ((read_len = esp_http_client_read(client, buffer, sizeof(buffer)-1)) > 0) {
buffer[read_len] = '\0';
printf("%s", buffer);
} -
使用 esp_http_client_get_status_code()获取状态码 -
通过 esp_http_client_read()流式读取响应体
「四、高级功能实现」
-
「HTTPS安全连接」 -
配置证书(PEM格式): .cert_pem = (const char *)server_cert_pem_start,
.skip_cert_common_name_check = false // 严格校验域名 -
「POST请求与数据上传」 -
设置请求头 Content-Type -
发送JSON数据示例: const char *post_data = "{\"sensor\":\"temperature\",\"value\":25.5}";
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_post_field(client, post_data, strlen(post_data)); -
「处理重定向」 -
自动跟随重定向(默认启用) -
通过 esp_http_client_get_redirect_location()获取跳转URL -
「分块传输编码」 -
使用 esp_http_client_fetch_headers()预加载响应头 -
循环读取分块数据
「五、调试与性能优化」
-
「错误排查技巧」 -
常见错误码解析: ESP_ERR_HTTP_CONNECT、ESP_ERR_HTTP_TIMEOUT -
启用调试日志: esp_log_level_set("esp_http_client", ESP_LOG_DEBUG) -
「内存优化建议」 -
复用 esp_http_client句柄减少开销 -
使用流式处理避免大响应体内存溢出 -
「网络稳定性增强」 -
重试机制实现(结合 esp_http_client_set_redirection_count()) -
超时配置: .timeout_ms = 5000
「六、实战案例:获取天气API数据」
实战代码:获取天气数据并解析 (main.c)
#include <stdio.h>
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_http_client.h"
#include "cJSON.h"
#include "nvs_flash.h"
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASS "YOUR_WIFI_PASSWORD"
#define API_KEY "YOUR_OPENWEATHER_API_KEY"
#define CITY "Beijing"
#define TAG "HTTP_CLIENT"
// 根证书(OpenWeatherMap有效期至2034年)
static const char *ROOT_CA = "根证书";
// WiFi连接初始化
static void wifi_init() {
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_connect());
ESP_LOGI(TAG, "Connecting to WiFi...");
vTaskDelay(pdMS_TO_TICKS(5000));
}
// HTTP事件处理器
esp_err_t _http_event_handler(esp_http_client_event_t *evt) {
static char *response_buffer = NULL;
static int buffer_len = 0;
switch(evt->event_id) {
case HTTP_EVENT_ON_DATA:
// 动态扩展响应缓冲区
response_buffer = realloc(response_buffer, buffer_len + evt->data_len + 1);
memcpy(response_buffer + buffer_len, evt->data, evt->data_len);
buffer_len += evt->data_len;
response_buffer[buffer_len] = '\0';
break;
case HTTP_EVENT_ON_FINISH:
if (response_buffer != NULL) {
// 解析JSON响应
cJSON *root = cJSON_Parse(response_buffer);
if (root) {
cJSON *main = cJSON_GetObjectItem(root, "main");
cJSON *temp = cJSON_GetObjectItem(main, "temp");
cJSON *humidity = cJSON_GetObjectItem(main, "humidity");
ESP_LOGI(TAG, "Temperature: %.1f°C", temp->valuedouble - 273.15);
ESP_LOGI(TAG, "Humidity: %d%%", humidity->valueint);
cJSON_Delete(root);
} else {
ESP_LOGE(TAG, "JSON Parse Error: %s", cJSON_GetErrorPtr());
}
free(response_buffer);
response_buffer = NULL;
buffer_len = 0;
}
break;
case HTTP_EVENT_ERROR:
ESP_LOGE(TAG, "HTTP Error: %s", esp_err_to_name(evt->error_handle));
if (response_buffer) free(response_buffer);
response_buffer = NULL;
buffer_len = 0;
break;
default:
break;
}
return ESP_OK;
}
// 执行HTTP请求
void fetch_weather_data() {
char url[256];
snprintf(url, sizeof(url),
"https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s",
CITY, API_KEY);
esp_http_client_config_t config = {
.url = url,
.event_handler = _http_event_handler,
.cert_pem = ROOT_CA,
.timeout_ms = 8000,
.buffer_size = 4096,
.buffer_size_tx = 2048,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
int status = esp_http_client_get_status_code(client);
if (status == 200) {
ESP_LOGI(TAG, "HTTP Request Successful");
} else {
ESP_LOGE(TAG, "HTTP Status: %d", status);
}
} else {
ESP_LOGE(TAG, "HTTP Request Failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
void app_main() {
// 初始化NVS和WiFi
ESP_ERROR_CHECK(nvs_flash_init());
wifi_init();
// 等待WiFi连接
vTaskDelay(pdMS_TO_TICKS(3000));
// 获取天气数据
fetch_weather_data();
// 每5分钟更新一次
while(1) {
vTaskDelay(pdMS_TO_TICKS(5 * 60 * 1000));
fetch_weather_data();
}
}
关键代码说明:
-
「HTTPS安全连接」
-
使用硬编码的PEM格式根证书(OpenWeatherMap) -
配置 cert_pem参数启用证书验证 -
「动态响应处理」
-
在 HTTP_EVENT_ON_DATA事件中动态扩展缓冲区 -
完整接收响应后触发 HTTP_EVENT_ON_FINISH -
「JSON数据解析」
-
使用 cJSON库解析嵌套的JSON结构 -
温度单位转换(开尔文→摄氏度) -
「错误处理」
-
检查HTTP状态码(200表示成功) -
捕获JSON解析错误( cJSON_GetErrorPtr()) -
处理网络错误(超时、连接失败等) -
「性能优化」
-
设置合理的缓冲区大小(4KB接收/2KB发送) -
复用HTTP客户端连接(每次请求后清理)
使用前需要:
-
在menuconfig中启用组件: Component config → ESP HTTP Client → Enable
Component config → cJSON → Enable -
替换关键信息: -
WIFI_SSID和WIFI_PASS -
API_KEY(从OpenWeatherMap获取) -
连接WiFi后自动每5分钟获取天气数据
典型输出:
I (15823) HTTP_CLIENT: HTTP Request Successful
I (15825) HTTP_CLIENT: Temperature: 22.5°C
I (15825) HTTP_CLIENT: Humidity: 65%
此代码可直接集成到ESP-IDF项目中,实现了从HTTPS请求到数据解析的完整流程,包含生产环境所需的错误处理和资源管理机制。
「七、常见问题与解决方案」
-
「证书验证失败」 -
原因:根证书过期或域名不匹配 -
方案:更新证书或临时跳过校验(仅限测试) -
「内存泄漏排查」 -
确保每次 init后必有cleanup -
使用Heap Trace工具监控 -
「长连接保持技巧」 -
配置 Connection: keep-alive头部 -
复用TCP连接减少握手延迟
「八、总结」
-
esp_http_client的核心优势:轻量级、高集成度 -
适用场景与局限性(高并发需求建议使用HTTP Server) -
扩展方向:MQTT协议对比、自定义协议设计
「九、参考资料」
-
ESP-IDF HTTP Client官方文档 -
推荐工具:Postman(API调试)、Wireshark(网络抓包)