BLE

本文说明 BLE 通信功能的配置、服务入口和应用接入方式。

1. 参考代码

文件

作用

solution\components\ble\ble_connect\ble_connect.c

BLE 连接管理:连接建立、断开和状态维护

solution\components\ble\ble_connect\ble_msg.c

BLE 消息处理:BLE 事件分发和业务消息转换

solution\components\ble\ble_service\ble_service.c

BLE 服务入口:服务初始化、注册和对外接口

solution\components\ble\ble_protocol\ble_parse.c

协议解析:BLE 私有协议解析和命令处理

solution\components\ble\ble_protocol\ble_serial.c

串行通道:BLE 串行数据封包和发送

2. 应用例程

场景

文件

调用接口

BLE 设置入口

setting_ble_main_gui.c

BLE 设置页事件回调、连接管理接口

BLE 扫描页面

setting_ble_scan_gui.c

BLE 扫描 / 连接事件回调

BLE 配对弹窗

popup_ble_pair.c

BLE 配对确认回调

3. BLE介绍

  • 定义:BLE(Bluetooth Low Energy)是一种短距离、低成本、低功耗的无线技术。

  • 支持功能:支持主模式、从模式及多连接,暂不支持 BLE Mesh。

  • 文档范围:主要介绍 BLE 相关接口及使用方法。

4. 使能/关闭BLE功能

BLE 基础功能按下表配置。客户只需要先打开蓝牙总开关,再按角色选择是否启用中心角色和连接管理。

使用场景

需要启用

说明

蓝牙基础能力

BLUETOOTH

蓝牙总开关;关闭后 BLE 和经典 BT 都不可用。

BLE 基础服务

BSP_BLE_SIBLES

提供 BLE 从设备、广播、连接和 GATT 服务能力。

BLE 中心角色 / GATT Client

BLE_GAP_CENTRAL / BLE_GATT_CLIENT

需要主动扫描、连接其他 BLE 设备时启用。

统一管理 BLE 连接

BSP_BLE_CONNECTION_MANAGER

需要统一管理连接状态、参数和多连接时启用。

HCPU 与 PC Simulator 工程也提供同名 BLE 选项入口。

BLE功能配置

5. HCI功能

HCI(Host Controller Interface)是Host与Controller的通信接口,用于控制蓝牙芯片动作(如inquiry,connect,disconnect),主要用于调试分析。

5.1 配置与控制

  1. HCI 日志在 Bluetooth Hardware Config 中配置;按调试目的选择写入 flash、输出到 ulog 或关闭 HCI 日志。量产版本建议关闭或降低日志输出,避免影响蓝牙性能。

  2. 通过 FinSH 命令开关(需重启设备):

    nvds update hci_log 1  // 1:打开HCI;0:关闭HCI
    

5.2 打印说明

  • 52X系列的HCI信息在HCPU打印,其他系列在LCPU打印。

  • 需按如下串口配置才能正常显示HCI数据(HCI回放需对应.bin和.bintime文件): HCI串口配置

    1. HCI全显示:HCI数据会完整显示

    2. HCI无数据:仅显示HCI数据的头信息,不显示具体内容

    3. HCI隐藏:不显示HCI数据(抓取HCI数据时建议采用此模式,便于分析)

6. BLE连接

BLE连接速度(首次连接或回连)仅由两个参数决定:设备的广播间隔和手机的扫描窗口/间隔比例。

6.1 设备的广播间隔

  • 快广播(100-200ms): 连接速度快(以100ms为例,通常3秒内,多数情况1秒内可连接),功耗相对较高。

  • 慢广播(800-1000ms): 更省电,但连接速度较慢,且受手机扫描间隔影响显著。

建议的广播切换策略

  1. 基础策略: 开机或BLE断开后,先快广播30秒至1分钟,超时后自动切换为慢广播。适用于拉距或异常断开后快速重连。

  2. 优化策略: 开机、BLE断开、设备亮屏时触发快广播,超时后切换为慢广播。实现较复杂,但能进一步加速连接。

6.2 手机的扫描窗口/间隔比例

扫描窗口是手机在一个扫描间隔内的实际扫描时间,该比例越接近1:1,扫描速度越快。

  • IOS系统: 系统自动扫描,扫描窗口与间隔比例通常为1:1或1:2(如30ms窗口+30ms间隔,或30ms窗口+60ms间隔)。

  • 安卓/鸿蒙系统: 分为APK发起和系统发起两种扫描方式:

    1. APK主动扫描:建议使用SCAN_MODE_LOW_LATENCY标志,此时扫描比例通常为1:1或1:2。

    2. 系统发起扫描:配对并使能HID时,部分手机会像IOS一样主动发起连接。

7. BLE回连

BLE回连通常由手机主动发起,设备端仅通过广播等待连接,不同系统的回连机制存在差异。

7.1 IOS系统

  • 自动回连特性:与IOS配对并激活ANCS后,IOS会持续尝试回连,即使APK未发起连接,设备端也可能显示已连接。

  • 异常排查:

    1. 若APK显示未连接,但设备端显示已连接,可通过手机设置确认连接状态,判断是APK问题还是BLE连接异常。

  1. 若设备清除本地LinkKey但手机未删除配对,会导致iPhone频繁连接后断开(因无配对信息)。解决方案:

    • 设备恢复出厂时,提示用户同步删除手机端配对信息;

    • 或设备恢复出厂时保留BLE配对信息(需APK配合处理,避免被非预期iPhone连接)。

7.2 安卓/鸿蒙系统

  • 主动回连场景:仅在配对并使能HID时,部分手机会主动发起回连,但可能效率低且覆盖APK连接,导致连接缓慢。

  • 常规回连逻辑:需APK主动发起连接。若APK被杀死,将无法发起连接,导致蓝牙始终无法连上。

8. BLE相关API

函数原型

功能描述

uint16_t get_mtu_size()

获取当前MTU(最大传输单元)大小

bool ble_get_ble_status(uint8_t type)

获取BLE状态: - CONNECT_STATUS:连接状态 - ADV_STATUS:广播状态

void ble_start_advertising(void)

开启BLE广播

void ble_stop_advertising(void)

关闭BLE广播

void ble_update_advertising(ble_roadcast_mode_t mode)

切换广播模式(可自定义不同模式的广播信息)

void ble_enter_low_power(uint8_t conn_idx)

使指定连接进入低功耗模式(低速模式)

void ble_exit_low_power(uint8_t conn_idx)

使指定连接退出低功耗模式(进入快速模式)

void ble_switch_onoff(uint8_t switch_on)

开关BLE功能: - 1:开启 - 0:关闭

uint8_t sibles_exchange_mtu(uint8_t conn_idx)

与对端设备交互MTU大小(建议在连接成功时调用)

uint8_t ble_gap_scan_start(ble_gap_scan_start_t *scan_param)

开始搜索周围BLE设备

uint8_t ble_gap_scan_stop(void)

停止搜索BLE设备

uint8_t ble_gap_create_connection(ble_gap_connection_create_param_t *conn_param)

主动创建与其他BLE设备的连接(主模式下使用)

uint8_t ble_gap_update_conn_param(ble_gap_update_conn_param_t *conn_para)

更新当前连接的参数(如间隔、超时等)

uint8_t connection_manager_delete_all_bond(void)

删除所有已保存的BLE配对绑定信息

uint8_t connection_manager_get_addr_by_conn_idx(uint8_t conn_idx, bd_addr_t *data)

通过连接索引获取对端设备的蓝牙地址

uint8_t ble_gap_disconnect(ble_gap_disconnect_t *conn)

断开指定的BLE连接

9. BLE 相应事件

BLE 相关事件统一在 ble_connect.c 文件的 ble_event_handler 函数中处理,主要事件如下:

事件名

描述

BLE_GAP_CONNECTED_IND

连接成功事件

BLE_GAP_DISCONNECTED_IND

断开连接事件

BLE_GAP_UPDATE_CONN_PARAM_IND

连接参数更新事件

SIBLES_MTU_EXCHANGE_IND

MTU 交互事件(上报当前 MTU 值)

BLE_GAP_SCAN_START_CNF

搜索开始事件(可清除上一次搜索结果)

BLE_GAP_EXT_ADV_REPORT_IND

搜索结果事件

10. 增加一个service uuid

客户增加自己的UUID时可参考"ble_msg.c"中的信息,"ble_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,
};

11. 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"文件中的对应函数。

12. BLE作为master连接其他设备

BLE master 功能用于主动扫描并连接其他 BLE 设备。使用前启用 USING_BLE_MASTER_DEVICE,并确认 BLE 基础服务已打开;如需连接管理,同时打开 BLE_GAP_CENTRAL、BLE_GATT_CLIENT 和 BSP_BLE_CONNECTION_MANAGER。如下图所示: 可参考此参数"diss scan start 1 500 50 60000 -60"搜索设备 搜索得到的结果事件为“BLE_GAP_EXT_ADV_REPORT_IND”,如下图所示: 连接设备,其中地址类型和地址在搜索结果中有。 其他操作参考如下命令

13. BLE修改广播参数

BLE 广播的核心修改点在 ble_connect.c 的 ble_advertising_start() 函数。这里会先组装 sibles_advertising_para_t para,再调用 sibles_advertising_init() 或 sibles_advertising_update_adv_and_scan_rsp_data() 下发到协议栈。

如果要实现“设备名、广播内容、扫描响应内容、广播间隔、广播模式”等定制需求,优先修改下面几个位置:

  1. para.config:控制广播方式、间隔、时长、是否自动重启。

  2. para.adv_data:真正放进广播包的数据。

  3. para.rsp_data:放进扫描响应包的数据。

sibles_adv_data_t 里常用字段说明如下:

  1. completed_name:完整设备名,通常设备名较长时放这里。

  2. manufacturer_data:厂商自定义数据,适合放设备标识、业务协议字段。

  3. completed_uuid / srv_data:服务 UUID 或服务数据,适合给 App 快速识别设备类型。

  4. appearance:设备外观标识。

  5. customized_data:以上字段都不满足时可自定义广播段。

  6. disc_mode + flags:控制广播可发现性,只有自定义 disc_mode 时 flags 才需要手动设置。

13.1 sibles_adv_data_t 每个成员说明

sibles_adv_data_t 表示“一个广播数据集合”。结构体中哪个指针成员不为 NULL,协议栈就会把对应 AD Type 组装进广播包或扫描响应包。

typedef struct
{
    uint8_t disc_mode;
    uint8_t flags;
    uint8_t *tx_pwr_level;
    uint16_t *appearance;
    uint16_t *advertising_interval;
    sibles_adv_type_name_t *shortened_name;
    sibles_adv_type_name_t *completed_name;
    sibles_adv_type_srv_uuid_t *completed_uuid;
    sibles_adv_type_srv_uuid_t *incompleted_uuid;
    sibles_adv_type_srv_data_t *srv_data;
    sibles_adv_type_conn_interval_t *preferred_conn_interval;
    sibles_adv_type_manufacturer_data_t *manufacturer_data;
    sibles_adv_type_customize_t *customized_data;
} sibles_adv_data_t;

各成员用途如下:

  1. disc_mode 广播发现模式,用来决定 Flags 的生成方式。常见用途是设置普通可发现、有限可发现或自定义模式。

  2. flags 广播 Flags 字段,仅当 disc_mode 设为 GAPM_ADV_MODE_CUSTOMIZE 时需要手动填写。
    常见值如 0x1E,表示设备支持 BLE、不可同时 BR/EDR 等标志位组合。

  3. tx_pwr_level 发射功率字段,对端可据此粗略判断发射功率。多数场景不是必须字段,只有 App 或协议明确要求时再带。

  4. appearance 外观类型,例如键盘、手表、心率带等。
    主要用于标准 BLE 设备让手机或系统快速识别类型。ble_connect.c 中 HID 场景就填了 BT_APPEARANCE_HID_KEYBOARD。

  5. advertising_interval 把广播间隔作为一个 AD 字段带出去,供对端读取。
    注意这不等于实际广播参数本身,真正的广播间隔仍然要改 para.config.mode_config.xxx.interval。

  6. shortened_name 缩短设备名。
    当名字太长、放不下完整名称时,可以在广播包里放短名,把完整名字放到扫描响应里。

  7. completed_name 完整设备名。
    这是最常用字段之一,手机扫描列表里看到的名字通常来自这里。当前 ble_connect.c 就在 para.adv_data.completed_name 中填了本地名称。

  8. completed_uuid 完整服务 UUID 列表。
    适合告诉手机“这个设备支持哪些标准服务”。例如 HID 设备可把 HID Service UUID 带出去,方便系统或 App 快速过滤。

  9. incompleted_uuid 不完整服务 UUID 列表。
    表示“这里只列出了一部分服务 UUID,还有别的服务没有放进来”。当服务较多、广播空间不够时可用它。

  10. srv_data 服务数据。格式是“服务 UUID + 业务数据”。
    适合做轻量业务广播,例如支付宝、微信、FindMy、配网、绑定发现等。当前 BLE_ALIPAY_MODE 和 BLE_WECHAT_MODE 就是把 MAC 地址放在 srv_data 里。

  11. preferred_conn_interval 建议连接参数范围。
    设备可通过这个字段告诉对端希望的连接间隔范围,但对端是否采用仍由连接协商决定。

  12. manufacturer_data 厂商自定义数据。格式是“Company ID + 自定义负载”。
    这是最常用的业务扩展字段之一,适合放产品类型、协议版本、MAC、绑定码、序列号摘要等。当前 BLE_NORMAL_MODE 就把特定格式的数据放进这个字段,供 App 识别。

  13. customized_data 自定义原始 AD 结构。
    当标准字段不够用,或客户协议要求发一个特殊 AD Type 时使用。这里填写的是完整的广播段内容,要求开发者自己保证格式符合 Bluetooth Core Spec。

13.2 广播包和扫描响应包怎么分配

  1. adv_data 主广播包,适合放“手机一眼就要看到”的信息,例如 Flags、短名、服务 UUID、厂商标识。

  2. rsp_data 扫描响应包,只有对端主动发起 scan request 才会返回。适合放较长的设备名、补充说明字段、较长的业务数据。

  3. 普通广播包容量有限 如果内容太多,优先把核心识别字段放 adv_data,把长名字或补充字段放 rsp_data。再不够就考虑扩展广播。

13.3 常见修改场景

  1. 只想改设备名 改 completed_name 或 shortened_name。

  2. 想让自家 App 快速识别设备 优先使用 manufacturer_data 或 srv_data。

  3. 想声明标准服务 使用 completed_uuid。

  4. 想让手机系统识别设备类别 使用 appearance。

  5. 想发标准字段之外的协议 使用 customized_data。

13.4 示例1:广播中带设备名和厂商数据

下面示例适合大部分客户项目:手机扫描时能看到设备名,App 还能通过厂商数据识别设备。

static void ble_adv_example(void)
{
    sibles_advertising_para_t para = {0};
    char local_name[] = "SIFLI_DEMO";
    uint16_t company_id = 0x0A4C;
    uint8_t manu_data[] = {0x13, 0x07, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};

    para.own_addr_type = GAPM_STATIC_ADDR;
    para.config.adv_mode = SIBLES_ADV_CONNECT_MODE;
    para.config.mode_config.conn_config.duration = 0x0;
    para.config.mode_config.conn_config.interval = 160;   // 160 * 625us = 100ms
    para.config.is_auto_restart = 1;

    para.adv_data.completed_name = rt_malloc(sizeof(sibles_adv_type_name_t) + rt_strlen(local_name));
    para.adv_data.completed_name->name_len = rt_strlen(local_name);
    rt_memcpy(para.adv_data.completed_name->name, local_name, para.adv_data.completed_name->name_len);

    para.adv_data.manufacturer_data = rt_malloc(sizeof(sibles_adv_type_manufacturer_data_t) + sizeof(manu_data));
    para.adv_data.manufacturer_data->company_id = company_id;
    para.adv_data.manufacturer_data->data_len = sizeof(manu_data);
    rt_memcpy(para.adv_data.manufacturer_data->additional_data, manu_data, sizeof(manu_data));

    para.adv_data.disc_mode = GAPM_ADV_MODE_CUSTOMIZE;
    para.adv_data.flags = 0x1E;

    sibles_advertising_init(ble_advertising_context, &para);
    sibles_advertising_start(ble_advertising_context);

    rt_free(para.adv_data.completed_name);
    rt_free(para.adv_data.manufacturer_data);
}

说明:

  1. 设备名通过 completed_name 广播出去。

  2. 自定义识别内容通过 manufacturer_data 带出去。

  3. interval = 160 表示 100ms 广播间隔,因为单位是 625us。

13.5 示例2:把长设备名放到扫描响应里

如果广播内容太多,建议把业务识别字段放在 adv_data,把设备名放在 rsp_data。

static void ble_adv_rsp_example(void)
{
    sibles_advertising_para_t para = {0};
    char local_name[] = "SIFLI_WATCH_LONG_NAME";
    uint16_t company_id = 0x0A4C;
    uint8_t manu_data[] = {0x01, 0x02, 0x03, 0x04};

    para.own_addr_type = GAPM_STATIC_ADDR;
    para.config.adv_mode = SIBLES_ADV_CONNECT_MODE;
    para.config.mode_config.conn_config.interval = 320;   // 200ms

    para.adv_data.manufacturer_data = rt_malloc(sizeof(sibles_adv_type_manufacturer_data_t) + sizeof(manu_data));
    para.adv_data.manufacturer_data->company_id = company_id;
    para.adv_data.manufacturer_data->data_len = sizeof(manu_data);
    rt_memcpy(para.adv_data.manufacturer_data->additional_data, manu_data, sizeof(manu_data));

    para.rsp_data.completed_name = rt_malloc(sizeof(sibles_adv_type_name_t) + rt_strlen(local_name));
    para.rsp_data.completed_name->name_len = rt_strlen(local_name);
    rt_memcpy(para.rsp_data.completed_name->name, local_name, para.rsp_data.completed_name->name_len);

    sibles_advertising_init(ble_advertising_context, &para);
    sibles_advertising_start(ble_advertising_context);

    rt_free(para.adv_data.manufacturer_data);
    rt_free(para.rsp_data.completed_name);
}

说明:

  1. adv_data 放短而关键的数据,保证手机容易扫到并识别。

  2. rsp_data 放长设备名,避免主广播包超长。

  3. 如果广播已启动,可改用 ble_update_advertising_data(&para.adv_data, &para.rsp_data) 动态更新。

ble_connect.c 中的典型写法如下:

para.own_addr_type = GAPM_STATIC_ADDR;
para.config.adv_mode = SIBLES_ADV_CONNECT_MODE;
para.config.mode_config.conn_config.duration = 0x0;
para.config.mode_config.conn_config.interval = 0x640;
para.config.is_auto_restart = 1;
para.adv_data.completed_name = ...
para.adv_data.manufacturer_data = ...

修改建议:

  1. 想改广播间隔,改 para.config.mode_config.conn_config.interval。

  2. 想改成不可连接广播,改 para.config.adv_mode 为 SIBLES_ADV_BROADCAST_MODE。

  3. 想修改手机扫描到的名字,改 para.adv_data.completed_name。

  4. 想让 App 识别设备类型,改 manufacturer_data 或 srv_data。

  5. 想同步修改扫描响应包,可同时填充 para.rsp_data,或者打开 is_rsp_data_duplicate 让扫描响应与广播数据一致。

如果只是替换广播内容,通常直接改 ble_advertising_start();如果是运行中动态更新广播内容,则调用 ble_update_advertising_data()。

14. 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);

15. 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();

16. BLE配对

BLE 与手机配对时如需弹窗确认,启用 BLE_USING_PAIRING_CONFIRMATION,并确认 BLE 基础服务已打开。Zephyr 蓝牙 (ZBT) 配置下不使用该 Solution BLE 配对弹窗。

事件名称

描述

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);

17. BLE对接客户的BLE应用协议到Solution

我们发布的 BLE 通信协议使用 Nanopb 编码和解码。客户不使用 Nanopb 时可关闭 PKG_USING_NANOPB。 客户对接自己的协议时首先将"ble_msg.c"文件中的UUID修改为客户需要的UUID,然后在ble接收到数据后去调用我们提供的对应函数(如设置时间,设置自动心率等)可参考"protocol_setting.c"文件中的对应函数。

18. 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   },
};

19. AMS

AMS全称Apple Media Service(苹果媒体服务),是提供给BLE设备的一种简单控制媒体应用程序的方式,并且用于获取已连接的 IOS 设备的媒体状态信息。 除了标准的通用属性配置文件(GATT)子程序集外,AMS没有任何依赖性。作为GATT客户端的设备在使用AMS时,可以自己访问和使用IOS设备提供的其他服务。

19.1 术语

Apple Media Service 称为 AMS AMS 服务的发布者(也就是我们的 ios 设备)应被称为媒体源(MS)。 AMS 服务的客户端(也就是我们的蓝牙设备)应被称为媒体控制器(MR)。 下面统一使用 媒体源 和 媒体控制 来描 IOS 设备和蓝牙设备。

19.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)有变动时,会通知其变动内容

19.3 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 值确定当前是什么属性发生了变化。

19.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);

20. LE Audio

LE Audio 仅使用 Zephyr 蓝牙栈。使用前先启用 ZBT,再按应用角色选择 BLE_AURACAST_SRC 或 BLE_AURACAST_SINK。

使用场景

需要启用

说明

LE Audio 基础能力

ZBT

启用 Zephyr 蓝牙栈。

Auracast Source

BLE_AURACAST_SRC

用于广播音频源应用。

Auracast Sink

BLE_AURACAST_SINK

用于扫描并接收广播音频。

启用 ZBT 后,传统 Solution BLE 服务中的中心角色、GATT Client 和 BLE 连接管理选项不可用。

56X要使用le audio功能时需要做以下修改。

  • lcpu_patch的大小由16K增加到64K及FLASH_BOOT_LOADER_START_ADDR的地址要增加48K(64K-16K)。

  • LE Audio 中使用工作队列。启用 RT_USING_SYSTEM_WORKQUEUE,并将 RT_SYSTEM_WORKQUEUE_PRIORITY 设为 12,以满足该应用的调度要求。

  • 由于 LE Audio 蓝牙交互数据量大,建议在 HCPU 启用 HCI_DISABLE,避免 HCI 日志导致音频卡顿;需要查看日志时在 LCPU 侧采集。

  • lcpu中MAX_BT_SCO、MAX_BLE_ACT、MAX_BLE_ISO配置如下