蓝牙Ble4.0通讯的步骤及实现

在一家偏硬件的一家公司从事穿戴式设备的开发,通过近几个月学习与研究对于蓝牙4.0的通讯还有有点自己的见解,有不足的地方大家可以一起讨论,互相学习,废话不多说,那么如何进行蓝牙4.0的通讯与数据传输呢?本demo比较简单,大家应该都可以很好理解与学习的!

有基本的几个步骤,下面是一些代码段,希望对大家有所帮助吧。

添加蓝牙权限,

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le"
    android:required="true"/>
判断手机手机是否支持蓝牙ble

// 检查当前手机是否支持ble 蓝牙,如果不支持退出程序
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, "设备不支持BLE 蓝牙", Toast.LENGTH_SHORT).show();
    finish();
}

//获得蓝牙适配器对象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

对于蓝牙的搜索过程采用广播:
/** 因为蓝牙在搜索到设备和搜索完毕都是通过广播发送的,这里我们需要注册广播接收器 */
IntentFilter intentFilter = new IntentFilter(
        BluetoothDevice.ACTION_FOUND);
registerReceiver(receiver, intentFilter);

intentFilter = new IntentFilter(
        BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(receiver, intentFilter);

//注册蓝牙信号强度
intentFilter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
registerReceiver(receiver, intentFilter);

广播的注册:

 //注册广播
    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            String action = intent.getAction();
            // 判断这个广播是否是蓝牙搜索到设备的广播
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                // 获取到传递过来的设备信息
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    //信号强度
                    short rssi = intent.getExtras().getShort(
                            BluetoothDevice.EXTRA_RSSI);

                    String s = String.valueOf(rssi);
                    mDeviceList.add(device.getName()  + "\n"
                            + device.getAddress() + "\n" + "信号强度:" + s +"dbm");
                    arrayAdapter.notifyDataSetChanged();
                }
                // 判断是否为搜索完毕的广播
            } else if (action
                    .equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                /***/
//             setTitle("连接蓝牙设备");
                bt_search.setText("搜索完毕");
            }

        }
    };

项目创建时需要打开蓝牙,详细代码片段

// 为了确保设备上蓝牙能使用, 如果当前蓝牙设备没启用,弹出对话框向用户要求授予权限来启用
if (!mBluetoothAdapter.isEnabled()) {
    Intent enabletIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enabletIntent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_CANCELED) {
        finish();
        return;
    }
}

基本工作有了这些后,我们建立一个listview来显示搜索到的蓝牙设备,

//适配器 + Listview显示
arrayAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,mDeviceList);
listView.setAdapter(arrayAdapter);

2.如何进行扫描呢?这里采用的是按钮触发:

bt_search.setText("正在搜索蓝牙设备...");
if(mBluetoothAdapter.isDiscovering()){
    mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();
其实这个时候点击按钮,并没有任何显示,因为我们得用一个集合加载搜索到的设备,前面采用MVC的模式进行适配,将搜索到的设备加载到链表里面

//数据源存放蓝牙设备----------------获取已绑定的设备,将其存放于set集合
Set devices = mBluetoothAdapter.getBondedDevices();
if(devices.size() > 0){
    for (BluetoothDevice device:devices) {
        mDeviceList.add(device.getName()  + "\n"
                + device.getAddress());
    }
}

将搜索到的设备显示到listview上后对其item项进行进一步的连接,并进行通信、
因为要连接设备的地址,这里进行字符串的截取

        String s = mDeviceList.get(position);
        String a[] = s.split("\n");
        String name = a[0];
        String address = a[1];
        Log.i(TAG, "address =" + address);
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "蓝牙适配器为空或者地址为空.");
            return;
        }
        //根据地址取得设备
        device = mBluetoothAdapter.getRemoteDevice(address);
        //获取链接 这个时候需要实现BluetoothGattCallback
        bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
接下来执行回调方法,回调方法主要有
onConnectionStateChange, // 返回链接状态
onServicesDiscovered,   //发现服务时调用此方法
onCharacteristicRead      //字段读
onCharacteristicWrite   //字段写
onCharacteristicChanged   //蓝牙通信内容写入写出时调用此方法
onDescriptorRead       // 字段读
onDescriptorWrite    //字段写


 //回调方法
    private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        // 返回链接状态
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            Log.i(TAG,"onConnectionStateChange");
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //连接成功  因为是异步调用的 所以刷新UI的操作要放在主线程中,当然也可以使用hanlder  Eventbus等 随便
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        link = 1;
                        tv_address.setText("连接成功" + device.getAddress());
                    }
                });
                //发现服务,连接成功
                gatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //断开连接
                link = 2;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv_address.setText("连接断开");
                    }
                });
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            Log.i(TAG,"onServicesDiscovered");
            if (status == BluetoothGatt.GATT_SUCCESS) {

                bluetoothGattServiceList = bluetoothGatt.getServices();
                for (int i = 0; i < bluetoothGattServiceList.size(); i++) {
                    BluetoothGattService bluetoothGattService = bluetoothGattServiceList
                            .get(i);
                    if (bluetoothGattService.getUuid().equals(BleConstants.TEMP_SERVICE_UUID)) {
                        BleConstants.type = 1;
                        break;
                    } else if (bluetoothGattService.getUuid().equals(BleConstants.TEMP_SERVICE_UUID_G)) {
                        BleConstants.type = 0;
                        break;
                    }
                }
                //-------------------------
                for (int i = 0; i < bluetoothGattServiceList.size(); i++) {
                    BluetoothGattService bluetoothGattService = bluetoothGattServiceList
                            .get(i);
                    Log.i(TAG,"Service UUID-" + i + ":" + bluetoothGattService.getUuid());

                    List bluetoothGattCharacteristicList = bluetoothGattService
                            .getCharacteristics();
                    for (int j = 0; j < bluetoothGattCharacteristicList.size(); j++) {
                        BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattCharacteristicList
                                .get(j);
                        Log.i(TAG,"Characteristic UUID-" + i + "-" + j + ":"
                                + bluetoothGattCharacteristic.getUuid());
                        if (bluetoothGattCharacteristic.getUuid().equals(BleConstants.TEMP_CHAR_UUID)) {
                            Log.i(TAG, "TEMP_CHAR_UUID"); 

                        } else if (bluetoothGattCharacteristic.getUuid().equals(BleConstants.TEMP_BATTERY_CHAR_UUID)) {
                            Log.i(TAG, "BATTERY_CHAR_UUID" + bluetoothGattCharacteristic.getUuid().toString());

                        } else if (bluetoothGattCharacteristic.getUuid()
                                .equals(BleConstants.TEMP_UART_WRITE_CHAR_UUID)) {

                        } else if (bluetoothGattCharacteristic.getUuid()
                                .equals(BleConstants.TEMP_UART_READ_CHAR_UUID)) {

                        }
                    }
                }
                read();
                Log.i(TAG, "BluetoothGatt.GATT_SUCCESS");
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            Log.i(TAG,"onCharacteristicRead");
        }

        /**
         * write成功(发送值成功) 写入Characteristic成功与否的回调 可以根据
         * characteristic.getValue()来判断是哪个值发送成功了,比如 连接上设备之后你有一大串命令需要下发,你调用多次写命令,
         * 这样你需要判断是不是所有命令都成功了,因为android不太稳定,有必要来check命令是否成功,否则你会发现你明明调用
         * 写命令,但是设备那边不响应
         */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            Log.i(TAG,"onCharacteristicWrite");
        }


        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.i(TAG,"onCharacteristicChanged");

            byte[] read_data = characteristic.getValue();
            String as = byteToHexString(read_data);
            Log.i(TAG, "as = " + as);  

            float abcd = 0;   
            if(read_data[1] == 0x01){
                //温度       
                abcd = (float) ((read_data[2]) * 256 + (0x000000FF & read_data[3])) / 10;
            }
          
            string1 = String.valueOf(abcd);
            Log.i(TAG, "string1 =" + string1);
        }


        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            Log.i(TAG,"onDescriptorRead");
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            Log.i(TAG,"onDescriptorWrite");
        }
    };

read()方法如下:

public void read() {
    if (BleConstants.type == 1) {
        BluetoothGattService bluetoothGattServic = bluetoothGatt
                .getService(BleConstants.TEMP_UART_SERVICE_UUID);
        BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattServic
                .getCharacteristic(BleConstants.TEMP_UART_READ_CHAR_UUID);

        for (int k = 0; k < bluetoothGattCharacteristic.getDescriptors().size(); k++) {
            BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic
                    .getDescriptors().get(k);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            bluetoothGatt.writeDescriptor(descriptor);
        }
        //发送通知
        bluetoothGatt.setCharacteristicNotification(
                bluetoothGattCharacteristic, true);

    } else if (BleConstants.type == 0) {
        BluetoothGattService bluetoothGattServic = bluetoothGatt
                .getService(BleConstants.TEMP_SERVICE_UUID_G);
        BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattServic
                .getCharacteristic(BleConstants.TEMP_READ_CHAR_UUID_G);

        for (int k = 0; k < bluetoothGattCharacteristic.getDescriptors().size(); k++) {
            BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptors().get(k);
            //ENABLE_NOTIFICATION_VALUE
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            bluetoothGatt.writeDescriptor(descriptor);
        }
        bluetoothGatt.setCharacteristicNotification(
                bluetoothGattCharacteristic, true);
    }
}

进制转换的方法:

    public String byteToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        Log.i(TAG, "bArray.length = " + bArray.length);    //长度为5
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            Log.i(TAG, "bArray = " + bArray[i]);    // 5, 1, 0, -2, -6
            //将整数变为字符串
            // 0xff是十六进制FF的表示方法,因为一个十六进制数字转换成二进制是四位,即F=1111,
            // 所以0xff占用一个字节 。也就是说是1B,1KB是1024B。
            //另外你表达不太清楚,如果你问FF KB是多少,十六进制FF=15*16+15*1=255,即255KB
            sTemp = Integer.toHexString(0xFF & bArray[i]);
//            Log.i(TAG, "sTemp" + sTemp);
            //Log: sTemp 5, sTemp 1, sTemp 0, sTemp fc, sTemp f8     ---as: 05 01 00F6 F2

            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

蓝牙通信需要用到的UUID,关于uuid不懂的就自己百度了。

// 温度值蓝牙特征服务UUID
public static final UUID TEMP_SERVICE_UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb");
// 温度值蓝牙特征值UUID
public static final UUID TEMP_CHAR_UUID = UUID.fromString("00002A1C-0000-1000-8000-00805f9b34fb");

那为了保证数据传输并显示,这里主要测试了温度的显示。

// handler类接收数据
    Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                tv_display.setText(string1 + "℃");
            }
        };
    };

    // 线程类
    class ThreadShow implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(300);
                    Message msg = new Message();
                    msg.what = 1;
                    handler.sendMessage(msg);                 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }


以上就是关于蓝牙如何连接以及通信数据的自己的一点学习经验,目前还在深圳找工作,期待找到一家适合自己的工作吧!


你可能感兴趣的:(蓝牙Ble4.0通讯的步骤及实现)