GoogleからBeacon用のオープンフォーマットEddystoneが発表されました。
- Google Developers Blog: Lighting the way with BLE beacons
- Google、BLEビーコンのためのフォーマット「Eddystone」を公開、Android/iOS向けアプリに実装可能なAPIも提供:CodeZine(コードジン)
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が送られてきたためバリデーションでエラーとなっています。
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();
}
あとはbuildServiceData
をbuildUrlData
に変えるだけ。
// serviceData = buildServiceData();
serviceData = buildUrlData();
この状態でビルドしてTxスイッチをオンにすると受信側ではURLが表示されます。
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_CNT
、SEC_CNT
が変化していないためです。
まとめ
今回、GoogleからEddystoneが発表されたのでお試しで触ってみました。
フォーマット自体もそんなに複雑ではなく、自分でデータを作るのも難しくないと思いました。
発表によるとGoogle Play Serviceの7.8からは"Neaby API"が追加されているということなので、今回触った辺りはアプリ側では特に意識すること無く扱えるようになるのかな?とも思いますが簡単に仕組みを分かっておくのもありだと思います。