Android提高之Android手机与BLE终端通信
最近穿戴设备发展得很火,把相关技术也带旺了,其中一项是ble(bluetooth low energy)。ble是蓝牙4.0的核心profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于ble的低功耗特点,因此普遍用于穿戴设备。android 4.3才开始支持ble api,所以请各位客官把本文代码运行在蓝牙4.0和android 4.3及其以上的系统,另外本文所用的ble终端是一个蓝牙4.0的串口蓝牙模块。
注:笔者的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,还有一些必要的读写蓝牙方法,应该算是简化到大家一看就可以懂了。本文完整代码可以点击此处本站下载。
接下来贴出本文运行的结果,首先是连接ble设备后,枚举出设备所有service、characteristic、descriptor,并且手机会往characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,ble终端收到数据通过串口传到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项目开发有所帮助。
推荐阅读
-
android手机端与PC端使用adb forword通信
-
Android提高之SurfaceView与多线程的混搭实例
-
Android提高之XML解析与生成实例详解
-
Android提高之Android手机与BLE终端通信
-
Android提高之BLE开发Android手机搜索iBeacon基站
-
Android BLE与终端通信(四)——实现服务器与客户端即时通讯功能
-
Android开发四大组件之Service如何使用(与Activity通信)
-
android手机端与PC端使用adb forword通信
-
Android提高之SurfaceView与多线程的混搭实例
-
Android提高之XML解析与生成实例详解