从上一篇GATT Profile 简介中提到过,BLE 设备工做的第一步就是向外广播数据。广播数据中带有设备相关的信息。本文主要说一下 BLE 的广播中的数据的规范以及广播包的解析。java
BLE 中有两种角色 Central 和 Peripheral ,也就是中心设备和外围设备。中心设备能够主动链接外围设备,外围设备发送广播或者被中心设备链接。外围经过广播被中心设备发现,广播中带有外围设备自身的相关信息。安全
广播包有两种: 广播包 (Advertising Data)和 响应包 (Scan Response),其中广播包是每一个设备必须广播的,而响应包是可选的。 数据包的格式以下图所示(图片来自官方 Spec):每一个包都是 31 字节,数据包中分为有效数据(significant)和无效数据(non-significant)两部分。ide
Len
,表示接下来的 Len
个字节是数据部分。数据部分的第一个字节表示数据的类型 AD Type ,剩下的 Len - 1
个字节是真正的数据 AD data 。其中 AD type 很是关键,决定了 AD Data 的数据表明的是什么和怎么解析,这个在后面会详细讲;全部的 AD type 的定义在文档 Core Specification Supplement 中。 AD Type 包括以下类型:函数
Flags: TYPE = 0x01。这个数据用来标识设备 LE 物理链接的功能。DATA 是 0 到多个字节的 Flag 值,每一个 bit 上用 0 或者 1 来表示是否为 True。若是有任何一个 bit 不为 0,而且广播包是可链接的,就必须包含此数据。各 bit 的定义以下:ui
Service UUID: 广播数据中通常都会把设备支持的 GATT Service 广播出来,用来告诉外面本设备所支持的 Service。有三种类型的 UUID:16 bit, 32bit, 128 bit。广播中,每种类型类型有有两个类别:完整和非完整的。这样就共有 6 种 AD Type。spa
Local Name: 设备名字,DATA 是名字的字符串。 Local Name 能够是设备的全名,也能够是设备名字的缩写,其中缩写必须是全名的前面的若干字符。code
TX Power Level: TYPE = 0x0A,表示设备发送广播包的信号强度。DATA 部分是一个字节,表示 -127 到 + 127 dBm。orm
带外安全管理(Security Manager Out of Band):TYPE = 0x11。DATA 也是 Flag,每一个 bit 表示一个功能:对象
外设(Slave)链接间隔范围:TYPE = 0x12。数据中定义了 Slave 最大和最小链接间隔,数据包含 4 个字节:blog
服务搜寻:外围设备能够要请中心设备提供相应的 Service。其数据定义和前面的 Service UUID 相似:
Service Data: Service 对应的数据。
公开目标地址:TYPE = 0x17,表示但愿这个广播包被指定的目标设备处理,此设备绑定了公开地址,DATA 是目标地址列表,每一个地址 6 字节。
随机目标地址:TYPE = 0x18,定义和前一个相似,表示但愿这个广播包被指定的目标设备处理,此设备绑定了随机地址,DATA 是目标地址列表,每一个地址 6 字节。
Appearance:TYPE = 0x19,DATA 是表示了设备的外观。
厂商自定义数据: TYPE = 0xFF,厂商自定义的数据中,前两个字节表示厂商 ID,剩下的是厂商本身按照需求添加,里面的数据内容本身定义。
还有一些其余的数据,我这里就不一一列举了,有须要的能够从这个文档查阅 Core Specification Supplement 。
在 Android 可使用 BluetoothAdapter
来发起扫描。基本用法以下:
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { // 解析广播数据 parseAdvData(scanRecord); } }; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 开始扫描设备 mBluetoothAdapter.startLeScan(mLeScanCallback); ... // 中止扫描设备 mBluetoothAdapter.stopLeScan(mLeScanCallback);
当扫描到设备之后,就会回调 onLeScan(...)
,这里的参数 scanRecord
就是广播数据,这里同时包含 广播数据 和 扫描相应数据 (若是有的话),因此长度通常就是 62 字节。
根据上一节的广播数据格式的说明,能够实现解析广播数据函数 parseAdvData(scanRecord);
,下面的代码实现了解析几个我关心的数据:
public static ParsedAd parseData(byte[] adv_data) { ParsedAd parsedAd = new ParsedAd(); ByteBuffer buffer = ByteBuffer.wrap(adv_data).order(ByteOrder.LITTLE_ENDIAN); while (buffer.remaining() > 2) { byte length = buffer.get(); if (length == 0) break; byte type = buffer.get(); length -= 1; switch (type) { case 0x01: // Flags parsedAd.flags = buffer.get(); length--; break; case 0x02: // Partial list of 16-bit UUIDs case 0x03: // Complete list of 16-bit UUIDs case 0x14: // List of 16-bit Service Solicitation UUIDs while (length >= 2) { parsedAd.uuids.add(UUID.fromString(String.format( "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort()))); length -= 2; } break; case 0x04: // Partial list of 32 bit service UUIDs case 0x05: // Complete list of 32 bit service UUIDs while (length >= 4) { parsedAd.uuids.add(UUID.fromString(String.format( "%08x-0000-1000-8000-00805f9b34fb", buffer.getInt()))); length -= 4; } break; case 0x06: // Partial list of 128-bit UUIDs case 0x07: // Complete list of 128-bit UUIDs case 0x15: // List of 128-bit Service Solicitation UUIDs while (length >= 16) { long lsb = buffer.getLong(); long msb = buffer.getLong(); parsedAd.uuids.add(new UUID(msb, lsb)); length -= 16; } break; case 0x08: // Short local device name case 0x09: // Complete local device name byte sb[] = new byte[length]; buffer.get(sb, 0, length); length = 0; parsedAd.localName = new String(sb).trim(); break;