Android bluetooth创建GATT连接并读取设备信息

一 GATT简介

蓝牙分为经典蓝牙和低功耗蓝牙(BLE),我们常用的蓝牙遥控器就是低功耗蓝牙
低功耗蓝牙(BLE)连接都是建立在 GATT (Generic Attribute Profile) 协议之上。
GATT全称Generic Attribute Profile(直译即:通用属性协议),是一个在蓝牙连接之上的发送和接收较短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。

二 GATT的结构

GATT的结构简单的讲,如下图:

  • gatt是多个service的集合,gatt包含多个不同的service
  • service下包含多个不同的Charcteristic(特征)
  • Charcteristic又包含value和Descriptor
  • 每个Service和Charcteristic有一个唯一标识UUID
    一般我们读取BLE设备的信息,就是读取Charcteristic下的Value值
    所以我们需要知道service和Charcteristic的UUID,就能拿到这个Charcteristic下的值

    三 如何知道指定的service和Charcteristic的UUID

    一般使用BLE测试工具即可
    我这里使用的BLE5.1ScanDemo apk,上传到网盘供大家使用
    https://pan.baidu.com/s/1TdsznZsdhPPDcKbkJknyug
    提取码:6666

  • 把BLE5.1ScanDemo apk安装到手机
  • 遥控器或其他BLE设备与手机蓝牙配对连接
  • 打开apk界面会显示已配对的蓝牙设备

    四 如何建立GATT连接及如何读取Charcteristic下的数据

    android 4.0以后添加了BLE的支持,在系统BluetoothDevice.java源码中已经提供了Gatt连接的接口函数
    那么我们只需要找到指定的蓝牙设备获取它的BluetoothDevice实例,然后调用connectGatt函数即可

    3、重写GattCallback回调

    在第2步建立GATT连接连接时,需要传入gattcallback实例
    所以我们要先实例BluetoothGattCallback类并重写其回调函数,如图
    这几个回调函数后续会用到

    4、GATT连接成功,onConnectionStateChange()函数回调

    gatt连接成功或失败,会回调gattCallback下的onConnectionStateChange()函数
    接下来调用mBluetoothGatt.discoverServices()函数(功能是查询已连接的gatt下的service)

    6、onCharacteristicRead()函数回调,读取Characteristic的value值

    上一步调用readCharacteristic()后,系统会回调gattCallback下的onCharacteristicRead()
    此时我们使用回参characteristic直接getValue()即可读取到数值

    import android.annotation.SuppressLint; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import java.util.Set; import java.util.UUID; * 作者:libeibei * 日期:20201222 * 类功能说明:读取指定名称遥控器的VID、PID public class MainActivity extends Activity { public static String TAG = "BLE_READ"; public static String BLE_NAME = "川流TV"; private Context mContext; private BluetoothManager bluetoothManager; private BluetoothAdapter bluetoothAdapter; protected BluetoothDevice mSelectedDevice; private BluetoothGatt mBluetoothGatt; private BluetoothGattCharacteristic mVIDPIDCharacteristic; //已配对的设备 Set<BluetoothDevice> pairedDevices; //GATT service UUID public static final UUID DEVICE_INFO_SERVICE_UUID = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb"); //Charcteristic UUID public static final UUID VID_PID_CHARACTERISTIC_UUID = UUID.fromString("00002a50-0000-1000-8000-00805f9b34fb"); TextView tv; String VID = ""; String PID = ""; @SuppressLint("HandlerLeak") Handler handler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x1: Toast.makeText(mContext, "VID、PID读取成功", Toast.LENGTH_LONG).show(); tv.setText("VID=" + VID + " PID=" + PID); break; default: break; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv_vid_pid); @Override protected void onResume() { super.onResume(); //第一步,初始化各工具 init(); //第二步,根据名称,获取指定的蓝牙设备:mSelectedDevice getTargetBLEDevice(); //第三步,声明mGattCallback,并重写回调函数,见下面step 3 //第四步,通过上两步获取的mSelectedDevice和mGattCallback建立GATT连接 connectGatt(); //第五步,建立gatt连接后,会回调mGattCallback下的onConnectionStateChange()函数 //在onConnectionStateChange()函数中调用mBluetoothGatt.discoverServices(); //见下面step 5 //第六步,调用mBluetoothGatt.discoverServices()后 // 会回调mGattCallback下的onServicesDiscovered()函数 // 在该函数下 // 1、获取DeviceInfoService 见下面step 6-1 // 2、通过拿到的service,获取VIDPIDCharacteristic 见下面step 6-2 // 3、读取获取到的这个VIDPIDCharacteristic 见下面step 6-3 //第七步,读取VIDPIDCharacteristic后 // 会回调mGattCallback下的onCharacteristicRead()函数 // step 7-1:在这个函数下将读取出的value值 // step 7-2:转码即可 //(ascii字符转ascii值,再将十进制ascii值转为十六进制字符,即为VID和PID) //step 1 private void init() { mContext = MainActivity.this; if (bluetoothManager == null) bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (bluetoothAdapter == null) bluetoothAdapter = bluetoothManager.getAdapter(); pairedDevices = bluetoothAdapter.getBondedDevices(); //step 2 private void getTargetBLEDevice() { if (pairedDevices != null && pairedDevices.size() > 0) { for (BluetoothDevice bluetoothDevice : pairedDevices) { String name = bluetoothDevice.getName(); Log.i(TAG, "bluetoothDevice name " + name); if (bluetoothDevice != null && name.equalsIgnoreCase(BLE_NAME)) { Log.i(TAG, "已找到指定蓝牙设备,该设备MAC=" + bluetoothDevice.getAddress()); mSelectedDevice = bluetoothDevice; break; //step 3 BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); Log.i(TAG, "onConnectionStateChange newstate:" + newState + " status:" + status); if (status == BluetoothGatt.GATT_SUCCESS) { if (newState == BluetoothProfile.STATE_CONNECTED) { Log.i(TAG, "============>GATT Connect Success!!<============="); //step 5 mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.i(TAG, "onServicesDiscovered(), status = " + status); if (status == BluetoothGatt.GATT_SUCCESS) { //step 6-1:获取DeviceInfoService BluetoothGattService mDeviceInfoService = gatt.getService(DEVICE_INFO_SERVICE_UUID); if (mDeviceInfoService == null) { Log.i(TAG, "Device Info Service is null ,disconnect GATT..."); gatt.disconnect(); gatt.close(); return; //step 6-2:获取遥控器VIDPID Characteristic mVIDPIDCharacteristic = mDeviceInfoService.getCharacteristic(VID_PID_CHARACTERISTIC_UUID); if (mVIDPIDCharacteristic == null) { Log.e(TAG, "read mModelCharacteristic not found"); return; } else { //step 6-3:读取遥控器VIDPID特性 mBluetoothGatt.readCharacteristic(mVIDPIDCharacteristic); } else { Log.i(TAG, "onServicesDiscovered status false"); @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { String value = ""; if (characteristic.getUuid().equals(VID_PID_CHARACTERISTIC_UUID)) { //step 7-1:读取出characteristic的value值 value = new String(characteristic.getValue()).trim().replace(" ", ""); Log.i(TAG, "=====>读取到 value =" + value); //step 7-2:此处为ascii表字符,需转换为十进制ascii值 //再将十进制ascii值,转换为十六进制 VID = changeAsciiTo16(value.charAt(0)); PID = changeAsciiTo16(value.charAt(value.length() - 1)); //设备VID、PID读取成功,handle更新主线程界面UI handler.sendEmptyMessage(0x1); } else { Log.i(TAG, "onCharacteristicRead status wrong"); if (mBluetoothGatt != null) mBluetoothGatt.disconnect(); @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.i(TAG, "onCharacteristicWrite:" + characteristic.getUuid().toString()); //step 4 private void connectGatt() { if (mSelectedDevice != null) mBluetoothGatt = mSelectedDevice.connectGatt(mContext, false, mGattCallback); Toast.makeText(mContext, "没有找到指定的蓝牙设备,无法建立GATT", Toast.LENGTH_LONG).show(); private String changeAsciiTo16(char a) { Log.i(TAG, "change from a =" + a); String value = ""; int val = (int) a; Log.i(TAG, "change to 10进制ASCII值 val =" + val); //ascii值到 value = Integer.toHexString(val).toUpperCase(); Log.i(TAG, "change to 16进制字符串 value =" + value); return value;

    最终通过GATT读取到的遥控器VID和PID显示到界面上如下