蓝牙(BLE/BT)¶
FAQ1 设备连接手机后,手机界面出现鼠标箭头,如何修改¶
可以按如图修改
FAQ2 设备蓝牙名如何修改?¶
默认solution会先读取nvm信息设置蓝牙名,读取失败会用mac地址拼接一个蓝牙名,如果更改mac地址后,蓝牙名字未更改,需要先检查nvm信息是否有更新,特别地, 如果要用烧录工具烧写的BT NAME,需要先用接口rt_flash_config_read(FACTORY_CFG_ID_BTNAME, (uint8_t *)local_name, sizeof(local_name));读出数据再去设置 蓝牙名
FAQ3 hci log无输出¶
先确保代码有打开’HCI_ON_LOG’ ‘LOG_ON_CONSOLE’ 这两个宏,再按文档排查hci常见faq
FAQ4 速查:BLE 传文件速度慢¶
1. 先按收益做 3 件事¶
优先级 |
优化项 |
目标值 |
收益 |
|---|---|---|---|
1 |
MTU 协商 |
517 / 185 |
+500~900% |
2 |
连接间隔切高性能档 |
15ms |
+200~500% |
3 |
PHY 切 2M |
2M PHY |
+80~100% |
3 项做完:速率通常从 <20 KB/s → 60~100 KB/s。
2. 影响因素排序¶
层级 |
因素 |
影响 |
|---|---|---|
ATT |
MTU 未协商(默认23B) |
差 9~25 倍 |
LL |
连接间隔太大(100ms vs 15ms) |
差 6 倍 |
PHY |
没切 2M(还在 1M) |
差 2 倍 |
应用 |
逻辑复杂 / 打印多 / 校验复杂 |
效率低 |
系统 |
外部存储速率 |
读写速度 |
射频 |
距离远 / WiFi 干扰 / RSSI 低 |
重传降速 |
对端 |
iOS 保守 / 老手机只支持 1M / BT、BLE 互相影响 |
视机型 |
并发 |
多连接 / 音频显示抢 CPU |
视场景 |
3. 连接参数:默认代码 + 手动控制¶
默认代码逻辑(自动档):
sibles_acquire_tx_pkts_hook
└─ if (buffer_num >= BLE_SPEED_ADAPT_TX_COUNT) // 数据包 >3
└─ 更新为快速模式
└─ 连续 30 秒无数据传输
└─ 自动更新为低速模式
OTA、表盘传输时速度自动控制,客户不用管。
默认代码里连接参数不是一连接就固定,而是按数据量动态切。
手动控制函数:
// 进低速
void ble_enter_low_power(uint8_t conn_idx);
// 进快速
void ble_exit_low_power(uint8_t conn_idx);
// 任意更新连接参数
uint8_t connection_manager_update_parameter(
uint8_t conn_idx,
uint8_t interval_level,
uint8_t *data
);
4. MTU:默认代码 + 手动协商¶
默认代码逻辑:
默认最大 MTU 由
ble_max_mtu_get()决定。如果用户没实现这个函数,协议栈用默认值 1024。
取值范围:
>= 23且<= 1024。连接成功后,默认代码不会自动把 MTU 提到最大,需要主动调用交换,或等对端发起。
手动协商函数:
// 连接成功后主动发起 MTU 交换
uint8_t sibles_exchange_mtu(uint8_t conn_idx);
// 协议栈回调,获取本端想用的最大 MTU
uint16_t ble_max_mtu_get();
对端设置成功后的上报事件:
SIBLES_MTU_EXCHANGE_IND
默认路径小结:
BLE 连接成功
└─ 可调用 sibles_exchange_mtu(conn_idx) 主动协商
└─ 或等对端发起,成功后上报 SIBLES_MTU_EXCHANGE_IND
└─ 本端最大 MTU 由 ble_max_mtu_get() 决定
└─ 未实现 → 默认 1024
5. PHY¶
默认代码连接成功后启动定时器切 2M:
ble_connect.c -> ble_event_handler -> case BLE_GAP_CONNECTED_IND:
-> ble_update_phy_timeout_start
-> ble_update_phy_timeout
-> ble_gap_update_phy
FAQ5 有些 BT 的 Profile 服务不需要,怎么关闭¶
背景问题
默认 solution 代码中,业务逻辑与 UI 绑定太深,对应的宏控制不全。
直接关闭宏编译会出现一堆报错,无法通过简单的宏开关来裁剪 Profile。
建议方案
不要动宏开关,直接控制对应 Profile 的 init 函数,将其注释掉。
位置:bts2_app_demo.c → bt_init_profile()
示例代码
void bt_init_profile(bts2_app_stru *bts2_app_data)
{
/*HFP HF */
#ifdef BT_USING_HF
//bt_hfp_hf_init(bts2_app_data);
//bt_hfp_hf_start_enb(bts2_app_data);
#endif
/*for sdk config do not change BT_USING_AG*/
#ifdef BT_USING_AG
//bt_hfp_ag_app_init(bts2_app_data);
//bt_hfp_start_profile_service(bts2_app_data);
#endif
#ifdef BT_FINSH_PAN
bt_pan_init(bts2_app_data);
bt_pan_reg(bts2_app_data);
#endif
/*A2DP SNK */
#ifdef BT_USING_A2DP
//bt_av_init(bts2_app_data);
#endif
#ifdef BT_USING_AVRCP
//bt_avrcp_init(bts2_app_data);
#endif
#ifdef BT_USING_HID
//bt_hid_init(bts2_app_data);
#endif
#ifdef BT_USING_PBAP
//bt_pbap_clt_init(bts2_app_data);
#endif
#ifdef BT_USING_SPP
bt_spp_init(bts2_app_data);
bt_spp_srv_start_enb(bts2_app_data);
#endif
}
操作要点
步骤 |
说明 |
|---|---|
1 |
找到 |
2 |
定位到不需要的 Profile 对应的 |
3 |
直接注释掉该行(不要删宏,避免其他文件引用报错) |
4 |
保留宏定义本身,仅注释函数调用 |
注意事项
对代码大小有严格要求的情况:此方法无效,因为宏仍然开启,相关代码仍会被编译进去,只是不执行。此时只能逐个解决编译报错,从宏层面彻底裁剪。
注释 init 后,该 Profile 的服务不会启动,但相关代码仍占用空间。
若注释后出现链接报错(如其他模块引用了该 Profile 的符号),需一并处理引用点。