蓝牙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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容