micro:bit **に** Web Bluetooth API で情報を送り込む

# micro:bit から BLE 受信する話はいっぱいあるのに、
なぜか送信する方の情報がほとんどないので試行錯誤してまとめました。何ができるようになるかというと、例えば **webブラウザのUIから micro:bit を無線制御** できます。**スマートフォンで動かすラジコン的おもちゃ**を作るときなどにどうぞ。

# 用語の簡易解説など

 - micro:bit
   英BBCが作った Cortex-M0 マイコンボード。基本的に子ども向けなので多少乱暴に扱っても大丈夫だったり、怪我しないようになっていたりとても良くできている。センサもいろいろついているし、贅沢な 5x5 ドットマトリクス LED まで装備 (さすが子ども向け！)。それでいて、なんと mbed enabled なので mbed マイコンとしても使える。2000円＋税くらい。

 - Web Bluetooth API
   HTML5 ブラウザの一部に装備されている API でクライアント側のスクリプトのみでローカルな BLE 通信ができる。…すみません。私はその程度の認識しかありません。

 - promise
   非同期処理を書くときに、コールバック関数を個別に登録するんじゃなくて返り値が `.then` みたいな名前のメソッドを持っていて、それを連ねて次々とコールバックを登録して行く最近よく見るきれいな書き方。最初に考えた人エラいと思います。Web Bluetooth API でもこの方式使うが、慣れてないと戸惑うと思うのでよくわからなくなってきたら promise で検索！

 - 無名関数
   JavaScript には `function(arg) {}` というかたちだけじゃなくて `(arg) => {}` なんていう関数オブジェクトの書き方もあるんですね。これも知らないと頭抱えるので注意。

 - UUID
   Universally Unique IDで、要するにID。この記事の文脈ではBLE通信をするときにその機能などを一意に指定するのに使う。何かの規格である程度決まっているものみたい。


# PC, スマートフォン側

## HTML
簡単な例としてbuttonをクリックしたら micro:bit が `42` と表示するようにします。html はこんな感じです。

```html:microbit_bt.html
<!DOCTYPE html>
<html>
  <head><title>BLE test</title></head>
  <body>
    <h1>micro:bit controller</h1>
    <label for="mbid">micro:bit ID:</label><input id="mbid"> &gt;
    <button onclick="connect()">connect</button> &gt;
    <button onclick="raiseEvent()">event</button>
    <script src="mbbt.js"></script>
  </body>
</html>
```

![mbpage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265397/0c851a8b-9324-a519-23ac-98d2aa812fd5.png)



## JavaScript

`connect()` で接続して `raiseEvent()` で micro:bit 側にイベントを発生させます。
`connect()`のところはごちゃごちゃ複雑に見えるかも知れませんが、これでもpromiseのお陰ですっきりしていると思います。いずれにせよ、お約束パタンなのであまりいじる余地がありませんからここはこのまま使えば良いと思います。(はい私自身も何かを見てそのまま写経したに近い部分です。)
一方の `raiseEvent()` は単純ですが、実際になにか作るときは自分でいじる部分です。下記の例ではイベント番号を1234 (0x04d2) にしており、これは任意です。(次節で micro:bit の MakeCode エディタ上で指定します。)

```javascript:mbbt.js
let UUID_EVENT_SERVICE = "e95d93af-251d-470a-a062-fa1922dfa9a8"
let UUID_CLIENT_EVENT_CHARACTERISTIC = "e95d5404-251d-470a-a062-fa1922dfa9a8"
let characteristic = null
    
function connect() {
  let mbid = document.getElementById("mbid").value
  let opt = {
    filters: [
      {services: [UUID_EVENT_SERVICE]},
      {name: "BBC micro:bit [" + mbid + "]"},
    ]
  }
  navigator.bluetooth.requestDevice(
    opt
  ).then((device) => {
    return device.gatt.connect()
  }).then((server) => {
    return server.getPrimaryService(UUID_EVENT_SERVICE)
  }).then((service) => {
    return service.getCharacteristic(UUID_CLIENT_EVENT_CHARACTERISTIC)
  }).then((ch) => {
    characteristic = ch
    alert("Connected!")
  })
    .catch(function(err) {
      console.log(err)
    })
}

function raiseEvent() {
  if (!characteristic) {
    alert("no characteristic")
    return
  }
  // 0x04d2 (=1234) がイベント発生源, 0x002a (=42) が イベントの値
  let data = new Uint32Array([0x002a04d2])
  return characteristic.writeValue(data)
}
```


# micro:bit 側

## 設定＆準備

1. MakeCode エディタの設定画面で `No Pairing Required` を on にします。
![microbit_bt_config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265397/24382cd1-733b-db0d-ea18-63908e3d810d.png)
2. 何でもいいのでコードをmicro:bitにダウンロードします。
3. BLE スキャンできるツール（スマートフォンならNordicのnRF Connectというアプリがおすすめ。)でmicro:bitのIDを確認します。ここで言うIDとは Local Name の `BBC micro:bit [*****]` の `*****` の部分です。メモしておきましょう。

## コード

ブロックエディタで作ります。使うのは【イベントが届いたとき】です。ただし、**自分で作ったイベント(1234)は選択肢にない**ので、《計算》のカテゴリから数値リテラル(?)を持ってきて発生源にハメ込みます。そこに1234と書けばokです。


![mbbtcode2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265397/688c6054-63e9-c207-861c-bc73df86f57e.png)

以上で完成です！

# 実行！

1. micro:bit に書き込む
2. PC のブラウザで`microbit_bt.html`を開く。
3. 調べてメモを取ったIDをブラウザのテキストボックスに入力。
4. 【connect】ボタンを押す。→ ダイアログが出るのでmicro:bitを選択し、ペア設定する。
5. 【event】ボタンを押す。→ micro:bitに`42`と表示されれば成功!

以上です。イベント番号の42は自由に設定できるので任意の 16 bit 値が送れる、というわけです。

# trouble shooting

## 接続できない
  - ブラウザはChromeかOperaでないとダメです。Chromeの場合、バージョン等によって`chrome://flags`にある`Experimental Web Platform features`を Enabled にする必要があります。
  - micro:bit の BLE がペアリング不要状態になっていないとダメかも知れません。上記のmicro:bit節の『設定＆準備』を確認してください。

## ローカルでは動くのにデプロイしたら動かない
  - ローカルコンテンツじゃない場合、Web Bluetooth API は https じゃないと動きません。