作为一款集成了WiFi和蓝牙功能的低成本微控制器,ESP32在物联网和嵌入式网络应用中大放异彩。今天我将分享一个完整的ESP-IDF项目,演示如何让ESP32连接到WiFi网络并建立TCP客户端通信。这个方案已经在我参与的多个智能家居项目中得到验证,稳定性值得信赖。
开发环境搭建是项目的第一步,也是新手最容易踩坑的环节。推荐使用VS Code + ESP-IDF插件方案,相比纯命令行方式更友好:
idf.py --version应显示4.4以上版本注意:如果遇到Python环境问题,建议使用官方推荐的Python 3.8版本。太高版本的Python可能会导致兼容性问题。
在代码开头,我们需要定义关键的连接参数:
c复制#define EXAMPLE_ESP_WIFI_SSID "Your_WiFi_SSID"
#define EXAMPLE_ESP_WIFI_PASS "Your_WiFi_Password"
#define EXAMPLE_ESP_MAXIMUM_RETRY 5
安全提示:在实际项目中,建议通过以下方式管理敏感信息:
完整的连接流程包含这些关键步骤:
核心代码结构:
c复制void wifi_init_sta(void) {
// 1. 初始化底层驱动
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 2. 创建Station模式网络接口
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
// 3. WiFi驱动初始化
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 4. 注册事件处理器
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler, NULL, &instance_any_id);
// 5. 配置并启动WiFi
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK
},
};
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());
}
使用FreeRTOS的事件组(Event Group)来跟踪连接状态是最可靠的方式:
c复制#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
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_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "Retry attempt %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, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
调试技巧:如果遇到连接问题,可以尝试以下方法:
TCP通信的核心是BSD Socket API,ESP-IDF通过lwIP实现了这套接口:
c复制void tcp_client_task(void *pvParameters) {
char rx_buffer[128];
char host_ip[] = "192.168.1.100"; // 替换为你的服务器IP
int port = 6000;
while(1) {
struct sockaddr_in dest_addr = {
.sin_addr.s_addr = inet_addr(host_ip),
.sin_family = AF_INET,
.sin_port = htons(port)
};
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, "Socket creation failed: %s", strerror(errno));
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
if (connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) != 0) {
ESP_LOGE(TAG, "Connection failed: %s", strerror(errno));
close(sock);
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "Successfully connected to server");
// ... 通信逻辑
}
}
稳定的TCP通信需要考虑以下因素:
改进后的通信循环:
c复制while (1) {
const char *message = "Hello from ESP32";
int err = send(sock, message, strlen(message), 0);
if (err < 0) {
ESP_LOGE(TAG, "Send error: %s", strerror(errno));
break;
}
struct timeval tv = {
.tv_sec = 5,
.tv_usec = 0
};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
int len = recv(sock, rx_buffer, sizeof(rx_buffer)-1, 0);
if (len > 0) {
rx_buffer[len] = '\0';
ESP_LOGI(TAG, "Received: %s", rx_buffer);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed by server");
break;
} else {
ESP_LOGE(TAG, "Recv error: %s", strerror(errno));
break;
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
健壮的TCP客户端应该具备自动恢复能力:
c复制if (sock != -1) {
shutdown(sock, SHUT_RDWR);
close(sock);
}
// 指数退避重连策略
static int reconnect_delay = 1000;
vTaskDelay(reconnect_delay / portTICK_PERIOD_MS);
reconnect_delay = reconnect_delay < 10000 ? reconnect_delay * 2 : 10000;
在menuconfig中调整以下参数可以优化内存使用:
对于电池供电设备,启用WiFi省电模式:
c复制#include "esp_wifi.h"
void app_main() {
// ... 其他初始化
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
}
可选模式:
建议在项目初期就集成OTA功能:
partitions.csv复制# Name, Type, SubType, Offset, Size, Flags
ota_0, 0, ota_0, 0x10000, 1M,
ota_1, 0, ota_1, 0x110000, 1M,
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法扫描到AP | 硬件问题/频道不匹配 | 检查天线连接,尝试设置特定频道 |
| 认证失败 | 密码错误/加密方式不匹配 | 确认加密方式(WPA2-PSK最通用) |
| 获取IP失败 | DHCP问题 | 尝试静态IP配置 |
连接被拒绝
数据包丢失
连接意外断开
c复制int keepalive = 1;
int keepidle = 30;
int keepintvl = 5;
int keepcnt = 3;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
在TCP基础上可以轻松扩展:
结合其他通信方式:
在实际项目中,我发现将WiFi TCP通信与蓝牙配网结合使用效果最佳。用户可以通过手机APP配置WiFi凭证,设备通过TCP与云平台保持长连接。这种架构既保证了用户体验,又确保了通信可靠性。