第2章 BLE通信基础:GATT协议、Service/Characteristic/Descriptor、MTU与连接参数
2.1 GATT协议概述
GATT(Generic Attribute Profile)是BLE(Bluetooth Low Energy)应用层通信的核心协议,定义了设备之间如何通过Attribute Protocol(ATT)传输数据。GATT采用Client/Server模型:
- GATT Client(通常为手机、平板)发起请求,读写数据。
- GATT Server(通常为外设、传感器)存储数据,响应请求。
GATT 建立在 GAP(Generic Access Profile)之上——GAP 负责发现与连接,GATT 负责连接后的数据交互。两者分工明确:GAP 管“连接”,GATT 管“通信”。
要点:在依赖注入框架集成BLE组件时,GATT 的 Client/Server 关系决定了 Service 层的接口设计——通常将 GATT Client 抽象为可注入的 BLE 连接管理器。
2.2 GATT 三层结构:Service / Characteristic / Descriptor
GATT 数据以层级结构组织,自上而下依次为 Profile → Service → Characteristic → Descriptor,每个元素通过 16-bit 或 128-bit UUID 唯一标识。
| 层级 | 说明 | UUID 示例 | 包含关系 |
|---|---|---|---|
| Service(服务) | 一组相关功能的集合,如“电量服务” | 0x180F | 包含 1~N 个 Characteristic |
| Characteristic(特征) | 一个具体数据点,如“电量百分比” | 0x2A19 | 包含 Value + 0~N 个 Descriptor |
| Descriptor(描述符) | 描述 Characteristic 的属性或配置,如 CCCD 用于启用通知 | 0x2902 | 属于一个 Characteristic |
- Service:逻辑分组,例如设备信息服务(DIS, 0x180A)包含设备名、序列号等特征。
- Characteristic:实际读写的数据单元,包含 Value 和 Properties(读、写、通知、指示)。
- Descriptor:附加元数据,最常用的是 CCCD(Client Characteristic Configuration Descriptor,0x2902),用于启停通知/指示。
设计习惯:在依赖注入框架中,每个 Service 通常对应一个
@Singleton 的 Repository 或 Service 类,Characteristic 的读写方法则封装为可注入的 UseCase。
2.3 MTU 与连接参数
MTU(Maximum Transmission Unit)定义了 GATT 单次数据包的最大 payload 字节数。BLE 4.x 默认 MTU 为 23 字节(含 3 字节 ATT 头部,实际有效负载 20 字节),BLE 5.x 可协商至 512 字节。
连接参数决定了 BLE 链路的功耗与实时性,由连接间隔、从机延迟、超时时间三个关键值构成:
| 参数 | 含义 | 典型范围 | 对性能的影响 |
|---|---|---|---|
| Connection Interval | 主机与从机之间的数据交换周期 | 7.5 ms ~ 4 s | 间隔越小,数据延迟越低,功耗越高 |
| Slave Latency | 从机可跳过的最大连接事件次数 | 0 ~ 4 次 | 延迟越大,从机休眠时间越长,功耗越低 |
| Supervision Timeout | 连接丢失的超时阈值 | 10 ms ~ 32 s | 超时过短易误判断开,过长则响应迟钝 |
| MTU | 单个 ATT PDU 的最大字节数 | 23 ~ 512 bytes | MTU 越大,大包传输越快,但需双方协商支持 |
常见误区:MTU 并非越大越好——超过双方硬件 FIFO 容量会导致分片重传,反而降低吞吐。建议在连接后通过
requestMtu() 协商到双方都支持的最大值。
2.4 GATT 通信流程
一次完整的 BLE GATT 交互遵循以下步骤:
- 扫描与连接(GAP 层):发现外设并发起连接。
- 服务发现:Client 调用
discoverServices(),Server 返回所有 Service 及下属 Characteristic、Descriptor。 - MTU 协商:Client 发起
requestMtu(),双方确认最大传输单元。 - 读写操作:根据业务需求读取 Characteristic Value 或写入数据。
- 通知/指示订阅:写入 CCCD 开启通知通道,Server 主动推送数据。
- 断开与清理:释放 Gatt 资源,断开连接。
依赖注入关注点:将上述流程中的“服务发现”和“MTU 协商”封装为 Observable/Flow 异步操作,注入到 ViewModel 或 UseCase 层,可使 BLE 逻辑与 UI 完全解耦。
2.5 代码示例:GATT 服务发现与特征读写
以下示例展示了 Android(Java)和 iOS(Swift)两端执行 GATT 服务发现与特征读取的核心代码。
Android(使用 BluetoothGatt)
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();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 处理断开
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService service : gatt.getServices()) {
// 根据 UUID 找到目标 Service
if (service.getUuid().toString().equals(DEVICE_INFO_SERVICE_UUID)) {
for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
// 找到目标 Characteristic 后读取
if (characteristic.getUuid().toString().equals(BATTERY_LEVEL_UUID)) {
gatt.readCharacteristic(characteristic);
}
}
}
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
byte[] value = characteristic.getValue();
// 处理读取到的数据
}
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// MTU 协商成功,当前 mtu 为协商值
}
}
});
// 发起 MTU 协商(在连接成功后调用)
gatt.requestMtu(256);
iOS(使用 CoreBluetooth)
class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var peripheral: CBPeripheral?
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: [DIS_SERVICE_UUID], options: nil)
}
}
func centralManager(_ central: CBCentralManager,
did