Deskryptor — to pomocniczy atrybut cechy BLE, który dostarcza metadane lub konfigurację dla jej wartości. Bluetooth Core Specification 5.4 definiuje deskryptory jako integralną część profilu GATT: opisują one jednostki miary, zakresy wartości, nazwy tekstowe i zarządzają subskrypcją powiadomień. Najbardziej znanym deskryptorem jest Client Characteristic Configuration Descriptor (CCCD, 0x2902), który umożliwia Central subskrypcję powiadomień z cechy. Według Bluetooth SIG, określono 8 standardowych deskryptorów, a niestandardowe deskryptory mogą być tworzone przez programistę dla dowolnych dodatkowych metadanych.
Najważniejsze
Deskryptor — to atrybut protokołu GATT, który dołącza się do cechy i dostarcza dodatkowych informacji o jej wartości lub sposobie użycia. W terminologii GATT cecha to dane, a deskryptor to metadane tych danych.
Każda cecha może mieć zero lub więcej deskryptorów. Deskryptory są definiowane jako oddzielne atrybuty w tabeli GATT i mają własne 16-bitowe UUID w przestrzeni Bluetooth SIG. Deskryptory nie mogą istnieć samodzielnie — są zawsze powiązane z konkretną cechą.
Według Bluetooth Core Specification 5.4 (2023), deskryptory dzielą się na dwie kategorie: standardowe (określone przez Bluetooth SIG) i niestandardowe (definiowane przez programistę). Standardowe deskryptory mają UUID w zakresie 0x2900–0x2908 i pełnią ściśle określone funkcje. Niestandardowe deskryptory używają 128-bitowych UUID, podobnie jak niestandardowe usługi i cechy.
Deskryptory to kluczowy mechanizm konfiguracji zachowania połączenia BLE. Bez CCCD (0x2902) nie można subskrybować powiadomień, bez Presentation Format (0x2904) Central nie dowie się, jak interpretować surowe bajty cechy.
CCCD z UUID 0x2902 to najważniejszy i najszerzej używany deskryptor w BLE. Zarządza on subskrypcją Central na powiadomienia (Notify) i potwierdzone powiadomienia (Indicate) z cechy. Bez zapisu w CCCD cecha nie będzie wysyłać powiadomień, nawet jeśli ma właściwość Notify.
CCCD to 16-bitowa wartość, w której najmłodszy bit (0x0001) włącza Notify, a drugi bit (0x0002) włącza Indicate. Central zapisuje odpowiednią wartość w CCCD, subskrybując w ten sposób powiadomienia. Jeśli Central zapisze 0x0000 — subskrypcja jest anulowana.
Mechanizm działania CCCD wygląda następująco: gdy Central wykryje cechę z obsługą Notify, znajduje jej deskryptor CCCD i zapisuje 0x0001. Następnie, za każdym razem gdy Peripheral aktualizuje wartość cechy, jest ona automatycznie wysyłana do zasubskrybowanego Central przez Handle Value Notification (bez zapytania od Central).
Według Apple Core Bluetooth Programming Guide, CCCD jest automatycznie zarządzany przez system przy wywołaniu setNotifyValue dla cechy. Programista nie musi ręcznie szukać i zapisywać CCCD — Core Bluetooth robi to automatycznie przy aktywacji powiadomień.
W iOS subskrypcja odbywa się przez metodę setNotifyValue na obiekcie CBPeripheral.
import CoreBluetooth
// Subskrybuj powiadomienia cechy
peripheral.setNotifyValue(true, for: characteristic)
// Delegate otrzymuje zaktualizowane wartości
func peripheral(
_ peripheral: CBPeripheral,
didUpdateValueFor characteristic: CBCharacteristic,
error: Error?
) {
// characteristic.value ma nowe dane
guard let data = characteristic.value else { return }
}
Na Android subskrypcja powiadomień odbywa się przez BluetoothGatt.setCharacteristicNotification. Programista musi ręcznie zapisać wartość 0x0001 w CCCD.
import android.bluetooth.*;
// Włącz powiadomienia
bluetoothGatt.setCharacteristicNotification(characteristic, true);
// Zapisz 0x0001 w CCCD
BluetoothGattDescriptor descriptor = characteristic
.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
Bluetooth SIG określił 8 standardowych deskryptorów dla typowych zadań konfiguracji cech. Każdy deskryptor ma ściśle określony UUID i format wartości.
| UUID | Nazwa deskryptora | Przeznaczenie | Typ wartości |
|---|---|---|---|
| 0x2900 | Characteristic Extended Properties | Dodatkowe właściwości cechy | uint16 (maska bitowa) |
| 0x2901 | Characteristic User Description | Tekstowa nazwa cechy | UTF-8 string |
| 0x2902 | Client Characteristic Configuration | Zarządzanie powiadomieniami | uint16 |
| 0x2903 | Server Characteristic Configuration | Konfiguracja serwera dla broadcast | uint16 |
| 0x2904 | Characteristic Presentation Format | Format prezentacji danych | Struktura (7 bajtów) |
| 0x2905 | Characteristic Aggregate Format | Łączenie wielu cech | Lista UUID |
| 0x2906 | Valid Range | Zakres dozwolonych wartości | uint16 × 2 (min, max) |
| 0x2907 | External Report Reference | Odnośnik do zewnętrznego raportu | uint16 |
| 0x2908 | Report Reference | Odnośnik do wewnętrznego raportu | uint8 + uint16 |
Characteristic User Description (0x2901) jest szczególnie przydatny dla UI: zawiera czytelną dla człowieka nazwę cechy, którą można wyświetlić w interfejsie aplikacji. Na przykład cecha Battery Level może mieć deskryptor User Description z wartością „Poziom naładowania baterii”.
Characteristic Presentation Format (0x2904) zawiera informacje o tym, jak interpretować wartość cechy: format (uint8, uint16, float), eksponenta, jednostka miary (przez UUID z rejestru Bluetooth SIG), przestrzeń nazw i opis. Na przykład temperatura może być int16 z eksponentą -2 (wartość podziałki 0.01°C) i jednostką miary „stopnie Celsjusza”.
Dostęp do deskryptorów różni się na iOS i Android. Core Bluetooth automatycznie zarządza CCCD przy włączaniu powiadomień, a Android wymaga ręcznego zapisu wartości w deskryptorze. Rozważmy oba podejścia.
W iOS programista zwykle nie wchodzi w interakcję z deskryptorami bezpośrednio. Core Bluetooth automatycznie znajduje CCCD i zapisuje w nim odpowiednią wartość przy wywołaniu setNotifyValue. Do odczytu deskryptorów używa się metody discoverDescriptors.
// Odkryj deskryptory
peripheral.discoverDescriptors(for: characteristic)
// Odczytaj wartość 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)
}
}
}
Na Android programista musi sam znaleźć deskryptor CCCD cechy i zapisać w nim wartość, aby włączyć powiadomienia. To dodatkowy krok w porównaniu do iOS.
// Odczytaj wartość deskryptora User Description
BluetoothGattDescriptor userDesc = characteristic
.getDescriptor(
UUID.fromString("00002901-0000-1000-8000-00805F9B34FB")
);
if (userDesc != null) {
bluetoothGatt.readDescriptor(userDesc);
}
Niestandardowe deskryptory są używane, gdy standardowe deskryptory Bluetooth SIG są niewystarczające. Na przykład producent czujnika medycznego może dodać deskryptor z informacją o kalibracji sensora lub deskryptor z dziennikiem błędów.
Niestandardowy deskryptor jest tworzony z 128-bitowym UUID, podobnie jak niestandardowa cecha. Po stronie Peripheral programista dodaje go do cechy przez odpowiednie API. Po stronie Central deskryptor jest wykrywany automatycznie przy wywołaniu discoverDescriptors.
Przy tworzeniu niestandardowego deskryptora przestrzegaj zasad: długość wartości musi odpowiadać oczekiwanemu typowi danych, prawa dostępu (read/write) muszą być jawnie określone. Nie używaj UUID z zakresu standardowych deskryptorów Bluetooth SIG (0x2900–0x2908) do niestandardowych celów.
Cecha i deskryptor — to różne poziomy atrybutów GATT. Cecha to punkt danych, a deskryptor to jej metadane lub konfiguracja. Różnica przejawia się w kilku aspektach: przeznaczeniu, UUID i zasadach dostępu.
Cecha ma obowiązkową deklarację (declaration), która zawsze znajduje się w tabeli GATT. Deskryptor jest opcjonalny — cecha może nie mieć deskryptorów. Cecha może być odczytana, zapisana lub wysyłać powiadomienia niezależnie. Deskryptor jest zawsze powiązany z cechą i nie istnieje osobno.
Według Bluetooth Core Specification 5.4, w tabeli GATT kolejność atrybutów jest następująca: deklaracja usługi, deklaracja cechy, wartość cechy, deskryptory cechy (jeśli są), następna usługa. Ta kolejność jest obowiązkowa dla prawidłowego działania klientów GATT.
Często zadawane pytania
CCCD jest potrzebny do zarządzania subskrypcją: właściwość Notify tylko mówi, że cecha może wysyłać powiadomienia, ale ich nie włącza. Central musi jawnie zasubskrybować, zapisując 0x0001 w CCCD. Zapobiega to niepożądanemu wysyłaniu danych do nieprzygotowanego Central.
Specyfikacja nie ogranicza liczby deskryptorów. W praktyce każda cecha zwykle ma 1–3 deskryptory: CCCD (dla cech z Notify/Indicate), opcjonalnie User Description i Presentation Format. Zbyt wiele deskryptorów zwiększa rozmiar tabeli GATT.
W większości przypadków nie trzeba. Core Bluetooth na iOS i Android automatycznie dodaje CCCD do cech, które mają właściwości Notify lub Indicate. Niestandardowe deskryptory są tworzone ręcznie tylko dla specyficznych metadanych.
Nie, CCCD ma ściśle określony format (uint16) i przeznaczenie — zarządzanie powiadomieniami. Zapis innych wartości narusza specyfikację Bluetooth i może spowodować niesprawność subskrypcji. Do niestandardowych danych używaj niestandardowych deskryptorów z 128-bitowym UUID.
User Description zawiera czytelną dla człowieka nazwę cechy — na przykład „Temperatura obudowy” zamiast UUID 0x2A6E. Aplikacja może wyświetlić tę nazwę w interfejsie, czyniąc interakcję z urządzeniem BLE zrozumiałą dla użytkownika bez sztywno zakodowanych ciągów znaków.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również