GATT (Generic Attribute Profile) — profil Bluetooth Low Energy (BLE), który definiuje strukturę danych i zasady wymiany informacji między urządzeniami BLE. GATT jest zbudowany na bazie Attribute Protocol (ATT) i organizuje dane w hierarchię: usługi (services), charakterystyki (characteristics) i deskryptory (descriptors). Według danych Bluetooth SIG (2025), profil GATT jest używany w 98% wszystkich aplikacji BLE — od trackerów fitness po inteligentne zamki i czujniki medyczne.
Najważniejsze
GATT (Generic Attribute Profile) — to profil Bluetooth Low Energy (BLE), który określa, w jaki sposób dwa urządzenia BLE wymieniają dane przez Attribute Protocol (ATT). GATT standaryzuje strukturę danych: wszystkie dane są zorganizowane w hierarchię „usługa → charakterystyka → deskryptor”. Profil został wprowadzony w specyfikacji Bluetooth 4.0 (2010) wraz z BLE i od tego czasu pozostaje głównym mechanizmem przesyłania danych dla energooszczędnych urządzeń Bluetooth. W przeciwieństwie do klasycznego Bluetooth, gdzie dane są przesyłane przez port szeregowy (SPP), GATT zapewnia strukturalny dostęp do danych poprzez operacje odczytu, zapisu i powiadomień.
Stos BLE składa się z kilku warstw: Physical Layer (radio), Link Layer (zarządzanie połączeniem), L2CAP (logiczna transmisja kanałowa), ATT (Attribute Protocol — dostęp do atrybutów) i GATT (profil oparty na ATT). GATT to najwyższa warstwa, z którą pracuje programista aplikacji. Leżący poniżej ATT zapewnia podstawowe operacje: odczyt, zapis, powiadomienie i wskazanie atrybutów. GATT natomiast dodaje semantykę: definiuje, czym jest usługa, charakterystyka, jak są grupowane i jakie zasady obowiązują przy ich odczycie i zapisie. Według Bluetooth Core Specification 5.4 (2023), GATT obsługuje do 65 535 atrybutów (usług + charakterystyk + deskryptorów) na jednym urządzeniu.
Hierarchia GATT składa się z trzech poziomów. Usługa (Service) — logiczna grupa charakterystyk rozwiązująca jedno zadanie (np. „Usługa baterii” lub „Usługa Heart Rate”). Charakterystyka (Characteristic) — jednostka danych o znanym typie: bieżący poziom naładowania, odczyty czujnika, stan przełącznika. Każda charakterystyka ma wartość (value) i jeden lub więcej deskryptorów (Descriptors) opisujących metadane: jednostki miary, ustawienia powiadomień, zakres wartości. UUID (Universally Unique Identifier) jednoznacznie identyfikuje każdą usługę i każdą charakterystykę.
GATT obsługuje cztery typy operacji interakcji z charakterystykami. Read — klient żąda bieżącej wartości charakterystyki od serwera. Write — klient wysyła nową wartość do serwera. Notify — serwer wysyła wartość do klienta bez potwierdzenia (szybszy, ale mniej niezawodny sposób). Indicate — serwer wysyła wartość z potwierdzeniem odbioru (bardziej niezawodny, ale wolniejszy). Programista wybiera typ w zależności od scenariusza: do odczytów czujnika tętna wystarczy Notify, do zapisu konfiguracji inteligentnego zamka — Write z potwierdzeniem.
// Struktura danych GATT w kodzie Android
data class BleService(
val uuid: UUID,
val characteristics: List<BleCharacteristic>
)
data class BleCharacteristic(
val uuid: UUID,
val properties: Int, // ODCZYT, ZAPIS, POWIADOMIENIE, WSKAZANIE
val descriptors: List<BleDescriptor>,
var value: ByteArray?
)
W specyfikacji BLE zdefiniowane są dwa główne profile: GAP (Generic Access Profile) i GATT (Generic Attribute Profile). GAP odpowiada za wykrywanie urządzeń, nawiązywanie połączenia i zarządzanie widocznością — to „warstwa sieciowa” BLE. GATT — za wymianę danych po nawiązaniu połączenia, czyli „warstwa aplikacyjna”. Programista używa GAP do skanowania i łączenia się z urządzeniem, a GATT — do odczytu, zapisu i otrzymywania powiadomień od podłączonego urządzenia.
| Cecha | GAP | GATT |
|---|---|---|
| Przeznaczenie | Wykrywanie i łączenie | Wymiana danych |
| Role | Central / Peripheral | Client / Server |
| Protokół | HCI, Link Layer | ATT (Attribute Protocol) |
| Etap | Przed połączeniem | Po połączeniu |
| Główna klasa | BluetoothAdapter | BluetoothGatt |
GATT definiuje dwie role: GATT Server i GATT Client. Serwer — urządzenie dostarczające dane (np. tracker fitness przesyłający odczyty tętna). Klient — urządzenie żądające danych (np. smartfon odczytujący pomiary). W większości scenariuszy aplikacja na Android pełni rolę GATT Client, a peryferia BLE — GATT Server. Jednak Android może również być GATT Server — na przykład, gdy aplikacja emuluje urządzenie BLE dla innych urządzeń. Rola jest określana na etapie nawiązywania połączenia GATT i nie zmienia się w trakcie sesji.
Proces komunikacji GATT rozpoczyna się po nawiązaniu połączenia BLE między urządzeniami. Najpierw GATT Client wykrywa usługi na GATT Server poprzez operację Service Discovery — Android wykonuje ją automatycznie po połączeniu przez BluetoothGatt.discoverServices(). Po wykryciu klient otrzymuje listę dostępnych usług, charakterystyk i deskryptorów. Następnie klient może odczytywać wartości charakterystyk (Read), zapisywać nowe wartości (Write) lub subskrybować powiadomienia (Set Notify/Indicate). GATT Server może wysyłać Notify/Indicate do klienta w dowolnym momencie po nawiązaniu połączenia.
Domyślnie rozmiar pojedynczego pakietu GATT (MTU, Maximum Transmission Unit) w BLE wynosi 23 bajty, z czego 3 bajty to nagłówek ATT, a 20 bajtów to dane użyteczne. Jeśli aplikacja musi przesłać więcej danych (np. aktualizacja OTA firmware), można zażądać zwiększenia MTU do 517 bajtów przez requestMtu() w Android. Zwiększone MTU zmniejsza liczbę pakietów potrzebnych do przesłania jednego bloku danych — zamiast 50 małych pakietów można wysłać 2 duże, co obniża zużycie energii i przyspiesza transmisję. Maksymalny MTU zależy od wersji BLE i możliwości układu — Bluetooth 5.0 obsługuje do 517 bajtów, starsze wersje — do 247 bajtów.
// Subskrybuj powiadomienia o zmianach charakterystyki
fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, true)
// Włącz CCCD (Client Characteristic Configuration Descriptor)
val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val descriptor = characteristic.getDescriptor(cccdUuid)
descriptor?.let {
it.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
gatt.writeDescriptor(it)
}
}
Aby otrzymywać powiadomienia od GATT Server w Android, należy zaimplementować callback BluetoothGattCallback.onCharacteristicChanged(). W tej metodzie aplikacja otrzymuje zaktualizowaną wartość charakterystyki za każdym razem, gdy serwer wysyła Notify lub Indicate. Do przetwarzania danych w czasie rzeczywistym używaj bufora i korutyn Kotlin — pozwoli to nie blokować wątku UI przy intensywnych powiadomieniach (np. 100+ odczytów tętna na sekundę). Przy indykacjach (Indicate) koniecznie wywołuj gatt.sendResponse() po stronie serwera — w kliencie Android jest to obsługiwane automatycznie.
Na Android praca z GATT jest realizowana przez klasy pakietu android.bluetooth: BluetoothGatt (połączenie), BluetoothGattService (usługa), BluetoothGattCharacteristic (charakterystyka) i BluetoothGattDescriptor (deskryptor). Połączenie z urządzeniem BLE rozpoczyna się od wywołania BluetoothDevice.connectGatt() — ta metoda zwraca BluetoothGatt, przez który wykonywane są wszystkie dalsze operacje. Wszystkie callbacki GATT przychodzą w BluetoothGattCallback — to asynchroniczny interfejs wywoływany w tym samym wątku, w którym został utworzony BluetoothGatt. Ważne: wszystkie operacje GATT muszą być wykonywane sekwencyjnie — jednoczesne wywołanie kilku operacji na tym samym BluetoothGatt prowadzi do błędów.
Prawidłowe zarządzanie cyklem życia połączenia GATT jest krytyczne dla stabilności aplikacji BLE. Po wywołaniu connectGatt() aplikacja oczekuje na callback onConnectionStateChange() ze stanem STATE_CONNECTED. Następnie system automatycznie uruchamia Service Discovery, po zakończeniu którego wywoływane jest onServicesDiscovered(). Dopiero po tym można wykonywać odczyt, zapis i subskrypcję. Po zakończeniu pracy koniecznie wywołaj gatt.close() w onDestroy() lub onPause() — niezamknięte połączenia GATT zużywają baterię i mogą blokować ponowne połączenie z tym samym urządzeniem na Android.
// Połącz z urządzeniem BLE przez GATT
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val service = gatt.getService(UUID.fromString("180D"))
val characteristic = service?.getCharacteristic(
UUID.fromString("2A37")
)
characteristic?.let { enableNotifications(gatt, it) }
}
}
Bluetooth SIG (Special Interest Group) definiuje dziesiątki standardowych profili i usług GATT ze stałymi 16-bitowymi UUID. Najczęściej spotykane: usługa Battery Service (UUID 180F, charakterystyka Battery Level 2A19), usługa Heart Rate (UUID 180D, charakterystyka Heart Rate Measurement 2A37), usługa Device Information (UUID 180A, charakterystyki Manufacturer Name, Serial Number). Używanie standardowych usług gwarantuje kompatybilność między urządzeniami różnych producentów — każda opaska fitness z usługą Heart Rate powinna działać z każdą aplikacją Android, która ją obsługuje.
Jeśli standardowe usługi nie pokrywają zadania, programista może stworzyć własne usługi GATT z 128-bitowymi UUID (niestandardowe UUID). Przy projektowaniu niestandardowej usługi należy: określić logiczną grupę charakterystyk (np. „Usługa zarządzania zamkiem”), nadać każdej charakterystyce odpowiednie właściwości (Read, Write, Notify), określić dozwolone wartości i jednostki miary przez deskryptor Characteristic Presentation Format. W przypadku złożonych protokołów zaleca się dołączenie do usługi charakterystyki Command z właściwością Write i charakterystyki Status z Notify — odpowiada to wzorcowi Command/Status przyjętemu w przemysłowych aplikacjach BLE.
// Utwórz niestandardową usługę GATT (Android jako GATT Server)
private fun createCustomService(): BluetoothGattService {
val serviceUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef0")
val service = BluetoothGattService(
serviceUuid, BluetoothGattService.SERVICE_TYPE_PRIMARY
)
val charUuid = UUID.fromString("12345678-1234-5678-1234-56789abcdef1")
val characteristic = BluetoothGattCharacteristic(
charUuid,
BluetoothGattCharacteristic.PROPERTY_READ or
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ
)
service.addCharacteristic(characteristic)
return service
}
Często zadawane pytania
GATT (Generic Attribute Profile) — to profil Bluetooth Low Energy określający strukturę wymiany danych między urządzeniami. Służy do standaryzacji dostępu do informacji: wszystkie urządzenia BLE organizują dane w usługi i charakterystyki, co pozwala każdemu klientowi odczytywać dane z czujników, sterować urządzeniami i otrzymywać powiadomienia.
GAP odpowiada za wykrywanie i łączenie urządzeń BLE (skanowanie, reklama, nawiązywanie połączenia). GATT — za wymianę danych po połączeniu (odczyt, zapis, powiadomienia). GAP działa przed połączeniem, GATT — po. Oba profile są obowiązkowe dla BLE, ale pełnią różne funkcje: GAP to „warstwa sieciowa”, GATT to „warstwa aplikacyjna”.
Standardowe usługi obejmują Battery Service (180F, poziom naładowania), Heart Rate (180D, tętno), Device Information (180A, dane urządzenia). Każda usługa zawiera kilka charakterystyk z 16-bitowymi UUID. Programiści mogą tworzyć niestandardowe usługi z 128-bitowymi UUID do specyficznych zadań — na przykład sterowania inteligentnym zamkiem lub przesyłania danych trackera fitness.
Na Android GATT jest implementowany przez BluetoothGatt (połączenie), BluetoothGattService (usługa), BluetoothGattCharacteristic (charakterystyka) i BluetoothGattDescriptor (deskryptor). Połączenie wykonuje się przez connectGatt(), po czym w callbacku BluetoothGattCallback przychodzą zdarzenia: onConnectionStateChange, onServicesDiscovered, onCharacteristicChanged (dla powiadomień). Wszystkie operacje GATT muszą być wykonywane sekwencyjnie.
UUID (Universally Unique Identifier) — 16-bitowy lub 128-bitowy identyfikator jednoznacznie określający usługę lub charakterystykę w GATT. Standardowe usługi Bluetooth SIG używają 16-bitowych UUID (np. 180D dla Heart Rate). Niestandardowe usługi programistów — 128-bitowych UUID (np. 12345678-1234-5678-1234-56789abcdef0). UUID pozwala klientowi znaleźć potrzebne dane na GATT Server.
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ż