蓝牙BLE
前提说明:
去年有个项目是关于通过手机控制平衡车(小车)来实现加速、锁车等一系列操作,其实就是通过蓝牙来控制的,项目做了很长时间了,一直想把有关蓝牙的这个模块自己记录下来方便自己查看,也算知识的一个积累吧。
自己第一次接触蓝牙开发的项目,对蓝牙这块不太熟悉,然后经过一番搜索找到的都是以下的蓝牙开发,自己也开始做起来,以为很简单,直接上代码了。
步骤(不支持BLE):
1、搜索蓝牙设备;
<pre>
// 1、获取系统的蓝牙适配器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 2、搜索蓝牙设备(搜索过程中会发出广播,可以注册广播,从广播中搜索到我们需要的设备)
bluetoothAdapter.startDiscovery();
</pre>
简单说明:
- bluetoothAdapter.startDiscovery():是异步方法,它会对其他蓝牙设备进行搜索,持续时间为12秒;
- 搜索过程其实是在System Service中进行,我们可以通过cancelDiscovery()方法来停止这个搜索;
- 在系统搜索蓝牙设备的过程中,系统可能会发送以下三个广播:
ACTION_DISCOVERY_START(开始搜索)
ACTION_DISCOVERY_FINISHED(搜索结束)
ACTION_FOUND(找到设备) - ACTION_FOUND这个才是我们想要的,这个Intent中包含两个extra fields:EXTRA_DEVICE、EXTRA_CLASS,包含的分别是BluetoothDevice和BluetoothClass,BluetoothDevice中的EXTRA_DEVICE就是我们搜索到的设备对象;确认搜索到设备后,我们可以从得到的BluetoothDevice对象中获得设备的名称和地址;
2、获取设备、配对、连接;
<pre>
// 3、注册系统广播,得到我们需要的设备
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
receiver = new BluetoothReceiver();
registerReceiver(receiver, filter);
// 广播
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (isLock(device)) {
devices.add(device.getName());
}
deviceList.add(device);
}
showDevices();
}
}
// 4、获取设备,并配对(只有配对好之后才能连接)
// 在连接设备之前停掉搜索设备,否则连接可能会变得非常慢并且容易失败
bluetoothAdapter.cancelDiscovery();</pre>
3、连接后获取输入输出流和蓝牙设备进行通信;
4、断开连接、关闭蓝牙;
<pre>
这两个步骤不说明,因为到这里我就发现问题了,
开发文档上说明找到uuid,在通道上发送和接收命令,
我却连个通道都没有见到过,找了很久都是以上这样的文章,
后来再从头阅读文档,发现了蓝牙BLE,
呵呵~,懵逼了吧,再重新去搜索,又是另一番景象了。
</pre>
蓝牙BLE介绍(摘抄)
- 在BLE协议中,有两个角色,周边(Periphery)和中央(Central);
- 周边是数据提供者,中央是数据使用/处理者;
- 在Android SDK里面,在Android4.4.2,Android手机只能作为中央来使用和处理数据;那数据从哪儿来?从BLE设备来,现在的很多可穿戴设备都是用BLE来提供数据的。
- 一个中央可以同时连接多个周边,但是一个周边某一时刻只能连接一个中央。
蓝牙BLE相关API:
- BluetoothAdapter(系统默认蓝牙适配器)
- BluetoothManager(蓝牙管理类)
- BluetoothDevice
- BluetoothGattServer作为周边来提供数据、BluetoothGattServerCallback返回周边的状态;
- BluetoothGattService(服务):每一个周边BluetoothGattServer,包含多个服务Service;
- BluetoothGattCharacteristic(特征):每一个Service包含多个特征Characteristic;
- BluetoothGatt作为中央来使用和处理数据、BluetoothGattCallback返回中央的状态和周边提供的数据;
步骤:
1、开启手机蓝牙、搜索设备、取消搜索设备和以上相同;
2、蓝牙连接,发现服务、读写数据全部在回调里可以监听到:
<pre>
mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
// 回调
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
final String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED)
{
intentAction = ACTION_GATT_CONNECTED;
// 开启查找服务
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 断开连接
// Log.d(TAG, "Disconnected from GATT server.");
intentAction = ACTION_GATT_DISCONNECTED;
}
if (status == 133) { // 蓝牙连接自动断开的原因->需要清除所有的连接,重连机制
// Log.e(TAG, "蓝牙连接自动断开,status=" + status);
close();
sleep();
connect(mBluetoothDeviceAddress);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
// Log.d(TAG, "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
// 解析服务
displayGattServices(getSupportedGattServices());
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
}
@Override public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
}
};
</pre>
解析服务,因为我是在FFE0、FFE5通道上操作的,所以只需要找到这两个通道即可:
<pre>
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) {
return;
}
for (BluetoothGattService gattService : gattServices) {
//-----Service的字段信息-----//
// Log.e(TAG,"-->service uuid:"+gattService.getUuid());
String serviceUUID = gattService.getUuid().toString().toUpperCase();
if (serviceUUID.contains(DefaultConstant.SERVICE_UUID_FFE0))
{
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());
String charUUID = gattCharacteristic.getUuid().toString().toUpperCase();
if (charUUID.contains(DefaultConstant.CHAR_UUID_FFE4)) {
mCharacFFE4 = gattCharacteristic;
// 打开通道,只有打开了通道,才能在通道上进行读和写
mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
break;
}
}
}
if (serviceUUID.contains(DefaultConstant.SERVICE_UUID_FFE5))
{
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
// Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());
String charUUID = gattCharacteristic.getUuid().toString().toUpperCase();
if (charUUID.contains(DefaultConstant.CHAR_UUID_FFE9)) {
mCharacFFE9 = gattCharacteristic;
break;
}
}
}
if (mCharacFFE4 != null && mCharacFFE9 != null) {
break;
}
}
}
</pre>
实际的值如下所示:
实际打印的服务和特征值.png
3、读写完成后记得关闭连接;
总结:最后总算顺利的完成了项目的开发,但是也还是遇到了很多的问题,这里自己做下记录:
- 某些手机不支持蓝牙4.0设备,记得做事先的判断;(一般4.3及以上手机才支持)
- 搜索蓝牙设备过程很缓慢,且项目要求一直都需要开启搜索,但是我还是在连接成功后取消了搜索,一个是为了加快蓝牙的连接,一个也是为了省电,毕竟是很耗电的,既然已经连接了为啥还要一直在搜索呢;为了让用户视觉上觉得搜索很快,记得搜索到一个设备就立即展示,否则可能搜到你需要的设备会等很长时间,这样也能起到缓冲作用;
-
蓝牙的连接,最好能对非BLE设备或者不是你需要的设备连接的时候做迅速的处理,因为我试过,故意去连接某些电脑或者不知名设备的时候会有一个很长时间的响应,最后我自己处理是找到了BluetoothDevice的type
bluetooth device type.png简单的判断了下,是否是BLE设备,直接断开不去连接;
- 蓝牙的连接、读写过程我都是通过发送广播进行处理,项目需要在所有页面都能监听到设备的连接的状态以及在某些页面需要进行读写,干脆我改成了单例+广播的形式,这里要注意内存的泄漏哦;
-
蓝牙的断开不彻底问题:断开小车的时候总是断开的不彻底,导致别人手机搜索不到,自己也不能再去搜索连接,据说是小车有反应的时间,经过多次测试后,发现每次断开不彻底会返回133状态,至今没弄明白,只能这样处理了,表示很无奈,彻底关闭、沉睡500毫秒,再次重连;
彻底断开处理.png
或者再多加层判断:
彻底断开重连处理.png 总算起了一定的效果了。。。 - 还漏写了一个蓝牙读写全是二进制数据,这里面的转换也要注意,避免溢出;
好不容易鼓起自己去写了,一定会再接再励,代码自己会抽时间把之前写在项目中的代码抽离出来写个demo放github上去,知识需要沉淀。
添加github demo地址,写的较简单,后续逐渐完善https://github.com/ywqian/BluetoothDemo;
参考和好的链接文章:
http://www.pinnace.cn/bluetooth/tech/1940.shtml
http://www.cnblogs.com/savagemorgan/p/3722657.html
一个不错的github列子:https://github.com/alt236/Bluetooth-LE-Library---Android