GATT (Generic Attribute Profile) — профіль Bluetooth Low Energy (BLE), що визначає структуру даних і правила обміну інформацією між BLE-пристроями. GATT побудований поверх Attribute Protocol (ATT) і організовує дані в ієрархію: сервіси (services), характеристики (characteristics) та дескриптори (descriptors). За даними Bluetooth SIG (2025), GATT-профіль використовується в 98% усіх BLE-застосунків — від фітнес-трекерів до розумних замків і медичних датчиків.
Головне
GATT (Generic Attribute Profile) — це профіль Bluetooth Low Energy (BLE), який визначає, як два BLE-пристрої обмінюються даними через Attribute Protocol (ATT). GATT стандартизує структуру даних: усі дані організовані в ієрархію «сервіс → характеристика → дескриптор». Профіль був представлений у специфікації Bluetooth 4.0 (2010) разом із BLE і з того часу залишається основним механізмом передачі даних для енергоефективних Bluetooth-пристроїв. На відміну від класичного Bluetooth, де дані передаються через послідовний порт (SPP), GATT надає структурований доступ до даних через операції читання, запису та сповіщень.
BLE-стек складається з кількох рівнів: Physical Layer (радіо), Link Layer (керування з'єднанням), L2CAP (логічна канальна передача), ATT (Attribute Protocol — доступ до атрибутів) і GATT (профіль на основі ATT). GATT — найвищий рівень, з яким працює розробник застосунку. Нижчий ATT надає базові операції: читання, запис, сповіщення та індикацію атрибутів. GATT додає семантику: визначає, що таке сервіс, характеристика, як вони групуються та які правила діють при їх читанні та записі. За даними Bluetooth Core Specification 5.4 (2023), GATT підтримує до 65 535 атрибутів (сервісів + характеристик + дескрипторів) на одному пристрої.
GATT-ієрархія складається з трьох рівнів. Сервіс (Service) — логічна група характеристик, що вирішує одне завдання (наприклад, «Сервіс батареї» або «Сервіс Heart Rate»). Характеристика (Characteristic) — це одиниця даних з відомим типом: поточний рівень заряду, покази датчика, стан перемикача. Кожна характеристика має значення (value) та один або кілька дескрипторів (Descriptors), що описують метадані: одиниці вимірювання, налаштування сповіщень, діапазон значень. UUID (Universally Unique Identifier) однозначно ідентифікує кожен сервіс і кожну характеристику.
GATT підтримує чотири типи операцій для взаємодії з характеристиками. Read — клієнт запитує поточне значення характеристики у сервера. Write — клієнт надсилає нове значення на сервер. Notify — сервер надсилає значення клієнту без підтвердження (швидший, але менш надійний спосіб). Indicate — сервер надсилає значення з підтвердженням отримання (надійніше, але повільніше). Розробник обирає тип залежно від сценарію: для показів датчика пульсу достатньо Notify, для запису конфігурації розумного замка — Write з підтвердженням.
// Структура даних GATT у коді Android
data class BleService(
val uuid: UUID,
val characteristics: List<BleCharacteristic>
)
data class BleCharacteristic(
val uuid: UUID,
val properties: Int, // READ, WRITE, NOTIFY, INDICATE
val descriptors: List<BleDescriptor>,
var value: ByteArray?
)
У специфікації BLE визначено два основні профілі: GAP (Generic Access Profile) і GATT (Generic Attribute Profile). GAP відповідає за виявлення пристроїв, встановлення з'єднання та керування видимістю — це «мережевий рівень» BLE. GATT — за обмін даними після встановлення з'єднання, тобто «прикладний рівень». Розробник використовує GAP для сканування та підключення до пристрою, а GATT — для читання, запису та отримання сповіщень від підключеного пристрою.
| Характеристика | GAP | GATT |
|---|---|---|
| Призначення | Виявлення та підключення | Обмін даними |
| Ролі | Central / Peripheral | Client / Server |
| Протокол | HCI, Link Layer | ATT (Attribute Protocol) |
| Етап | До підключення | Після підключення |
| Основний клас | BluetoothAdapter | BluetoothGatt |
GATT визначає дві ролі: GATT Server і GATT Client. Сервер — пристрій, який надає дані (наприклад, фітнес-трекер, що передає покази пульсу). Клієнт — пристрій, який запитує дані (наприклад, смартфон, що читає покази). У більшості сценаріїв Android-застосунок виступає GATT Client, а BLE-периферія — GATT Server. Однак Android також може бути GATT Server — наприклад, якщо застосунок емулює BLE-пристрій для інших пристроїв. Роль визначається на етапі встановлення GATT-з'єднання і не змінюється протягом сесії.
Процес GATT-комунікації починається після встановлення BLE-з'єднання між пристроями. Спочатку GATT Client виявляє сервіси на GATT Server через операцію Service Discovery — Android виконує її автоматично при підключенні через BluetoothGatt.discoverServices(). Після виявлення клієнт отримує список доступних сервісів, характеристик і дескрипторів. Потім клієнт може читати значення характеристик (Read), записувати нові значення (Write) або підписуватися на сповіщення (Set Notify/Indicate). GATT Server може надсилати Notify/Indicate клієнту в будь-який момент після встановлення з'єднання.
За замовчуванням розмір одного GATT-пакета (MTU, Maximum Transmission Unit) у BLE становить 23 байти, з яких 3 байти — заголовок ATT, і 20 байтів — корисні дані. Якщо застосунку потрібно передати більше даних (наприклад, OTA-оновлення прошивки), можна запросити збільшення MTU до 517 байтів через requestMtu() в Android. Збільшений MTU скорочує кількість пакетів для передачі одного блоку даних — замість 50 маленьких пакетів можна відправити 2 великих, що знижує енергоспоживання та прискорює передачу. Максимальний MTU залежить від версії BLE та можливостей чипа — Bluetooth 5.0 підтримує до 517 байтів, старіші версії — до 247 байтів.
// Підписатися на сповіщення характеристик
fun enableNotifications(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, true)
// Увімкнути 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)
}
}
Для отримання сповіщень від GATT Server у Android необхідно реалізувати колбек BluetoothGattCallback.onCharacteristicChanged(). У цьому методі застосунок отримує оновлене значення характеристики кожного разу, коли сервер надсилає Notify або Indicate. Для обробки даних у реальному часі використовуйте буфер і корутини Kotlin — це дозволить не блокувати UI-потік при інтенсивних сповіщеннях (наприклад, 100+ показів пульсу за секунду). При індикаціях (Indicate) обов'язково викликайте gatt.sendResponse() на стороні сервера — в Android-клієнті це обробляється автоматично.
На Android робота з GATT реалізується через класи пакета android.bluetooth: BluetoothGatt (з'єднання), BluetoothGattService (сервіс), BluetoothGattCharacteristic (характеристика) та BluetoothGattDescriptor (дескриптор). Підключення до BLE-пристрою починається з виклику BluetoothDevice.connectGatt() — цей метод повертає BluetoothGatt, через який виконуються всі подальші операції. Всі GATT-колбеки приходять у BluetoothGattCallback — це асинхронний інтерфейс, що викликається в тому ж потоці, де був створений BluetoothGatt. Важливо: всі операції GATT повинні виконуватися послідовно — одночасний виклик кількох операцій на одному BluetoothGatt призводить до помилок.
Правильне керування життєвим циклом GATT-з'єднання критичне для стабільності BLE-застосунку. Після виклику connectGatt() застосунок очікує колбек onConnectionStateChange() зі станом STATE_CONNECTED. Потім система автоматично запускає Service Discovery, після завершення якого викликається onServicesDiscovered(). Тільки після цього можна виконувати читання, запис і підписку. При завершенні роботи обов'язково викличте gatt.close() в onDestroy() або onPause() — незакриті GATT-з'єднання споживають батарею та можуть блокувати повторне підключення до того ж пристрою на Android.
// Підключитися до BLE-пристрою через 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) визначає десятки стандартних GATT-профілів і сервісів із фіксованими 16-бітними UUID. Найпоширеніші: сервіс Battery Service (UUID 180F, характеристика Battery Level 2A19), сервіс Heart Rate (UUID 180D, характеристика Heart Rate Measurement 2A37), сервіс Device Information (UUID 180A, характеристики Manufacturer Name, Serial Number). Використання стандартних сервісів гарантує сумісність між пристроями різних виробників — будь-який фітнес-браслет із сервісом Heart Rate повинен працювати з будь-яким Android-застосунком, який його підтримує.
Якщо стандартні сервіси не покривають завдання, розробник може створити власні GATT-сервіси з 128-бітними UUID (кастомні UUID). При проєктуванні кастомного сервісу потрібно: визначити логічну групу характеристик (наприклад, «Сервіс керування замком»), задати кожній характеристиці правильні властивості (Read, Write, Notify), визначити допустимі значення та одиниці вимірювання через дескриптор Characteristic Presentation Format. Для складних протоколів рекомендується включати в сервіс характеристику Command із Write-властивістю та характеристику Status із Notify — це відповідає шаблону Command/Status, прийнятому в промислових BLE-застосунках.
// Створити кастомний GATT-сервіс (Android як 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
}
Часті запитання
GATT (Generic Attribute Profile) — це профіль Bluetooth Low Energy, що визначає структуру обміну даними між пристроями. Він потрібен для стандартизації доступу до інформації: всі BLE-пристрої організовують дані в сервіси та характеристики, що дозволяє будь-якому клієнту читати покази датчиків, керувати пристроями та отримувати сповіщення.
GAP відповідає за виявлення та підключення BLE-пристроїв (сканування, реклама, встановлення з'єднання). GATT — за обмін даними після підключення (читання, запис, сповіщення). GAP працює до з'єднання, GATT — після. Обидва профілі обов'язкові для BLE, але виконують різні функції: GAP — «мережевий рівень», GATT — «прикладний».
Стандартні сервіси включають Battery Service (180F, рівень заряду), Heart Rate (180D, пульс), Device Information (180A, дані пристрою). Кожен сервіс містить кілька характеристик із 16-бітними UUID. Розробники можуть створювати кастомні сервіси з 128-бітними UUID для специфічних завдань — наприклад, керування розумним замком або передачі даних фітнес-трекера.
На Android GATT реалізується через BluetoothGatt (з'єднання), BluetoothGattService (сервіс), BluetoothGattCharacteristic (характеристика) та BluetoothGattDescriptor (дескриптор). Підключення виконується через connectGatt(), після чого в колбеці BluetoothGattCallback приходять події: onConnectionStateChange, onServicesDiscovered, onCharacteristicChanged (для сповіщень). Всі GATT-операції повинні виконуватися послідовно.
UUID (Universally Unique Identifier) — 16-бітний або 128-бітний ідентифікатор, що однозначно визначає сервіс або характеристику в GATT. Стандартні сервіси Bluetooth SIG використовують 16-бітні UUID (наприклад, 180D для Heart Rate). Кастомні сервіси розробників — 128-бітні UUID (наприклад, 12345678-1234-5678-1234-56789abcdef0). UUID дозволяє клієнту знайти потрібні дані на GATT Server.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також