跳转至

通用 RPC 指令 (0x30)

对应源码: components/airlink/include/luat_airlink_rpc.h, components/airlink/src/luat_airlink_rpc.c, components/airlink/AGENTS.md:82-100

启用宏: LUAT_USE_AIRLINK_RPC (RPC 框架), LUAT_USE_AIRLINK_RPC_<MODULE> (各 typed handler)

cmd 0x30 是 AirLink 的通用 nanopb RPC 单一入口. 所有 typed handler (GPIO/UART/WLAN/PM/SDATA) 都通过这一个 cmd 进入, 按 rpc_id 分发到对应的静态 handler.

线格式

[ pkgid   : 8 bytes ]  -- 0 = NOTIFY (无需响应), 非 0 = REQUEST (期望对端回应)
[ rpc_id  : 2 bytes ]  -- 功能号, 按模块分段
[ msg_type: 1 byte  ]  -- 0 = REQUEST, 1 = NOTIFY
[ payload : N bytes ]  -- nanopb 编码

最小头部 RPC_WIRE_HDR_LEN = 11 字节.

字段详解

字段 长度 含义
pkgid 8B uint64, 小端. REQUEST 时用于对端回响应时匹配; NOTIFY 时填 0
rpc_id 2B RPC id, 按模块分段
msg_type 1B AIRLINK_RPC_MSG_TYPE_REQUEST=0AIRLINK_RPC_MSG_TYPE_NOTIFY=1
payload NB nanopb 编码的 protobuf 消息, REQUEST 时是 req, NOTIFY 时是消息体

RPC ID 分配

来源: components/airlink/AGENTS.md:93-100

范围 模块 启用宏
0x0100 GPIO LUAT_USE_AIRLINK_RPC_GPIO
0x0200 UART LUAT_USE_AIRLINK_RPC_UART
0x0300 WLAN LUAT_USE_AIRLINK_RPC_WLAN
0x0400 PM LUAT_USE_AIRLINK_RPC_PM
0x0500 SDATA notify LUAT_USE_AIRLINK_RPC_SDATA

协议层保留 rpc_id 分配. 添加新模块时需要分配新的高位 id 段.

响应格式

RPC 响应通过 cmd 0x08 承载:

[new_pkgid    : 8 bytes]  // 本端 next_pkg_id
[req_pkgid    : 8 bytes]  // 来自原始请求的 pkgid
[result_code  : 2 bytes]  // 大端, 0 = 成功, 负值 = 错误码
[resp_payload : N bytes]  // nanopb 编码的响应 (可选)
模式 行为
RPC luat_airlink_result_send() 自动填充 [req_pkgid + result_code + payload], 系统再加 8 字节 new_pkgid 前缀 (src/luat_airlink.c:1094-1103)
raw 调用方拼好 [req_pkgid + result_code + resp], 传入 luat_airlink_result_send()

接收端用 req_pkgid 在 64 槽位 luat_airlink_result_reg_t 表中查找等待者.

两种调用方式

1. Raw 字节 RPC

调用方自己处理二进制 payload:

-- 阻塞调用, 超时 3 秒
local ok, resp = airlink.rpc(airlink.MODE_SPI_MASTER, 0x0300, req_bytes, 3000)

C API:

int luat_airlink_rpc(uint8_t mode, uint16_t rpc_id,
                     const uint8_t* req, uint16_t req_len,
                     uint8_t* resp, uint16_t resp_size, uint16_t* resp_len,
                     uint32_t timeout_ms);

2. Typed nanopb RPC

C 结构体直传, handler 收到的是已解码的 struct. 由静态 handler 表管理:

// 每个 exec handler 文件导出一个全局常量:
// const luat_airlink_rpc_nb_reg_t luat_airlink_rpc_XXX_reg;

// luat_airlink_rpc_nb_table.c 按宏汇编成指针数组:
// { 0x0100, ..., &luat_airlink_rpc_gpio_reg },
// { 0x0200, ..., &luat_airlink_rpc_uart_reg },
// { 0x0300, ..., &luat_airlink_rpc_wlan_reg },
// { 0x0400, ..., &luat_airlink_rpc_pm_reg   },
// { 0x0500, ..., &luat_airlink_rpc_sdata_reg },

C 调用 (协程中真正同步):

int luat_airlink_rpc_nb_call_yield(lua_State* L, uint8_t mode, uint16_t rpc_id,
                                    const pb_msgdesc_t* req_desc, const void* req,
                                    uint32_t timeout_ms,
                                    lua_KFunction cont, lua_KContext user_ctx);

int luat_airlink_rpc_nb_call(uint8_t mode, uint16_t rpc_id,
                              const pb_msgdesc_t* req_desc, const void* req,
                              const pb_msgdesc_t* resp_desc, void* resp,
                              uint32_t timeout_ms);

int luat_airlink_rpc_nb_notify(uint8_t mode, uint16_t rpc_id,
                                const pb_msgdesc_t* desc, const void* msg);

错误码

错误码 名称 含义
0 SUCCESS 成功
-1 TIMEOUT 调用超时
-2 OOM 内存不足
-3 SEND_FAIL 发送失败
-4 ENCODE/DECODE_FAIL nanopb 编解码失败
-5 REQ_TOO_LARGE 请求超出 AIRLINK_RPC_MAX_PAYLOAD = 4096
-6 RESP_BUF_OVERFLOW 响应超出接收缓冲区
-7 RESULT_REG_FULL result 注册表满 (64 槽位全占用)
-404 NO_HANDLER handler 未注册

统计与性能 API

typedef struct {
    uint64_t call_total;
    uint64_t call_success;
    uint64_t call_timeout;
    uint64_t call_send_fail;
    uint64_t call_encode_fail;
    uint64_t call_decode_fail;
    uint64_t call_buf_overflow;
    uint64_t notify_total;
    uint64_t notify_success;
    uint64_t notify_encode_fail;
} luat_airlink_rpc_stats_t;

typedef struct {
    uint64_t total_ms;
    uint64_t count;
    uint32_t min_ms;
    uint32_t max_ms;
} luat_airlink_rpc_latency_t;

typedef struct {
    uint64_t encode_total_us;
    uint64_t decode_total_us;
    uint64_t encode_count;
    uint64_t decode_count;
    uint32_t encode_max_us;
    uint32_t decode_max_us;
} luat_airlink_rpc_perf_t;

int luat_airlink_rpc_get_stats(luat_airlink_rpc_stats_t* stats);
int luat_airlink_rpc_get_perf(luat_airlink_rpc_latency_t* latency,
                               luat_airlink_rpc_perf_t* perf);
int luat_airlink_rpc_reset_stats(void);
void luat_airlink_rpc_print_stats(void);
void luat_airlink_rpc_cleanup_dead_yields(void); // shutdown / task delete 时清理

添加新 RPC 模块的步骤

来源: components/airlink/AGENTS.md:107-117

  1. components/nanopb/proto/ 创建 .proto 文件
  2. 运行 nanopb 生成器, 将 .pb.hinclude/, .pb.csrc/ (注意 .pb.c 不会自动放到 src/, 需要手动移)
  3. 创建 src/exec/luat_airlink_cmd_exec_rpc_XXX.c:
  4. 第一行 #include "luat_base.h" (必须在 #ifdef 之前, 否则会因宏未定义整文件被跳过)
  5. 外层 #ifdef LUAT_USE_AIRLINK_RPC, 内层 #ifdef LUAT_USE_AIRLINK_RPC_XXX
  6. 导出 const luat_airlink_rpc_nb_reg_t luat_airlink_rpc_XXX_reg 全局量
  7. luat_airlink_rpc_nb_table.c 增加 extern 声明和指针数组条目
  8. bsp/<chip>/include/luat_conf_bsp.h 增加 #define LUAT_USE_AIRLINK_RPC_XXX 1
  9. 分配 RPC id (在 .proto 文件注释中记录)
问一下 AI