Android低功耗蓝牙总结

声明 这里只列出重点原理内容,更加细节的内容请阅读前面文章 java

首先要搞清楚一点,咱们在 Android 中经过 SDK 得到的蓝牙广播包是通过底层的 SDK 给咱们处理过的,是一个长度为 62 的字节数组。这个长度为 62 的字节数组是怎么来的呢?数组

想要搞清楚这个问题,首先咱们要明白 iBeacon 向外发送的最原始的广播包是什么样的?markdown

首先咱们要搞清楚一点,蓝牙在向外发送数据的时候是分红两个部分的一个就是普通的广播包还有一个叫作应答包。这是蓝牙协议的规定内容,针对于全部的蓝牙设备(iBeacon 只是蓝牙设备的一种)oop

  • 普通的广播包格式是定义好的,长度为 30 byteui

  • 应答包中的内容是能够由 蓝牙的各个制造厂商本身向里面放数据的。最大长度是 32 bytespa

须要注意的是,发送数据是从低位到高位一次发送,因此接收到的数据要返回来按字节拼接,例如接收到的MAC为 8b 03 00 b0 01 c2,那么实际的MAC为 c2:01:b0:00:03:8b翻译

蓝牙广播包

首先咱们来看一下第一个蓝牙广播包(来自 iBeacon 设备),一共 59 个字节3d

04 3e 38 0d 01 13 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e	   29个字节	
02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5  30个字节
复制代码

第一行的内容能够认为是蓝牙广播包中的附带信息,经过 Android SDK 是无法看到的,第二行是对应咱们 Android SDK 中收到的广播包中的前一部分。code

第一个字节是HCI Packet Type,04表示这是HCI Event;剩下的58bytes则是HCI Event的具体内容
第二个字节是EventCode,3e是此事件的代码;第三个字节是Parameter Length,0x38(十进制56)表示后面数据长度56bytes
第四个字节是SubEvent,0d表示这是LE Extended Advertising Report;第五个字节是Num Reports,数值为01
1b 00这两个字节表明Event Type,因为发送数据都是按字节发送以及从低位向高位发送,所以真实值是 001b
01 表示这是随机设备地址
8b 03 00 b0 01 c2 是此设备的MAC,根据从低向高的发送规则,因此真实MAC是 c2:01:b0:00:03:8b
01 表明首要广播信道的带宽
00 表明次要广播信道的带宽,此处表示不使用次要信道
ff 表示广播SID
7f 表明Tx Power的大小,此处是127dbm
af 表明RSSI的大小,此处是-81dbm
00 00 表明周期广播间隔
00 表明直接地址类型,次数是公共设备地址
00 00 00 00 00 00 表明直接BD_ADDR
1e 表明接下的的数据的字节数(长度),如下数据就是最重要的广播数据了
复制代码

上面的内容就是对应第一行的解释了,其实 Android SDK 已经帮咱们把这些数据中的部份内容解析出来,咱们能够直接经过对应的 SDK 的方法来直接获取。orm

下面咱们再来看 真正意义上的广播包

格式是这样的:

一个广播包是由若干个广播单元 AD Structure 构成的。每一个 AD Structure 的组成是:第一个字节表示长度值 length,表示接下来的 length 个字节是数据部分,数据部分的第一个字节表示数据的类型 AD Type,AD Type 决定了下面的数据表明了什么,关于每一个数值表明的数据类型见官方文档,剩下的 length - 1 个字节表示真正的数据

02 01 06  

02 表示接下来的数据有两个字节  01 表示数据类型,此处类型是 Flags    06 就是具体的数值了 0x06 = 0000 0110  每一位都有不一样的含义,见官方文档			
复制代码

1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25 27 11 4c b9 c5

1a 表示接下来的数据有 26 字节

FF 表示数据类型,此处类型是 厂商自定义数据类型(这里的厂商指的是苹果公司,由于 iBeacon 是苹果公司提出的)

4C 00 表示公司的 ID,此处的 004C 表明苹果公司
02 15  Beacon 的标识位,必须是这样的

fd a5 06 93 a4 e2 4f b1 af cf c6 eb 07 64 78 25
表示 Beacon UUID 

27 11  是 major 的值

4C b9  是 minor 的值

C5 表示 Measured Power  表示的是此设备在 1 米处的 RSSI 值,用于距离测算
复制代码

这段内容其实主要是苹果公司在蓝牙协议的基础上定义的。

若是符合 1AFF4C000215 则说明此设备是 iBeacon 设备

蓝牙应答包

04 3e 38 0d 01 1b 00 01 8b 03 00 b0 01 c2 01 00 ff 7f af 00 00 00 00 00 00 00 00 00 1e      29个字节
02 0a 00 08 16 f0 ff 64 27 11 4c b9 11 09 4d 69 6e 69 42 65 61 63 6f 6e 5f 30 30 39 30 37   30个字节
复制代码
其中第一行与上面同样,这里再也不介绍

02 0a 00 
02 表示接下来的数据长度 2 个字节
0a 表示数据类型 这里表示 Tx Power Level   取值范围是 -127 到 127 dBm
00 表示 0 dBm

08 16 f0 ff 64 27 11 4c b9
08 表示数据长度 
16 表示 Service Data 由 Service UUID 和 service 数据组成 前两个字节是 UUID 后面是数据
f0ff 是 Service UUID 
64 27 11 4c b9  是数据

11 09 4d 69 6e 69 42 65 61 63 6f 6e
11 表示数据长度
09 表示设备完整的名字
4d 69 6e 69 42 65 61 63 6f 6e  就是设备名字的 ASSIC 码了 对应 MiniBeacon
M   i n  i  B  e  a  c  o  n 

5f 30 30 39 30 37
这几个数据就是 Beacon 开发者随便乱加入的数据了,不符合协议内容
复制代码

Android 中接受到的广播包

上面咱们分别分析了蓝牙原始数据包中的广播包和应答包,其实对于 iBeacon 来讲广播包中的大多数内容其肯定的,只有 UUID Major Minor 会有变化。并且每一个位置所表明的做用都已经被 苹果公司 定义好了。若是想要 iBeacon 发出的数据包有更多的内容,那么咱们就能够在应答包中作文章了,应答包是有 32 个字节的。咱们只须要按照协议的内容向应答包中添加数据就能够了。

对于 Android 客户端,经过 Scanresult.getScanRecord().getBytes() 得到的广播包是 62 个字节,它把上面原始数据包中的内容提取出来了,只保留了第二行内容。就是 蓝牙广播包第二行(30 byte) + 蓝牙应答包第二行(最多 32 byte,数目不肯定),若是位数不够的话就用 0 补充。

因此咱们如今就能够很好根据得到的 byte[] 数组来解析广播包了。

// 如今就得到广播包了
byte[] result = ScanResult.getScanRecord().getBytes();
// UUID 包含 result[9] 和 result[24]
result[9]---result[24];
// Major
result[25]  result[26]
// Minor
result[27]   result[28]    
// Measured Power
result[29] 

// 通常咱们都是直接会先把 广播包转成 16 进制的格式而后来截取
String uuid = broadcast.substring(18, 50);   

// 至于后面应答包的内容就要根据具体的广播包格式来进行解析了,好比大家公司的硬件开发人员把电池电量放入了里面,那么大家就约定好放在什么位置,到时候你直接取就能够了。 
复制代码
关于 ScanResult 中的方法

这几个方法所得到内容都不是直接从 Android 中收到的广播(ScanResult.getScanRecord().getBytes())中解析出来的,而是从原始数据包中解析的。

getTxPower 获取传输功率,若是这个 iBeacon 不支持的话,那么结果就是 127

后面这几个方法做用不大,关键看设备是否支持

关键方法

ScanRecord 中的这几个方法就很重要的,这几个方法都和咱们收到的广播包有关系。

好比:若是应答包中对 Tx Power Level 进行了设置咱们就能够经过 getTxPowerLevel() 来直接获取。好比上面例子中的广播包,经过调用方法 getTxPowerLevel() 就能够获得 0

其余方法相似,只要你的应答包中数据的格式正确,就能够解析出来。

举例说明:

好比 Android 端收到的广播包是:

0201061AFF4C0002150123456789ABCDEF0123456789ABCDEF00000007C5   广播包

020A00    0303F1FF  0E16F1FF6400000007AC233F66C401   070965526F7574650000  应答包
复制代码

getTxPowerLevel() 返回 0 由于在应答包中有正确的格式数据 020A00

getServiceData() 也会返回值,由于在应答包中有对应的数据 0E16F1FF6400000007AC233F66C401

0E 表示数据长度
16 表示类型  此处表示 Service Data - 16-bit UUID (不只仅是 UUID 还带有数据) 前两个字节表示 UUID 后面是数据
F1FF 表示 UUID

6400000007AC233F66C401  表示数据  

Map<ParcelUuid, byte[]>  getServiceData() 返回的值就是用 UUID 和 数据做为键值对的形式
此处返回的 Map 集合中的内容是  注意:变化的 UUID 其他位数不会变化,若是广播包中 UUID 不是 F1FF,那么只须要对应替换就能够了
ParcelUuid = ParcelUuid.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
byte[] 就是数据部分对应的字节数值

复制代码

List<ParcelUuid> getServiceUuids() 方法对应的就是应答包中的数据 0303F1FF 因为只出现一次,因此 list 的 size就只有一个就是 F1FF 对应的 ParcelUuid 就是 ParcelUuid.fromString("0000fff1-0000-1000-8000-00805f9b34fb");

一样的下面几个方法也是对 Android 端收到的 62 byte 的广播包中数据的解析所得

String getDeviceName() 得到是名字 须要广播包中有对应的数据 070965526F7574650000

SpareArray<byte[]> getManufacturerSpecificaData() 获取的制造商的数据,对应 4C000215

byte[] getManufacturerSpecificData(int manufacture) 根据制造商代码(4c 对应的十进制)得到byte[] (0215)

数据类型对应表

仍是有一些字段翻译过来不够精细,详细见官方文档:www.bluetooth.com/specificati…

相关文章
相关标签/搜索