蓝牙 GATT

711 阅读4分钟

蓝牙 GATT(Generic Attribute Profile)

GATT(通用属性协议)是低功耗蓝牙(BLE)的核心协议之一,定义了设备之间如何通过属性(Attribute) 进行数据交换。它是构建 BLE 应用的基础框架,适用于设备间传输小规模、低频次数据(如传感器数据、指令控制等)。

一、GATT 核心概念与角色

1、角色划分

  • GATT Server(服务端):存储数据并提供服务(如心率传感器存储心率数据)。
  • GATT Client(客户端):主动发起请求,读取或修改服务端数据(如手机 App 读取传感器数据)。 注:一个设备可以同时作为 Server 和 Client(如智能手表既向手机发送数据,又控制耳机)。

2、协议栈位置 GATT 基于ATT(Attribute Protocol)协议实现,属于 BLE 协议栈的上层应用层:

┌───────────────┐  
│   GATT        │  
├───────────────┤  
│   ATT         │  
├───────────────┤  
│   L2CAP       │  
├───────────────┤  
│   HCI         │  
└───────────────┘  

二、GATT 数据模型:服务、特征与描述符

GATT 数据以层级结构 组织,包含三个核心层级:

Service(服务)

  • 作用:封装一组功能相关的操作(如电池服务、心率服务)。
  • UUID:唯一标识符(16-bit 或 128-bit)。
    • 标准服务 UUID(如 0x180D 代表心率服务)。
    • 自定义服务 UUID(开发者定义,格式如 0000XXXX-0000-1000-8000-00805F9B34FB)。

Characteristic(特征)

  • 作用:服务中的具体数据点(如心率值、电池电量)。
  • 属性
    • PROPERTY_READ:可读
    • PROPERTY_WRITE:可写
    • PROPERTY_NOTIFY:可通知(服务端主动推送数据)
    • PROPERTY_INDICATE:需确认的通知(客户端需回复确认)
  • 结构
┌───────────────────┐  
│ Characteristic    │  
│  - Value          │  // 实际数据(如 0x64 表示心率 100)  
│  - Descriptors    │  // 描述符(如客户端配置通知)  
└───────────────────┘  

Descriptor(描述符)

  • 作用:描述特征的元数据或配置选项。
  • 常用描述符
    • Client Characteristic Configuration Descriptor(CCCD,UUID: 0x2902)用于启用/禁用特征的通知(NOTIFY 或 INDICATE)。写入 0x0001 启用通知,0x0000 禁用。
    • Characteristic User Description(UUID: 0x2901)存储人类可读的特征描述(如“心率测量值”)。

三、GATT 数据操作流程(Android 示例)

1. 客户端连接与发现服务

// 连接 GATT 服务端(以手机作为客户端连接传感器为例)
BluetoothGatt gatt = device.connectGatt(context, false, new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            gatt.discoverServices(); // 发现服务
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // 获取所有服务
            List<BluetoothGattService> services = gatt.getServices();
            // 查找目标服务(如心率服务)
            BluetoothGattService heartRateService = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"));
        }
    }
});

2. 读取特征值

BluetoothGattCharacteristic characteristic = heartRateService.getCharacteristic(UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB"));
gatt.readCharacteristic(characteristic); // 触发读取请求

// 在回调中接收数据
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        byte[] data = characteristic.getValue(); // 解析数据(如心率值)
    }
}

3. 启用通知(监听服务端推送)

// 启用通知
gatt.setCharacteristicNotification(characteristic, true);

// 配置 gatt 描述符
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);

// 接收通知数据
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    byte[] data = characteristic.getValue(); // 实时数据更新
}

4. 写入特征值(向服务端发送指令)

characteristic.setValue(new byte[]{0x01}); // 设置写入值(如开启传感器)
gatt.writeCharacteristic(characteristic);

// 写入结果回调
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.d("GATT", "写入成功");
    }
}

四、GATT 开发注意事项

连接与线程安全‌

  • BLE 操作(如 connectGatt、readCharacteristic)必须在主线程调用,但回调可能在其他线程触发,需使用 Handler 或 runOnUiThread 更新 UI。
  • 连接超时处理:Android 默认无连接超时机制,需手动实现(如 Handler.postDelayed)。

MTU 与数据传输优化

  • MTU(Maximum Transmission Unit):默认 23 字节,可通过 gatt.requestMtu() 协商更大的值(如 512 字节),提升传输效率。
  • 数据分包:长数据需分多次传输,并处理顺序和完整性。

权限与兼容性

  • Android 12+:需动态申请 BLUETOOTH_CONNECT、BLUETOOTH_SCAN 权限。
  • 设备差异:部分设备对通知(NOTIFY)支持不稳定,需测试重连机制。

资源释放

断开连接后必须调用 gatt.close(),否则会导致连接泄漏。

五、常见问题与解决

  • 连接失败(status ≠ GATT_SUCCESS)

    • 检查设备是否在广播,或尝试重启蓝牙适配器。
    • 确保应用有正确的定位权限(Android 6.0+ 需要 ACCESS_FINE_LOCATION 用于 BLE 扫描)。
  • 特征读写返回 GATT_READ_NOT_PERMITTED

    • 服务端特征未配置正确的权限(如客户端尝试写入只读特征)。
  • 通知不触发

    • 确认已调用 setCharacteristicNotification(true) 并正确写入 CCCD 描述符。
    • 服务端特征需配置 PROPERTY_NOTIFY 或 PROPERTY_INDICATE。
  • 跨设备兼容性问题‌

    • 不同厂商对 GATT 规范实现可能不一致,需实测并处理异常(如重试机制)。

通过理解 GATT 的结构与操作流程,结合 Android API 的实现,可以高效开发 BLE 应用(如健康监测、智能家居控制等)。