一个基于 ESP-IDF 的 RPC over UDP 示例
创建了一个基于 ESP-IDF 的 RPC over UDP 示例,包括服务端与客户端的完整代码。您可以查看并根据需求调整方法名、参数解析和网络配置等
/*
- ESP32 RPC over UDP Example (Detailed Version)
- Description:
- This example demonstrates a simple Remote Procedure Call (RPC) mechanism
- over UDP. An ESP32 device acts as both server and client in separate RTOS tasks.
-
- The server listens on a UDP port and handles incoming requests of the form:
-
<method>:<arg1>,<arg2>\n
-
Supported methods:
-
add - return sum of two integers
-
sub - return difference between two integers (arg1 - arg2)
-
- The client periodically sends RPC commands to the server and logs the responses.
- Key Components:
-
- FreeRTOS tasks: udp_server_task, udp_client_task
-
- BSD sockets: socket(), bind(), recvfrom(), sendto()
-
- Simple parsing via sscanf() and strcmp()
- Configuration:
-
- SERVER_PORT: UDP port on which server listens
-
- CLIENT_PORT: UDP port bound by client to receive responses
-
- CONFIG_EXAMPLE_SERVER_IP: set to server IP in
menuconfig
- CONFIG_EXAMPLE_SERVER_IP: set to server IP in
- ESP-IDF Requirements:
-
- esp_netif (network interface)
-
- Wi-Fi or Ethernet connectivity established via example_connect()
-
- NVS flash initialization for Wi-Fi credentials
*/
- NVS flash initialization for Wi-Fi credentials
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_system.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <esp_netif.h>
#include <esp_event.h>
#include <esp_wifi.h>
#include <protocol_examples_common.h> // For example_connect()
// UDP port definitions
#define SERVER_PORT 3333 // Port on which the RPC server listens
#define CLIENT_PORT 4444 // Local port for RPC client to receive responses
#define BUF_SIZE 128 // Maximum buffer size for UDP packets
static const char *TAG = “rpc_udp”; // Logging tag
/**
- @brief Simple RPC handler function.
- Parses the incoming request string and executes the requested method.
- @param req The null-terminated request string, e.g. “add:2,3”.
- @param resp Buffer to write the null-terminated response.
- @param resp_max_len Maximum length of the response buffer.
- @return Number of bytes written into resp (excluding null terminator),
-
or negative on error.
/
static int rpc_handler(const char req, char* resp, size_t resp_max_len)
{
char method[16]; // Buffer for method name (“add” or “sub”)
int arg1 = 0;
int arg2 = 0;
// Use sscanf to extract method and two integer arguments.
// Format: <method>:<arg1>,<arg2>
int parsed = sscanf(req, "%15[^:]:%d,%d", method, &arg1, &arg2);
if (parsed != 3) {// Parsing failed: invalid formatreturn snprintf(resp, resp_max_len, "error: invalid format");
}// Dispatch based on method name
if (strcmp(method, "add") == 0) {int result = arg1 + arg2;return snprintf(resp, resp_max_len, "%d", result);
} else if (strcmp(method, "sub") == 0) {int result = arg1 - arg2;return snprintf(resp, resp_max_len, "%d", result);
}// Unknown method
return snprintf(resp, resp_max_len, "error: unknown method");
}
/**
-
@brief UDP RPC server task.
-
Creates a UDP socket, binds to SERVER_PORT, and waits for incoming requests.
-
For each request, calls rpc_handler() and sends the response back to the client.
/
static void udp_server_task(void arg)
{
char rx_buffer[BUF_SIZE]; // Buffer for incoming request
char addr_str[INET_ADDRSTRLEN]; // Human-readable client IP
struct sockaddr_in server_addr, client_addr;
socklen_t addr_len = sizeof(client_addr);// Create UDP socket
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, “Failed to create socket: errno %d”, errno);
vTaskDelete(NULL);
return;
}// Prepare server address struct
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces
server_addr.sin_port = htons(SERVER_PORT);// Bind socket to the specified port
if (bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
ESP_LOGE(TAG, “Socket bind failed: errno %d”, errno);
close(sock);
vTaskDelete(NULL);
return;
}ESP_LOGI(TAG, “UDP server listening on port %d”, SERVER_PORT);
while (1) {
// Receive a UDP packet
int len = recvfrom(sock, rx_buffer, BUF_SIZE - 1, 0,
(struct sockaddr*)&client_addr, &addr_len);
if (len < 0) {
ESP_LOGE(TAG, “recvfrom error: errno %d”, errno);
break;
}// Null-terminate and log requestrx_buffer[len] = '\0';inet_ntoa_r(client_addr.sin_addr, addr_str, sizeof(addr_str));ESP_LOGI(TAG, "Received %d bytes from %s:%d", len,addr_str, ntohs(client_addr.sin_port));ESP_LOGI(TAG, "Request: %s", rx_buffer);// Process the RPC commandchar response[BUF_SIZE];int resp_len = rpc_handler(rx_buffer, response, sizeof(response));// Send response back to the clientint err = sendto(sock, response, resp_len, 0,(struct sockaddr*)&client_addr, addr_len);if (err < 0) {ESP_LOGE(TAG, "sendto error: errno %d", errno);break;}
}
// Clean up socket and task
close(sock);
vTaskDelete(NULL);
}
/**
-
@brief UDP RPC client task (example usage).
-
Sends predefined RPC commands to the server and logs the responses.
/
static void udp_client_task(void arg)
{
char rx_buffer[BUF_SIZE]; // Buffer for incoming response
struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(CONFIG_EXAMPLE_SERVER_IP);
dest_addr.sin_port = htons(SERVER_PORT);// Create UDP socket
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0) {
ESP_LOGE(TAG, “Unable to create socket: errno %d”, errno);
vTaskDelete(NULL);
return;
}// Bind client socket to receive responses on CLIENT_PORT
struct sockaddr_in client_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_port = htons(CLIENT_PORT)
};
if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0) {
ESP_LOGE(TAG, “Client bind failed: errno %d”, errno);
close(sock);
vTaskDelete(NULL);
return;
}// Example RPC commands
const char* rpc_cmds[] = { “add:5,3”, “sub:10,4” };
size_t cmd_count = sizeof(rpc_cmds) / sizeof(rpc_cmds[0]);for (size_t i = 0; i < cmd_count; i++) {
// Send RPC command
ESP_LOGI(TAG, “Sending RPC command: %s”, rpc_cmds[i]);
sendto(sock, rpc_cmds[i], strlen(rpc_cmds[i]), 0,
(struct sockaddr*)&dest_addr, sizeof(dest_addr));// Wait for response (blocking)int len = recvfrom(sock, rx_buffer, BUF_SIZE - 1, 0, NULL, NULL);if (len < 0) {ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);break;}rx_buffer[len] = '\0'; // Null-terminateESP_LOGI(TAG, "RPC response: %s", rx_buffer);// Delay before next commandvTaskDelay(pdMS_TO_TICKS(2000));
}
// Clean up
close(sock);
vTaskDelete(NULL);
}
/**
-
@brief Main application entry point.
-
Initializes NVS, network interfaces, connects to Wi-Fi/Ethernet,
-
and launches the UDP server and client tasks.
*/
void app_main(void)
{
// 1. Initialize NVS for Wi-Fi credentials storage
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. Initialize TCP/IP network interface
ESP_ERROR_CHECK(esp_netif_init());// 3. Create default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. Connect to configured Wi-Fi or Ethernet network
// This call blocks until an IP address is obtained.
example_connect();// 5. Start the RPC server and client tasks
xTaskCreate(udp_server_task, “udp_server_task”, 4096, NULL, 5, NULL);
xTaskCreate(udp_client_task, “udp_client_task”, 4096, NULL, 5, NULL);
}