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.

A tree with a top node labelled "GATT database". Below it, three Service nodes labelled "Generic Access (0x1800)", "Battery (0x180F)", and "Environmental Sensing (0x181A)". Each Service has child Characteristic nodes; the Battery service has "Battery Level (0x2A19)" with a child Descriptor "CCCD". The Environmental Sensing service has "Temperature (0x2A6E)" and "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: