Android BLE开发之Android手机与BLE终端通信
本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!
近期穿戴设备发展得非常火。把相关技术也带旺了,当中一项是BLE(Bluetooth Low Energy)。BLE是蓝牙4.0的核心Profile,主打功能是高速搜索,高速连接。超低功耗保持连接和数据传输,弱点是数据传输速率低,因为BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才開始支持BLE API,所以请各位客官把本文代码执行在蓝牙4.0和Android 4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。
PS:我的i9100刷了4.4系统后,居然也能跟BLE蓝牙模块通信。
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。
一个蓝牙4.0的终端能够包括多个Service,一个Service能够包括多个Characteristic。一个Characteristic包括一个Value和多个Descriptor,一个Descriptor包括一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,比如PERMISSION和PROPERTY,而当中最经常使用的是PROPERTY。本文所用的BLE蓝牙模块居然没有标准的Characteristic的PERMISSION。Characteristic的PROPERTY能够通过位运算符组合来设置读写属性,比如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合(本文代码已含此分解方法)。
本文代码改自Android 4.3 Sample的BluetoothLeGatt。把冗余代码去掉,获取的BLE设备信息都通过Log。另一些必要的读写蓝牙方法,应该算是简化到大家一看就能够懂了。
本文代码能够到http://download.csdn.net/detail/hellogv/7228819下载。接下来贴出本文执行的结果,首先是连接BLE设备后。枚举出设备全部Service、Characteristic、Descriptor,而且手机会往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,BLE终端收到数据通过串口传到PC串口助手(见PC串口助手的截图):
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00
这里红字是由BluetoothGattCallback的onCharacteristicRead()回调而打出Log
下面Log是PC上的串口工具通过BLE模块发送过来。由BluetoothGattCallback的 onCharacteristicChanged()打出Log
04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
接下来贴出本文核心代码:
public class DeviceScanActivity extends ListActivity {
private final static String TAG = DeviceScanActivity.class.getSimpleName();
private final static String UUID_KEY_DATA = "0000ffe1-0000-1000-8000-00805f9b34fb";
private LeDeviceListAdapter mLeDeviceListAdapter;
/**搜索BLE终端*/
private BluetoothAdapter mBluetoothAdapter;
/**读写BLE终端*/
private BluetoothLeClass mBLE;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setTitle(R.string.title_devices);
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
//开启蓝牙
mBluetoothAdapter.enable();
mBLE = new BluetoothLeClass(this);
if (!mBLE.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
//发现BLE终端的Service时回调
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
//收到BLE终端数据交互的事件
mBLE.setOnDataAvailableListener(mOnDataAvailable);
}
@Override
protected void onResume() {
super.onResume();
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter(this);
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
}
@Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
mBLE.disconnect();
}
@Override
protected void onStop() {
super.onStop();
mBLE.close();
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
if (mScanning) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning = false;
}
mBLE.connect(device.getAddress());
}
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
/**
* 搜索到BLE终端服务的事件
*/
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){
@Override
public void onServiceDiscover(BluetoothGatt gatt) {
displayGattServices(mBLE.getSupportedGattServices());
}
};
/**
* 收到BLE终端数据交互的事件
*/
private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){
/**
* BLE终端数据被读的事件
*/
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS)
Log.e(TAG,"onCharRead "+gatt.getDevice().getName()
+" read "
+characteristic.getUuid().toString()
+" -> "
+Utils.bytesToHexString(characteristic.getValue()));
}
/**
* 收到BLE终端写入数据回调
*/
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.e(TAG,"onCharWrite "+gatt.getDevice().getName()
+" write "
+characteristic.getUuid().toString()
+" -> "
+new String(characteristic.getValue()));
}
};
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
for (BluetoothGattService gattService : gattServices) {
//-----Service的字段信息-----//
int type = gattService.getType();
Log.e(TAG,"-->service type:"+Utils.getServiceType(type));
Log.e(TAG,"-->includedServices size:"+gattService.getIncludedServices().size());
Log.e(TAG,"-->service uuid:"+gattService.getUuid());
//-----Characteristics的字段信息-----//
List<BluetoothGattCharacteristic> gattCharacteristics =gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic: gattCharacteristics) {
Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid());
int permission = gattCharacteristic.getPermissions();
Log.e(TAG,"---->char permission:"+Utils.getCharPermission(permission));
int property = gattCharacteristic.getProperties();
Log.e(TAG,"---->char property:"+Utils.getCharPropertie(property));
byte[] data = gattCharacteristic.getValue();
if (data != null && data.length > 0) {
Log.e(TAG,"---->char value:"+new String(data));
}
//UUID_KEY_DATA是能够跟蓝牙模块串口通信的Characteristic
if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
//測试读取当前Characteristic数据。会触发mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBLE.readCharacteristic(gattCharacteristic);
}
}, 500);
//接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic, true);
//设置数据内容
gattCharacteristic.setValue("send data->");
//往蓝牙模块写入数据
mBLE.writeCharacteristic(gattCharacteristic);
}
//-----Descriptors的字段信息-----//
List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
int descPermission = gattDescriptor.getPermissions();
Log.e(TAG,"-------->desc permission:"+ Utils.getDescPermission(descPermission));
byte[] desData = gattDescriptor.getValue();
if (desData != null && desData.length > 0) {
Log.e(TAG, "-------->desc value:"+ new String(desData));
}
}
}
}//
}
}
Android BLE开发之Android手机与BLE终端通信的更多相关文章
- Android BLE开发之Android手机搜索iBeacon基站
本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处! 上次讲了Android手机与BLE终端之间的通信,而最常见的BLE终端应该是苹果公司倡导的iBeacon基站. ...
- Android NDK开发之Android.mk文件
Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南 Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...
- Android安全开发之WebView中的地雷
Android安全开发之WebView中的地雷 0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者 ...
- android软件开发之webView.addJavascriptInterface循环渐进【一】
本篇文章由:http://www.sollyu.com/android-software-development-webview-addjavascriptinterface-cycle-of-gra ...
- Android 异步开发之 AsyncQueryHandler 批量添加联系人
AsyncQueryHandler: 官方解释是一个异步帮助类(A helper class to help make handling asynchronous ContentResolver qu ...
- Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值
Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...
- Android混合开发之WebViewJavascriptBridge实现JS与java安全交互
前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...
- Android混合开发之WebView与Javascript交互
前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...
- Android混合开发之WebView使用总结
前言: 今天修改项目中一个有关WebView使用的bug,激起了我总结WebView的动机,今天抽空做个总结. 混合开发相关博客: Android混合开发之WebView使用总结 Android混合开 ...
随机推荐
- 关于CCRect
一直有一个误区,因为之前处理的公司引擎是屏幕坐标系 导致觉得CCRect的坐标起始值(x,y),习惯性的认为就是左上角的点. 但是,真正的x,y值,是跟x轴与y轴相对应的.
- DataGrid( 数据表格) 组件[8]
本节课重点了解 EasyUI 中 DataGrid(数据表格)组件的使用方法,这个组件依赖于Panel(面板).Resizeable(调整大小).LinkButton(按钮).Pageination( ...
- 关于Lambda表达式的理解
在.NET 1.0的时候,大家都知道我们经常用到的是委托.有了委托呢,我们就可以像传递变量一样的传递方法.在一定程序上来讲,委托是一种强类型的托管的方法指 针,曾经也一时被我们用的那叫一个广泛呀,但是 ...
- css中表格的table-layout属性特殊用法
table-layout: 属性1:auto,使用它,表格的大小由单元格里的内用决定,即td的宽高由内容的多少而变化. 属性2:fixed,如果内容是中文的话td的宽高固定,宽高有内容决定,没有限制. ...
- sqlserver编程基本语法
一.定义变量 --简单赋值 declare @a int set @a=5 print @a --使用select语句赋值 declare @user1 nvarchar(50) select @ ...
- 关于控制文件和redo log损坏的恢复
前段时间一朋友自己电脑上的开发测试用的数据库出了点问题,电脑操作系统是Win8,直接在Win8上安装了Oracle11g,后来系统自动升级到Win8.1,Oracle相关的服务全都不见了,想想把数据文 ...
- [转]Swift编程风格指南
语言 使用美式英语拼写以匹配苹果公司的API 优选: var color = "red" 不建议使用: var colour = "red" 间隔 使用2个空格 ...
- linux 线程备忘
三种线程同步机制 •互斥锁 •信号量 •条件变量 pthread_t thread_id; 主要函数 pthread_create(),pthread_exit(),pthread_join(), ...
- uva 10609 - Fractal
题目大意:给出A,B两个点的坐标,以及T,每次找到A.B的四等分点C,D,然后以AB/2为边长,C,D为顶点,构建一个等边三角形,E为另外一个顶点,然后再对C,E:E,D做同样的操作,直到构建的等边三 ...
- JQuery控制input的readonly和disabled属性
jquery设置元素的readonly和disabled Jquery的api中提供了对元素应用disabled和readonly属性的方法,在这里记录下.如下: 1.readonly $('in ...