2015年7月22日水曜日

EddystoneでURL送信

Eddystoneというものが発表されましたので早速遊んでみましたー。

RFIDじゃないじゃないかぁ!うりゃぁ!というご意見は大変ごもっともでございますが無視させていただきたくご了承ください。

Eddystoneはビーコンなんですが、そこにURLを載せれるというステキな機能になっております。これでビーコンに近づくだけでURLにアクセスできるわけです。タッチなんかしなくていいんです!(悲

とりあえず Eddystone の仕様を見てみます。
https://github.com/google/eddystone

ここに仕様とサンプルプログラムが置いてあります。
Eddystone-UID、Eddystone-URL、Eddystone-TLM と3種類あります。
AndroidのUID送信用のサンプルプログラムがありますので、とりあえずこれを改造してURLを飛ばすようにしてみましょう。

その前に基礎知識ですが、ビーコンはBLE(Bluetooth Low Energy)という技術を使っています。BLEにはセントラルとペリフェラルがあります。まぁ、クライアント/サーバ、マスター/スレーブのようなものですね。ビーコン自体はペリフェラルで、スマートフォン側がセントラルになるわけです。

で、スマートフォンをビーコンに仕立てるにはスマートフォンがペリフェラルになる必要があります。

と、いろいろ調べてましたらこれを発見
Android 5.0でBLE advertising するための要件

おー、ハードルたけー。そんなデバイス手元にあるのか?
もちろんありません。orz

あー、でも EddystoneValidator というEddystoneを検証するアプリの方は何とか Nexus7(2013)で動きました。検証はできそうです。

さて、もう無理か、でも iPhone があるぞ。iPhoneなら対応してるだろ。Eddystoneを発見するサンプルプログラムもあるし、これならできるに違いない!

ということで久しぶりに Mac mini を起動し、開発しようとするが iPhoneが iOS8.3 でXcodeが対応していない。Xcode6.3以上じゃないといかんらしいがそうすると Yosemiteが必要らしい。仕方なくインストール。

iOSでペリフェラルの使用についてはこちらのサイトに詳しく解説されています。
https://sites.google.com/a/gclue.jp/ble-docs/gatt-1/gatt-ios

ペリフェラルには簡単に言うとサービスとキャラクタリスティックというのが含まれています。サービスとキャラクタリスティックを生成し、アドバタイズを開始します。アドバタイズは通知みたいなもんで、「ほらーここに居ますよー」みたいな信号を出すわけですね。基本的にはビーコンはキャラクタリスティックとかは使ってなくて、アドバタイズだけを使用しているようです。(恐らく)

受信側は、アドバタイズを検知して、「おー、じゃあどんなサービスやキャラクタリスティックがあるのかなー」と見に来ます。ビーコンはこれをやらないわけですね。(たぶん)

EddystoneValidatorのソースを見てみると、アドバタイズを検知した後のコードで

byte[] serviceData = scanRecord.getServiceData(EDDYSTONE_SERVICE_UUID);

と引数からサービスデータを取り出してこのデータを検証していますので、アドバタイズを受けたあとでビーコンに問い合わせというのはしていないことがわかります。

で、実際に iOSでアドバタイズを開始します。(抜粋コード)

NSDictionary *advertising = @{ CBAdvertisementDataServiceUUIDsKey:@{ myService.UUID, }, };
[myPeriopheralManager startAdvertising:advertising];

アドバタイズ自体は簡単で、NSDictionaryに必要なものを詰めて startAdvertising すればOKです。ここで先ほどの EddystoneValidator のソースを参照すると、getServiceDataとなっていますから、サービスデータというのを詰めてあげればよさそうです。ここに詰められるのは先ほどのサイトを参照すると
https://sites.google.com/a/gclue.jp/ble-docs/advertising-1/advertising-ios
これだけあります。CBAdvertisementDataServiceDataKey というのがありますねー。きっとこれでしょー。

Byte value[] = { 0xFE, 0xAA, 0x10, 0x10, 0x00, 0x68, 0x61, 0x79, 0x61, 0x74, 0x6F, 0x04 };
NSDictionary *advertising = @{ CBAdvertisementDataServiceDataKey:[NSData dataWithBytes:&value[0] length:10], }, };
[myPeriopheralManager startAdvertising:advertising];

配列の値はこの時点でのものなので気にしないでください。
動かしてみると... あれ?何も起きない。
リファレンスを確認すると、
「That said, only two of the keys are supported for peripheral manager objects: CBAdvertisementDataLocalNameKey and CBAdvertisementDataServiceUUIDsKey.」

ぐぉ、CBAdvertisementDataServiceDataKeyがサポートされていない。orz(2回目)

ダメか、ここまでか、何かないのか。

そうだ!、Mac mini があるじゃないか!Macならビーコンになれるだろ!


ほらー、これを参考にすれば大丈夫だ!
と、やってみるが動かない。何で?
先ほどのサイトをよく見てみると。

「CBPeripheralManager が Yosemite (OS X 10.10) では動かないバグがあるようです。」

がーん サッキ Yosemite シチャッタヨ orz(3回目)

くそー、ここまでか。ここまできて遊べないのか。いやこの過程が遊びだからいいのか。しかし目標を達成できない気持ち悪さが残るな。いやまて、別に達成されなくても Eddystone対応ビーコンを待てばいいだけのことじゃないか。

そうだ、これがあった!


Intel Edison! あー、買っといてよかったー。
新しいファームの2.1になると従来の方法でアップデートできないみたいなのでこちらを参考にアップデート

Bluetoothのスタックは何だろう。どうやら BlueZ というのが入ってるらしい。
ここら辺を参考にしてみました。


しかしどれもうまく動きません。カオスになってきました。ただ唯一、blenoを使用した場合にうまく行きました。(iBeaconとして。唯一というのも何かの勘違いかもしれません)

bluez-ibeaconというのを発見したのでこれを試したところこれも動きました。

ゆらゆらとですが正解に近付いているようです。
あとは iBeacon のフォーマットを Eddystone に変えてあげればOKなはず。

Eddystoneの仕様を見ながら慎重に変更していきます。
ServiceDataのAD Typeはこちらを参照

変えたところだけソース

定義を追加
#define EIR_SERVICE_DATA            0x16
#define EIR_SERVICE_UUID            0x03

データ本体部分を変更
uint8_t segment_length = 1;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_FLAGS); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x06); segment_length++;
adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);

adv_data_cp.length += segment_length;

segment_length = 1;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_SERVICE_UUID); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0xAA); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0xFE); segment_length++;

adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);

adv_data_cp.length += segment_length;

segment_length = 1;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_SERVICE_DATA); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0xAA); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0xFE); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x10); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x10); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x00); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x68); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x61); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x79); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x61); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x74); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x6F); segment_length++;
adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x04); segment_length++;

adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);

adv_data_cp.length += segment_length;

データはFlags、ServiceUUID、ServiceDataの順になっています。
Eddystoneの仕様の表と比べていただくとわかりやすいかと思います。

ServiceDataの部分がURLの仕様で、こちらと比べていただくとわかりやすいかと思います。

ではコンパイルして動かしてみます。
EddystoneValidatorを起動すると...


キタ―! ついに念願の Eddystone URL ビーコン(Edison Eddystone)の完成です!

...で、これってどうやってアプリ無しでブラウザで見れるんだろう?OSアップデート待ち?そうなの? orz(4回目)

0 件のコメント:

コメントを投稿