废话
接上一篇整体架构介绍后,相信大家对NXP低功耗协议的使用还是没有什么了概念(Yes,我没写错你也没看错)。由于第一篇博文信息量过大,没有在NXP BLE SDK上做过一定开发的的同学看起来肯定是云里雾里。不用担心!从本篇开始将BLE SDK中逐个功能进行剖析,并且尽可能按照由浅入深的顺序发布。
前置条件
由于网上有大量的文章介绍BLE技术,这儿就不从零讲起基本概念了,假定各位同学对BLE广播与扫描功能有基本的了解,至少知道得以下几个方面:
- 广播和扫描的意义(为什么双方要做这件事)
- 双方的角 {MOD}扮演(简单说主机扫描,从机广播)
- 基本的广播参数:时间间隔,广播通道,扫描占空比等
- 广播中带有一些数据,如果知道他们的格式就更好了
虽然文章主要目的是要在介绍如何在NXP低功耗蓝牙SDK中进行广播和扫描,但如果一上来就讲API又过于干涩。在涉及到笔者认为有必要展开的协议内容的时候我还是会将一下原理的。
BLE 5对广播和扫描这部分有一些比较大的变化,目前市面上相关的应用还比较少,因此本篇仍以BLE 4.2规范中定义的功能作为出发点,后续如有必要再单独介绍BLE 5带来的广播扩展功能。
什么时候可以开始广播 & 扫描?
在BLE协议规范中,对主机(Host)和控制器(Controller)初始化的流程有一个清晰定义,用户要走完这个流程才能向协议栈提交请求,同时BLE芯片也需要对射频部分寄存器进行初始化来保证RF电路的正常工作。NXP BLE SDK中的系统入口
main_task()
任务体实现里,在正式进入事件loop之前将会调用
Ble_Initialize()
来准备以上两项工作。注意!是准备,而非完成。也就是说该函数返回后,协议栈可能仍然没有准备好。
记得千万不要直接在这个函数后面开启广播和扫描!
那何时才可以呢?初始化过程在调用
Ble_Initialize()
时SDK会让安装一个默认的回调函数
App_GenericCallback()
,回调触发后再由她转而触发用户层的
BleApp_GenericCallback()
。当协议栈完成所有初始化工作后,用户将在这个回调函数里收到
gInitializationComplete_c
事件,这才标志着用户可以正常使用BLE协议栈提供的服务了。
广播(advertising)
在NXP BLE SDK中涉及到广播主要是4个API:
// 设置广播数据(和扫描回复数据)
bleResult_t Gap_SetAdvertisingData(gapAdvertisingData_t* pAdvertisingData,
gapScanResponseData_t* pScanResponseData);
// 设置广播参数
bleResult_t Gap_SetAdvertisingParameters(gapAdvertisingParameters_t*
pAdvertisingParameters);
// 开启广播
bleResult_t Gap_StartAdvertising(gapAdvertisingCallback_t advertisingCallback,
gapConnectionCallback_t connectionCallback);
// 停止广播
bleResult_t Gap_StopAdvertising(void);
以上的4个API都是异步的,就意味着调用后会立刻得到返回值,该返回值仅表示函数调用(如参数传递是否正确,内存是否充足)的结果,实际功能的执行完成,用户应等到各个的回调事件到来作为判断。
下表列出了相关的事件:
事件Tag |
触发函数&事件 |
用户回调函数 |
gAdvertisingDataSetupComplete_c
Gap_SetAdvertisingData
通用回调函数
BleApp_GenericCallback
gAdvertisingParametersSetupComplete_c
Gap_SetAdvertisingParameters
通用回调函数
BleApp_GenericCallback
gAdvertisingStateChanged_c *
Gap_StartAdvertising Gap_StopAdvertising
广播回调函数
BleApp_AdvertisingCallback
注意:用户需要自己记录当该事件产生时广播是被打开了还是被关闭了。
通常用户需要先设置好广播数据和广播参数再开启广播,需要按照一定顺序调用这几个API(利用回调作为衔接)。在使用NXP BLE SDK的时,ble conn manager和每一份例程代码都已经帮用户把广播流程规划好了,用户只需要在合适的时候调用例程代码中的
BleApp_Advertise()
即可。如果例程带的广播策略符合用户的需求,则用户只需要关心
app_config.c
中所填充的广播数据和广播参数即可。
不要用户去调用这
BLE的广播分为四种类型
广播数据
根据BLE协议规定,广播包可以发送最多31个字节的数据,如果设备支持扫描请求(Scan Request),还可以在扫描回复(Scan Response)里在回复31个字节,这样最长也就是62字节的信息量。这些信息不是随便填的,必须按照BLE协议规范定义的格式,主机才能正确的解析其中的内容。
广播参数
调用
Gap_Connect
时需要主设备填充一组连接参数,这些参数有一部分直接映射到BLE协议栈链路层CONNECT_IND数据包的内容,通过空口传递给从设备。另外一部分是应用层给协议栈传递的信息,告诉协议栈与
扫描API和Event
连接 (connect)
主设备在完成扫描流程后,获取了周围设备的列表和基本信息。如果要进一步与某一个设备进行用户数据交互则双方需要进入连接状态(非beacon的应用)。
建立(和断开)BLE连接只涉及到2个API和2个Event:
// 建立连接,仅主设备允许调用
bleResult_t Gap_Connect(gapConnectionRequestParameters_t* pParameters,
gapConnectionCallback_t connCallback);
// 断开连接,主从都可以调用
bleResult_t Gap_Disconnect(deviceId_t deviceId);
这两个API各自对应了一个'连接'和'断开连接'事件,通知应用层示连接已经建立或者已经断开:
事件Tag |
触发函数 |
回调函数 |
gConnEvtConnected_c *
Gap_Connect
连接回调函数
BleApp_ConnectionCallback
gConnEvtDisconnected_c *
Gap_Disconnect
连接回调函数
BleApp_ConnectionCallback
注意:这两个事件并非只有当调用函数才会产生,如果对端是主设备与我们建立连接,作为从设备我们也会得gConnEvtConnected_c 。同样,如果对端主动与我们断开,或者链路因某些异常原因断开,协议栈也会抛出gConnEvtDisconnected_c事件。这种会被协议栈主动触发的事件在SDK中还有不少,我们可以称为异步事件。
在NXP BLE SDK开发时,
Gap_Connect
的API被包在
App_Connect
内的,用户一般是调用
App_Connect
,她传入的连接回调函数会在用户线程上下文中被调用。在实时操作系统环境下,事件处理中即使有长时间占用CPU的行为(比如打印出事件参数),也不会对协议栈造成影响。这个我们在前面架构介绍中有讲解这个机制,有困惑的同学可以回去上一篇博文。
下图面的时序图完整的展示了主从双方连接建立和断开过程
连接请求参数
调用
Gap_Connect
时需要主设备填充一组连接参数,这些参数有一部分直接映射到BLE协议栈链路层CONNECT_IND数据包的内容,通过空口传递给从设备。另外一部分是应用层给协议栈传递的信息,告诉协议栈与
先直接看看有哪些参数需要填写的:
typedef struct gapConnectionRequestParameters_tag {
uint16_t scanInterval;
uint16_t scanWindow;
bleInitiatorFilterPolicy_t filterPolicy;
bleAddressType_t ownAddressType;
bleAddressType_t peerAddressType;
bleDeviceAddress_t peerAddress;
uint16_t connIntervalMin;
uint16_t connIntervalMax;
uint16_t connLatency;
uint16_t supervisionTimeout;
uint16_t connEventLengthMin;
uint16_t connEventLengthMax;
bool_t usePeerIdentityAddress;
} gapConnectionRequestParameters_t;
参数 |
取值范围 |
说明 |
connIntervalMin
连接回调函数
BleApp_ConnectionCallback
connIntervalMax
Gap_Disconnect
连接回调函数
BleApp_ConnectionCallback
connLatency
Gap_Disconnect
连接回调函数
BleApp_ConnectionCallback
supervisionTimeout
Gap_Disconnect
连接回调函数
BleApp_ConnectionCallback
uint16_t connIntervalMin;
uint16_t connIntervalMax;
uint16_t connLatency;
uint16_t supervisionTimeout;
NXP BLE SDK作为主设备类型的例程在
app_config.c
都会给出一个连接参数配置
gConnReqParams
,用户直接根据自己需求修改即可,
peerAddress
和
peerAddressType
两个参数则来自于扫描得到的数据。至于主设备连接哪一个扫描到的从设备,这完全是用户的定义的行为了,通过广播数据的字段来判断是一种常用的做法。
更多内容
动态改变广播数据
有时候用户希望广播数据需要修改
同时广播与扫描
有的产品担任双角 {MOD},既可以作为主设备扫描并连接其他从设备,同时也可以作为从设备,广播被其他主设备发现并连接。甚至需要同一时刻做这两件事(同时广播和扫描)。
虽然通常蓝牙芯片只有一个RF端口,但广播和扫描这两项功能一个是只发送数据(Tx),一个仅接收数据(Rx),因此射频上不存在问题。从BLE 4.1协议规范后,定义了BLE芯片在链路层支持多个状态机,这样就完整的支持了同时进行广播和扫描,以及建立多个连接,和混合的拓扑结构。
在NXP BLE SDK中实现这个功能是非常简单的,首先确认使用的协议栈库文件是支持主从设备的,而非仅支持从设备(peripheral)的库,然后按照文章前面介绍的广播与扫描API调用和Event处理流程操作即可,二者并不冲突。
连接之后呢...
建立完连接只是万里长征第一步,连接建立后双方通常还不能直接进行用户数据交互,后面还要进行一系列BLE协议规范所要求的过程,如配对加密,客户端对服务器端的服务进行查询,双方一些约定信息的交互等等。若知后事如何,请听下回分解。(PS: 恭喜正式入坑BLE开发)。