Web Bluetooth API で BLE（Chrome と micro:bit をつなぐ）

## はじめに

昨年、アドベントカレンダーの記事を書く中で、いくつか BLE関連の内容（以下のもの）を試しました。

**【2019年のアドベントカレンダー用に書いた記事（BLE関連のものを抜粋）】**

1. [新デバイスと新機能：obniz Board 1Y と obniz-noble（2019/12/18）](https://qiita.com/youtoy/items/13017a7625732a0dc27d)
2. [新しい obniz で obniz-noble を試す（2019/12/22）](https://qiita.com/youtoy/items/1a2e92ae7a83df7e9a82)
3. [#obniz と micro:bit で obniz-noble を使った BLE通信 （26記事目）](https://qiita.com/youtoy/items/bb24dd9051f3eb8a33e2)
4. [micro:bit で BLE ＋ UART による文字列の送受信（29記事目）](https://qiita.com/youtoy/items/46502b9bd17fad16d427)
5. [micro:bit と #obniz で BLE ＋ UART を試そうとした話と Mac用ツール（30記事目）](https://qiita.com/youtoy/items/34b70bb57c9f54c9e971)

そして、micro:bit と BLE利用の組み合わせが中途半端になっている部分があったため、その続きを試していった内容になります。

### Web Bluetooth API で試す内容

上記の記事 5）の最後に、「[Web Bluetooth API](https://caniuse.com/#feat=mdn-api_bluetooth) を使った内容は、micro:bit との連携を行った事例のサンプルがけっこう見つかった」と書いていました。
今回はサンプルが多く見つかっていた、Web Bluetooth API を使った ブラウザ（Chrome）と micro:bit との間でのテキストデータのやりとりを試してみます。

### Web Bluetooth API のブラウザの対応状況（2020/1/1 時点）

まずは、記事執筆時点（2020/1/1 時点）で Web Bluetooth API が利用可能なブラウザについて、以下のページで確認してみます。

●Can I use... Support tables for HTML5, CSS3, etc
　https://caniuse.com/

Web Bluetooth API の対応状況は、以下のようになっており、とりあえず Chrome を使っておくのが良さそうでした。

<img width="493" alt="Can_I_use____Support_tables_for_HTML5__CSS3__etc.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9b98992c-bdaa-c9ca-dc21-b7c0ccdd1080.jpeg">

## Web Bluetooth API を利用するための準備とお試し

いくつか見つかっていたサンプルのうち、まずは以下を試してみました。
内容は「ブラウザ上から micro:bit にテキストデータを送信し、micro:bit上の LED にテキストを表示させる」というものです。

●Web Bluetooth API を使ってブラウザだけでMicro:bitとBLE通信してみる。
　https://shimz.me/blog/microbit/5456

### GitHub Pages を利用したオンライン上での環境準備

これを試すための環境準備の方法はいくつかありますが、まずはブラウザ上での操作のみで GitHub Pages を利用し、Web Bluetooth API を動作させるための環境を準備してみようと思います。

環境を GitHub上で準備するにあたり、既存のリポジトリを利用することも可能ですが、今回は専用のリポジトリを作成する手順で進めることにします。

▼ I） GitHub にログインし、https://github.com/new にアクセス。
▼ II） Repository name を設定し、Public のリポジトリを新規作成する（自分の場合は「API_Test」という名称にしました）。
▼ III） 作成したリポジトリのページの右上から「create new file」を選択。
<img width="603" alt="yo-to_API_Test__API_のテストを行うページを置くためのリポジトリ.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c262f475-c192-fdf5-45f8-9e07ed168299.jpeg">
▼ IV） 作成するファイル名を以下に入力する。
<img width="542" alt="New_File_GitHub.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d63ae0d9-c86c-6eb0-66a4-ee406504936b.jpeg">
　 ※ 自分の場合は、以下のとおり「WebBluetoothAPI/test01.html」としました
<img width="498" alt="New_File_GitHub2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/77eed30b-a22f-795a-3788-fbdd024f783c.jpeg">
▼ V） 「<> Edit new file」の下に[上記の参考ページとして書いた記事](https://shimz.me/blog/microbit/5456)のソースコードを書き（＋ 少し書きかえをして）、以下の画像で示されたページ下部で「Git のコミットメッセージ」を書くなどして、「Commit new file」を押す。
<img width="473" alt="New_File_GitHub3.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4ee98edd-f638-1db2-a276-0e09ea5d9d1e.jpeg">
▼ VI） リポジトリのページ右上の「Settings」を選択。
<img width="601" alt="yo-to_API_Test__API_のテストを行うページを置くためのリポジトリ_設定.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/e21e53b1-9146-9e8d-0a37-40b514047f7f.jpeg">
▼ VII） 以下の「Select source」で None 以外を選び（※ 自分が今回行った内容では「master branch」を選択）び、
<img width="610" alt="Options_1.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/81225465-5c2b-3480-1830-e1e4a5b17e18.jpeg">
　 選択後に以下のような表示になっていれば OK。
<img width="607" alt="Options_2.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d7719ff3-3f1c-2f75-b21d-e32f6558c859.jpeg">

上記の V）の部分で書きかえを行ったのは、元記事のソースの 41行目「{name: "BBC micro:bit [vaget]"}」の部分です。この「vaget」となっている部分は、micro:bit のデバイスごとに固有で決まる部分のため、自身が利用している micro:bit を検索できるようにするために書きかえが必要です。

自分のデバイスの固有の情報に書きかえても良いですが、以下の別の記事を見ると、上記のデバイス固有の情報を含めないやり方もあったため、それを採用しました。

●micro:bitとWebBluetoothで通信してみました - Qiita
　https://qiita.com/yokmama/items/5522fabfb5b9623278e2

具体的には、以下のような指定をします（文字列のプレフィックスのみをマッチさせる方法です）。

```JavaScript
  filters: [{
    namePrefix: 'BBC micro:bit',
  }],
```

このやり方で、以下の動画のようにブラウザから micro:bit へのテキスト送信が行えることが確認できました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Web Bluetooth API を使った、Chrome と micro:bit の通信！<br><br>ペアリング後に、テキストデータをブラウザから送信し、そのデータを micro:bit の LED に表示できたことを確認。 <a href="https://t.co/BUHw9Udikh">pic.twitter.com/BUHw9Udikh</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1212351342787715074?ref_src=twsrc%5Etfw">January 1, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

なお、ブラウザ側のソースコードは以下になります（参照元のソース、ほぼそのままですが）。

```JavaScript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Web Bluetooth API のテスト</title>
</head>

<body>
<button id="connect">接続</button>
<button id="disconnect">切断</button>
<input id="message" value="hello" />
<button id="send">送信</button>

<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
<script>
// 情報元： https://shimz.me/blog/microbit/5456
var bluetoothDevice;
var characteristic;
//micro:bit BLE UUID
var LED_SERVICE_UUID                        = 'e95dd91d-251d-470a-a062-fa1922dfa9a8';
var LED_TEXT_CHARACTERISTIC_UUID            = 'e95d93ee-251d-470a-a062-fa1922dfa9a8';

//ボタンイベントリスナー
d3.select("#connect").on("click", connect);
d3.select("#disconnect").on("click", disconnect);
d3.select("#send").on("click", sendMessage);

//micro:bitに接続する
function connect() {
  let options = {};

  //options.acceptAllDevices = true;
  options.filters = [
    {services: [LED_SERVICE_UUID]},
    {namePrefix: "BBC micro:bit"}
  ];

  navigator.bluetooth.requestDevice(options)
  .then(device => {
    bluetoothDevice = device;
    console.log("device", device);
    return device.gatt.connect();
  })
  .then(server =>{
    console.log("server", server)
    return server.getPrimaryService(LED_SERVICE_UUID);
  })
  .then(service => {
    console.log("service", service)
    return service.getCharacteristic(LED_TEXT_CHARACTERISTIC_UUID)
  })
  .then(chara => {
    console.log("characteristic", chara)
    alert("BLE接続が完了しました。");
    characteristic = chara;
  })
  .catch(error => {
    console.log(error);
  });
}

//LEDに表示するメッセージを送信
function sendMessage() {
  if (!bluetoothDevice || !bluetoothDevice.gatt.connected || !characteristic) return ;
  var text = document.querySelector("#message").value;
  var arrayBuffe = new TextEncoder().encode(text);
  characteristic.writeValue(arrayBuffe);
}

//BLE切断処理
function disconnect() {
  if (!bluetoothDevice || !bluetoothDevice.gatt.connected) return ;
  bluetoothDevice.gatt.disconnect();
  alert("BLE接続を切断しました。")
}
</script>
</body>
</html>
```

### ローカル環境でのサーバ準備

ここまでは、オンライン上で環境を準備しましたが、ローカルの PC上でも環境を準備して試してみようと思います。

#### Python を利用

Web Bluetooth API に関して情報を調べてみると、「SSL通信が必須」と書かれたページがたくさん出てくるものの、「SSL通信なしで利用できた」という情報もありました。

まずは、簡易な方法でサーバを準備してみます。
以下のページに色々な言語でのやり方が書かれていたため、これを利用しようと思います。

●ワンライナーWebサーバを集めてみた - Qiita
　https://qiita.com/sudahiroshi/items/e74d61d939f18779970d

いくつかある中の「Python3.x編」を試したところ、SSL通信なしで Web Bluetooth API を用いた通信を試すことができました。

```bash:8000番ポートで待ち受けする例
$ python -m http.server 8000
```

#### node.js の light-server を利用（※ npx を利用）

今回、SSL通信なしに上記が試すことができましたが、SSL通信を利用できる環境も準備してみます。
「[node.js の light-server](https://www.npmjs.com/package/light-server)」を利用した以下のコマンドで証明書を自動生成できる、といった記載があり、これを試すことにしました（http2 での通信になったりはしますが）。

````bash
$ light-server -s . --http2
light-server is listening at https://0.0.0.0:4000
  serving static dir: .
````

今回は、上記をそのまま実行してみるのではなく、「npx」を使ってみました。
npx を利用したことで、light-server が事前に準備されていない環境で、今回の実行時のみ一時的に light-server を利用可能な状態になりました（※ 以下を停止させた後は、light-server が準備されていなかった状態に戻ります）。

````bash
$ npx light-server -s . --http2
npx: 88個のパッケージを8.196秒でインストールしました。
light-server is listening at https://0.0.0.0:4000
  serving static dir: .
````

これで、`https://localhost:4000/（ファイル名）.html`にアクセスすると、「自己署名証明書」を利用した形になるため、当然、以下のような警告はでます。
<img width="549" alt="プライバシー_エラー.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8e68834d-30ae-cc3e-8c16-343302cf94f7.jpeg">
さらに先へ進むと、警告は残りつつも以下のとおりページを開くことができました。
<img width="577" alt="Web_Bluetooth_API_のテスト.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/f54ec5ce-b8c5-7830-f5d0-e231cbd096a3.jpeg">

### おわりに

今回は、micro:bit の「Bluetooth LEDサービス」のみを利用しましたが、他にもいくつも利用可能なものがあるため、今後はそれらも試してみようと思います。