Komunikacja z urządzeniami Bluetooth przez JavaScript

Interfejs Web Bluetooth API umożliwia witrynom komunikowanie się z urządzeniami Bluetooth.

François Beaufort
François Beaufort

Co zrobić, jeśli strony mogą komunikować się z urządzeniami Bluetooth w pobliżu? w bezpieczny sposób z zachowaniem prywatności? Dzięki temu monitory pracy serca, śpiewają, żarówki, a nawet żółwie mogą wchodzić w bezpośrednią interakcję ze stroną internetową.

Do tej pory można było obsługiwać urządzenia Bluetooth w przypadku aplikacji działających na określonych platformach. Interfejs Web Bluetooth API ma to zmienić pojawia się również w przeglądarkach.

Zanim zaczniemy

W tym dokumencie zakładamy, że wiesz już trochę o tym, jak Bluetooth Low Energia (BLE) i ogólny profil atrybutów działają.

Chociaż specyfikacja interfejsu Web Bluetooth API nie została jeszcze opracowana, autorzy aktywnie szukają entuzjastycznych programistów, którzy chcieliby wypróbować ten interfejs API. prześlij opinię o specyfikacji i o implementacji.

Część interfejsu Web Bluetooth API jest dostępna w ChromeOS, Chrome na Androida 6.0, Mac (Chrome 56) i Windows 10 (Chrome 70). Oznacza to, że możesz być w stanie aby wysyłać żądania i łączyć się z urządzeniami Bluetooth Low Energy w pobliżu; odczyt/zapis charakterystyki Bluetooth, otrzymywanie powiadomień GATT, informacje gdy urządzenie Bluetooth zostaje odłączone, a nawet odczytuje i zapisuje dane na Deskryptory Bluetooth Więcej informacji znajdziesz w tabeli MDN na temat zgodności z przeglądarką. i informacjami o nich.

W systemie Linux i starszych wersjach systemu Windows włącz Flaga #experimental-web-platform-features w: about://flags.

Dostępne w przypadku testowania origin

Aby uzyskać jak najwięcej opinii od programistów korzystających z internetu Interfejs API Bluetooth w tej sekcji, Chrome dodał wcześniej tę funkcję do Chrome wersji 53 jako okresu próbnego origin dla ChromeOS, Androida i Maca.

Okres próbny zakończył się w styczniu 2017 roku.

Wymagania dotyczące bezpieczeństwa

Aby poznać zalety zabezpieczeń, polecam artykuł Web Bluetooth Security Post o modelu napisany przez Jeffreya Yasskina, inżyniera oprogramowania w zespole Chrome nad specyfikacją interfejsu Web Bluetooth API.

Tylko HTTPS

Ten eksperymentalny interfejs API to nowa, zaawansowana funkcja dodana do internetu, dlatego udostępniane tylko w bezpiecznych kontekstach. Oznacza to, że Pamiętaj o TLS.

Wymagany gest użytkownika

W ramach funkcji zabezpieczeń wykrywanie urządzeń Bluetooth Funkcja navigator.bluetooth.requestDevice musi być wywoływana gestem użytkownika, w ten sposób: po kliknięciu lub kliknięciu myszy. Mówimy o słuchaniu Zdarzenia pointerup, click i touchend.

button.addEventListener('pointerup', function(event) {
  // Call navigator.bluetooth.requestDevice
});

Zapoznaj się z kodem

Web Bluetooth API w znacznym stopniu opiera się na obietnicach JavaScriptu. Jeśli nie jesteś zapoznaj się z tym świetnym samouczkiem. I jeszcze jedno, () => {} to funkcje strzałek ECMAScript 2015.

Żądanie urządzeń Bluetooth

Ta wersja specyfikacji interfejsu Web Bluetooth API zezwala na witryny działające w główną rolę, czyli łączenie się ze zdalnymi serwerami GATT przez połączenie BLE. it obsługuje komunikację między urządzeniami z Bluetooth 4.0 lub nowszym.

Gdy strona prosi o dostęp do urządzeń w pobliżu za pomocą navigator.bluetooth.requestDevice, przeglądarka prosi użytkownika na urządzeniu wyboru, w którym można wybrać jedno urządzenie lub anulować prośbę.

Pytanie użytkownika urządzenia Bluetooth.

Funkcja navigator.bluetooth.requestDevice() przyjmuje obowiązkowy obiekt, który definiuje filtry. Te filtry służą tylko do wyświetlenia tylko tych urządzeń, które spełniają określone kryteria reklamowanych usług GATT Bluetooth i/lub nazwy urządzenia.

Filtr usług

Na przykład, aby zażądać urządzeń Bluetooth reklamujących GATT Bluetooth Serwis baterii:

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Jeśli Twoja usługa Bluetooth GATT nie znajduje się na liście ustandaryzowanych usług Bluetooth GATT, możesz jednak podać pełny identyfikator UUID Bluetooth Format 16- lub 32-bitowy.

navigator.bluetooth.requestDevice({
  filters: [{
    services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
  }]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Filtr nazwy

Możesz też zażądać urządzeń Bluetooth na podstawie reklamowanej nazwy urządzenia z kluczem name lub nawet prefiksem tej nazwy z kluczem namePrefix . W tym przypadku musisz też określić optionalServices, aby uzyskać dostęp do usług niezawartych w filtr usługi. Jeśli tego nie zrobisz, później podczas próby uzyskania dostępu pojawi się błąd .

navigator.bluetooth.requestDevice({
  filters: [{
    name: 'Francois robot'
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Filtr danych producenta

Można też poprosić o zezwolenie na urządzenia Bluetooth w zależności od producenta określonych danych reklamowanych za pomocą klucza manufacturerData. Ten klucz to tablica obiektów z wymaganym kluczem identyfikatora firmy Bluetooth o nazwie companyIdentifier Możesz też podać prefiks danych, który filtruje dane producenta z urządzeń Bluetooth, które od nich zaczynają. Pamiętaj, że trzeba też zdefiniować klucz optionalServices, aby uzyskać dostęp do usług nie uwzględniono w filtrze usługi. Jeśli tego nie zrobisz, później pojawi się błąd, dostęp do nich.

// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
  filters: [{
    manufacturerData: [{
      companyIdentifier: 0x00e0,
      dataPrefix: new Uint8Array([0x01, 0x02])
    }]
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Maski można też używać z prefiksem danych, aby dopasować niektóre wzorce w danych producenta. Zapoznaj się z wyjaśnieniem filtrów danych Bluetooth, aby dowiedzieć się więcej. i innych.

Filtry wykluczania

Opcja exclusionFilters w tabeli navigator.bluetooth.requestDevice() pozwala możesz wykluczyć niektóre urządzenia z selektora przeglądarki. Może służyć do wykluczania które pasują do szerszego filtra, ale nie są obsługiwane.

// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
  filters: [{
    namePrefix: "Created by"
  }],
  exclusionFilters: [{
    name: "Created by Francois"
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Brak filtrów

Na koniec, zamiast filters, możesz użyć klawisza acceptAllDevices, aby wyświetlić wszystkie urządzenia Bluetooth w pobliżu. Musisz także określić optionalServices klucz, aby mieć dostęp do niektórych usług. Jeśli tego nie zrobisz, później pojawi się błąd podczas próby uzyskania do nich dostępu.

navigator.bluetooth.requestDevice({
  acceptAllDevices: true,
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Nawiązywanie połączenia z urządzeniem Bluetooth

Co możesz zrobić, gdy masz już BluetoothDevice? Połączmy się z Zdalny serwer GATT Bluetooth, na którym jest przechowywana usługa i charakterystyka definicji.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
  // Human-readable name of the device.
  console.log(device.name);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

Odczytuj cechy Bluetooth

Tutaj łączymy się z serwerem GATT zdalnego urządzenia Bluetooth. Teraz chcesz uzyskać podstawową usługę GATT i odczytać cechę, która należy do tę usługę. Spróbujmy na przykład poznać bieżący poziom naładowania baterii urządzenia.

W przykładzie poniżej battery_level to standaryzowany poziom baterii Cecha.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
  // Getting Battery Service…
  return server.getPrimaryService('battery_service');
})
.then(service => {
  // Getting Battery Level Characteristic…
  return service.getCharacteristic('battery_level');
})
.then(characteristic => {
  // Reading Battery Level…
  return characteristic.readValue();
})
.then(value => {
  console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });

Jeśli używasz niestandardowej właściwości GATT Bluetooth, możesz podać parametr pełny identyfikator UUID Bluetooth albo krótki 16- lub 32-bitowy format service.getCharacteristic

Pamiętaj, że możesz też dodać detektor zdarzeń characteristicvaluechanged do do obsługi odczytu jej wartości. Sprawdź cechy czytania Przykładowe dane o zmianie wartości, które pokazują, jak opcjonalnie obsługiwać przyszłe GATT jak również powiadomienia.


.then(characteristic => {
  // Set up event listener for when characteristic value changes.
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleBatteryLevelChanged);
  // Reading Battery Level…
  return characteristic.readValue();
})
.catch(error => { console.error(error); });

function handleBatteryLevelChanged(event) {
  const batteryLevel = event.target.value.getUint8(0);
  console.log('Battery percentage is ' + batteryLevel);
}

Zapisz w charakterze Bluetooth

Zapisywanie danych w charakterze GATT Bluetootha jest równie łatwe jak czytanie. Tym razem użyjmy punktu kontrolnego tętna, by zresetować wartość zużytej energii na 0 na pulsie.

Obiecuję, że nie ma tu magii. Wszystko zostało wyjaśnione w sekcji Kontrola tętna Strona Cecha punktu.

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
  // Writing 1 is the signal to reset energy expended.
  const resetEnergyExpended = Uint8Array.of(1);
  return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
  console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });

Otrzymywanie powiadomień GATT

Zobaczmy teraz, jak otrzymać powiadomienie po pomiarze tętna. zmian cech charakterystycznych na urządzeniu:

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleCharacteristicValueChanged);
  console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });

function handleCharacteristicValueChanged(event) {
  const value = event.target.value;
  console.log('Received ' + value);
  // TODO: Parse Heart Rate Measurement value.
  // See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}

Przykład powiadomień pokazuje, jak wyłączyć powiadomienia z stopNotifications() i prawidłowo usuń dodany element: characteristicvaluechanged detektora zdarzeń.

Rozłączanie z urządzeniem Bluetooth

Aby zadbać o wygodę użytkowników, możesz nasłuchiwać zdarzeń rozłączenia i poproś go o ponowne połączenie:

navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
  // Set up event listener for when device gets disconnected.
  device.addEventListener('gattserverdisconnected', onDisconnected);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

function onDisconnected(event) {
  const device = event.target;
  console.log(`Device ${device.name} is disconnected.`);
}

Możesz też wywołać device.gatt.disconnect(), by odłączyć aplikację internetową od Urządzenie Bluetooth. Spowoduje to aktywowanie istniejącego zdarzenia gattserverdisconnected słuchaczom. Pamiętaj, że NIE spowoduje to przerwania komunikacji z urządzeniem Bluetooth, jeśli inna Aplikacja komunikuje się już z urządzeniem Bluetooth. Sprawdź urządzenie Odłącz próbkę i próbkę automatycznego ponownego połączenia, aby dowiedzieć się więcej.

Odczyt i zapis w deskryptorach Bluetooth

Deskryptory GATT Bluetooth to atrybuty opisujące wartość charakterystyczną. Można je odczytywać i zapisywać w podobny sposób jak w GATT Bluetooth dla niektórych cech produktu.

Przyjrzyjmy się np. temu, jak odczytać opis pomiaru podany przez użytkownika między termometrem zdrowotnym urządzenia.

W poniższym przykładzie health_thermometer to usługa termometru zdrowotnego, measurement_interval charakter interwału pomiaru oraz gatt.characteristic_user_description Opis użytkownika deskryptor.

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
  const decoder = new TextDecoder('utf-8');
  console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });

Po przeczytaniu opisu przez użytkownika interwału pomiaru termometru zdrowotnego urządzenia, zobaczmy, jak go zaktualizować i napisać własną .

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
  const encoder = new TextEncoder('utf-8');
  const userDescription = encoder.encode('Defines the time between measurements.');
  return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });

Przykłady, wersje demonstracyjne i ćwiczenia z programowania

Wszystkie poniższe przykłady Web Bluetooth zostały przetestowane. Ciesz się zalecamy zainstalowanie [BLE Peripheral Simulator] Android App Store], który symuluje urządzenie peryferyjne BLE z usługą baterii, pomiarem tętna lub serwisu termometru zdrowotnego.

Początkujący

  • Informacje o urządzeniu – pobieranie podstawowych informacji o urządzeniu z urządzenia BLE.
  • Poziom baterii – pobiera informacje o baterii z urządzenia BLE wyświetlającego informacje o baterii.
  • Resetuj energię – zresetuj energię wydaną przez urządzenie BLE wyświetlające wskaźniki tętna.
  • Właściwości charakterystyczne – wyświetlanie wszystkich właściwości określonych cech urządzenia BLE.
  • Powiadomienia – uruchamianie i zatrzymywanie powiadomień o charakterze z urządzenia BLE.
  • Odłączanie urządzenia – rozłącz urządzenie BLE i otrzymuj powiadomienie o jego rozłączeniu po połączeniu z nim.
  • Uzyskaj cechy – pozwala uzyskać wszystkie cechy reklamowanej usługi z urządzenia BLE.
  • Pobierz deskryptory – uzyskaj wszystkie cechy” deskryptory reklamowanej usługi z urządzenia BLE.
  • Filtr danych producenta – pobiera z urządzenia BLE podstawowe informacje o urządzeniu zgodne z danymi producenta.
  • Filtry wykluczania – pobieranie podstawowych informacji o urządzeniu z urządzenia BLE obejmujące podstawowe filtry wykluczania.

Łączenie kilku operacji

Sprawdź też nasze wyselekcjonowane wersje demonstracyjne Web Bluetooth i oficjalne ćwiczenia z programowania Web Bluetooth.

Biblioteki

  • web-bluetooth-utils to moduł npm, który zapewnia dodatkowe wygodne funkcje interfejs API.
  • Podkładka Web Bluetooth API jest dostępna w noble, najpopularniejszym środowisku Node.js BLE z modułu centralnego. Pozwala to na tworzenie pakietów internetowych/przeglądarek szlachetnych bez konieczności dla serwera WebSocket lub innych wtyczek.
  • angular-web-bluetooth to moduł narzędzia Angular, który wyodrębnia wszystkie potrzebne do skonfigurowania interfejsu Web Bluetooth API.

Narzędzia

  • Pierwsze kroki z Web Bluetooth to prosta aplikacja internetowa, która wygeneruje stały kod JavaScript, by zacząć interakcję z urządzeniem Bluetooth. Wpisz nazwę urządzenia, usługę, charakterystykę, określ jego właściwości oraz Wszystko gotowe.
  • Jeśli jesteś już programistą Bluetooth, Web Bluetooth Developer Studio Wtyczka wygeneruje też kod JavaScript Web Bluetooth dla Urządzenie Bluetooth.

Wskazówki

Strona Wewnętrzne Bluetooth znajdziesz w Chrome na: about://bluetooth-internals, aby przeglądać wszystko w pobliżu Urządzenia Bluetooth: stan, usługi, parametry i deskryptory.

Zrzut ekranu pokazujący stronę wewnętrzną poświęconą debugowaniu Bluetootha w Chrome
Wewnętrzna strona w Chrome służąca do debugowania urządzeń Bluetooth.

Zalecamy też zapoznanie się z oficjalnym artykułem na temat zgłaszania błędów przez Web Bluetooth. ponieważ debugowanie Bluetootha może być czasem trudne.

Co dalej?

Najpierw sprawdź stan implementacji przeglądarki i platformy, aby dowiedzieć się, które interfejsu Web Bluetooth API jest obecnie wdrażany.

Chociaż nie zostało ono jeszcze do końca gotowe, zobacz zdrapek o tym, czego można się spodziewać w niedalekiej przyszłości przyszłe:

  • Skanuję w poszukiwaniu reklam BLE w pobliżu będzie dotyczyć navigator.bluetooth.requestLEScan().
  • Nowe zdarzenie serviceadded będzie śledzić nowo wykryte usługi GATT Bluetooth a serviceremoved zdarzenie – śledzenie usuniętych. Nowy servicechanged jest wywoływane po dodaniu właściwości lub deskryptora zostało usunięte z usługi GATT Bluetooth.

Pokaż wsparcie dla interfejsu API

Czy planujesz korzystanie z interfejsu API Web Bluetooth? Twoje publiczne wsparcie pomaga zespołowi Chrome nadaje priorytet funkcjom i pokazuje innym dostawcom przeglądarek, jak ważne jest ich wsparcie.

Wyślij tweeta na adres @ChromiumDev, używając hashtagu #WebBluetooth. i daj nam znać, gdzie i jak go używasz.

Zasoby

Podziękowania

Dziękujemy Kayce Basques za przeczytanie tego artykułu. Baner powitalny: SparkFun Electronics z Boulder, USA.