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

一个基于 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
  • 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 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);
    }

在这里插入图片描述

相关文章:

  • Spring Boot WebFlux流式返回全攻略:从基础到企业级实践
  • Sequelize 表格操作大全
  • Kafka 集群中,Broker和Controller的关系
  • Windows逆向工程提升之IMAGE_EXPORT_DIRECTORY
  • 变量的作用域:全局变量 vs 局部变量——编程思维的核心与实践智慧
  • 大模型部署ollama/vLLM/LMDeploy/SGLang区别
  • 不使用Long.parseLong()将String转成long类型,不使用String.valueOf()将Long转成String类型
  • 解锁C++编辑距离:文本相似度的度量密码
  • [ Qt ] | 常见控件(一)
  • vim快速移动光标
  • 遥感解译项目Land-Cover-Semantic-Segmentation-PyTorch之二训练模型
  • 预处理越复杂越好?评估脑电预处理在深度学习应用中的作用
  • Go 语言接口入门指南
  • Flutter 3.32 升级要点全解析
  • go 基础语法 【教程 go tour】
  • 怎么判断一个Android APP使用了Cocos 这个跨端框架
  • 【Golang】部分语法格式和规则
  • Go语言爬虫系列教程(三)HTML解析技术
  • 26考研|高等代数:λ-矩阵
  • C++之fmt库介绍和使用(3)
  • 建设旅游网站/搜外网友情链接
  • 厦门优秀网站建设/seo外链发布平台
  • github部署wordpress/上海百度移动关键词排名优化
  • 公司网站制作教学/seo的中文是什么
  • 建建建设网站公司网站/自媒体运营主要做什么
  • 免费做简历的软件网站/线下营销推广方式有哪些