Subterranean Flower

WebBluetoothでブラウザ上からBluetoothLE通信

Author
古都こと
ふよんとえんよぷよぐやま!!!

WebGL、WebAudio、WebRTC…など「Web○○」系のAPIのおかげで様々なことがブラウザ上でできるようになってきましたが、そこにWebBluetoothのおかげでBluetoothも加わることになりそうです。

仕様

WebBluetooth APIを使用することで、ブラウザ上でBluetoothLEを用いた通信が可能になります。WebBluetoothで扱えるのはBluetoothLEのみで、名前に反してBluetoothは使用できません。セキュリティのため、WebBluetoothには以下の制限があります。

  • https上でのみ動作
  • 通信するにはユーザジェスチャー(クリックやタップなど)が必要

また、現在のところCentralとしてのみの動作になり、Peripheralとしての動作は未策定です。詳しくは以下の仕様書をご覧ください。

実装状況

現在WebBluetoothを実装しているのはChromeのみで、Firefoxは実装途中、EdgeはUnder Considerationとなっています。また、Chromeに関してもデフォルトで有効化されるのはChrome56以降で、それまではchrome://flagsでフラグを有効化する必要があります。

同じChromeでもプラットフォームによっても実装状況は異なります。詳しくは以下のリンクをご覧ください。

Peripheralシミュレータ

テストのためのPeripheral機器を用意するのは簡単ではありません。そこでスマートデバイス向けのシミュレータが用意されています。

必要に応じて使用してください。

API

検出

BluetoothLEデバイスの検出にはnavigator.bluetooth.requestDeviceメソッドを使用します。requestDeviceメソッドはPromise<BluetoothObject>を返します。

navigator.bluetooth.requestDevice({filters: [{services: ['battery_service']}]})
    .catch(error => console.log(error));

このメソッドはフィルタ条件を引数にとり、条件に一致したデバイスをChooserUIで表示します。ChooserはBLEデバイスを選択する画面のことで、実装ごとに表示は異なります。

セキュリティの都合上、requestDeviceメソッドの呼び出しには何かしらのユーザジェスチャーが必要です。「通信開始」ボタンなどを用意するといいでしょう。

フィルタの条件を空白にすることはできません。フィルタには以下の3種類があります。

  • services:サービス名の文字列か、16進数のUUID。
  • name:デバイス名文字列
  • namePrefix:デバイス名文字列(前方一致)

主に使用するのはservicesでしょう。{services: [‘battery_service’]}のようにサービス名を指定するか、{services: [0x180F]}のようにUUIDを指定します。BLEで使用できるサービス名とUUIDの一覧は以下のリンクをご覧ください。

また、フィルタを指定せずに、すべてのデバイスを検出することもできます。その場合はacceptAllDevicesをtrueに設定します。

navigator.bluetooth.requestDevice({ acceptAllDevices:true })
    .catch(error => console.log(error));

フィルタとacceptAllDevicesを併用することはできません。

接続

Promiseを介して受け取ったBluetoothDeviceオブジェクトのgatt.connectメソッドを使用することでGATTサーバに接続できます。

navigator.bluetooth.requestDevice({filters: [{services: ['battery_service']}]})
    .then(device => device.gatt.connect())
    .catch(error => console.log(error));

gatt.connectメソッドはPromise<BluetoothRemoteGATTServer>を返します。

サービスの検出

BluetoothRemoteGATTServerオブジェクトのgetPrimaryServiceメソッドを実行することでサービスが検出できます。

navigator.bluetooth.requestDevice({filters: [{services: ['battery_service']}]})
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('battery_service'))
    .catch(error => console.log(error));

getPrimaryServiceメソッドはPromise<BluetoothRemoteGATTService>を返します。ここでもサービス名またはUUIDを指定します。また、複数形であるgetPrimaryServicesメソッドを使用すれば複数のサービスを得ることができます。

キャラクタリスティックの取得

BluetoothRemoteGATTServiceオブジェクトのgetCharacteristicメソッドを使用することでキャラクタリスティックを取得できます。getCharacteristicsメソッドを使用すれば複数のキャラクタリスティックが取得できます。

navigator.bluetooth.requestDevice({filters: [{services: ['battery_service']}]})
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('battery_service'))
    .then(service => service.getCharacteristic('battery_level'))
    .catch(error => console.log(error));

getCharacteristicメソッドはPromise<BluetoothRemoteGATTCharacteristic>を返します。引数にはキャラクタリスティック名かUUIDを指定します。

キャラクタリスティックの値を読み込む

BluetoothRemoteGATTCharacteristicオブジェクトのreadValueメソッドを実行することで値を読み込むことができます。readValueメソッドはPromise<DataView>を返します。

navigator.bluetooth.requestDevice({filters: [{services: ['battery_service']}]})
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('battery_service'))
    .then(service => service.getCharacteristic('battery_level'))
    .then(characteristic => characteristic.readValue())
    .then(value => {
      let batteryLevel = value.getUint8(0);
      console.log(batteryLevel);
    });
    .catch(error => console.log(error));

キャラクタリスティックに値を書き込む

値を書き込む場合は、writeValueメソッドを使用します。writeValueが取るのはArrayBufferオブジェクトです。

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 => {
      let resetEnergyExpended = new Uint8Array([1]);
      return characteristic.writeValue(resetEnergyExpended);
    });
    .catch(error => console.log(error));

接続の解除

接続の解除にはBluetoothDeviceオブジェクトのgatt.disconnectメソッドを使用します。

device.gatt.disconnect()

動作サンプル

以下のURLに様々な動作サンプルが用意されています。