目录
前言
最近由于项目需求在学习 BLE,网上Android BLE开发 的资料相比其他 Android 资料显得有些匮乏,在此记录学习例程,希望能能对学习 BLE 的童鞋有所帮助。
在上手 Android 之前我们需要先搞清楚蓝牙的协议及通讯过程,才不会在调用 Google 提供的 API 时似懂非懂。
蓝牙的分类
当前的蓝牙协议分为 基础率 / 增强数据率(BR/EDR)和 低耗能(BLE)两种类型。
当然现在 BLE蓝牙模块 还有单模和双模之分,单模指的是 仅支持BLE ,双模即 Bluetooth Classic + BLE。
蓝牙BD/EDR和蓝牙BLE主要区别
BLE协议框架
蓝牙协议规定了两个层次的协议,分别为 蓝牙核心协议(Bluetooth Core)和 蓝牙使用层协议(Bluetooth Application)。
蓝牙核心协议关心对蓝牙核心技术的描述和规范,它只提供基础的机制,并不关心如何运用这些机制;
蓝牙使用层协议,是在蓝牙核心协议的基础上,根据具体的使用需要定义出各种各样的策略,如 FTP、文件传输、局域网...
蓝牙核心协议(Bluetooth Core)又包含 BLE Controller 和 BLE Host 两部分。
这两部分在不一样的蓝牙技术中(BR/EDR、AMP、BLE),承担角色略有不一样,但大致的功能是相同的。
Controller 负责定义 RF、Baseband 等偏硬件的规范,并在这之上抽象出用于通信的逻辑链路(Logical Link);
Host 负责在逻辑链路的基础上,进行更为友好的封装,这样就可以屏蔽掉蓝牙技术的细节,让 Bluetooth Application 更为方便的运用。
BLE 低功耗蓝牙核心协议层详解(Bluetooth Core)
物理层(Physical Layer,简写 PHY):PHY层 用来指定 BLE 所用的 无线频段,调制解调方式等。例如 1Mbps 自适应跳频的 GFSK 射频,工作于免许可证的 2.4GHz ISM(工业、科学与医疗)频段。
链路层(Link Layer,简写 LL):LL层 是整个 BLE 协议栈 的核心。LL 层 要做的事情非常多,比如 选择射频通道 ,怎么 识别空中数据包,具体在 哪个时间点把数据包发送出去,怎么 保证数据的完整性,ACK 如何接收,如何进行重传,以及 如何对链路进行管理和控制 等等。
ps:LL 层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的 GAP 或者 ATT。
主机控制接口层(Host Controller Interface,简写 HCI):HCI 是 可选的,HCI 主要用于 2 颗芯片实现 BLE 协议栈 的场合,用来规范两者之间的通信协议和通信命令等。
通用访问配置文件层(Generic access profile,简写 GAP):GAP 是对 LL 层 payload(有效数据包)进行 解析 的两种方式中的一种,而且是最简单的那一种。GAP 简单的对 LL payload 进行一些规范和定义,因此 GAP 能实现的功能极其有限。
ps:GAP 现在主要用来进行广播,扫描和发起连接等。
逻辑链路控制及自适应协议层(Logical Link Control and Adaptation Protocol,简写 L2CAP):L2CAP 对 LL 进行了一次简单封装,LL 只关心传输的数据本身,L2CAP 就要 区分是加密通道还是普通通道,同时还要 对连接间隔进行管理。
安全管理层(Security Manager,简写 SM):SMP 用来管理 BLE 连接的加密和安全的,如何保证连接的安全性,同时不影响用户的体验,这些都是 SMP 要考虑的工作。
属性协议层(Attribute protocol,简写 ATT):简单来说,ATT 层用来定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据。BLE 协议栈中,开发者接触最多的就是 ATT。BLE 引入了 attribute(属性) 概念,用来描述一条一条的数据。Attribute 除了定义数据,同时定义该数据可以运用的 ATT 命令。
通用属性配置文件层(Generic Attribute profile,简写 GATT):GATT 用来规范 attribute 中的数据内容,并运用 group(分组)的概念对 attribute 进行分类管理。没有 GATT,BLE 协议栈 也能跑,但互联互通就会出问题,也正是因为有了 GATT 和各种各样的使用 profile,BLE 摆脱了 ZigBee 等无线协议的兼容性困境,成了出货量最大的 2.4G 无线通信产品。
关键术语及概念
GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。
安卓官方文档中显示:目前所有低能耗应用配置文件均基于 GATT。
GAT
简介
介绍 GATT 之前,需要了解 GAP(Generic Access Profile),它在用来 控制设备连接和广播 。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与其他设备进行交互。例如 Beacon 设备就只是向外广播,不支持连接,小米手环就等设备就可以与中心设备连接。
GAP 给设备定义了若干角色,其中主要的两个是:外围设备(Peripheral)和中心设备(Central)。
- 外围设备:这一般就是非常小或者简单的低功耗设备,用来提供数据,并连接到一个更加相对强大的中心设备。例如小米手环。
- 中心设备:中心设备相对比较强大,用来连接其他外围设备。例如手机等。
在 GAP 中外围设备通过两种方式向外广播数据: Advertising Data Payload(广播数据)和 Scan Response Data Payload(扫描回复),每种数据最长可以包含 31 byte。这里广播数据是必需的,因为外设必需不停的向外广播,让中心设备知道它的存在。广播间隔越长,越省电,同时也不太容易扫描到。扫描回复是可选的,中心设备可以向外设请求扫描回复,扫描回复可以包含一些额外的数据,例如设备的名字。
广播数据格式
说明
-
Flags: TYPE = 0x01。这个数据用来标识设备 LE 物理连接的功能。VALUE 是 0 到多个字节的 Flag 值,每个 bit 上用 0 或者 1 来表示是否为 True。如果有任何一个 bit 不为 0,并且广播包是可连接的,就必须包含此数据。各 bit 的定义如下:
- bit 0: LE 有限发现模式
- bit 1: LE 普通发现模式
- bit 2: 不支持 BR/EDR
- bit 3: 对 Same Device Capable (Controller) 同时支持 BLE 和 BR/EDR
- bit 4: 对 Same Device Capable (Host) 同时支持 BLE 和 BR/EDR
- bit 5..7: 预留
-
Service UUID: 广播数据中一般都会把设备支持的 GATT Service 广播出来,用来告诉外面本设备所支持的 Service。有三种类型的 UUID:16 bit, 32bit, 128 bit。广播中,每种类型类型有有两个类别:完整和非完整的。这样就共有 6 种 Type。
- 非完整的 16 bit UUID 列表: TYPE = 0x02;
- 完整的 16 bit UUID 列表: TYPE = 0x03;
- 非完整的 32 bit UUID 列表: TYPE = 0x04;
- 完整的 32 bit UUID 列表: TYPE = 0x05;
- 非完整的 128 bit UUID 列表: TYPE = 0x06;
- 完整的 128 bit UUID 列表: TYPE = 0x07;
-
Local Name: 设备名字,VALUE是名字的字符串。Local Name 可以是设备的全名,也可以是设备名字的缩写,其中缩写必须是全名的前面的若干字符。
- 设备全名: TYPE = 0x08
- 设备简称: TYPE = 0x09
-
TX Power Level: TYPE = 0x0A,表示设备发送广播包的信号强度。VALUE部分是一个字节,表示 -127 到 + 127 dBm。
-
带外安全管理(Security Manager Out of Band):TYPE = 0x11。VALUE也是 Flag,每个 bit 表示一个功能:
- bit 0: OOB Flag,0 表示没有 OOB 数据,1 表示有
- bit 1: 支持 LE
- bit 2: 对 Same Device Capable (Host) 同时支持 BLE 和 BR/EDR
- bit 3: 地址类型,0 表示公开地址,1 表示随机地址
-
外设(Slave)连接间隔范围:TYPE = 0x12。数据中定义了 从设备 Slave 最大和最小连接间隔,数据包含 4 个字节:
- 前 2 字节:定义最小连接间隔,取值范围:0x0006 ~ 0x0C80,而 0xFFFF 表示未定义;
- 后 2 字节:定义最大连接间隔,同上,不过需要保证最大连接间隔大于或者等于最小连接间隔。
-
服务搜寻:外围设备可以要请中心设备提供相应的 Service。其数据定义和前面的 Service UUID 类似:
- 16 bit UUID 列表: TYPE = 0x14
- 32 bit UUID 列表: TYPE = 0x??
- 128 bit UUID 列表: TYPE = 0x15
-
Service Data: Service 对应的数据。
- 16 bit UUID Service: TYPE = 0x16, 前 2 字节是 UUID,后面是 Service 的数据;
- 32 bit UUID Service: TYPE = 0x??, 前 4 字节是 UUID,后面是 Service 的数据;
- 128 bit UUID Service: TYPE = 0x??, 前 16 字节是 UUID,后面是 Service 的数据;
-
公开目标地址:TYPE = 0x17,表示希望这个广播包被指定的目标设备处理,此设备绑定了公开地址,VALUE 是目标地址列表,每个地址 6 字节。
-
随机目标地址:TYPE = 0x18,定义和前一个类似,表示希望这个广播包被指定的目标设备处理,此设备绑定了随机地址,VALUE 是目标地址列表,每个地址 6 字节。
-
Appearance:TYPE = 0x19,VALUE是表示了设备的外观。
-
厂商自定义数据: TYPE = 0xFF,厂商自定义的数据中,前两个字节表示 厂商ID,剩下的是厂商自己按照需求添加,里面的数据内容可以自己定义。
-
其他说明可查看 官方文档 BLE广播类型定义
广播网络拓扑
大部分情况下,外设通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。但是也有些情况是不需要连接的,只要外设广播自己的数据即可。用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。而基于 GATT 连接的方式中,只能是一个外设连接一个中心设备。使用广播这种方式最典型的应用就是苹果的 iBeacon。它将自定义数据插入 Manufacturer Specific Data 中。其网络拓扑如下:
在外围设备和中央设备之间建立连接后,广播通常会停止,并使用 GATT 的服务和特征实现双向的通信。
GATT
简介
GATT 的全名是 Generic Attribute Profile(普通属性协议),它定义两个 BLE 设备通过叫做 Service 和 Characteristic 的东西进行通信。GATT 就是使用了 ATT(Attribute Protocol)协议,ATT 协议 把 Service, Characteristic 对应的数据保存在一个查找表中,查找表使用 16 bit ID 作为每一项的索引。这个 ID 貌似就是后面的 UUID 。
一旦两个设备建立起了连接,GATT 就开始起作用了,这也意味着,你必需完成前面的 GAP 协议。
GATT 连接,必需先经过 GAP 协议。实际上,我们在 Android 开发中,可以直接使用设备的 MAC 地址,发起连接,可以不经过扫描的步骤。这并不意味不需要经过 GAP,实际上这只是为我们提供的接口。蓝牙芯片发起连接,总是先扫描设备,扫描到了才会发起连接。
GATT 连接需要特别注意的是:GATT 连接是独占的。这意味着 BLE 外围设备一次只能连接到一个中心设备(手机等)一旦外设被连接,它就会马上停止广播,这样它就对其他设备不可见了。当设备断开,它又开始广播。而中央设备是可以连接到多个外围设备的。
中心设备和外设需要双向通信的话,唯一的方式就是建立 GATT 连接。
GATT 连接的网络拓扑
一个外设只能连接一个中心设备,而一个中心设备可以连接多个外设。
一旦建立起了连接,通信就是双向的了,对比前面的 GAP 广播的网络拓扑,GAP 通信是单向的。如果你要让两个设备外设能通信,就只能通过中心设备中转。
GATT 通讯事务
GATT 通信的双方是 C/S 关系。外设作为GATT 服务端(Server),它维持了 ATT 的查找表以及 service 和 characteristic 的定义。中心设备是 GATT 客户端(Client),它向 Server 发起请求。需要注意的是,所有的通信事件,都是由客户端(也叫主设备,Master)发起,并且接收服务端(也叫从设备,Slave)的响应。
一旦连接建立,外设将会给中心设备建议一个连接间隔(Connection Interval), 这样,中心设备就会在每个连接间隔尝试去重新连接,检查是否有新的数据。但是,这个连接间隔只是一个建议,你的中心设备可能并不会严格按照这个间隔来执行,例如你的中心设备正在忙于连接其他的外设,或者中心设备资源太忙。
下图应说明外围设备(GATT 服务器)和中央设备(GATT 客户端)之间的数据交换过程:
可以看到,每个时间间隔中,都说是由中央设备(GATT 客户端)首先发起请求,外围设备(GATT 服务器)给予响应。
服务和特征
简介
BLE 中的 GATT 事务 基于名为 Profiles,Services 和 Characteristics 的高级嵌套对象,而每一个Characteristic 还包含了一个 value和 n 个描述符Descripter。其嵌套结构如下:
BLE 外设本身实际上不存在配置文件,它是由 Bluetooth SIG 或外围设计人员编译的预定义Service 集合。可在此处查看官方采用的基于 GATT 的配置文件的完整列表 配置文件概述 。
Service
Service 用于将数据拆分为逻辑实体,它包含称为“Characteristic”的数据块。 Service 可以具有一个或多个Characteristic;每个Service 以 UUID 作为唯一数字 ID 以此将其自身与其他Service 区分开来。UUID 可以是 16 位(官方正式采用的 BLE Service )或 128 位(自定义Service ) )。
可以在 Bluetooth Developer Portal 的 “ Service ” 页面上看到正式采用的 BLE Service 的完整列表。
UUID 形如 0x18xx 都是 BLE自身定义的Service ;而 128 位的是厂商自定义的 Service
Characteristic
GATT 事务中的最低级别概念是 Characteristic ,其作用是封装单个数据点。当然它可能包含一个组关联的数据,例如加速度计的 X/Y/Z 三轴值。
与 Service 类似,每个Characteristic 通过预定义的 16 位或 128 位 UUID 进行区分,我们可以使用官方蓝牙 SIG 定义的标准特性(确保可互操作和支持 BLE 的硬件 / 软件)也可以自行定义Characteristic 。
Characteristic 是与 BLE 外设交互的要点,我们读写操作是通过读写 Characteristic 来完成的。实际上,和 BLE 外设打交道,主要是通过 Characteristic。我们可以从 Characteristic 读取数据,也可以往 Characteristic 写数据。这样就实现了双向的通信。
例如:我们定义了一个具有读写权限的Characteristic