ble蓝牙通信

来源:互联网 发布:js年龄正则表达式 编辑:程序博客网 时间:2024/05/18 05:08

蓝牙4.0和传统蓝牙classic bluetooth不同,使用的是GATT协议进行的通信,这里描述下通信过程。传统的classic bluetooth此处暂不描述。

BLE通信使用的是属性赋值的方式进行的通信,每次只能传输少量的数据,具体最大传输多少还没时间去查看。

简单来说,对于一个bluetooth device,可以获取到若干个service,每个service又包含若干个characteristic,而每个characteristic又可以包含若干descriptor来额外限定规范characteristic的一些属性权限之类的。

一般通信使用的是characteristic对象来进行write,read。

首先获取device:

 mBluetoothAdapter.startLeScan(mLeScanCallback);

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


此后大概通信流程如下:首先获取GATT对象:

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

这里device上述已知,通过扫描即可得到。

mGattCallback为通信消息回调,任何状态的改变,读写数据传输都会回调这个接口。

private final BluetoothGattCallback gttCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) {// TODO Auto-generated method stubsuper.onConnectionStateChange(gatt, status, newState);}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {// TODO Auto-generated method stubsuper.onServicesDiscovered(gatt, status);}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {// TODO Auto-generated method stubsuper.onCharacteristicRead(gatt, characteristic, status);}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {// TODO Auto-generated method stubsuper.onCharacteristicWrite(gatt, characteristic, status);}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic) {// TODO Auto-generated method stubsuper.onCharacteristicChanged(gatt, characteristic);}@Overridepublic void onDescriptorRead(BluetoothGatt gatt,BluetoothGattDescriptor descriptor, int status) {// TODO Auto-generated method stubsuper.onDescriptorRead(gatt, descriptor, status);}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt,BluetoothGattDescriptor descriptor, int status) {// TODO Auto-generated method stubsuper.onDescriptorWrite(gatt, descriptor, status);}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {// TODO Auto-generated method stubsuper.onReliableWriteCompleted(gatt, status);}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {// TODO Auto-generated method stubsuper.onReadRemoteRssi(gatt, rssi, status);}@Overrideprotected Object clone() throws CloneNotSupportedException {// TODO Auto-generated method stubreturn super.clone();}@Overridepublic boolean equals(Object o) {// TODO Auto-generated method stubreturn super.equals(o);}@Overrideprotected void finalize() throws Throwable {// TODO Auto-generated method stubsuper.finalize();}@Overridepublic int hashCode() {// TODO Auto-generated method stubreturn super.hashCode();}@Overridepublic String toString() {// TODO Auto-generated method stubreturn super.toString();}};

那么获取到gatt对象之后,便会回调onConnectionStateChange,具体参数是:onConnectionStateChange(BluetoothGatt gatt, int status, int newState)。通常如果连接成功,便会进入if (newState == BluetoothProfile.STATE_CONNECTED),如果连接不成功,便会走if (newState == BluetoothProfile.STATE_DISCONNECTED)

如果连接不成功,需要重新连接。这里分析连接成功的分支:

在连接成功的地方直接进行扫描设备:mBluetoothGatt.discoverServices()

上面这句也会走回调通知,如果执行成功,便会走回调onServicesDiscovered,然后在这里需要对service下的所有characteristic进行setnotification操作,

代码见下:

BluetoothGattService   service = mBluetoothGatt.getService(SERVICE_UUID);

mBluetoothGatt就是上面获取到的gatt对象,SERVICE_UUID是服务id,和设备相关,请联系设备提供商获取。

然后对每个characteristic进行notify操作,代码见下:

List<BluetoothGattCharacteristic> gattCharacteristics = service.getCharacteristics();for (int j = 0; j < gattCharacteristics.size(); j++) {BluetoothGattCharacteristic chara = gattCharacteristics.get(j);                        <strong>setCharacteristicNotification(mBluetoothGatt,chara, true);</strong>。。。}  
setCharacteristicNotification(mBluetoothGatt,chara, true);

上面这行说明一下,对某一个characteristic进行setnorification操作,

bluetoothGatt.setCharacteristicNotification(characteristic, true);
        try {
            BluetoothGattDescriptor descriptor = characteristic
                    .getDescriptor(VALUE);
            if (descriptor != null) {
                descriptor
                        .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                return (bluetoothGatt.writeDescriptor(descriptor));
            } else {
                return false;
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }

上面代码中的VALUE就是要从每一个characteristic寻找合适的descriptor,找到了就setnotification,找不到不做操作,实际上有些有,有些并没有,需要调试。

说明:上面那段有点混乱,是因为每次对某一个characteristic进行setCharacteristicNotification之后,需要在回调ondescriptrowrite回调中对下一个characteristic接着进行setCharacteristicNotification操作,必须一个一个的来,不能一次在for循环中全部执行出去,那样容易引发错误,总体思路就是挨个对List<BluetoothGattCharacteristic> gattCharacteristics  中的characteristic进行setCharacteristicNotification操作之后,就可以了。

在对所有的characteristic进行setnotification操作中,必须指定一个write-characteristic对象,也就是指明哪个是用来负责写操作的characteristic对象,然后以后的通信过程中,都用这个write-characteristic进行赋值操作即可。

  下面可以使用gatt对象来进行蓝牙通信了,比如读characteristic:

    /**     * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported     * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}     * callback.     *     * @param characteristic The characteristic to read from.     */    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {        if (mBluetoothAdapter == null || mBluetoothGatt == null) {            Log.w(TAG, "BluetoothAdapter not initialized");            return;        }        mBluetoothGatt.readCharacteristic(characteristic);    }
还有写:

    public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {        if (mBluetoothAdapter == null || mBluetoothGatt == null) {            Log.w(TAG, "BluetoothAdapter not initialized");            return;        }        byte[] b=  HexBytesUtils.hexStr2Bytes("xxx");        characteristic.setValue(b);        mBluetoothGatt.writeCharacteristic(characteristic);    }
,这种主动调用方法的返回都会走异步回调,会回到上面mGattCallback的相应方法中,比如:

read会走onRead,write会走onWrite等等。

具体如下:

BluetoothGatt gatt = device.connectGatt(this, false, mGattCallback);
1.8.1:notification对应onCharacteristicChanged;
gatt.setCharacteristicNotification(characteristic, true);

1.8.2:readCharacteristic对应onCharacteristicRead;
gatt.readCharacteristic(characteristic);

1.8.3: writeCharacteristic对应onCharacteristicWrite;
gatt.wirteCharacteristic(mCurrentcharacteristic);

1.8.4:连接蓝牙或者断开蓝牙 对应 onConnectionStateChange;

1.8.5: readDescriptor对应onDescriptorRead;

1.8.6:writeDescriptor对应onDescriptorWrite;
gatt.writeDescriptor(descriptor);

1.8.7:readRemoteRssi对应onReadRemoteRssi;
gatt.readRemoteRssi()

1.8.8:executeReliableWrite对应onReliableWriteCompleted;

1.8.9:discoverServices对应onServicesDiscovered。
gatt.discoverServices()

======================================================

下面继续说明:

BluetoothGattDescriptor,这个类是对characteristic的一种通信的约束,限定,比如是否notiry,是否增加某种限制,具体解释见下:

/**
 * Represents a Bluetooth GATT Descriptor
 *
 * <p> GATT Descriptors contain additional information and attributes of a GATT
 * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
 * the characteristic's features or to control certain behaviours of the characteristic.
 */

其中

  // If a given GATT characteristic is selected, check for supported features.  This sample
    // demonstrates 'Read' and 'Notify' features.  See
    // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
    // list of supported characteristic features.

 final int charaProp = characteristic.getProperties();                        if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {                            // If there is an active notification on a characteristic, clear                            // it first so it doesn't update the data field on the user interface.                            if (mNotifyCharacteristic != null) {                                mBluetoothLeService.setCharacteristicNotification(                                        mNotifyCharacteristic, false);                                mNotifyCharacteristic = null;                            }                            mBluetoothLeService.readCharacteristic(characteristic);                                             }

需要注意的一个问题是(摘抄自网页http://www.myext.cn/Android/a_4699.html):

1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。
2、一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。
例如discoverServices与onServicesDiscovered回调,readCharacteristic与onCharacteristicRead回调,
setCharacteristicNotification与onCharacteristicChanged回调等。

========================================================
本文分析基本按照官方BLE sample做的分析,官方的sample中有几个类,这里少做描述:
BluetoothLeService:调用gatt进行相关的通信,比如读写,连接断开之类的操作。
/** * Service for managing connection and data communication with a GATT server hosted on a * given Bluetooth LE device. */
DeviceControlActivity:主动调用类,负责UI展现,数据主动发送操作等。本例使用ExpandableListView展示service,characteristic之间的所属关系,并读取了characteristic中的value进行显示。
/** * For a given BLE device, this Activity provides the user interface to connect, display data, * and display GATT services and characteristics supported by the device.  The Activity * communicates with {@code BluetoothLeService}, which in turn interacts with the * Bluetooth LE API. */
DeviceScanActivity:主要用来扫描BLE device,获取周围所有的BLE设备,显示mac之类的信息,UI是一个列表,点击listitem跳转到DeviceControlActivity类进行更详细的展现。
/** * Activity for scanning and displaying available Bluetooth LE devices. */
HexBytesUtils:辅助类,因为BLE通信的数据基本都为Hex形式的,所以需要Hex --> byte --> String进行转换显示,便于识别。
SampleGattAttributes:辅助类,主要定义了一些UUID,可以供BluetoothLeService根据相应的需求进行使用。
/** * This class includes a small subset of standard GATT attributes for demonstration purposes. */
================================================================================

说明:BLE通信的数据量一次为20字节,16进制。
只有20。协议规定,payload 最大27。有兴趣你可以去看蓝牙的协议栈。第六章中的2.4.刨去L2CAP的头,4个字节,剩下的就23个字节MTU。就是你看到的。ATT层会用掉上1个字节的op code, 2个字节的attribute handle,就剩下20了。不要被517所迷惑,这个是目前CC254x中ATT测试的时候用的。
你们看看notification那个数据结构:typedef struct{uint16 handle; //!< Handle of the attribute that has been changed (must be first field)uint8 len; //!< Length of valueuint8 value[ATT_MTU_SIZE-3]; //!< New value of the attribute ,Minimum ATT MTU size =23 宏定义为23} attHandleValueNoti_t;value[ATT_MTU_SIZE-3]; //!< New value of the attribute ,Minimum ATT MTU size =23 宏定义为23 而ATT_MTU_SIZE大小可以设置为23-517字节!所以说BLE是可以实现最大517字节帧的传递的!现在主要是在设置属性表characteristic数组大小的时候,不能超过20字节,超过20字节的大小的数组就连接不上主机,估计是程序跑偏,我想楼主想问的问题就是characteristic最大能定义多大,而不是说BLE能传递多大的数据!BLE底层协议栈是可以传递517字节以下的数据帧的,估计是TI底层了characteristic的数组大小,不知道我这样的理解正不正确,希望TI员工出来解释下为何characteristic最大只能定义20字节的数组,但是他notification数据value的数组大小确可以再23-517字节之间的范围内选择?如果characteristic只能20字节,notification数据有517字节有何用啊?按照BLE传递的数据包结构推算,传递20字节的characteristic整个数据包不会超过50字节!好比水池里的水就是1吨,你要用适配100吨的水管去放水池的水,是不是有点老牛拉小车?

传输数据大小参考地址:
http://www.deyisupport.com/question_answer/wireless_connectivity/bluetooth/f/103/t/53406.aspx


从上面的分析中,基本看出BLE通信的过程,
首先扫描BLE device,然后通过device获取gatt对象,然后使用gatt去对相关的characteristic进行readcharacteristic和
writecharacteristic操作,执行操作结果会异步回调到gattcallback回调中。如此循环来进行通信。

说明:根据目前的情况看,当调用writecharacteristic之后,会走onCharacteristicWrite回调,也就是会调用public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic)
是否走回调,是和characteristic的属性相关的,比如属性为只写,那么便不会回调,如果属性为enable notify,则会走回调。而且回调的characteristic和writecharacteristic的对象是同一个。
更多描述见下面一篇文章分析。


本文参考文章有:
http://www.2cto.com/kf/201411/349575.html
http://www.myext.cn/Android/a_4699.html

待续


0 0
原创粉丝点击