低功耗蓝牙Bluetooth Low Energy(BLE)

Android4.3以上加入了低功耗蓝牙,可以大大节省设备功耗。低功耗蓝牙包括的术语及概念:


如上图所示,使用低功耗蓝牙可以包括多个Profile,一个Profile中有多个Service,一个Service中有多个Characteristic,一个Characteristic中包括一个value和多个Descriptor。

Android中进行蓝牙开发需要使用到的类的执行过程是:

1、使用BluetoothAdapter.startLeScan来扫描低功耗蓝牙设备

2、在扫描到设备的回调函数中会得到BluetoothDevice对象,并使用BluetoothAdapter.stopLeScan停止扫描

3、使用BluetoothDevice.connectGatt来获取到BluetoothGatt对象

4、执行BluetoothGatt.discoverServices,这个方法是异步操作,在回调函数onServicesDiscovered中得到status,通过判断status是否等于BluetoothGatt.GATT_SUCCESS来判断查找Service是否成功

5、如果成功了,则通过BluetoothGatt.getService来获取BluetoothGattService

6、接着通过BluetoothGattService.getCharacteristic获取BluetoothGattCharacteristic

7、然后通过BluetoothGattCharacteristic.getDescriptor获取BluetoothGattDescriptor


BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才开始支持BLE API,所以请各位客官把本文代码运行在蓝牙4.0和Android 4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。

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后要分解成所用的组合。


Android4.3(API级别18)引入内置平台支持BLE的central角色,同时提供API和app应用程序用来发现设备,查询服务,和读/写characteristics。与传统蓝牙
(ClassicBluetooth)不同,蓝牙低功耗(BLE)的目的是提供更显著的低功耗。这使得Android应用程序可以和具有低功耗的要求BLE设备,如接近传感器,心脏速率监视器,健身设备等进行通信。

关键术语和概念 
下面是关键BLE术语和概念的总结: 
通用属性规范(GATT)—GATTprofile是一个通用规范用于在BLE链路发送和接收被称为―属性(attributes)‖的数据片。目前所有的低功耗应用 profile都是基于GATT。 
蓝牙SIG定义了许多profile用于低功耗设备。Profile(配置文件)是一个规范,规范了设备如何工作在一个特定的应用场景。注意:一个设备可以实现多个profile。例如,一个设备可以包含一个心脏监测仪和电池电平检测器。 

属性协议( ATT )—GATT是建立在属性协议( ATT )的顶层,通常也被称为GATT/ ATT 。 ATT进行了优化用于在BLE设备上运行。为此,它采用尽可能少的字节越好。每个attribute属性被UUID(通用唯一标识符)唯一标识 ,UUID是标准128-bit格式的ID用来唯一标识信息。attributes 被 ATT 格式化characteristics和services形式进行传送。 
特征(Characteristics)— 一个characteristics包含一个单独的value值和0 –n个用来描述characteristic 值(value)的descriptors。一个characteristics可以被认为是一种类型的,类似于一个类。 
描述符(descriptor)—descriptor是被定义的attributes,用来描述一个characteristic的值。例如,一个descriptor可以指定一个人类可读的描述中,在可接受的范围里characteristic值,或者是测量单位,用来明确characteristic的值。 
服务(service)—service是characteristic的集合。例如,你可以有一个所谓的―Heart RateMonitor‖service,其中包括characteristic,如―heart rate measurement ‖。你可以在 bluetooth.org找到关于一系列基于GATT的profile和service。

角色和职责 
以下是适用于当一个Android设备与BLE设备交互的角色和责任: 
中心设备(central)与外围设备(peripheral)。这也适用于BLE连接本身。Central设备进行扫描,寻找advertisenment,peripheral设备发出advertisement。 
GATT server(服务器)与GATTclient(客户端)。这决定了两个设备建立连接后如何互相交互。    
要了解它们的区别,假设你有一个Android手机和活动跟踪器,活动跟踪器是一个BLE装置。这款手机扮演central角色;活动跟踪器扮演peripheral角色(建立一个BLE连接,必须具备两者。如果两个设备只支持central角色或peripheral角色,不能跟对方建立一个BLE连接)。

一旦手机与活动跟踪器已经建立连接,他们开始相互传送GATT数据。根据它们传送数据的种类,其中一个可能作为 GATT server。例如,如果该活动跟踪器将传感器数据汇报到手机上,活动跟踪器作为server。如果活动跟踪器想要从手机接收更新,那么手机作为server。  在本文档中使用的示例中,Android应用程序(在Android设备上运行)是GATT client。该应用从GATT server 获取数据,server是一款支持 HeartRate Profile的BLE心脏速率监测仪。但你可以设计你的Android应用程序,作为GATT server角色。见BluetoothGattServer 获取更多信息。

BLE权限 
为了使用应用程序中的蓝牙功能,你必须声明蓝牙权限BLUETOOTH。你需要这个权限执行任意蓝牙通讯,如请求连接,接受连接,传输数据。 
如果你希望你的应用程序启动设备发现或操纵蓝牙设置,还必须声明BLUETOOTH_ADMIN权限。注意:如果您使用BLUETOOTH_ADMIN权限,那么你还必须有BLUETOOTH权限。  声明蓝牙权限在你的应用程序清单(manifest)文件。例如: 
<uses-permission android:name="android.permission.BLUETOOTH"/> 
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 
如果你想声明,你的应用程序是只提供给BLE功能的设备,在您的应用程序的清单包括如下语句: 
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> 
不过,如果你想使你的应用程序提供给那些不支持BLE设备,你仍然应该在您的应用程序的清单包含这个上述语句,但设置required="false"。然后在运行时可以通过使用 PackageManager.hasSystemFeature()确定BLE可用性:

// 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(); }


设置BLE 
在你的应用程序可以进行BLE通信之前 ,你需要验证这个设备上BLE是否被支持,如果支持,请确保它已启用。请注意,如果<uses-feature.../>设置为false,这个检查才是必需的。 如果不支持BLE ,那么你应该适当地禁用任何BLE功能。如果BLE支持,但被禁用,那么你可以要求用户启动蓝牙时不要离开应用程序。这种设置两个步骤完成,使用 BluetoothAdapter.

1.获取BluetoothAdapter 
该BluetoothAdapter是所有的蓝牙活动所必需的。该BluetoothAdapter代表设备自身的蓝牙适配器(蓝牙无线电)。只有一个蓝牙适配器用于整个系统,并且你的应用程序可以使用该对象进行交互。下面的代码片段显示了如何获取适配器。注意,该方法使用
getSystemService() 返回BluetoothManager的一个实例, 用于获取适配器。Android 4.3 ( API级别18 )引入BluetoothManager: 
// Initializes Bluetooth adapter. 
 final BluetoothManager bluetoothManager=         
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); 
2. 启用蓝牙 
接下来,你需要确保蓝牙已启用。用isEnabled() 来检查蓝牙当前是否启用。如果此方法返回false,那么蓝牙被禁用。下面的代码片段检查蓝牙是否开启。如果不是,该片段将显示错误提示用户去设置以启用蓝牙:

private BluetoothAdapter mBluetoothAdapter; ... 
// Ensures Bluetooth is available on the device and it is enabled. If not, // displays a dialog requesting user permission to enable Bluetooth. if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new 
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 
    startActivityForResult(enableBtIntent,REQUEST_ENABLE_BT);  }

查找BLE设备 
为了找到BLE设备,您可以使用startLeScan() 方法。此方法需要一个
BluetoothAdapter.LeScanCallback作为参数。你必须实现这个回调,因为它决定扫描结果如何返回。因为扫描耗电量大,你应当遵守以下准则: 1)只要你找到所需的设备,停止扫描。 
2)不要扫描一个循环,并设置您的扫描时间限制。以前可用的设备可能已经移出范围,继续扫描消耗电池电量。 

如何你想要扫描指定类型的peripheral设备,你可以
用 startLeScan(UUID[],BluetoothAdapter.LeScanCallback)取代, 它提供了一组UUID对象用于说明你的应用程序支持的GATT services .

注意:你可以只扫描BLE设备或传统蓝牙设备,就像Bluetooth描述那样。你不可以同时扫描BLE设备和传统蓝牙设备。

连接到GATT server 
在和一个BLE设备交互的第一步是连接到它,更具体地,连接到所述装置上的GATT server。要连接到一个BLE设备上的GATT server,您可以使用connectGatt()方法。这个方法有三个参数:一个Context 对象,一个autoConnect(布尔值,表示是否自动连接到BLE装置)和BluetoothGattCallback: 
mBluetoothGatt = device.connectGatt(this,false, mGattCallback);   
这个函数连接到由BLE设备上的GATTserver,并返回一个BluetoothGatt实例,您可以使用它来进行GATT client操作。调用者(Android应用程序)是GATT client。该
BluetoothGattCallback是用来提供结果给client,如连接状态,以及任何进一步的GATT client操作。   
在这个例子中,BLE应用程序提供了一个活动(DeviceControlActivity)连接,显示数据,和显示该设备支持的GATTservices和characteristics。根据用户的输入,这一活动与一个叫BluetoothLeService的Service交互,BluetoothService通过Android BLE的API与 BLE设备进行交互:

当一个特定的回调被触发时,它会调用相应的broadcastUpdate()辅助方法并传递给它一个动作。注意,在该部分中的数据解析参照 Bluetooth Heart Rate Measurement profile specifications进行。

读取BLE属性 
一旦你的Android应用程序已连接到GATTserver和discoveriable service,它可以读取和写入支持的attributes。例如,通过server的service和characteristic这个片段进行迭代,在UI上显示它们:

接收GATT通知 (Notifications) 
这是常见的设备上的BLE应用程序要求被通知,当一个特定characteristic改变时。这段代码显示了如何使用 setCharacteristicNotification()方法设置一个Notifications用于characteristic:

一旦启动了属性通知( notifications for acharacteristic),如果在远程设备上characteristic 发生改变,onCharacteristicChanged() 回调函数将被启动。

关闭Client应用程序 
一旦你的应用程序已经使用BLE装置完成后,应该调用close(),这样系统就可以适当地释放

public void close() { 
    if (mBluetoothGatt == null) {         return;     } 
    mBluetoothGatt.close();     mBluetoothGatt = null; } 

-------------

一、关键概念:

Generic Attribute Profile (GATT)
通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
 
Attribute Protocol (ATT)
GATT是基于ATT Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
 
Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
 
Descriptor
对Characteristic的描述,例如范围、计量单位等。
 
Service
Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart rate measurement"的Characteristic。
 

二、角色和职责:

Android设备与BLE设备交互有两组角色:
 
中心设备和外围设备(Central vs. peripheral);
GATT server vs. GATT client.
 
Central vs. peripheral:
中心设备和外围设备的概念针对的是BLE连接本身。Central角色负责scan advertisement。而peripheral角色负责make advertisement。
 
GATT server vs. GATT client:
这两种角色取决于BLE连接成功后,两个设备间通信的方式。
 
举例说明:
现 有一个活动追踪的BLE设备和一个支持BLE的Android设备。Android设备支持Central角色,而BLE设备支持peripheral角 色。创建一个BLE连接需要这两个角色都存在,都仅支持Central角色或者都仅支持peripheral角色则无法建立连接。
 
当 连接建立后,它们之间就需要传输GATT数据。谁做server,谁做client,则取决于具体数据传输的情况。例如,如果活动追踪的BLE设备需要向 Android设备传输sensor数据,则活动追踪器自然成为了server端;而如果活动追踪器需要从Android设备获取更新信息,则 Android设备作为server端可能更合适。
 
 

三、权限及feature:

和经典蓝牙一样,应用使用蓝牙,需要声明BLUETOOTH权限,如果需要扫描设备或者操作蓝牙设置,则还需要BLUETOOTH_ADMIN权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
 
除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
 
按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature:
 
// 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();
}
 
 

四、启动蓝牙:

在使用蓝牙BLE之前,需要确认Android设备是否支持BLE feature(required为false时),另外要需要确认蓝牙是否打开。 
如果发现不支持BLE,则不能使用BLE相关的功能。如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。
 
打开蓝牙的步骤:
 
1、获取BluetoothAdapter
 
BluetoothAdapter是Android系统中所有蓝牙操作都需要的,它对应本地Android设备的蓝牙模块,在整个系统中BluetoothAdapter是单例的。当你获取到它的示例之后,就能进行相关的蓝牙操作了。
 
获取BluetoothAdapter代码示例如下:
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
        (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
 
注:这里通过getSystemService获取BluetoothManager,再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
 
2、判断是否支持蓝牙,并打开蓝牙
 
获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。
如果没打开,需要让用户打开蓝牙:
private BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
 
 

五、搜索BLE设备:

通过调用BluetoothAdapter的 startLeScan()搜索BLE设备。调用此方法时需要传入  BluetoothAdapter.LeScanCallback参数。
因此你需要实现  BluetoothAdapter.LeScanCallback接口,BLE设备的搜索结果将通过这个callback返回。
 
由于搜索需要尽量减少功耗,因此在实际使用时需要注意:
 
1、当找到对应的设备后,立即停止扫描;
2、不要循环搜索设备,为每次搜索设置适合的时间限制。避免设备不在可用范围的时候持续不停扫描,消耗电量。
 
搜索的示例代码如下:
/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    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);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }
...
}
 
如果你只需要搜索指定UUID的外设,你可以调用  startLeScan(UUID[], BluetoothAdapter.LeScanCallback)方法。
其中UUID数组指定你的应用程序所支持的GATT Services的UUID。
 
 
BluetoothAdapter.LeScanCallback的实现示例如下:
private LeDeviceListAdapter mLeDeviceListAdapter;
...
// 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();
           }
       });
   }
};
 
注意:搜索时,你只能搜索传统蓝牙设备或者BLE设备,两者完全独立,不可同时被搜索。
 
 

六、连接GATT Server:

两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server。
连接GATT Server,你需要调用BluetoothDevice的 connectGatt()方法。此函数带三个参数:Context、autoConnect(boolean)和 BluetoothGattCallback对象。调用示例:
 
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
 
函数成功,返回 BluetoothGatt对象,它是GATT profile的封装。通过这个对象,我们就能进行GATT Client端的相关操作。 BluetoothGattCallback用于传递一些连接状态及结果。
 
BluetoothGatt常规用到的几个操作示例:
 
connect() :连接远程设备。
discoverServices() : 搜索连接设备所支持的service。
disconnect():断开与远程设备的GATT连接。
close():关闭GATT Client端。
readCharacteristic(characteristic) :读取指定的characteristic。
setCharacteristicNotification(characteristic, enabled) :设置当指定characteristic值变化时,发出通知。
getServices() :获取远程设备所支持的services。
 
等等。
 
注:
1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。
2、 一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。例如 discoverServices与onServicesDiscovered回调,readCharacteristic与 onCharacteristicRead回调,setCharacteristicNotification与 onCharacteristicChanged回调等。
 
http://www.blogjava.net/zh-weir/archive/2014/04/02/407373.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值