Android蓝牙开发(二) BLE4.0低功耗蓝牙
来源:互联网 发布:js实现分页功能 编辑:程序博客网 时间:2024/05/20 21:22
一、BLE4.0低功耗蓝牙
Bluetooth Low Energy,蓝牙低功耗,是从蓝牙4.0开始支持的技术。相较传统蓝牙,传输速度更快、覆盖范围广、安全性高、延时短、耗电低等特点。
二、关键术语
1.GATT(通用属性配置):通用属性配置文件,用于ble链路上发送和接收“属性”的数据块。目前所有的ble应用都是基于GATT的,一个设备可以实现多个配置文件。2.ATT(属性协议):GATT是构建于ATT上面的,每一个属性都是由唯一标识码(UUID)来唯一确定。
ble交互的桥梁是Service、Characteristic、Descriptor 三者都是由UUID作为唯一标识符
3.Characteristic(特征):一个特征包含一个单一的值和0-n个描述符(Descriptor),描述符描述用于特征的值。一个特质可以被认为是一个数据类型,或一个类。
4.Descriptor(描述符):对Characteristic的描述,如范围、单位等。
5.Service(服务):服务是特征的集合。可以包含多个Characteristic。一个ble终端可以包含多个Service,一个Characteristic可以包含一个Value和多个Descriptor。
三、权限申请
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>如果需要声明应用仅对低功耗蓝牙有效,还需要在app的manifest中声明
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
四、相关类
蓝牙4.0API的相关类在Framework的 frameworks/base/core/java/android/bluetooth/ 中其中主要的类有:1.BluetoothGatt:中央设备使用的类,处理数据
2.BluetoothGattCallback: 中央设备回调
3.BluetoothGattServer:周边设备提供数据
4.BluetoothGattServerCallback:周边设备的回调
5.BluetoothGattService:Gatt服务
6.BluetoothGattCharacteristic:Gatt特性
7.BuletoothGattDecriptor:Gatt描述
五、角色和职责
1.中央与周边: 中央设备,可以进行扫描和搜索周边设备发出的广播。 而周边设备,可以发出设备广播。2.GATT服务器与GATT客户端: 这两个 决定了建立连接后的通信方式。
六、配置BLE
在使用BLE之前,我们需要验证设备是否支持BLE4.0,如果支持,则需要验证蓝牙是否打开。而这些操作,都是使用BluetoothAdapter.1.获取BluetoothAdapter
BluetoothAdapter代表设备自身的蓝牙适配器,整个系统,只会有一个该适配器。我们需要通过获取系统服务来获取BluetoothAdapter。
Android 4.3(API 18)以后,才支持BluetoothManager
// Initializes Bluetooth adapter. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();
2.使用蓝牙
我们可以通过 BluetoothAdapter 的isEnable()方法来判断蓝牙是否打开。如果没打开,我们需要提醒用户打开蓝牙,又或者,直接调用enable()方法来开启蓝牙。
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); }
七、中央扫描周边BLE设备
1.扫描设备:
//搜索附近所有的外围设备 mBluetoothAdapter.startLeScan(mLeScanCallback); //搜索某些uuid的外围设备。 可指定uuid mBluetoothAdapter.startLeScan(uuid[] ,mLeScanCallback); 停止扫描 mBluetoothAdapter.stopLeScan(mLeScanCallback);
2.扫描结果回调:
mLeScanCallback = new BluetoothAdapter.LeScanCallback() { public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { } }其中,返回的 device:搜索到的ble设备 rssi:信号强度 scanRecord:远程设备广告记录的内容(蓝牙名称)八、发送链接请求,获取中央设备
根据第七步搜索到的外围设备,我们需要去链接它。链接,指的是链接到GATT服务器设备,
此处我们需要传进去三个参数:
1.context
2.false:直接立即链接 true:等待远端设备可用时自动链接
3.蓝牙链接回调 其中包括:链接状态改变、characteristic的read、write、change、和MTU change的监听
// Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered.,所有函数的回调函数 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; //收到设备notify值 (设备上报值) 链接状态改变回调方法,此处处理链接成功 if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "Disconnected from GATT server."); broadcastUpdate(intentAction); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { //链接成功后,从下面获取service列表 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ if(MTU>20){ boolean ret = mBluetoothGatt.requestMtu(MTU); Log.d("BLE","requestMTU "+MTU+" ret="+ret); } } broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); System.out.println("onServicesDiscovered received: " + status); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //读取从周边设备传递过来的数据值,在这里读数据 if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //特征状态改变 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (status == BluetoothGatt.GATT_SUCCESS) { Log.e(TAG, "onMtuChanged: "+mtu); //local var to record MTU size } } };1.通过onServicesDiscovered()成功回调获取的BluetoothGatt 我们可以调用gatt的getServices()方法,来获取List<BluetoothGattService>集合。
2.从集合中找到我们需要的service后,可以调用该service中的getCharacteristics()方法,来获取List<Characteristic> 集合。
3.再从指定的Characteristic中,我们可以通过getDescriptor()方法来获取该特征所包含的descriptor
以上的BluetoothGattService、BluetoothGattCharacteristic、BluetoothGattDescriptor。我们都可以通过其getUuid()方法,来获取其对应的Uuid,从而判断是否是自己需要的。
九、中央设备写入和接收数据
1.写入特征值写入特征值,首先我们的特征值属性,满足BluetoothGattCharacteristic.PROPERTY_WRITE或BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,如果其property都不包含这两个,写特征值writeCharacteristic()函数直接返回false,什么都不做处理。
其次此characteristic权限应满足BluetoothGattCharacteristic.PERMISSION_WRITE,否则onCharacteristicWrite()回调收到GATT_WRITE_NOT_PERMITTED回应。
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); characteristic.setValue(string); boolean isSuccess = mBluetoothLeService.writeCharacteristic(characteristic);
如上代码,在写入特征之前,我们可以设置写入的类型,写入类型有三种
WRITE_TYPE_DEFAULT 默认类型,需要外围设备的确认,也就是需要外围设备的回应,这样才能继续发送写。
WRITE_TYPE_NO_RESPONSE 设置该类型不需要外围设备的回应,可以继续写数据。加快传输速率。
WRITE_TYPE_SIGNED 写特征携带认证签名
当外围设备收到中央写特征值的请求,会回调 onCharacteristicWriteRequest。
如果此次请求需要回应,则外围设备回应 mGattServer.sendResponse
中央设备收到响应,回调onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status)
2.读取特征值
读取特征值,需要调用BluetoothLeService的readCharacteristic(characteristic)方法。 读特征,也需要特征具有相应的权限和属性。
(1)该特征属性必须包含PROPERTY_READ,否则返回false.
(2)该特征属性必须满足BluetoothGattCharacteristic.PERMISSION_READ权限,否则onCharacteristicRead()回调收到GATT_READ_NOT_PERMITTED回应。
外围设备接收到中央设备读特征值请求时,则会调用onCharacteristicReadRequest()函数回调。
外围设备回应此请求,则调用sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value)。
而中央设备收到外围设备回应时,则会调用onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 回调。
3.订阅
我们可以通过BluetoothLeService的setCharacteristicNotification(characteristic, true);(后面的第二个参数,true表示订阅 false表示取消订阅)方法来指定一个Characteristic特征。当该特征发生变化时,会回调onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) 方法,通过参数characteristic,可获得getValue获得其中的内容。
注意:如果该特征的属性没有设置value为:descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 则收不到订阅信息。
十、外围设备的设置
1.获取打开外围设备mGattServer = mBluetoothManager.openGattServer(mContext, callback); //其中callback是一个MyGattServerCallback(继承了BluetoothGattServerCallback)对象。
2.初始化特征
其中我们可以看到上面第八步所介绍的,外围设备的特征值的写入和读取的权限设置。
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic( UUID.fromString("0000bbb1-0000-1000-8000-00805f9b34fb"), BluetoothGattCharacteristic.PROPERTY_NOTIFY +BluetoothGattCharacteristic.PROPERTY_WRITE +BluetoothGattCharacteristic.PROPERTY_READ , BluetoothGattCharacteristic.PERMISSION_WRITE+BluetoothGattCharacteristic.PERMISSION_READ );
3.设置特征属性
第二行代码,是第九步介绍的,订阅步骤中所需要的特征属性值的设定。
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), BluetoothGattDescriptor.PERMISSION_WRITE); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); characteristic.addDescriptor(descriptor);
4.设置服务
第二个参数为service type,
SERVICE_TYPE_PRIMARY 基础服务、主要服务。
SERVICE_TYPE_SECONDARY 辅助服务(由初级服务包含在内)。
BluetoothGattService 类中方法
addService(bluetoothGattService),将辅助服务添加到主要服务中。
getIncludeedServices() 获取包含的服务列表。
getType() 获取服务的type。
getUuid() 获取服务的UUID。
final BluetoothGattService service = new BluetoothGattService(UUID.fromString("0000bbb0-0000-1000-8000-00805f9b34fb"), BluetoothGattService.SERVICE_TYPE_PRIMARY); service.addCharacteristic(characteristic);
5.添加服务
boolean isSuccess = gattServer.addService(service); LogUtils.e(TAG," 添加service2:"+isSuccess );
6.开启广播
如何需要让其他中央设备搜索到我们的周边设备呢? 这里我们需要开启广播
mGattServer.startAdvertising();//开始广播
mGattServer.stopAdvertising();//停止广播
首先我们需要判断设备是否支持广播的开启
private void startService() { //判断你的设备到底支持不支持BLE Peripheral,不支持则返回空 mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); Log.e(TAG,"mBluetoothLeAdvertiser"+mBluetoothLeAdvertiser); if(mBluetoothLeAdvertiser == null){ return; } startAdvertising(); //初始化BLE蓝牙广播 }
public void startAdvertising() { byte[] broadcastData = {0x34, 0x56}; String bleName = "小郎"; byte[] broadcastData = bleName.getBytes(); //广播设置参数,广播数据,还有一个是Callback mBluetoothLeAdvertiser.startAdvertising(createAdvSettings(true, 0), createAdvertiseData(broadcastData), mAdvertiseCallback); }
上面的开启广播中 有三个参数
(1)广播的基本设置
public AdvertiseSettings createAdvSettings(boolean connectable, int timeoutMillis) { AdvertiseSettings.Builder mSettingsbuilder = new AdvertiseSettings.Builder(); mSettingsbuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY); mSettingsbuilder.setConnectable(connectable); mSettingsbuilder.setTimeout(timeoutMillis); AdvertiseSettings mAdvertiseSettings = mSettingsbuilder.build(); return mAdvertiseSettings; }
(2)设置广播携带的参数
public AdvertiseData createAdvertiseData(byte[] data) { AdvertiseData.Builder mDataBuilder = new AdvertiseData.Builder(); mDataBuilder.addManufacturerData(0x01AC, data); mDataBuilder.addServiceUuid(ParcelUuid.fromString(uid)); mDataBuilder.setIncludeDeviceName(true); //设置是否携带设备名称 AdvertiseData mAdvertiseData = mDataBuilder.build(); return mAdvertiseData; }(3)广播开启回调
此处,我是在广播开启成功后,再初始化周边设备的
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() { @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { super.onStartSuccess(settingsInEffect); LogUtils.e(TAG, "开启广播成功"); ToastUtils.showToast(BLEConnectService.this, "开启广播成功", 2000); initGattServer(); //初始化GATT服务 } @Override public void onStartFailure(int errorCode) { super.onStartFailure(errorCode); ToastUtils.showToast(BLEConnectService.this, "开启广播失败 errorCode:" + errorCode, 2000); } };
其中第十步骤的第一小步 打开外围设备的回调方法 在第九步有介绍 此处就不再继续解释了
十一、开发中的注意事项 (有其他的欢迎补充)
1.中央和外围设备传输的数据 有20个字节长度的限制。 在5.0之后 我们可以通过中央设备 设置MTU值,来增大传输长度if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ if(MTU>20){ boolean ret = mBluetoothGatt.requestMtu(MTU); Log.d("BLE","requestMTU "+MTU+" ret="+ret); } }
次代码可写入到onServicesDiscovered成功的回调中。 当我们设置MTU长度成功时,中央和外围设备都会回调onMtuChanged()。
如果我们的手机不是5.0的 目前我的解决方法是,通过数据拆分成多份,分多次传输的。
阅读全文
0 0
- Android蓝牙开发(二) BLE4.0低功耗蓝牙
- Android蓝牙低功耗开发
- 【android 蓝牙开发——BLE(低功耗)蓝牙】
- android蓝牙ble4.0开发
- Android低功耗蓝牙译文(二)
- 蓝牙Ble4.0开发
- Android ble低功耗蓝牙开发
- Android BLE低功耗蓝牙开发
- Android-BLE低功耗蓝牙开发
- Android 低功耗蓝牙(BLE)开发(1)-- 基本概念
- Android 低功耗蓝牙(BLE)开发(2)-- BluetoothAdapter详解
- Android 低功耗蓝牙(BLE)开发(3)-- BluetoothDevice详解
- Android-低功耗蓝牙BLE(Bluetooth Low Energy)开发
- Android BLE低功耗蓝牙开发极简系列(二)之读写操作
- Android BLE与终端通信(五)——Google API BLE4.0低功耗蓝牙文档解读之案例初探
- 【Android应用开发】Android 蓝牙低功耗 (BLE) ( 第一篇 . 概述 . 蓝牙低功耗文档 翻译)
- android 蓝牙4.0 ble 低功耗蓝牙
- Android低功耗蓝牙详解
- 一点一滴探究JVM之内存结构
- Dell Alienware Aurora R6 (1080ti)安装Ubuntu17.04记录
- 树莓派spi通信
- 副本的创建以及画板DIY
- HHUOJ_1327: 电子钟
- Android蓝牙开发(二) BLE4.0低功耗蓝牙
- 大数据早报:合肥公证机构启用人脸识别系统;阿里AI时尚助手进驻全国13家门店 (11.16)
- 阿里服务器(1)---如何连接上我的服务器
- 运维之监控与安全篇------1. Linux基本防护 、 用户切换与提权 、 SSH访问控制
- 自创数据集,用TensorFlow预测股票教程 !(附代码)
- Java中的运算符
- NYOJ 1057 寻找最大数(三)(贪心)
- 小白学习c语言之基本数据类型,运算符,基本语句
- 撕衣服小案例