蓝牙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() {

		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status,
				int newState) {
			// TODO Auto-generated method stub
			super.onConnectionStateChange(gatt, status, newState);
		}

		@Override
		public void onServicesDiscovered(BluetoothGatt gatt, int status) {
			// TODO Auto-generated method stub
			super.onServicesDiscovered(gatt, status);
		}

		@Override
		public void onCharacteristicRead(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			// TODO Auto-generated method stub
			super.onCharacteristicRead(gatt, characteristic, status);
		}

		@Override
		public void onCharacteristicWrite(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			// TODO Auto-generated method stub
			super.onCharacteristicWrite(gatt, characteristic, status);
		}

		@Override
		public void onCharacteristicChanged(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic) {
			// TODO Auto-generated method stub
			super.onCharacteristicChanged(gatt, characteristic);
		}

		@Override
		public void onDescriptorRead(BluetoothGatt gatt,
				BluetoothGattDescriptor descriptor, int status) {
			// TODO Auto-generated method stub
			super.onDescriptorRead(gatt, descriptor, status);
		}

		@Override
		public void onDescriptorWrite(BluetoothGatt gatt,
				BluetoothGattDescriptor descriptor, int status) {
			// TODO Auto-generated method stub
			super.onDescriptorWrite(gatt, descriptor, status);
		}

		@Override
		public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
			// TODO Auto-generated method stub
			super.onReliableWriteCompleted(gatt, status);
		}

		@Override
		public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
			// TODO Auto-generated method stub
			super.onReadRemoteRssi(gatt, rssi, status);
		}

		@Override
		protected Object clone() throws CloneNotSupportedException {
			// TODO Auto-generated method stub
			return super.clone();
		}

		@Override
		public boolean equals(Object o) {
			// TODO Auto-generated method stub
			return super.equals(o);
		}

		@Override
		protected void finalize() throws Throwable {
			// TODO Auto-generated method stub
			super.finalize();
		}

		@Override
		public int hashCode() {
			// TODO Auto-generated method stub
			return super.hashCode();
		}

		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return 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 value
uint8 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