Wprowadzenie do WebBluetooth
Opublikowany: 2022-03-10Dzięki progresywnym aplikacjom internetowym internet coraz bardziej zbliża się do aplikacji natywnych. Jednak z dodatkowymi korzyściami, które są nieodłącznie związane z siecią, takimi jak prywatność i kompatybilność między platformami.
Sieć była tradycyjnie fantastyczna w rozmowach z serwerami w sieci, a konkretnie z serwerami w Internecie. Teraz, gdy internet zmierza w kierunku aplikacji, potrzebujemy również tych samych możliwości, jakie mają aplikacje natywne.
Ilość nowych specyfikacji i funkcji, które zostały wdrożone w ciągu ostatnich kilku lat w przeglądarkach, jest oszałamiająca. Mamy specyfikacje dotyczące obsługi 3D, takich jak WebGL i nadchodzące WebGPU. Możemy przesyłać strumieniowo i generować dźwięk, oglądać filmy i używać kamery internetowej jako urządzenia wejściowego. Możemy również uruchamiać kod z niemal natywną prędkością za pomocą WebAssembly. Co więcej, mimo że początkowo był tylko medium sieciowym, sieć przeszła w kierunku wsparcia offline z pracownikami usług.
To świetnie i wszystko, ale jeden obszar był niemal wyłączną domeną aplikacji natywnych: komunikowanie się z urządzeniami. To jest problem, który staramy się rozwiązać od dłuższego czasu i jest to coś, z czym prawdopodobnie każdy się spotkał w pewnym momencie. Internet doskonale nadaje się do komunikowania się z serwerami, ale nie do komunikowania się z urządzeniami . Pomyśl na przykład o próbie skonfigurowania routera w swojej sieci. Możliwe, że trzeba było wprowadzić adres IP i korzystać z interfejsu internetowego przez zwykłe połączenie HTTP bez żadnych zabezpieczeń. To tylko kiepskie doświadczenie i złe zabezpieczenia. Co więcej, skąd wiesz, jaki jest właściwy adres IP?
HTTP to także pierwszy problem, na jaki napotykamy, gdy próbujemy utworzyć progresywną aplikację internetową, która próbuje komunikować się z urządzeniem. PWA obsługują tylko protokół HTTPS, a urządzenia lokalne zawsze obsługują tylko protokół HTTP. Potrzebujesz certyfikatu dla HTTPS, a żeby uzyskać certyfikat, potrzebujesz publicznie dostępnego serwera z nazwą domeny (mówię o urządzeniach w naszej sieci lokalnej, która jest poza zasięgiem).
Tak więc w przypadku wielu urządzeń potrzebujesz natywnych aplikacji, aby skonfigurować urządzenia i korzystać z nich, ponieważ natywne aplikacje nie są ograniczone ograniczeniami platformy internetowej i mogą oferować przyjemne wrażenia dla użytkowników. Nie chcę jednak pobierać w tym celu aplikacji o pojemności 500 MB. Być może urządzenie, które masz, ma już kilka lat, a aplikacja nigdy nie została zaktualizowana, aby działała na nowym telefonie. Być może chcesz skorzystać z komputera stacjonarnego lub laptopa, a producent zbudował tylko aplikację mobilną. Również nie jest to idealne doświadczenie.
WebBluetooth to nowa specyfikacja, która została zaimplementowana w Chrome i Samsung Internet, która pozwala nam komunikować się bezpośrednio z urządzeniami Bluetooth Low Energy z poziomu przeglądarki. Progresywne aplikacje internetowe w połączeniu z WebBluetooth zapewniają bezpieczeństwo i wygodę aplikacji internetowych z możliwością bezpośredniego komunikowania się z urządzeniami.
Bluetooth ma dość złą nazwę ze względu na ograniczony zasięg, złą jakość dźwięku i problemy z parowaniem. Ale prawie wszystkie te problemy to już przeszłość. Bluetooth Low Energy to nowoczesna specyfikacja, która ma niewiele wspólnego ze starymi specyfikacjami Bluetooth , poza wykorzystaniem tego samego widma częstotliwości. Każdego dnia ponad 10 milionów urządzeń jest dostarczanych z obsługą Bluetooth. Obejmuje to komputery i telefony, ale także różne urządzenia, takie jak monitory tętna i glukozy, urządzenia IoT, takie jak żarówki i zabawki, takie jak zdalnie sterowane samochody i drony.
Zalecana literatura : Zrozumienie platform opartych na interfejsach API: przewodnik dla menedżerów produktów
Nudna część teoretyczna
Ponieważ sam Bluetooth nie jest technologią sieciową, używa pewnego słownictwa, które może wydawać się nam nieznane. Przyjrzyjmy się więc, jak działa Bluetooth i trochę terminologii.
Każde urządzenie Bluetooth jest albo „urządzeniem centralnym” albo „urządzeniem peryferyjnym”. Tylko urządzenia centralne mogą inicjować komunikację i mogą komunikować się tylko z urządzeniami peryferyjnymi. Przykładem urządzenia centralnego może być komputer lub telefon komórkowy.
Urządzenie peryferyjne nie może zainicjować komunikacji i może komunikować się tylko z urządzeniem centralnym. Co więcej, urządzenie peryferyjne może jednocześnie komunikować się tylko z jednym urządzeniem centralnym. Urządzenie peryferyjne nie może komunikować się z innym urządzeniem.
Centralne urządzenie może rozmawiać z wieloma urządzeniami peryferyjnymi w tym samym czasie i może przekazywać wiadomości, jeśli chce. Tak więc czujnik tętna nie może komunikować się z żarówkami, jednak możesz napisać program, który działa na centralnym urządzeniu, które odbiera tętno i włącza czerwone światła, jeśli tętno przekroczy określony próg.
Kiedy mówimy o WebBluetooth, mówimy o określonej części specyfikacji Bluetooth zwanej Generic Attribute Profile, która ma bardzo oczywisty skrót GATT. (Najwyraźniej GAP został już zajęty.)
W kontekście GATT nie mówimy już o urządzeniach centralnych i peryferiach, ale o klientach i serwerach. Twoje żarówki to serwery. Może się to wydawać sprzeczne z intuicją, ale w rzeczywistości ma to sens, jeśli się nad tym zastanowisz. Żarówka oferuje usługę, czyli światło. Podobnie jak wtedy, gdy przeglądarka łączy się z serwerem w Internecie, telefon lub komputer jest klientem, który łączy się z serwerem GATT w żarówce.
Każdy serwer oferuje jedną lub więcej usług. Niektóre z tych usług są oficjalnie częścią standardu, ale możesz też zdefiniować własne. W przypadku pulsometru w specyfikacji zdefiniowany jest oficjalny serwis. W przypadku żarówki nie ma i prawie każdy producent stara się wymyślać koło na nowo. Każda usługa ma jedną lub więcej cech. Każda cecha ma wartość, którą można odczytać lub zapisać. Na razie najlepiej byłoby myśleć o tym jako o tablicy obiektów, przy czym każdy obiekt ma właściwości, które mają wartości.
W przeciwieństwie do właściwości obiektów usługi i cechy nie są identyfikowane za pomocą ciągu znaków. Każda usługa i cecha posiada unikalny UUID, który może mieć długość 16 lub 128 bitów. Oficjalnie 16-bitowy UUID jest zarezerwowany dla oficjalnych standardów, ale prawie nikt nie przestrzega tej zasady. Wreszcie każda wartość jest tablicą bajtów. W Bluetooth nie ma wymyślnych typów danych.
Bliższe spojrzenie na żarówkę Bluetooth
Spójrzmy więc na rzeczywiste urządzenie Bluetooth: Mipow Playbulb Sphere. Możesz użyć aplikacji, takiej jak BLE Scanner lub nRF Connect, aby połączyć się z urządzeniem i zobaczyć wszystkie usługi i cechy. W tym przypadku korzystam z aplikacji BLE Scanner na iOS.
Pierwszą rzeczą, którą widzisz po podłączeniu do żarówki, jest lista usług. Istnieje kilka ustandaryzowanych, takich jak serwis informacji o urządzeniu i serwis baterii. Ale są też usługi niestandardowe. Szczególnie interesuje mnie usługa z 16-bitowym UUID 0xff0f
. Jeśli otworzysz tę usługę, zobaczysz długą listę cech. Nie mam pojęcia, co robi większość tych cech, ponieważ są one identyfikowane tylko przez UUID i ponieważ są niestety częścią niestandardowej usługi; nie są znormalizowane, a producent nie dostarczył żadnej dokumentacji.
Szczególnie interesująca wydaje się pierwsza cecha z UUID 0xfffc
. Ma wartość czterech bajtów. Jeśli zmienimy wartość tych bajtów z 0x00000000
na 0x00ff0000
, żarówka zmieni kolor na czerwony. Zmiana na 0x0000ff00
zmienia żarówkę na zieloną, a 0x000000ff
na niebieską. Są to kolory RGB i dokładnie odpowiadają kolorom szesnastkowym, których używamy w HTML i CSS.
Co robi ten pierwszy bajt? Cóż, jeśli zmienimy wartość na 0xff000000
, żarówka zmieni kolor na biały. Żarówka zawiera cztery różne diody LED, a zmieniając wartość każdego z czterech bajtów, możemy stworzyć każdy kolor, jaki chcemy.
Interfejs API WebBluetooth
To fantastyczne, że możemy użyć natywnej aplikacji do zmiany koloru żarówki, ale jak to zrobić z przeglądarki? Okazuje się, że przy wiedzy o Bluetooth i GATT, którą właśnie poznaliśmy, jest to stosunkowo proste dzięki API WebBluetooth. Wystarczy kilka linijek kodu JavaScript, aby zmienić kolor żarówki.
Przejdźmy do interfejsu API WebBluetooth.
Podłączanie do urządzenia
Pierwszą rzeczą, którą musimy zrobić, to połączyć się z przeglądarki z urządzeniem. Wywołujemy funkcję navigator.bluetooth.requestDevice()
i dostarczamy jej obiekt konfiguracyjny. Obiekt ten zawiera informacje o tym, z jakiego urządzenia chcemy korzystać i jakie usługi powinny być dostępne dla naszego API.
W poniższym przykładzie filtrujemy według nazwy urządzenia, ponieważ chcemy widzieć tylko urządzenia zawierające w nazwie prefiks PLAYBULB
. Określamy również 0xff0f
jako usługę, z której chcemy korzystać. Ponieważ funkcja requestDevice()
zwraca obietnicę, możemy czekać na wynik.
let device = await navigator.bluetooth.requestDevice({ filters: [ { namePrefix: 'PLAYBULB' } ], optionalServices: [ 0xff0f ] });
Kiedy wywołujemy tę funkcję, pojawia się okno z listą urządzeń, które są zgodne z określonymi przez nas filtrami. Teraz musimy ręcznie wybrać urządzenie, z którym chcemy się połączyć. Jest to niezbędny krok w kierunku bezpieczeństwa i prywatności oraz daje kontrolę użytkownikowi. Użytkownik decyduje, czy aplikacja internetowa może się połączyć i oczywiście z jakim urządzeniem może się połączyć. Aplikacja internetowa nie może uzyskać listy urządzeń ani połączyć się bez ręcznego wybrania urządzenia przez użytkownika.
Po uzyskaniu dostępu do urządzenia możemy połączyć się z serwerem GATT, wywołując funkcję connect()
we właściwości gatt
urządzenia i czekać na wynik.
let server = await device.gatt.connect();
Gdy już mamy serwer, możemy wywołać getPrimaryService()
na serwerze z identyfikatorem UUID usługi, której chcemy użyć jako parametru i czekać na wynik.
let service = await server.getPrimaryService(0xff0f);
Następnie wywołaj getCharacteristic()
usługi z identyfikatorem UUID charakterystyki jako parametrem i ponownie poczekaj na wynik.
Mamy teraz nasze cechy, które możemy wykorzystać do zapisu i odczytu danych:
let characteristic = await service.getCharacteristic(0xfffc);
Zapisywanie danych
Aby zapisać dane, możemy wywołać funkcję writeValue()
na charakterystyce z wartością, którą chcemy zapisać jako ArrayBuffer, która jest metodą przechowywania danych binarnych. Powodem, dla którego nie możemy użyć zwykłej tablicy, jest to, że zwykłe tablice mogą zawierać dane różnych typów, a nawet mogą mieć puste dziury.
Ponieważ nie możemy bezpośrednio tworzyć ani modyfikować ArrayBuffer, zamiast tego używamy „tablicy wpisywanej”. Każdy element tablicy wpisywanej jest zawsze tego samego typu i nie zawiera żadnych dziur. W naszym przypadku użyjemy Uint8Array
, która nie ma znaku, więc nie może zawierać żadnych liczb ujemnych; liczba całkowita, więc nie może zawierać ułamków; i ma 8 bitów i może zawierać tylko wartości od 0 do 255. Innymi słowy: tablica bajtów.
characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );
Wiemy już, jak działa ta konkretna żarówka. Musimy dostarczyć cztery bajty, po jednym dla każdej diody LED. Każdy bajt ma wartość od 0 do 255, a w tym przypadku chcemy używać tylko czerwonej, zielonej i niebieskiej diody LED, więc zostawiamy białą diodę LED wyłączoną, używając wartości 0.
Czytanie danych
Aby odczytać aktualny kolor żarówki, możemy użyć funkcji readValue()
i poczekać na wynik.
let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);
Wartość, którą otrzymujemy, to DataView z ArrayBuffer i oferuje sposób na pobranie danych z ArrayBuffer. W naszym przypadku możemy użyć funkcji getUint8()
z indeksem jako parametrem do wyciągnięcia poszczególnych bajtów z tablicy.
Otrzymywanie powiadomień o zmianach
Wreszcie istnieje również sposób na otrzymanie powiadomienia o zmianie wartości urządzenia. To nie jest przydatne w przypadku żarówki, ale w przypadku naszego czujnika tętna mamy ciągle zmieniające się wartości i nie chcemy ręcznie sprawdzać bieżącej wartości co sekundę.
characteristic.addEventListener( 'characteristicvaluechanged', e => { let r = e.target.value.getUint8(1); let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); } ); characteristic.startNotifications();
Aby otrzymać wywołanie zwrotne za każdym razem, gdy zmienia się wartość, musimy wywołać funkcję addEventListener()
na charakterystyce z parametrem characteristicvaluechanged
rystykavaluechanged i funkcją zwrotną. Za każdym razem, gdy wartość się zmieni, funkcja wywołania zwrotnego zostanie wywołana z obiektem zdarzenia jako parametrem i możemy pobrać dane z właściwości value obiektu docelowego zdarzenia. I na koniec ponownie wyodrębnij poszczególne bajty z DataView ArrayBuffer.
Ponieważ przepustowość w sieci Bluetooth jest ograniczona, musimy ręcznie uruchomić ten mechanizm powiadomień, wywołując startNotifications()
na charakterystyce. W przeciwnym razie sieć zostanie zalana niepotrzebnymi danymi. Co więcej, ponieważ urządzenia te zazwyczaj wykorzystują baterię, każdy bajt, którego nie musimy wysyłać, definitywnie wydłuży żywotność baterii urządzenia, ponieważ wewnętrzne radio nie musi być włączane tak często.
Wniosek
Przeszliśmy już ponad 90% interfejsu API WebBluetooth. Wystarczy kilka wywołań funkcji i wysłanie 4 bajtów, aby stworzyć aplikację internetową, która steruje kolorami żarówek. Jeśli dodasz jeszcze kilka linii, możesz nawet sterować samochodzikiem lub latać dronem. Wraz z coraz większą liczbą urządzeń Bluetooth pojawiających się na rynku możliwości są nieograniczone.
Dalsze zasoby
- Bluetooth.skały! Prezentacje | (Kod źródłowy na GitHub)
- „Specyfikacja sieciowa Bluetooth”, grupa społeczności sieciowej Bluetooth
- Open GATT Registry Nieoficjalny zbiór dokumentacji usług Generic Attribute dla urządzeń Bluetooth Low Energy.