简介
UDP(User Datagram Protocol,用户数据报协议)是一种无连接、轻量级的传输层协议,它属于 TCP/IP 协议族。UDP 协议的主要特点是传输速度快、延迟低,适合需要快速、实时传输数据的应用场景。不过,它并不保证数据传输的可靠性,也就是说,数据可能丢失、乱序或重复。但是相对于TCP, 它的传输速度更快, 传输过程中开销更小. 在本文中我们将探索如何使用ESP32-S3在ESP-IDF的环境下使用UDP和上位机实现数据的收发功能.
需求设备
ESP32-S3 * 1
Python 上位机服务端
网络拓扑
其主要的步骤为使用ESP32作为UDP客户端, 使用Python作为服务端. 当两者服务启动后, ESP32-S3 将会向指定IP地址和端口的UDP服务端发起连接. 当两者连接成功之后即可进行数据的收发.
首先, 我们需要在Python中新建一个UDP服务器, 并且成功的启动服务.
import socket # 配置 HOST = "0.0.0.0" # 绑定所有局域网 IP PORT = 12345 # 创建 UDP socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((HOST, PORT)) print(f"UDP server is listening on {HOST}:{PORT}...") while True: data, addr = sock.recvfrom(1024) # 接收数据(最大1024字节) print(f"Received from {addr}: {data.decode()}") # 给数据加 tag response = f"[RECV] {data.decode()}".encode() # 发送回客户端 sock.sendto(response, addr)
使用上述代码, 可以成功的启动一个UDP的服务端, 并且对局域网内的所有设备可用. 其监听的端口为12345, 当有客户端连接到服务端的时候, 那么它将接受客户端的消息,然后将消息打上一个tag再返回给服务端. 接下来我们来完成ESP-32的客户端程序开发.
对于客户端而言, 首先需要初始化UDP的客户端, 然后当连接上之后来每隔一秒向服务端发送消息. 核心代码如下
static void udp_client_task(void *pvParameters) { struct sockaddr_in server_addr, client_addr; int sock; char udp_buffer[BUFFER_SIZE]; socklen_t client_addr_len = sizeof(client_addr); // 创建 UDP Socket sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { ESP_LOGE(TAG, "Socket creation failed"); vTaskDelete(NULL); return; } // 配置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(UDP_SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP); ESP_LOGI(TAG, "UDP client started, sending and receiving data..."); while (1) { // 发送 "hello world" const char *message = "hello world"; int message_len = strlen(message); int sent_len = sendto(sock, message, message_len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (sent_len < 0) { ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); break; } ESP_LOGI(TAG, "Sent message: %s", message); // 接收数据 int len = recvfrom(sock, udp_buffer, sizeof(udp_buffer) - 1, 0, (struct sockaddr *)&client_addr, &client_addr_len); if (len < 0) { ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno); break; } else if (len > 0) { udp_buffer[len] = '\0'; // 确保字符串以 NULL 结尾 ESP_LOGI(TAG, "Received data: %s", udp_buffer); } vTaskDelay(1000 / portTICK_PERIOD_MS); // 每秒发送一次数据 } // 关闭 Socket close(sock); vTaskDelete(NULL); }
由于服务端是接受到消息之后才给消息打上一个TAG然后再发送回来, 因此需要客户端先发送消息. 上述代码在连接上了服务端之后便会每秒向服务端发送一条消息.
效果如下所示(客户端发送消息和接受来自服务端打了标签的数据)
服务端接受到的数据
总结
在本文中主要对UDP的通讯进行了概述,并且探究了如何在ESP-IDF下使用UDP和服务端进行通讯. 从而实现了上位机和UDP进行双向的数据通讯. 甚至并不需要使用上位机, 也可以使用两个ESP32来完成上述的所有操作.