11.6. Usługi i charakterystyki

Gdy GAP nawiąże otwarte połączenie między dwoma urządzeniami, warstwa znajdująca się nad nim – Generic Attribute Profile, GATT – musi nadać znaczenie bajtom przepływającym przez to połączenie. Wybór BLE jest tutaj nietypowy. Tam gdzie TCP udostępnia surowy strumień bajtów i pozostawia aplikacji wymyślenie własnego ramkowania, GATT udostępnia małą bazę danych klucz/wartość, którą jedna strona hostuje, a druga odczytuje, zapisuje lub subskrybuje.

To właśnie ta baza danych zajmuje większość czasu, który projektanci aplikacji poświęcają na BLE. To, co kamera publikuje dla telefonu, co obserwuje na zdalnym sensorze, w jaki sposób klawiatura Bluetooth informuje swojego hosta o naciśniętym klawiszu – wszystko to są wartości charakterystyk w jakiejś bazie danych GATT.

11.6.1. Dwie osie ról, a nie jedna

Częste źródło nieporozumień: peripheral / central oraz server / client to dwie niezależne osie, a nie synonimy.

  • Peripheral i central to role GAP, ustalane podczas nawiązywania połączenia. Peripheral rozgłasza się i przyjmuje połączenie; central skanuje i inicjuje połączenie. Jest to ustalane w momencie nawiązania łącza i nie zmienia się.

  • Server i client to role GATT, ustalane dla każdej operacji na charakterystyce. Server hostuje charakterystykę; client odczytuje ją, zapisuje lub subskrybuje.

Te dwie osie są przez specyfikację rozdzielone. Peripheral jest zazwyczaj serwerem (pasek do pomiaru tętna publikuje swoje odczyty), a central jest zazwyczaj klientem (telefon je odczytuje), ale BLE dopuszcza dowolną kombinację – peripheral może odkryć charakterystykę na centralu, z którym właśnie się połączył, a pojedyncze połączenie może hostować usługi po obu stronach jednocześnie.

Większość aplikacji kamerowych trzyma się konwencjonalnego parowania (peripheral + server lub central + client), więc w pozostałej części tej sekcji traktujemy je jako jedną oś, gdy opisywany jest przypadek konwencjonalny. Gdy rozróżnienie ma znaczenie, oba terminy są wypisane wprost.

11.6.2. Wewnątrz bazy danych

Baza danych GATT to drzewo. Liście niosą rzeczywiste bajty. Gałęzie grupują powiązane liście w jednostki zrozumiałe dla człowieka.

Drzewo z węzłem nadrzędnym oznaczonym "GATT database". Poniżej niego znajdują się trzy węzły Service oznaczone "Generic Access (0x1800)", "Battery (0x180F)" oraz "Environmental Sensing (0x181A)". Każda usługa Service ma podrzędne węzły Characteristic; usługa Battery ma "Battery Level (0x2A19)" z podrzędnym deskryptorem "CCCD". Usługa Environmental Sensing ma "Temperature (0x2A6E)" oraz "Humidity (0x2A6F)".

Baza danych GATT. Usługi grupują charakterystyki; charakterystyki niosą bajty aplikacji; deskryptory niosą metadane dotyczące charakterystyki.

Istnieją trzy rodzaje węzłów:

  • Usługa (service) to logiczna grupa powiązanych wartości. Bluetooth SIG publikuje standardowe definicje usług dla typowych zastosowań – Battery Service dla poziomu naładowania baterii, Environmental Sensing dla temperatury / wilgotności / ciśnienia, Heart Rate dla monitorów tętna – dzięki czemu uniwersalna aplikacja na telefonie potrafi rozpoznać usługę, której nigdy wcześniej nie widziała. Aplikacja może też swobodnie definiować własne usługi dla rzeczy, których SIG nie ustandaryzował.

  • Charakterystyka (characteristic) to jedna nazwana wartość wewnątrz usługi. Usługa Battery ma pojedynczą charakterystykę – Battery Level, jednobajtową wartość procentową. Environmental Sensing ma osobne charakterystyki dla temperatury, wilgotności, ciśnienia i tak dalej. Charakterystyka jest jednostką operacji GATT – odczytujesz charakterystykę, zapisujesz charakterystykę, subskrybujesz charakterystykę.

  • Deskryptor (descriptor) to metadane dołączone do charakterystyki. Niektóre deskryptory są ustandaryzowane – najsłynniejszym jest Client Characteristic Configuration Descriptor (CCCD), ponieważ zapis do niego jest sposobem, w jaki klient mówi serwerowi „wysyłaj mi powiadomienia dla tej charakterystyki”. Inne są definiowane przez użytkownika i niosą takie informacje jak format prezentacji czy rozszerzone właściwości.

Serwer GATT (zazwyczaj peripheral) deklaruje swoją bazę danych raz przy uruchomieniu i baza ta nie zmienia się w trakcie działania. Klient GATT (zazwyczaj central) odkrywa zawartość bazy danych po połączeniu – przechodząc po drzewie, odczytując UUID-y znalezionych usług, a następnie charakterystyk wewnątrz każdej z nich.

11.6.3. UUID-y

Każda usługa, charakterystyka i deskryptor posiada UUID (Universally Unique IDentifier), który identyfikuje jakiego rodzaju to jest obiekt. UUID-y występują w trzech szerokościach:

  • 16-bitowe. Zarezerwowane dla standardów definiowanych przez Bluetooth SIG. Battery Service to 0x180F. Battery Level (charakterystyka) to 0x2A19. Pełna lista jest publikowana na stronie przypisanych numerów Bluetooth SIG pod adresem https://www.bluetooth.com/specifications/assigned-numbers/.

  • 32-bitowe. Rzadko używany środek pomiędzy.

  • 128-bitowe. Tego używają wszyscy pozostali – producent lub aplikacja generuje takie UUID losowo i używa go dla swojej własnej usługi lub charakterystyki. Kamery definiujące własny protokół znajdują się tutaj.

Klasa bluetooth.UUID akceptuje każdą z tych trzech szerokości:

import bluetooth

BATTERY_SERVICE = bluetooth.UUID(0x180F)
CUSTOM_SERVICE = bluetooth.UUID("12345678-1234-5678-9abc-def012345678")

16-bitowy UUID koduje się do małego ładunku rozgłoszeniowego, co jest jednym z powodów, dla których standardowe usługi są preferowane, gdy taka istnieje – pasek do pomiaru tętna, który rozgłasza 0x180D (Heart Rate), kosztuje dwa bajty; własny UUID kosztuje szesnaście. Dla aplikacji, które nie wymagają standardowej interoperacyjności, wygenerowany 128-bitowy UUID jest właściwą odpowiedzią.

11.6.4. Co dają usługi ustandaryzowane przez SIG

Argument za używaniem standardowej usługi jest prosty: istniejące aplikacje już wiedzą, jak się z nią komunikować. Urządzenie, które rozgłasza usługę Heart Rate (0x180D) i udostępnia charakterystykę Heart Rate Measurement (0x2A37), działa z każdą aplikacją fitness na świecie bez konieczności pisania nowego kodu. Urządzenie, które ponownie implementuje te same dane z własnymi UUID-ami, potrzebuje własnej aplikacji towarzyszącej i własnej dokumentacji protokołu.

Standardy mają jednak swoją cenę. Układy bajtów wewnątrz każdej charakterystyki są określone – SIG zdecydował, że Heart Rate Measurement to jednobajtowe pole flag, po którym następuje 8-bitowa lub 16-bitowa wartość tętna, opcjonalnie a po niej interwały R-R – i zgodne urządzenie musi przestrzegać tych układów. Usługi własne są wolne od tego ograniczenia.

Pragmatyczna odpowiedź dla kamer: użyj standardowej usługi, gdy taka istnieje dla rodzaju posiadanych danych (Battery Service, Environmental Sensing), a dla wszystkiego, co specyficzne dla Twojej aplikacji, zdefiniuj własną usługę ze 128-bitowym UUID.

11.6.5. Obiekty po stronie serwera i po stronie klienta

Dla tych samych koncepcyjnych elementów składowych (usługa, charakterystyka, deskryptor) każda biblioteka GATT udostępnia dwa równoległe zestawy obiektów: