Android从4.3(Api level 18)开始支持BLE的开发,本文记录了Android 4.4.2设备与BLE设备通讯的流程。

权限需求:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

步骤:
1.获取蓝牙服务
BluetoothManager btManager= (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
2.获取蓝牙设备管理器(适配器)
BluetoothAdapter mBluetoothAdapter = btManager.getAdapter();
3.判断设备是否支持BLE蓝牙通讯,支持则判断蓝牙是否已开启,未开启蓝牙的设置开启。
if (mBluetoothAdapter != null && (!mBluetoothAdapter.isEnabled())){
mBluetoothAdapter.enable();//打开蓝牙
}
4.扫描当前附近的蓝牙BLE设备(这个操作比较耗电,要适时地停止)
5.停止设备扫描
6.与指定BLE设备建立Gatt连接
7.在连接成功后,设置扫描BLE设备支持的service和characteristic,并设置。这里需要注意,需要开启通知的,需要设置相应描述符。
8.读写操作
9.断开Gatt连接
10.关闭Gatt连接

在开发过程中,遇到的主要问题是onCharacteristicWrite失败回调,这个可能跟低功耗蓝牙外设有关,需要断开并关闭Gatt连接重新建立

 下面是主要参考代码

package com.rollup.bluetoothlock;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.android.peephole.MyApplication;
import com.rollup.bluetoothlock.MyLog;
import com.rollup.bluetoothlock.Utils;
import com.rollup.bluetoothlock.Value;



import android.R.integer;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter.LeScanCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.os.Message;
import android.widget.EditText;
import android.widget.Toast;

public class BleManager {
    private static final String TAG = "BleManager";
    public static final int ERROR_NO_ERROR = 0;//成功
    public static final int ERROR_BTDEVICE_EXC_FAIL = 1;// 蓝牙锁端执行错误
    public static final int ERROR_PSD_ERROR = 2; // 钥匙错误
    public static final int ERROR_CONNECT_FAIL = 3;
    public static final int ERROR_DEFAULT = 4;
    public static final int ERROR_NOT_FOUND_DEVICE = 5;// 未扫描到指定编码的锁
    public static final int ERROR_HAVE_CONNECTED = 6;//上次连接未执行完毕
    public static final int ERROR_NO_DEVICE = 7;//设备没有初始化
    public static final int ERROR_CREATE_FAIL = 8;

    private static final int STOP_LESCAN = 1;
    private final int OVER_TIMER = 60 * 1000;// 扫描超时时间

    private Context mContext = null;
    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothManager mBluetoothManager = null;
    public BluetoothDevice mBluetoothDevice = null;
    private BluetoothGatt mBluetoothGatt;
    private static boolean isScanning = false;
    private BluetoothGattCharacteristic writeCharacteristic = null;
    private static boolean isConnectToDevice = false;
    private String btName = null;
    private String psd = null;
    private boolean hasExcuteOpenDoor = false;// 开门命令已执行
    private byte[] devceKey;

    private static boolean haveRetry = false;
    private static BleManager instance = null;
    private MyHandler mHandler = null;

    public static long lastOpendoorTime = 0;

    private OnBleScanOpenDoorCallback openDoorCallback = null;

    public interface OnBleScanOpenDoorCallback {
        public void openDoorSuc();
        public void openDoorFail(int error);
    }

    private BleManager(Context context, String btName, String psd) {
        this.mContext = context;
        initBluetooth();
        if (mBluetoothManager == null) {
            MyLog.e(TAG, "Unable to initialize BluetoothManager.");
            return;
        }
        this.btName = btName;
        this.psd = psd;
        devceKey = new byte[16];
    }

    public static BleManager getInstance(String btName, String psd) {
        if (instance == null) {
            instance = new BleManager(MyApplication.getMyApplication(), btName,
                    psd);
        } else {
            instance.setBTinfo(btName, psd);
        }
        return instance;
    }

    private void setBTinfo(String name, String psd) {
        this.btName = name;
        this.psd = psd;
    }

    private void initBluetooth() {
        mHandler = new MyHandler(this);
        mBluetoothManager = (BluetoothManager) mContext
                .getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager != null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter != null) {
                if (!mBluetoothAdapter.isEnabled()) {
                    mBluetoothAdapter.enable(); // 打开蓝牙
                }
            }
        }
    }
    
    /**
     * 是否空闲可用
     * @return
     */
    public static boolean isFree(){
        return (!isConnectToDevice)&&(!isScanning);
    }

    public void startLeScan(boolean autoConnect) {
        if (mBluetoothAdapter == null) {
            return;
        }
        if (btName == null || btName.length() == 0) {
            return;
        }

        if (isScanning) {
            return;
        }
        isScanning = true;

        if (autoConnect) {
            mBluetoothAdapter.startLeScan(mLeScanCallback2);
        } else {
            mBluetoothAdapter.startLeScan(mLeScanCallback); // 此mLeScanCallback为回调函数
        }

        mHandler.sendEmptyMessageDelayed(STOP_LESCAN, OVER_TIMER); // 这个搜索30秒,如果搜索不到则停止搜索
    }

    private void stopLeScan() {
        if (mHandler != null && mHandler.hasMessages(STOP_LESCAN)) {
            mHandler.removeMessages(STOP_LESCAN);
        }
        if (mBluetoothAdapter == null) {
            return;
        }

        if (!isScanning) {
            return;
        }
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
        mBluetoothAdapter.stopLeScan(mLeScanCallback2);
        isScanning = false;
    }

    private LeScanCallback mLeScanCallback = new LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
            MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
            if (device.getAddress() != null) {
                if (device.getName().toLowerCase().replace("_", "")
                        .contentEquals(btName.toLowerCase().replace(" ", ""))) {
                    mBluetoothDevice = device;
                    stopLeScan();
                }
            }
        }
    };

    private LeScanCallback mLeScanCallback2 = new LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
            MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
            if (device.getAddress() != null) {
                if (device.getName().toLowerCase().replace("_", "")
                        .contentEquals(btName.toLowerCase().replace(" ", ""))) {
                    mBluetoothDevice = device;
                    stopLeScan();
                    connect();
                }
            }
        }
    };

    private static class MyHandler extends Handler {
        WeakReference<BleManager> wf = null;

        public MyHandler(BleManager manager) {
            // TODO Auto-generated constructor stub
            wf = new WeakReference<BleManager>(manager);
        }

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            switch (msg.what) {
            case STOP_LESCAN:
                MyLog.i(TAG,  "MyHandler handleMessage STOP_LESCAN");
                if (wf.get()!=null){
                    wf.get().stopLeScan();
                }
                MyLog.i(TAG, "release(), Scan time is up");
                break;
            }
        }
    }

    public int connect() {
        if (mBluetoothDevice == null) {
            MyLog.i(TAG, "BluetoothDevice is null.");
            return ERROR_NO_DEVICE;
        }
        
        if(isConnectToDevice){
            MyLog.i(TAG, "Have connected to device");
            return ERROR_HAVE_CONNECTED;
        }
        isConnectToDevice = true;
        hasExcuteOpenDoor = false;

        // 两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server

        mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false,
                mGattCallback); // mGattCallback为回调接口

        if (mBluetoothGatt != null) {

            if (mBluetoothGatt.connect()) {
                MyLog.d(TAG, "Connect succeed.");
                return ERROR_NO_ERROR;
            } else {
                MyLog.d(TAG, "Connect fail.");
                return ERROR_CONNECT_FAIL;
            }
        } else {
            MyLog.d(TAG, "BluetoothGatt null.");
            return ERROR_CREATE_FAIL;
        }
    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                isConnectToDevice = true;
                gatt.discoverServices(); // 执行到这里其实蓝牙已经连接成功了
                MyLog.i(TAG, "Connected to GATT server.");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                if (!hasExcuteOpenDoor) {
                    disconnect();
                    retryUnlock();
                    if (!haveRetry) {
                        //已失败重试过
                        if (openDoorCallback != null) {
                            openDoorCallback.openDoorFail(ERROR_CONNECT_FAIL);
                        }
                    } 
                } else {
                    MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED  hasExcuteOpenDoor=TRUE");
                    haveRetry = false;
                    disconnect();
                }
                MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED --release()");
            }
        }

        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                MyLog.i(TAG, "onServicesDiscovered");
                List<BluetoothGattService> services = gatt.getServices();
                for (int i = 0; i < services.size(); i++) {
                    MyLog.i(TAG, "[Service " + i + "] uuid:"
                            + services.get(i).getUuid());
                    if (services.get(i).getUuid().equals(Value.uuid)) {
                        List<BluetoothGattCharacteristic> characteristics = services
                                .get(i).getCharacteristics();
                        for (int j = 0; j < characteristics.size(); j++) {
                            MyLog.i(TAG, "[Characteristic]"
                                    + characteristics.get(j).getUuid());
                            if (characteristics.get(j).getUuid()
                                    .equals(Value.CHARACTERISTIC_READ)) {
                                boolean res = mBluetoothGatt
                                        .setCharacteristicNotification(
                                                characteristics.get(j), true);
                                // 打开通知描述
                                for (BluetoothGattDescriptor descriptor : characteristics
                                        .get(j).getDescriptors()) {
                                    descriptor
                                            .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                    gatt.writeDescriptor(descriptor);
                                    MyLog.i(TAG,
                                            "Characteristic set notification is Success!");
                                }
                                MyLog.i(TAG,
                                        "onServicesDiscovered setCharacteristicNotification result="
                                                + res);
                                break;
                            }
                        }
                        characteristics = null;
                    }
                }

                for (int i = 0; i < services.size(); i++) {
                    MyLog.i(TAG, "[Service " + i + "] uuid:"
                            + services.get(i).getUuid());
                    if (services.get(i).getUuid().equals(Value.uuid)) {
                        List<BluetoothGattCharacteristic> characteristics = services
                                .get(i).getCharacteristics();
                        for (int j = 0; j < characteristics.size(); j++) {
                            MyLog.i(TAG, "[Characteristic]"
                                    + characteristics.get(j).getUuid());
                            if (characteristics.get(j).getUuid()
                                    .equals(Value.CHARACTERISTIC_WRITE)) {
                                writeCharacteristic = characteristics.get(j);
                                final int charaProp = characteristics.get(j)
                                        .getProperties();
                                // 如果该char可写
                                if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                                    byte[] value = new byte[] { 0x01, 0x00,
                                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                            0x00, 0x00, 0x00 };
                                    characteristics.get(j).setValue(value);
                                    writeCharacteristic
                                            .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
                                    mBluetoothGatt
                                            .writeCharacteristic(characteristics
                                                    .get(j));
                                    value = null;
                                }
                                break;
                            }
                        }
                        characteristics = null;
                    }
                }
                services = null;
            } else {
                MyLog.i(TAG, "onServicesDiscovered status------>" + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            MyLog.i(TAG,
                    "onCharacteristicRead------>"
                            + Utils.bytesToHexString(characteristic.getValue()));

        }

        

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic) {
            MyLog.i(TAG,
                    "onCharacteristicChanged------>"
                            + Utils.bytesToHexString(characteristic.getValue()));
            MyLog.i(TAG, "UUID------>" + characteristic.getUuid().toString());

            if (Value.CHARACTERISTIC_READ.equals(characteristic.getUuid())) {
                byte[] value = characteristic.getValue();
                if ((value[0] & 0xFF) == 0x81) {
                    for (int i = 0; i < 16; i++) {
                        devceKey[i] = value[i + 1];
                    }
                    // MyLog.i(TAG, "加密秘钥为:"+Utils.bytesToHexString(devceKey));
                    if (writeCharacteristic != null) {
                        byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00 };
                        if (psd.length() == 8) {
                            int j = 0;
                            for (int i = 0; i < 4; i++) {
                                cmd[5 + i] = (byte) (((((Integer.valueOf(psd
                                        .charAt(j)) - 48) << 4) & (0xF0)) + ((Integer
                                        .valueOf(psd.charAt(j + 1)) - 48) & 0x0F)) & 0xFF);
                                MyLog.i(TAG, "cmd" + (9 + i) + "=" + cmd[9 + i]);
                                j = j + 2;
                            }
                        }
                        byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
                        if (cmd_encrypt != null) {
                            byte[] request = new byte[17];
                            request[0] = 0x03;
                            for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
                                    : request.length); i++) {
                                request[i] = cmd_encrypt[i - 1];
                            }
                            writeCharacteristic.setValue(request);
                            writeCharacteristic
                                    .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
                            // MyLog.i(TAG,
                            // "发送开锁命令:"+Utils.bytesToHexString(request));
                            mBluetoothGatt
                                    .writeCharacteristic(writeCharacteristic);
                            request = null;
                        }
                        cmd_encrypt  = null;
                        cmd = null;
                    }

                } else if ((value[0] & 0xFF) == 0x83) {
                    byte[] res = new byte[16];
                    for (int i = 0; i < 16; i++) {
                        res[i] = value[i + 1];
                    }
                    // MyLog.i(TAG, "解密秘钥为:"+Utils.bytesToHexString(devceKey));
                    // MyLog.i(TAG, "待解密数据为:"+Utils.bytesToHexString(res));
                    byte[] decode_res = Utils.deCode(devceKey, res);
                    hasExcuteOpenDoor = true;
                    if (decode_res != null) {
                        MyLog.i(TAG,
                                "获取到蓝牙开锁返回数据:"
                                        + Utils.bytesToHexString(decode_res));
                        if ((decode_res[0] & 0xFF) == 0x01) {
                            if ((decode_res[1] & 0xFF) == 0x00) {
                                MyLog.i(TAG, "开锁成功");

                                if (openDoorCallback != null) {
                                    openDoorCallback.openDoorSuc();
                                }
                                lastOpendoorTime = System.currentTimeMillis();
                            } else if ((decode_res[1] & 0xFF) == 0x01) {
                                MyLog.i(TAG, "开锁失败:命令执行失败");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_BTDEVICE_EXC_FAIL);
                                }
                            } else if ((decode_res[1] & 0xFF) == 0x02) {
                                MyLog.i(TAG, "开锁失败:密钥解密失败,无效用户密钥");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_PSD_ERROR);
                                }
                            } else {
                                MyLog.e(TAG, "开锁返回异常");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_DEFAULT);
                                }
                            }

                            new Timer().schedule(new TimerTask() {

                                @Override
                                public void run() {
                                    // TODO Auto-generated method stub
                                    sendEndCmd();
                                }
                            }, 500);

                        } else {
                            MyLog.e(TAG, "开锁返回动作标志位异常,此时锁还会再次返回其他数据");
                            if (openDoorCallback != null) {
                                openDoorCallback.openDoorFail(ERROR_DEFAULT);
                            }
                        }

                    } else {
                        MyLog.e(TAG, "蓝牙开锁数据获取失败");
                        if (openDoorCallback != null) {
                            openDoorCallback.openDoorFail(ERROR_DEFAULT);
                        }
//                        disconnect(true);
                    }
                    decode_res = null;
                    res = null;
                } else {
                    MyLog.e(TAG, " value[0] =  " + value[0]
                            + " ===release()    11111111");
//                    disconnect(true);
                }
                value = null;
            }
        }

        // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发onCharacteristicWrite
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            MyLog.i(TAG,
                    "onCharacteristicWrite status = " + status
                            + ",onCharacteristicWrite------>"
                            + Utils.bytesToHexString(characteristic.getValue()));
            if (status != 0) {
//                disconnect(true);
            }
        }
    };
    
    private void sendEndCmd() {
        if (writeCharacteristic != null) {
            byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00 };
            byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
            if (cmd_encrypt != null) {
                byte[] request = new byte[17];
                request[0] = 0x08;
                for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
                        : request.length); i++) {
                    request[i] = cmd_encrypt[i - 1];
                }
                writeCharacteristic.setValue(request);
                writeCharacteristic
                        .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
                mBluetoothGatt.writeCharacteristic(writeCharacteristic);
                request = null;
            }
            cmd = null;
            cmd_encrypt = null;
        }
        // release();
    }

    private void retryUnlock() {
        if (!haveRetry) {
            haveRetry = true;
            connect();
        } else {
            haveRetry = false;
            MyLog.i(TAG, "retryUnlock  haveRetry");
        }
    }

    public void setOpenDoorCallback(OnBleScanOpenDoorCallback callback) {
        openDoorCallback = callback;
    }
    
    public synchronized void disconnect() {
        MyLog.i(TAG, "disconnect");
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        isConnectToDevice = false;
    }
}
View Code

相关文章:

  • 2021-12-31
  • 2022-12-23
  • 2021-11-22
  • 2021-12-23
  • 2021-06-17
  • 2021-07-25
  • 2021-07-29
  • 2021-06-09
猜你喜欢
  • 2021-07-10
  • 2021-11-23
  • 2021-11-23
  • 2021-12-17
  • 2022-01-23
  • 2021-12-23
相关资源
相似解决方案