蓝牙Ble4.0开发

一、参考的demo:

1、http://download.csdn.net/detail/kjunchen/9363233

2、http://download.csdn.net/detail/lqw770737185/8116019

3、https://github.com/lidong1665/Android-ble

4、https://github.com/litesuits/android-lite-bluetoothLE

5、https://github.com/alt236/Bluetooth-LE-Library---Android


二、基本知识

研究蓝牙,还是要把那些基本知识了解好了,在去研究,我是觉得我还是朦朦的,都是因为赶项目,匆匆了解的

1、(英文精通的可以看看这个)https://developer.android.com/guide/topics/connectivity/bluetooth-le.html

2、http://www.blogjava.net/zh-weir/archive/2013/12/09/407373.html

3、http://blog.csdn.net/shulianghan/article/details/50515359


三、对于通过监听设备接收数据的简单的流程:

a、扫描

b、发现设备,匹配建立链接

c、扫描服务,匹配服务与特征的uuid

d、监听匹配到的uuid。(如果是读写的话,就读写该uuid,回调函数里的onCharacteristicChanged()方法会得到蓝牙那边返回来的值)

 

四、实际中遇到的问题:(开发的是蓝牙锁,对时间要求很高)

android的BLE实际中的遇到的问题很多,设备与IOS通信不会出问题,与android通信容易出问题。

1、app与设备连接问题,点击连接后,返回的SEVICE(服务)和Characteristic(特征值)有的手机(例:小米3C、华为C199s)收到很快,有的手机返回特别慢(MX4),魅族有的快有的慢。连接后开锁后,老是会断开,蓝牙那边一直说不会断开。数据接收不稳定时快时慢。有的手机(例:小米3)连接后好像是直接断开了,也就找不到SEVICE(服务)和Characteristic(特征值),也就无法接受发送数据了。咨询别人,有人说是手机系统不同的问题,安卓手机都很不稳定。

2、给蓝牙下发数据(写数据)的时候,回调函数中的onCharacteristicChanged()方法有时候会调用,有时候不会调用,以至于有时候有返回值,有时候没有返回值。咨询别人,有的人说有可能是丢包的问题,或者接受发送数据的效率。

3、我们需要发送64个字节的数组,如果一次性发送过去,蓝牙设备那里可能无法及时处理以致没有任何回应。要想畅通的与蓝牙模块通信,考虑数据接收的延时时间非常重要。调整字节的发送速率,就成为非常关键的一步。值得注意的是,数据的发送是非常快的,就是因为这样才会导致蓝牙设备那里无法及时处理,所以,每次发送后的延时(Thread.sleep(200);)是非常重要的。每次发送数据后要延迟才能收到蓝牙返回的值。

4android的蓝牙开发遇到最常见的问题就是发现连接蓝牙设备连接不上,仔细一看竟然是BluetoothGatt status 133或者129,我是通过重启手机或者重启蓝牙后才能在连接的。

(别人的方法)最后终于找到缓解这种现象的办法android ble 133,解决办法就是要重新连接同一个蓝牙设备的时候,记得调用BluetoothGatt的.close()方法来关闭当前的蓝牙连接并清掉已使用过的蓝牙连接。

 

五、别人遇到的问题:

1、某些函数调用之间存在先后关系。例如首先需要connect上才能discoverServices。

2、一些函数调用是异步的,需要得到的值不会立即返回,而会在BluetoothGattCallback的回调函数中返回。

3、http://www.race604.com/android-ble-tips/

 

六、开发蓝牙可能会卡住的点:

1根据接收到的蓝牙设备端的无线信号强度(RSSI)来估算距离。其计算公式是:

  d=10^ (( abs ( RSSI ) - A ) / ( 10*n ) )

d是计算距离,RSSI是信号强度,A为发射端和接收端相隔1米时的信号强度,n是环境衰减因子。对于不同的蓝牙设备该值是不一样的,同样的设备在不同的发射功率的情况下其信号强度也是不一样的,而且对于同是1米的情况下,环境对于信号强度也是有影响的。n是环境衰减因子,自然跟环境有关。所以在确切发射功率的情况下,A和n对于同一款设备来说,也是一个经验值。

2、一秒获取rssi信号强度)(在开发中因为要一直获取rssi来判断要不要自动开锁)

(1)扫描蓝牙的时候,可以获取到rssi

  // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback;
    {
        mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("url","rssi=="+rssi);
                        mLeDeviceListAdapter.addDevice(device);
                        mLeDeviceListAdapter.notifyDataSetChanged();
                    }
                });
            }
        };
    }

(2)连接蓝牙后一秒获取一次rssi

  /**
     * 读取蓝牙RSSI线程
     */
    Thread readRSSI = new Thread() {
        int Rssi = 0;

        @Override
        public void run() {
            // TODO Auto-generated method stub
            super.run();
            while (isReadRssi) {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if (MainActivity.is_upload == false) {
                    // 如果读取蓝牙RSSi回调成功
                    if (mBleService.getRssiVal()) {
                        // 获取已经读到的RSSI值
                        Rssi = mBleService.getBLERSSI();
                        sendRSSI(Rssi);//当连接成功下发RSSI值
                        Log.e("url", "蓝牙连接后的rssi==" + Rssi);
                    }
                }

            }

        }
    };   /**
     * Implements callback methods for GATT events that the app cares about.  For example,
     * connection change and services discovered.
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
           
        }

        // New services discovered
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
           
        }

        // Result of a characteristic read operation
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            //将回调的RSSI值赋值
            BLERSSI = rssi;
        }

    };
  
  
  //获取已经得到的RSSI值
    public static int getBLERSSI() {
        return BLERSSI;
    }
    //是都能读取到已连接设备的RSSI值
    //执行该方法一次,获得蓝牙回调onReadRemoteRssi()一次

    /**
     * Read the RSSI for a connected remote device.
     */
    public boolean getRssiVal() {

        if (mBluetoothGatt == null)
            return false;
        return mBluetoothGatt.readRemoteRssi();

    }
    
    

3、写蓝牙数据用byte,数据转换

 /**
     * 十六进制串转化为byte数组
     *
     * @return the array of byte
     */
    public static final byte[] hex2byte(String hex)
            throws IllegalArgumentException {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException();
        }
        char[] arr = hex.toCharArray();
        byte[] b = new byte[hex.length() / 2];
        for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
            String swap = "" + arr[i++] + arr[i];
            int byteint = Integer.parseInt(swap, 16) & 0xFF;
            b[j] = new Integer(byteint).byteValue();
        }
        return b;
    }
    
    
     /** Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。
            * @param src byte[] data
    * @return  hex string
    */
      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();
    }
    
     /**
     * java 合并两个byte数组
     */
    public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
        byte[] byte_3 = new byte[byte_1.length + byte_2.length];
        System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
        System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
        return byte_3;
    }

4、写蓝牙数据:要调用writeCharacteristic()方法和setCharacteristicNotification()方法。写数据后,蓝牙的返回值在回调函数中的onCharacteristicChanged()方法中获取

 /**
     * Write data to characteristic, and send to remote bluetooth le device.
     *
     * @param serviceUUID        remote device service uuid
     * @param characteristicUUID remote device characteristic uuid
     * @param value              Send to remote ble device data.
     */

    public boolean writeCharacteristic(String serviceUUID, String characteristicUUID, byte[] value) {

        if (mBluetoothGatt != null && characteristicUUID != null) {
            BluetoothGattService service =
                    mBluetoothGatt.getService(UUID.fromString(serviceUUID));
            if (service != null) {
                BluetoothGattCharacteristic characteristic =
                        service.getCharacteristic(UUID.fromString(characteristicUUID));
                if (characteristic != null) {
                    characteristic.setValue(value);
                    Log.e("url", "Write Success, DATA1: " + Arrays.toString(characteristic.getValue()));
                    return mBluetoothGatt.writeCharacteristic(characteristic);
                }
            }
        }
        return false;
    } /**
     * Enables or disables notification on a give characteristic.
     *
     * @param characteristic Characteristic to act on.
     * @param enabled        If true, enable notification.  False otherwise.
     */

    public void setCharacteristicNotification(String serviceUUID, String characteristicUUID,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Toast.makeText(this, "蓝牙断开了,请重新连接!", Toast.LENGTH_SHORT).show();
            Log.w(TAG, "BluetoothAdapter not initialized1111");
            isconnect = false;
            ble_connect = "disconnect";
            return;
        }
        ble_connect = "connect";
        BluetoothGattService service =
                mBluetoothGatt.getService(UUID.fromString(serviceUUID));
        if (service != null) {
            BluetoothGattCharacteristic characteristic =
                    service.getCharacteristic(UUID.fromString(characteristicUUID));

            mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        }
    } /**
     * Implements callback methods for GATT events that the app cares about.  For example,
     * connection change and services discovered.
     */
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        }


        // New services discovered
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        }

        // Result of a characteristic read operation
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e("tag", "onCharacteristicWrite" );

            super.onCharacteristicWrite(gatt, characteristic, status);
            String address = gatt.getDevice().getAddress();
            for (int i = 0; i < characteristic.getValue().length; i++) {
                Log.i(TAG, "address: " + address + ",Write: " + characteristic.getValue()[i]);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {

            Log.e("tag", "返回信息--> " + DigitalTrans.bytesToHexString(characteristic.getValue()));

            if (mOnDataAvailableListener != null) {
                mOnDataAvailableListener.onCharacteristicChanged(gatt, characteristic);
            }

            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);

        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            if (mOnDataAvailableListener != null) {
                mOnDataAvailableListener.onDescriptorRead(gatt, descriptor, status);
            }
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            //将回调的RSSI值赋值
            BLERSSI = rssi;
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            if (mOnMtuChangedListener != null) {
                mOnMtuChangedListener.onMtuChanged(gatt, mtu, status);
            }
        }

    };

5、因为我搜索到蓝牙分类是通过蓝牙的名称,所以在搜索的时候会遇到搜索到蓝牙名称为空的其他蓝牙,比如手机上的蓝牙,所以需要对空名称的蓝牙信号进行处理。

过滤掉空指针的异常(过滤空名字的蓝牙)

解决方案:

try {
    //实现方法;
    }
} catch (NullPointerException e) {
    e.printStackTrace();
}

6、如果有设置提示音,蓝牙连接或者开锁的时候,要有提示音。

提示音实现方法一:

private SoundPool sp;//声明一个SoundPool
private int music;//定义一个整型用load();来设置suondID
 
sp= new SoundPool(10, AudioManager.STREAM_SYSTEM, 5);//第一个参数为同时播放数据流的最大个数,第二数据流类型,第三为声音质量
music = sp.load(this, R.raw.connect_dingdong, 1); //把你的声音素材放到res/raw里,第2个参数即为资源文件,第3个为音乐的优先级
 
button.onclick(){
  sp.play(music,7, 7, 0, 0, 1);
}

提示音实现方法二:

private MediaPlayer music = null;// 播放器引用
 
button.onclick(){
music = MediaPlayer.create(MainActivity.this,R.raw.lock_open);
music.start();
}

 7、CRC检验

要求下发数据是:0x02 0x53 0x4A CRC (根据{0x02, 0x53, 0x4A}求得crc)然后在下发

  public void crc() {
        short[] upload = new short[]{0x02, 0x53, 0x4A};
        short crc1 = 0;
        crc1 =appData_Crc(upload, crc1, upload.length);
        String str_crc = Integer.toHexString(crc1);

        if (str_crc.length() == 1) {//如果长度为1,那么DigitalTrans.hex2byte(str_crc)的时候会报错
            str_crc = 0 + str_crc;
        }

        Log.e("url", "02_53_4a的str_CRC==" + str_crc);//55
        byte[] crc = com.yundiankj.ble_lock.Resource.DigitalTrans.hex2byte(str_crc);//十六进制串转化为byte数组
        Log.e("url", "02_53_4a的byte_crc==" + crc[0]); // crc[0]就是根据0x02, 0x53, 0x4A求得的
    
  }
    
    
    /**
     * crc的求法
     *
     * @return the array of byte
     */
      public static short appData_Crc(short[] src, short crc, int len) {
        int i;
        short bb;
        for (int j = 0; j < len; j++) {
            bb = src[j];
            for (i = 8; i > 0; --i) {  //Boolean.parseBoolean(Integer.toBinaryString((bb & 0x01)^(crc &0x01)))
                if ((((bb ^ crc) & 0x01)) == 1) {     //判断与x7异或的结果(x8)((bb ^ crc) & 0x01)

                    crc ^= 0x18;               //反馈到x5   x4
                    crc >>= 1;                     //移位
                    crc |= 0x80;               //x7异或的结果送x0
                } else {
                    crc >>= 1;
                }
                bb >>= 1;
            }
        }
        return (crc);
    }
    
     /**
     * 十六进制串转化为byte数组
     *
     * @return the array of byte
     */
    public static final byte[] hex2byte(String hex)
            throws IllegalArgumentException {
        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException();
        }
        char[] arr = hex.toCharArray();
        byte[] b = new byte[hex.length() / 2];
        for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
            String swap = "" + arr[i++] + arr[i];
            int byteint = Integer.parseInt(swap, 16) & 0xFF;
            b[j] = new Integer(byteint).byteValue();
        }
        return b;
    }

这是我们项目要求这样弄的,可能每个项目的做法不一样,仅供参考哈

 

 

 



 

 有问题欢迎指正!(后面继续补充)

 

 

 


你可能感兴趣的:(蓝牙Ble4.0开发)