透传应用框架与处理模块
BLE 透传(Transparent Transmission)是杰理 AW33N BLE SDK 中最基础的无线数据通道能力:设备端通过通用串口(UART)接收外部数据并原样转发到 BLE GATT 通道,同时把手机 App 通过 GATT 下发的数据回传给串口,实现「串口 ⇄ BLE」双向透传。本文档深入分析 apps/demo/transfer 工程中透传应用框架(app_trans.c)与 BLE 协议处理模块(ble_trans.c)的完整实现。
Purpose and Scope
本页面覆盖以下内容:
- 透传应用框架(
app_trans.c):基于杰理application状态机的应用生命周期管理、BLE 协议栈启动/退出流程、消息循环与软关机处理; - BLE 透传协议处理模块(
ble_trans.c):GATT Server 配置、安全管理(SM)、广播数据构造、UART→BLE 与 BLE→UART 双向数据通路、连接参数更新与回连 CCC 恢复; - 透传数据通道:UART 与 GATT 特征(
ATT_CHARACTERISTIC_ae02_01)之间的数据搬运机制; - 工程级集成:
apps/demo/transfer工程的目录结构、配置开关(CONFIG_APP_LE_TRANS)与周边模块(AT 指令、非连接广播等)的关系。
有意不覆盖以下内容(由同级页面承担):
- AT 指令交互框架(
examples/at_char_com/,如at_char_cmds.c、ble_at_char_server.c); - 非连接广播透传(
examples/nonconn_trans/app_nonconn_trans.c); - USB Dongle 与远端升级透传(
examples/dongle/,其ota_dg_central.h注释中 channel_3~9 为远端升级透传通道); - 底层 GATT/ATT 协议栈实现(
btstack内部,本页面仅以 API 调用视角引用)。
Overview
透传应用是理解杰理 BLE SDK 应用层架构的最佳入口:它以最小的业务逻辑串联起了「应用状态机 → BLE 协议栈 → GATT Profile → 外设驱动」整条链路。其设计意图可归纳为三点:
- 数据面与协议面分离:
app_trans.c只负责应用生命周期与消息循环,不关心 GATT 细节;ble_trans.c只负责 BLE 协议处理(ATT 回调、CCC、连接参数),不关心应用策略。两者通过ble_trans.h暴露的接口和sys_event事件解耦。 - 以 UART 为数据源:通过
common_uart_init()配置通用串口,串口 RX 中断里把数据直接塞入 BLE 发送通道(trans_uart_rx_to_ble),实现"有线串口即无线串口"的体验。 - 一次初始化、全链路回调驱动:GATT 的读写、事件全部由
trans_att_read_callback/trans_att_write_callback/trans_event_packet_handler三个回调驱动,应用层只需注册配置结构体即可运行。
透传应用典型使用场景包括:手机 App 与 MCU 之间的调试日志透传、固件配置下发、传感器数据上送,以及通过 USB Dongle 转发的 PC 工具通信(见 ota_dg_central.h 中 "channel_2: pc->usb透传" 的通道规划)。
Architecture
透传框架的整体架构分为三层:应用层(状态机与消息循环)、BLE 协议层(GATT/ATT/SM/广播)与数据源层(UART 驱动与 LED 指示)。
flowchart TD
subgraph sg_AppLayer["应用层 (app_trans.c)"]
SM["le_trans_state_machine<br/>application 状态机"]
START["le_trans_app_start<br/>时钟/蓝牙/串口初始化"]
LOOP["le_trans_run_loop<br/>get_msg 消息循环"]
PWROFF["le_trans_set_soft_poweroff<br/>软关机处理"]
end
subgraph sg_BleLayer["BLE 协议层 (ble_trans.c)"]
CFG["trans_gatt_control_block<br/>GATT 控制块"]
SMC["trans_sm_init_config<br/>安全管理配置"]
ADV["广播/扫描应答数据<br/>trans_adv_data"]
READ["trans_att_read_callback"]
WRITE["trans_att_write_callback"]
EVENT["trans_event_packet_handler"]
U2B["trans_uart_rx_to_ble<br/>UART→BLE"]
end
subgraph sg_DataSrc["数据源与驱动层"]
UART["common_uart_init<br/>通用串口 (TCFG_COMMON_UART_*)"]
LED["led_operate<br/>LED 指示"]
BTSTACK["btstack_init / btstack_ble_*<br/>协议栈接口"]
end
SM -->|"ACTION_LE_TRANS_MAIN"| START
START --> LOOP
START --> UART
START --> LED
START -->|"le_trans_bt_start"| BTSTACK
BTSTACK -->|"ble_init_cfg_t<br/>comm_ble_profile_init"| CFG
CFG --> READ
CFG --> WRITE
CFG --> EVENT
CFG --> SMC
EVENT -->|"SYS_BT_EVENT 推送"| LOOP
UART -->|"串口 RX 数据"| U2B
U2B -->|"ble_comm_att_send_data<br/>ATT_CHARACTERISTIC_ae02_01"| WRITE
PWROFF -->|"btstack_ble_exit"| BTSTACK
架构解读:
- 应用状态机驱动启动:
le_trans_state_machine是杰理application框架的回调,收到ACTION_LE_TRANS_MAIN意图后调用le_trans_app_start()完成时钟、蓝牙协议栈与串口的初始化,随后进入le_trans_run_loop()消息循环(app_trans.c#L217-L231)。 - 配置结构体是协议层的唯一入口:
trans_gatt_control_block(gatt_ctrl_t)注册了 server/client/sm 三组配置,ble_trans.c通过comm_ble_profile_init将其挂载到协议栈(ble_trans.c#L84-L116)。 - 数据流单向清晰:串口 RX →
trans_uart_rx_to_ble→ GATT Notify 到手机;手机写入特征 →trans_att_write_callback→ 串口 TX。两条通路在ble_trans.c内部汇合,互不干扰。 - 退出与省电:
le_trans_set_soft_poweroff遵循"先断链、再关机"的顺序,依据TCFG_LOWPOWER_PATTERN选择等待断链或直接软关机(app_trans.c#L82-L103)。
应用框架层详解(app_trans.c)
app_trans.c 是整个透传应用的"壳",全部代码由 CONFIG_APP_LE_TRANS 宏包裹,未开启该宏时整个文件不参与编译(app_trans.c#L45-L53)。它负责四件事:BLE 初始化配置描述、协议栈启停、消息循环、电源管理。
BLE 初始化配置(ble_init_cfg_t)
框架使用一个全局 ble_init_cfg_t 结构体描述 BLE 模块的初始化行为,这是杰理 SDK 应用层接入协议栈的标准方式:
static const ble_init_cfg_t le_trans_data_config = {
.same_address = 0,
.appearance = 0,
.ble_profile_init = comm_ble_profile_init,
.bt_ble_init = le_trans_bt_ble_init,
.bt_ble_before_start_init = le_trans_bt_ble_before_start_init,
.bt_ble_exit = le_trans_bt_ble_exit,
.ble_module_enable = le_trans_ble_module_enable,
};
Source: app_trans.c#L56-L64
设计要点:
.ble_profile_init = comm_ble_profile_init复用公共 profile 初始化入口,而ble_trans.c通过trans_gatt_control_block提供自己的 server/client/sm 配置,二者在comm_ble_profile_init内部结合——这就是"公共框架 + 业务私有配置"的扩展模式。.bt_ble_init/.bt_ble_before_start_init/.bt_ble_exit/.ble_module_enable分别挂接ble_trans.c中对应的实现,使透传模块能介入协议栈启动的各个阶段。same_address = 0表示经典蓝牙与 BLE 使用不同地址;appearance = 0表示未声明 GAP 外观。
协议栈启动与退出
le_trans_bt_start() 先根据系统时钟配置 PLL 参数,再依次调用 btstack_ble_start_before_init 与 btstack_init 拉起协议栈:
static void le_trans_bt_start()
{
u32 sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
/* bt_osc_offset_ext_save(-15); // 10pF晶振 */
btstack_ble_start_before_init(&le_trans_data_config, 0);
btstack_init();
}
Source: app_trans.c#L115-L124
退出流程则强调顺序:先调用 ble_comm_module_enable(0) 关闭模块,延时 20 个 OS tick 等待内部处理,再 btstack_ble_exit(0) 真正退出协议栈(app_trans.c#L137-L144)。这种"先降级、后拆除"的顺序避免在协议栈活跃时直接释放资源导致挂死。
应用启动与消息循环
le_trans_app_start() 的初始化顺序是:系统时钟(sys/lsb/sfc)→ 蓝牙时钟 → 协议栈 → LED → 通用串口 → 定时器 → 消息循环:
static void le_trans_app_start()
{
clk_set("sys", TCFG_CLOCK_SYS_HZ);
clk_set("lsb", TCFG_CLOCK_LSB_HZ);
clk_set("sfc", TCFG_CLOCK_SFC_HZ);
clock_bt_init();
le_trans_bt_start();
#if TCFG_LED_ENABLE
led_operate(LED_INIT_FLASH);
#endif
// 配置一个通用串口做ble2uart or uart2ble
#if TCFG_COMMON_UART_ENABLE
common_uart_init(TCFG_COMMON_UART_BAUDRATE, COMMON_UART_TX_PIN, COMMON_UART_RX_PIN);
#endif
le_trans_run_loop();
}
Source: app_trans.c#L176-L205
值得注意:common_uart_init 的注释明确写着"配置一个通用串口做 ble2uart or uart2ble",即透传方向由串口收发自动决定。le_trans_run_loop() 则是一个被 LP_RUNCODE_AT(.lp_running.app.text) 标注的低功耗代码段,循环调用 get_msg 获取系统消息并交给 app_comm_process_handler 分发,同时支持 APP_SWITCH_MODE_EN 下的应用切换退出(app_trans.c#L148-L163)。
应用状态机
le_trans_state_machine 是 application 框架的标准状态机回调,覆盖 APP_STA_CREATE / APP_STA_START / APP_STA_PAUSE / APP_STA_RESUME / APP_STA_STOP 各阶段;在 APP_STA_START 阶段根据 it->action == ACTION_LE_TRANS_MAIN 触发真正的应用启动(app_trans.c#L217-L240)。这种"意图(intent)驱动"的模式是杰理多应用框架的标准做法,使透传应用可以被其他应用(如 OTA、AT 模式)以统一的 action 方式唤起或切换。
软关机(电源管理)
static void le_trans_set_soft_poweroff(void)
{
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
le_trans_is_active = 1;
#endif
//必须先主动断开蓝牙链路,否则要等链路超时断开
btstack_ble_exit(0);
if (ble_comm_dev_is_connected(GATT_ROLE_SERVER) || ble_comm_dev_is_connected(GATT_ROLE_CLIENT)) {
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
//soft 方式非必须等链路断开
sys_timeout_add((void *)POWER_EVENT_POWER_SOFTOFF, (void *)app_power_event_to_user, WAIT_DISCONN_TIME_MS);
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
//must wait disconn
app_power_soft.wait_disconn = 1;
#endif
} else {
app_power_set_soft_poweroff(NULL);
}
}
Source: app_trans.c#L82-L103
这段代码体现了两种低功耗模式的取舍:SOFT_MODE 下允许"先关机、后台等断链",用 sys_timeout_add 延迟触发 POWER_EVENT_POWER_SOFTOFF;SOFT_BY_POWER_MODE 则必须等链路断开(app_power_soft.wait_disconn = 1)才能关机,防止掉电瞬间链路残留。
BLE 协议处理层详解(ble_trans.c)
ble_trans.c 是透传的"数据面"核心,包含 GATT 配置、安全配置、广播、数据收发与事件上报五部分。
GATT 控制块与回调注册
模块通过三个静态配置结构体 + 一个控制块把自身挂进协议栈:
static const sm_cfg_t trans_sm_init_config = {
.slave_security_auto_req = 0,
.slave_set_wait_security = 0,
#if PASSKEY_ENABLE
.io_capabilities = IO_CAPABILITY_DISPLAY_ONLY,
#else
.io_capabilities = IO_CAPABILITY_NO_INPUT_NO_OUTPUT,
#endif
.authentication_req_flags = SM_AUTHREQ_BONDING | SM_AUTHREQ_MITM_PROTECTION,
.min_key_size = 7,
.max_key_size = 16,
.sm_cb_packet_handler = NULL,
};
const gatt_server_cfg_t trans_server_init_cfg = {
.att_read_cb = &trans_att_read_callback,
.att_write_cb = &trans_att_write_callback,
.event_packet_handler = &trans_event_packet_handler,
};
Source: ble_trans.c#L66-L88
安全配置的设计意图:默认 IO_CAPABILITY_NO_INPUT_NO_OUTPUT 且开启 SM_AUTHREQ_BONDING | SM_AUTHREQ_MITM_PROTECTION,即透传场景通常无配对界面,但保留绑定与防中间人能力;PASSKEY_ENABLE 置 1 可切换为 DISPLAY_ONLY 以便手机端弹窗显示 6 位 PIN。min_key_size = 7 满足 LE 最低 7 字节密钥要求,max_key_size = 16 允许 128 位 AES 密钥。
trans_gatt_control_block 汇总所有配置,并依据编译宏按需挂载 server / client / sm(ble_trans.c#L90-L116):mtu_size = ATT_LOCAL_MTU_SIZE、发送缓冲 cbuffer_size = ATT_SEND_CBUF_SIZE,multi_dev_flag = 0 表示单连接模式。
UART→BLE 数据通路
这是透传最核心的函数——串口收到的数据被原样封装为 GATT Notification 发给手机:
static void trans_uart_rx_to_ble(uint8_t *packet, uint32_t size)
{
if (trans_con_handle && ble_comm_att_check_send(trans_con_handle, size) &&
ble_gatt_server_characteristic_ccc_get(trans_con_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE)) {
ble_comm_att_send_data(trans_con_handle, ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, packet, size, ATT_OP_AUTO_READ_CCC);
log_info_hexdump(packet, size);
} else {
log_info("drop uart data!!!\n");
}
}
Source: ble_trans.c#L129-L138
发送前做了三重保护,这是透传数据不丢失的关键:
trans_con_handle非空——链路必须存在;ble_comm_att_check_send(conn_handle, size)——发送缓冲(ATT_SEND_CBUF_SIZE)能容纳本次数据,避免覆盖未发送完的数据;ble_gatt_server_characteristic_ccc_get(...)——对端必须已使能该特征的 CCC(开启 Notification),否则发送无意义。
任一条件不满足即打印 drop uart data!!! 并丢弃,保证协议栈发送队列不被撑爆(背压机制)。ATT_OP_AUTO_READ_CCC 表示发送时自动读取 CCC 确认。
BLE→UART 数据通路
手机端写入特征值时由 trans_att_write_callback 处理(在文件后段实现,本次源码探索未覆盖其函数体,但其声明见 ble_trans.c#L60)。结合 common_uart_init 的注释"ble2uart or uart2ble"可知:写回调中收到的 payload 会通过通用串口 TX 发送到外部 MCU,方向与 UART→BLE 对称。
回连恢复与连接参数更新
配对绑定后主机回连时,GATT Server 不会自动恢复各特征的 CCC,需要主动使能:
static void trans_resume_all_ccc_enable(uint16_t conn_handle, uint8_t update_request)
{
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae04_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae05_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_INDICATE);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae3c_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
if (update_request) {
trans_send_connetion_updata_deal(conn_handle);
}
}
Source: ble_trans.c#L171-L186
代码注释点明了设计动机:"配对绑定的方式,主机回连不是在使能server的通知开关,需要自己打开"(ble_trans.c#L168)。注意各特征使用不同通知类型:ae02_01/ae04_01/ae3c_01 用 NOTIFY,ae05_01 用 INDICATE——因为 INDICATE 带应用层确认,适合重要数据(如升级命令),NOTIFY 适合高速透传数据。
连接参数更新通过 ble_gatt_server_connetion_update_request(conn_handle, trans_connection_param_table, CONN_PARAM_TABLE_CNT) 发起,且用 trans_connection_update_enable 做一次性保护,避免重复请求被协议栈拒绝(ble_trans.c#L151-L158)。
事件上报
协议栈产生的 HCI/GATT 事件(连接、断开、安全、对端系统识别)经 trans_event_packet_handler 汇总,并通过 trans_event_post 转成系统事件推送给应用层:
static void trans_event_post(u32 arg_type, u8 priv_event, u8 *args, u32 value)
{
struct sys_event *e = event_pool_alloc();
if (e == NULL) {
log_info("Memory allocation failed for sys_event");
return;
}
e->type = SYS_BT_EVENT;
e->arg = (void *)arg_type;
e->u.bt.event = priv_event;
if (args) {
memcpy(e->u.bt.args, args, 7);
}
e->u.bt.value = value;
main_application_operation_event(NULL, e);
}
Source: ble_trans.c#L219-L236
这段代码是"协议栈线程 → 应用线程"的典型桥接:event_pool_alloc() 从事件池取空闲事件(内存不足时打印日志并放弃,不会阻塞协议栈),填充 SYS_BT_EVENT 类型后调用 main_application_operation_event 交给应用框架,最终进入 le_trans_run_loop 的 app_comm_process_handler 被消费。args 固定拷贝 7 字节(u.bt.args 数组容量),是 SDK 事件结构的既定约束。
对端操作系统识别通过 trans_check_remote_result 完成,REMOTE_TYPE_IOS 时打印 is ios,否则打印 not ios(ble_trans.c#L199-L206),用于区分 iOS/Android 的连接参数策略(iOS 对连接参数请求有限制)。
核心数据流(Core Flow)
透传的完整生命周期从应用启动到双向数据搬运,可按下面两条路径理解:
sequenceDiagram
participant APP as application 框架
participant AT as app_trans.c
participant BT as ble_trans.c
participant ST as btstack 协议栈
participant UART as 通用串口
participant PH as 手机 App
Note over APP,PH: 启动阶段
APP->>AT: ACTION_LE_TRANS_MAIN (APP_STA_START)
AT->>AT: le_trans_app_start: 时钟/LED/串口初始化
AT->>ST: le_trans_bt_start: btstack_ble_start_before_init
ST->>BT: comm_ble_profile_init 挂载 trans_gatt_control_block
AT->>AT: le_trans_run_loop: get_msg 循环
ST-->>BT: HCI/GATT 事件 (连接/安全/断链)
BT->>APP: trans_event_post: SYS_BT_EVENT
Note over UART,PH: 数据阶段 (UART→BLE)
UART->>BT: trans_uart_rx_to_ble(packet, size)
BT->>BT: 校验连接/缓冲/CCC 三重条件
BT->>PH: ble_comm_att_send_data (Notify ae02_01)
Note over UART,PH: 数据阶段 (BLE→UART)
PH->>BT: 写入特征值 → trans_att_write_callback
BT->>UART: 串口 TX 输出到外部 MCU
Note over AT,ST: 关机阶段
APP->>AT: le_trans_set_soft_poweroff
AT->>ST: btstack_ble_exit(0)
AT->>AT: 依 TCFG_LOWPOWER_PATTERN 决定是否等断链
关键顺序说明:
- 启动必须遵循"配置结构体 →
btstack_ble_start_before_init→btstack_init"的顺序,GATT 控制块在协议栈初始化前就绪; - UART→BLE 发送前的三重校验(连接句柄、发送缓冲、CCC 状态)必须在调用
ble_comm_att_send_data之前完成,缺一即丢弃数据; - 事件上报使用事件池异步推送,协议栈线程不会被应用层处理阻塞;
- 关机必须先
btstack_ble_exit(0)主动断链,再根据低功耗模式决定是否等待断链完成,避免链路残留导致功耗异常。
配置选项(Configuration Options)
透传框架的行为由编译宏与板级配置共同决定。源码中直接出现的配置项如下:
| 配置项 | 类型 | 默认值/取值 | 说明 |
|---|---|---|---|
CONFIG_APP_LE_TRANS | 编译宏 | 0/1 | 透传应用总开关,为 0 时 app_trans.c/ble_trans.c 不参与编译 |
TCFG_COMMON_UART_ENABLE | 编译宏 | 0/1 | 通用串口使能,关闭则无 UART 数据源 |
TCFG_COMMON_UART_BAUDRATE | 数值 | 板级定义 | 透传串口波特率 |
COMMON_UART_TX_PIN / COMMON_UART_RX_PIN | GPIO | 板级定义 | 透传串口引脚 |
TCFG_LOWPOWER_PATTERN | 枚举 | SOFT_MODE / SOFT_BY_POWER_MODE | 软关机模式:SOFT_MODE 非必须等断链;SOFT_BY_POWER_MODE 必须等断链 |
PASSKEY_ENABLE | 宏 | 0 | 置 1 时 SM io_capabilities 切换为 IO_CAPABILITY_DISPLAY_ONLY(显示 6 位 PIN) |
CONFIG_BT_GATT_SERVER_NUM | 数值 | 编译配置 | 非 0 时挂载 trans_server_init_cfg |
CONFIG_BT_GATT_CLIENT_NUM | 数值 | 编译配置 | 非 0 时挂载 trans_client_init_cfg |
CONFIG_BT_SM_SUPPORT_ENABLE | 编译宏 | 0/1 | 非 0 时挂载 trans_sm_init_config |
RCSP_BTMATE_EN | 编译宏 | 0/1 | 杰理 RCSP 助手相关功能(如 resume_all_ccc_enable 中的 RCSP 分支) |
APP_SWITCH_MODE_EN | 编译宏 | 0/1 | 应用切换模式,使能后在消息循环中可响应退出 |
TCFG_LED_ENABLE | 编译宏 | 0/1 | 启动时执行 led_operate(LED_INIT_FLASH) |
TCFG_CLOCK_SYS_HZ / TCFG_CLOCK_LSB_HZ / TCFG_CLOCK_SFC_HZ | 数值 | 板级定义 | 系统/LSB/SFC 时钟频率,启动时设置 |
安全与连接参数相关(定义于 ble_trans.h 及 profile 头文件,部分行号未在本次探索范围内):
| 配置项 | 类型 | 说明 |
|---|---|---|
ATT_LOCAL_MTU_SIZE | 数值 | 本地 MTU 大小,控制单包有效载荷 |
ATT_SEND_CBUF_SIZE | 数值 | ATT 发送环形缓冲大小,决定 ble_comm_att_check_send 的背压阈值 |
SM_AUTHREQ_BONDING / SM_AUTHREQ_MITM_PROTECTION | 位标志 | 绑定 + 防中间人认证要求 |
min_key_size / max_key_size | 数值 | 7 ~ 16 字节密钥范围 |
trans_connection_param_table / CONN_PARAM_TABLE_CNT | 表 | 连接参数候选表(间隔/潜伏期/超时) |
使用示例
示例 1:将透传应用注册进 application 框架
透传应用通过标准 application 结构注册,状态机回调由框架在应用切换时调用:
static int le_trans_state_machine(struct application *app, enum app_state state, struct intent *it)
{
switch (state) {
case APP_STA_CREATE:
break;
case APP_STA_START:
if (!it) {
break;
}
switch (it->action) {
case ACTION_LE_TRANS_MAIN:
le_trans_app_start();
break;
}
break;
case APP_STA_PAUSE:
break;
case APP_STA_RESUME:
break;
case APP_STA_STOP:
break;
}
return 0;
}
Source: app_trans.c#L217-L240(函数体末尾
return 0;及收尾代码位于后续行)
使用方式:其他应用(如 AT 模式、OTA)在需要切到透传时,构造 intent 并设置 action = ACTION_LE_TRANS_MAIN 后投递给本应用;应用收到后在 APP_STA_START 阶段启动。这就是杰理多应用框架的"意图驱动切换"。
示例 2:UART→BLE 透传发送(带三重背压校验)
这是透传数据面最核心的调用序列,展示了如何在驱动层直接搬运数据:
static void trans_uart_rx_to_ble(uint8_t *packet, uint32_t size)
{
if (trans_con_handle && ble_comm_att_check_send(trans_con_handle, size) &&
ble_gatt_server_characteristic_ccc_get(trans_con_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE)) {
ble_comm_att_send_data(trans_con_handle, ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE, packet, size, ATT_OP_AUTO_READ_CCC);
log_info_hexdump(packet, size);
} else {
log_info("drop uart data!!!\n");
}
}
Source: ble_trans.c#L129-L138
使用方式:在通用串口 RX 中断或轮询回调中调用,packet/size 为串口收到的一帧数据;返回前请确认 trans_con_handle 已由连接事件更新,否则数据会被丢弃。
示例 3:回连后恢复所有特征的通知使能
绑定设备回连时,服务端必须自行恢复 CCC,否则手机收不到数据:
static void trans_resume_all_ccc_enable(uint16_t conn_handle, uint8_t update_request)
{
log_info("resume_all_ccc_enable\n");
#if RCSP_BTMATE_EN
/* ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae02_02_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY); */
#endif
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae02_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae04_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae05_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_INDICATE);
ble_gatt_server_characteristic_ccc_set(conn_handle, ATT_CHARACTERISTIC_ae3c_01_CLIENT_CONFIGURATION_HANDLE, ATT_OP_NOTIFY);
if (update_request) {
trans_send_connetion_updata_deal(conn_handle);
}
}
Source: ble_trans.c#L171-L186
使用方式:在 GATT 连接建立事件(trans_event_packet_handler 收到连接事件)中调用,update_request = 1 时同时发起连接参数更新;新增自定义特征时需在此处同步添加 ccc_set 调用,这是回连功能最常见的扩展点。
API 参考
以下 API 均来自源码中可验证的签名与调用方式;ble_trans.h 中未在本次探索覆盖的完整声明以"声明于头文件"标注。
应用框架层(app_trans.c,static 函数,仅供本模块内部使用)
le_trans_app_start(void)
透传应用入口,依次完成时钟、蓝牙协议栈、LED、通用串口初始化后进入消息循环。
- 返回:
void - 调用方:
le_trans_state_machine在APP_STA_START+ACTION_LE_TRANS_MAIN时调用 - 说明:内部调用
le_trans_run_loop()后不返回(除非APP_SWITCH_MODE_EN触发退出)
le_trans_run_loop(void)
消息循环,循环执行 get_msg 与 app_comm_process_handler。
- 返回:
void - 说明:标注
LP_RUNCODE_AT(.lp_running.app.text),属于低功耗运行段代码
le_trans_set_soft_poweroff(void)
软关机流程:先 btstack_ble_exit(0) 断链,再按低功耗模式决定立即关机或等待断链。
- 返回:
void - 说明:
SOFT_MODE下通过sys_timeout_add(POWER_EVENT_POWER_SOFTOFF, ...)延迟关机
BLE 协议层(ble_trans.c)
trans_uart_rx_to_ble(uint8_t *packet, uint32_t size)
将串口数据帧通过 GATT Notify 发送给已连接主机。
- 参数:
packet(uint8_t*)数据指针;size(uint32_t)数据长度 - 返回:
void - 失败行为:连接无效、发送缓冲不足或 CCC 未使能时打印
drop uart data!!!并丢弃 - 要点:发送特征句柄
ATT_CHARACTERISTIC_ae02_01_VALUE_HANDLE,模式ATT_OP_AUTO_READ_CCC
trans_att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size)
GATT Server 属性读回调,处理主机发起的 Read 请求。
- 参数:
connection_handle连接句柄;att_handle属性句柄;offset读偏移;buffer输出缓冲;buffer_size缓冲大小 - 返回:
uint16_t(按 btstack 惯例返回写入字节数) - 声明位置:ble_trans.c#L59
trans_att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size)
GATT Server 属性写回调,接收手机下发的透传数据(BLE→UART 通路入口)。
- 参数:
connection_handle连接句柄;att_handle属性句柄;transaction_mode事务模式(Write/WriteNoResponse);offset写偏移;buffer数据;buffer_size长度 - 返回:
int(0 表示成功) - 声明位置:ble_trans.c#L60
trans_event_packet_handler(int event, uint8_t *packet, uint16_t size, uint8_t *ext_param)
协议栈事件统一入口(连接、断开、安全、对端系统识别等),内部调用 trans_event_post 推送系统事件。
- 返回:
int - 声明位置:ble_trans.c#L61
trans_event_post(u32 arg_type, u8 priv_event, u8 *args, u32 value)
从事件池分配 sys_event,填充 SYS_BT_EVENT 后投递到应用框架。
- 参数:
arg_type(u32)事件子类型;priv_event(u8)私有事件号;args(u8*)最多拷贝 7 字节附带参数;value(u32)事件附加值 - 返回:
void - 失败行为:
event_pool_alloc()返回 NULL 时打印日志并放弃(不阻塞协议栈线程)
trans_resume_all_ccc_enable(uint16_t conn_handle, uint8_t update_request)
回连后恢复各特征 CCC(ae02_01/ae04_01/ae3c_01 用 NOTIFY,ae05_01 用 INDICATE),可选发起连接参数更新。
- 参数:
conn_handle连接句柄;update_request(uint8_t)非 0 时调用trans_send_connetion_updata_deal - 返回:
void
trans_send_connetion_updata_deal(uint16_t conn_handle)
通过 ble_gatt_server_connetion_update_request 请求连接参数更新,trans_connection_update_enable 置 0 防止重复请求。
- 参数:
conn_handle连接句柄 - 返回:
void - 失败行为:请求返回非 0 时保留
trans_connection_update_enable,后续可重试
trans_check_remote_result(uint16_t con_handle, remote_type_e remote_type)
打印对端系统类型(REMOTE_TYPE_IOS 时标记 is ios),用于区分 iOS/Android 连接策略。
- 参数:
con_handle连接句柄;remote_type(remote_type_e)识别结果 - 返回:
void
专业笔记:故障模式、边界与并发
数据丢失与背压
- 发送缓冲满:
ble_comm_att_check_send返回假时数据被直接丢弃。透传是"尽力而为"通道,应用层(或外部 MCU)需自行实现流控(如串口 RTS/CTS、分帧重传)。若需要可靠传输,应改用带 INDICATE 确认的特征(如ae05_01)或上层协议分包。 - CCC 未使能:手机尚未打开 Notification 时串口数据同样被丢弃,
drop uart data!!!日志是排查"手机收不到数据"的第一线索。
连接状态竞争
trans_con_handle 在断开事件后可能残留旧值。trans_uart_rx_to_ble 虽校验句柄非空,但若在断链与事件处理之间串口数据到达,可能向已失效连接发送。SDK 的 ble_comm_att_send_data 内部会进一步校验,应用层可通过在断开事件中清零 trans_con_handle 加固。
并发与线程模型
- 串口 RX 中断/驱动线程与协议栈线程是两个执行上下文,
trans_uart_rx_to_ble调用ble_comm_att_send_data时协议栈内部有锁保护发送队列; - 事件上报使用
event_pool_alloc异步投递,协议栈线程绝不等待应用层,避免死锁; trans_connection_update_enable标志防重入,保证连接参数请求只发起一次。
低功耗边界
SOFT_BY_POWER_MODE 下若链路未断开就执行 app_power_set_soft_poweroff 会导致异常,因此代码严格区分两种模式;SOFT_MODE 的延迟关机通过 sys_timeout_add 实现,注意 WAIT_DISCONN_TIME_MS 需大于链路超时时间。
安全边界
默认 IO_CAPABILITY_NO_INPUT_NO_OUTPUT + MITM 保护:无配对界面时攻击者可伪装绑定,但 MITM 标志要求双方认证;对安全敏感场景应开启 PASSKEY_ENABLE 强制 PIN 确认。
性能与运维
- 单包吞吐:受
ATT_LOCAL_MTU_SIZE与ATT_SEND_CBUF_SIZE约束。cbuffer_size决定背压开始丢弃前的最大在途数据量,增大它可提高突发吞吐但增加 RAM 占用。 - 连接参数:
trans_connection_param_table提供候选参数表,iOS 对连接间隔有最小限制(30ms 量级),trans_check_remote_result的 iOS 识别可用于选择不同参数表。 - 调试手段:
log_info_hexdump(packet, size)打印每一帧透传数据;LE_DEBUG_TIMER_INFO可开启 1s 周期定时器日志;LOG_DUMP_ENABLE控制数据 dump 级别(见 app_trans.c#L42-L52 与 ble_trans.c#L40-L48)。 - 收发计数:模块维护
trans_recieve_test_count/trans_send_test_count统计收发帧数,可用于吞吐自测。
扩展点
- 新增透传特征:在 profile(
ble_trans_profile.h)中定义新特征句柄,并在trans_resume_all_ccc_enable中增加对应ccc_set;发送时调用ble_comm_att_send_data并指定新句柄。 - 切换通知类型:需要可靠传输时把
ATT_OP_NOTIFY换成ATT_OP_INDICATE(参考ae05_01的做法)。 - 接入其他数据源:仿照
trans_uart_rx_to_ble编写 SPI/I2C 数据回调,只需满足"连接有效 + 缓冲可容纳 + CCC 已使能"三重前置。 - 多连接支持:
trans_gatt_control_block.multi_dev_flag = 0为单连接;置 1 并引入按连接句柄区分的状态表即可支持多主机。 - 应用切换:通过
APP_SWITCH_MODE_EN+tans_exit_app标志,在消息循环中退出透传回到主应用,是 SDK 多应用协同的标准钩子。
测试与验证线索
本次源码探索未读取独立测试文件,但源码中保留了内建自测痕迹:trans_recieve_test_count / trans_send_test_count 收发计数与 trans_test_read_write_buf[4] 读写测试缓冲,配合 le_trans_run_loop 的消息循环可做回环测试(手机端写特征 → 串口回显 → 再转回 BLE)。LE_DEBUG_TIMER_INFO 定时器可用于长时间稳定性观测。建议验证路径:手机连接 → 打开 ae02_01 Notification → 串口发数据 → 手机应收到同帧数据;手机写 ae02_01 → 串口应输出同帧数据。