前言
前面我们介绍了蓝牙ble的扫描解析、连接与通信。本次我们来介绍一下android如何向外发送广播数据。
在正式开始我们的蓝牙广播介绍之前,我们先回顾一下,在我们的蓝牙扫描篇,我们使用了nRF来进行蓝牙扫描,如下如所示:
点开其中的一项我们发现
里面多次提及到了UUID,那么什么是UUID呢?这里的UUID和广播包里面的ADTYPE有什么关系呢?
蓝牙ble:UUID
首先UUID是一个种数据类型,它由128bit,换算起来就是32位16进制的字符串组成的一个数据,在android当中。如下所示:
// 32位的字符串数据
0000-XXXX-AAAA-BBBB-CCCC-DDDD-EEEE-FFFF
蓝牙使用GATT的通信协议,GATT里面的数据交换就是通过UUID的格式进行通信的。在扫描解析篇,我们涉及到两个重要的概念,Service和Character,其实两者的数据表示就是以上的方式。其中,蓝牙SIG联盟为我们规定了一个基础的蓝牙UUID格式。
0x 0000 xxxx -0000-1000-8000-00805F9B34FB
基础的蓝牙UUID格式主要分为
- 0x:代表16进制
- 0000:在默认情况下,均为0,当然开发商也可以自己定义
- xxxx:这里包含了蓝牙SIG联盟标准制定的协议特征(包括服务类型、厂商标记等等),也提供了部分供开发者自己定义
- 0000-1000-8000-00805F9B34FB(后面这串是蓝牙固定的尾部数据) 因此,我们可以发现,其实供我们自己定义的部分,就只有前面的4字节的数据,也就是32位。因此,当我们所说的16位的UUID的时候,指的是xxxx部分,当我们说32位的时候,通常指的是0000xxxx的部分。
了解到这里是否还是有点抽象?没关系,我们再来看一组真实的例子:
有一个设备,里面有一个心率服务,心率服务的16位UUID为0x180D,完整数据如下: 0x0000180D-0000-1000-8000-00805F9B34FB 在心率服务下面,又有一个特征值为心率测量值,心率测量值的16位UUID位0x2A73,完整数据如下: 0x00002A37-0000-1000-8000-00805F9B34FB
Service: Heart Rate Measurement
|-- Characteristic: Heart Rate Measurement Value
| |-- Properties: Read, Notify
所以,他们的格式虽然是一样的,但是存在着从属关系。
那么AdType跟UUID有什么关系呢?本来这两者没有什么重要的联系,主要是因为本章要介绍一下发送广播包,所以感觉讲讲清楚会让大家好理解一点。
我们通过nRF看到的广播包数据,有很多项数据,是一种格式。因为在广播里面不仅有service、character。还有蓝牙的昵称,用户的自定义的数据。因此ADType就是蓝牙Sig联盟用来规定后面跟着的数据是什么类型的数据。 如下图:
其中Service就是ADData部分(通常不太会将character暴露在广播包里面)。
关键的一些type数据类型如下所示: 以下是一些蓝牙低功耗 (Bluetooth Low Energy, BLE) 广播中的关键数据类型值(type):
- 0x01:Flags
- 0x02:Incomplete List of 16-bit Service Class UUIDs
- 0x03:Complete List of 16-bit Service Class UUIDs
- 0x04:Incomplete List of 32-bit Service Class UUIDs
- 0x05:Complete List of 32-bit Service Class UUIDs
- 0x06:Incomplete List of 128-bit Service Class UUIDs
- 0x07:Complete List of 128-bit Service Class UUIDs
- 0x08:Shortened Local Name
- 0x09:Complete Local Name
- 0x0A:Tx Power Level
- 0x16:Service Data
- 0x19:Appearance
- 0x1B:LE Bluetooth Device Address
- 0x1C:LE Role
- 0x20:Service Data - 32-bit UUID
- 0x21:Service Data - 128-bit UUID
- 0xFF:Manufacturer Specific Data
以上述的心跳服务距离。如果我们想要在广播包里面体现我们的心率服务(Services),蓝牙名称为“health watch”,那么我们的广播包怎么体现?如下:
- ADLength:0x03(代表后续跟了3个字节的数据)
- ADType:0x02(代表不完整的16bits的服务uuid值的类型标记)
- ADData:0x0D,0x18(16bits的服务uuid,小端格式)
- ADLength:0x0D(代表后续跟了13个字节的数据)
- ADType:0x09(代表完整的本地蓝牙名称)
- ADData:“health watch”(将“health watch”转换成的16进制数据)
看到这里大家就明白了,我们的广播包里面的数据的表现形式,以及uuid所代表的意思了。(更加详细的ADType、Service-UUID、Charachter-UUID)可以查询对应的官方协议文档。
说了这么多,那么android如何对外发送蓝牙广播包呢?
蓝牙BluetoothLeAdvertiser API
val build = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER)
.setConnectable(false)// 设置是否可连接
.setDiscoverable(true)// 设置是否可以被发现
.setTimeout(60_000)// 设置广播的时间
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)// 设置功率
.build()
val advertiseData = AdvertiseData.Builder()
.addServiceUuid("xxxx") // 添加服务,对应上面讲述的UUID
.addServiceData("xxxx"), byteArrayOf())// 给对应的UUID服务添加服务数据
.addManufacturerData(0, byteArrayOf())// 制造商自定义的数据(manufacturerId是统一分配的)
.build()
val callback = object : AdvertiseCallback(){
override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) {
// 开始发送成功
}
override fun onStartFailure(errorCode: Int) {
// 开始发送失败
}
}
bluetoothLeAdvertiser.startAdvertising(build, advertiseData,callback)
当然,以上的方式也尽量使用消费者-监听者的模式进行一些封装是最好的。
注意事项
- 在实际使用当中,蓝牙的广播包的数据实际是有限的,因此不太适合传输大量的数据,如果需要使用蓝牙广播包进行大数据传输的话,就需要在业务层上实现(类似UDP实现TCP的可靠性传输一样)
- 在实际使用发现,使用serviceUUID的方式发送数据,比ManufacturerData要高效的多,这个可能是因为ManufacturerData组装更加耗时。因为ManufacturerData是蓝牙响应包里面的数据,而serviceUUID是扫描里面的。
- 在使用蓝牙广播的时候,要注意里面内容一定要按照规范使用。