Android BLE4.0(蓝牙通信)

前言:

本文参考文献:
1、https://learn.adafruit.com/introduction-to-bluetooth-low-energy?view=all
中文翻译:
http://www.race604.com/gatt-profile-intro/
2、http://blog.csdn.net/jimoduwu/article/details/21604215

一、工作原理

在说蓝牙通信之前,我们先了解一下蓝牙的工作原理。

蓝牙规定每一对设备进行通讯时,必须一个为主角色,另一为从角色,必须由主端进行查找,发起配对。连接成功,双方才可收发数据。

一个具备蓝牙功能的设备,可以在两个角色间切换,平时处于从模式,等待其它主设备来连接,需要时,转换为主模式,向其它设备发起呼叫。一个蓝牙设备以主模式发起连接时,需要知道对方的蓝牙地址,配对密码等信息。

二、相关名词和概念

在介绍名词概念之前,我们应该知道,现在的BLE连接都是建立在 GATT (Generic Attribute Profile) 协议之上。GATT 是一个在蓝牙连接之后,发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。

GAP

介绍 GATT 之前,需要了解 GAP (Generic Access Profile),它用来控制设备连接和广播。GAP 使一个设备可被其他设备发现,并决定了该设备是否可以或者怎样与其他设备进行交互。

GAP 给设备定义了若干角色,其中主要的两个是:

  • 外围设备(Peripheral):一般是非常小或者简单的低功耗设备,用来提供数据,并能够连接到一个更加相对强大的中心设备。如蓝牙手环。
  • 中心设备(Central):相对比较强大,用来连接其他外围设备。如手机。

GATT

GATT 的全名是 Generic Attribute Profile(普通属性协议),它定义两个 BLE 设备通过叫做 Service 和 Characteristic 的东西进行通信。

中心设备和外设需要双向通信的话,唯一的方式就是建立 GATT 连接。

GATT 结构

GATT 事务是建立在嵌套的Profiles、Services 和 Characteristics之上的,如图:

《Android BLE4.0(蓝牙通信)》 Paste_Image.png

  • Profile: Profile 并不是实际存在于 BLE 外设上的,它只是一个被 Bluetooth SIG 或者外设设计者预先定义的 Service 的集合。

  • Service: Service 是把数据分成一个个独立逻辑项,它包含一个或者多个 Characteristic。每个 Service 有一个 UUID 唯一标识。

  • Characteristic: Characteristic在 GATT 事务中是最低级别的,是最小的逻辑数据单元,与 Service 类似,每个 Characteristic 也有一个UUID 唯一标识。

三、蓝牙通信

1、对应Android的API

在Android BLE的API中,对应于GATT结构的有3个文件:

  • ** BluetoothGatt**
    作为中央来使用和处理数据,通过device.connectGatt(this, false, mGattCallback) 得到。

  • ** BluetoothGattServer**
    作为周边来提供数据,通过BluetoothGatt.getService(uuid)获取指定的BluetoothGattServer。

  • ** BluetoothGattCharacteristic**
    周边服务的一些特性,分为Read,Write,notification。

2、获取蓝牙连接状态

2.1 BluetoothGattCallback

在上一篇中介绍了如何连接一个设备:

device.connectGatt(this, false, mGattCallback) 

其中有一个BluetoothGattCallback 的一个实例:mGattCallback。

BluetoothGattCallback是返回中央的状态和周边提供的数据的一个重要的抽象类。看一下源码:


public abstract class BluetoothGattCallback {

    /**
     * 当GATT客户端已连接到GATT服务器或者从GATT服务器断开连接
     * 时回调。
     *
     * @param gatt 
     * @param status 连接或断开操作的状态。BluetoothGatt.GATT_SUCCESS表示操作成功
     *  
     * @param newState 返回新的连接状态。 如 BluetoothProfile.STATE_DISCONNECTED或
      * BluetoothProfile.STATE_CONNECTED
     */
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                        int newState) {
    }

    /**
     * 当远程设备的远程服务列表,特征和描述符已被更新,即已发现新服务时,调用回调。
     *
     * @param gatt 
     * @param status BluetoothGatt.GATT_SUCCESS 远程设备的远程服务列表可被发现
     */
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    }

    /**
     * Read特性的操作回调
     *
     * @param gatt 
     * @param characteristic 从相关的远程设备读取的特性。
     * @param status BluetoothGatt.GATT_SUCCESS 操作成功
     */
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
                                     int status) {
    }

    /**
     * write 特性的操作回调
     *
     * 如果在可靠的写事务正在进行时调用此回调,则characteristic的值表示远程设备报告的值。
     * 应用程序应该将此值与要写入的所需值进行比较。
     * 如果值不匹配,应用程序必须中止可靠的写事务。
     *
     * @param gatt 
     * @param characteristic 
     * @param status write 操作的结果
     *               {BluetoothGatt.GATT_SUCCESS}
     */
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic, int status) {
    }

    /**
     * notification 特性的结果
     *
     * @param gatt 
     * @param characteristic 由于远程通知事件而更新的特性。
     */
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
    }

    /**
     * 报告描述符读操作的结果的回调.
     *
     * @param gatt 
     * @param descriptor 从关联的远程设备读取的描述符。
     * @param status 如果读操作已成功完成 {BluetoothGatt.GATT_SUCCESS} 
     */
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                 int status) {
    }

    /**
     * 指示描述符写操作的结果。
     *
     * @param gatt 
     * @param descriptor 写入相关远程设备的描述符
     * @param status 如果操作成功,写入操作的结果为{BluetoothGatt#GATT_SUCCESS}。
     */
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                  int status) {
    }

    /**
     * 当可靠的写事务已完成时调用回调。
     *
     * @param gatt 
     * @param status 如果可靠的写事务已成功执行,则{BluetoothGatt#GATT_SUCCESS}
     */
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
    }

    /**
     * 报告远程设备连接的RSSI。
     *
     * 此回调是响应{BluetoothGatt#readRemoteRssi}函数而触发的。
     *
     * @param gatt 
     * @param rssi 远程设备的RSSI值
     * @param status 如果RSSI已成功读取 {BluetoothGatt#GATT_SUCCESS}
     */
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
    }

    /**
     * 指示给定设备连接的MTU已更改。
     *
     * 此回调是响应{BluetoothGatt#requestMtu}函数或响应连接事件而触发的。
     *
     * @param gatt 
     * @param mtu 新的MTU大小
     * @param status 如果MTU已成功更改{ BluetoothGatt#GATT_SUCCESS}
     */
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
    }
}

从上面的代码可以看出,当我们与一个外设建立连接时,连接的状态以及连接成功之后与设备通信都会在BluetoothGattCallback中回调过来。

3、与设备的通信

3.1、BluetoothGatt.discoverServices()

此方法在成功连接到远程设备时调用,不调用此方法,无法与远程设备进行后续的通信。

@Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.i(TAG, "Connected to GATT server.");
            mBluetoothGatt.discoverServices();
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.i(TAG, "Disconnected from GATT server.");
        }
    }

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

@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            //服务发现成功
        } else {
            //服务发现失败
        }
    }

只有当Service可被发现是即status为BluetoothGatt.GATT_SUCCESS时,我们才能继续后续的操作。

3.2、启用notification

如果设备主动发信息,可以通过notification的方式,这种方式不用去轮询地读设备上的数据。

        BluetoothGattService service = bluetoothGatt.getService(serviceUuid);
        if (service == null) {
            return;
        }

        BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
        if (characteristic == null) {
            return;
        }

        bluetoothGatt.setCharacteristicNotification(characteristic, true);//激活通知

        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descriptorUuid);
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        bluetoothGatt.writeDescriptor(descriptor);

如果notificaiton方式对于某个Characteristic是enable的,那么当设备上的这个Characteristic改变时,手机上的onCharacteristicChanged()回调就会被促发。


        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);

            byte[] notice = characteristic.getValue();

            onNotify(notice);
        }

3.3、发送数据

BluetoothGattService service = bluetoothGatt.getService(serviceUuid);
    if (service == null) {
        return;
    }
    BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
    if (characteristic == null) {
        return;
    }
    characteristic.setValue(data);
    bluetoothGatt.writeCharacteristic(characteristic);
}

3.4、读取数据


    public void readData(UUID serviceUuid, UUID characteristicUuid) {
        BluetoothGattService service = bluetoothGatt.getService(serviceUuid);
        if (service == null) {
            return;
        }
        BluetoothGattCharacteristic characteristic = service.getCharacteristic(characteristicUuid);
        if (characteristic == null) {
            return;
        }
        bluetoothGatt.readCharacteristic(characteristic);
    }

官方建议在进行read是如果有通知服务进行中,先关闭

bluetoothGatt.setCharacteristicNotification(characteristic, false);

读取的数据会在onCharacteristicRead()回调中返回

@Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            //读取到值,在这里读数据
            if (status == BluetoothGatt.GATT_SUCCESS) {
            }
        }

4、断开连接

public void disConnection() {
    if (bluetoothGatt == null) {
        return;
    }
    bluetoothGatt.disconnect();
}

5、关闭Gatt

使用给定的BLE设备后,应用程序必须调用此方法以确保资源正确释放。

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

总结:

关于蓝牙BLE的开发流程,到这里就结束了,从扫描、连接到与设备的收发数据,基本上也就这些东西。

最后加上GitHub上面的仓库:https://github.com/eson-yunfei/AndroidBle ;项目刚创建不久,还在完善,欢迎大家Fork。

    原文作者:esonyf
    原文地址: https://www.jianshu.com/p/2b4620c52d47
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞