安卓ble蓝牙开发
1首先说一下BLE设备有什么东西:
- 一个BLE终端可以包含多个Service(服务)一个Service可以包含多个Characteristic(特征),一个Characteristic包含一个value和多个Descriptor(描述符),一个Descriptor包含一个Value。其中,我们要注意的是,每一个Service、Characteristic都会有一个uuid,这是一个唯一值,我们接下来的传输数据,将用到这个。每一个Characteristic都有一个Value,我们就是通过改变这个值,来对设备进行交互的。
- 说白了,ble开发主要就是找到指定的Service,Characteristic,然后通过写的Characteristic写入数据给蓝牙设备,通过通知的Characteristic读蓝牙设备返回的数据,通过读的Characteristic读取蓝牙设备的某些值。
这些东西一般蓝牙文档中就会给出来,如下图:
当然如果没有文档可下载nrf master control panel 这个app根据协议文档进行测试,找出设备使用的Service和Characteristic。
2.在android中,对ble设备的操作实际上是对BluetoothGatt的操作,所以我们首先要想办法获取到BluetoothGatt。对蓝牙设备的连接过程也就是获取BluetoothGatt的获取过程,大概有以下几步:
- 添加权限(你也知道啦,想要连接ble设备,肯定得获取手机相关的权限使用权啦)
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
- 扫描Ble设备:想要扫描设备,首先我们先要拿到BluetoothManager,通过BluetoothManager的getAdapter()方法再拿到BluetoothAdapter,然后再通过BluetoothAdapter的startSacn( LeScanCallback)的方法开始扫描设备。值得注意的是,我们还需要实现LeScanCallback的回调方法。(这里可通过蓝牙名称或者蓝牙mac地址找到蓝牙设备)
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
ULog.d(TAG,"onscan");
// BluetoothDevice name=eBody-Scale address=BC:6A:29:26:97:5E
ULog.d(TAG,"BluetoothDevice name=" + device.getName() + " address=" + device.getAddress());
// BluetoothDevice name=eBody-Scale address=BC:6A:29:26:97:5E
//根据蓝牙名称或者mac地址找到对应的蓝牙设备
if (isScanByName){
if (name.equals(device.getName())) {
ULog.d(TAG,"find_device_by_name");
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
if (mScanning) {
scanLeDevice(false);
}
}
}else {
if (mac.equals(device.getAddress())) {
ULog.d(TAG,"find_device_by_mac");
mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
if (mScanning) {
scanLeDevice(false);
}
}
}
}
};
在其回调方法中会有这么一个函数,onLeScan(BluetoothDevice, rssi, byte[])。当中的bluetoothDevice就是我们扫描的设备;rssi是int类型,代表设备的信号强度,是负的,数值越大代表信号强度越大;byte[]这个byte数组就是设备广播的相关数据(在我们项目中,我们是依靠这个广播来判断我们设备时候在充电状态的,各个设备应该都有自己的商定)。
3.建立连接:当扫描到我们所需的设备,就可以开始建立连接了。主要是使用上面所扫描到设备的BluetoothDevice.connectGatt(context, boolean, BluetoothGattCallback)的方法进行连接,这个函数将返回BluetoothGatt的实例,到此,我们就拿到了BluetoothGatt了,就可以进行相关读写数据操作了。(连接之后关闭蓝牙扫描,蓝牙扫描很耗电和占用系统资源)BluetoothGattCallback抽象类,只有9个方法,字面意思就都可以看懂,在处理连接事件上,需要处理方法:比如连接状态:
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//连接成功判断
if (newState == BluetoothProfile.STATE_CONNECTED) {
ULog.d(TAG,"connected");
btMsgCallBack.onConnected();
mBluetoothGatt.discoverServices();
// 连接断开判断
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
ULog.d(TAG,"onDisConnected");
btMsgCallBack.onDisConnected();
gatt.close();
//连接断开延时一秒后继续扫秒蓝牙连接
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isScan)
scanLeDevice(true);
} else if (status != BluetoothGatt.GATT_SUCCESS) { // 连接失败判断
ULog.d(TAG,"onConnectFail");
btMsgCallBack.onConnectFail();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isScan)
scanLeDevice(true);
}
}
其他几个回调就不说了,项目中有注释,大致可以去看下。。。
3.蓝牙连接成功后就可以找出我们指定的服务和通知的特征,写的特征,读的特征。//通知特征设置激活(有些设备特征值有很多个,要一个一个去激活)
BluetoothGattCharacteristic characteristic = btService.getCharacteristic(UUID.fromString(notifyUUid[0]));
if (characteristic != null) {
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(BleConfig.CLIENT_CHARACTERISTIC_CONFIG));
if (descriptor != null) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
// mBluetoothGatt.readCharacteristic(characteristic);
}
成功后,蓝牙设备如果有数据就会回调onCharacteristicChanged(),我们这里接受处理数据即可(有的蓝牙设备还需要发送一条命令给他成功后才会返回数据)
/**
* 返回数据。
*/
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// 数据
ULog.d(TAG,"onCharacteristicChanged");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < characteristic.getValue().length; i++) {
// sb.append (characteristic.getValue()[i] & 0xff).append(" ") ; //变成int字符串数据,方便处理
//这里也可变为16进制数据,方便对协议
sb.append (String.format("%02X ",characteristic.getValue()[i] & 0xff)).append(" ") ;
}
btMsgCallBack.onReceiveMessage(sb.toString());
}
};
4.由于项目中经常用到,所以自己封装了一个,调用起来非常方便,代码已上传github.
//蓝牙辅助类初始化:
btMsgListener = BlueToothMessageListener.create(this).setCallback(btMsgCallBack)
.setServiceUUid(DEVICE_SERVICE_UUID) //必须要
.setNotifyUUid(NOTIFY_CHARACTERISTICSUUID)//必须要
.setReadUUid(READ_CHARACTERISTICSUUID)
.setWriteUUid(WRITE_CHARACTERISTICSUUID)
.setBleName(DEVICE_NAME_SPO2) //蓝牙名称和mac地址二选一
.setBleMac(DEVICE_MAC_SPO2);
github项目地址[bleUtils](https://github.com/tangchao5206/BleUtils本人水平有限,如果觉得还行,记得点赞.