Wi-Fi 工作模式
ESP32 的 Wi-Fi 驱动支持三种工作模式,可以根据项目需求灵活切换。理解这三种模式是设计 IoT 产品网络架构的基础。
Wi-Fi STA 连接流程
STA 模式完整代码
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "freertos/event_groups.h"
#include "nvs_flash.h"
#define WIFI_SSID "YourSSID"
#define WIFI_PASSWORD "YourPassword"
#define WIFI_RETRY_MAX 5
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "WIFI";
static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < WIFI_RETRY_MAX) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGW(TAG, "重连中... 第 %d 次", s_retry_num);
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "获取 IP: " IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
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_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler, NULL, NULL);
esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&event_handler, NULL, NULL);
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
/* 等待连接成功或彻底失败 */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "Wi-Fi 已连接!");
} else {
ESP_LOGE(TAG, "Wi-Fi 连接失败");
}
}
void app_main(void)
{
nvs_flash_init(); // Wi-Fi 驱动需要 NVS 存储校准数据
wifi_init_sta();
/* 此后可开始网络通信 */
}
HTTP 客户端
ESP-IDF 内置 esp_http_client 组件,支持 HTTP 和 HTTPS、GET/POST/PUT 等方法、分块传输等功能,是调用 REST API 和云端服务的标准方式。
#include "esp_http_client.h"
#include "esp_log.h"
static const char *TAG = "HTTP";
/* 事件处理回调(可选) */
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ON_DATA:
/* 接收到数据时回调,evt->data 和 evt->data_len */
ESP_LOGI(TAG, "收到 %d 字节", evt->data_len);
break;
default: break;
}
return ESP_OK;
}
void http_get_example(void)
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/get",
.event_handler = http_event_handler,
.buffer_size = 1024,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP GET 状态码: %d,内容长度: %lld",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
}
esp_http_client_cleanup(client);
}
void http_post_json(void)
{
const char *post_data = "{\"temperature\":25.6,\"humidity\":65.3}";
esp_http_client_config_t config = {
.url = "http://your-server.com/api/data",
.method = HTTP_METHOD_POST,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
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_perform(client);
esp_http_client_cleanup(client);
}
HTTPS 与证书验证
生产环境中必须使用 HTTPS 来保护数据传输。ESP-IDF 使用 mbedTLS 实现 TLS,证书验证是防止中间人攻击的关键。
有些教程为图方便设置 .skip_cert_common_name_check = true 或不提供 CA 证书,这相当于不做 HTTPS 验证,与 HTTP 一样不安全。IoT 设备在生产中必须正确配置服务器 CA 证书。
/* 将服务器的 CA 证书嵌入固件(CMakeLists.txt 中添加 target_add_binary_data) */
extern const char server_cert_pem_start[] asm("_binary_server_cert_pem_start");
extern const char server_cert_pem_end[] asm("_binary_server_cert_pem_end");
void https_get(void)
{
esp_http_client_config_t config = {
.url = "https://api.example.com/sensors",
.cert_pem = server_cert_pem_start, // 服务器 CA 证书
.transport_type = HTTP_TRANSPORT_OVER_SSL,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_http_client_perform(client);
esp_http_client_cleanup(client);
}
OTA 空中升级原理
OTA(Over-The-Air)升级让 ESP32 无需物理连接即可更新固件,是 IoT 产品运维的核心功能。ESP-IDF 通过分区表实现:Flash 中划分两个 OTA 分区(ota_0 和 ota_1),轮流烧录新固件,验证成功后切换启动。
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#define OTA_URL "https://your-server.com/firmware.bin"
#define OTA_BUF_SIZE 4096
void ota_task(void *arg)
{
esp_ota_handle_t update_handle;
const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL);
esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
/* 下载并写入固件... */
char buf[OTA_BUF_SIZE];
int data_read;
/* ... HTTP 读取循环 ... */
esp_ota_write(update_handle, buf, data_read);
esp_ota_end(update_handle);
esp_ota_set_boot_partition(update_partition);
esp_restart();
vTaskDelete(NULL);
}
ESP-IDF 官方提供了 examples/system/ota/native_ota_example,包含完整的 HTTPS OTA 流程,建议直接参考该示例,它处理了分块下载、错误回滚等边界情况。
ESP32 Wi-Fi 通过事件驱动模型管理连接状态:esp_event_handler_register() 注册 WIFI_EVENT 和 IP_EVENT 回调,WIFI_EVENT_STA_CONNECTED 表示关联成功,IP_EVENT_STA_GOT_IP 表示 DHCP 获取 IP 后才能进行 TCP/IP 通信。实际工程必须处理断线重连(最大重试次数 + 延时退避)。HTTP 客户端用 esp_http_client,支持流式下载大文件(分块读取,避免一次性分配大缓冲)。AP 模式可同时运行 Station(WIFI_MODE_APSTA),适合配网场景。OTA 升级利用分区表中的两个 OTA 分区(ota_0/ota_1)交替写入,esp_ota_set_boot_partition() 切换启动分区,断电后安全重启。生产固件应将 OTA 服务器证书嵌入固件,强制 TLS 验证,防止中间人攻击。