BLE アドバタイズのパケット/iBeacon の詳細

なかなか、実際のプログラムコードに至りませんが、その前の前提を知っておいたほうがいいので少しずつ書いていきます。これ、Android や iOS で BLE ライブラリと使って Flutter とか React Native で作るときはさっくりと通していいのですが、m5Stack とか ESP32 とか使って自分で BLE 周りを制御しようとすると途端に必要な知識になっているので(知らないと落とし穴にはまる)、前提として知っておいたほうがいいです。

BLE パケットの Protorol Data Unit (PDU)

BLE パケット全体の大きさは、たかだか 47 バイト程度です。データ通信の場合はもっと長くなるのですが、ここで扱うのは BLE アドバタイズなので、非常に短いデータ量で済みます。データ量が少ないので、0.4 ms 以下という短い単位で送信/受信が可能です。

だから、受信しようと思えば Scan Window を広く取っておけば、いつでも BLE アドバタイズを受信できる確率は高くなります。ただし、それは概念的なので、実際的にはハードウェア的に Scan Window で待ち受けている時間とあ Scan Interval で受信できない時間が交互にやってきます。これ、バッテリーの問題なのか、ハードウェアの制限なのかはよくわからないのですが(つまり、無限に Scan Window を広げられるか私にはわかりません)、現実n BLE のハードウェアはそういう構造になっているということです。

さて、BLE パケットの内、データを乗せることができる Protocol Data Unit (PDU) の詳細をみていきます。

BLUETOOTH CORE SPECIFICATION Version 6.0 の Vol 6 Low Energy Controller の Part B LINK LAYER SPECIFICATION の 2 AIR INTERFACE PACKETS あたりから書いてあります。

全部で 4000 ページ位あるのですが、Core Specification 6.0 | Bluetooth® Technology Website https://www.bluetooth.com/specifications/specs/core60-html/ からダウンロードが可能です。通読するのは大変なので、適度にピックアップして読みます…が、この部分は PDU の部分だけじゃなくて、BLE パケット全体の仕様が書いているので、肝心のデータ部分がわかりません。多分、2.3.1.1 ADV_IND あたりの AdvData の部分のフォーマットだと思うのですが、これは後で調べ直し。

要は、BLE アドバタイズでも色々なヘッダーがあって種類があるのですが、iBeacon のようなフォーマットを送っている場合は ADV_IND の種類を流しておいて、その中のデータ = AdvData を送受信側でプロトコルとして決めるわけです。物理層/リンク層のもうひとつ上のレイヤーになります。

この AdvData の中身を決めているのが、Supplement to the Bluetooth Core Specification https://www.bluetooth.com/specifications/specs/core-specification-supplement-10/ です。これも PDF 形式でダウンロードができます。

この中の Part A DATA TYPES SPECIFICATION にプロトコル仕様が書いてあります。

これはサービス UUID の書き方ですね。

まあ、表だけ並べられても解り辛いので、いくつかのサンプルデータも載っています。

これはデバイス名をアドバタイズする場合です。

BLE のフォーマットはちょっと変わった形式になっていて、

  • データ長
  • データタイプなど
  • データ自身

という順番で並んでいます。データ長が1バイトと制限になるので(最大 255 バイト)とは思います。
上のデータを書き直すとこんな感じになります。

最初の 0x02 のように、フラグだけ並んでいる場合と、次の 0x0A のようにデバイス名のような可変長のデータが続く場合があります。デバイス名は特殊でデータ長が入っているのですが、大抵は Service UUID のように 16 バイトとか 4 バイトとか決まった長さのデータが続く場合が多いです。

なので、BLE アドバタイズ自身は Data types specification の書式に従っていれば自由に BLE アドバタイズのデータ送信が可能です。実際、Apple の iBeacon のフォーマットや、Google の Eddystone のフォーマットもこの書式に従っています。おそらく BLE 端末の機器メーカーも流しているのかはこれかな、と思うのですが、定かではありません。

BLE アドバタイズのフォーマットは自由に決められるということは解ったのですが、接触確認アプリのように既存の OS や BLE ライブラリを使う場合にはそれほど自由ではありません。と言いまうsか、Apple の場合は iBeacon フォーマットしか使えません。Android の場合は、もう少し自由に作れますが、iPhone との共同運用を考えると iBeacon フォーマットしか使えないというのが現実的な選択肢です。

もちろん、m5stack とか ESP32 を使て独自の BLE アドバタイズのフォーマットを使えば自由に作れます。Android の場合も多少の制限がありますが、Android 同士であれば結構自由に作れます。これは先行き解説していきます。

話を元に戻すと、FolkBears のコレクションレスモードの場合は、iBeacon のフォーマットを使っています。この iBeacon フォーマットは何処に記述されているのでしょうか?

https://developer.apple.com/ibeacon/ から Artwork and specifications をダウンロードして、Proximity Beacon Specification を開くと正式なものがあります。

これ、なかなか探してもわからなかったのですが、こんなところにあったんですね。

これだと解り辛いので、書籍や Wikipedia の iBeacon の記事を参考にするとわかりやすいです。

iBeacon – Wikipedia https://en.wikipedia.org/wiki/IBeacon

これを図に書き直すとこんな感じになります。

これが慣れないと難しいのですが、

  • 最初の3バイトは、BLE アドバタイズのヘッダー情報
  • 次の4バイトは、Maniufacturer Specific Data のデータタイプを示すための Length + 0xFF + Company ID (Apple の場合は 0x004C) が続く
  • 次の 2 バイトは、Apple 決めた iBeacon を示すための固定値 0x02 と 長さ(0x15)
  • 次の 16 バイトは、iBeacon の UUID
  • 次の 2 バイトは、Major
  • 次の 2 バイトは、Minor
  • 最後の 1 バイトは、Tx Power

という訳で2段階に分かれています。

BLE の規格としては、Manufacturer Specific Data を示す 0xFF のフラグと、その後の Company ID までで、その後は各社自由に作れます。自由に作れるといっても、アドバタイズのデータ全体が 31 バイトまでの制限なので、さらに小さくなります。

さらに、Company ID 自体は Bluetooth SIG が管理しているので、お金を払っている Apple 社は 16 ビット(2バイト)の Company ID を持っているのですが、実験的には 0xFFFF などを使わないといけません。
このあたりの細かいところは、後で ESP32 などで実装するときに詳しく解説する予定です。すくなくとも m5stack などの BLE ライブラリを使うと、かなり自由に BLE アドバタイズのフォーマットが作れるので iBeacon フォーマットにこだわる必要はありません。

iBeacon フォーマットの区切りがややこしいのですが、最初の Lnetgh + Flag の組みあわせは BLE 規格のほうで、 Manufacturer ID 以降の、Sub Type + Sub Length のほうの組み合わせは iBeacon 規格を出している Apple 独自の仕様です。ここで、Lenth と Type が逆になっているのはそのせいです。これは当悩んだのですが、つまりは内部規格ということです。

他にも Google の定義する AltBeacon フォーマットとか Eddystone フォーマットとかもありますが、これはまた別に解説します。同じ Beacon フォーマットとはいえ、ちょっとずつ違っているのは各社設定してしまっているからです。

とはいえ、Beacon タグからのデータは Apple だげが受信するものでもなく、Beacon タグの作成自体も Apple だけが作るわけでもありません。

  • 非 Apple 製の Beacon タグもある
  • 非 Apple 製の iBeacon 受信アプリもある

という事情があります。互換性というわけです。

iBeacon データを実測する

iBeacon フォーマット詳細がわかったところで実測をしてみましょう。

実測とはいえ、きちんとした iBeacon 受信機を作るところからスタートしないといけないのですが、ここでは私が以前したデータを使います。と、言いますか、ここからが問題があるのです。

iOS から発信された iBeacon データ

02011A1AFF4C00021590FA7ABEFAB6485EB7001A17804CAA13A6D0CFC1C5

これを先の表に従って分解していきます。

iBeacon UUID: 90FA7ABEFAB6485EB7001A17804CAA13 が取得できればひとまず ok です。
このデータ自体、Major や Minor、Tx Power も取得できるので、iBeacon フォーマットとしては正しいことがわかります。
実は、実測すると分かるのですが 3 バイト目のFlag データの 0x1A の部分は、iBeacon 発信機によって異なります。ここは BLE アドバタイズに仕様なので、先の iBeacon 仕様の例のように同じ値になるとは限りません。つまりは、内部的に Maniufacturer Specific Data の部分は同じ Apple の iBeacon の規格を使っているとしても前後の BLE のフラグは発信する機種/メーカーによって異なります。

これを Android が iBeacon データを発信したときのデータを見てみましょう。

1AFF4C00021590FA7ABEFAB6485EB7001A17804CAA13F15FA5E2C5

実は、先頭の 3バイト(02011A)がありません。

この現象は Android の org.altbeacon.beacon ライブラリと使ったときに発生します。

private fun startBeaconTransmission() {
    val beacon = Beacon.Builder()
        .setId1(SERVICE_UUID.toString()) // UUID
        .setId2(major.toString()) // Major (10進数文字列)
        .setId3(minor.toString()) // Minor (10進数文字列)
        .setManufacturer(0x004C) // Apple iBeacon のメーカーコード
        .setTxPower(-59) // 信号強度 (dBm)は仮設定
        .build()

    // val beaconParser = BeaconParser().setBeaconLayout(BeaconParser.ALTBEACON_LAYOUT)
    val beaconParser = BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")
    beaconTransmitter = org.altbeacon.beacon.BeaconTransmitter(context, beaconParser)
    beaconTransmitter?.startAdvertising(beacon, object : AdvertiseCallback() {
        override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
            Log.d("BeaconService", "iBeacon 発信開始")
        }
        override fun onStartFailure(errorCode: Int) {
            Log.e("BeaconService", "iBeacon 発信に失敗: $errorCode")
        }
    })
}

この iBeacon 形式のデータを iOS 側で受信させると、うまく受信できるので iBeacon 発信機としてうまく動いています。先頭部分の 02011A がなくても iBeacon フォーマットとしては問題ない、ということなのです。
ただし、この iBeacon データを、android.bluetooth.le.ScanFilter でフィルタリングしようとすると、うまくフィルタリングできない、という問題があります。なんだかよくよくわからないので、FolkBears では Maniufacturer Data の中身をシークしているのですが、ここは現在のところ回避策をとっているところです。

ひとまず、この BLE 規格の部分と iBeacon フォーマットを把握しておけば、BLE アドバタイズで受信したデータを解析できることがわかります。このあたり、m5stack や ESP32 で BLE アドバタイズを受信するときにも使える知識です。

カテゴリー: 開発, FolkBears パーマリンク