您当前的位置:首页 > 互联网教程

如何使用android原生BLE蓝牙进行操作

发布时间:2025-05-12 00:00:46    发布人:远客网络

如何使用android原生BLE蓝牙进行操作

一、如何使用android原生BLE蓝牙进行操作

在处理物联网项目中,我们采用了BLE(低功耗蓝牙)技术,该技术支持Android 4.3及以上版本的手机。BLE是基于蓝牙4.0标准的,它有以下特点:

- BLE仅使用三个广播通道,相较于传统蓝牙技术的16至32个频道,功耗更低。

-广播间隔时间从传统的22.5毫秒缩短至0.6至1.2毫秒。

-相较于传统蓝牙的2至10米传输距离,BLE的有效传输距离可达到60至100米。

- BLE使用AES-128 CCM加密算法对数据包进行加密和认证。

关于BLE蓝牙的更深入解析,您可以参考我的博客系列:“BLE4.0教程一蓝牙协议连接过程与广播分析”。

在Android应用程序中使用BLE蓝牙功能,首先需要添加必要的权限,并执行以下步骤:

-获取BluetoothManager实例:`BluetoothManager bluetoothManager=(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);`

-获取BluetoothAdapter实例:`bluetoothAdapter= bluetoothManager.getAdapter();`

接下来,需要检查蓝牙是否已打开。如果未打开,则应提示用户打开蓝牙设置,除了魅族手机外,其他设备通常会显示系统设置选项。

在扫描设备的过程中,通常会设置设备过滤原则,以确保只搜索到需要的设备。例如,可以通过检查`scanRecord`中的`beacon`类型来过滤设备。

建立连接后,需要及时进行读写操作。关键问题是在连接成功后的两秒内必须向设备写入一个值,否则设备可能会断开连接。操作包括:

-写入特征值:`gatt.writeCharacteristic(mCurrentcharacteristic);`

-读取特征值:`gatt.readCharacteristic(characteristic);`

-设置特征值通知:`bluetoothGatt.setCharacteristicNotification(data, true);`

在实际工作中,我们通常会使用第三方库来简化BLE操作,例如BlueToothKit。更多详细信息,请参阅我的另一篇博客:“android蓝牙入门知识和优秀蓝牙第三方库BluetoothKit的使用”。

二、android 蓝牙BLE 该怎么搞,我是想搞个中心和周边 ,推送消息

Android4.3规范了BLE的API,但是直到目前的4.4,还有些功能不完善。

在BLE协议中,有两个角色,周边(Periphery)和中央(Central);周边是数据提供者,中央是数据使用/处理者;在iOS SDK里面,可以把一个iOS设备作为一个周边,也可以作为一个中央;但是在Android SDK里面,直到目前最新的Android4.4.2,Android手机只能作为中央来使用和处理数据;那数据从哪儿来?从BLE设备来,现在的很多可穿戴设备都是用BLE来提供数据的。

一个中央可以同时连接多个周边,但是一个周边某一时刻只能连接一个中央。

大概了解了概念后,看看Android BLE SDK的四个关键类(class):

a)BluetoothGattServer作为周边来提供数据;BluetoothGattServerCallback返回周边的状态。

b)BluetoothGatt作为中央来使用和处理数据;BluetoothGattCallback返回中央的状态和周边提供的数据。

因为我们讨论的是Android的BLE SDK,下面所有的BluetoothGattServer代表周边,BluetoothGatt代表中央。

一.创建一个周边(虽然目前周边API在Android手机上不工作,但还是看看)

a)先看看周边用到的class,蓝色椭圆

每一个周边BluetoothGattServer,包含多个服务Service,每一个Service包含多个特征Characteristic。

1.new一个特征:character= new BluetoothGattCharacteristic(

UUID.fromString(characteristicUUID),

BluetoothGattCharacteristic.PROPERTY_NOTIFY,

BluetoothGattCharacteristic.PERMISSION_READ);

2.new一个服务:service= new BluetoothGattService(UUID.fromString(serviceUUID),

BluetoothGattService.SERVICE_TYPE_PRIMARY);

3.把特征添加到服务:service.addCharacteristic(character);

4.获取BluetoothManager:manager=(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

5.获取/打开周边:BluetoothGattServer server= manager.openGattServer(this,

new BluetoothGattServerCallback(){...});

6.把service添加到周边:server.addService(service);

7.开始广播service:Google还没有广播Service的API,等吧!!!!!所以目前我们还不能让一个Android手机作为周边来提供数据。

二.创建一个中央(这次不会让你失望,可以成功创建并且连接到周边的)

a)先看看中央用到的class,蓝色椭圆

三、android蓝牙BLE(三) —— 广播

​在蓝牙开发中,有些情况是不需要连接的,只要外设广播自己的数据即可,例如苹果的 ibeacon。自 Android 5.0更新蓝牙API后,手机可以作为外设广播数据。

其中广播包是每个外设都必须广播的,而响应包是可选的。每个广播包的长度必须是 31个字节,如果不到 31个字节,则剩下的全用 0填充补全,这部分的数据是无效的

广播包中包含若干个广播数据单元,广播数据单元也称为 AD Structure。

广播数据单元=长度值Length+ AD type+ AD Data。

长度值 Length只占一个字节,并且位于广播数据单元的第一个字节。

概念的东西有些抽象,先看看下面的广播报文:

​ 0x代表这串字符串是十六进制的字符串。两位十六进制数代表一个字节。因为两个字符组成的十六进制字符串最大为 FF,即255,而Java中byte类型的取值范围是-128到127,刚好可以表示一个255的大小。所以两个十六进制的字符串表示一个字节。

​继续查看报文内容,开始读取第一个广播数据单元。读取第一个字节: 0x07,转换为十进制就是7,即表示后面的7个字节是这个广播数据单元的数据内容。超过这7个字节的数据内容后,表示是一个新的广播数据单元。

​而第二个广播数据单元,第一个字节的值是 0x16,转换为十进制就是22,表示后面22个字节为第二个广播数据单元。

​在广播数据单元的数据部分中,第一个字节代表数据类型(AD type),决定数据部分表示的是什么数据。(即广播数据单元第二个字节为AD type)

​这bit 1~7分别代表着发送该广播的蓝牙芯片的物理连接状态。当bit的值为1时,表示支持该功能。

蓝牙广播的数据格式大致讲了一下,有助于下面的广播操作的理解。

先看看广播设置( AdvertiseSettings)如何定义:

(1)、通过 AdvertiseSettings.Builder#setAdvertiseMode()设置广播模式。其中有3种模式:

(2)、通过 AdvertiseSettings.Builder#setAdvertiseMode()设置广播发射功率。共有4种功率模式:

(3)、通过 AdvertiseSettings.Builder#setTimeout()设置持续广播的时间,单位为毫秒。最多180000毫秒。当值为0则无时间限制,持续广播,除非调用 BluetoothLeAdvertiser#stopAdvertising()停止广播。

(4)、通过 AdvertiseSettings.Builder#setConnectable()设置该广播是否可以连接的。

之前说过,外设必须广播广播包,扫描包是可选。但添加扫描包也意味着广播更多得数据,即可广播62个字节。

可见无论是广播包还是扫描包,其广播的内容都是用 AdvertiseData类封装的。

(1)、 AdvertiseData.Builder#setIncludeDeviceName()方法,可以设置广播包中是否包含蓝牙的名称。

(2)、 AdvertiseData.Builder#setIncludeTxPowerLevel()方法,可以设置广播包中是否包含蓝牙的发射功率。

(3)、 AdvertiseData.Builder#addService UUID(Parcel UUID)方法,可以设置特定的 UUID在广播包中。

(4)、 AdvertiseData.Builder#addServiceData(Parcel UUID,byte[])方法,可以设置特定的 UUID和其数据在广播包中。

(5)、 AdvertiseData.Builder#addManufacturerData(int,byte[])方法,可以设置特定厂商Id和其数据在广播包中。

​从 AdvertiseData.Builder的设置中可以看出,如果一个外设需要在不连接的情况下对外广播数据,其数据可以存储在 UUID对应的数据中,也可以存储在厂商数据中。但由于厂商ID是需要由Bluetooth SIG进行分配的,厂商间一般都将数据设置在厂商数据。

另外可以通过 BluetoothAdapter#setName()设置广播的名称

先看一个例子,我们分别在广播包和扫描包中设置 AdvertiseData.Builder的每一种广播报文参数,得到一下报文内容:

(1)、Type= 0x01表示设备LE物理连接。

(3)、Type= 0x03表示完整的16bit UUID。其值为0xFFF7。

(4)、Type= 0xFF表示厂商数据。前两个字节表示厂商ID,即厂商ID为0x11。后面的为厂商数据,具体由用户自行定义。

(5)、Type= 0x16表示16 bit UUID的数据,所以前两个字节为 UUID,即 UUID为0xF117,后续为 UUID对应的数据,具体由用户自行定义。

最后继承 AdvertiseCallback自定义广播回调。

初始化完毕上面的对象后,就可以进行广播:

​广播主要是通过 BluetoothLeAdvertiser#startAdvertising()方法实现,但在之前需要先获取 BluetoothLeAdvertiser对象。

BluetoothLeAdvertiser对象存在两个情况获取为Null:

所以在调用 BluetoothAdapter#getBluetoothLeAdvertiser()前,需要先调用判断蓝牙已开启,并判断在 BluetoothAdapter中获取的 BluetoothLeAdvertiser是否为空(测试过某些华为手机 mBluetoothAdapter.isMultipleAdvertisementSupported()为 false,但是能发送ble广播)。

​与广播成对出现就是 BluetoothLeAdvertiser.stopAdvertising()停止广播了,传入开启广播时传递的广播回调对象,即可关闭广播:

​虽然通过广播告知外边自身拥有这些Service,但手机自身并没有初始化Gattd的Service。导致外部的中心设备连接手机后,并不能找到对应的 GATT Service和获取对应的数据。

创建 BluetoothGattService时,传入两个参数: UUID和Service类型:

​我们都知道Gatt中, Service的下一级是 Characteristic, Characteristic是最小的通信单元,通过对 Characteristic进行读写操作来进行通信。

​特征属性表示该 BluetoothGattCharacteristic拥有什么功能,即能对 BluetoothGattCharacteristic进行什么操作。其中主要有3种:

权限属性用于配置该特征值所具有的功能。主要两种:

Characteristic下还有 Descriptor,初始化 BluetoothGattDescriptor时传入: Descriptor UUID和权限属性

为 Service添加 Characteristic,为 Characteristic添加 Descriptor:

​通过蓝牙管理器 mBluetoothManager获取 Gatt Server,用来添加 Gatt Service。添加完 Gatt Service后,外部中心设备连接手机时,将能获取到对应的 GATT Service和获取对应的数据

​定义 Gatt Server回调。当中心设备连接该手机外设、修改特征值、读取特征值等情况时,会得到相应情况的回调。

最后开启广播后,用nRF连接后看到的特征值信息如下图所示:(加多了一个只能都的特征值)