Android BLE蓝牙4.0开发详解
1、简介
一、蓝牙版本介绍:
蓝牙发展至今经历了8个版本的更新。1.一、1.二、2.0、2.一、3.0、4.0、4.一、4.2。那么在1.x~3.0之间的咱们称之为传统蓝牙,4.x开始的蓝牙咱们称之为低功耗蓝牙(即蓝牙ble),固然4.x版本的蓝牙也是向下兼容的。android手机必须系统版本4.3及以上才支持BLE API。
二、蓝牙低功耗芯片有两种模式:单模和双模。
单模:只能执行低功耗协议栈,也就是只支持ble。
双模:支持传统蓝牙以及ble的使用。
三、传统蓝牙低功耗蓝牙区别:
1)低功耗蓝牙较传统蓝牙,传输速度更快,覆盖范围更广,安全性更高,延迟更短,耗电极低等等优势。这也是为何近年来智能穿戴的东西愈来愈多,愈来愈火。
2)还有传统蓝牙与低功耗蓝牙通讯方式也有所不一样,传统的通常经过socket方式,而低功耗蓝牙是经过Gatt协议来实现。如果以前没作过传统蓝牙开发,也是能够直接上手低功耗蓝牙开发的。由于它们在通讯协议上都有所改变,关联不大。
3)低功耗蓝牙也叫BLE,下面都称之为BLE
2、关键术语和概念:
一、Gatt:(Generic Attribute Profile)—通用属性配置文件,用于在ble链路上发送和接收被称为“属性”的数据块。目前全部的ble应用都是基于GATT的。一个设备能够实现多个配置文件。
二、ble交互的桥梁是Service、Characteristic、Desciptor。
三、Characteristic:能够理解为一个数据类型,它包括一个value和0至多个对此characteristic的描述(Descriptor)。
四、Descriptor:对Characterisctic的描述,如范围、单位等。
五、Service:Characteristic的集合。它能够包含多个Characteristic。
六、BLE分为三部分:Service,Characteristic,Descriptor。这三部分都用UUID做为惟一标识符。UUID为这种格式:0000ffe1-0000-1000-8000-00805f9b34fb。好比有3个Service,那么就有三个不一样的UUID与Service对应。这些UUID都写在硬件里,咱们经过BLE提供的API能够读取到。
一个BLE终端能够包含多个Service, 一个Service能够包含多个Characteristic,一个Characteristic包含一个value和多个Descriptor,一个Descriptor包含一个Value。Characteristic是比较重要的,是手机与BLE终端交换数据的关键,读取设置数据等操做都是操做Characteristic的相关属性。
好比我有个BLE的硬件,咱们能够用android 版本的light blue去链接上这个硬件,进入应用,就能够扫描到你的BLE设备,点击就会链接上,而后咱们能够看到UUID列表,这里每一行的UUID都表明一个Service,再点击任意一行进去,又能够看到一个UUID列表,这里每一行的UUID都表明一个Characteristic,再点击任意一行进去,便可以操做这个Characteristic,好比写入数据或者读出数据等。
七、UUID(Universally Unique Identifier),含义是通用惟一识别码,它是在必定范围内惟一的机器生成的标识符。标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。
八、ble中有四个角色:
广播者(Braodcaster):广播发送者,是不可链接的设备。
观察者(Observer):扫描广播,不可以启动链接。
广播者和观察者不能创建链接。应用:温度传感器和温度显示器。
---------------------------------------------------------------------
外围(periphery):广播发送者,可链接的设备,在单一链路层做为从机。
中央(central):扫描广播,启动链接,在单一或多链路层做为主机。
中央和外围能够进行配对、链接、数据通讯。应用:手机和手表。
一个中央能够同时链接多个周边,可是一个周边只能链接一个中央(可是我测试,周边能够链接多个中央设备,而且能正常通讯)。
3、BLE蓝牙4.0 android开发(中央设备)java
注意:Android 4.3(API 18)引入ble相关接口。
相关类
目录:frameworks/base/core/java/android/bluetooth/
BluetoothGatt:中央使用和处理数据;
BluetoothGattCallback:中央的回调。
BluetoothGattServer:周边提供数据;
BluetoothGattServerCallback:周边的回调
BluetoothGattService:Gatt服务
BluetoothGattCharacteristic:Gatt特性
BluetoothGattDescriptor:Gatt描述
一、首先在程序里咱们要开启蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
若是你想声明你的应用程序只能在支持BLE的设备上运行,能够将下面声明包含进你的应用程序manifest文件中:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"></uses-feature>
二、判断设备是否支持蓝牙ble:
getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
三、获取蓝牙适配器BluetoothAdapter
final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
或者:
//若是mBluetoothAdapter == null,说明设备不支持蓝牙
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
四、启动、禁止蓝牙:弹出是否启用蓝牙的对话框
//也能够直接调用mBluetoothAdapter.enable()mBluetoothAdapter.disable()来启用禁用蓝牙。不过这种方式不会弹出询问对话框
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_ENABLE:
if (resultCode == Activity.REQUEST_ENABLE_BT) {
Toast.makeText(this, "蓝牙已启用", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show();
}
break;
}
}
五、搜索蓝牙设备,回调。
mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索附近全部的外围设备
//搜索某些uuid的外围设备。
mBluetoothAdapter.startLeScan(uuid[] ,mLeScanCallback);
mBluetoothAdapter.stopLeScan(mLeScanCallback);//中止搜索
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
//device 搜索到的ble设备;rssi 信号强度;scanRecord 远程设备扫描记录的内容(蓝牙名称)
//device.getName();获取蓝牙设备名字
//device.getAddress();获取蓝牙设备mac地址
});
}
};
六、选择一个设备进行链接。链接后会返回一个BluetoothGatt 类型的对象,这里定义为mBluetoothGatt。该对象比较重要,后面发现服务读写设备等操做都是经过该对象。
//第二个参数:若是为false,则直接当即链接。若是为true,则等待远程设备可用时(在范围内,。。)链接。并非断开后从新链接。
mBluetoothGatt = device.connectGatt(mContext, false,mGattCallback);
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG,"BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect. (先前链接的设备。 尝试从新链接)
if (mBluetoothDeviceAddress != null&& address.equals(mBluetoothDeviceAddress)&& mBluetoothGatt != null) {
Log.d(TAG,"Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the
// autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, false, mGattCallback); //该函数才是真正的去进行链接
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
链接后会回调BluetoothGattCallback接口,包括读取设备,往设备里写数据及设备发出通知等都会回调该接口。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override //当链接上设备或者失去链接时会回调该函数
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) { //链接成功
mBluetoothGatt.discoverServices(); //链接成功后就去找出该设备中的服务 private BluetoothGatt mBluetoothGatt;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { //链接失败
}
}
@Override //当设备是否找到服务时,会回调该函数
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) { //找到服务了
//在这里能够对服务进行解析,寻找到你须要的服务
//此处返回获取到的服务列表
mBluetoothGatt.getServices();
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override //当读取设备时会回调该函数
public void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
System.out.println("onCharacteristicRead");
if (status == BluetoothGatt.GATT_SUCCESS) {
//读取到的数据存在characteristic当中,能够经过characteristic.getValue();函数取出。而后再进行解析操做。
//int charaProp = characteristic.getProperties();if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0)表示可发出通知。 判断该Characteristic属性
}
}
@Override //当向设备Descriptor中写数据时,会回调该函数
public void onDescriptorWrite(BluetoothGatt gatt,BluetoothGattDescriptor descriptor, int status) {
System.out.println("onDescriptorWriteonDescriptorWrite = " + status + ", descriptor =" + descriptor.getUuid().toString());
}
@Override //设备发出通知时会调用到该接口
public void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {
if (characteristic.getValue() != null) {
System.out.println(characteristic.getStringValue(0));
}
System.out.println("--------onCharacteristicChanged-----");
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
System.out.println("rssi = " + rssi);
}
@Override //当向Characteristic写数据时会回调该函数
public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
System.out.println("--------write success----- status:" + status);
};
}
七、发现服务,解析出有哪些服务,服务里有哪些Characteristic,哪些Characteristic可读可写可发通知等等
mBluetoothGatt.discoverServices();去发现服务。发现服务后会回调BluetoothGattCallback接口里面的 onServicesDiscovered函数。
//在里面咱们能够获取服务列表
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null)
return null;
return mBluetoothGatt.getServices(); //此处返回获取到的服务列表
}
//解析出有哪些服务,服务里有哪些Characteristic,哪些Characteristic可读可写可发通知等等
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null)
return;
for (BluetoothGattService gattService : gattServices) { // 遍历出gattServices里面的全部服务
List<BluetoothGattCharacteristic> gattCharacteristics = gattServices.getCharacteristics();
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { // 遍历每条服务里的全部Characteristic
if (gattCharacteristic.getUuid().toString().equalsIgnoreCase(须要通讯的UUID)) {
// 有哪些UUID,每一个UUID有什么属性及做用,通常硬件工程师都会给相应的文档。咱们程序也能够读取其属性判断其属性。
// 此处能够可根据UUID的类型对设备进行读操做,写操做,设置notification等操做
// BluetoothGattCharacteristic gattNoticCharacteristic 假设是可设置通知的Characteristic
// BluetoothGattCharacteristic gattWriteCharacteristic 假设是可读的Characteristic
// BluetoothGattCharacteristic gattReadCharacteristic 假设是可写的Characteristic
}
}
}
}
经过以下代码能够获取某一个服务(BluetoothGattService)对应BluetoothGattCharacteristic的特性:是否可读、可写、可通知等。
StringBuilder property = new StringBuilder();
int charaProp = gattCharacteristics.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
property.append("Read");
property.append(" , ");
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
property.append("Write");
property.append(" , ");
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
property.append("Write No Response");
property.append(" , ");
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
property.append("Notify");
property.append(" , ");
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
property.append("Indicate");
property.append(" , ");
}
八、经过上步解析出来的哪些Characteristic可读可写可发通知等等,选取本身须要的BluetoothGattService及对应的BluetoothGattCharacteristic,可经过UUID指定本身须要通讯的UUID。进而进行读写通知等操做
BluetoothGattService、BluetoothGattCharacteristic和BluetoothGattDescriptor三个类中都提供了一个方法getUuid(),经过该方法能够获取其对应的uuid,从而能够判断是不是本身须要的service、characteristic或者descriptor。
经过获取的特征值,能够进行下操做:
写入特性值
读取特性值
订阅特性值。
1)写入特征值:
characteristic.setValue(data.getBytes());
mBluetoothGatt.writeCharacteristic(characteristic);
要想成功写入特征值:
首先此characteristic属性知足BluetoothGattCharacteristic.PROPERTY_WRITY或BluetoothGattCharacteristic.PROPERTY_WRITY_NO_RESPONSE,若是其property都不包含这两个,写特征值writeCharacteristic()函数直接返回false,什么都不作处理(具体能够看BluetoothGatt源码)。
其次此characteristic权限应知足BluetoothGattCharacteristic.PERMISSION_WRITE,不然onCharacteristicWrite()回调收到GATT_WRITE_NOT_PERMITTED回应。
写特征值前能够设置写的类型setWriteType(),写类型有三种,以下:
WRITE_TYPE_DEFAULT 默认类型,须要外围设备的确认,也就是须要外围设备的回应,这样才能继续发送写。
WRITE_TYPE_NO_RESPONSE 设置该类型不须要外围设备的回应,能够继续写数据。加快传输速率。
WRITE_TYPE_SIGNED 写特征携带认证签名,具体做用不太清楚。
外围设备收到中央写特征值的请求,会回调 onCharacteristicWriteRequest
若是这次请求须要回应,则外围设备回应 mGattServer.sendResponse
中央设备收到响应,回调onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
代码:
//可写的UUID。下面函数参数为上面的gattWriteCharacteristic。writeCharacteristic调用成功会回调步骤6中的onCharacteristicWrite函数
public void wirteCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
characteristic.setValue("xxxx");
mBluetoothGatt.writeCharacteristic(characteristic);
}
2)读取特性值:
mBluetoothGatt.readCharacteristic(characteristic);
读特征值与写相似,也须要响应的权限和属性。
该characteristic属性需包含PROPERTY_READ,不然直接返回false(具体能够看BluetoothGatt源码)。
该characteristic权限应知足BluetoothGattCharacteristic.PERMISSION_READ,不然onCharacteristicRead()回调收到GATT_READ_NOT_PERMITTED回应。
外围设备接收到中央设备的读特征值请求,则会回调 onCharacteristicReadRequest()函数,
外围设备应该回应此请求 sendResponse。
中央设备收到响应回调onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
代码:
//可读的UUID。下面函数参数为上面的gattReadCharacteristic。readCharacteristic调用成功会回调步骤6中的onCharacteristicRead函数
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
3)订阅特性值
//第二个参数:true则订阅该特征,false则取消订阅。
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
当指定Characteristic值发生变化时,是否接收通知。
当设为true,若是Characteristic发生变化时,会回调方法:onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
经过参数characteristic,可得到getValue得到其中的内容。
//可接收通知的UUID,设置其能够接收通知(notification)。下面函数参数为上面的gattNoticCharacteristic
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
if (descriptor != null) {
System.out.println("write descriptor");
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
九、中央设备的其余一些方法
readDescriptor(descriptor) 读取描述
writeDescriptor(descriptor) 写描述
readRemoteRssi() 读取链接设备的rssi。
disconnect(); 断开bel链接。
close(); 关闭中央设备。(不用时及时关闭,不然有的手机重连连不上。)
4、BLE蓝牙4.0 android开发(外围设备)
外围设备开发可参考:http://blog.csdn.net/vnanyesheshou/article/details/51943870
5、开源框架:android
1)FastBle:可安装APK,查看源码及git文档学习使用 github:https://github.com/Jasonchenlijian/FastBle 2)luetoothKit:可查看github源码及README.md文档查看使用 github:https://github.com/dingjikerbo/BluetoothKit