BLE pod mikroskopem (ATTy GATTy…)

BLE pod mikroskopem (ATT i GATT...)

BLE pod mikroskopem (ATTy GATTy…)

Część 1, przeglądowa

Minęło już sporo czasu od momentu, gdy wydano pierwszą specyfikację Bluetooth 4.0. I chociaż temat BLE jest bardzo interesujący, wciąż odstrasza wielu programistów z powodu swojej złożoności. W moich wcześniejszych artykułach omawiałem głównie najniższy poziom Link Layer i Physical Layer. To pozwoliło uniknąć tak skomplikowanych i zamotanych pojęć jak protokół atrybutów (ATT) i ogólny profil atrybutów (GATT). Jednak nie ma ucieczki — nie rozumiejąc ich, nie można opracować kompatybilnych urządzeń. Dziś chciałbym podzielić się z Wami tą wiedzą. W swoim artykule będę opierał się na podręcznik dla początkujących z witryny Nordic-a. A więc, zaczynajmy.

Dlaczego wszystko jest tak skomplikowane?

Moim zdaniem, od początku było jasne, że zarządzanie urządzeniami za pomocą smartfonów to bardzo obiecujący i długoterminowy temat. Dlatego postanowiono go od razu uporządkować i z maksymalnym naciskiem. Aby producenci różnych gadżetów nie wymyślali swoich protokołów, które będą później niekompatybilne. Stąd bierze się złożoność. Już na pierwszym etapie w protokół BLE próbowano wcisnąć wszystko, co tylko było możliwe. I nie ważne, czy to się przyda później, czy nie. Co więcej, przewidziano możliwość rozszerzenia listy urządzeń na przyszłość.

Spójrzmy na obrazek, na którym przedstawiona jest schemat protokołu BLE. Składa się on z kilku warstw. Najniższa, warstwa fizyczna (PHY) odpowiada za kanał radiowy urządzenia. Link Layer (LL) zawiera całą sekwencję bajtów w przesyłanej wiadomości. W poprzednich artykułach właśnie to badaliśmy. Host Controller Interface (HCI) – to protokół wymiany między warstwami lub układami BLE, jeśli kontroler i host są realizowane na różnych chipach. Za formowanie pakietów, dzielenie na ramki, kontrolę błędów i zbieranie pakietów odpowiada Logical Link Control and Adaptation Protocol (L2CAP). Za szyfrowanie pakietów odpowiada Security Manager Protocol (SMP). Profil ogólnego dostępu (GAP) odpowiada za początkową wymianę danych między urządzeniami, aby określić „Kto jest kim”. Do niego odnoszą się również skanowanie i reklamowanie. W tym artykule skupię się na dwóch pozostałych częściach protokołu — GATT i ATT. GATT jest nadbudową nad ATT, dlatego są one ze sobą ściśle powiązane.

BLE pod mikroskopem (ATT i GATT...)

Aby uprościć narrację, chciałbym posłużyć się analogią. Gdzieś o niej słyszałem i chciałbym ją wspierać. Wyobraź sobie urządzenie BLE jako regał z książkami z kilkoma półkami. Każda półka to osobna tematyka. Na przykład, mamy półki z fantastyką, matematyką, encyklopediami. Na każdej półce stoją książki na dany temat. A w niektórych książkach są nawet papierowe zakładki z notatkami. Ponadto, mamy niewielki papierowy katalog wszystkich książek. Jeśli pamiętacie szkolne biblioteki — to był wąski szuflad z papierowymi kartami. W tej analogii regał to profil naszego urządzenia. Półki to usługi, książki to cechy, a katalog to tabela atrybutów. Zakładki w książkach to deskryptory, o których również opowiem później, bardziej szczegółowo.

Wszyscy, którzy tworzyli urządzenia, wiedzą, że w wielu projektach są podobne fragmenty kodu. Chodzi o to, że wiele urządzeń ma zbliżoną funkcjonalność. Na przykład, jeśli urządzenia działają na akumulatorach, to problem ładowania i monitorowania ich poziomu będzie taki sam. To samo dotyczy czujników. Tak naprawdę, podejście obiektowe w programowaniu „umożliwia tworzenie obiektów, które łączą właściwości i zachowania w samodzielny związek, który następnie można wielokrotnie używać”. Moim zdaniem w BLE podjęto próbę podobnego podejścia. Grupa Bluetooth Special Interest Group (SIG) opracowała profile. Urządzenia różnych producentów, mające te same profile, powinny bez trudu współpracować ze sobą. Profile składają się z usług, a usługi z cech, uzupełnianych deskryptorami. W ogólnym przypadku może to wyglądać tak:

BLE pod mikroskopem (ATT i GATT...)

Na przykład, rozważmy schemat profilu monitora pracy serca (fitness bracelet). Składa się on z dwóch usług i kilku cech. Od razu widać hierarchię profilu. Cecha kontrolna zeruje całkowity licznik wydatków kalorycznych na zero.

1. Usługa rytmu serca zawiera trzy cechy (0x180D):
    a) Obowiązkowa cecha częstotliwości skurczów serca (0x2A37)
    b) Opcjonalna cecha położenia czujnika ciała (0x2A38)
    c) Warunkowa cecha kontrolnej punktu rytmu serca (0x2A39)
2. Usługa zarządzania baterią (0x180F):
    a) Wymagana właściwość poziomu naładowania baterii (0x2A19)

UUID

Aby można było jednoznacznie odnosić się do elementów profilu (usług, właściwości i deskryptorów), należy je jakoś ponumerować. W tym celu wprowadza się pojęcie Universally Unique ID (UUID) lub Uniwersalnego unikalnego identyfikatora. W nawiasach każdej linii właśnie wskazano UUID. I tu jest jedna cecha. Dla UUID zdecydowano się na zastosowanie kodu o długości 16 i 128 bitów. Czemu, zapytacie? W protokole BLE wszystko podporządkowane jest oszczędzaniu energii. Dlatego rozmiar w 16 bitach jest zupełnie uzasadniony. Mało prawdopodobne, aby w najbliższej przyszłości powstało więcej niż 65 tys. unikalnych usług i właściwości. Na ten moment wszystko, co mogli, już policzyli (pamiętacie skąd to — „on i was policzył” :-)) Ponumerowane elementy profilów, usług, właściwości i deskryptorów możecie Państwo zobaczyć w linkach.

Jednak, myślę, że wszyscy pamiętają historię z 4 bajtami adresu IP w internecie. Najpierw myśleli, że wystarczy, a teraz nie mogą przejść na 6-bajtowy adres. Aby nie powtórzyć tego błędu i dać pole do popisu rękom majsterkowiczów, SIG zdecydowało się od razu wprowadzić również 128-bitowe UUID. To osobiście przypomina mi nielegalny zakres 433MHz, który został oddany w ręce różnych Kuliabinów od radia. W naszym przypadku oddano 128-bitowy identyfikator usług i właściwości. Oznacza to, że dla naszych usług i urządzeń możemy używać praktycznie dowolnej wartości 128-bitowej. I tak nadal prawdopodobieństwo wymyślenia takiego samego UUID dąży do zera.

W rzeczywistości krótkie 16-bitowe UUID mają swoje rozszerzenie do wartości 128-bitowej. W specyfikacji to rozszerzenie nosi nazwę Bluetooth Base UUID i ma wartość 00000000-0000-1000-8000-00805F9B34FB. Jeśli na przykład 16-bitowy UUID atrybutu ma wartość 0x1234, to odpowiadający mu 128-bitowy UUID ma wartość 00001234-0000-1000-8000-00805F9B34FB. A nawet podano odpowiednią formułę:

                                128_bit_value = 16_bit_value * 2^96 + Bluetooth_Base_UUID

Skąd wzięła się ta magiczna liczba, nie wiem. Jeśli ktoś z czytelników wie — niech napisze w komentarzach (Użytkownik o nicku Sinopteek już to zrobił. Zobacz komentarze). Co do wymyślania 128-bitowych UUID, to w zasadzie można skorzystać ze specjalnego generatora, który zrobi to za Ciebie.

ATTy GATTy…

Zaraz zacznie się to, co najciekawsze. Przypomnę, że ATT oparty jest na relacji klient-serwer. Teraz rozważamy urządzenie serwera. Zawiera ono informacje, takie jak wartości czujników, stan przełącznika światła, dane dotyczące lokalizacji itd. Teraz, gdy wszyscy „uczestnicy naszej parady” są ponumerowani, musimy je jakoś umieścić w pamięci urządzenia. W tym celu umieszczamy je w tabeli, która nazywa się tabelą atrybutów. Zapamiętaj to dobrze. To jest serce BLE. To właśnie to będziemy analizować dalej. Teraz każdą linię będziemy nazywać atrybutem. Ta tabela znajduje się głęboko w stosie i zazwyczaj nie mamy do niej bezpośredniego dostępu. Inicjujemy ją i do niej się odwołujemy, ale co dzieje się wewnątrz, pozostaje dla nas tajemnicą.

Przyjrzyjmy się obrazkowi ze specyfikacji, ale przed tym chciałbym zwrócić uwagę na częste zamieszanie terminologiczne, a konkretnie w odniesieniu do deskryptorów. Rola deskryptora polega na uzupełnieniu opisu charakterystyki. Gdy trzeba rozszerzyć jego możliwości, wtedy stosuje się deskryptory. Są również atrybutami i obok usług i charakterystyk znajdują się w tabeli atrybutów. Dokładnie omówimy je w drugiej części artykułu. Jednak czasami deskryptorami nazywają numer linii w tabeli atrybutów. Należy to mieć na uwadze. Aby uniknąć zamieszania, będziemy używać terminu „wskaźnik atrybutu” w tym kontekście.
BLE pod mikroskopem (ATT i GATT...)

Zatem atrybut — to dyskretna wartość, która ma następujące właściwości związane z nią:
1. Wskaźnik atrybutu (Attribute Handle) — to indeks w tabeli odpowiadający atrybutowi.
2. Typ atrybutu (Attribute Type) — to UUID, który opisuje jego typ.
3. Wartość atrybutu (Attribute Value) — to dane, indeksowane przez wskaźnik atrybutu.
4. Uprawnienia atrybutów (Attribute Permissions) — to część atrybutu, uprawnienia, które nie mogą być odczytywane ani zapisywane za pomocą protokołu atrybutów.

Jak to wszystko rozumieć? Wskaźnik atrybutu — to, że tak powiem, jego numer w naszej tabeli.
Umożliwia to klientowi odwoływanie się do atrybutu w zapytaniach odczytu lub zapisu. Możemy numerować nasze wiersze (atrybuty) od 0x0001 do 0xFFFF. W naszym skojarzeniu z biblioteczką — to numer karty w papierowym katalogu. Podobnie jak w katalogu biblioteki, karty są uporządkowane rosnąco według numeru. Numer każdego kolejnego wiersza musi być większy od poprzedniego. Jak w bibliotece, czasami znikają niektóre karty, tak i u nas — w numeracji wierszy mogą występować luki. Jest to dopuszczalne. Najważniejsze, aby były uporządkowane rosnąco.

Typ atrybutu określa, co ten atrybut reprezentuje. Podobnie jak w języku C,
gdzie istnieją zmienne boolowskie, liczbowe i ciągi, tak i tutaj. Dzięki typowi atrybutu dowiadujemy się,
z czym mamy do czynienia i jak musimy dalej pracować z tym atrybutem. Poniżej omówimy niektóre specyficzne typy atrybutów. Na przykład „deklaracja usługi” (0x2800), „deklaracja cechy” (0x2803), „deklaracja deskryptora” (0x2902).

Wartość atrybutu to właściwie jego wartość, przepraszam za tautologię. Jeśli typ atrybutu to ciąg, to wartością atrybutu może być na przykład slogan „Hello World !!!”. Jeśli typ atrybutu to „deklaracja usługi”, to jego wartością jest sama usługa. A czasami to informacje o tym, gdzie znaleźć inne atrybuty i ich właściwości.

Uprawnienia atrybutów pozwalają serwerowi zrozumieć, czy dostęp do odczytu lub zapisu jest dozwolony.
Zwróć uwagę, że te uprawnienia stosują się tylko do wartości atrybutu, a nie do wskaźnika, typu i samego pola uprawnień. Tzn. jeśli zapis atrybutu jest dozwolony, możemy na przykład zmienić ciąg „Hello World !!!” na ciąg „Good morning”. Ale nie możemy zabronić zapisu nowego wiersza ani zmienić typu atrybutu i oznaczyć ciąg jako „deklaracja usługi”. Kiedy klient zwraca się do serwera, klient żąda jego atrybutów. Umożliwia to klientowi dowiedzenie się, co może zaoferować serwer. Chociaż nie jest konieczne odczytywanie i zapisywanie wartości.

Jak to wygląda

Koncepcja GATT polega na zgrupowaniu atrybutów w tabeli atrybutów w bardzo specyficzny i logiczny sposób. Przejrzyjmy dokładniej profil częstości akcji serca przedstawiony poniżej. Najlewsza kolumna tej tabeli jest opcjonalna. Opisuje, czym jest ten wiersz (atrybut). Wszystkie pozostałe kolumny są nam już znane.

BLE pod mikroskopem (ATT i GATT...)

Na górze każdej grupy zawsze znajduje się atrybut ogłoszenia usługi. Jego typ zawsze wynosi 0x2800, a wskaźnik zależy od liczby atrybutów już obecnych w tabeli. Jego uprawnienia są zawsze dostępne tylko do odczytu, bez żadnej autoryzacji czy uwierzytelnienia. O tych pojęciach porozmawiamy później. Wartość — to kolejny UUID, który określa, co to za usługa. W tabeli wartość wynosi 0x180D, co jest zdefiniowane przez Bluetooth SIG jako usługa częstości akcji serca.

Po ogłoszeniu usługi następuje ogłoszenie cechy. Pod względem formy przypomina ogłoszenie usługi. Jego UUID zawsze wynosi 0x2803, a uprawnienia są zawsze dostępne tylko do odczytu bez żadnej autoryzacji czy uwierzytelnienia. Przyjrzyjmy się polu Value Atrybutu, które zawiera niektóre dane. Zawsze zawiera wskaźnik, UUID i zestaw właściwości. Te trzy elementy opisują kolejne ogłoszenie wartości cechy. Wskaźnik naturalnie oznacza miejsce ogłoszenia wartości cechy w tabeli atrybutów. UUID opisuje, jaki typ informacji lub wartości możemy się spodziewać. Na przykład wartość temperatury, stan przełącznika światła lub inna dowolna wartość. I w końcu właściwości, które opisują, jak można wchodzić w interakcję z wartością cechy.

Czeka nas tu kolejna pułapka. Dotyczy to uprawnień atrybutów i właściwości cech. Przyjrzyjmy się obrazkowi z właściwościami bitowego pola w specyfikacji.

BLE pod mikroskopem (ATT i GATT...)

Jak widać, tutaj również znajdują się pola umożliwiające odczyt i zapis. Możesz zadać sobie pytanie, dlaczego mamy uprawnienia do odczytu/zapisu dla atrybutu i właściwości.
Czytanie/zapisywanie wartości cechy? Czy nie powinny one zawsze być takie same? Rzecz w tym, że właściwości dla wartości cechy są w rzeczywistości jedynie zaleceniami dla klienta, stosowanymi w GATT oraz w warstwach aplikacyjnych. To po prostu wskazówki dotyczące tego, czego klient może oczekiwać od atrybutu ogłoszenia cechy. Zajmijmy się tym bardziej szczegółowo. Jakie rodzaje uprawnień mają atrybuty?

1. Uprawnienia dostępu:
     — do odczytu
     — do zapisu
     — do odczytu i zapisu
2. Uprawnienia uwierzytelniania:
     — wymagane uwierzytelnienie
     — niewymagane uwierzytelnienie
3. Uprawnienia autoryzacji:
     — wymagana autoryzacja
     — niewymagana autoryzacja

Główna różnica między uprawnieniami atrybutów a właściwościami cech polega na tym, że pierwsze dotyczą serwerów, a drugie klientów. Serwer może mieć dozwolony odczyt wartości cechy, ale może także wymagać uwierzytelnienia lub autoryzacji. Dlatego, gdy klient prosi o właściwości cechy, uzyskuje informacje, że odczyt jest dozwolony. Jednak gdy spróbuje dokonać odczytu, otrzyma błąd. Można więc śmiało powiedzieć, że uprawnienia mają priorytet nad właściwościami. Nie możemy jednak z perspektywy klienta zdobyć informacji o tym, jakie uprawnienia ma dany atrybut.

Descriptor

Wróćmy do naszej tabeli. Po ogłoszeniu wartości cechy możliwe są następujące ogłoszenia atrybutów:
1. Nowe ogłoszenie cechy (w usłudze może być wiele cech)
2. Nowa deklaracja usługi (może ich być wiele w tabeli)
3. Ogłoszenie deskryptora

W przypadku charakterystyki pomiaru tętna w naszej tabeli ogłoszenie wartości charakterystyki towarzyszy ogłoszeniu deskryptora. Deskryptor to atrybut z dodatkowymi informacjami o charakterystyce. Istnieje kilka rodzajów deskryptorów. O nich szczegółowo porozmawiamy w drugiej części tego artykułu. Teraz skupimy się tylko na deskryptorze konfiguracji charakterystyk klienta (Client Characteristic Configuration Descriptor — CCCD). Ma on UUID równy 0x2902. Dzięki temu deskryptorowi klient ma możliwość włączenia na serwerze wskazania lub powiadomienia. Różnica między nimi jest niewielka, ale jednak istnieje. Powiadomienie nie wymaga potwierdzenia odbioru ze strony klienta. Wskazanie natomiast to wymaga, chociaż dzieje się to na poziomie GATT, nie docierając do poziomu aplikacji. Po co tak, zapytacie? Niestety, nie wiem. Mogę tylko powiedzieć, że specjaliści z Nordic-a zalecają użycie powiadomienia. Tym bardziej, że sprawdzenie integralności pakietu (przy użyciu CRC) odbywa się w obu przypadkach.

Podsumowanie

Na końcu artykułu chciałbym powiedzieć o jednej rzeczy. Ostatnia tabela jest nieco myląca. Zatrzymałem się przy niej, ponieważ jest podana w artykuł, na którą się opieram. W drugiej części mojego artykułu zamierzam zgłębić specyfikację Bluetooth 4.0. Czekają nas tam dokładniejsze schematy i rysunki. W trzeciej części chciałbym przeanalizować log uzyskany za pomocą programu Wireshark z jednego z gadżetów i zobaczyć „na żywo” całą tę teorię, którą wspólnie studiujemy.

Pracownik Grupy Firm "Cezar Satellite"
Włodzimierz Piecherski

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster