低功耗蓝牙协议栈入门(五)广播者与观察者(NimBLE 应用)
在 低功耗蓝牙协议栈入门(一) 中有提到 BLE 一共有四种 GAP 角色:广播者;观察者;外围设备;中央设备。一个设备可以同时支持多个角色。所有的蓝牙应用都是围绕着这四个角色来的。
本文将结合 NimBLE 蓝牙协议栈的代码来介绍蓝牙的广播者与观察者这两种角色。
一. 广播者
广播者是发送广播报文的设备,通常广播一些服务数据给处于观察者角色的设备。 NimBLE 的蓝牙广播,涉及到这两个结构体:struct ble_gap_adv_params
, struct ble_hs_adv_fields
。
1 | /* (1).广播配置 */ |
1 | /* (2).广播数据 */ |
(1). 广播配置
就可连接性而言,蓝牙的广播可以分为三种模式:
- 不可连接模式;
- 定向连接模式;
- 无向连接模式;
需要配置结构体 ble_gap_adv_params
的conn_mode
成员值。
连接的模式,可以是下列宏定义之一:
#define BLE_GAP_CONN_MODE_NON //(non-connectable; 对应蓝牙白皮书章节:3.C.9.3.2).
#define BLE_GAP_CONN_MODE_DIR //(directed-connectable; 对应蓝牙白皮书章节:3.C.9.3.3).
#define BLE_GAP_CONN_MODE_UND //(undirected-connectable; 对应蓝牙白皮书章节:3.C.9.3.4).
就可发现性而言,蓝牙的广播可以分为三种模式:
- 不可发现模式;
- 有限可发现模式;
- 一般可发现模式;
需要配置结构体 ble_gap_adv_params
的disc_mode
成员值。
发现的模式,可以是下列宏定义之一:
#define BLE_GAP_DISC_MODE_NON //(non-discoverable; 对应蓝牙白皮书章节:3.C.9.2.2).
#define BLE_GAP_DISC_MODE_LTD //(limited-discoverable; 对应蓝牙白皮书章节:3.C.9.2.3).
#define BLE_GAP_DISC_MODE_GEN //(general-discoverable; 对应蓝牙白皮书章节:3.C.9.2.4).
以上两个成员是广播配置过程当中最常用的,除此之外,蓝牙的广播间隔,信道地图,过滤策略,等相关配置也是通过这个结构体内部成员变量配置的。
(2). 广播数据
如下图所示,红色框框为广播数据,广播数据内部的成员由黄色框框部分组成。每个黄色框框成员内容的开始处均使用Length字节来表示这个成员的长度,紧接着是Type,用于表示这个广播数据成员类型。
决定广播内容的是结构体 ble_hs_adv_fields
。这个结构体内部定义了以下几种成员:
- 标识(Flags):定义了设备的情况。如,有限可发现模式;通用可发现模式;不支持 BR/EDR;等。
- 服务:多种不同类型的服务数据类型,也就是服务 UUID。
- 本地名称:蓝牙的名称,以字符串的形式保存。
- 发射功率等级:长度为一个字节,单位为dBm。
- 从设备连接间隔范围:通过这个数据,中央设备可以确定从设备倾向的连接时间间隔。
- 服务请求:代表了发出该广播的从设备向外界中央设备发送的服务请求。
- 服务数据:设置服务广播时使用的广播数据类型。
- 制造商指定数据:起始的两个字节代表公司,后面跟着公司的自定义数据。
(3). 广播实例代码
完成广播配置;广播数据;这两个相关结构体的配置后即可发起一段时间的广播。以下为代码流程注解:
1 | static void advertise(void) |
二. 观察者
观察者是扫描广播者,并将信息报告给应用的设备。 NimBLE 的蓝牙扫描,涉及到的结构体为:struct ble_gap_disc_params
。通过对这个结构体的初始化,可以配置 NimBLE 的 扫描类型;扫描间隔;扫描窗口;扫描过滤策略;以及扫描“有限发现设备”。
扫描类型:
- 被动扫描:设备被动监听来自外围设备的广播数据包。
- 主动扫描:监听到广播数据包后,主动发起扫描请求,进一步获取扫描响应数据包。
以下两图来自蓝牙《core_v5.2.pdf》 Vol 6, Part D 第4节。
扫描间隔与扫描窗口:
如果扫描间隔配置为 500ms,扫描窗口配置为 250ms,那么设备就会在每 500ms 内进行 250ms 的扫描。
扫描过滤策略:
配置接收任何广播数据包或者仅接收白名单设备的广播数据包。
扫描“有限发现设备”:
仅扫描处于有限可发现模式的设备广播,忽略其他模式下的设备广播。
以下为代码流程注解:
1 | static void scan(void) |
scan_event
回调函数用于处理扫描事件:
1 | static int scan_event(struct ble_gap_event *event, void *arg) |