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

八、【ESP32开发全栈指南:UDP客户端】

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));// 配置WiFiwifi_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-terminateinet_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);// 连接WiFiwifi_init_sta();// 启动UDP任务xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}

4. 关键改进说明

  1. 健壮的错误处理

    • 添加了WiFi连接最大重试机制(MAX_RETRY
    • 完善的socket错误码处理
    • 接收超时设置(2秒)
  2. 网络优化

    • 使用inet_ntop替代已弃用的inet_ntoa
    • 设置SO_RCVTIMEO接收超时选项
    • 每次发送后关闭socket释放资源
  3. 增强可读性

    • 结构化日志输出
    • 动态生成测试消息(带时间戳)
    • 清晰的错误分类(ERROR/WARNING/INFO)
  4. 资源管理

    • 正确释放事件组资源
    • 安全的字符串处理(snprintf
    • 内存边界检查

5. 编译烧录

idf.py set-target esp32  # 根据实际芯片选择
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor  # 替换实际串口

6. 测试服务器示例 (Python)

# UDP_test_server.py
import socketUDP_IP = "0.0.0.0"
UDP_PORT = 8888sock = 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. 注意事项

  1. 配置修改

    • 替换YOUR_WIFI_SSIDYOUR_WIFI_PASSWORD
    • 根据网络环境修改SERVER_IP
    • 调整MAX_RETRY和超时时间
  2. 常见问题排查

    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抓包验证网络连通性
http://www.xdnf.cn/news/12655.html

相关文章:

  • NLP学习路线图(三十):微调策略
  • Python图论与网络可视化——网络结构、路径分析与生物代谢通路
  • 【Linux shell】shell中的变量——构建脚本逻辑的基石
  • 水利工程流速监测中的雷达流速仪
  • FreeRTOS事件组-笔记
  • 33、原子操作
  • C++常用的自动化测试库
  • PostgreSQL数据类型使用
  • 【生活】程序员防猝si指南
  • java_网络服务相关_gateway_nacos_feign区别联系
  • JAVA-springboot log日志
  • 打卡46天
  • DDPM优化目标公式推导
  • JAVA国际版二手交易系统手机回收好物回收发布闲置商品系统源码支持APP+H5
  • 每日算法刷题Day25 6.7:leetcode二分答案3道题,用时1h40min(遇到两道动态规划和贪心时间较长)
  • 【Go语言基础【15】】数组:固定长度的连续存储结构
  • FreeRTOS任务之深入篇
  • ps蒙版介绍
  • Mac软件卸载指南,简单易懂!
  • MySQL复杂SQL(多表联查/子查询)详细讲解
  • 程序代码篇---Python串口
  • Vibe Coding AI编程
  • 安全访问家中 Linux 服务器的远程方案 —— 专为单用户场景设计
  • nginx日志的一点理解
  • Nginx 事件驱动理解
  • LeetCode 118 杨辉三角 (Java)
  • 移动应用开发专业核心课程以及就业方向
  • QT使用AES加解密,openssl及QCA问题记录
  • OpenLayers 从后端服务加载 GeoJSON 数据
  • Windows 下搭建 Zephyr 开发环境