多连接与自定义服务示例
本页介绍 AW33N BLE SDK 中 apps/demo/transfer/examples/multi_conn 多连接示例(CONFIG_APP_MULTI),包括其角色架构、初始化流程、GATT 控制块、安全管理配置、低功耗处理方式以及基于自定义 GATT Profile 的多链路服务实现方式。
Purpose and Scope
本页覆盖以下内容:
multi_conn示例的模块划分(peripheral / central / adv / scan / profile)与文件职责;- 多连接应用从注册、启动到 BLE 协议栈初始化的完整控制流;
- GATT 控制块(
gatt_ctrl_t)中multi_dev_flag多设备标志与多链路 ATT 相关 API(multi_att_*); - 多角色(server + client + 非连接 adv/scan)同时运行的控制器层配置(
lib_btctrler_config.c); - 低功耗(软关机)流程中多链路断开的处理。
以下内容属于其他页面,不在本页展开:通用 BLE 通信框架 ble_comm 的完整实现、单独的单连接 GATT Server/Client 示例(如 simple_ble)、dongle/OTA 示例以及 RCSP 协议。相关页面见文末 Related Links。
Overview
在典型的单连接 BLE 应用中,设备要么作为 GATT Server 被手机连接,要么作为 GATT Client 去连接外设,链路数量固定为一。multi_conn 示例演示的是 SDK 的多链路能力:同一台设备可以同时运行多个 GATT Server 实例(被多台主机连接)、多个 GATT Client 实例(同时连接多台从机),并且可以叠加非连接的广播(ADV)与扫描(SCAN)通道。这种能力适用于 TWS/音箱双连接、一拖多遥控器、数据采集网关等场景。
该示例由以下几个子模块构成,每个子模块都可以通过编译宏独立开关:
ble_multi_peripheral.c/ble_multi_central.c:多 GATT Server / 多 GATT Client 的实现,二者内部的 Profile 定义集中在ble_multi_profile.h,即"自定义服务"的载体;ble_multi_adv.c/ble_multi_scan.c:非连接广播与扫描通道,可叠加在连接通道之上;ble_multi.c:多连接模块的核心胶水层,负责 GATT 控制块、安全配置与初始化编排;app_multi_conn.c:应用层入口,定义ble_init_cfg_t配置并驱动整个 app 的消息循环。
设计上,SDK 通过 gatt_ctrl_t.multi_dev_flag = 1 显式打开多设备模式,控制器层通过 config_btctler_le_master_multilink 等配置使能底层多链路调度,从而把"多连接"从应用层一路贯通到射频链路层。
Architecture
flowchart TD
subgraph sg_App["应用层 (apps/demo/transfer)"]
AppMain["app_main.c<br/>CONFIG_APP_MULTI → ACTION_MULTI_MAIN"]
AppMultiConn["app_multi_conn.c<br/>multi_data_config (ble_init_cfg_t)"]
end
subgraph sg_Multi["multi_conn 示例模块"]
MultiCore["ble_multi.c<br/>gatt 控制块 + sm 配置"]
Server["ble_multi_peripheral.c<br/>多 GATT Server"]
Client["ble_multi_central.c<br/>多 GATT Client"]
Profile["ble_multi_profile.h<br/>自定义服务 Profile"]
Adv["ble_multi_adv.c<br/>非连接广播"]
Scan["ble_multi_scan.c<br/>非连接扫描"]
end
subgraph sg_Stack["协议栈层 (apps/include_lib/bt_include)"]
BleComm["ble_comm (le_gatt_common.h)"]
Att["att.h<br/>multi_att_set/get/clear_ccc_config"]
GattCtrl["gatt_ctrl_t<br/>multi_dev_flag"]
end
subgraph sg_Ctrl["控制器配置层 (config)"]
BtCtrlCfg["lib_btctrler_config.c<br/>multi-link 角色配置"]
end
AppMain -->|"ACTION_MULTI_MAIN"| AppMultiConn
AppMultiConn -->|"bt_ble_init / bt_ble_before_start_init / bt_ble_exit"| MultiCore
MultiCore -->|"multi_server_init"| Server
MultiCore -->|"multi_client_init"| Client
Server --> Profile
Client --> Profile
MultiCore -->|"multi_adv_init/enable"| Adv
MultiCore -->|"multi_scan_init/enable"| Scan
MultiCore -->|"ble_comm_init(&ctrl_block)"| BleComm
BleComm --> GattCtrl
BleComm --> Att
GattCtrl -->|"server/client 数量宏"| BtCtrlCfg
如上图所示,多连接能力是一条从应用入口到控制器层的完整链路:app_main.c 根据 CONFIG_APP_MULTI 把应用名注册为 multi_conn 并派发到 ACTION_MULTI_MAIN;app_multi_conn.c 通过 ble_init_cfg_t 把 multi_bt_ble_init、multi_bt_ble_before_start_init、multi_bt_ble_exit 等回调挂到协议栈;ble_multi.c 则统一装配 GATT 控制块、安全配置并逐个初始化 server/client/adv/scan 子模块。底层 ble_comm 依赖 att.h 提供的 multi_att_* 系列 API 维护每连接独立的 CCC(Client Characteristic Configuration)状态,这是多链路 GATT 通信正确性的关键。
实现详解
1. 应用注册与入口分发
multi_conn 是 transfer demo 下的一个可选应用,通过编译宏 CONFIG_APP_MULTI 启用。在 app_main.c 中,应用名被注册为 "multi_conn",动作类型为 ACTION_MULTI_MAIN:
#elif CONFIG_APP_MULTI
it->name = "multi_conn";
it->action = ACTION_MULTI_MAIN;
Source: app_main.c
随后 app_multi_conn.c 定义 multi_data_config(类型 ble_init_cfg_t),把多连接模块的生命周期回调全部挂载到协议栈。这里 same_address = 0 表示多链路各实例使用独立的地址(避免多个角色共用地址导致链路冲突),appearance = 0 表示不广播外观字段:
static const ble_init_cfg_t multi_data_config = {
.same_address = 0,
.appearance = 0,
.ble_profile_init = comm_ble_profile_init,
.bt_ble_init = multi_bt_ble_init,
.bt_ble_before_start_init = multi_bt_ble_before_start_init,
.bt_ble_exit = multi_bt_ble_exit,
.ble_module_enable = multi_ble_module_enable,
};
Source: app_multi_conn.c
multi_app_start() 是应用主入口:先初始化时钟,再调用 multi_bt_start() 完成 PLL 配置与协议栈启动,最后进入消息循环,所有事件(按键、BLE 事件、电源事件等)统一交给 app_comm_process_handler 处理:
static void multi_app_start()
{
log_info("-----------multi_conn demo-------------");
clock_bt_init();
clk_set("sys", TCFG_CLOCK_SYS_HZ);
clk_set("lsb", TCFG_CLOCK_LSB_HZ);
clk_set("sfc", TCFG_CLOCK_SFC_HZ);
multi_bt_start();
int msg[4] = {0};
while (1) {
get_msg(sizeof(msg) / sizeof(int), msg);
app_comm_process_handler(msg);
}
}
Source: app_multi_conn.c
multi_bt_start() 在启动协议栈前先配置蓝牙 PLL 参数,然后调用 btstack_ble_start_before_init(&multi_data_config, 0) 注册配置,最后 btstack_init() 拉起协议栈任务:
static void multi_bt_start(void)
{
uint32_t sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_ble_start_before_init(&multi_data_config, 0);
btstack_init();
}
Source: app_multi_conn.c
2. GATT 控制块:多设备模式的开关
多连接的核心在于 ble_multi.c 中定义的 multi_gatt_control_block。gatt_ctrl_t 的 multi_dev_flag = 1 显式告知 GATT 层按照多设备模式运行——ATT 缓存、CCC 配置、发送队列都必须按连接句柄(conn_handle)区分:
//gatt 控制块初始化
static gatt_ctrl_t multi_gatt_control_block = {
//public
.mtu_size = ATT_LOCAL_MTU_SIZE,
.cbuffer_size = ATT_SEND_CBUF_SIZE,
.multi_dev_flag = 1,
//config
#if CONFIG_BT_GATT_SERVER_NUM
.server_config = &multi_server_init_cfg,
#else
.server_config = NULL,
#endif
#if CONFIG_BT_GATT_CLIENT_NUM
.client_config = &multi_client_init_cfg,
#else
.client_config = NULL,
#endif
#if CONFIG_BT_SM_SUPPORT_ENABLE
.sm_config = &multi_sm_init_config,
#else
.sm_config = NULL,
#endif
//cbk,event handle
.hci_cb_packet_handler = NULL,
};
Source: ble_multi.c
要点解读:
server_config/client_config分别指向多 Server 与多 Client 的初始化配置(multi_server_init_cfg/multi_client_init_cfg),它们由ble_multi_peripheral.c/ble_multi_central.c提供,并在文件中以extern声明引用(见 ble_multi.c L71-L72);mtu_size取ATT_LOCAL_MTU_SIZE,cbuffer_size由ble_multi.h中的宏计算得出——ATT_PACKET_NUMS_MAX (2)表示 ATT 发送缓存支持 2 个待发数据包,每个包的大小为ATT_PACKET_HEAD_SIZE + ATT_LOCAL_MTU_SIZE:
//ATT缓存的buffer支持缓存数据包个数
#define ATT_PACKET_NUMS_MAX (2)
//ATT缓存的buffer大小, note: need >= 23,可修改
#define ATT_SEND_CBUF_SIZE (ATT_PACKET_NUMS_MAX * (ATT_PACKET_HEAD_SIZE + ATT_LOCAL_MTU_SIZE))
Source: ble_multi.h
multi_dev_flag = 1 在多连接场景下至关重要:单连接模式下 CCC 配置通常是全局一份,多连接模式下每个对端设备各自使能/关闭通知,因此必须按 conn_handle 隔离。协议栈为此提供了 multi_att_set_ccc_config / multi_att_get_ccc_config / multi_att_clear_ccc_config 三个按连接句柄操作的 API(att.h L114-L118),链路断开时用 multi_att_clear_ccc_config(conn_handle) 清理该链路的 CCC 状态。
3. 安全管理配置
multi_sm_init_config 定义了多连接模式下的配对/加密策略:主机侧(Client)主动发起安全请求并等待安全完成;从机侧(Server)不主动请求安全,但等待安全流程完成。默认 io_capabilities = IO_CAPABILITY_NO_INPUT_NO_OUTPUT(无输入输出,即 Just Works 配对),如需 PIN 码配对可打开 PASSKEY_ENABLE 宏切换为 IO_CAPABILITY_DISPLAY_ONLY:
//输入passkey 加密
#define PASSKEY_ENABLE 0
static const sm_cfg_t multi_sm_init_config = {
.master_security_auto_req = 1,
.master_set_wait_security = 1,
.slave_security_auto_req = 0,
.slave_set_wait_security = 1,
#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,
};
Source: ble_multi.c
设计意图:master_security_auto_req = 1 保证 Client 在连接建立后自动触发加密协商,无需应用层额外调用;slave_set_wait_security = 1 则让 Server 端在收到安全请求前保持等待状态,二者配合确保主从双方都能感知链路安全状态。authentication_req_flags 同时设置 Bonding 与 MITM 保护,密钥长度范围 7~16 字节,符合 BLE 安全规范中对加密密钥长度的要求。
4. BLE 初始化编排(核心控制流)
ble_multi.c 的三个回调 multi_bt_ble_before_start_init → multi_bt_ble_init → multi_bt_ble_exit 对应协议栈生命周期的三个阶段。
启动前初始化在协议栈任务运行之前执行,将 GATT 控制块交给 ble_comm 框架:
void multi_bt_ble_before_start_init(void)
{
log_info("%s", __FUNCTION__);
ble_comm_init(&multi_gatt_control_block);
}
Source: ble_multi.c
正式初始化按依赖顺序执行:先设置本地名称,再分别初始化 GATT Server 与 GATT Client(各自内部完成自定义 Profile 注册),随后使能 BLE 模块,最后叠加非连接广播与扫描通道:
void multi_bt_ble_init(void)
{
log_info("%s\n", __FUNCTION__);
ble_comm_set_config_name(bt_get_local_name(), 1);
#if CONFIG_BT_GATT_SERVER_NUM
multi_server_init();
#if CFG_USE_24G_CODE_ID_ADV
rf_set_adv_24g_hackable_coded(CFG_RF_24G_CODE_ID_ADV);//设置为2.4g模式
#endif
#endif
#if CONFIG_BT_GATT_CLIENT_NUM
multi_client_init();
#if CFG_USE_24G_CODE_ID_SCAN
rf_set_scan_24g_hackable_coded(CFG_RF_24G_CODE_ID_SCAN);//设置为2.4g模式
#endif
#endif
ble_module_enable(1);
#if CONFIG_BT_NOCONN_ADV_NUM
multi_adv_init();
multi_adv_enable(1);
#endif
#if CONFIG_BT_NOCONN_SCAN_NUM
multi_scan_init();
ble_comm_dev_scan_report_handler_register(multi_scan_report_handle);
multi_scan_enable(1);
#endif
}
Source: ble_multi.c
注意 multi_scan_init() 之后通过 ble_comm_dev_scan_report_handler_register(multi_scan_report_handle) 把扫描上报回调注册到 ble_comm,说明非连接扫描与连接管理共用同一套设备发现框架。
退出流程与初始化严格对称,先关 BLE 模块,再退出 ble_comm,最后退出各子模块:
void multi_bt_ble_exit(void)
{
log_info("%s\n", __FUNCTION__);
ble_module_enable(0);
ble_comm_exit();
#if CONFIG_BT_GATT_SERVER_NUM
multi_server_exit();
#endif
#if CONFIG_BT_GATT_CLIENT_NUM
multi_client_exit();
#endif
}
Source: ble_multi.c
按键测试入口 ble_multi_key_test() 则按编译宏把按键事件分发给 Client 与 Server 两侧,便于在没有手机 App 时用按键验证多链路收发(ble_multi.c L103-L112)。
5. 控制器层多链路配置
多连接不是纯应用层特性,底层控制器(BT Controller)必须同时调度多条链路。config/lib_btctrler_config.c 中按 CONFIG_APP_MULTI 调整角色配置宏:当存在非连接广播时从机角色仅保留 LE_ADV,当存在非连接扫描时主机角色仅保留 LE_SCAN,并且只要四个通道数量之和大于 1 就打开主链路多连接开关:
#elif CONFIG_APP_MULTI && CONFIG_BT_NOCONN_ADV_NUM
#define SET_SLAVE_ROLS_CFG (LE_ADV)
...
#elif CONFIG_APP_MULTI && CONFIG_BT_NOCONN_SCAN_NUM
#define SET_MASTER_ROLS_CFG (LE_SCAN)
...
#if CONFIG_APP_MULTI
const int config_btctler_le_master_multilink = ((CONFIG_BT_GATT_CLIENT_NUM + CONFIG_BT_GATT_SERVER_NUM + CONFIG_BT_NOCONN_SCAN_NUM + CONFIG_BT_NOCONN_ADV_NUM) > 1) ? 1 : 0;
#else
Source: lib_btctrler_config.c
这里 config_btctler_le_master_multilink 是控制器多链路调度的总开关,取值取决于 GATT Client 数、GATT Server 数、扫描数与广播数之和。这意味着"多连接"的最终形态(纯双从机、纯多主机、主机+从机+广播+扫描混跑)完全由这四组宏决定,应用代码无需改动。
Core Flow
下面是多连接示例从上电到建立两条链路的完整时序:
sequenceDiagram
participant App as app_main / app_multi_conn
participant MC as ble_multi.c
participant BC as ble_comm
participant Stack as BTStack
participant Ctrl as BT Controller
participant Peer as 对端设备(手机/从机)
App->>MC: btstack_ble_start_before_init(&multi_data_config)
App->>MC: btstack_init() 启动协议栈
MC->>BC: multi_bt_ble_before_start_init → ble_comm_init(&multi_gatt_control_block)
Note over BC: multi_dev_flag=1 打开多设备模式
MC->>MC: multi_bt_ble_init
MC->>MC: multi_server_init() 注册自定义服务
MC->>MC: multi_client_init() 注册客户端
MC->>MC: ble_module_enable(1)
MC->>MC: multi_adv_init/enable、multi_scan_init/enable
Stack->>Ctrl: 使能 ADV / SCAN
Peer->>Stack: 连接请求 (作为 Server 被连接)
Stack-->>MC: 连接事件 (conn_handle A)
MC->>BC: 按 conn_handle A 管理 GATT/CCC
MC->>Stack: 主动连接对端 (作为 Client)
Stack-->>MC: 连接完成 (conn_handle B)
MC->>BC: 按 conn_handle B 管理 GATT/CCC
Note over MC,BC: multi_att_set_ccc_config(conn_handle, ...) 每链路独立
时序要点:
- 协议栈启动前,
ble_comm_init收到带multi_dev_flag=1的控制块,此后所有 GATT 状态均按连接句柄隔离; - Server/Client 各自初始化后,设备同时处于"可被连接"与"可主动连接"状态,并叠加广播/扫描通道;
- 每条链路建立后,CCC 配置、通知使能、发送队列都绑定各自的
conn_handle,互不干扰; - 低功耗/软关机时,
multi_set_soft_poweroff先主动断开所有链路再进入低功耗,避免链路超时导致关机失败(见下一节)。
6. 低功耗与软关机处理
多链路场景下进入低功耗比单连接更复杂:任何一条链路不断开,射频都必须保持活跃。app_multi_conn.c 的 multi_set_soft_poweroff() 展示了标准处理流程——先主动关闭协议栈链路,再根据剩余连接情况决定是立即软关机还是等待断开:
static void multi_set_soft_poweroff(void)
{
log_info("set_soft_poweroff\n");
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
multi_is_active = 1;
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
multi_is_active = 0;//allow enter lowpower
#endif
//必须先主动断开蓝牙链路,否则要等链路超时断开
btstack_ble_exit(0);
//延时,确保BT退出链路断开
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_multi_conn.c
其中 ble_comm_dev_is_connected(GATT_ROLE_SERVER) / ble_comm_dev_is_connected(GATT_ROLE_CLIENT) 分别查询从机侧与主机侧是否仍存在活动链路——这是多连接特有的检查,单连接实现通常只查一个角色。multi_is_active 标志(app_multi_conn.c L49)配合 multi_state_idle_set_active() 控制空闲状态是否允许进入低功耗;app_to_recover() 则预留了恢复流程的钩子(默认注释掉 scan 低功耗开关)。
Configuration Options
多连接示例的行为完全由编译宏驱动,无运行时配置。关键选项如下:
| 配置项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
CONFIG_APP_MULTI | bool | 0/1 | 是否编译 multi_conn 应用(应用注册名 "multi_conn") |
CONFIG_BT_GATT_SERVER_NUM | int | 0~N | GATT Server 实例数量,即"可同时被多少台主机连接" |
CONFIG_BT_GATT_CLIENT_NUM | int | 0~N | GATT Client 实例数量,即"可同时主动连接多少台从机" |
CONFIG_BT_NOCONN_ADV_NUM | int | 0~N | 非连接广播通道数量(叠加在连接之上) |
CONFIG_BT_NOCONN_SCAN_NUM | int | 0~N | 非连接扫描通道数量 |
CONFIG_BT_SM_SUPPORT_ENABLE | bool | 0/1 | 是否使能安全管理器;为 0 时 sm_config 置 NULL |
PASSKEY_ENABLE | bool | 0 | 置 1 时 io_capabilities 切换为 IO_CAPABILITY_DISPLAY_ONLY(PIN 码配对) |
ATT_PACKET_NUMS_MAX | int | 2 | ATT 发送缓存支持的数据包个数(可修改) |
ATT_SEND_CBUF_SIZE | int | 派生 | ATT 发送缓存总大小,需 ≥ 23 字节 |
CFG_USE_24G_CODE_ID_ADV/SCAN | bool | 0/1 | 是否将广播/扫描切换为 2.4G 私有模式(rf_set_*_24g_hackable_coded) |
TCFG_LOWPOWER_PATTERN | enum | SOFT_MODE 等 | 低功耗模式,决定软关机是否等待链路断开 |
WAIT_DISCONN_TIME_MS | int | 自定义 | 软关机等待链路断开的超时时间 |
其中 Server/Client/Adv/Scan 四个数量宏不仅决定应用层初始化哪些子模块,还通过 config_btctler_le_master_multilink 决定控制器是否进入多链路调度模式(见 lib_btctrler_config.c L176)。
API Reference
ble_multi.h 声明了多连接模块对外暴露的全部接口(ble_multi.h L17-L37):
生命周期接口
| 函数 | 说明 |
|---|---|
void multi_bt_ble_before_start_init(void) | 协议栈启动前调用,执行 ble_comm_init(&multi_gatt_control_block) 注册 GATT 控制块 |
void multi_bt_ble_init(void) | 协议栈启动后初始化 Server/Client/Adv/Scan 全部子模块并 ble_module_enable(1) |
void multi_bt_ble_exit(void) | 关闭 BLE 模块并依次退出 ble_comm、Server、Client |
void multi_ble_module_enable(uint8_t en) | 使能/关闭 BLE 模块(透传 ble_comm_module_enable) |
void multi_state_idle_set_active(uint8_t active) | 设置空闲状态是否允许进入低功耗 |
子模块接口
| 函数 | 说明 |
|---|---|
void multi_server_init(void) / multi_server_exit(void) | 多 GATT Server 初始化/退出(自定义服务注册) |
void multi_client_init(void) / multi_client_exit(void) | 多 GATT Client 初始化/退出 |
int multi_server_clear_pair(void) / int multi_client_clear_pair(void) | 清除 Server/Client 侧配对信息 |
void multi_server_key_test(uint8_t key_type, uint8_t key_value) / multi_client_key_test(...) | 按键测试分发,验证收发 |
void ble_multi_key_test(uint8_t key_type, uint8_t key_value) | 按键测试总入口,按宏分发给 Client/Server |
void multi_adv_init(void) / multi_adv_enable(u8 enable) | 非连接广播初始化/使能 |
void multi_scan_init(void) / multi_scan_enable(u8 enable) | 非连接扫描初始化/使能 |
void multi_scan_report_handle(adv_report_t *report_pt, uint16_t len) | 扫描上报回调,注册给 ble_comm 使用 |
多链路 ATT 辅助 API(协议栈提供)
| 函数 | 说明 |
|---|---|
void multi_att_set_ccc_config(uint16_t conn_handle, uint16_t att_handle, uint16_t cfg) | 按连接句柄设置某属性的 CCC 值 |
uint16_t multi_att_get_ccc_config(uint16_t conn_handle, uint16_t att_handle) | 按连接句柄读取某属性的 CCC 值 |
int multi_att_clear_ccc_config(uint16_t conn_handle) | 断开时清除指定链路的所有 CCC 状态 |
Source: att.h
自定义服务(Profile)扩展方式
"自定义服务"在多连接示例中的载体是 ble_multi_profile.h 与 ble_multi_peripheral.c / ble_multi_central.c。扩展一个新服务时,遵循以下模式:
- 在
ble_multi_profile.h中定义服务 UUID、特征 UUID 与句柄索引(该文件位于examples/multi_conn/目录下,与ble_multi.c同目录,被 Server/Client 两端共同引用); - 在
ble_multi_peripheral.c的multi_server_init_cfg(gatt_server_cfg_t)中把新服务加入服务表; - 若该服务需要向对端发送通知,使用
multi_att_set_ccc_config(conn_handle, handle, ...)按链路维护 CCC; - Server/Client 数量宏(
CONFIG_BT_GATT_SERVER_NUM/CONFIG_BT_GATT_CLIENT_NUM)决定协议栈为该服务分配多少实例。
由于本示例的四个数量宏全部独立可配,同一份 Profile 代码即可编译成"纯双从机"、"一主多从"或"主从+广播扫描混跑"等不同形态。
失败模式、边界情况与并发
- 链路断开清理:多链路下断开事件按
conn_handle区分,必须调用multi_att_clear_ccc_config(conn_handle)清理该链路的 CCC,否则新连接可能读到残留的使能状态(协议栈 API 见 att.h L114-L118)。 - 软关机等待链路断开:
SOFT_BY_POWER_MODE下必须等所有链路断开(app_power_soft.wait_disconn = 1)才能关机,否则射频仍占用功耗;SOFT_MODE下则通过sys_timeout_add(..., WAIT_DISCONN_TIME_MS)延迟关机,超时后强制执行。 - 地址冲突:
multi_data_config.same_address = 0明确要求多实例使用不同地址。若改为 1(共享地址),多角色同时广播/扫描可能造成地址解析冲突,应避免在混跑模式下使用。 - ATT 发送缓存瓶颈:
ATT_SEND_CBUF_SIZE仅支持ATT_PACKET_NUMS_MAX = 2个待发包。多链路并发下发通知时,发送缓冲是共享的,若多条链路同时产生大量数据,可能出现队列拥塞,需要按业务调节包数或错峰发送。 - 安全流程并发:
master_security_auto_req = 1使每条 Client 链路在连接后自动发起加密。多条链路同时建连时,安全协商按链路串行完成,master_set_wait_security/slave_set_wait_security保证双方都等待安全状态稳定后再进行数据收发。
性能与运维
- 资源占用与宏强相关:每个 Server/Client 实例都会占用 ATT 缓存、链路资源与 RAM,实例数量由
CONFIG_BT_GATT_SERVER_NUM等宏决定,按需裁剪可显著降低内存占用。 - 日志开关:
MULTI_CONN日志标签在 log_config.c L176-L180 中统一控制(log_tag_const_v/i/d/w/e_MULTI_CONN),排查多链路问题时建议打开d级日志观察每链路事件。 - 24G 私有模式:
CFG_USE_24G_CODE_ID_ADV/SCAN可把广播/扫描切换到 2.4G 私有编码模式,用于私有协议互联;开启后蓝牙标准连接行为不受影响,但需确认对端同样支持该模式。
Related Links
- ble_multi.c(核心胶水层)
- app_multi_conn.c(应用入口与低功耗)
- ble_multi.h(接口与缓存宏定义)
- lib_btctrler_config.c(控制器多链路配置)
- att.h(多链路 ATT/CCC API)
- 示例目录其他子模块:
ble_multi_peripheral.c、ble_multi_central.c、ble_multi_profile.h、ble_multi_adv.c、ble_multi_scan.c(位于apps/demo/transfer/examples/multi_conn/) - 相关页面:BLE 通信公共框架(
ble_comm)、单连接 GATT Server/Client 示例、dongle/OTA 示例