11.6. Сервіси та характеристики#
Після того як GAP встановив відкрите з’єднання між двома пристроями, шар над ним – Generic Attribute Profile, GATT – має надати значення байтам, що передаються через це з’єднання. Вибір BLE тут незвичний. Там де TCP надає необроблений потік байтів і залишає застосунку вигадувати власне кадрування, GATT надає невелику базу даних ключ/значення, яку одна сторона розміщує, а інша читає, записує або підписується на неї.
Саме ця база даних займає більшу частину часу розробників BLE-застосунків. Що камера публікує на телефон, що вона відстежує на віддаленому датчику, як Bluetooth-клавіатура повідомляє хосту про натиснуту клавішу – все це значення характеристик у певній базі даних GATT.
11.6.1. Два виміри ролей, а не один#
Поширене джерело плутанини: peripheral / central і server / client – це два незалежні виміри, а не синоніми.
Peripheral і central – це ролі GAP, що встановлюються під час з’єднання. Peripheral анонсує себе і приймає підключення; central сканує і ініціює з’єднання. Це визначається в момент встановлення зв’язку і не змінюється.
Server і client – це ролі GATT, що встановлюються для кожної операції з характеристикою. Server розміщує характеристику; client читає, записує або підписується на неї.
Два виміри роз’єднані специфікацією. Peripheral є зазвичай server (монітор серцевого ритму публікує свої показники), а central є зазвичай client (телефон їх читає), але BLE допускає будь-яку комбінацію – peripheral може виявити характеристику на central, до якого щойно підключився, або одне з’єднання може розміщувати сервіси з обох боків одночасно.
Більшість застосунків для камер дотримуються стандартного поєднання (peripheral + server або central + client), тому решта цього розділу розглядає їх як один вимір, коли йдеться про стандартний випадок. Коли різниця важлива, обидва терміни вказуються явно.
11.6.2. Всередині бази даних#
База даних GATT – це дерево. Листки містять власне байти. Гілки групують пов’язані листки у логічно зрозумілі одиниці.
База даних GATT. Сервіси групують характеристики; характеристики несуть байти застосунку; дескриптори несуть метадані про характеристику.#
Є три види вузлів:
Service – логічна група пов’язаних значень. Bluetooth SIG публікує стандартні визначення сервісів для поширених випадків використання – Battery Service для рівня заряду, Environmental Sensing для температури / вологості / тиску, Heart Rate для моніторів серцевого ритму – тому звичайний застосунок на телефоні може розпізнати сервіс, якого він ніколи раніше не бачив. Застосунок також може визначати власні сервіси для речей, які SIG не стандартизував.
Characteristic – одне іменоване значення всередині сервісу. Battery service має одну характеристику – Battery Level, однобайтовий відсоток. Environmental Sensing має окремі характеристики для температури, вологості, тиску тощо. Характеристика є одиницею операцій GATT – ви читаєте характеристику, записуєте характеристику, підписуєтеся на характеристику.
Descriptor – метадані, прикріплені до характеристики. Деякі дескриптори стандартизовані – Client Characteristic Configuration Descriptor (CCCD) є найвідомішим, оскільки запис до нього – це спосіб, яким client повідомляє server «надсилайте мені сповіщення про цю характеристику». Інші визначаються користувачем і містять такі речі, як формат представлення або розширені властивості.
GATT server (зазвичай peripheral) оголошує свою базу даних один раз під час запуску, і вона не змінюється під час роботи. GATT client (зазвичай central) виявляє вміст бази даних після підключення – обходячи дерево, читаючи UUID сервісів, а потім характеристики всередині кожного.
11.6.3. UUID#
Кожен сервіс, характеристика та дескриптор має UUID (Universally Unique IDentifier), що ідентифікує що це таке за видом. UUID бувають трьох розмірів:
16-бітні. Зарезервовані для стандартів, визначених Bluetooth SIG. Battery Service –
0x180F. Battery Level (характеристика) –0x2A19. Повний список опублікований на сайті assigned-numbers Bluetooth SIG за адресою https://www.bluetooth.com/specifications/assigned-numbers/.32-бітні. Рідко використовуваний проміжний варіант.
128-бітні. Те, що використовують усі інші – постачальник або застосунок генерує один випадково і використовує для свого власного сервісу або характеристики. Камери, що визначають власний протокол, знаходяться тут.
Клас bluetooth.UUID приймає будь-який з трьох розмірів:
import bluetooth
BATTERY_SERVICE = bluetooth.UUID(0x180F)
CUSTOM_SERVICE = bluetooth.UUID("12345678-1234-5678-9abc-def012345678")
16-бітний UUID кодується в невеликий рекламний пакет, що є однією з причин переваги стандартних сервісів, коли такий існує – монітор серцевого ритму, що анонсує 0x180D (Heart Rate), коштує два байти; власний UUID коштує шістнадцять. Для застосунків, яким не потрібна стандартна сумісність, згенерований 128-бітний UUID є правильною відповіддю.
11.6.4. Що дають стандартизовані SIG сервіси#
Аргумент на користь використання стандартного сервісу простий: існуючі застосунки вже знають, як з ним спілкуватися. Пристрій, що анонсує Heart Rate service (0x180D) і надає характеристику Heart Rate Measurement (0x2A37), працює з кожним фітнес-застосунком на планеті без написання нового коду. Пристрій, що повторно реалізує ті самі дані з власними UUID, потребує власного застосунку-компаньйона та власного документа протоколу.
Стандарти мають свою ціну. Розкладка байтів всередині кожної характеристики визначена – SIG вирішив, що Heart Rate Measurement – це однобайтове поле прапорів, за яким іде або 8-бітне, або 16-бітне значення серцевого ритму, після якого необов’язково йдуть R-R інтервали – і відповідний пристрій повинен дотримуватися цього розкладання. Власні сервіси вільні від цього обмеження.
Практична відповідь для камер: використовуйте стандартний сервіс, коли він існує для типу даних, які ви маєте (Battery Service, Environmental Sensing), і визначайте власний з 128-бітним UUID для всього, що специфічно для вашого застосунку.
11.6.5. Об’єкти на стороні server та client#
Для однакових концептуальних будівельних блоків (service, characteristic, descriptor) кожна бібліотека GATT надає два паралельних набори об’єктів:
Об’єкти на стороні server – те, що peripheral оголошує, що він розміщує. У
aioble:aioble.Service,aioble.Characteristic,aioble.Descriptor. Вони конструюються до початку реклами і реєструються за допомогоюaioble.register_services().Об’єкти на стороні client – те, що central виявляє на peer після підключення. У
aioble:aioble.ClientService,aioble.ClientCharacteristic,aioble.ClientDescriptor. Вони обходяться черезaioble.DeviceConnection.service()/services()після підключення.