ble蓝牙 android

2019-10-14  本文已影响0人  CreScert

emmmmm..
最近要求通过ble蓝牙连接一台设备。。。因为第一次接触,总是碰壁,最后用了Fast-ble。
之后还是调不通,可以连接上,但是数据怎么都不对,后来才知道,厂家发错设备了...
最后换回设备后,没两三个小时就通了。最后反过来记录下走过的坑。
先推荐一下fast-ble地址:https://github.com/Jasonchenlijian/FastBle

1.通知(onCharacteristicChanged)不回调

如果你在确定发出后设备有返回时,但是实际你却死活收不到可能就是uuid,或者通知,或者发送没弄对..
首先排除问题:

  1. 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就是只能选一个,因为他们一个占一个字节。

可能本文很多人看着觉得很废话,但是,很多刚接触的不太了解,希望能帮到一些人.

上一篇 下一篇

猜你喜欢

热点阅读