- 新增 ble_bond_multi_module.c 实现多槽位蓝牙绑定管理功能 - 添加 ble_bond_multi_event 事件系统支持槽位状态广播 - 在 CMakeLists.txt 中注册新模块和事件源文件 - 更新 Kconfig 配置添加 BLINKY_BLE_BOND_MULTI 选项 - 修改 prj.conf 配置支持 4 个配对设备和 5 个身份标识 - 关闭默认 CAF ble_bond 模块使用自定义实现 - 更新 ui_settings_controller.h 接口支持槽位元数据设置 - 在 display_module.c 中添加事件订阅刷新UI显示 - 编写详细的设计文档 ble_multi_slot_design.md
14 KiB
Blinky 多槽蓝牙设计方案
1. 目标与约束
为 C:\projects\blinky 增加多槽蓝牙能力,当前阶段只实现 BLE,多 dongle 方案先预留结构,不实现完整业务流程。
本方案已经按当前讨论结论收敛为以下硬约束:
- 支持 3 个 BLE 槽位
Slot 1~3 - 每个槽位固定对应一个 Bluetooth local identity
- 槽位切换本质上是切换
bt_identity_id - 不引入
app_slot_id - 不使用临时 identity
- 不做 erase advertising
- 不做擦除确认
- 不做擦除回滚
- 擦除当前槽位时直接删除该 identity 上的 bond
- settings 只持久化
current_slot和各槽位meta slot_meta中必须保留display_name,用于 UI 显示24G / dongle采用方案 A,但本阶段只预留专用槽位
2. 本地参考基线
本方案主要参考本地 NCS 3.2.3 中以下实现和文档:
c:\ncs\v3.2.3\nrf\applications\nrf_desktop\doc\ble_bond.rstc:\ncs\v3.2.3\nrf\applications\nrf_desktop\src\modules\ble_bond.cc:\ncs\v3.2.3\nrf\applications\nrf_desktop\src\modules\Kconfig.ble_bondc:\ncs\v3.2.3\nrf\include\caf\events\ble_common_event.hc:\ncs\v3.2.3\nrf\subsys\caf\modules\ble_adv.cc:\ncs\v3.2.3\nrf\subsys\caf\modules\Kconfig.ble_statec:\ncs\v3.2.3\nrf\doc\nrf\libraries\caf\ble_state.rst
参考方式如下:
- 继承
nrf_desktop的总体方向:应用自定义 bond 管理模块,而不是 CAF 默认ble_bond - 继续通过
ble_peer_operation_event驱动 CAFble_adv - 保留 dongle 专用 identity 的规划方式
明确不采用 nrf_desktop 的部分:
- 不使用
app_slot_id <-> bt_identity_id的二级映射 - 不使用临时 identity
- 不采用
ERASE_ADV - 不采用“新配对成功后再替换旧槽位”的回滚保护机制
3. 对现有项目的判断
blinky 当前已经具备多槽蓝牙需要的大部分基础能力:
- 已启用
CONFIG_CAF_BLE_STATE - 已启用
CONFIG_CAF_BLE_ADV - 已启用
CONFIG_BT_SETTINGS - HID 和 NUS 都已基于
ble_peer_event跟踪当前连接 - UI 已经预留 3 个蓝牙槽位和擦除当前槽位入口
- 模式策略层已经区分:
MODE_SWITCH_BLE -> BLE_PROFILE_POLICY_GENERALMODE_SWITCH_24G -> BLE_PROFILE_POLICY_DONGLE
相关文件:
C:\projects\blinky\src\ble_hid_module.cC:\projects\blinky\src\ble_nus_module.cC:\projects\blinky\src\ui\ui_settings_ble.cC:\projects\blinky\src\ui\ui_settings_controller.cC:\projects\blinky\src\mode_policy_module.cC:\projects\blinky\prj.conf
当前限制也很明确:
CONFIG_BT_MAX_PAIRED=1,全设备只能保存 1 个 bond- 当前是单槽逻辑
- 还在使用 CAF 默认
CONFIG_CAF_BLE_BOND=y - UI 的槽位状态仍是内存假数据
4. 配置语义结论
基于本地 Kconfig 和 CAF 文档,关键配置的含义如下:
CONFIG_BT_MAX_PAIRED说明:整个设备允许保存的 bond 总数CONFIG_CAF_BLE_STATE_MAX_LOCAL_ID_BONDS说明:每个 Bluetooth local identity 允许的 bond 数上限CONFIG_BT_ID_MAX说明:设备允许使用的 local identity 总数
如果目标是:
- 3 个 BLE 槽位
- 每槽 1 个 bond
- 预留 1 个 dongle 槽位
那么推荐配置应为:
CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=4
CONFIG_CAF_BLE_STATE_MAX_LOCAL_ID_BONDS=1
CONFIG_BT_ID_MAX=5
语义如下:
BT_MAX_CONN=1说明:同时只保持 1 条 BLE 连接BT_MAX_PAIRED=4说明:全设备最多保存 4 个 bondCAF_BLE_STATE_MAX_LOCAL_ID_BONDS=1说明:每个 identity 只允许绑定 1 个主机BT_ID_MAX=5说明:可用 identity 为0..4
配合本方案的 identity 规划:
0:默认 identity,不参与槽位1:Slot 12:Slot 23:Slot 34:Dongle Slot 预留
5. 总体架构
5.1 设计结论
建议采用:
- 保留
CAF_BLE_STATE - 保留
CAF_BLE_ADV - 关闭 CAF 默认
BLE_BOND - 新增应用自定义模块
ble_bond_multi_module
即:
CONFIG_CAF_BLE_BOND=n- 新增
CONFIG_BLINKY_BLE_BOND_MULTI - 自定义模块
select CAF_BLE_BOND_SUPPORTED - 由自定义模块向 CAF 提交
ble_peer_operation_event
原因:
- CAF 默认
ble_bond不适合固定多 identity 槽位管理 - 你的需求已经明确不需要
nrf_desktop那种复杂映射和回滚流程 - 自定义模块更适合和现有 UI、模式策略、settings 同步
5.2 核心模型
本方案直接把“槽位”和“identity”绑定:
Slot 1=bt_identity_id 1Slot 2=bt_identity_id 2Slot 3=bt_identity_id 3Dongle Slot=bt_identity_id 4
所以:
current_slot实际上就是当前使用的bt_identity_id- UI 显示“Slot 1/2/3”时,后端直接映射到固定 identity
- 不再存在可变的
id_lut
这个设计的优点:
- 逻辑简单
- settings 简单
- 调试简单
- 后续接入 dongle 不需要迁移已有 BLE 槽位
6. identity 规划
建议固定如下:
| identity | 角色 | 当前阶段 |
|---|---|---|
0 |
默认 identity | 不使用 |
1 |
BLE Slot 1 | 使用 |
2 |
BLE Slot 2 | 使用 |
3 |
BLE Slot 3 | 使用 |
4 |
Dongle Slot | 预留 |
设计原则:
- 不使用
BT_ID_DEFAULT原因:默认 identity 的 reset/unpair 行为不适合作为固定槽位基线 - 3 个 BLE 槽位 identity 固定不变
identity 4只给BLE_PROFILE_POLICY_DONGLE
7. 模块设计
7.1 新增模块和文件
建议新增:
src/ble_bond_multi_module.csrc/events/ble_bond_multi_event.cinc/events/ble_bond_multi_event.h
不建议新增:
inc/ble_bond_multi.h
原因:
- 当前设计是事件驱动模块
- 没有必要向外暴露一组直接调用 API
- 共享内容更适合放在事件头文件里
只有在后续出现明确的跨模块直接调用需求时,才需要再补 inc/ble_bond_multi.h
7.2 模块职责边界
ble_bond_multi_module
负责:
- 管理当前活动
bt_identity_id - 启动时恢复
current_slot - 维护各 identity 槽位的
slot_meta - 处理“切换当前槽位”
- 处理“直接擦除当前槽位”
- 监听连接、加密、断链事件,更新
slot_meta - 向 CAF 提交
ble_peer_operation_event - 向 UI / 显示层广播槽位状态
不负责:
- 发 HID/NUS 数据
- 改 GATT 服务
- 绘制 UI
ble_hid_module / ble_nus_module
保持现有思路,不理解“多槽逻辑”,只跟随当前活动连接:
- 连接上谁,就服务谁
- 切槽或擦除造成断链时,内部复位连接状态
mode_policy_module
负责决定当前是:
BLE_PROFILE_POLICY_GENERALBLE_PROFILE_POLICY_DONGLE
但不负责槽位 bond 细节。
8. 状态机设计
第一阶段建议极简化,只保留:
DISABLEDIDLESWITCHINGERASING
明确不引入:
SELECT_SLOTERASE_PENDINGERASE_ADVTEMP_IDENTITYROLLBACK
8.1 切槽流程
- 用户在 UI 选择
Slot N - 模块判断:
- 如果
N == current_slot,直接返回 - 否则进入
SWITCHING
- 如果
- 提交
PEER_OPERATION_SELECTED - CAF
ble_adv切换当前 advertising identity - 当前连接断开
- 设备以新 identity 重新广播
- 目标主机重新连接
- 保存新的
current_slot - 返回
IDLE
这里采用“直接切换”,不做预选确认。
8.2 擦除当前槽位流程
- 用户选择“Erase Bond”
- 模块进入
ERASING - 对当前
bt_identity_id执行:bt_unpair(identity, BT_ADDR_LE_ANY)
- 清除该槽位
slot_meta - 重新以当前 identity 进入可配对广播
- 返回
IDLE
这里采用产品级简化语义:
- 不确认
- 不保护旧配对
- 不回滚
- 擦除后旧主机立即失效
9. Settings 设计
9.1 持久化范围
只持久化:
current_slot- 各槽位
slot_meta
不持久化:
id_lut- 临时 identity 状态
- 擦除中间态
9.2 settings namespace
建议采用:
ble_multi/current_slotble_multi/meta/1ble_multi/meta/2ble_multi/meta/3ble_multi/meta/4
其中:
1/2/3是 BLE 槽位4是 dongle 预留槽位
9.3 slot_meta 结构
slot_meta 建议固定包含以下字段:
occupiedlast_peer_addrdisplay_name
字段说明:
occupied说明:该槽位当前是否有有效 bondlast_peer_addr说明:最近一次绑定主机的 BLE 地址,用于识别和调试display_name说明:UI 显示名,不做简化,作为正式字段保留
这里 display_name 是明确保留项,不是可选优化项。
设计目的就是让 UI 能直接显示:
MacBook ProiPad MiniWindows-PC
而不是只能显示 Bonded / Empty。
9.4 display_name 来源
需要注意一点:
- Zephyr bond 本身不会自动提供“友好主机名”
因此 display_name 的来源需要在实现阶段明确,一般有两个方向:
- 配对后由应用层通过协议或自定义方式写入
- 若当前阶段无法拿到真实主机名,则先允许 UI/上位机为该槽位写入名称
也就是说:
display_name必须作为正式持久化字段存在- 但其填充机制可以分阶段实现
第一阶段如果没有自动名称来源,建议默认值如下:
- 有 bond 但无名称:
"Bonded Device" - 无 bond:空串或
"Empty"
10. UI 设计
现有 UI 结构已经适合复用,不建议重做。
现有文件:
C:\projects\blinky\src\ui\ui_settings_ble.cC:\projects\blinky\src\ui\ui_settings_controller.c
建议交互规则如下:
- 进入 BLE 设置页后显示
Slot 1~3 - 每个槽位 value 直接显示
display_name - 若该槽位无 bond,则显示
Empty - 当前槽位显示为
Slot N - 擦除项显示为
Erase Slot N
操作方式:
- 选中某个槽位后直接切换
- 选中擦除项后直接擦除
- 不加确认页
UI 需要的数据来源:
current_slotslot_meta[1]slot_meta[2]slot_meta[3]
所以建议 UI 不再维护本地假数据,而改为订阅 ble_bond_multi_event 或读取统一状态缓存。
11. 与模式切换的兼容设计
11.1 BLE 档
MODE_SWITCH_BLE 下:
- 只允许使用
identity 1/2/3 - UI 允许切槽和擦除
current_slot必须属于1..3
11.2 24G / dongle 档
采用方案 A:
MODE_SWITCH_24G绑定专用identity 4identity 4不与普通 BLE 槽位混用- 第一阶段只预留,不实现完整 dongle bond 生命周期
这样做的好处:
- 保持
mode_policy_module.c现有设计语义 - 普通 BLE 槽位和 dongle 配对状态不会互相污染
- 后续接入 dongle 时无需改动 BLE 三槽数据模型
12. 事件设计
建议新增应用事件:
ble_bond_multi_event
放在:
inc/events/ble_bond_multi_event.hsrc/events/ble_bond_multi_event.c
字段建议:
current_slotactive_identity_idslot_occupied_bitmapslot_meta_summaryop
至少要能支持:
- UI 刷新槽位名称
- 显示层提示当前槽位
- 设置层同步当前 active slot
继续复用 CAF 事件:
ble_peer_eventble_peer_operation_event
第一阶段不需要依赖:
ble_peer_search_event
13. 对现有代码的影响评估
13.1 必改
prj.confCMakeLists.txt- 新增
src/ble_bond_multi_module.c - 新增
inc/events/ble_bond_multi_event.h - 新增
src/events/ble_bond_multi_event.c ui_settings_controller.c
13.2 小改
mode_policy_module.c说明:把BLE_PROFILE_POLICY_GENERAL约束到identity 1/2/3,把BLE_PROFILE_POLICY_DONGLE预留到identity 4display_module.c说明:如果要显示当前槽位或名称,需要订阅新事件
13.3 原则上不应大改
ble_hid_module.cble_nus_module.ckeyboard_core_module.c
这些模块只应该感知当前连接状态,不承担槽位管理职责。
14. 实施分阶段建议
阶段 1:固定 identity 多槽骨架
- 替换 CAF 默认
ble_bond - 新增
ble_bond_multi_module - 固定
identity 1/2/3为 BLE 槽位 - 预留
identity 4为 dongle 槽位 - 恢复和保存
current_slot - 打通直接切槽
交付标准:
Slot 1~3可以切换- 每个槽位 bond 独立
- 重启后当前槽位可恢复
阶段 2:slot_meta 与 UI 打通
- 维护
occupied - 维护
last_peer_addr - 正式持久化
display_name - UI 改为显示真实槽位数据
交付标准:
- 屏幕上显示真实槽位名
display_name可稳定持久化
阶段 3:直接擦除与 dongle 预留对齐
- 实现当前槽位直接
unpair - 擦除后刷新
slot_meta - 擦除后重新进入可配对状态
- 明确
identity 4的模式入口
交付标准:
- 擦除后旧主机不能再连接
- 当前槽位可重新配对
24G路径已有干净预留点
15. 风险与注意事项
15.1 直接擦除的用户体验
本方案放弃回滚能力。
一旦擦除:
- 旧 bond 立即失效
- 用户必须重新配对
这是当前明确接受的产品行为。
15.2 display_name 获取机制
display_name 虽然是正式字段,但自动来源未必现成。
实现阶段必须明确:
- 名称是自动采集
- 还是通过 UI / 上位机写入
这不是字段是否保留的问题,而是字段填充路径的问题。
15.3 BT_ID_MAX=5 可用性
必须在目标板上验证:
- identity 创建正常
- 切换 advertising identity 正常
- settings 恢复正常
15.4 默认 identity 不参与槽位
本方案建议 BT_ID_DEFAULT 不参与槽位管理,避免后续行为不一致。
16. 当前版本结论
按目前所有已确认信息,最终设计结论如下:
- 只做 BLE 三槽,dongle 只预留专用槽位
- 三个 BLE 槽位直接固定到
bt_identity_id 1/2/3 bt_identity_id 4预留给 dongle- 不使用
app_slot_id - 不使用临时 identity
- 不做 erase advertising
- 不做擦除确认和回滚
slot_meta固定包含:occupiedlast_peer_addrdisplay_name
display_name作为正式持久化字段保留,用于 UI 显示- 不新增
inc/ble_bond_multi.h,共享接口放到事件头文件
如果你认可这个版本,下一步就可以按这份方案进入实现阶段。