BLE¶
1. BLE介绍¶
定义:BLE(Bluetooth Low Energy)是一种短距离、低成本、低功耗的无线技术。
支持功能:支持主模式、从模式及多连接,暂不支持 BLE Mesh。
文档范围:主要介绍 BLE 相关接口及使用方法。
2. 使能/关闭BLE功能¶
打开方式:在menuconfig中开启"BLUETOOTH"宏,并按需配置其他功能:
关闭方式:在menuconfig中关闭"BLUETOOTH"宏。

3. HCI功能¶
HCI(Host Controller Interface)是Host与Controller的通信接口,用于控制蓝牙芯片动作(如inquiry,connect,disconnect),主要用于调试分析。
3.1 配置与控制¶
在menuconfig中开启HCI配置:

通过finsh命令开关(需重启设备):
nvds update hci_log 1 // 1:打开HCI;0:关闭HCI
3.2 打印说明¶
52X系列的HCI信息在HCPU打印,其他系列在LCPU打印。
需按如下串口配置才能正常显示HCI数据(HCI回放需对应.bin和.bintime文件):

HCI全显示:HCI数据会完整显示
HCI无数据:仅显示HCI数据的头信息,不显示具体内容
HCI隐藏:不显示HCI数据(抓取HCI数据时建议采用此模式,便于分析)
4. BLE连接¶
BLE连接速度(首次连接或回连)仅由两个参数决定:设备的广播间隔和手机的扫描窗口/间隔比例。
4.1 设备的广播间隔¶
快广播(100-200ms):
连接速度快(以100ms为例,通常3秒内,多数情况1秒内可连接),功耗相对较高。慢广播(800-1000ms):
更省电,但连接速度较慢,且受手机扫描间隔影响显著。
建议的广播切换策略
基础策略:
开机或BLE断开后,先快广播30秒至1分钟,超时后自动切换为慢广播。适用于拉距或异常断开后快速重连。优化策略:
开机、BLE断开、设备亮屏时触发快广播,超时后切换为慢广播。实现较复杂,但能进一步加速连接。
4.2 手机的扫描窗口/间隔比例¶
扫描窗口是手机在一个扫描间隔内的实际扫描时间,该比例越接近1:1,扫描速度越快。
IOS系统:
系统自动扫描,扫描窗口与间隔比例通常为1:1或1:2(如30ms窗口+30ms间隔,或30ms窗口+60ms间隔)。安卓/鸿蒙系统:
分为APK发起和系统发起两种扫描方式:APK主动扫描:建议使用
SCAN_MODE_LOW_LATENCY标志,此时扫描比例通常为1:1或1:2。系统发起扫描:配对并使能HID时,部分手机会像IOS一样主动发起连接。
5. BLE回连¶
BLE回连通常由手机主动发起,设备端仅通过广播等待连接,不同系统的回连机制存在差异。
5.1 IOS系统¶
自动回连特性:与IOS配对并激活ANCS后,IOS会持续尝试回连,即使APK未发起连接,设备端也可能显示已连接。
异常排查:
若APK显示未连接,但设备端显示已连接,可通过手机设置确认连接状态,判断是APK问题还是BLE连接异常。
若设备清除本地LinkKey但手机未删除配对,会导致iPhone频繁连接后断开(因无配对信息)。解决方案:
设备恢复出厂时,提示用户同步删除手机端配对信息;
或设备恢复出厂时保留BLE配对信息(需APK配合处理,避免被非预期iPhone连接)。
5.2 安卓/鸿蒙系统¶
主动回连场景:仅在配对并使能HID时,部分手机会主动发起回连,但可能效率低且覆盖APK连接,导致连接缓慢。
常规回连逻辑:需APK主动发起连接。若APK被杀死,将无法发起连接,导致蓝牙始终无法连上。
6. BLE相关API¶
函数原型 |
功能描述 |
|---|---|
|
获取当前MTU(最大传输单元)大小 |
|
获取BLE状态: |
|
开启BLE广播 |
|
关闭BLE广播 |
|
切换广播模式(可自定义不同模式的广播信息) |
|
使指定连接进入低功耗模式(低速模式) |
|
使指定连接退出低功耗模式(进入快速模式) |
|
开关BLE功能: |
|
与对端设备交互MTU大小(建议在连接成功时调用) |
|
开始搜索周围BLE设备 |
|
停止搜索BLE设备 |
|
主动创建与其他BLE设备的连接(主模式下使用) |
|
更新当前连接的参数(如间隔、超时等) |
|
删除所有已保存的BLE配对绑定信息 |
|
通过连接索引获取对端设备的蓝牙地址 |
|
断开指定的BLE连接 |
函数原型 |
功能描述 |
|---|---|
|
获取当前MTU(最大传输单元)大小 |
|
获取BLE状态: - |
|
开启BLE广播 |
|
关闭BLE广播 |
|
切换广播模式(可自定义不同模式的广播信息) |
|
使指定连接进入低功耗模式(低速模式) |
|
使指定连接退出低功耗模式(进入快速模式) |
|
开关BLE功能: - |
|
与对端设备交互MTU大小(建议在连接成功时调用) |
|
开始搜索周围BLE设备 |
|
停止搜索BLE设备 |
|
主动创建与其他BLE设备的连接(主模式下使用) |
|
更新当前连接的参数(如间隔、超时等) |
|
删除所有已保存的BLE配对绑定信息 |
|
通过连接索引获取对端设备的蓝牙地址 |
|
断开指定的BLE连接 |
7. BLE 相应事件¶
BLE 相关事件统一在 ble_connect.c 文件的 ble_event_handler 函数中处理,主要事件如下:
事件名 |
描述 |
|---|---|
|
连接成功事件 |
|
断开连接事件 |
|
连接参数更新事件 |
|
MTU 交互事件(上报当前 MTU 值) |
|
搜索开始事件(可清除上一次搜索结果) |
|
搜索结果事件 |
8. 增加一个service uuid¶
客户增加自己的UUID时可参考"ble_app_msg.c"中的信息,"ble_app_msg.c"文件是与我们APP对应的UUID,客户如不使用时,可直接使用此文件进行修改。 其中如果客户增加增加了UUID,相应的初始化函数要在“notify_ble_start_adv”函数中有调用。 其中权限设置,对应如下代码所示,用户根据自己的需要设置对应的权限。
PERM(WRITE_REQ, ENABLE) | PERM(WRITE_COMMAND, ENABLE) | PERM(RD, ENABLE) | PERM(NTF, ENABLE) | PERM(IND, ENABLE), PERM(UUID_LEN, UUID_128) | PERM(RI, ENABLE)
/**
* 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
* |EXT | WS | I | N | WR | WC | RD | B | NP | IP | WP | RP |
* +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
*
* Bit [0-1] : Read Permission (0 = NO_AUTH, 1 = UNAUTH, 2 = AUTH, 3 = SEC_CON)
* Bit [2-3] : Write Permission (0 = NO_AUTH, 1 = UNAUTH, 2 = AUTH, 3 = SEC_CON)
* Bit [4-5] : Indication Permission (0 = NO_AUTH, 1 = UNAUTH, 2 = AUTH, 3 = SEC_CON)
* Bit [6-7] : Notification Permission (0 = NO_AUTH, 1 = UNAUTH, 2 = AUTH, 3 = SEC_CON)
*
* Bit [8] : Extended properties present (only relevant for a characteristic value)
* Bit [9] : Broadcast permission (only relevant for a characteristic value)
* Bit [10] : Write Command accepted
* Bit [11] : Write Signed accepted
* Bit [12] : Write Request accepted
* Bit [13] : Encryption key Size must be 16 bytes
*/
enum attm_perm_mask
{
/// retrieve all permission info
PERM_MASK_ALL = 0x0000,
/// Read Permission Mask
PERM_MASK_RP = 0x0003,
PERM_POS_RP = 0,
/// Write Permission Mask
PERM_MASK_WP = 0x000C,
PERM_POS_WP = 2,
/// Indication Access Mask
PERM_MASK_IP = 0x0030,
PERM_POS_IP = 4,
/// Notification Access Mask
PERM_MASK_NP = 0x00C0,
PERM_POS_NP = 6,
/// Broadcast descriptor present
PERM_MASK_BROADCAST = 0x0100,
PERM_POS_BROADCAST = 8,
/// Read Access Mask
PERM_MASK_RD = 0x0200,
PERM_POS_RD = 9,
/// Write Command Enabled attribute Mask
PERM_MASK_WRITE_COMMAND = 0x0400,
PERM_POS_WRITE_COMMAND = 10,
/// Write Request Enabled attribute Mask
PERM_MASK_WRITE_REQ = 0x0800,
PERM_POS_WRITE_REQ = 11,
/// Notification Access Mask
PERM_MASK_NTF = 0x1000,
PERM_POS_NTF = 12,
/// Indication Access Mask
PERM_MASK_IND = 0x2000,
PERM_POS_IND = 13,
/// Write Signed Enabled attribute Mask
PERM_MASK_WRITE_SIGNED = 0x4000,
PERM_POS_WRITE_SIGNED = 14,
/// Extended properties descriptor present
PERM_MASK_EXT = 0x8000,
PERM_POS_EXT = 15,
/// Properties
PERM_MASK_PROP = 0xFF00,
PERM_POS_PROP = 8,
};
初始化时Sercurity level for service如下代码所示,可根据实际需要进行设置
svc.sec_lvl = PERM(SVC_AUTH, SEC_CON) | PERM(SVC_UUID_LEN, UUID_128) | PERM(SVC_MI, ENABLE);
/**
* Service permissions
*
* 7 6 5 4 3 2 1 0
* +----+----+----+----+----+----+----+----+
* |SEC |UUID_LEN |DIS | AUTH |EKS | MI |
* +----+----+----+----+----+----+----+----+
*
* Bit [0] : Task that manage service is multi-instantiated (Connection index is conveyed)
* Bit [1] : Encryption key Size must be 16 bytes
* Bit [2-3]: Service Permission (0 = NO_AUTH, 1 = UNAUTH, 2 = AUTH, 3 = Secure Connect)
* Bit [4] : Disable the service
* Bit [5-6]: UUID Length (0 = 16 bits, 1 = 32 bits, 2 = 128 bits, 3 = RFU)
* Bit [7] : Secondary Service (0 = Primary Service, 1 = Secondary Service)
*/
enum attm_svc_perm_mask
{
/// Task that manage service is multi-instantiated
PERM_MASK_SVC_MI = 0x01,
PERM_POS_SVC_MI = 0,
/// Check Encryption key size for service Access
PERM_MASK_SVC_EKS = 0x02,
PERM_POS_SVC_EKS = 1,
/// Service Permission authentication
PERM_MASK_SVC_AUTH = 0x0C,
PERM_POS_SVC_AUTH = 2,
/// Disable the service
PERM_MASK_SVC_DIS = 0x10,
PERM_POS_SVC_DIS = 4,
/// Service UUID Length
PERM_MASK_SVC_UUID_LEN = 0x60,
PERM_POS_SVC_UUID_LEN = 5,
/// Service type Secondary
PERM_MASK_SVC_SECONDARY = 0x80,
PERM_POS_SVC_SECONDARY = 7,
};
9. BLE数据接收和发送¶
/**
* @brief Send new service attribute to remote using gatt notify.
* @param[in] conn_idx Connection index for the service.
* @param[in] value Attribute content buffer.
*/
int sibles_write_value(uint8_t conn_idx, sibles_value_t *value);
/**
* @brief Register GATT service callback.
* @param[in] hdl Service handle.
* @param[in] gcbk Service attribute get callback.
* @param[in] scbk Service attribute set callback.
*/
void sibles_register_cbk(sibles_hdl hdl, sibles_get_cbk gcbk, sibles_set_cbk scbk);
其中接收函数在初始化时设置的"scbk"函数中,可参考"ble_msg.c"文件中的对应函数。
10. BLE作为master连接其他设备¶
打开ble做主设备功能如下图所示:

可参考此参数"diss scan start 1 500 50 60000 -60"搜索设备

搜索得到的结果事件如下,其值没往UI传,需要自定义添加一个对应的事件。

连接设备,其中地址类型和地址在搜索结果中有。

其他操作参考如下命令

ble做主和多连接可参考“sdk\example\ble\central_and_peripheral\src\main.c"文件
11. BLE修改广播参数¶
如果要修改ble广播参数可在"ble_advertising_start"函数中更新"para"变化中的各个参数即可。
void ble_advertising_start(ble_roadcast_control_t control, ble_roadcast_mode_t mode)
12. BLE修改连接参数¶
我们发布的版本中,ble的速度会在“sibles_acquire_tx_pkts_hook”函数当数据包大于3(if (buffer_num >= BLE_SPEED_ADAPT_TX_COUNT))时更新为快速模式,连续30秒没数据传输时,自动更新为低速模式。OTA及表盘传输时速度也是自动控制的,不用客户控制。如客户想自行控制ble连接参考,可以参考下面的的对应函数实现。
//进入low power 命令,调此函数时ble进入低速模式
void ble_enter_low_power(uint8_t conn_idx);
//退出low power 命令,调此函数时ble进入快速模式
void ble_exit_low_power(uint8_t conn_idx);
/**
* @brief update conneciton parameters. 此函数可任意更新连接参数
* @param[conn_idx] connection index.
* @param[interval_level] interval level to be set, @see connection_manager_update_conneciton.
* @param[data] conneciton parameters vlaue, @see cm_conneciont_parameter_value_t.
* @return set status @see connection_manager_state
*/
uint8_t connection_manager_update_parameter(uint8_t conn_idx, uint8_t interval_level, uint8_t *data);
13. BLE修改MTU¶
在ble连接成功时可调用“sibles_exchange_mtu”函数交互MTU,或是对端设置MTU成功后“SIBLES_MTU_EXCHANGE_IND”事件上报设置的MTU值。
/**
* @brief Exchange MTU size.
* @param[in] conn_idx connection index.
* @retval result.
*/
uint8_t sibles_exchange_mtu(uint8_t conn_idx);
/**
* @brief User implmentation function. Stack will call this function to get max mtu.
* if not implmentation, stack will use default value as 1024.
* @param
* @return Maximum MTU value you want to use,
* should greater than or equal to 23, less than or equal to 1024.
*/
uint16_t ble_max_mtu_get();
14. BLE配对¶
ble在与手机进行配对时支持弹窗,只需打开“BLE_USING_PAIRING_CONFIRMATION”宏即可。

事件名称 |
描述 |
|---|---|
BLE_APP_PAIRING_CONFIRMATION |
弹窗事件 |
BLE_APP_PAIRING_FAILED |
配对失败事件 |
BLE_APP_PAIRING_SUCCEED |
配对成功事件 |
//弹窗时用户点击“确定”或“取消”时需要调用的函数
uint8_t connection_manager_bond_ack_reply(uint8_t conn_idx, uint8_t command, bool accept);
15. BLE对接客户的BLE应用协议到solution¶
我们发布的ble通信协议中使用了nanopb协议进行编码和解码,客户如不需要可在menuconfig中关闭“PKG_USING_NANOPB”宏。
客户对接自己的协议时首先将"ble_msg.c"文件中的UUID修改为客户需要的UUID,然后在ble接收到数据后去调用我们提供的对应函数(如设置时间,设置自动心率等)可参考"protocol_setting.c"文件中的对应函数。
16. ANCS¶
ANCS(Apple Notification Center Service)是IOS系统中的一个通知服务,使用该服务的方式为,设备连接手机后,设备上的GATT客户端发现IOS手机端上的ANCS服务,并且使能其通知源Characteristics的notifys功能,之后IOS系统就会通过ble推送消息给设备。
ANCS消息入口函数为“app_ancs_notification”最终调用“message_send_to_gui_thread”函数进行处理。
如需要增加ANCS的消息类型则在“app_ancs.c”文件中增加对应的类型即可。
static message_item_t message_item[] =
{
{"com.apple.MobileSMS", MESSAGE_TYPE_MESSAGE },
{"com.tencent.mqq", MESSAGE_TYPE_QQ },
{"com.tencent.xin", MESSAGE_TYPE_WECHAT },
{"net.whatsapp.WhatsApp", MESSAGE_TYPE_WHATSAPP },
{"com.facebook.Messenger", MESSAGE_TYPE_MESSENGER },
{"com.atebits.Tweetie2", MESSAGE_TYPE_TWITTER },
{"com.linkedin.Zephyr", MESSAGE_TYPE_LINKEDIN },
{"com.burbn.instagram", MESSAGE_TYPE_INSTAGRAM },
{"com.facebook.Facebook", MESSAGE_TYPE_FACEBOOK },
{"com.apple.mobilephone", MESSAGE_TYPE_INCOMINGCALL },
};
17. AMS¶
AMS全称Apple Media Service(苹果媒体服务),是提供给BLE设备的一种简单控制媒体应用程序的方式,并且用于获取已连接的 IOS 设备的媒体状态信息。 除了标准的通用属性配置文件(GATT)子程序集外,AMS没有任何依赖性。作为GATT客户端的设备在使用AMS时,可以自己访问和使用IOS设备提供的其他服务。
17.1 术语¶
Apple Media Service 称为 AMS AMS 服务的发布者(也就是我们的 ios 设备)应被称为媒体源(MS)。 AMS 服务的客户端(也就是我们的蓝牙设备)应被称为媒体控制器(MR)。 下面统一使用 媒体源 和 媒体控制 来描 IOS 设备和蓝牙设备。
17.2 AMS Service UUID¶
AMS 主服务(Service) UUID 为 89D3502B-0F36-433A-8EF4-C502AD55F8DC
AMS 服务下的三个特征(Characteristic) UUID:
远程命令(Remote Command)(writeable,notifiable)
9B3C81D8-57B1-4A8A-B8DF-0E56F7CA51C2
实体更新(Entity Update)(writeable with response,notifiable)
2F7CABCE-808D-411F-9A0C-BB92BA96C102
实体属性(Entity Attribute)(readable, writeable)
C6B2F38C-23AB-46D8-A6AB-A3A870BBD5D7
下图展示了媒体源和媒体控制器间的服务设置:

1.媒体控制器发现媒体源的 AMS 服务和特征
2.媒体控制器订阅媒体源实体更新
3.媒体控制器向媒体源写入需要监听的实体(EntityID),例如监听歌曲变化等
4.当媒体源监听的实体(EntityID)有变动时,会通知其变动内容
17.3AMS Characteristic¶
实体
AMS 定义了 3 个不同的实体,每个实体都有一组不同的属性:
播放器 (Player): 当前活跃的媒体应用。该实体属性包括其名称、播放状态和播放音量等
队列 (Queue): 当前加载的播放队列。该实体的属性包括其队列数及其随机和重复模式等
音轨 (Track): 当前加载的音轨。该实体的属性包括其艺术家、标题和总的播放时间等。

播放器 (Player)属性表

// 获取播放器属性函数
app_ams_player_t *app_ams_get_player(void)
{
return app_ams_player;
}
通过播放器属性,我们可以获取 PlaverAttributelDName 当前音乐播放器的字符串,例如 IOS 手机使用的网易云音乐,那么媒体源推送的字符串转换为 unicode 编码后再转成中文,实际上就是“网易云音乐”,同理其他的音乐 APP,也会显示其相应的名称,非音乐APP 是不会有推送的。
PlayerAttributelDPlaybacklnfo 可以获取对应的播放状态、播放速率以及歌曲经过的时间,例如接收到推送为 49 44 49 46 48 44 55 49 46 49 52 55 表示 1,1.0,71.147,当前状态为播放、速率 1.0、已过时间 71.147 秒
PlaverAtributelDvolume 用于获取当前的音量浮点值,音量最大时为 1,最小时为 0,IOS 按一次音量按键,每次递进 0.0625。
注: 通过上面的信息,我们可以计算出当前经过的时间,公式如下:
CurrentElapsedTime = ElapsedTime + ((TimeNow-TimePlaybacklnfoWasReceived)* PlaybackRate)
上方展示的音乐进度条,也是依靠该方式和歌曲总长度计算的
队列 (Queue) 属性表

// 获取队列属性函数
app_ams_queue_t *app_ams_get_queue(void)
{
return app_ams_queue;
}
这一块为切换播放器播放模式时,会通知对应的模式变化。
音轨 (Track)属性表

// 获取音轨属性函数
app_ams_track_t *app_ams_get_track(void)
{
return app_ams_track;
}
这一块包含了歌曲名、歌手名、专辑名称以及当前音乐总时长。假设音乐时长03:08,那么媒体控制器接收到的则是188.000秒。

若媒体控制器订阅了Track属性,那么当媒体源播放音乐时,会将A音乐的曲名和歌手名推送给媒体控制器,每次有变动时,都会通知媒体控制器。
代码中对应事件为“BLE_APP_AMS_NOTI_IND”
/**
* @brief EntityID values.
*/
typedef enum
{
BLE_AMS_ENTITY_ID_PLAYER, /**< Player entity. The attribute is @ref ble_ams_entity_player_attribute_t. */
BLE_AMS_ENTITY_ID_QUEUE, /**< Queue entity. The attribute is @ref ble_ams_entity_queue_attribute_t. */
BLE_AMS_ENTITY_ID_TRACK, /**< Track entity. The attribute is @ref ble_ams_entity_queue_attribute_t. */
} ble_ams_entity_id_t;
/**
* @brief The structure of #BLE_AMS_ENTITY_ATTRIBUTE_PAIR_IND.
*/
typedef struct
{
uint8_t entity_id; /**< @see enum ble_ams_entity_id_t */
uint8_t attr_id; /**< Associated attribute ID for an dedicated entityID. */
uint8_t entity_up_flag; /**< Entity update flag. 1 is truncated. */
uint16_t len; /**< length of attribute value. */
uint8_t value[0]; /**< Attribute value. */
} ble_ams_entity_attr_value_t;
ble_ams_entity_attr_value_t *value = (ble_ams_entity_attr_value_t *)data;
ipc_send_msg_from_ble_to_app(BLE_APP_AMS_NOTI_IND, sizeof(ble_ams_entity_attr_value_t) + value->len, (uint8_t *)value);
//当收到“BLE_APP_AMS_NOTI_IND”事件时可依据 value->entity_id 值确定当前是什么属性发生了变化。
17.4 远程命令(Remote Command)¶
远程命令这个特征值是用于 媒体控制器 发送给 媒体源 的播放状态特征,例如播放/暂停、音量加/减、上/下首、循环播放等。
媒体控制器 发送命令格式如下:


媒体控制器 发现 媒体源 AMS 服务后,可以通过发送控制命令,播放/暂停、音量加减等操作媒体源播放器。
此特性还用于向 媒体控制器 报告当前 媒体源 支持的命令集,当媒体播放器支持的命令列表发生变化时,媒体源 会使用如下所示的格式,在该特征生产一条通知,通知中包括了支持的命令集:


通过该命令集,我们的蓝牙设备就可以对 IOS 设备的媒体播放器进行控制,该方式的音乐控制,相比较 HID,我觉得更优,因为 HID 控制只能在媒体播放器界面进行控制,模拟按键的方式去控制播放,并且 HID 的方式不能获取歌曲名、歌曲进度等,所以相较下,如果用于控制IOS 媒体播放器,那么便用 AMS 是最优的方式。
对应代码中命令及函数如下:
/**
* @brief Remote commandID values.
*/
typedef enum
{
BLE_AMS_CMD_PLAY, /**< Play command. */
BLE_AMS_CMD_PAUSE, /**< Pause command. */
BLE_AMS_CMD_TOGGLE_PLAY_PAUSE, /**< Toggle command. */
BLE_AMS_CMD_NEXT, /**< Next command. */
BLE_AMS_CMD_PREV, /**< Previous command. */
BLE_AMS_CMD_VOL_UP, /**< Voloume up command. */
BLE_AMS_CMD_VOL_DOWN, /**< Volume down command. */
BLE_AMS_CMD_REPEAT_MODE, /**< Repeat mode command. */
BLE_AMS_CMD_SHUFFLE_MODE, /**< Shuffle mode command. */
BLE_AMS_CMD_SKIP_FWD, /**< Skip forward command. */
BLE_AMS_CMD_SKIP_BACKWD, /**< Skip backward command. */
BLE_AMS_CMD_LIKE_TRACK, /**< Like track command. */
BLE_AMS_CMD_DISLIKE_TRACK, /**< Dislike track command. */
BLE_AMS_CMD_BOOKMARK_TRACK, /**< Bookmark track command. */
BLE_AMS_CMD_TOTAL /**< Total commands number. */
} ble_ams_cmd_t;
void app_ams_remote_cmd(ble_ams_cmd_t cmd);