描述符 — 是BLE特征的辅助属性,为其值提供元数据或配置。Bluetooth Core Specification 5.4将描述符定义为GATT概要的组成部分:它们描述计量单位、值范围、文本名称并管理通知订阅。最著名的描述符是Client Characteristic Configuration Descriptor (CCCD,0x2902),它允许Central订阅来自特征的通知。据Bluetooth SIG报告,已规范了8个标准描述符,开发者可以为任何额外元数据创建自定义描述符。
主要要点
描述符 — 是GATT协议的属性,附加到特征上并提供关于其值或使用方式的额外信息。在GATT术语中,特征是数据,而描述符是这些数据的元数据。
每个特征可以有零个或更多描述符。描述符在GATT表中定义为单独的属性,并在Bluetooth SIG空间中拥有自己的16位UUID。描述符不能独立存在 — 它们始终绑定到特定的特征。
据Bluetooth Core Specification 5.4 (2023),描述符分为两类:标准(由Bluetooth SIG定义)和自定义(由开发者定义)。标准描述符的UUID在0x2900–0x2908范围内,执行严格定义的功能。自定义描述符使用128位UUID,类似于自定义服务和特征。
描述符是BLE连接行为配置的关键机制。没有CCCD (0x2902)无法订阅通知,没有Presentation Format (0x2904) Central将不知道如何解释特征的原始字节。
CCCD (UUID 0x2902)是BLE中最重要且最广泛使用的描述符。它管理Central对特征通知(Notify)和确认通知(Indicate)的订阅。未向CCCD写入时,即使特征具有Notify属性,也不会发送通知。
CCCD是一个16位值,其中最低位(0x0001)启用Notify,第二位(0x0002)启用Indicate。Central向CCCD写入所需值,从而订阅通知。如果Central写入0x0000— —订阅被取消。
CCCD的工作机制如下:当Central检测到支持Notify的特征时,它找到其CCCD描述符并写入0x0001。此后,每当Peripheral更新特征值时,该值会通过Handle Value Notification自动发送给已订阅的Central(无需Central请求)。
据Apple Core Bluetooth Programming Guide报告,当为特征调用setNotifyValue时,CCD由系统自动管理。开发者无需手动查找和写入CCCD — Core Bluetooth在激活通知时自动完成此操作。
在iOS中,订阅通过CBPeripheral对象上的setNotifyValue方法执行。
import CoreBluetooth
// 订阅特征通知
peripheral.setNotifyValue(true, for: characteristic)
// 委托接收更新后的值
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
// characteristic.value有新数据
guard let data = characteristic.value else { return }
}
在Android上,订阅通知通过BluetoothGatt.setCharacteristicNotification执行。开发者还必须手动将值0x0001写入CCCD。
import android.bluetooth.*;
// 启用通知
bluetoothGatt.setCharacteristicNotification(characteristic, true);
// 将0x0001写入CCCD
BluetoothGattDescriptor descriptor = characteristic
.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
Bluetooth SIG已定义8个标准描述符,用于常见的特征配置任务。每个描述符都有严格定义的UUID和值格式。
| UUID | 描述符名称 | 用途 | 值类型 |
|---|---|---|---|
| 0x2900 | Characteristic Extended Properties | 特征的附加属性 | uint16(位掩码) |
| 0x2901 | Characteristic User Description | 特征的文本名称 | UTF-8字符串 |
| 0x2902 | Client Characteristic Configuration | 管理通知 | uint16 |
| 0x2903 | Server Characteristic Configuration | 广播服务器配置 | uint16 |
| 0x2904 | Characteristic Presentation Format | 数据呈现格式 | 结构体(7字节) |
| 0x2905 | Characteristic Aggregate Format | 合并多个特征 | UUID列表 |
| 0x2906 | Valid Range | 允许值范围 | uint16 × 2 (min, max) |
| 0x2907 | External Report Reference | 引用外部报告 | uint16 |
| 0x2908 | Report Reference | 引用内部报告 | uint8 + uint16 |
Characteristic User Description (0x2901)对UI特别有用:它包含可以在应用界面中显示的人可读特征名称。例如,特征Battery Level可以具有值为“电池充电水平”的User Description描述符。
Characteristic Presentation Format (0x2904)包含关于如何解释特征值的信息:格式(uint8, uint16, float)、指数、计量单位(通过来自Bluetooth SIG注册表的UUID)、名称空间和描述。例如,温度可以是int16,指数为-2(分度值0.01°C),计量单位为“摄氏度”。
访问描述符在iOS和Android上不同。Core Bluetooth在启用通知时自动管理CCCD,而Android需要手动将值写入描述符。让我们看看这两种方法。
在iOS中,开发者通常不直接与描述符交互。Core Bluetooth自动查找CCCD并在调用setNotifyValue时写入所需值。要读取描述符,使用discoverDescriptors方法。
// 发现描述符
peripheral.discoverDescriptors(for: characteristic)
// 读取User Description值
func peripheral(
_ peripheral: CBPeripheral,
didDiscoverDescriptorsFor characteristic: CBCharacteristic,
error: Error?
) {
for descriptor in characteristic.descriptors ?? [] {
if descriptor.uuid == CBUUID("2901") {
peripheral.readValue(for: descriptor)
}
}
}
在Android上,开发者必须自行找到特征的CCCD描述符并写入值以启用通知。与iOS相比,这是一个额外步骤。
// 读取User Description描述符值
BluetoothGattDescriptor userDesc = characteristic
.getDescriptor(
UUID.fromString("00002901-0000-1000-8000-00805F9B34FB")
);
if (userDesc != null) {
bluetoothGatt.readDescriptor(userDesc);
}
自定义描述符在标准Bluetooth SIG描述符不足时使用。例如,医疗传感器制造商可以添加具有传感器校准信息的描述符或带有错误日志的描述符。
自定义描述符使用128位UUID创建,类似于自定义特征。在Peripheral端,开发者通过相应API将其添加到特征。在Central端,描述符在调用discoverDescriptors时自动发现。
创建自定义描述符时请遵守规则:值的长度必须与预期数据类型匹配,访问权限(read/write)必须明确指定。不要将标准Bluetooth SIG描述符UUID范围(0x2900–0x2908)中的UUID用于自定义目的。
特征和描述符 — 是GATT属性的不同层级。特征是数据点,描述符是其元数据或配置。差异体现在几个方面:用途、UUID和访问规则。
特征有必要声明(declaration),始终位于GATT表中。描述符是可选的 — 特征可以没有描述符。特征可以独立读取、写入或发送通知。描述符始终绑定到特征,不能单独存在。
据Bluetooth Core Specification 5.4,在GATT表中属性的顺序如下:服务声明、特征声明、特征值、特征描述符(如果有),然后是下一个服务。这种顺序对于GATT客户端的正确运作是必不可少的。
常见问题
CCCD用于管理订阅:Notify属性只表示特征可以发送通知,但不会启用它们。Central必须通过向CCCD写入0x0001来明确订阅。这防止了不必要的数据发送到未准备好的Central。
规范没有限制描述符的数量。实际上,每个特征通常有1–3个描述符:CCCD(用于具有Notify/Indicate的特征)、可选的User Description和Presentation Format。过多的描述符会增大GATT表的尺寸。
在大多数情况下不需要。iOS和Android上的Core Bluetooth会自动将CCCD添加到具有Notify或Indicate属性的特征。自定义描述符仅为特定元数据手动创建。
不能,CCCD有严格定义的格式(uint16)和用途 — 管理通知。写入其他值会违反Bluetooth规范,可能导致订阅不能工作。对于自定义数据,请使用具有128位UUID的自定义描述符。
User Description包含人可读的特征名称 — 例如,“机笼温度”代暿UUID 0x2A6E。应用程序可以在界面中显示这个名称,使用户无需硬编码字符串即可理解与BLE设备的交互。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。