Android上低功耗蓝牙(BLE)在网易医疗项目中的开发实践(一)-BLE蓝牙原理

达芬奇密码2018-07-12 13:43

背景

  • 网易医疗

    主要是一个外设的盒子,定位在健康产品系列,移动端有一个APP是和这个盒子进行通信,然后完成一些数据传输。而这个盒子就是一个基于BLE协议的硬件设备。

  • 蓝牙历史

    说到蓝牙,就不得不说下蓝牙技术联盟(Bluetooth SIG),它负责蓝牙规范制定和推广的国际组织。如果需要开发蓝牙硬件产品,应该会和他们打交道。有些小伙伴可能只听过蓝牙1.0,蓝牙2.0,蓝牙3.0,蓝牙4.0之类的以数字结尾的蓝牙版本号。这些是以前的标准,而最新的标准中,已经不使用数字版本号来作为蓝牙版本的区分了,取而代之的就是经典蓝牙以及低功耗蓝牙。稍微提下蓝牙版本的演进过程:99年蓝牙1.0发布,后面就是蓝牙2.1。蓝牙2.1的版本使用最广,很多产品都是这个版本,也就是我们所谓的经典蓝牙,以前非智能手机也是支持这个版本的。到了蓝牙3.0,又名为高速蓝牙,在2.1的基础上大大提升了传输速度(24Mbps)。后面4.0/4.1引入了低功耗蓝牙。蓝牙5.0也已经发布。主要是在信号范围,连接速度,以及广播速度进行了优化,蓝牙5.0还对物联网方向做了单独的改进。

    Ps:可能很多人会误解蓝牙4.0就是低功耗蓝牙,但是完整的蓝牙4.0规范实际上是包含经典蓝牙和低功耗蓝牙两部分的。只是说在蓝牙4.0规范,蓝牙技术联盟才引入了BLE。

  • 低功耗蓝牙--BLE

    全称是Bluetooth Low Energy,简称BLE。也就是说最大的一个特点就是低功耗了。有一些BLE设备一个纽扣电池可以使用一两年。当然所有的优点和缺点都是相对的,而BLE的对立面当然就是历史悠久的经典蓝牙了。经典传统蓝牙有3个功率级别,Class1,Class2,Class3分别支持100m,10m,1m的传输距离,一般要求至少使用两节3A电池。而且工作几天或者几周就没电了。前面说了低功耗蓝牙一个纽扣电池就可以工作一两年,3A电池的电量一般是纽扣电池的10至12倍。这是BLE的一个很大的优点,尤其是对于现在的穿戴设备以及各种物联网传感器,BLE电池的续航是一个很大的突破点。正所谓有得必有失,低功耗带来的低传输速率,当然BLE被设计的本来就是传输少量数据的,对于很多传感器设备,例如心跳带,血压计等设备,是非常适合的。

  • BLE ON Android

    从Android 4.3 Jelly Bean(API 18)才开始支持低功耗蓝牙,但是仅仅是支持中心(Central)模式。所谓的中心模式,即可以连接其他蓝牙外设。直到2014.6.26 Android Lollipop的面世,才带来了周边API的支持(BluetoothLeAdvertiser)。即Android 5.0以后的手机可以作为一个外设来进行发布。Android SDK 中 BLE 相关的 API 都在 android.bluetooth. 下面,同时在 Android 5.0 也引入了一些也需要用到 android.bluetooth.le 下面的 API。另外,要在 APP 中使用蓝牙功能,需要在 Manifest 中申请蓝牙相关的权限。在 Android 6.0 及以上平台中,还需要申请定位权限。

协议栈

蓝牙4.1BLE协议栈的结构图如下:

下面是详细介绍各个层级的含义:

  1. PHY(Physical Layer):物理层,蓝牙是工作在2.4GHz附近,这是工业、科学、医疗ISM的频段,免许可证。WIFI也是工作在同一个频段。蓝牙把频段切分为40个通道,3个广播通道,37个数据通道,按照一个规律跳频通信。
  2. LL(Linker Layer):链路层,用于控制设备的射频状态,设备将处于五种状态之一:等待、广告、扫描、初始化、连接。广播设备不需要建立连接就可以发送数据,而扫描设备接收广播设备发送的数据;发起连接的设备通过发送连接请求来回应广播设备,如果广播设备接受连接请求,那么广播设备与发起连接的设备将会进入连接状态。发起连接的设备称为主机,接受连接请求的设备称为从机。
  3. HCI(Host Controller Interface):主机和控制器就是通过这个接口来进行同学的,通信的介质就是HCI命令。这层在协议栈中是可选的,一些小型终端可能没有,但是Android设备上肯定有,这层是蓝牙上层和芯片的交互必经之路,对于蓝牙硬件开发者,这里的log能够很好的帮助解决问题。
  4. HOST部分要复杂一些,有链路控制和适配层(L2CAP),安全管理(SM)等。其中L2CAP和SM我们知道概念就可以了,这里就不多关注了。我们重点来看属性协议层,也就是ATT。它是整个BLE通信的基础。ATT负责数据封装,向外暴露为"属性",提供"属性"的为服务端,获取"属性"的为客户端。ATT是专门为BLE低功耗蓝牙而设计的传输协议,结构简单,传输数据短(后面会有提及)。
  5. GATT(Generic Attribute Profile):全称叫做通用属性配置文件,是基于ATT做进一步的逻辑封装,定义数据的交互方式和含义,APP的开发其实就已经接触到这一层了。GATT按照层级定义了三个非常重要的概念:服务(Service)、特征(Characteristic)、描述(Descripter)。他们之间的关系如下:                                           

    一个Service可以包含若干个Characteristic,一个Characteristic可以包含属性(properties)和值(value),还可以包含多个descripter。Characteristic实际上具有读、写、通知等权限,我们在对一个BLE设备发起连接成功以后,对他进行读写,其实就是对Characteristic的读写或者订阅通知。图中所谓的Profile,实际上是一组服务的集合,这些服务被人组合起来就形成了一个特定的使用场景。比如说,小米手环,里面就有一个计算用户当前步数的服务。这就是这个Ble可以做的事情,也就是它的profile。

  6. 第5个我们又提到一个BLE设备实际上就是一组Service的集合,那BLE蓝牙以什么来标识多个Service呢。答案就是UUID。BLE中的Service,Characteristic,Descripter都是使用UUID来作为唯一标识。所以我们在读写BLE蓝牙数据时,都要带上相应的UUID。

  7. GAP(Generic Access Profile):通用访问控制配置文件。定义了BLE整个通信过程中的流程,负责处理设备访问模式和程序,包括设备发现,建立连接,终止连接,初始化安全特性,设备配置。GAP层总是作为下面四种角色之一:(1)广播者:不可连接的广播设备。(2)观察者:扫描设备,但不发起建立连接。(3)外部设备:可连接的广播设备,可以在单个链路层连接中作为从机。(4)集中器:扫描广播设备并发起连接,可以在单个链路层连接中作为主机。

应用开发

BLE应用可以分为两大类:基于非连接的和基于连接的。

基于非连接的:意思就是外设和周边设备不发生连接,外设主要依赖周边设备发出的BLE广播,也叫做Beacon。这里有两个角色,发送广播的一方叫做Broadcaster。监听广播的一方叫做Oberver。这个在蓝牙协议栈的GAP层都有相应的角色定义。

基于连接的:就是外设和周边设备要建议显式的GATT连接,需要双方有通信。这个也有两个角色,外设设备(周边)叫做Peripheral。中心设备(一般是手机)叫做Centeral。

下面就对于这两类应用做更详细的解释,其实搞懂了这些,基本上BLE蓝牙开发的整个流程及原理就搞得很清楚了。

Beacon

基于非连接的BLE应用,下面是它的网络拓扑结构:

由于广播是单向的,Broadcaster向外发送广播,Observer接受广播。总体来说是多对多的关系。BLE广播中也能够带上数据,包括某些私有协议等。所以基于这种协议也是能够开发出独特的应用。完全基于广播的应用,有大名鼎鼎的iBeacon,这是苹果公司基于BLE广播实现的功能,可以实现广告推送以及室内定位。包括某些商场内部柜台寻找的功能,也是基于这种协议开发出来的。

在上图我画的设备都是单一的角色,而实际上有些设备是可以同时实现两种角色的,它即可以发送广播,也可以接受广播。想象一下,在现在比较火的智能家居系统中,有很多的传感器,如果一个设备接受到广播,做了处理以后在发送出去,就形成了一个双向的网格,是不是就有点像因特网了,这也就是有名的蓝牙Mesh。有兴趣的同学可以去看看。

关于Beacon应用里有一个很重要的点就是广播包的数据结构。数据包的格式如下图(官方图):

从图中就可以看出,每个广播数据包包含31个字节,分为有效数据以及无效数据两部分。

  • 无效数据部分:因为广播包的长度必须是 31 个 byte,如果有效数据部分不到 31 自己,剩下的就用 0 补全。这部分的数据是无效的,解释的时候,忽略即可。

  • 有效数据部分:包含若干个广播数据单元,称为 AD Structure。如图中所示,AD Structure 的组成是:第一个字节是长度值 Len,表示接下来的 Len 个字节是数据部分。数据部分的第一个字节表示数据的类型 AD Type,剩下的 Len - 1 个字节是真正的数据 AD data。其中 AD type 非常关键,决定了 AD Data 的数据代表的是什么和怎么解析。

我们详细来解释一下这个有效数据部分。上面的解释可能比较抽象,这里我用一个例子来解释一下:

E/TAG:scandata:02011A05FFAC0134560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

我们只截取有效数据部分: 02011A05FFAC013456。我们按照图中的协议进行一下分隔,02 01 1A 05 FF AC013456。我们看第一个字节为02,表示后面两个字节为一段数据。第二个字节为01,表示就是数据的type。1A就是这个type的内容数据了。05 表示后面5个字节是一个数据段,FF表示一种数据type。AC013456就是数据。扫描的数据块都是通过这种协议来组织的,所以这里最重要的就是这个数据type。官方解释看这里。我稍稍做了部分整理,如下:

扫描的工作流程

对于扫描的建议:1、首先,尽可能使用新的 API,功能更强大;2、尽可能少地扫描,因为毕竟扫描是一个比较重的操作,耗电,也会减慢 BLE 连接速度;3、扫描的时候,尽量设置 ScanFilter,只扫描那些你感兴趣的设备,而不是全盘扫描;4、正确使用 API,特别是合理停止扫描,防止资源泄漏,开始扫描的callback一定要和停止的callback是一个对象,防止底层句柄造成泄露。

基于连接的蓝牙应用(Connection App)

网路拓扑结构:

拓扑结构比较简单,一个中心设备可以连接多个外设,但是一个外设只能连接一个中心(主要是因为连接成功以后,外设就会停止对外广播,别人则发现不了它了)。其实一个中心连接的外设设备数量也是有限的,据说是7个。原因不明,应该底层芯片做了限制。

这篇文章中我就不贴出具体的代码了(下篇介绍蓝牙连接sdk的时候会有)。先通过一个demo来了解一下GATT的结构,以求更深的理解基于连接的蓝牙应用的原理。先看下面的demo图,图里面是一个客户端连接服务端后返回的数据。

图片左边的外设我一个私有服务都没写,但是蓝牙联盟预定了一些服务,分别是1801和1800。这两个都是官方指定的service。1800和1801分别表示Generic Access和Generic Attribute,描述了设备连接相关属性。有的设备(小米手环)还有1802这个官方服务,表示Immediate Alert,如果有这个服务,则说明外设设备支持即时提醒功能,如果向其中包含的Characteristic写入一个值,那么设备应当可以发出对应的响声。而右边的图,很明显多了两个私有服务,这个私有服务也只有服务提供者知道。关于这块的代码会在下一篇文章的蓝牙连接sdk中一并发出。这里只是知道原理即可。

连接的工作流程

可以看出来,BLE从连接到读写还是比较复杂的,各种回调。客户端先发起connect请求,不像读写更多的是软件来完成,connect实际上是双方芯片来完成然后回调给各自上层。所以不管服务端和客户端,都会有一个onConnectStateChanged回调。参数也是一致的,对于客户端来说,返回的BluetoothDevice就是外设设备,对于外设设备返回的就是中心设备。这里有一点需要注意,由于BLE蓝牙并不想经典蓝牙有永久mac地址一说,对于经典蓝牙,获取到mac地址后,这个蓝牙的mac地址就是不变的,所以可以做离线连接。对于BLE,mac地址在每次重新广播都会发生改变,从而导致当客户端触发onConnectionStateChanged后获取的BluetoothDevice的mac地址都是不一样的。这个是蓝牙官方规定,无法修改。那对于BLE蓝牙,如果做离线连接就要复杂的多。

图中倒数第二和第三是BLE特有的机制,这里简单说明一下,如果某个服务设置了Notify或者Indicate机制(UUID是固定的)。则改服务上的该属性可以接受服务端对客户端的notify,前提是连接完成以后客户端需要设置改属性为true。这样服务端可以推送一些固定的信息给客户端,这就比较适合那种电量推送的场景。

结语

上面我们分别介绍了BLE蓝牙的一些基本概念,并且分析了蓝牙的协议栈。而且也讲解了Android设备作为BLE应用的四个角色:监听者、广播者、中心设备以及外设。也提到了一些我在从事医疗项目开发遇到的坑。

实际情况在我们平时的开发中,也许是硬件团队丢给我们一台设备,然后协议文档,或许我们只需要了解两个角色就可以了,监听者和中心设备,然后对着API写相关的业务代码,但是我觉得了解协议栈或者蓝牙服务端是如何工作的,才能更好的解决问题。

下篇文章中我会介绍医疗项目中封装的Android蓝牙连接sdk。即支持经典蓝牙,也支持BLE蓝牙。

本文来自网易实践者社区,经作者徐正峰授权发布。