surga Lab

営業部でも開発したい!!

Web Bluetooth API + BLE Nanoでブラウザから通信してみる

ブラウザからBluetooth(BLE)が使えるで話題のWeb Bluetooth APIが少し前に正式リリースされました。

まだ発展途上感がありますが、今後に期待ができますね。

Web Bluetooth

せっかく手元にBLE Nanoがあるので、今回はBLE NanoとブラウザをBluetoothで通信してみます。

blog.hisurga.com

Web Bluetooth API

まずWeb Bluetooth APIについて簡単に説明します。

先述したようにWebからBLE通信できる代物で、わざわざネイティブアプリを入れなくてもWebアプリで簡単にBLE通信を可能にするものです。

端末が対応していれば準備オッケー、いいですね。

対応ブラウザなどは以下から確認可能です。

web-bluetooth/implementation-status.md at master · WebBluetoothCG/web-bluetooth · GitHub

その他いろいろ制限(SSLが必須であるとか)があるみたいですが、とりあえず割愛(よくわからなかった)

作るもの

今回はブラウザからBLE Nanoにメッセージを送信し、その後は逆にBLE Nanoからメッセージを取得します。

ボトルメッセージ的な物をイメージしていただけたらなと。

BLE Nano側の実装

BLE NanoのサンプルコードのSimple Chatを元に改変します。

場所はスケッチの例Examples for BLE NanoBLE_ExamplesSimple Chatです。

そのままだとBLE Nanoから読み込みができないので、必要なタイミングでread用キャラクタリスティックをアップデートさせます。

ble.updateCharacteristicValue(characteristic2.getValueAttribute().getHandle(), rx_buf, rx_buf_num);

全体はGitHubで、

github.com

ブラウザ側の実装(html + javascript)

通信ではGATT(Generic Attribute Profile)を使用します。

GATTとはBLE通信を行う際の共通の形式です。これによってメーカに関わらず通信を可能にします。

今回の通信で重要なのはUUIDとValueかな、UUIDを指定することででBLE Nanoのサービスとデータ(Value)を扱うことができます。

コード

開発にはCloud9を利用しました。

そこでコード書いてファイルをpublicにすれば、自動的にssl通信の環境になるので楽です。おすすめです。

全体はGitHubで、

github.com

requestDevice

本来であれば、「心拍数」などのすでに定義されているサービスを指定して検索をかけますが、今回のメッセージ保存に合致するBLEのサービスが見つからなかったので、acceptAllDevicesで周りのBLEを全て検索します。

それだけだと繋がらないので、optionalServicesでUUIDも指定します。(セキュリティ的な問題?)

UUIDはBLE Nanoの実装時に定義した物を利用します。

  navigator.bluetooth.requestDevice({
    optionalServices:[SERVICE_UUID],
    acceptAllDevices:true
  })
接続
    .then(device => { 
      console.log("devicename:" + device.name);
      console.log("id:" + device.id);

      return device.gatt.connect();
    })
サービスの取得
    .then(server => {
      console.log("success:connect to device");

      return server.getPrimaryService(SERVICE_UUID);
    })
キャラクタリスティックを取得します。

こちらのUUIDもBLE Nano実装時の物を利用します。

RXがBLE Nanoからの読み込みに使うUUIDで、TXはBLE Nanoへの書き込みに使用します。

私はRXとTXを逆にしてしまっていて、それに気づかず1ヶ月ぐらい進捗ストップでした。

    .then(service => {
      console.log("success:service");

      // 複数のキャラクタリスティックを配列で取得可能
      return Promise.all([
        service.getCharacteristic(RX_CHARACTERISTIC_UUID),
        service.getCharacteristic(TX_CHARACTERISTIC_UUID)
      ]);
    })

使用するキャラクタリスティックが1つであれば、以下のようで問題ありません。

service.getCharacteristic(CHARACTERISTIC_UUID)
データ書き込み

writeValue()でデータの書き込みです。

文字列をUTF-8形式で送信しています。

変換にはtext-encodingを使用しました。

var form_d = document.getElementById("data-form").value;

var ary_u8 = new Uint8Array( new TextEncoder("utf-8").encode(form_d) );
console.log(ary_u8);
try {
    txCharacteristic.writeValue(ary_u8);
}
catch (e) {
  console.log(e);
}
データ読み込み

readValue()でキャラクタリスティックからデータ読み込みです。

取得したvalueをArraybuffer形式にして、UTF-8形式でデコードしています。

let message;

  try {
    rxCharacteristic.readValue()
      .then(value => {
        message = value.buffer;
        console.log(new Uint8Array(message));
        document.getElementById("data-form").value = new TextDecoder("utf-8").decode(message);
      });
  }
  catch (e) {
    console.log(e);
  }

動作

MacBook+ChromeとAndroid+Chromeでは動くのを確認しました。

こんな感じに動きます。

可能性

つまるところ、Webコンテンツの可能性が増えたってことですよね。

以前までだとBluetoothを経由する場合アプリを入れることが必須でしたが、そもそもアプリを入れること自体にハードルがありました。

しかし今回、簡易的な通信であればWebで終わらせることが可能になったので、ハードルが下がって利便性もあがるんじゃないかなーと思います。

IoTデバイスからデータを引っ張ってくるだけならWebBluetoothで十分ですしね。

参考

ics.media

yegang.hatenablog.com