安卓蓝牙4.0BLE 通信

            安卓蓝牙4.0BLE通信之体重称

          最近正在做一个关于手机跟蓝牙体重称之间数据交互的工程,因为之前没接触过蓝牙开发,所以浪费了不少时间,但是经过查资料和大神们的文章,终于在一周后完工了,现在总结下这个demo,先来介绍下关于蓝牙BLE有关的知识。

        什么是BLE?

        BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才开始支持BLE API,所以请各位客官把本文代码运行在蓝牙4.0和Android 4.3及其以上的系统

      BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多Descriptor,一个Descriptor包含一个Value一般来说,Characteristic是手机与BLE终端交换数据的关键,两者就是通过改变Characteristic的值来实现数据交互的Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTYCharacteristic的PROPERTY可以通过位运算符组合来设置读写属性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合。

   下面直接贴代码,边贴核心代码边解释。

     上面是我的项目目录结构,BluetothLeClass是提取的蓝牙工具类,Conversion与Utils是转码的工具类,ProgressDialogsUtils是对话框工具类,LeDeviceListAdapter是设备ListView的Adapter,activity_main.xml的视图如下:

       点击蓝牙搜索按钮后,会查找蓝牙设备,然后将搜索到的设备显示在ListView上,再通过点击列表项,去连接设备,再发数据给设备然后处理设备发回的回调信息,下面粘一下核心代码:

            这是按钮点击事件的方法,主要功能是判断手机是否支持蓝牙4.0,开启蓝牙和设置发现设备和数据交互的回调:

 public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_search_bluetooth:
                openBluetooth();

                break;
        }
    }

    private void openBluetooth() {
        //判断手机是否支持蓝牙4.0
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }
        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
        //开启蓝牙
        mBluetoothAdapter.enable();
        mBLE = new BluetoothLeClass(this, mHandler);
        if (!mBLE.initialize()) {
            Log.e(TAG, "Unable to initialize Bluetooth");
            finish();
        }
        //发现BLE终端的Service时回调
        mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
        //收到BLE终端数据交互的事件
        mBLE.setOnDataAvailableListener(mOnDataAvailable);
        mLeDeviceListAdapter = new LeDeviceListAdapter(this);
        ls_setup.setAdapter(mLeDeviceListAdapter);
        scanLeDevice(true);
    }

          这是发现设备的回调接口:

  private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new BluetoothLeClass.OnServiceDiscoverListener() {

        @Override
        public void onServiceDiscover(BluetoothGatt gatt) {
            Log.w(TAG, "onServiceDiscover");
            //gattServices=mBLE.getSupportedGattServices();

            displayGattServices(mBLE.getSupportedGattServices());
            Log.e(TAG, "found");
        }


    };

       这是数据交互的回调接口:

/**
     * 收到BLE终端数据交互的事件
     */
    private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new BluetoothLeClass.OnDataAvailableListener() {


        /**
         * BLE终端数据被读的事件
         */
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic, int status) {
            Log.w(TAG, "onServiceDiscover");
            if (status == BluetoothGatt.GATT_SUCCESS)
                Log.e(TAG, "onCharRead " + gatt.getDevice().getName()
                        + " read "
                        + characteristic.getUuid().toString()
                        + " -> "
                        + Utils.bytesToHexString(characteristic.getValue()))
                        ;
        }

        /**
         * 收到BLE终端写入数据回调
         */

        public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic) {

            mProgressDialogUtils.dismissProgressDialog();


            Log.e(TAG, "onCharWrite " + gatt.getDevice().getName()
                            + " write "
                            + characteristic.getUuid().toString()
                            + " -> "
                            + "结果是:" + Utils.bytesToHexString(characteristic.getValue())
            );
            if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) {
                getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(10, 14));
            } else {
                getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(4, 8));
            }

        }

displayGattServices方法主要是输出搜索到的设备中的各种Services,Characteristic,和Descriptor的各种信息,同时设置Charicteristic被写的通知,往BLE设备上写数据,
和读出数据

private void displayGattServices(List<BluetoothGattService> gattServices) {
        Log.w(TAG, "displayGattServices");
        if (gattServices == null) return;

        for (BluetoothGattService gattService : gattServices) {
            //-----Service的字段信息-----//
            int type = gattService.getType();
            Log.e(TAG, "-->service type:" + Utils.getServiceType(type));
            Log.e(TAG, "-->includedServices size:" + gattService.getIncludedServices().size());
            Log.e(TAG, "-->service uuid:" + gattService.getUuid());

            //-----Characteristics的字段信息-----//
            List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
            for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                Log.e(TAG, "---->char uuid:" + gattCharacteristic.getUuid());

                int permission = gattCharacteristic.getPermissions();
                Log.e(TAG, "---->char permission:" + Utils.getCharPermission(permission));

                int property = gattCharacteristic.getProperties();
                Log.e(TAG, "---->char property:" + Utils.getCharPropertie(property));

                byte[] data = gattCharacteristic.getValue();
                if (data != null && data.length > 0) {
                    Log.e(TAG, "---->char value:" + Utils.bytesToHexString(data));
                }
                //UUID_KEY_DATA是可以跟蓝牙模块串口通信的Characteristic
                //if (gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)) {
                //当测试读取前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
                //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
                mBLE.setCharacteristicNotification(gattCharacteristic, true);
                //设置数据内容
                //gattCharacteristic.setValue("A5,00,19,AF,50,5A,19");
                //attCharacteristic.setValue(Conversion.HexString2Bytes("A5019AF505A19"));
                byte[] b = {(byte) 0xA5, (byte) 0x00, (byte) 0x19, (byte) 0xAF, (byte) 0x50, (byte) 0x5A, (byte) 0x19};
                gattCharacteristic.setValue(b);
                //往蓝牙模块写入数据
                mBLE.writeCharacteristic(gattCharacteristic);
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mBLE.readCharacteristic(gattCharacteristic);
                    }
                }, 500);


                Log.e(TAG, "转换是C:" + Utils.bytesToHexString(b));


                //}

                //-----Descriptors的字段信息-----//
                List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
                for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {


                    Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
                    int descPermission = gattDescriptor.getPermissions();
                    Log.e(TAG, "-------->desc permission:" + Utils.getDescPermission(descPermission));

                    byte[] desData = gattDescriptor.getValue();
                    if (desData != null && desData.length > 0) {
                        Log.e(TAG, "-------->desc value:" + Utils.bytesToHexString(desData));
                    }
                }
            }
        }//

    }

setValue()为写数据关键,参数为设备上规定的APP到BLE设备上的协议内容。必须设置数据改变通知监听:mBLE.setCharacteristicNotification(gattCharacteristic, true);不然特征值改变也收不到回调信息,具体的通知方法为:

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
                                              boolean enabled) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        List<BluetoothGattDescriptor> gattDescriptors = characteristic.getDescriptors();
        for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {

            gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(gattDescriptor);//写数据回调关键


            mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
        }
    }

谨记,只有mBluetoothGatt.writeDescriptor(gattDescriptor);才能保证可准确收到BLE设备往特征值写入数据的监听,具体原理我也不是特别清楚,只知道删除这句话将收不到监听方法。

下面来介绍下当BLE设备往特征值写入数据时的回调方法:

 public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic) {

            mProgressDialogUtils.dismissProgressDialog();


            Log.e(TAG, "onCharWrite " + gatt.getDevice().getName()
                            + " write "
                            + characteristic.getUuid().toString()
                            + " -> "
                            + "结果是:" + Utils.bytesToHexString(characteristic.getValue())
            );
            if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) {
                getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(10, 14));
            } else {
                getWeight(gatt, Utils.bytesToHexString(characteristic.getValue()).substring(4, 8));
            }

        }

当设备写入数据时,将对话框取消掉,因为我连接的体重秤有两种,协议的内容也不同,第一张设备中是去回调值的第10到14位,而第二种是取4到8位,所以我根据设备名称做了个判断,getWeight()方法主要是转码和计算体重值:

private void getWeight(BluetoothGatt gatt, String s) {
            int a = Integer.parseInt(s, 16);
            if (mt == null) {
                mt = new MyThread();
                mt.start();

                Log.e(TAG, "当前线程为current:" + mt.currentThread() + cout++);
            }
            if (gatt.getDevice().getName().equalsIgnoreCase("Chipsea-BLE")) {
                double w = a;
                weight = w / 10;
            } else {
                double w = a;
                weight = w / 100;
            }


        }
    };

这里我通过handle与Thead的方法,当体重秤的数据改变时,我不断地刷新界面上的体重值,具体代码为:

class MyThread extends Thread {

        @Override
        public void run() {
            super.run();
            while (finish) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message msg = new Message();
                Bundle bundle = new Bundle();
                bundle.putDouble("weight", weight);

                msg.setData(bundle);
                msg.what = 1;
                mHandler.sendMessage(msg);
            }
        }

    }

private void intHandle() {
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        Bundle b = msg.getData();
                        setWeight(b.getDouble("weight"));
                        if (weight == 0.0) {

                            mHandler.removeCallbacks(mt);
                            mt = null;


                        }
                        break;
                    case 2:

                        Log.e(TAG, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~handlemessage" + i++);

                        mProgressDialogUtils.dismissProgressDialog();
                        mProgressDialogUtils.showProgressDialog(MainActivity.this, "连接成功,正常测量您的体重,请勿离开称体!");

                        break;
                    case 3:

                        mProgressDialogUtils.dismissProgressDialog();
                        mProgressDialogUtils.showProgressDialogWithButton(MainActivity.this, "连接失败!", "确定");

                        break;

                }

            }
        };

    }

最后的结果为:




时间比较赶,所以逻辑有点混乱,思路不是很清晰,所以提供源码给各位小伙伴参考,谢谢!

源码地址:点击打开链接











评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值