Google発のBeacon用オープンフォーマットEddystoneをAndroidで触ってみた

  • 36
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

GoogleからBeacon用のオープンフォーマットEddystoneが発表されました。

Android用のサンプルも用意されていたので早速触ってみました。

Eddystoneとは

Googleが発表したBeacon用のオープン規格です。
Beaconが送信するデータの種類として下記の3種類が規定されています。

  • Eddystone-UID
    • 端末識別のためのUID
  • Eddystone-URL
    • URLデータ
  • Eddystone-TLM
    • 温度やバッテリーの電圧など端末状態測定用データ

お試し環境

用意されているサンプルコードはminSdkVersionが22となっているため送信と受信両方の動作を見るためにはLolipop以上の端末が2台必要です。

  • 送信側: Nexus 6 (Android 5.1.1)
  • 受信側: Nexus 9 (Android M)

準備

サンプルコードが含まれているGithubのEddystoneリポジトリを適当な場所にcloneします。

git clone git@github.com:google/eddystone.git

受信側

cloneしたリポジトリのeddystone/tools/eddystone-validator/EddystoneValidatorをAndroid Studioで開いてビルド、インストールして端末側で実行します。
起動後にToastでエラーが表示されなければOKです。エラーが表示される場合は端末のBluetoothデバイスが対応していないか、デバイスの状態がおかしい可能性があります。
自分の場合は、Nexus 9でアプリを起動したところエラーが発生しましたが、端末を再起動すると正常に起動させることができました。何か別のプロセスがBluetoothデバイスを掴んでいたのかもしれません。

送信側

受信側と同じく、cloneしたリポジトリのeddystone/eddystone-uid/tools/txeddystone-uid/TxEddystone-UIDをAndroid Studioで開いてアプリにインストールします。

Eddystone-UID

サンプルのTxEddystone-UIDは名前の通りEddystone-UIDを送信します。
フォーマットは下記の通り、これはアプリ内で生成されているので興味があれば見てみるといいでしょう。

Byte offset Field Description
0 Frame Type Value = 0x00
1 Ranging Data Calibrated Tx power at 0 m
2〜11 NID 10-byte ID Namespace (端末を一意に識別する)
12〜17 BID 6-byte ID Instance (サービスを一意に識別する)
18 RFU Reserved for future use, must be0x00
19 RFU Reserved for future use, must be0x00

送信側アプリのTxスイッチをオンにすると、受信側アプリに情報が表示されることが確認できます。
一度、送信をオフにしてRNDボタンでIDをランダムに変更し再度オンにすると新しい情報が受信側に表示されます。
このとき、前回と違うIDが送られてきたためバリデーションでエラーとなっています。

Screenshot_20150716-135838 のコピー.png

Eddystone-URL

サンプルで送信できるのはEddystone-UIDだけなので、TxEddystone-UIDを改造してURLを送信してみます。
Eddystone-URLの構造は下記のようになっています。

Byte offset Field Description
0 Frame Type Value = 0x10
1 TX Power Calibrated Tx power at 0 m
2 URL Scheme Encoded Scheme Prefix
3+ Encoded URL Length 0-17

ぱっと見は簡単そうなのですが、URLは次の規則で変換してやる必要があります。

URL Encoding

SchemeによってURL最初の1バイト目の値が決まります。

Decimal Hex Expansion
0 0x00 http://www.
1 0x01 https://www.
2 0x02 http://
3 0x03 https://

また、次のようにいくつかのドメインが1byteに変換できるようになっています。

Decimal Hex Expansion
0 0x00 .com/
1 0x01 .org/
2 0x02 .edu/
3 0x03 .net/
4 0x04 .info/
5 0x05 .biz/
6 0x06 .gov/
7 0x07 .com
8 0x08 .org
9 0x09 .edu
10 0x0a .net
11 0x0b .info
12 0x0c .biz
13 0x0d .gov
14..32 0x0e..0x20 Reserved for Future Use
127..255 0x7F..0xFF Reserved for Future Use

実装

この変換を自分で実装するのはちょっと面倒なのでeddystone/eddystone-url/tools/eddystone-url-config-validator/app/libsの中にあるuribeacon-library-release.aarをTxEddystone-UIDのappフォルダ直下にlibsフォルダを作成してコピーします。
build.gradleにはこのaarを使うように記述します。

repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compile 'org.uribeacon:uribeacon-library-release@aar'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

UID送信用のデータを作っているbuildServiceDataメソッドを参考にURLを送信するデータを作成します。

    private byte[] buildUrlData() throws IOException {
        byte[] url = UriBeacon.encodeUri("http://www.google.co.jp/");
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        os.write(new byte[]{(byte) 0x10 /* URL frame type */, (byte) 0x10 /* Tx power */});
        os.write(url);
        return os.toByteArray();
    }

あとはbuildServiceDatabuildUrlDataに変えるだけ。

  // serviceData = buildServiceData();
  serviceData = buildUrlData();

この状態でビルドしてTxスイッチをオンにすると受信側ではURLが表示されます。

Screenshot_20150716-133622.png

Eddystone-TLM

最後にBeacon端末の状態測定用のEddystone-TLMです。
あまりスマホからこいつを実装して送信する用途は思いつかないのでサクッといきます。

Byte offset Field Description
0 Frame Type Value = 0x20
1 Version TLM version, value = 0x00
2〜3 VBATT Battery voltage, 1 mV/bit
4〜5 TEMP Beacon temperature
6〜9 ADV_CNT Advertising PDU count
10〜13 SEC_CNT Time since power-on or reboot

各データの細かいフォーマットはドキュメントを確認してください。
2バイト目がUIDやURLのTx Powerではなくバージョンになっていることに注意が必要です。

実装

今回は、お試ししたいだけなので値は決め打ちしてしまいます。
なお、2バイト目のバージョンは0x00にしないと怒られるようです。

    private byte[] buildTlmData() throws IOException {
        byte[] data = {(byte) 0x00, (byte) 0xf0, (byte) 0x08, (byte) 0x58,
                (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x49, (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x49};
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        os.write(new byte[]{(byte) 0x20 /* TLM frame type */, (byte) 0x00 /* Version */});
        os.write(data);
        return os.toByteArray();
    }

URLの時と同じくbuildServiceDataを呼び出している箇所をbuildTlmDataに変えて実行するとTLMを受信していることがわかります。
ただ、すこし時間が経つとバリデーションエラーとなってしまいます。
これは、時間経過でカウントアップされなくてはならない値ADV_CNTSEC_CNTが変化していないためです。

Screenshot_20150716-133240.png

まとめ

今回、GoogleからEddystoneが発表されたのでお試しで触ってみました。
フォーマット自体もそんなに複雑ではなく、自分でデータを作るのも難しくないと思いました。

発表によるとGoogle Play Serviceの7.8からは"Neaby API"が追加されているということなので、今回触った辺りはアプリ側では特に意識すること無く扱えるようになるのかな?とも思いますが簡単に仕組みを分かっておくのもありだと思います。