要注意人物!? iBeacon でアイツから逃げてみた! 【その1】

こんにちは!テクニカルコンサルティンググループの monma です。

※umako編集長

※umako編集長

今回ブログを書くのが 4 記事目になりますが、この Mobage Developers Blog は umako という編集長に牛耳られています。毎回毎回「ブログ書け〜」という念力と実際に書かなければならないという圧力を与えてきます。

そして umako という名前がやばいです。飛鳥時代のあの人を思い出します。(全国の umako さんごめんなさい)

 

今回は iBeacon に関しての記事になるのですが、iBeacon の説明は色々なところで記事になっているので詳しい説明は省かせてもらい、この記事ではタイトルにもあるように iBeacon を使って、この恐ろしい umako 編集長から徹底的に逃げまわってみたいと思います。(なぜ某球団マスコットの帽子をかぶっているかは謎です。。。)

計画としては umako 編集長に iBeacon をこっそりつけて、近づいてきたら iPhone に通知が来るようなアプリを開発し、通知が無くなるまで逃げるということを考えてみました。

※ ちなみに iBeacon とは何かを簡単に説明すると、ビーコンという電波を定期的に発信することで、数十 cm から数十 m の範囲で正確ではないですが iOS 端末との距離を測定したり、範囲内に入ってきたビーコンを特定できたりする技術です。Bluetooth Low Energy により少ない電力消費でビーコン電波を出し続けることができるようになったことから実現できるようになった技術となります。(iBeacon については詳しくはこちら

 

今回使用した iBeacon 端末は、Stick N Find という商品になります。この商品は、例えば鍵などに取り付けておくと、iPhone や Android でビーコンの強さから鍵を探し出せるという商品です。

Stick N Find はデフォルトのままでは iBeacon としては使用できないので、iBeacon 化するのには、SNF Beacon というアプリ(https://itunes.apple.com/jp/app/snf-beacon/id840531013?mt=8 )を使用しました。検出された Stick N Find に以下を設定することができます。

※ 書き換えを行った端末でしか元に戻すことができなくなってしまうっぽいので注意が必要です。ご自身の責任のもと、使用してください。

  • UUID

    • ビーコンに設定する固有の値。UUID 形式で設定できます。

  • major

    • ビーコンに設定する固有の値。整数値で設定できます。

  • minor

    • ビーコンに設定する固有の値。整数値で設定できます。

  • 電波強度 (Tx)

    • 電波の強度を設定できます。後から気づいたのですが、最大設定にすると 3 日くらいで電池がなくなりました。。。

  • ビーコン周期

    • ビーコン電波が出力される周期です。20 ms から設定できます。細かく周期を設定すると、反応が良くなる気がしますが、それだけ電池も食います。

major, minor の値は、受信側で受信できる数値になります。

今回は、以下の設定にしてみました。

  • major = 1

  • minor = 1

  • 電波強度 (Tx) = 4dB

  • ビーコン周期 = 20ms

電波強度とビーコン周期は精度を上げたかったので、一旦、最大の値を指定してみました。

 

さて、それでは受信側のアプリを開発していきます。

受信側のアプリは以下の項目を満たすアプリを制作したいと思います。

  • umako 編集長からの距離を算出する。

  • umako 編集長が近づいてきたらビーコン音を再生する。

    • umako 編集長との距離が近づくにつれ、音量が大きくなり、再生スピードが上がる。

  • umako 編集長との距離に応じて iPhone 上にメッセージを出す。

まずはビーコン音を再生してみます。再生するビーコン音は Audacity というアプリで自作した beacon.m4a というファイルを再生することにします。

   NSBundle* bundle = [NSBundle mainBundle];
    NSString* soundPath = [bundle pathForResource:@"beacon" ofType:@"m4a"];
    NSURL* soundUrl = [NSURL fileURLWithPath:soundPath];
        
    avap = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
    avap.delegate = self;
    avap.volume = 0.0f;
    avap.numberOfLoops = -1;
    [avap setEnableRate:YES];
    [avap setRate:0.5];
        
    [avap prepareToPlay];      
    [avap play];

実装する UIViewController の初期化時に上記の実装を行ないました。ビーコン音はループ再生、初期化時には音量 0 の状態で再生を行ない、再生スピードの変更も行えるようにします。

 

次に iBeacon の検出を開始するメソッドを実装します。

    _manager = [[CLLocationManager alloc] init];
    _manager.delegate = self;
    
    _region = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:BEACON_UUID] identifier:@"test"];
    
    // 距離の測定を開始する
    [_manager startRangingBeaconsInRegion:_region];

BECON_UUID はビーコンに設定された UUID を指定します。UUID を指定すると、同じ UUID のビーコンが検出できるようになります。

そして上記コードをボタンに割り当てて、iPhone 側で検知開始を出来るようにしました。

@interface ViewController : UIViewController<CLLocationManagerDelegate>

CLLocationManagerDelegate をプロトコルに追加して、locationManager:didRangeBeacons:inRegion: メソッドが一定間隔で呼ばれる様にします。

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
    // 一番最初に検出されたビーコンを使います
    CLBeacon* beacon = [beacons objectAtIndex:0]; 
    ...
}

locationManager メソッドを追加実装しました。locationManager メソッドには同じ UUID の複数のビーコンが NSArray 型で近い順から通知されますが、一番最初の要素のビーコンのみを使用することにします。

CLBeacon オブジェクトは、以下のプロパティが存在します。

  • proximityUUID

    • ビーコンの UUID

  • major

    • ビーコンに設定された major 値

  • minor

    • ビーコンに設定された minor 値

  • proximity

    • ビーコンとの距離(不明・すごく近い・近い・遠い)

  • accuracy

    • 精度

  • rssi

    • 受信した信号の強度

上記プロパティには proximity という大体の距離は存在するもののメートル単位での距離は存在しないので、RSSI 値から推定することにしました。

以下の URL に記載されている距離算出法を用いました。txPower は iPhone と iBeacon を 1m 離した時に計測した RSSI 値を入れています。(実測では -56 dB でした)

http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing

    double ratio = (double)beacon.rssi*1.0f/txPower;
    double distance = (0.89976f)*pow(ratio,7.7095f) + 0.111f;  

算出した距離を元に、ビーコン音の音量とスピードを変化させます。2 m 未満の距離の場合、 umako 編集長に捕まってしまったとみなして音を止めます。20 m より大きい距離であれば、これもまだ安心なのでビーコン音を止めてしまいます。

    if(distance > 2.0 && distance <= 20.0){
        if(![avap isPlaying]){
            [avap play];
        }
        double vol = ((20.0f - distance + 2.0) / 20.0f) * 1.0f;
        avap.volume = vol;
        [avap setRate:((20.0f - distance) / 20.0f) * 5.0f];
    }else{
        [avap stop];
    }

画面に表示する内容は、以下のように出し分けることにしました。20m 以上、30m 未満の場合は、”気配を感じます”とだけ表示して、ビーコン音は鳴らさないようにしました。

    if(distance >= 30.0){
        _labelText.text = @"";
    }else if(distance >= 20.0 && distance < 30.0){
        _labelText.text = @"気配を感じます...";        
    }else if(distance >= 15.0 && distance < 20.0){
        _labelText.text = @"接近中!"; 
    }else if(distance >= 10.0 && distance < 15.0){
        _labelText.text = @"急接近中!!";        
    }else if(distance >= 5.0 && distance < 10.0){
        _labelText.text = @"絶対やばい!逃げろ!";            
    }else if(distance >= 2.0 && distance < 5.0){
        _labelText.text = @"捕まる直前だ!逃げろ!";        
    }else if(distance >= 0.0 && distance < 2.0){
        _labelText.text = @"捕まってしまった。。。";        
    }

上記実装まででほぼ完成しました!

あとは表示するラベルやボタンなどを作成して完成したのが以下のアプリになります。音も鳴ります。(たくさん数字が並んでいますが、実験の為につけたものですので無視してください)

 

ではさっそく umako 編集長に付けてみたいと思います。打ち合わせ中にこっそりつけてみました。

image3b.png

ちょっとヒヤヒヤしましたが、ビーコン取り付け完了です!

これで umako 編集長が近づいてきたらビーコン音が鳴り、逃げることが可能になるはずです。

image4b.png

ちょっと大げさに驚いていますが、上の画面のように umako 編集長が近づいてきたら接近中!と表示され、ゆるやかにビーコン音が鳴り出しました。

 

image5b.png

上の写真では方向がわからないので、完全に前向いちゃってますね。ビーコンの出力もこれだけ近づいているので高まるはずなのですが、背中につけているからか、距離が正確ではないです。。

image6b.png

捕まってしまいました。。距離は 3 m くらいの位置から急激にビーコン出力が高まり、音はけたたましくなったのですが、時既に遅しというところでしょうか。。

image7b.png

今回捕まってしまった反省も踏まえて、次回は、

  • 大体の方角がわかるようにビーコンを複数設置する。

  • ビーコンは天井に設置し、なるべく遮蔽物や電波による影響を受けないようにする。

  • 設置したビーコンを検出するように umako 編集長の iPhone にアプリを入れる。

  • レンジングを使い、 umako 編集長が範囲内に侵入したらプッシュで警告を出す。

  • ビーコン音は気づかれやすくなるので鳴らさない。

という観点で捕まらないようにしてみたいと思います。

※当記事における編集長像は演出上のイメージです。実在の人物とは大きく異なりますのでご注意ください。