1. 环境准备
- 安装ESP-IDF v4.4+ (官方指南)
- 确保Python 3.7+ 和Git已安装
2. 创建项目
idf.py create-project udp_client
cd udp_client
3. 完整优化代码 (main/main.c
)
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
// 配置区 ========================================
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASS "YOUR_WIFI_PASSWORD"
#define SERVER_IP "192.168.1.100" // 目标服务器IP
#define SERVER_PORT 8888 // 目标端口
#define MAX_RETRY 5 // WiFi最大重连次数
// ===============================================
static const char *TAG = "UDP_Client";
static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
/* 事件组位定义 */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
// WiFi事件处理函数
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 < MAX_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "Retry connecting to AP. Attempt %d/%d", s_retry_num, MAX_RETRY);
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
ESP_LOGE(TAG, "Failed to connect after %d attempts", MAX_RETRY);
}
} 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);
}
}
// WiFi初始化
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_t *sta_netif = 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_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
// 配置WiFi
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
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());
ESP_LOGI(TAG, "WiFi initialization complete. Connecting to AP...");
// 等待连接结果
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, "Connected to AP SSID: %s", WIFI_SSID);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGE(TAG, "Failed to connect to SSID: %s", WIFI_SSID);
} else {
ESP_LOGE(TAG, "Unexpected event");
}
// 清理事件组
vEventGroupDelete(s_wifi_event_group);
}
// UDP客户端任务
void udp_client_task(void *pvParameters) {
ESP_LOGI(TAG, "Starting UDP client task");
struct sockaddr_in dest_addr = {
.sin_addr.s_addr = inet_addr(SERVER_IP),
.sin_family = AF_INET,
.sin_port = htons(SERVER_PORT)
};
char rx_buffer[128];
char tx_buffer[50];
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &dest_addr.sin_addr, addr_str, sizeof(addr_str));
ESP_LOGI(TAG, "Target server: %s:%d", addr_str, SERVER_PORT);
while (1) {
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, "Failed to create socket: errno %d", errno);
vTaskDelay(2000 / portTICK_PERIOD_MS);
continue;
}
// 设置超时选项(2秒)
struct timeval timeout = {
.tv_sec = 2,
.tv_usec = 0
};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// 发送数据
snprintf(tx_buffer, sizeof(tx_buffer), "Hello #%d", (int)(xTaskGetTickCount()/1000));
int err = sendto(sock, tx_buffer, strlen(tx_buffer), 0,
(struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Send error: errno %d", errno);
close(sock);
vTaskDelay(2000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "Sent: %s", tx_buffer);
// 接收响应
struct sockaddr_in source_addr;
socklen_t addr_len = sizeof(source_addr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0,
(struct sockaddr *)&source_addr, &addr_len);
if (len > 0) {
rx_buffer[len] = 0; // Null-terminate
inet_ntop(AF_INET, &source_addr.sin_addr, addr_str, sizeof(addr_str));
ESP_LOGI(TAG, "Received %d bytes from %s:%d", len, addr_str, ntohs(source_addr.sin_port));
ESP_LOGI(TAG, "Data: %s", rx_buffer);
} else if (len == 0) {
ESP_LOGW(TAG, "Connection closed by server");
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
ESP_LOGW(TAG, "Receive timeout");
} else {
ESP_LOGE(TAG, "Receive failed: errno %d", errno);
}
}
close(sock);
ESP_LOGI(TAG, "Next message in 3 seconds...");
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
void app_main() {
// 初始化NVS存储
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);
// 连接WiFi
wifi_init_sta();
// 启动UDP任务
xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}
4. 关键改进说明
-
健壮的错误处理:
- 添加了WiFi连接最大重试机制(
MAX_RETRY
) - 完善的socket错误码处理
- 接收超时设置(2秒)
- 添加了WiFi连接最大重试机制(
-
网络优化:
- 使用
inet_ntop
替代已弃用的inet_ntoa
- 设置
SO_RCVTIMEO
接收超时选项 - 每次发送后关闭socket释放资源
- 使用
-
增强可读性:
- 结构化日志输出
- 动态生成测试消息(带时间戳)
- 清晰的错误分类(ERROR/WARNING/INFO)
-
资源管理:
- 正确释放事件组资源
- 安全的字符串处理(
snprintf
) - 内存边界检查
5. 编译烧录
idf.py set-target esp32 # 根据实际芯片选择
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor # 替换实际串口
6. 测试服务器示例 (Python)
# UDP_test_server.py
import socket
UDP_IP = "0.0.0.0"
UDP_PORT = 8888
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
print(f"Listening on {UDP_PORT}")
while True:
data, addr = sock.recvfrom(1024)
print(f"Received: {data.decode()} from {addr}")
sock.sendto(b"ACK:" + data, addr)
7. 注意事项
-
配置修改:
- 替换
YOUR_WIFI_SSID
和YOUR_WIFI_PASSWORD
- 根据网络环境修改
SERVER_IP
- 调整
MAX_RETRY
和超时时间
- 替换
-
常见问题排查:
I (1845) UDP_Client: Got IP: 192.168.1.101 I (1845) UDP_Client: Starting UDP client task I (1845) UDP_Client: Target server: 192.168.1.100:8888 I (1855) UDP_Client: Sent: Hello #18 W (2855) UDP_Client: Receive timeout
- 检查服务器IP/端口是否正确
- 确认服务器防火墙允许UDP流量
- 使用Wireshark抓包验证网络连通性