emmmmm..
最近要求通过ble蓝牙连接一台设备。。。因为第一次接触,总是碰壁,最后用了Fast-ble。
之后还是调不通,可以连接上,但是数据怎么都不对,后来才知道,厂家发错设备了...
最后换回设备后,没两三个小时就通了。最后反过来记录下走过的坑。
先推荐一下fast-ble地址:https://github.com/Jasonchenlijian/FastBle
1.通知(onCharacteristicChanged)不回调
如果你在确定发出后设备有返回时,但是实际你却死活收不到可能就是uuid,或者通知,或者发送没弄对..
首先排除问题:
- UUID不正确
public static UUIDService initServiceAndChara(BluetoothGatt gatt){
UUIDService uuidService = new UUIDService();
List<BluetoothGattService> bluetoothGattServices= gatt.getServices();
for (BluetoothGattService bluetoothGattService:bluetoothGattServices){
List<BluetoothGattCharacteristic> characteristics=bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic:characteristics){
int charaProp = characteristic.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
uuidService.read_UUID_chara=characteristic.getUuid();
uuidService.read_UUID_service=bluetoothGattService.getUuid();
Log.e("initServiceAndChara","read_chara="+uuidService.read_UUID_chara+"----read_service="+uuidService.read_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
uuidService.write_UUID_chara=characteristic.getUuid();
uuidService.write_UUID_service=bluetoothGattService.getUuid();
Log.e("initServiceAndChara","write_chara="+uuidService.write_UUID_chara+"----write_service="+uuidService.write_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
uuidService.write_UUID_chara=characteristic.getUuid();
uuidService.write_UUID_service=bluetoothGattService.getUuid();
Log.e("initServiceAndChara","write_chara="+uuidService.write_UUID_chara+"----write_service="+uuidService.write_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
uuidService.notify_UUID_chara=characteristic.getUuid();
uuidService.notify_UUID_service=bluetoothGattService.getUuid();
Log.e("initServiceAndChara","notify_chara="+uuidService.notify_UUID_chara+"----notify_service="+uuidService.notify_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
uuidService.indicate_UUID_chara=characteristic.getUuid();
uuidService.indicate_UUID_service=bluetoothGattService.getUuid();
Log.e("initServiceAndChara","indicate_chara="+uuidService.indicate_UUID_chara+"----indicate_service="+uuidService.indicate_UUID_service);
}
}
}
return uuidService;
}
uuidService这个对象是我自己定义的,刚开始有用,后来发现有和设备通信协议书上一致的uuid,就没怎么用了。
public class UUIDService {
public UUID read_UUID_chara;
public UUID read_UUID_service;
public UUID write_UUID_chara;
public UUID write_UUID_service;
public UUID notify_UUID_chara;
public UUID notify_UUID_service;
public UUID indicate_UUID_chara;
public UUID indicate_UUID_service;
}
这里面的write_uuid_service和write_UUID_chara以及notify_UUID_chara和notify_UUID_service不一定会是你需要的。关键还是以协议书说明书上的一致。我之前没有用说明书的,有数据,但是不是你需要的,可能是别的功能的数据。不过我在打印的时候发现会走几遍write和 notify。但是只有一个有用,建议就找协议书上的。
这个可能大多数人都知道,不过我当初用的时候,因为设备不对,然后我就以为这个就是万能的,网上也没说这个有多大用,最后才知道,人家会列举出所有的,你从里面选一个用,如果设备和协议书正确,那这个就会有你需要的。
2.如果确定uuid正确,那么需要确定注册 写入和通知的uuid正确写入。如果你不知道怎么写入,我把fast-ble的拿出来了:
/**
* notify setting
*/
private boolean setCharacteristicNotification(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
boolean useCharacteristicDescriptor,
boolean enable
) {
if (gatt == null || characteristic == null) {
return false;
}
boolean success1 = gatt.setCharacteristicNotification(characteristic, enable);
if (!success1) {
return false;
}
BluetoothGattDescriptor descriptor;
if (useCharacteristicDescriptor) {
descriptor = characteristic.getDescriptor(characteristic.getUuid());
} else {
descriptor = characteristic.getDescriptor(formUUID("00002902-0000-1000-8000-00805f9b34fb"));
}
if (descriptor == null) {
return false;
} else {
descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE :
BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
return gatt.writeDescriptor(descriptor);
}
}
private UUID formUUID(String uuid) {
return uuid == null ? null : UUID.fromString(uuid);
}
第三个参数设置为false。
我是把 write和notify都设置了才有用的:
BluetoothGattCharacteristic characteristic2 = gatt.getService(uuidService.notify_UUID_service).getCharacteristic(uuidService.notify_UUID_chara);
BluetoothGattCharacteristic characteristic1 = gatt.getService(uuidService.write_UUID_service).getCharacteristic(uuidService.write_UUID_chara);
setCharacteristicNotification(gatt,characteristic2,false,true);
setCharacteristicNotification(gatt,characteristic1,false,true);
Log.e(TAG,uuidService.notify_UUID_service+"=="+uuidService.notify_UUID_chara);
Log.e(TAG,uuidService.write_UUID_service+"=="+uuidService.write_UUID_chara);
这个应该是没有问题的,当你注册成功后,会调用onDescriptorWrite方法。然后我是在这个回调里写入数据的。写入成功就是onCharacteristicWrite这个回调。这个应该都知道。
我记得就这么多了,不过用fast-ble的还是靠谱快些。
总结:(可能网上都有)
1.建议uuid的service和characteristic使用协议书上的,
2.注册的写入和通知不正确,或者缺少其中一个。
最后把一些可能用到的工具类方法贴上:
/**
* 16禁止转二进制
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.trim();
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
*
* @param data1
* @param data2
* @return data1 与 data2拼接的结果
*/
public static byte[] addBytes(byte[] data1, byte[] data2) {
byte[] data3 = new byte[data1.length + data2.length];
System.arraycopy(data1, 0, data3, 0, data1.length);
System.arraycopy(data2, 0, data3, data1.length, data2.length);
return data3;
}
/**
* @功能: 10进制串转为BCD码
* @参数: 10进制串
* @结果: BCD码
*/
/**
* <编码>
* <数字字符串编成BCD格式字节数组>
* @param asc 数字字符串
* @return
* @see [类、类#方法、类#成员]
*/
public static byte[] str2bcd(String asc) {
int len = asc.length();
int mod = len % 2;
if (mod != 0) {
asc = "0" + asc;
len = asc.length();
}
byte abt[] = new byte[len];
if (len >= 2) {
len = len / 2;
}
byte bbt[] = new byte[len];
abt = asc.getBytes();
int j, k;
for (int p = 0; p < asc.length()/2; p++) {
if ( (abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
j = abt[2 * p] - '0';
} else if ( (abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
j = abt[2 * p] - 'a' + 0x0a;
} else {
j = abt[2 * p] - 'A' + 0x0a;
}
if ( (abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
k = abt[2 * p + 1] - '0';
} else if ( (abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
k = abt[2 * p + 1] - 'a' + 0x0a;
}else {
k = abt[2 * p + 1] - 'A' + 0x0a;
}
int a = (j << 4) + k;
byte b = (byte) a;
bbt[p] = b;
}
return bbt;
}
/**
* 2进制数组转16进制字符串
* 用于接收到蓝牙ble设备后的数据转换
* @param src
* @return
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* 将当前时间转成字符串。
* 比如2019-10-14 17:45:32 转为20191014174532
* 用于转成bcd字符串
* @return
*/
public static String getTimeStr(){
Calendar c = Calendar.getInstance();//
String mYear = c.get(Calendar.YEAR)+""; // 获取当前年份
String mMonth = c.get(Calendar.MONTH) + 1+"";// 获取当前月份
mMonth = checkDouble(mMonth);
String mDay = c.get(Calendar.DAY_OF_MONTH)+"";// 获取当日期
mDay = checkDouble(mDay);
String mHour = c.get(Calendar.HOUR_OF_DAY)+"";//时
mHour = checkDouble(mHour);
String mMinute = c.get(Calendar.MINUTE)+"";//分
mMinute = checkDouble(mMinute);
String mSecond = c.get(Calendar.SECOND)+"";//秒
mSecond = checkDouble(mSecond);
String timeStr = stringToHexString("01");
String time = mYear+mMonth+mDay+mHour+mMinute+mSecond;
return time;
}
public static String checkDouble(String str){
if(str.length() == 1){
return "0"+str;
}
return str;
}
对了,我给个例子:
sdf.png
如上图:
我发送的时候就是:
// 事件类型
byte[] byteevent = {5};
// 这里对应的是发出的 权限/R_W 一栏。
// 权限分别是0x20和0x50,r_w分别是0x01和0x04,这个时候,因为他们两个占一个字节,而且,权限0x20和0x50是高位字节的。
// 所以发送的时候就会有:21,24,51和54四种情况,
byte [] extra = hexStringToBytes("24");
// 时间bcd
byte[] bytes = str2bcd(getTimeStr());
// 合并
byte[] bytes1 = addBytes(byteevent,extra);
byte[] bytes2 = addBytes(bytes1,bytes);
在这条数据发出成功后,会在onCharacteristicWrite回调中的characteristic.getValue()的值里返回一模一样的数据,但是是byte[]类型的,需要使用
// 二进制数组转为16进制字符串
String ret = bytesToHexString(characteristic.getValue());
这个时候ret的值为:052420191014181259。
接受的时候也是如此,只不过在onCharacteristicChanged里的characteristic.getValue()方法里,转成16进制大概就是下面的样子:
05301234567890
这个30和60就是只能选一个,因为他们一个占一个字节。
可能本文很多人看着觉得很废话,但是,很多刚接触的不太了解,希望能帮到一些人.
网友评论