安卓蓝牙4.0BLE 通信
来源:互联网 发布:软件测试技术语言 编辑:程序博客网 时间:2024/06/05 03:13
安卓蓝牙4.0BLE通信之体重称
最近正在做一个关于手机跟蓝牙体重称之间数据交互的工程,因为之前没接触过蓝牙开发,所以浪费了不少时间,但是经过查资料和大神们的文章,终于在一周后完工了,现在总结下这个demo,先来介绍下关于蓝牙BLE有关的知识。
什么是BLE?
BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才开始支持BLE API,所以请各位客官把本文代码运行在蓝牙4.0和Android 4.3及其以上的系统。
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多Descriptor,一个Descriptor包含一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,两者就是通过改变Characteristic的值来实现数据交互的。Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY。Characteristic的PROPERTY可以通过位运算符组合来设置读写属性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合。
下面直接贴代码,边贴核心代码边解释。
上面是我的项目目录结构,BluetothLeClass是提取的蓝牙工具类,Conversion与Utils是转码的工具类,ProgressDialogsUtils是对话框工具类,LeDeviceListAdapter是设备ListView的Adapter,activity_main.xml的视图如下:
点击蓝牙搜索按钮后,会查找蓝牙设备,然后将搜索到的设备显示在ListView上,再通过点击列表项,去连接设备,再发数据给设备然后处理设备发回的回调信息,下面粘一下核心代码:
这是按钮点击事件的方法,主要功能是判断手机是否支持蓝牙4.0,开启蓝牙和设置发现设备和数据交互的回调:
public void onClick(View v) { switch (v.getId()) { case R.id.btn_search_bluetooth: openBluetooth(); break; } } private void openBluetooth() { //判断手机是否支持蓝牙4.0 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); finish(); return; } //开启蓝牙 mBluetoothAdapter.enable(); mBLE = new BluetoothLeClass(this, mHandler); if (!mBLE.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } //发现BLE终端的Service时回调 mBLE.setOnServiceDiscoverListener(mOnServiceDiscover); //收到BLE终端数据交互的事件 mBLE.setOnDataAvailableListener(mOnDataAvailable); mLeDeviceListAdapter = new LeDeviceListAdapter(this); ls_setup.setAdapter(mLeDeviceListAdapter); scanLeDevice(true); }
这是发现设备的回调接口:
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new BluetoothLeClass.OnServiceDiscoverListener() { @Override public void onServiceDiscover(BluetoothGatt gatt) { Log.w(TAG, "onServiceDiscover"); //gattServices=mBLE.getSupportedGattServices(); displayGattServices(mBLE.getSupportedGattServices()); Log.e(TAG, "found"); } };
这是数据交互的回调接口:
/** * 收到BLE终端数据交互的事件 */ private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new BluetoothLeClass.OnDataAvailableListener() { /** * BLE终端数据被读的事件 */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.w(TAG, "onServiceDiscover"); if (status == BluetoothGatt.GATT_SUCCESS) Log.e(TAG, "onCharRead " + gatt.getDevice().getName() + " read " + characteristic.getUuid().toString() + " -> " + Utils.bytesToHexString(characteristic.getValue())) ; } /** * 收到BLE终端写入数据回调 */ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { mProgressDialogUtils.dismissProgressDialog(); Log.e(TAG, "onCharWrite " + gatt.getDevice().getName() + " write " + characteristic.getUuid().toString() + " -> " + "结果是:" + Utils.bytesToHexString(characteristic.getValue()) ); if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) { getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(10, 14)); } else { getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(4, 8)); } }
displayGattServices方法主要是输出搜索到的设备中的各种Services,Characteristic,和Descriptor的各种信息,同时设置Charicteristic被写的通知,往BLE设备上写数据,
和读出数据
private void displayGattServices(List<BluetoothGattService> gattServices) { Log.w(TAG, "displayGattServices"); 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:" + Utils.bytesToHexString(data)); } //UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic //if (gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)) { //当测试读取前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead() //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite() mBLE.setCharacteristicNotification(gattCharacteristic, true); //设置数据内容 //gattCharacteristic.setValue("A5,00,19,AF,50,5A,19"); //attCharacteristic.setValue(Conversion.HexString2Bytes("A5019AF505A19")); byte[] b = {(byte) 0xA5, (byte) 0x00, (byte) 0x19, (byte) 0xAF, (byte) 0x50, (byte) 0x5A, (byte) 0x19}; gattCharacteristic.setValue(b); //往蓝牙模块写入数据 mBLE.writeCharacteristic(gattCharacteristic); mHandler.postDelayed(new Runnable() { @Override public void run() { mBLE.readCharacteristic(gattCharacteristic); } }, 500); Log.e(TAG, "转换是C:" + Utils.bytesToHexString(b)); //} //-----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:" + Utils.bytesToHexString(desData)); } } } }// }
setValue()为写数据关键,参数为设备上规定的APP到BLE设备上的协议内容。必须设置数据改变通知监听:mBLE.setCharacteristicNotification(gattCharacteristic, true);不然特征值改变也收不到回调信息,具体的通知方法为:
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } List<BluetoothGattDescriptor> gattDescriptors = characteristic.getDescriptors(); for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) { gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(gattDescriptor);//写数据回调关键 mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); } }
谨记,只有mBluetoothGatt.writeDescriptor(gattDescriptor);才能保证可准确收到BLE设备往特征值写入数据的监听,具体原理我也不是特别清楚,只知道删除这句话将收不到监听方法。
下面来介绍下当BLE设备往特征值写入数据时的回调方法:
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { mProgressDialogUtils.dismissProgressDialog(); Log.e(TAG, "onCharWrite " + gatt.getDevice().getName() + " write " + characteristic.getUuid().toString() + " -> " + "结果是:" + Utils.bytesToHexString(characteristic.getValue()) ); if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) { getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(10, 14)); } else { getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(4, 8)); } }
当设备写入数据时,将对话框取消掉,因为我连接的体重秤有两种,协议的内容也不同,第一张设备中是去回调值的第10到14位,而第二种是取4到8位,所以我根据设备名称做了个判断,getWeight()方法主要是转码和计算体重值:
private void getWeight(BluetoothGatt gatt, String s) { int a = Integer.parseInt(s, 16); if (mt == null) { mt = new MyThread(); mt.start(); Log.e(TAG, "当前线程为current:" + mt.currentThread() + cout++); } if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) { double w = a; weight = w / 10; } else { double w = a; weight = w / 100; } } };
这里我通过handle与Thead的方法,当体重秤的数据改变时,我不断地刷新界面上的体重值,具体代码为:
class MyThread extends Thread { @Override public void run() { super.run(); while (finish) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = new Message(); Bundle bundle = new Bundle(); bundle.putDouble("weight", weight); msg.setData(bundle); msg.what = 1; mHandler.sendMessage(msg); } } }
private void intHandle() { mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: Bundle b = msg.getData(); setWeight(b.getDouble("weight")); if (weight == 0.0) { mHandler.removeCallbacks(mt); mt = null; } break; case 2: Log.e(TAG, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~handlemessage" + i++); mProgressDialogUtils.dismissProgressDialog(); mProgressDialogUtils.showProgressDialog(MainActivity.this, "连接成功,正常测量您的体重,请勿离开称体!"); break; case 3: mProgressDialogUtils.dismissProgressDialog(); mProgressDialogUtils.showProgressDialogWithButton(MainActivity.this, "连接失败!", "确定"); break; } } }; }
最后的结果为:
时间比较赶,所以逻辑有点混乱,思路不是很清晰,所以提供源码给各位小伙伴参考,谢谢!
源码地址:点击打开链接
- 安卓蓝牙4.0BLE 通信
- 浅谈安卓蓝牙4.0BLE开发
- 基于intel芯片的安卓蓝牙4.0 BLE通信总结
- 蓝牙4.0BLE在安卓项目中的使用详解
- 安卓基于蓝牙4.0的BLE开发
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓蓝牙通信
- 安卓--第十天--蓝牙app实现与BLE设备简单通信
- 安卓蓝牙BLE设备开发
- 安卓ble蓝牙快速开发
- IOS学习之蓝牙4.0 BLE通信
- Android BLE蓝牙通信
- ble蓝牙通信
- Android 蓝牙Ble通信
- 安卓蓝牙通信初步
- No symbol "xxx" in current context
- 实战Nginx与PHP(FastCGI)的安装、配置与优化
- linux cache总结
- 链接服务器 "(null)" 的 OLE DB 访问接口 "SQLNCLI11" 指示该对象没有列,或当前用户没有访问该对象的权限。
- 30+有用的CSS代码片段
- 安卓蓝牙4.0BLE 通信
- 利用FreeMarker生成java源代码
- HTTP请求
- ruby on rails爬坑(一):用户账号密码管理
- universal-ImageLoader加载图片
- 《C++ Primer》真的适合入门吗?
- Android 返回按钮的实现
- ASP.NET GridView控件固定表头(适用于IE浏览器,css操作)
- Android M 新的运行时权限开发者需要知道的一切