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