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

October 08, 2017

ブラウザから Bluetooth(BLE)が使えるで話題の Web Bluetooth API が少し前に正式リリースされました。
まだ発展途上感がありますが、今後に期待ができますね。

Web Bluetooth

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

Web Bluetooth API

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

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

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

対応ブラウザは以下から確認可能です。
GitHub WebBluetoothCG/web-bluetooth

その他いろいろ制限(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 を参照してください。
https://github.com/hiknot/rw_blenanogithub.com

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

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

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

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

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

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

全体は GitHub を参照してください。
https://github.com/hiknot/rw_messagegithub.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 への書き込みに使用します。

    .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 で十分かもしれませんね。

参考

https://ics.media/entry/15520

http://yegang.hatenablog.com/entry/2014/08/09/195246


Profile picture

Twitter GitHub