蓝牙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