一个基于 ESP-IDF 的 RPC over UDP 示例

发布于:2025-05-24 ⋅ 阅读:(9) ⋅ 点赞:(0)

创建了一个基于 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
  • ESP-IDF Requirements:
    • esp_netif (network interface)
    • Wi-Fi or Ethernet connectivity established via example_connect()
    • 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 format
    return 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 request
     rx_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 command
     char response[BUF_SIZE];
     int resp_len = rpc_handler(rx_buffer, response, sizeof(response));
    
     // Send response back to the client
     int 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-terminate
     ESP_LOGI(TAG, "RPC response: %s", rx_buffer);
    
     // Delay before next command
     vTaskDelay(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);
    }

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到