当前位置: 首页 > news >正文

【ESP32-S3 从入门到精通-06】2026 最新 Wi-Fi 网络开发与配网技术全实战(Station/AP/TCP/UDP/SmartConfig)


写在前面

大家好,我是 EmbeddedCore。上一讲我们学习了 FreeRTOS 实时操作系统,掌握了多任务编程、任务间通信与同步的核心技术。从这一讲开始,我们将正式进入物联网的核心 ——网络开发

Wi-Fi 是 ESP32-S3 最具竞争力的功能之一,也是物联网设备连接云端、实现智能化的基础。无论是远程控制设备、数据上传云端,还是固件 OTA 升级,都离不开 Wi-Fi 网络的支持。

本讲将从最基础的 Wi-Fi 连接讲起,一步步带你掌握 ESP32-S3 的所有 Wi-Fi 相关功能。我们不仅会学习如何连接路由器和创建热点,还会深入学习 TCP/UDP 网络通信、HTTP 协议以及各种智能配网技术。学完本讲,你将能够独立开发出能够连接互联网的物联网设备。


一、Wi-Fi 基础与 ESP32-S3 Wi-Fi 架构

1.1 Wi-Fi 基本概念

Wi-Fi 是一种基于 IEEE 802.11 标准的无线局域网技术,它使用 2.4GHz 和 5GHz 频段进行数据传输。ESP32-S3 支持 Wi-Fi 4(802.11b/g/n)标准,最高传输速率可达 150Mbps。

1.2 ESP32-S3 Wi-Fi 工作模式

ESP32-S3 支持三种 Wi-Fi 工作模式:

表格

工作模式描述适用场景
Station 模式(站点模式)ESP32-S3 作为一个无线终端,连接到路由器等 Wi-Fi 热点大多数物联网应用,设备需要连接互联网
AP 模式(接入点模式)ESP32-S3 自己创建一个 Wi-Fi 热点,其他设备可以连接到它智能配网、设备直连、无路由器环境
混合模式(Station+AP)ESP32-S3 同时作为站点和接入点设备在连接路由器的同时,也允许其他设备连接自己

1.3 ESP-IDF Wi-Fi 事件驱动架构

ESP-IDF 的 Wi-Fi 子系统采用事件驱动架构,这是 ESP-IDF v5.0 之后最重要的变化之一。当 Wi-Fi 状态发生变化时(如连接成功、断开连接、获取 IP 地址等),系统会发送相应的事件,我们只需要注册事件处理函数来响应这些事件即可。

这种架构的优点是:

  • 代码结构清晰,逻辑分明
  • 不需要在主循环中轮询 Wi-Fi 状态
  • 响应及时,效率高

ESP-IDF 中最重要的两个事件组是:

  • WIFI_EVENT:Wi-Fi 相关事件,如WIFI_EVENT_STA_CONNECTEDWIFI_EVENT_STA_DISCONNECTED
  • IP_EVENT:IP 相关事件,如IP_EVENT_STA_GOT_IPIP_EVENT_STA_LOST_IP

二、Wi-Fi Station 模式开发(连接路由器)

Station 模式是最常用的 Wi-Fi 工作模式,它允许 ESP32-S3 连接到现有的 Wi-Fi 路由器,从而访问互联网。

2.1 Wi-Fi Station 初始化流程

ESP32-S3 Wi-Fi Station 模式的初始化流程如下:

  1. 初始化 NVS Flash(用于存储 Wi-Fi 配置信息)
  2. 初始化网络接口
  3. 创建默认事件循环
  4. 创建默认 Wi-Fi Station 网络接口
  5. 初始化 Wi-Fi 驱动
  6. 注册 Wi-Fi 事件和 IP 事件处理函数
  7. 配置 Wi-Fi 参数(SSID 和密码)
  8. 设置 Wi-Fi 模式为 Station 模式
  9. 启动 Wi-Fi 驱动

2.2 实战项目 1:Wi-Fi 自动连接与重连

这是最基础的 Wi-Fi 实验,我们将实现 ESP32-S3 自动连接到指定的 Wi-Fi 路由器,并在断开连接时自动重连。

完整代码

c

运行

#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" static const char *TAG = "WIFI_STA"; #define WIFI_SSID "你的Wi-Fi名称" #define WIFI_PASS "你的Wi-Fi密码" // Wi-Fi事件处理函数 static void wifi_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) { // Wi-Fi启动完成,开始连接 ESP_LOGI(TAG, "Wi-Fi started, connecting to %s...", WIFI_SSID); esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { // 连接成功 ESP_LOGI(TAG, "Connected to %s successfully", WIFI_SSID); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { // 连接断开,自动重连 ESP_LOGW(TAG, "Wi-Fi disconnected, retrying..."); esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { // 获取到IP地址 ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ESP_LOGI(TAG, "Got IP address: " IPSTR, IP2STR(&event->ip_info.ip)); ESP_LOGI(TAG, "Gateway: " IPSTR, IP2STR(&event->ip_info.gw)); ESP_LOGI(TAG, "Netmask: " IPSTR, IP2STR(&event->ip_info.netmask)); } } void wifi_init_sta(void) { // 1. 初始化NVS Flash esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // 2. 初始化网络接口 ESP_ERROR_CHECK(esp_netif_init()); // 3. 创建默认事件循环 ESP_ERROR_CHECK(esp_event_loop_create_default()); // 4. 创建默认Wi-Fi Station网络接口 esp_netif_create_default_wifi_sta(); // 5. 初始化Wi-Fi驱动 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // 6. 注册事件处理函数 ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL)); // 7. 配置Wi-Fi参数 wifi_config_t wifi_config = { .sta = { .ssid = WIFI_SSID, .password = WIFI_PASS, .threshold.authmode = WIFI_AUTH_WPA2_PSK, // 只连接WPA2加密的热点 }, }; // 8. 设置Wi-Fi模式为Station模式 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); // 9. 配置Wi-Fi参数 ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); // 10. 启动Wi-Fi ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "Wi-Fi Station initialization completed"); } void app_main(void) { wifi_init_sta(); }

代码说明

  • 我们使用了 ESP-IDF 的日志系统(ESP_LOGIESP_LOGW)来输出调试信息,比printf更专业
  • 添加了自动重连功能,当 Wi-Fi 断开连接时会自动尝试重新连接
  • 设置了认证模式为WIFI_AUTH_WPA2_PSK,只连接安全的 WPA2 加密热点
  • 打印了详细的网络信息,包括 IP 地址、网关和子网掩码

实验步骤

  1. 将代码中的WIFI_SSIDWIFI_PASS修改为你自己的 Wi-Fi 名称和密码
  2. 编译烧录代码到 ESP32-S3 开发板
  3. 打开串口监控,你将看到类似以下的输出:

    plaintext

    I (27) wifi:wifi driver task: 3ffc1d88, prio:23, stack:3584, core=0 I (31) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE I (41) wifi:wifi firmware version: 7d240d5 I (45) wifi:wifi certification version: v7.0 I (49) wifi:config NVS flash: enabled I (53) wifi:config nano formating: disabled I (57) wifi:Init data frame dynamic rx buffer num: 32 I (62) wifi:Init management frame dynamic rx buffer num: 32 I (67) wifi:Init management frame dynamic tx buffer num: 32 I (72) wifi:Init static rx buffer size: 1600 I (76) wifi:Init static rx buffer num: 10 I (80) wifi:Init dynamic rx buffer num: 32 I (84) wifi_init: rx ba win: 6 I (88) wifi_init: tcpip mbox: 32 I (92) wifi_init: udp mbox: 6 I (96) wifi_init: tcp mbox: 6 I (100) wifi_init: tcp tx win: 5744 I (104) wifi_init: tcp rx win: 5744 I (108) wifi_init: tcp mss: 1440 I (112) wifi_init: WiFi IRAM OP enabled I (116) wifi_init: WiFi RX IRAM OP enabled I (121) phy_init: phy_version 4670,phy0 Jun 21 2025,13:29:07 I (130) wifi:set rx active PTI: 0, rx ack PTI: 0, and default PTI: 0 I (136) wifi:mode : sta (7c:df:a1:xx:xx:xx) I (140) wifi:enable tsf I (143) WIFI_STA: Wi-Fi started, connecting to your_ssid... I (148) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1 I (856) wifi:state: init -> auth (b0) I (860) wifi:state: auth -> assoc (0) I (864) wifi:state: assoc -> run (10) I (872) wifi:connected with your_ssid, aid = 1, channel 1, BW20, bssid = xx:xx:xx:xx:xx:xx I (881) wifi:security: WPA2-PSK, phy: bgn, rssi: -58 I (886) WIFI_STA: Connected to your_ssid successfully I (891) wifi:pm start, type: 1 I (957) wifi:AP's beacon interval = 102400 us, DTIM period = 1 I (1456) esp_netif_handlers: sta ip: 192.168.1.100, mask: 255.255.255.0, gw: 192.168.1.1 I (1465) WIFI_STA: Got IP address: 192.168.1.100 I (1471) WIFI_STA: Gateway: 192.168.1.1 I (1476) WIFI_STA: Netmask: 255.255.255.0

恭喜你!你的 ESP32-S3 已经成功连接到 Wi-Fi 网络了!

2.3 Wi-Fi 扫描功能

ESP32-S3 还支持 Wi-Fi 扫描功能,可以扫描周围所有可用的 Wi-Fi 热点。

Wi-Fi 扫描代码

c

运行

void wifi_scan(void) { ESP_LOGI(TAG, "Scanning Wi-Fi networks..."); wifi_scan_config_t scan_config = { .ssid = NULL, .bssid = NULL, .channel = 0, .show_hidden = true }; ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true)); uint16_t ap_count = 0; ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count)); wifi_ap_record_t *ap_records = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * ap_count); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_records)); ESP_LOGI(TAG, "Found %d Wi-Fi networks:", ap_count); for(int i = 0; i < ap_count; i++) { ESP_LOGI(TAG, "%d. SSID: %s, Channel: %d, RSSI: %d dBm", i+1, ap_records[i].ssid, ap_records[i].primary, ap_records[i].rssi); } free(ap_records); }

三、Wi-Fi AP 模式开发(创建热点)

AP 模式允许 ESP32-S3 自己创建一个 Wi-Fi 热点,其他设备(如手机、电脑)可以连接到这个热点,实现设备之间的直接通信。

3.1 实战项目 2:创建 Wi-Fi 热点

完整代码

c

运行

#include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" #include "esp_netif.h" static const char *TAG = "WIFI_AP"; #define AP_SSID "ESP32-S3-AP" #define AP_PASS "12345678" #define AP_CHANNEL 1 #define AP_MAX_CONN 4 static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_id == WIFI_EVENT_AP_STACONNECTED) { wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; ESP_LOGI(TAG, "Station " MACSTR " connected, AID: %d", MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; ESP_LOGI(TAG, "Station " MACSTR " disconnected, AID: %d", MAC2STR(event->mac), event->aid); } } void wifi_init_ap(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_ap(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL)); wifi_config_t wifi_config = { .ap = { .ssid = AP_SSID, .ssid_len = strlen(AP_SSID), .password = AP_PASS, .channel = AP_CHANNEL, .authmode = WIFI_AUTH_WPA2_PSK, .max_connection = AP_MAX_CONN, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "Wi-Fi AP started"); ESP_LOGI(TAG, "SSID: %s", AP_SSID); ESP_LOGI(TAG, "Password: %s", AP_PASS); ESP_LOGI(TAG, "Channel: %d", AP_CHANNEL); ESP_LOGI(TAG, "Max connections: %d", AP_MAX_CONN); } void app_main(void) { wifi_init_ap(); }

实验现象

  • 编译烧录代码后,ESP32-S3 会创建一个名为 "ESP32-S3-AP" 的 Wi-Fi 热点
  • 你可以用手机或电脑搜索并连接这个热点,密码是 "12345678"
  • 当有设备连接或断开时,串口会打印相应的信息

四、TCP/UDP 网络通信

Wi-Fi 连接成功后,我们就可以进行网络通信了。TCP 和 UDP 是互联网中最常用的两种传输层协议。

4.1 TCP vs UDP

表格

特性TCPUDP
连接方式面向连接无连接
可靠性可靠,保证数据按序到达不可靠,不保证数据到达和顺序
速度较慢较快
适用场景文件传输、网页浏览、邮件视频通话、语音通话、实时数据传输

4.2 实战项目 3:TCP 服务器

我们将创建一个 TCP 服务器,监听端口 8080,接收客户端发送的数据并原样返回(回显服务器)。

完整代码

c

运行

#include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" #include "lwip/sockets.h" #include "lwip/netdb.h" static const char *TAG = "TCP_SERVER"; #define WIFI_SSID "你的Wi-Fi名称" #define WIFI_PASS "你的Wi-Fi密码" #define TCP_PORT 8080 #define BUF_SIZE 1024 // Wi-Fi初始化代码(同上,此处省略) void wifi_init_sta(void) { ... } void tcp_server_task(void *pvParameters) { char rx_buffer[BUF_SIZE]; char addr_str[128]; int addr_family = AF_INET; int ip_protocol = IPPROTO_IP; struct sockaddr_in dest_addr; dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(TCP_PORT); // 创建socket int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); if(listen_sock < 0) { ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); vTaskDelete(NULL); return; } // 绑定socket int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if(err != 0) { ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); close(listen_sock); vTaskDelete(NULL); return; } // 开始监听 err = listen(listen_sock, 1); if(err != 0) { ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno); close(listen_sock); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "TCP server listening on port %d", TCP_PORT); while(1) { struct sockaddr_in source_addr; socklen_t addr_len = sizeof(source_addr); // 等待客户端连接 int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); if(sock < 0) { ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno); break; } // 转换客户端IP地址为字符串 inet_ntoa_r(source_addr.sin_addr, addr_str, sizeof(addr_str) - 1); ESP_LOGI(TAG, "Client connected: %s", addr_str); // 处理客户端数据 while(1) { // 接收数据 int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); if(len < 0) { ESP_LOGE(TAG, "Error occurred during recv: errno %d", errno); break; } else if(len == 0) { ESP_LOGI(TAG, "Client disconnected"); break; } // 处理数据 rx_buffer[len] = '\0'; ESP_LOGI(TAG, "Received %d bytes from %s: %s", len, addr_str, rx_buffer); // 回显数据 send(sock, rx_buffer, len, 0); } // 关闭连接 close(sock); } close(listen_sock); vTaskDelete(NULL); } void app_main(void) { wifi_init_sta(); // 创建TCP服务器任务 xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL); }

实验步骤

  1. 修改代码中的 Wi-Fi 名称和密码
  2. 编译烧录代码
  3. 打开串口监控,记录 ESP32-S3 的 IP 地址
  4. 打开电脑上的网络调试助手(如 NetAssist)
  5. 选择 TCP 客户端模式,输入 ESP32-S3 的 IP 地址和端口 8080,点击连接
  6. 发送任意数据,你将收到 ESP32-S3 原样返回的数据

4.3 实战项目 4:UDP 客户端

我们将创建一个 UDP 客户端,向指定的服务器发送数据。

完整代码

c

运行

#include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" #include "lwip/sockets.h" #include "lwip/netdb.h" static const char *TAG = "UDP_CLIENT"; #define WIFI_SSID "你的Wi-Fi名称" #define WIFI_PASS "你的Wi-Fi密码" #define UDP_SERVER_IP "192.168.1.100" // 你的电脑IP地址 #define UDP_SERVER_PORT 8080 #define BUF_SIZE 1024 // Wi-Fi初始化代码(同上,此处省略) void wifi_init_sta(void) { ... } void udp_client_task(void *pvParameters) { char tx_buffer[BUF_SIZE]; int count = 0; struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(UDP_SERVER_PORT); inet_pton(AF_INET, UDP_SERVER_IP, &dest_addr.sin_addr); // 创建socket int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sock < 0) { ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "UDP client started, sending to %s:%d", UDP_SERVER_IP, UDP_SERVER_PORT); while(1) { // 构造数据 sprintf(tx_buffer, "Hello from ESP32-S3! Count: %d", count++); // 发送数据 int err = sendto(sock, tx_buffer, strlen(tx_buffer), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if(err < 0) { ESP_LOGE(TAG, "Error occurred during sendto: errno %d", errno); } else { ESP_LOGI(TAG, "Sent %d bytes: %s", err, tx_buffer); } vTaskDelay(pdMS_TO_TICKS(1000)); } close(sock); vTaskDelete(NULL); } void app_main(void) { wifi_init_sta(); // 创建UDP客户端任务 xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL); }

五、HTTP 客户端开发

HTTP 是互联网中最常用的应用层协议,我们可以使用 HTTP 协议访问互联网上的各种 API。

5.1 ESP-IDF HTTP 客户端组件

ESP-IDF 提供了一个功能强大的 HTTP 客户端组件esp_http_client,它支持 GET、POST、PUT、DELETE 等 HTTP 方法,支持 HTTPS,支持重定向,支持 Cookie 等。

5.2 实战项目 5:HTTP 客户端获取天气信息

我们将使用 HTTP 客户端访问和风天气 API,获取实时天气信息。

完整代码

c

运行

#include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" #include "esp_http_client.h" #include "cJSON.h" static const char *TAG = "HTTP_CLIENT"; #define WIFI_SSID "你的Wi-Fi名称" #define WIFI_PASS "你的Wi-Fi密码" #define WEATHER_API_KEY "你的和风天气API密钥" #define CITY_ID "101010100" // 北京的城市ID // Wi-Fi初始化代码(同上,此处省略) void wifi_init_sta(void) { ... } // HTTP事件处理函数 esp_err_t http_event_handler(esp_http_client_event_t *evt) { static char *response_buffer = NULL; static int response_len = 0; switch(evt->event_id) { case HTTP_EVENT_ON_DATA: // 接收HTTP响应数据 if(!esp_http_client_is_chunked_response(evt->client)) { // 重新分配缓冲区 response_buffer = realloc(response_buffer, response_len + evt->data_len + 1); memcpy(response_buffer + response_len, evt->data, evt->data_len); response_len += evt->data_len; response_buffer[response_len] = '\0'; } break; case HTTP_EVENT_ON_FINISH: // HTTP请求完成,解析JSON数据 if(response_buffer != NULL) { ESP_LOGI(TAG, "HTTP response: %s", response_buffer); // 解析JSON cJSON *root = cJSON_Parse(response_buffer); if(root != NULL) { cJSON *now = cJSON_GetObjectItemCaseSensitive(root, "now"); if(now != NULL) { cJSON *temp = cJSON_GetObjectItemCaseSensitive(now, "temp"); cJSON *text = cJSON_GetObjectItemCaseSensitive(now, "text"); if(temp != NULL && text != NULL) { ESP_LOGI(TAG, "=================================="); ESP_LOGI(TAG, "北京天气: %s", text->valuestring); ESP_LOGI(TAG, "温度: %s℃", temp->valuestring); ESP_LOGI(TAG, "=================================="); } } cJSON_Delete(root); } free(response_buffer); response_buffer = NULL; response_len = 0; } break; default: break; } return ESP_OK; } void http_client_task(void *pvParameters) { char url[256]; // 构造API请求URL sprintf(url, "https://devapi.qweather.com/v7/weather/now?location=%s&key=%s", CITY_ID, WEATHER_API_KEY); esp_http_client_config_t config = { .url = url, .event_handler = http_event_handler, .skip_cert_common_name_check = true, // 跳过证书验证(仅用于测试) }; while(1) { ESP_LOGI(TAG, "Fetching weather information..."); 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 request successful, status code: %d", esp_http_client_get_status_code(client)); } else { ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); } esp_http_client_cleanup(client); vTaskDelay(pdMS_TO_TICKS(300000)); // 每5分钟更新一次天气 } vTaskDelete(NULL); } void app_main(void) { wifi_init_sta(); // 创建HTTP客户端任务 xTaskCreate(http_client_task, "http_client", 8192, NULL, 5, NULL); }

注意

  1. 你需要先到和风天气官网(和风天气开发服务 ~ 强大、丰富的天气数据服务)注册账号,获取免费的 API 密钥
  2. 将代码中的WEATHER_API_KEY替换为你自己的 API 密钥
  3. 你可以在和风天气官网查询你所在城市的 ID,替换CITY_ID

六、智能配网技术

在实际的物联网产品中,我们不可能将 Wi-Fi 名称和密码硬编码到固件中。我们需要一种方式,让用户能够方便地将设备连接到自己的 Wi-Fi 网络,这就是智能配网技术

ESP32-S3 支持多种智能配网方式:

  • SmartConfig 一键配网:手机 APP 发送包含 Wi-Fi 名称和密码的广播包,ESP32-S3 监听并解析这些广播包
  • AP 配网:ESP32-S3 创建一个热点,用户用手机连接这个热点,然后通过网页或 APP 将 Wi-Fi 信息发送给 ESP32-S3
  • BLE 配网:通过蓝牙低功耗将 Wi-Fi 信息发送给 ESP32-S3

6.1 实战项目 6:SmartConfig 一键配网

SmartConfig 是最常用的配网方式,用户只需要在手机 APP 中输入 Wi-Fi 密码,点击配网即可完成设备连接。

完整代码

c

运行

#include <stdio.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "esp_log.h" #include "esp_smartconfig.h" static const char *TAG = "SMARTCONFIG"; static EventGroupHandle_t s_wifi_event_group; const int WIFI_CONNECTED_BIT = BIT0; const int ESPTOUCH_DONE_BIT = BIT1; static void wifi_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) { xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGW(TAG, "Wi-Fi disconnected"); esp_wifi_connect(); xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_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, "Got IP address: " IPSTR, IP2STR(&event->ip_info.ip)); xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); } } static void smartconfig_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) { ESP_LOGI(TAG, "Scan done"); } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) { ESP_LOGI(TAG, "Found channel"); } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) { ESP_LOGI(TAG, "Got SSID and password"); smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data; wifi_config_t wifi_config; bzero(&wifi_config, sizeof(wifi_config_t)); memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid)); memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password)); ESP_LOGI(TAG, "SSID: %s", wifi_config.sta.ssid); ESP_LOGI(TAG, "Password: %s", wifi_config.sta.password); // 配置Wi-Fi ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); // 连接Wi-Fi ESP_ERROR_CHECK(esp_wifi_connect()); } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) { xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT); } } void smartconfig_task(void *pvParameters) { EventBits_t uxBits; // 启动SmartConfig ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH)); smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_smartconfig_start(&cfg)); while(1) { // 等待Wi-Fi连接和SmartConfig完成 uxBits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY); if(uxBits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "Wi-Fi connected"); } if(uxBits & ESPTOUCH_DONE_BIT) { ESP_LOGI(TAG, "SmartConfig completed"); esp_smartconfig_stop(); vTaskDelete(NULL); } } } void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); s_wifi_event_group = xEventGroupCreate(); 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_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &smartconfig_event_handler, NULL)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); // 创建SmartConfig任务 xTaskCreate(smartconfig_task, "smartconfig", 4096, NULL, 5, NULL); }

实验步骤

  1. 编译烧录代码到 ESP32-S3 开发板
  2. 打开手机上的 ESP-Touch APP(可以在应用商店搜索下载)
  3. 确保手机连接到你想要让 ESP32-S3 连接的 Wi-Fi 网络
  4. 在 APP 中输入 Wi-Fi 密码,点击 "确认" 开始配网
  5. 观察串口输出,当看到 "SmartConfig completed" 时,说明配网成功

七、Wi-Fi 开发最佳实践

  1. 使用事件驱动架构:不要在主循环中轮询 Wi-Fi 状态,使用事件处理函数响应 Wi-Fi 状态变化
  2. 实现自动重连:当 Wi-Fi 断开连接时,自动尝试重新连接
  3. 合理设置任务优先级:网络相关任务的优先级应该高于一般的应用任务
  4. 注意内存管理:网络通信会使用大量内存,及时释放不需要的内存
  5. 处理网络错误:网络通信是不可靠的,一定要处理各种可能的错误情况
  6. 使用 HTTPS:在生产环境中,一定要使用 HTTPS 协议进行数据传输,保证数据安全

八、常见问题解答与避坑指南

8.1 Wi-Fi 连接失败怎么办?

解决方案

  1. 检查 Wi-Fi 名称和密码是否正确
  2. 确保 Wi-Fi 路由器工作正常
  3. 确保 ESP32-S3 在 Wi-Fi 信号覆盖范围内
  4. 检查 Wi-Fi 加密方式是否支持(ESP32-S3 不支持 WEP 加密)
  5. 尝试重启 ESP32-S3 和路由器

8.2 获取不到 IP 地址怎么办?

解决方案

  1. 检查路由器的 DHCP 服务是否开启
  2. 确保路由器的 IP 地址池没有用完
  3. 尝试重启路由器
  4. 尝试使用静态 IP 地址

8.3 TCP 通信不稳定怎么办?

解决方案

  1. 检查网络连接是否稳定
  2. 增加数据重传机制
  3. 合理设置 socket 超时时间
  4. 避免在网络任务中执行耗时操作

8.4 SmartConfig 配网失败怎么办?

解决方案

  1. 确保手机和 ESP32-S3 在同一个 2.4GHz Wi-Fi 网络下(ESP32-S3 不支持 5GHz Wi-Fi 配网)
  2. 确保 Wi-Fi 名称和密码不包含特殊字符
  3. 尝试靠近路由器,增强信号
  4. 尝试重启手机和 ESP32-S3
  5. 尝试使用 AP 配网方式

8.5 HTTP 请求失败怎么办?

解决方案

  1. 检查网络连接是否正常
  2. 检查 URL 是否正确
  3. 检查 API 密钥是否正确
  4. 确保 API 服务器正常工作
  5. 尝试增加超时时间

九、课后作业

  1. 基础练习:实现 ESP32-S3 连接到 Wi-Fi 路由器
  2. 进阶练习:创建一个 TCP 服务器,接收电脑发送的数据并回显
  3. 综合练习:实现 HTTP 客户端,获取天气信息并显示在 OLED 显示屏上
  4. 挑战练习:实现 SmartConfig 一键配网功能,配网成功后自动连接到 Wi-Fi 并上报设备信息

十、下期预告

第 7 讲将学习蓝牙 BLE 开发,这是 ESP32-S3 另一个重要的无线通信功能。我们将详细讲解 BLE 广播、连接、服务与特征值、数据传输等核心概念,并通过实战项目演示如何实现 ESP32-S3 与手机之间的 BLE 通信。


写在最后

Wi-Fi 网络开发是物联网开发的核心技能,也是 ESP32-S3 最强大的功能之一。希望通过本讲的学习,你能够熟练掌握 ESP32-S3 的 Wi-Fi 开发技术,能够独立开发出能够连接互联网的物联网设备。

本讲的所有实战代码我已经上传到了我的 GitHub 仓库,大家可以在评论区留言获取。如果你在学习过程中遇到任何问题,欢迎在评论区留言讨论。

点赞收藏不迷路,关注我,带你从零基础成为 ESP32-S3 开发高手!

ESP32-S3、ESP-IDF v5.5、Wi-Fi Station、Wi-Fi AP、TCP 通信、UDP 通信、HTTP 客户端、SmartConfig 一键配网、物联网

http://www.gsyq.cn/news/1455523.html

相关文章:

  • Nintendo Switch Cleaner and Builder:Switch游戏文件管理的专业一站式解决方案
  • 国产之光 DeepSeek 把 AI 大佬全炸出来了,对 AI 行业竞争格局有何影响?
  • MATLAB脑网络分析专用BCT工具包,支持功能/结构连接矩阵全流程计算
  • 魔兽争霸3终极优化指南:如何让经典游戏在现代电脑上完美运行
  • virtio-win:让Windows虚拟机在KVM/QEMU上实现原生级性能的驱动套件
  • PS去掉图片白色背景的5种方法,PS如何去白底变透明?
  • OpenVoiceV2实战指南:5分钟掌握开源语音克隆核心技术
  • 别再买AI采购SaaS了!真正降本增效的路径是这6种混合部署模式(含成本对比热力图与实施周期甘特图)
  • ESP32太阳能气象站:低功耗设计、云端同步与HomeKit接入全攻略
  • 终极Windows任务栏美化指南:3分钟让你的桌面焕然一新
  • 如何快速掌握云端数据库管理:CloudBeaver完全指南
  • 从“70%搭架子”到一键生成:飞算JavaAI如何重构上下文工程
  • 多智能体强化学习如何实现配电网主动电压控制的终极解决方案:MAPDN深度解析
  • 2026年6月线上一天完工的采暖供应商哪家可靠,暗装暖气片/暖气/地暖管/居家采暖/装修采暖/全屋采暖,采暖公司怎么选择 - 品牌推荐师
  • 【AI面试临阵磨枪-89】Skill 幻觉、参数缺失、格式错误、业务异常如何处理?
  • 深度解析Wine:突破性跨平台兼容技术实战指南
  • 在银河麒麟高级服务器上同步官网软件源并配置内网软件源的保姆级教程
  • 美团:去相关奖励优化多目标学习
  • 【AI面试临阵磨枪-90】Skill 之间如何调用、依赖、组合、编排?
  • 2026 武汉翡翠回收实测,原石玉器回收挑选靠谱商家 - 合扬奢侈品交易中心
  • PaperFlow项目进展记录:MinerU 全文精析与 Editor Pro 进展记录
  • KDiff3文件对比与合并工具:7个技巧让你成为版本管理高手
  • GetQzonehistory终极指南:3分钟学会QQ空间历史说说完整备份
  • 基于ESP32与Ubidots的远程温湿度监测系统实战指南
  • 洗不坏的新 e 选烤火罩,越用越顺手
  • 【AI面试临阵磨枪-91】Skill 市场 / Hub 设计:审核、上架、评分、更新、安全扫描?
  • 深入解析FPSLocker:Nintendo Switch帧率解锁的核心技术与配置实践
  • 男士小型剃须刀排行推荐 适配不同场景与胡须类型 - 互联网科技品牌测评
  • 英雄联盟客户端工具箱LeagueAkari:从青铜到王者的智能辅助指南
  • 弱电工程师效率工具全攻略:从设计到运维,10 款必备软件一次配齐