WebBluetoothの紹介
公開: 2022-03-10プログレッシブウェブアプリにより、ウェブはこれまで以上にネイティブアプリに近づいています。 ただし、プライバシーやクロスプラットフォームの互換性など、Webに固有の追加の利点があります。
Webは伝統的に、ネットワーク上のサーバー、特にインターネット上のサーバーとの通信について素晴らしいものでした。 Webがアプリケーションに移行している今、ネイティブアプリと同じ機能も必要です。
過去数年間にブラウザに実装された新しい仕様と機能の量は驚異的です。 WebGLや今後のWebGPUなどの3Dを処理するための仕様があります。 オーディオのストリーミングと生成、ビデオの視聴、Webカメラの入力デバイスとしての使用が可能です。 WebAssemblyを使用して、ほぼネイティブの速度でコードを実行することもできます。 さらに、当初はネットワークのみのメディアでしたが、Webはサービスワーカーによるオフラインサポートに移行しました。
それは素晴らしいことですが、1つの領域は、ネイティブアプリのほぼ排他的なドメインでした。それは、デバイスとの通信です。 それは私たちが長い間解決しようとしてきた問題であり、おそらく誰もが一度は遭遇したことでしょう。 Webはサーバーとの通信には最適ですが、デバイスとの通信には最適ではありません。 たとえば、ネットワークにルーターを設定しようとすることを考えてみてください。 IPアドレスを入力し、セキュリティなしでプレーンHTTP接続を介してWebインターフェイスを使用する必要があった可能性があります。 それは悪い経験と悪いセキュリティです。 その上、正しいIPアドレスが何であるかをどうやって知るのですか?
HTTPは、デバイスと通信しようとするプログレッシブWebアプリを作成しようとしたときに最初に遭遇する問題でもあります。 PWAはHTTPSのみであり、ローカルデバイスは常にHTTPのみです。 HTTPSの証明書が必要です。証明書を取得するには、ドメイン名が付いた公開されているサーバーが必要です(ローカルネットワーク上の手の届かないデバイスについて話しています)。
そのため、多くのデバイスでは、ネイティブアプリがデバイスをセットアップして使用する必要があります。これは、ネイティブアプリがWebプラットフォームの制限に縛られず、ユーザーに快適なエクスペリエンスを提供できるためです。 ただし、そのために500MBのアプリをダウンロードしたくありません。 おそらく、お持ちのデバイスはすでに数年前のものであり、アプリが新しい電話で実行できるように更新されたことはありません。 おそらく、デスクトップまたはラップトップコンピューターを使用したいと考えており、メーカーはモバイルアプリのみを作成しました。 また、理想的な体験ではありません。
WebBluetoothは、ChromeとSamsung Internetに実装された新しい仕様であり、ブラウザからBluetooth LowEnergyデバイスと直接通信できるようにします。 プログレッシブWebアプリとWebBluetoothの組み合わせにより、Webアプリケーションのセキュリティと利便性が提供され、デバイスと直接通信することができます。
Bluetoothの名前は、範囲が限られていること、音質が悪いこと、ペアリングの問題があるため、かなり悪い名前になっています。 しかし、これらの問題のほとんどは過去のものです。 Bluetooth Low Energyは、同じ周波数スペクトルを使用することを除けば、古いBluetooth仕様とはほとんど関係のない最新の仕様です。 毎日1,000万台以上のデバイスにBluetoothサポートが付属しています。 これには、コンピューターや電話だけでなく、心拍数や血糖値モニターなどのさまざまなデバイス、電球などのIoTデバイス、リモート制御可能な車やドローンなどのおもちゃも含まれます。
推奨読書: APIベースのプラットフォームを理解する:製品マネージャーのためのガイド
退屈な理論的部分
Bluetooth自体はWebテクノロジーではないため、私たちにはなじみのない語彙を使用しています。 それでは、Bluetoothの仕組みといくつかの用語について見ていきましょう。
すべてのBluetoothデバイスは、「中央デバイス」または「周辺機器」のいずれかです。 中央デバイスのみが通信を開始でき、周辺機器とのみ通信できます。 中央デバイスの例は、コンピューターまたは携帯電話です。
周辺機器は通信を開始できず、中央デバイスとのみ通信できます。 さらに、周辺機器は同時に1つの中央デバイスとのみ通信できます。 周辺機器は別の周辺機器と通信できません。
中央デバイスは、同時に複数の周辺機器と通信でき、必要に応じてメッセージを中継できます。 そのため、心拍数モニターは電球と通信できませんでしたが、心拍数を受信し、心拍数が特定のしきい値を超えるとライトを赤くする中央デバイスで実行されるプログラムを作成できます。
WebBluetoothについて話すときは、一般属性プロファイルと呼ばれるBluetooth仕様の特定の部分について話します。これには、非常に明白な略語GATTがあります。 (どうやら、GAPはすでに取られていました。)
GATTの文脈では、中央のデバイスと周辺機器ではなく、クライアントとサーバーについて話します。 あなたの電球はサーバーです。 それは直感に反するように思えるかもしれませんが、考えてみれば実際には理にかなっています。 電球はサービス、すなわちライトを提供します。 ブラウザがインターネット上のサーバーに接続するときと同じように、電話またはコンピュータは電球のGATTサーバーに接続するクライアントです。
各サーバーは1つ以上のサービスを提供します。 これらのサービスの一部は公式には標準の一部ですが、独自に定義することもできます。 心拍数モニターの場合、仕様で定義されている公式サービスがあります。 電球の場合はありません。ほとんどすべてのメーカーが車輪の再発明を試みています。 すべてのサービスには1つ以上の特性があります。 各特性には、読み取りまたは書き込みが可能な値があります。 今のところ、それをオブジェクトの配列と考えるのが最善であり、各オブジェクトには値を持つプロパティがあります。
オブジェクトのプロパティとは異なり、サービスと特性は文字列によって識別されません。 各サービスと特性には、16ビットまたは128ビットの長さの一意のUUIDがあります。 公式には、16ビットUUIDは公式標準用に予約されていますが、ほとんど誰もその規則に従いません。 最後に、すべての値はバイトの配列です。 Bluetoothには派手なデータ型はありません。
Bluetooth電球を詳しく見る
それでは、実際のBluetoothデバイスであるMipow PlaybulbSphereを見てみましょう。 BLEScannerやnRFConnectなどのアプリを使用してデバイスに接続し、すべてのサービスと特性を確認できます。 この場合、iOS用のBLEスキャナーアプリを使用しています。
電球に接続したときに最初に表示されるのは、サービスのリストです。 デバイス情報サービスやバッテリーサービスのようないくつかの標準化されたものがあります。 ただし、カスタムサービスもいくつかあります。 特に、16ビットUUIDが0xff0f
のサービスに興味があります。 このサービスを開くと、特徴の長いリストを見ることができます。 これらの特性のほとんどはUUIDによってのみ識別され、残念ながらカスタムサービスの一部であるため、私にはわかりません。 それらは標準化されておらず、製造元はドキュメントを提供していません。
UUIDが0xfffc
の最初の特性は特に興味深いようです。 値は4バイトです。 これらのバイトの値を0x00000000
から0x00ff0000
に変更すると、電球が赤に変わります。 0x0000ff00
に変更すると、電球が緑色になり、 0x000000ff
が青色になります。 これらはRGBカラーであり、HTMLおよびCSSで使用する16進カラーに正確に対応しています。
その最初のバイトは何をしますか? さて、値を0xff000000
に変更すると、電球は白に変わります。 電球には4つの異なるLEDが含まれており、4バイトのそれぞれの値を変更することで、必要なすべての色を作成できます。
WebBluetooth API
ネイティブアプリを使用して電球の色を変更できるのは素晴らしいことですが、ブラウザーからこれを行うにはどうすればよいですか? 先ほど学習したBluetoothとGATTに関する知識があれば、WebBluetoothAPIのおかげでこれは比較的簡単であることがわかります。 電球の色を変更するには、JavaScriptを数行使用するだけです。
WebBluetoothAPIを見てみましょう。
デバイスへの接続
最初に行う必要があるのは、ブラウザーからデバイスに接続することです。 関数navigator.bluetooth.requestDevice()
を呼び出し、関数に構成オブジェクトを提供します。 そのオブジェクトには、使用するデバイスとAPIで使用できるサービスに関する情報が含まれています。
次の例では、名前にプレフィックスPLAYBULB
を含むデバイスのみを表示するため、デバイスの名前でフィルタリングしています。 また、使用するサービスとして0xff0f
を指定しています。 requestDevice()
関数はpromiseを返すので、結果を待つことができます。
let device = await navigator.bluetooth.requestDevice({ filters: [ { namePrefix: 'PLAYBULB' } ], optionalServices: [ 0xff0f ] });
この関数を呼び出すと、指定したフィルターに準拠するデバイスのリストがウィンドウに表示されます。 次に、接続するデバイスを手動で選択する必要があります。 これはセキュリティとプライバシーにとって不可欠なステップであり、ユーザーに制御を与えます。 ユーザーは、Webアプリの接続を許可するかどうか、そしてもちろん、どのデバイスへの接続を許可するかを決定します。 ユーザーが手動でデバイスを選択しないと、Webアプリはデバイスのリストを取得したり接続したりすることはできません。
デバイスにアクセスした後、デバイスのgatt
プロパティでconnect()
関数を呼び出して、GATTサーバーに接続し、結果を待つことができます。
let server = await device.gatt.connect();
サーバーを取得したら、使用するサービスのUUIDをパラメーターとしてサーバーでgetPrimaryService()
を呼び出し、結果を待つことができます。
let service = await server.getPrimaryService(0xff0f);
次に、特性のUUIDをパラメーターとしてサービスでgetCharacteristic()
を呼び出し、再び結果を待ちます。
これで、データの書き込みと読み取りに使用できる特性が得られました。
let characteristic = await service.getCharacteristic(0xfffc);
データの書き込み
データを書き込むには、バイナリデータのストレージメソッドであるArrayBufferとして書き込みたい値を使用して、特性に対して関数writeValue()
を呼び出すことができます。 通常の配列を使用できない理由は、通常の配列にはさまざまなタイプのデータが含まれている可能性があり、空の穴が存在する可能性があるためです。
ArrayBufferを直接作成または変更することはできないため、代わりに「型付き配列」を使用しています。 型付き配列のすべての要素は常に同じ型であり、穴はありません。 この例では、 Uint8Array
を使用します。これは符号なしであるため、負の数を含めることはできません。 整数なので、分数を含めることはできません。 これは8ビットで、0から255までの値のみを含めることができます。言い換えると、バイトの配列です。
characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );
この特定の電球がどのように機能するかはすでにわかっています。 LEDごとに1つずつ、合計4バイトを提供する必要があります。 各バイトの値は0〜255です。この場合、赤、緑、青のLEDのみを使用するため、値0を使用して白のLEDをオフのままにします。
データの読み取り
電球の現在の色を読み取るには、 readValue()
関数を使用して、結果を待ちます。
let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);
返される値はArrayBufferのDataViewであり、ArrayBufferからデータを取得する方法を提供します。 この例では、 getUint8()
関数をパラメーターとしてインデックスとして使用して、配列から個々のバイトを引き出すことができます。
変更の通知を受け取る
最後に、デバイスの値が変更されたときに通知を受け取る方法もあります。 これは電球にはあまり役立ちませんが、心拍数モニターでは常に値が変化するため、現在の値を1秒ごとに手動でポーリングする必要はありません。
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();
値が変更されるたびにコールバックを取得するには、パラメーターcharacteristicvaluechanged
とコールバック関数を使用して特性に対してaddEventListener()
関数を呼び出す必要があります。 値が変更されるたびに、パラメーターとしてイベントオブジェクトを使用してコールバック関数が呼び出され、イベントのターゲットのvalueプロパティからデータを取得できます。 そして最後に、ArrayBufferのDataViewから個々のバイトを再度抽出します。
Bluetoothネットワークの帯域幅は制限されているため、特性でstartNotifications()
を呼び出して、この通知メカニズムを手動で開始する必要があります。 そうしないと、ネットワークが不要なデータで溢れかえってしまいます。 さらに、これらのデバイスは通常バッテリーを使用するため、内部無線を頻繁にオンにする必要がないため、送信する必要のない1バイトごとにデバイスのバッテリー寿命が大幅に向上します。
結論
これで、WebBluetooth APIの90%を超えました。 数回の関数呼び出しと4バイトの送信で、電球の色を制御するWebアプリを作成できます。 さらに数行追加すると、おもちゃの車を操作したり、ドローンを飛ばしたりすることもできます。 ますます多くのBluetoothデバイスが市場に出回っているため、可能性は無限大です。
その他のリソース
- Bluetooth.rocks! デモ| (GitHubのソースコード)
- 「WebBluetooth仕様」、 WebBluetoothコミュニティグループ
- GATTレジストリを開くBluetoothLowEnergyデバイスの汎用属性サービスに関するドキュメントの非公式コレクション。