连接BLE设备传输数据
安卓设备搜索到附近的蓝牙低功耗设备,并显示在一个ListView中后,就可以通过点击对应项与相应设备建立连接。
1)选择蓝牙低功耗设备:
为了连接ListView中相应项的设备,需要先为名为lv的ListView加入点击事件处理:
对应的事件处理函数为:
与BLE设备建立连接,需要使用BLE的MAC地址,在ListView中显示的每一项Item都是由name和Adress两部分组成,用"\n"分隔,因此代码中要把此字符串按"\n"分开为数组,索引1即为MAC地址。使用BluetoothAdapter的getRemoteDevice(macAddress)方法就能获取到连接对端的BluetoothDevice设备。
蓝牙低功耗使用Gatt协议,因此要使用BluetoothGatt建立连接,使用的是BluetoothDevice的connectGatt()方法,其中使用了一个建立连接的回调函数gattcallback。
当然,也可以不用在界面中列出附近BLE中的每一项,而是通过获取的BLE的name或MAC地址等信息选择连接的对端。
连接BLE设备的代码在gattcallback回调函数中:
在Android Studio中,这个函数具有固定的结构,包括GATT连接变化响应、GATT连接发现服务、GATT读属性、GATT写属性、GATT属性变化、GATT读描述、GATT写描述等等多个方法,只需要使用其中几个方法并修改其中的代码,其他方法可以使用系统提供的默认代码。
2)蓝牙低功耗设备的连接响应:
代码中,定义了4种连接状态,已连接、正在连接、已断开、正在断开,使用switch结构分别进行处理,并在一个名为sta的TextView中进行提示,不过一些状态持续时间比较短,未必能在界面中看到对应显示。
这个连接状态响应函数的主体是在一个单独线程中进行的,主要是为了避免在主界面出现卡死现象。如果连接成功,就会执行BluetoothGatt的discoverServices()方法,获取对端可以提供的服务。
2)蓝牙低功耗设备的服务发现:
要建立蓝牙低功耗数据传输,需要先得到连接对端的UUID对应的Characteristic。蓝牙连接建立后,就要运行可提供服务的发现代码:
代码中,如果状态status == btGatt.GATT_SUCCESS,即GATT协议建立成功,就使用BluetoothGatt的getServices()方法获取对端可以提供的服务BluetoothGattService,并存入BluetoothGattService类型的列表services中,后面的代码也是在一个单独的线程中运行,避免界面出现卡死现象。
然后在获取到的服务列表中遍历,使用getUuid()方法获取服务的UUID信息;再使用getCharacteristics()方法获取每个服务的特性BluetoothGattCharacteristic列表,继而遍历所有特性,得到每个Characteristic的UUID;再使用getDescriptors()方法获取每个特性的描述BluetoothGattDescriptor列表,遍历描述列表,使用getPermissions()方法获取授权信息,最后加以显示。这样就可以在界面中的TextView中显示出来对端所有的服务和特性的UUID,并有授权信息,当然也可以加入其他代码以显示出更多信息。
后面一部分代码是通过预先设置的UUID字符串进行判断,如果与TXDUUID相同,就将此Bluetooth Gatt Characteristic设为BLE的GATT发送Characteristic,如果与RXUUID相同,就设为BLE的GATT的接收Characteristic并使用enableNotification()方法使能其Notification功能。对特定的蓝牙低功耗模块硬件及软件版本,有固定的发送/接收数据的UUID,通过上述方法获取到后,就可以对应进行收发数据了。
3)蓝牙低功耗Notification及功能设置:
对蓝牙低功耗设备,发送数据使用write()方法,而获取数据则使用的是Notification。也就是在建立双方连接后,对端如果有数据就会以Notification方式传送过来,而Android系统则是通过onCharacteristicChanged事件处理获取,代码为:
在Android系统中,蓝牙低功耗数据收发都使用字节数组byte[]格式,从对端传来的数据使用接收characteristic的getValue()方法,获得的就是字节数组byte[]。
后续处理也在一个单独的线程中进行,如果是普通字符数据,可以使用new String(values)直接将字节数组转换为一个对应的字符串,然后在TextView中显示出来;但如果要以十六进制值的方式显示,就要按字节逐个转换,并连接为一个十六进制字符串,再赋值给一个TextView的文本以便显示。
上述代码也是需要在BluetoothGatt连接的Callback函数中定义,是回调的一部分。
不过,连接对端的Notification默认情况下并未使能,在连接过程中获取到接收Characteristic后,需要使用enableNotification()方法使其有效,具体代码为:
代码中,先直接使用BluetoothGatt的setCharacteristicNotification()方法,把对应的characteristic的Notification特性设为true;根据返回值进行判断,如果为true,使用getDescriptors()方法获取此characteristic的描述列表,如果获取到就进行遍历,对其中每一个descriptor都使用setValue()方法设置值为ENABLE _NOTIFICATION _VALUE。然后使用writeDescriptor()方法,如果返回值为true表示配置成功。
上述代码在BluetoothGatt连接的Callback函数之外,其中有多个分支,目的是配置不成功时检查是哪一步出现了问题,因此赋值了不同的返回值字符串,方便调试,调试完成运行正常后可以不再显示返回的字符串。
通过实测,连接的蓝牙低功耗模块启动时,具有Notification功能的Characteristic,有个16bit的可配置值,初始为0000,如果使用enableNotification()方法配置成功,低位就会变为1,这样就使能了Notification。不同的模块可能也会有一些差别,但通过此指令使能Notification功能是一致的。
4)蓝牙低功耗发送数据:
Android设备向蓝牙低功耗设备发送数据也是使用字节数组格式,代码为:
代码中,先从名为txd的TextView中获取到值并转换为字符串,直接使用String的getBytes()方法得到对应的字节数组,然后使用setValue()方法把此字节数组传递给对应的Bluetooth Gatt Characteristic,最后使用BluetoothGatt的writeCharacteristic()方法将数据发送出去。
蓝牙低功耗一次发送的数据一般为20字节左右,较大的数据包需要截为数段分别发送。为了避免发送数据操作影响界面的响应,可以放在单独的线程中。也可以根据发送的内容不同,使用不同的编码转换方法。
1)选择蓝牙低功耗设备:
为了连接ListView中相应项的设备,需要先为名为lv的ListView加入点击事件处理:
lv.setOnItemClickListener(this);
对应的事件处理函数为:
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
TextView txv = (TextView) v;
String s = txv.getText().toString();
String[] addr = s.split("\n");
btDevice = btAdapter.getRemoteDevice(addr[1]); //通过MAC地址获取蓝牙设备
//建立GATT连接
btGatt = btDevice.connectGatt(MainActivity.this, false, gattcallback);
}
与BLE设备建立连接,需要使用BLE的MAC地址,在ListView中显示的每一项Item都是由name和Adress两部分组成,用"\n"分隔,因此代码中要把此字符串按"\n"分开为数组,索引1即为MAC地址。使用BluetoothAdapter的getRemoteDevice(macAddress)方法就能获取到连接对端的BluetoothDevice设备。
蓝牙低功耗使用Gatt协议,因此要使用BluetoothGatt建立连接,使用的是BluetoothDevice的connectGatt()方法,其中使用了一个建立连接的回调函数gattcallback。
当然,也可以不用在界面中列出附近BLE中的每一项,而是通过获取的BLE的name或MAC地址等信息选择连接的对端。
连接BLE设备的代码在gattcallback回调函数中:
private BluetoothGattCallback gattcallback = new BluetoothGattCallback() {}
在Android Studio中,这个函数具有固定的结构,包括GATT连接变化响应、GATT连接发现服务、GATT读属性、GATT写属性、GATT属性变化、GATT读描述、GATT写描述等等多个方法,只需要使用其中几个方法并修改其中的代码,其他方法可以使用系统提供的默认代码。
2)蓝牙低功耗设备的连接响应:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) {
super.onConnectionStateChange(gatt, status, newState);
runOnUiThread(new Runnable() {
@Override
public void run() {
String status;
switch (newState) {
case BluetoothGatt.STATE_CONNECTED:
sta.setText(btDevice.getAddress()+"已连接");
btGatt.discoverServices();
break;
case BluetoothGatt.STATE_CONNECTING:
sta.setText("正在连接...");
break;
case BluetoothGatt.STATE_DISCONNECTED:
sta.setText("已断开");
break;
case BluetoothGatt.STATE_DISCONNECTING:
sta.setText("断开中...");
break;
}
}
});
}
代码中,定义了4种连接状态,已连接、正在连接、已断开、正在断开,使用switch结构分别进行处理,并在一个名为sta的TextView中进行提示,不过一些状态持续时间比较短,未必能在界面中看到对应显示。
这个连接状态响应函数的主体是在一个单独线程中进行的,主要是为了避免在主界面出现卡死现象。如果连接成功,就会执行BluetoothGatt的discoverServices()方法,获取对端可以提供的服务。
2)蓝牙低功耗设备的服务发现:
要建立蓝牙低功耗数据传输,需要先得到连接对端的UUID对应的Characteristic。蓝牙连接建立后,就要运行可提供服务的发现代码:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
txt = "";
if (status == btGatt.GATT_SUCCESS) {
final List<BluetoothGattService> services = btGatt.getServices();
runOnUiThread(new Runnable() {
@Override
public void run() {
for (final BluetoothGattService bluetoothGattService : services) {
gattSVC = bluetoothGattService;
txt = txt + "\n" + gattSVC.getUuid().toString(); //显示UUID列表
List<BluetoothGattCharacteristic> charc = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic charac : charc) {
tz = charac.getUuid().toString();
List<BluetoothGattDescriptor> dess=charac.getDescriptors();
String str="";
for(int i=0;i<dess.size();i++){
str+=dess.get(i).getPermissions();
}
String ret="";
txt = txt + "\n\t" + tz+"*"+str; //显示属性UUID列表
rxd.setText(txt); //显示UUID列表
if ((tz.equals(TXDUUID)) ) {
gattTxd = charac;
btTxd.setEnabled(true); //发送按钮使能
}
if ((tz.equals(RXUUID)) ) {
gattRxd = charac;
enableNotification(true, gattRxd);
}
rxd.setText(txt); //显示UUID列表
}
}
}
});
}
}
代码中,如果状态status == btGatt.GATT_SUCCESS,即GATT协议建立成功,就使用BluetoothGatt的getServices()方法获取对端可以提供的服务BluetoothGattService,并存入BluetoothGattService类型的列表services中,后面的代码也是在一个单独的线程中运行,避免界面出现卡死现象。
然后在获取到的服务列表中遍历,使用getUuid()方法获取服务的UUID信息;再使用getCharacteristics()方法获取每个服务的特性BluetoothGattCharacteristic列表,继而遍历所有特性,得到每个Characteristic的UUID;再使用getDescriptors()方法获取每个特性的描述BluetoothGattDescriptor列表,遍历描述列表,使用getPermissions()方法获取授权信息,最后加以显示。这样就可以在界面中的TextView中显示出来对端所有的服务和特性的UUID,并有授权信息,当然也可以加入其他代码以显示出更多信息。
后面一部分代码是通过预先设置的UUID字符串进行判断,如果与TXDUUID相同,就将此Bluetooth
3)蓝牙低功耗Notification及功能设置:
对蓝牙低功耗设备,发送数据使用write()方法,而获取数据则使用的是Notification。也就是在建立双方连接后,对端如果有数据就会以Notification方式传送过来,而Android系统则是通过onCharacteristicChanged事件处理获取,代码为:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
final byte[] values = characteristic.getValue();
runOnUiThread(() -> {
len = values.length;
String str= "";
for(byte b:values){
str.append(String.format("%02x",new Integer(b&0xff)));
}
rxd.setText(str.toString());*/
});
}
在Android系统中,蓝牙低功耗数据收发都使用字节数组byte[]格式,从对端传来的数据使用接收characteristic的getValue()方法,获得的就是字节数组byte[]。
后续处理也在一个单独的线程中进行,如果是普通字符数据,可以使用new String(values)直接将字节数组转换为一个对应的字符串,然后在TextView中显示出来;但如果要以十六进制值的方式显示,就要按字节逐个转换,并连接为一个十六进制字符串,再赋值给一个TextView的文本以便显示。
上述代码也是需要在BluetoothGatt连接的Callback函数中定义,是回调的一部分。
不过,连接对端的Notification默认情况下并未使能,在连接过程中获取到接收Characteristic后,需要使用enableNotification()方法使其有效,具体代码为:
private String enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
boolean res = btGatt.setCharacteristicNotification(characteristic, true);
if(res){
List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors();
if (null != descriptors && descriptors.size() > 0) {
for (BluetoothGattDescriptor descriptor : descriptors) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean res1 = btGatt.writeDescriptor(descriptor);
if (res1) {
return "Cfg Succ";
} else {
return "wDescri fail";
}
}
}else{
return "getDescri fail";
}
}else{
return "setCharNotif fail";
}
return "0";
}
代码中,先直接使用BluetoothGatt的setCharacteristicNotification()方法,把对应的characteristic的Notification特性设为true;根据返回值进行判断,如果为true,使用getDescriptors()方法获取此characteristic的描述列表,如果获取到就进行遍历,对其中每一个descriptor都使用setValue()方法设置值为ENABLE
上述代码在BluetoothGatt连接的Callback函数之外,其中有多个分支,目的是配置不成功时检查是哪一步出现了问题,因此赋值了不同的返回值字符串,方便调试,调试完成运行正常后可以不再显示返回的字符串。
通过实测,连接的蓝牙低功耗模块启动时,具有Notification功能的Characteristic,有个16bit的可配置值,初始为0000,如果使用enableNotification()方法配置成功,低位就会变为1,这样就使能了Notification。不同的模块可能也会有一些差别,但通过此指令使能Notification功能是一致的。
4)蓝牙低功耗发送数据:
Android设备向蓝牙低功耗设备发送数据也是使用字节数组格式,代码为:
if(btGatt!=null) {
strSend = txd.getText().toString();
byte[] hex=strSend.getBytes();
gattTxd.setValue(hex); //向蓝牙服务中的写属性写入待发送数据
boolean res=btGatt.writeCharacteristic(gattTxd); //发送数据
}
代码中,先从名为txd的TextView中获取到值并转换为字符串,直接使用String的getBytes()方法得到对应的字节数组,然后使用setValue()方法把此字节数组传递给对应的Bluetooth
蓝牙低功耗一次发送的数据一般为20字节左右,较大的数据包需要截为数段分别发送。为了避免发送数据操作影响界面的响应,可以放在单独的线程中。也可以根据发送的内容不同,使用不同的编码转换方法。