Descriptor — это вспомогательный атрибут BLE-характеристики, который предоставляет метаданные или конфигурацию для её значения. Bluetooth Core Specification 5.4 определяет дескрипторы как неотъемлемую часть GATT-профиля: они описывают единицы измерения, диапазоны значений, текстовые имена и управляют подпиской на уведомления. Наиболее известный дескриптор — Client Characteristic Configuration Descriptor (CCCD, 0x2902), который позволяет Central подписываться на уведомления от характеристики. По данным Bluetooth SIG, специфицировано 8 стандартных дескрипторов, а кастомные дескрипторы могут создаваться разработчиком для любых дополнительных метаданных.
Главное
Descriptor — это атрибут GATT-протокола, который прикрепляется к Characteristic и предоставляет дополнительную информацию о её значении или способе использования. В терминологии GATT, характеристика — это данные, а дескриптор — это метаданные этих данных.
Каждая Characteristic может иметь ноль или более дескрипторов. Дескрипторы определяются как отдельные атрибуты в GATT-таблице и имеют собственные 16-битные UUID в пространстве Bluetooth SIG. Дескрипторы не могут существовать самостоятельно — они всегда привязаны к конкретной характеристике.
По данным 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 обновляет значение характеристики, оно автоматически отправляется подписанному Central через Handle Value Notification (без запроса от Central).
По данным Apple Core Bluetooth Programming Guide, CCCD автоматически управляется системой при вызове setNotifyValue для характеристики. Разработчику не нужно вручную искать и записывать CCCD — Core Bluetooth делает это самостоятельно при активации уведомлений.
В iOS подписка выполняется через метод setNotifyValue на объекте CBPeripheral.
import CoreBluetooth
// Subscribe to characteristic notifications
peripheral.setNotifyValue(true, for: characteristic)
// Delegate receives updated values
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
// characteristic.value has new data
guard let data = characteristic.value else { return }
}
На Android подписка на уведомления выполняется через BluetoothGatt.setCharacteristicNotification. Разработчик также должен записать значение 0x0001 в CCCD вручную.
import android.bluetooth.*;
// Enable notifications
bluetoothGatt.setCharacteristicNotification(characteristic, true);
// Write 0x0001 to 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 string |
| 0x2902 | Client Characteristic Configuration | Управление уведомлениями | uint16 |
| 0x2903 | Server Characteristic Configuration | Конфигурация сервера для broadcast | 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), экспонента, единица измерения (через UUID из реестра Bluetooth SIG), пространство имён и описание. Например, температура может быть int16 с экспонентой -2 (цена деления 0.01°C) и единицей измерения "градус Цельсия".
Доступ к дескрипторам отличается на iOS и Android. Core Bluetooth автоматически управляет CCCD при включении уведомлений, а Android требует ручной записи значения в дескриптор. Рассмотрим оба подхода.
В iOS разработчик обычно не взаимодействует с дескрипторами напрямую. Core Bluetooth автоматически находит CCCD и записывает в него нужное значение при вызове setNotifyValue. Для чтения дескрипторов используется метод discoverDescriptors.
// Discover descriptors
peripheral.discoverDescriptors(for: characteristic)
// Read User Description value
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.
// Read User Description descriptor value
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) должны быть явно указаны. Не используйте UUID из диапазона стандартных дескрипторов Bluetooth SIG (0x2900–0x2908) для кастомных целей.
Характеристика и дескриптор — это разные уровни GATT-атрибутов. Характеристика — это точка данных, а дескриптор — это её метаданные или конфигурация. Разница проявляется в нескольких аспектах: назначении, UUID и правилах доступа.
Характеристика имеет обязательное объявление (declaration), которое всегда находится в GATT-таблице. Дескриптор опционален — характеристика может не иметь дескрипторов. Характеристика может быть прочитана, записана или отправлять уведомления независимо. Дескриптор всегда привязан к характеристике и не существует отдельно.
По данным Bluetooth Core Specification 5.4, в GATT-таблице порядок атрибутов следующий: объявление сервиса, объявление характеристики, значение характеристики, дескрипторы характеристики (если есть), затем следующий сервис. Этот порядок обязателен для корректной работы GATT-клиентов.
Часто задаваемые вопросы
CCCD нужен для управления подпиской: свойство Notify только говорит, что характеристика может отправлять уведомления, но не включает их. Central должен явно подписаться, записав 0x0001 в CCCD. Это предотвращает нежелательную отправку данных на неподготовленный Central.
Спецификация не ограничивает количество дескрипторов. На практике каждая характеристика обычно имеет 1–3 дескриптора: CCCD (для характеристик с Notify/Indicate), опционально User Description и Presentation Format. Слишком много дескрипторов увеличивает размер GATT-таблицы.
В большинстве случаев не нужно. Core Bluetooth на iOS и Android автоматически добавляют CCCD к характеристикам, у которых есть свойства Notify или Indicate. Кастомные дескрипторы создаются вручную только для специфических метаданных.
Нет, CCCD имеет строго определённый формат (uint16) и назначение — управление уведомлениями. Запись других значений нарушает спецификацию Bluetooth и может привести к неработоспособности подписки. Для кастомных данных используйте кастомные дескрипторы с 128-битным UUID.
User Description содержит человекочитаемое имя характеристики — например, "Температура корпуса" вместо UUID 0x2A6E. Приложение может отображать это имя в интерфейсе, делая взаимодействие с BLE-устройством понятным для пользователя без жёстко закодированных строк.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также