Web BluetoothとWebXRでImmersionしたくない？

みなさん、immersionという言葉をご存知ですか？
言葉そのものの意味は「没入」です。
バーチャルリアリティ(VR)では、VRヘッドセットをかぶることによって、視覚聴覚でバーチャルな空間に自分が本当にいるように感じます。しかしヘッドセットだけがバーチャルリアリティではありません。視覚聴覚以外の触覚や嗅覚も駆使することでよりバーチャル空間に没入することができます。Web Bluetoothが普及していくことにより新たに触覚や嗅覚のデバイスを連携させやすくなり、さらにバーチャル空間に没入できるのです。

そんなさらなるimmersionに繋がるWeb BluetoothとWebXRの話をしていきます。


DMM.com 19新卒のありかです。
「ユーザーに最高の体験を、ワクワクを、届けるためには何をすべきか？」をメンバーの皆さんと日頃から考えて毎日を過ごしています。

昨年はDMMにてマイニングラボとブロックチェーン研究室でインターンをしていました。
今年からは動画配信事業部に所属しており、複数あるVR動画プレイヤーの一つに関して機能開発プロジェクトの開発と進行を兼任しています。

さて、DMM アドベントカレンダー2019のDay5では、自分が最近趣味で調べているWeb Bluetoothのお話をしたいと思います。気になる点等あればコメントいただければ幸いです。


#Web Bluetooth APIとは
Bluetooth4.0を用いて、各種デバイスの検索や通信するためのAPIです。
Web APIの一種であり、Javascriptを用いてブラウザ上から扱うことができ便利です。
対応ブラウザは、PCに関してはほぼChromeのみです。
androidでも使用できますが、iosでは未対応です。
https://caniuse.com/#search=webbluetooth

Web Bluetoothに関してさらに知りたい方はこちらをご参照ください。
https://webbluetoothcg.github.io/web-bluetooth/
(日本語版はこちら https://tkybpp.github.io/web-bluetooth-jp/)

#本当にしたかったこと
Facebook社が提供する、Oculus Questというスタンドアロンでかつ極めて高品質のVR体験を味わえるVRデバイスがあります。
https://www.oculus.com/quest/
![C8CD9786-56DF-413E-83C3-F8783A00022E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/df55763c-0a15-548f-c840-6554209b62e1.jpeg)
左がOculus Quest、右がOculusGoです。

通常、高品質なVR体験を味わおうとすると、10万円以上するようなハイエンドPCに加えて、PCとケーブルで接続されたVRデバイスを使用する必要があります。しかし、Oculus QuestはPCが不必要なためケーブルがなく、非常に取り回しが良い上に、本当にそこに新たな世界があるかのような高品質なVR体験が可能です。
そんなOculus Questの標準ブラウザであるOculus Browser上で、Web Bluetooth APIを用いて好きなBluetoothデバイスと簡単な通信をしようとしました。そして、そのBluetoothデバイスをWebXRで実装したVR空間内のオブジェクトに合わせて操作しようとしました。

機能要件としては、下記を想定していました。
- Oculus Quest1台に対して、基本的に1台
- Oculus Questから10m以内での使用に限る
- 遅延に対しては数百ミリ sec程度の遅延は許す
- 通信で送受信するデータ量は数百kbps程度

というのも個人的に最近買ったおもちゃをワクワクするサービスと合わせて動かしてみたかったからです。※検証中に壊してしまいました😇最後におまけを書くので経緯が気になる方はご覧ください。


これは本当に個人的な話ですが、最高の体験のためには視聴覚的表現と触覚的表現の2つを掛け合わせることが非常に大事だと感じています。今後のさらなる発展に期待です。![最高の体験とは何か.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/5ef55aa1-8d57-262e-ff92-51777423880d.jpeg)



#実際にできたこと
Oculus Browserと同様にChromiumをベースとしているChromeを対象に、Windows10とMacOSでWeb bluetooth APIを使用し、後述するスマホアプリ上で作成した仮想Bluetoothデバイスと通信を試して、データの入力と読み取りができました。

なぜOculus QuestでしなかったかというとOculus Browserには今のところWeb Bluetooth APIが対応してなかったからです。また、以前はOculus Browser上で動いていたWebXRも動かなくなっていました。そのためOculus Questはいったん断念してPCのブラウザ上からWeb Bluetooth APIを試してみます。

###Web Bluetooth API
https://webbluetoothcg.github.io/web-bluetooth/
Web Bluetooth APIが使用できるかどうかは下記のコードで確認できます。
(とても悲しい。。。OculusBrowserのdeveloperの皆さん頑張って！)

```javascript
// undefinedが返ってきたら使用できない
// Bluetoothオブジェクト返ってきたら使用できる
navigator.bluetooth

```

###WebXR API (WebXR Device API)
https://www.w3.org/TR/webxr/
WebXRのAPIが使用できるかどうかは下記のコードで確認できます。

```javascript
// undefinedが返ってきたら使用できない
// XRオブジェクト返ってきたら使用できる
navigator.xr
```

9月ごろには下記の様にfunctionをボタン等にイベントとして付与するとXRモードに移行していたのですが、変更があったようです。以前記事にまとめたのですが、同様の方法をOculus Browserで試してもできませんでした。。。https://qiita.com/arika_nashika/items/e194ede7ed89a7e591a7 (OculusBrowserのdeveloperの皆さん！頼みます！)

```javascript

function onRequestSession(){
    navigator.xr.requestSession("immersive-vr").then((session) => {
    // ここにはVRデバイス情報を保持しているSessionを用いて、
    // レンダリング処理などを実装する
    });
}
```


netlifyを使ってWebBluetoothとWebXRの対応状況を確認できるhttps環境を作成したので、自分の環境が使用できる環境か確認したい方は是非ご確認ください。

- https://wakuwaku.co.jp

(co.jpドメインを取得したのですが、しばらく法人格を取得できそうにないので時期によってはリンクが切れてしまうかもしれませんごめんなさい。)


#環境
- Chrome v78(現在のstable)
- LightBlue® Explorer
    - iOS, androidアプリ(自分はiphoneユーザーなのでiosで試しました)
    - Bluetooth Low Energyを使用しているデバイスと通信できる
    - これを使用して、仮想的にBluetoothデバイスを作成し、Chromeと通信します
    - https://apps.apple.com/jp/app/lightblue-explorer/id557428110


#さあ仮想的なBluetoothデバイスを作成しよう！
LightBlue® Explorerを使用して、PCと通信するためにスマホ上に仮想的なBluetoothデバイスを作成します。仮想的なBluetoothデバイスを作成しなくてもProfile情報等が分かるデバイスをお持ちの方をそちらをご利用ください。

##近くにあるデバイス(Peripherals)を一覧

ここに表示されているデバイスが現在近くに存在するbluetoothデバイスです。自分の場合多くはUnnamedとなっていますが、Oculus Goを持っているためそれも識別されています。左側の電波強度のアイコンと共に表示されている数字はdB単位のものであり、値が0に近づくほど強い信号があることがわかります。大体近ければ-40とかの値になります。
<img width="375" alt="IMG_6729.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/ec6b42cb-2d2e-1ace-d3bd-6827ec7f786b.png">

##仮想的なBluetoothデバイス一覧
ここには今までにアプリで作成した仮想的なBluetoothデバイスが一覧されています。ここでは"Running Speed and Cadence"というランニング強度の情報を取得するデバイスが作成されていることがわかります。

右上の+ボタンを押すことで新たな仮想デバイスが作成できます。
<img width="375" alt="IMG_6730.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/96264eb2-906e-89ec-692c-e93de7092e49.png">

##作成できる仮想的なBluetoothデバイス一覧
Web Bluetoothに関する先人たちの記事ではHeart Rateデバイスが多く紹介されているので、ここでは、ランニング強度を測るためのRunning Speed and Cadenceというデバイスを使用します。
<img width="375" alt="IMG_6731.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/eb115162-102d-7d3f-af53-4bfae6d83d37.png">

#次はChromeから仮想的なBluetoothデバイスと通信しよう！

Web Bluetooth接続のためには下記の2つのUUIDが必要です。
- ServiceのUUID
- CharacteristicのUUID

これらの情報はBluetoothの公式ページを参照して取得します。
https://www.bluetooth.com/specifications/gatt/services/

今回はRunning Speed and Cadenceというデバイスを作成したので
上記のBluetooth公式ページの中からRunning Speed and CadenceというServiceを選択し、そこで得られるXMLの中のServiceとCharacteristicを示すタグから欲しい情報を取得します。
https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.running_speed_and_cadence.xml

###ServiceのUUID
具体的には下記に示すServiceタグの属性に含まれるuuidの部分に注目します。

```xml

<Service xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schemas.bluetooth.org/Documents/service.xsd" name="Running Speed and Cadence" type="org.bluetooth.service.running_speed_and_cadence" uuid="1814" last-modified="2012-3-26" approved="Yes">
```
このuuidが示す値がServiceのUUIDです。
また、typeが示す文字列とuuidは対応しているため、typeを用いても大丈夫です。
org.bluetooth.service.running_speed_and_cadenceの中からserviceより後の部分を使用します。ここがserviceのuuidと対応する文字列です。

###CharacteristicのUUID
Characteristicは1つのServiceに対して1個以上存在するため、ここでは速度に関する情報を通知してくれるCharacteristicを選び、そのタグをServiceの時と同じ様に調べます。

```xml
<Characteristic name="RSC Measurement" type="org.bluetooth.characteristic.rsc_measurement">
```
今度はuuidはないけれども、typeの文字列はuuidの代わりに使用できるので
org.bluetooth.characteristic.rsc_measurementの中からcharacteristicより後の
部分を使用します。ここがcharacteristicのuuidと対応する文字列です。

ここまでで、serviceのUUIDとcharacteristicのUUIDが取得できました。

```javascript
SERVICE_UUID = 0x1814; // or "running_speed_and_cadence"
CHARACTERISTIC_UUID = "rsc_measurement"
```
このUUIDを用いてWeb Bluetooth APIを使用します。UUIDは16進数であるためprefixとして0xを付加することに気を付けてください。
もし、ほかのデバイスで試したい方は同様の方法でserviceとcharacteristicのUUIDを取得してください。

##UUIDが取得できたので通信を開始しよう！

まずはブラウザ上でbluetoothデバイスを検索するだけの処理を試してみましょう。

```javascript
navigator.bluetooth.requestDevice({acceptAllDevice: true});
```
<img width="575" alt="Screen Shot 2019-12-05 at 7.09.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/b5419146-b1de-b7cb-9db5-0ede4dc0927d.png">
Chromeのconsoleなどで実行すると画像のように複数のデバイスが認識されているのがわかります。
ここでrunning_speed_and_cadenceを選択するとペアリングができます。

では次に、ペアリングするだけでなくデバイスから情報を取得するために必要なcharacteristicを取得しましょう。下記のコードが取得例です。

```javascript

navigator.bluetooth.requestDevice({
    // 近くにある全てのデバイスを検索しているので、
    // Running Speed and Cadenceという名前のデバイスを選択する。
    acceptAllDevices: true,

    // これがないとservice情報やcharacteristic情報が取れないため
    // 必ず取得したいserviceを設定する必要がある。
    // 見逃しやすいのでSERVICE_UUIDが配列の要素であることにも注意する。
    optionalServices: [SERVICE_UUID]
}).then(device => {
    return device.gatt.connect();
}).then(server => {
    return server.getPrimaryService(SERVICE_UUID);
}).then(service =>{
    return service.getCharacteristic(CHARACTERISTIC_UUID);
}).then((characteristic)  => {
    // ここでcharacteristicに応じた処理を実装する。
    // 試しにcharacteristicをログに出力する
    console.log(characteristic)
}).catch(error => console.log(error));
```

characteristicが取得できたら、read/write/notifyごとに機能を実装することで
想定している処理ができます。


```javascript
//read
characteristic.readValue();

//write
characteristic.writeValue([BufferSource型]);

//notify
characteristic.startNotifications();

```
characteristicのデータ取得詳細に関してはここも参考になります。
https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic

またデータ取得の実例はここで実際に試せます。-> https://wakuwaku.co.jp/

#まとめ
ここまでで、PCのブラウザとBluetoothデバイスだけを用いて通信を行うことができました。もともとしたかったOculus Questのブラウザとの通信はできませんでしたが、今後できるようになるか期待です。
明日DMM.comグループアドベントカレンダー2019の担当は　@kgmyshin です！


#おまけ
もともとWebBluetooth APIで通信してみようとしていたデバイスの画像です。
UUIDの取得はできるものの、どのような値をどのように送れば好きなように動かせるかわからなかったため、中を開けてみました。その過程でモーターと基盤を接続する部分のコードが切れてしまったのでまた今度はんだごてを買って修復を試みる予定です。同じデバイスを持っている方がいればぜひ知見を共有しましょう。よろしくおねがいします。
![IMG_6768.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/31d128cd-3848-1287-4f5b-0f244f9fdabe.jpeg)
![IMG_6771.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/1072e7cd-d71e-07d0-5940-0c42d7b50986.jpeg)
![IMG_6772.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93401/f0de51c0-b18f-6f59-f373-86cb15beb4b4.jpeg)


#参考文献
- Web Bluetooth(Read/Write/Notifications)の使い方まとめ
    - Web Bluetooth APIの扱い方が綺麗にまとまっています。
    - UUIDが取得できたので通信を開始しよう！以降の部分ではかなりの部分を参考にさせてもらっています
    - https://qiita.com/umi_kappa/items/45761c7454d5d4c10bf0

- Web Bluetooth APIを使って体温計のデータをブラウザでBLE経由で受信してみた
    - https://qiita.com/megadreams14/items/02f524540896449944cd
    - web bluetoothを実際にconsoleで試すのに参考になりました。

- Web Bluetooth API
    - https://webbluetoothcg.github.io/web-bluetooth/

- Oculus Browser Developer Release Notes
    - https://developer.oculus.com/documentation/oculus-browser/latest/concepts/browser-release-notes/
    - Oculus BrowserはChromium74の機能にほぼ対応

- Chrome Status.com でのWeb Bluetooth API
    - https://chromestatus.com/features/5264933985976320

- Web Bluetoothのサンプル
    - https://googlechrome.github.io/samples/web-bluetooth/

- GATTの公式リファレンス(日本語ページ)
https://www.bluetooth.com/ja-jp/specifications/gatt/services/