Android BLE开发——Android手机与BLE终端通信初识
蓝牙BLE官方Demo下载地址: http://download.csdn.net/detail/lqw770737185/8116019
参考博客地址: http://www.eoeandroid.com/thread-563868-1-1.html?_dsign=843d16d6
设备:MX5手机一台,农药残留检测仪一台(BLE终端设备)
目的:实现手机控制仪器,如发送打印指令,仪器能进行打印操作等
关于如何打开蓝牙,配置相关权限,搜索BLE设备等步骤网上有很多资料,这里就不多做作解释了,本文主要讲通信方面的内容。需要注意的是搜索BLE设备的结果是异步返回的,在BluetoothAdapter.LeScanCallback这个回调中返回,并且搜索过程是一个非常耗电的过程,所以我们应该做好相应处理,例如可以让它搜索10s就停止搜索等。
在我们理解Android设备与BLE终端设备通信过程之前,我们需要来先了解几个类:
BluetoothGatt:BluetoothGatt 是我们用的最多,也是我们最重要的一个类,为了尽可能通俗的理解,这里我们可以把它看成Android手机与BLE终端设备建立通信的一个管道,只有有了这个管道,我们才有了通信的前提。
BluetoothGattService:蓝牙设备的服务,在这里我们把BluetoothGattService比喻成班级。而Bluetoothdevice我们把它比喻成学校,一个学校里面可以有很多班级,也就是说我们每台BLE终端设备拥有多个服务,班级(各个服务)之间通过UUID(唯一标识符)区别。
BluetoothGattCharacteristic: 蓝牙设备所拥有的特征,它是手机与BLE终端设备交换数据的关键,我们做的所有事情,目的就是为了得到它。在这里我们把它比喻成学生,一个班级里面有很多个学生,也就是说我们每个服务下拥有多个特征,学生(各个特征)之间通过UUID(唯一标识符)区别。
总结:当我们想要用手机与BLE设备进行通信时,实际上也就相当于我们要去找一个学生交流,首先我们需要搭建一个管道,也就是我们需要先获取得到一个BluetoothGatt,其次我们需要知道这个学生在哪一个班级,学号是什么,这也就是我们所说的serviceUUID,和charUUID。这里我们还需要注意一下,找到这个学生后并不是直接和他交流,他就好像一个中介一样,在手机和BLE终端设备之间帮助这两者传递着信息,我们手机所发数据要先经过他,在由他传递到BLE设备上,而BLE设备上的返回信息,也是先传递到他那边,然后手机再从他那边进行读取。
Android手机与BLE终端设备通信结果都是以回调的形式返回,如下是几个常见的返回回调(可见于官方Demo的BluetoothLeservice类):

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { //连接状态改变的回调
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// 连接成功后启动服务发现
Log.e("AAAAAAAA", "启动服务发现:" + mBluetoothGatt.discoverServices());
}
}; //发现服务的回调
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "成功发现服务");
}else{
Log.e(TAG, "服务发现失败,错误码为:" + status);
}
}; //写操作的回调
public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "写入成功" +characteristic.getValue());
}
}; //读操作的回调
public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "读取成功" +characteristic.getValue());
}
} //数据返回的回调(此处接收BLE设备返回数据)
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
};
};
}

一、连接蓝牙BLE终端设备
在我们打开蓝牙,扫描发现想要连接的设备后,接下来要做的,当然就是去连接它了,连接方法很简单,我们一句代码就可以实现了(如下)。可以发现,当我们开始连接BLE终端设备的时候,连接方法就自动就帮我们返回了一个BluetoothGatt对象了,前面我们说到BluetoothGatt是我们最重要的一个类,它相当于一个管道,是我们建立通信的前提:(这里面有三个参数,第一个参数是上下文对象,第二个参数是是否自动连接,这里设置为false,第三个参数就是上面的回调方法)
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
连接成功与否都会通过下面这个回调来告诉我们:

// 连接状态改变的回调
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
//代表连接成功,此处我们可以发送一个广播回去告诉activity已成功连接
if (newState == BluetoothProfile.STATE_CONNECTED) {
//连接成功后启动服务发现
Log.e("AAAAAAAA", "启动服务发现:" + mBluetoothGatt.discoverServices());
}
}

二、启动服务发现
连接成功后,我们就要去寻找我们所需要的服务,这里需要先启动服务发现,使用一句代码即可:
mBluetoothGatt.discoverServices() ;
启动服务发现,它的结果也是通过回调函数返回:

// 发现服务的回调
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//成功发现服务后可以调用相应方法得到该BLE设备的所有服务,并且打印每一个服务的UUID和每个服务下各个特征的UUID
if (status == BluetoothGatt.GATT_SUCCESS) {
List<BluetoothGattService> supportedGattServices =mBluetoothGatt.getServices();
for(int i=0;i<supportedGattServices.size();i++){
Log.e("AAAAA","1:BluetoothGattService UUID=:"+supportedGattServices.get(i).getUuid());
List<BluetoothGattCharacteristic> listGattCharacteristic=supportedGattServices.get(i).getCharacteristics();
for(int j=0;j<listGattCharacteristic.size();j++){
Log.e("a","2: BluetoothGattCharacteristic UUID=:"+listGattCharacteristic.get(j).getUuid());
}
}
} else {
Log.e("AAAAA", "onservicesdiscovered收到: " + status);
}
}

我们也可以通过调用下面方法得知每个特征所具有的属性:可读或者可写或者具备通知功能或者都具备等

// 循环遍历服务以及每个服务下面的各个特征,判断读写,通知属性
for (BluetoothGattService gattService :supportedGattServices) {
List<BluetoothGattCharacteristic> gattCharacteristics =supportedGattServices.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
int charaProp = gattCharacteristic.getProperties();
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
// Log.e("nihao","gattCharacteristic的UUID为:"+gattCharacteristic.getUuid());
// Log.e("nihao","gattCharacteristic的属性为: 可读");
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
// Log.e("nihao","gattCharacteristic的UUID为:"+gattCharacteristic.getUuid());
// Log.e("nihao","gattCharacteristic的属性为: 可写");
}
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
// Log.e("nihao","gattCharacteristic的UUID为:"+gattCharacteristic.getUuid()+gattCharacteristic);
// Log.e("nihao","gattCharacteristic的属性为: 具备通知属性");
} }
}

三、获取Characteristic
之前我们说过,我们的最终目的就是获取Characteristic来进行通信,正常情况下,我们可以从硬件工程师那边得到serviceUUID和characteristicUUID,也就是我们所比喻的班级号和学号,以此来获得我们的characteristic,但如果我们无法得知这两个所需的UUID时,我们也可以通过上一步的方法来获取(打印所有特征UUID,取出自己想要的特征)。这次试验我就是通过此方法获得,但是通过打印日志发现,虽然我这边的BLE终端它每个服务下的所有特征都具有可读可写可通知属性,但是只有characteristicUUID="0000ffe1-0000-1000-8000-00805f9b34fb"这个特征UUID能进行通信,它对应的serviceUUID="0000ffe0-0000-1000-8000-00805f9b34fb",暂且就先用这个,反正一个能用就行。假设我们在知道serviceUUID和characteristicUUID的前提下,我们就可以通过下面代码获取相应特征值:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
BluetoothGattCharacteristic characteristic= service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
四、开始通信
我们在得到一个相应的特征以后,接下来就可以开始读写操作进行通信了。
a.读操作,读操作比较简单,只需将相应的特征值传入即可得到该特征值下的数据,如下代码:
mBluetoothGatt.readCharacteristic(characteristic);
读取的结果通过onCharacteristicRead回调返回:(通过characteristic.getValue()就可以得到读取到的值了)

// 读操作的回调
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "读取成功" +characteristic.getValue());
}
}

b.写操作,写操作是我们的重点,我们可以通过向characteristic写入指令(发送指令)以此来达到控制BLE终端设备的目的:
//将指令放置进特征中
characteristic.setValue(new byte[] {0x7e, 0x14, 0x00, 0x00,0x00,(byte) 0xaa});
//设置回复形式
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
//开始写数据
mBluetoothGatt.writeCharacteristic(chharacteristic);
注:与仪器通信,我们这里发送的是16进制的数据,发送的时候需要先将其装载到byte[]数组中,例如我发送 7e 14 00 00 00 aa这个指令,我需要把它转化为ew byte[] {0x7e, 0x14, 0x00, 0x00,0x00,(byte) 0xaa }这样去发送,因为BLE传输过程每次最大只能传输20个字节,所以如果发送的指令大于20字节的话要分包发送,例如现在要发送28个字节的,可以先write(前20个字节),开启线程sleep(几十毫秒)后在write(后面8个字节)。
五、BLE终端设备返回数据
当我们向BLE终端设备写入指令时,如果写入成功并且指令也正确,我们就会获得相应的响应指令,在下面这个回调中我们可以得到BLE设备返回回来的响应指令(通过characteristic.getValue()取出返回数据):
// 数据返回的回调(此处接收机器返回数据并作处理)
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.e("AAAAAAAA",characteristic.getValue());
};
接收到返回数据的前提是我们设置了该特征具有Notification功能,所以完整的写操作代码应该是这样的(注意设置特征Notification的代码要放在最前):

mBluetoothGatt.setCharacteristicNotification(characteristic,true)
//将指令放置进来
characteristic.setValue(new byte[] {0x7e, 0x14, 0x00, 0x00,0x00,(byte) 0xaa});
//设置回复形式
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
//开始写数据
mBluetoothGatt.writeCharacteristic(chharacteristic);

以上就是自己对Android手机与BLE设备通信一些初步认识,如果有哪里说的不正确,还请指正。
Android BLE开发——Android手机与BLE终端通信初识的更多相关文章
- android的开发 华为手机上不显示menu键
android的开发,华为手机上不显示menu键解决办法: 在AndroidManifest.xml中讲targetSdkVersion改为9. <uses-sdk android:minSdk ...
- android studio 开发android app 真机调试
大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...
- 码农人生——从未学过Android如何开发Android App 案例讲解-第002期案例
标题有点晃眼,本次分享是002期博文的实践故事,不会有任何代码.也不会教别人android 如何开发,类似博文已经有大批大批,而且还会有陆陆续续的人写,我写的文章,主要是经验之谈,希望总结出的一些方法 ...
- 用Android Studio 开发Android应用
目前AndroidStudio已经到了1.2版本了,我用了下,觉得还蛮好的,有些自动生成的资源,它会自动帮你管理.下面开始列一下,我的开发环境搭配.在开始前,你得有个VPN,可者代理.嗯.不然你下不了 ...
- [Hybrid App]--Android混合开发,Android、Js的交互
AndroidJs通信 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !imp ...
- Android Studio开发Android应用如何签名
1.使用jdk自带的工具生成keystore 使用cmd命令行进入到jdk的bin目录(比如:C:\Program Files\Java\jdk1.7.0_01\bin) 运行如下命令: C:\Pro ...
- Android多媒体开发-- android中OpenMax的实现整体框架
1.android中用openmax来干啥? android中的AwesomePlayer就 是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码.通过他的组 ...
- Android Studio开发Android问题集【持续更新】
问题一:emulator:ERROR:This AVD's configuration is missing a kernel file!! 答:打开Android SDK Manager,查看相应的 ...
- Android NDK开发 Android Studio使用新的Gradle构建工具配置NDK环境(一)
本文主要讲述了如何如何在Android Studio使用新的Gradle构建工具配置NDK环境,现在把相关的步骤整理出来分享给Android程序员兄弟们,希望给他们在配置NDK环境时带来帮助. 从An ...
随机推荐
- 限制sqlserver最大内存后无法连接-EXEC sp_configure max server memory
在sql server 中设置了过小的 "max server memory"最大内存后,sqlserver可启动,但是无法连接. 网络上流行的"sqlserver 内存 ...
- hdu 4445
今天模拟了一场去年金华的现场赛: 我和小珺两人出了5个题,感觉还可以: 不过这次的题目确实比较简单: 这个题目感觉不错,不难,以前见过用这种方法的,但一直没写过: 这次写下练练手: 思路,将角度分成1 ...
- ember.js
http://blog.geoinker.com/2012/12/29/seven-javascript/ http://www.csdn.net/article/2013-04-15/2814893 ...
- PHP 切割字符串 点号 不用双斜杠
$name = "tupian.png"; $nameArr = explode(".", $name); 习惯了Java的程序员容易写成 $nameArr = ...
- iOS开发UI基础—手写控件,frame,center和bounds属性
iOS开发UI基础—手写控件,frame,center和bounds属性 一.手写控件 1.手写控件的步骤 (1)使用相应的控件类创建控件对象 (2)设置该控件的各种属性 (3)添加控件到视图中 (4 ...
- 公告: 新博客已经迁移到 www.root.run
root.run www.root.run www.root.run/sitemap.html www.root.run/sitemap.xml
- dll的加载方式主要分为两大类,显式和隐式链接
之前简单写过如何创建lib和dll文件及简单的使用(http://blog.csdn.net/betabin/article/details/7239200).现在先再深入点写写dll的加载方式. d ...
- ARC模式下的单例写法
// 单例 + (id)sharedInstance { __strong static id sharedObject = nil; static dispatch_once_t onceToken ...
- cocos2d-x 使用UIWebView加载网页(顺便可以看到如何用OC调C++)
猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=248 前段时间项目中要微博授权登 ...
- (转载)php获取form表单中name相同的表单项
(转载)http://hi.baidu.com/ruhyxowwzhbqszq/item/5fd9c8b9b594db47ba0e12a9 比如下面的表单: /*form.php*/ <form ...