美文网首页
ble蓝牙 android

ble蓝牙 android

作者: CreScert | 来源:发表于2019-10-14 18:18 被阅读0次

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

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

    相关文章

      网友评论

          本文标题:ble蓝牙 android

          本文链接:https://www.haomeiwen.com/subject/roormctx.html