Web雑記帳 atsu666


iBeaconsを触ってみた

March 14 Fri 2014 | メモ

iBeaconとは

iBeacon は iOS の 位置情報サービス を拡張する新しいテクノロジーです。iBeacon の設置場所に近づいたり離れたりすると、iOS デバイスから App に通知させることができます。位置のモニタリングのほか、App によって iBeacon (たとえば、Apple Store 直営店のディスプレイやレジカウンター) との距離を測定することもできます。iBeacon では、位置情報を明らかにするために緯度と経度ではなく、BLE (Bluetooth Low Energy) 信号を利用します。この信号を iOS デバイスが検出します。Bluetooth テクノロジーについて詳しくは、Bluetooth の公式 Web サイト を参照してください。

引用 : support.apple.com

iBeaconはiOS7より新たに追加されたBlutoothを使った位置情報サービスの新しいテクノロジーです。少し前から 興味はあったのですが、まだ触っていませんでした。 しかし、iOS7.1になって様々な改良(iOS7.1でのiBeaconが改善されまくった話)が加えられて、実用的に使えそうとの噂を聞きつけたのでちょっと触ってみようと思います。

目標

今回はまずはどんな物かを実際に触ってみるという所ですので、サービスなどは考えずにとりあえず動作させる所までをやってみたいと思います。

  • beacon端末が買えなかったので MBAとiPhone5sの両方をiBeacon端末にしてみます
  • 受信側はiOS7.1
  • 受信したら通知メッセージを出す簡単なもの

予備知識

概要

  • BLE4.0 (Bluetooth Low Energy) を利用した近距離無線通信
  • CoreLocation.frameworkを使用
  • 使えるのはiPhone4s以降、 iPadは第3世代以降(なのでiPad2は使えない模様)

用語

Advertise(アドバタイズ)
ある機器が別の機器に管理情報などを伝達する事
proximity UUID
128bitのUUIDで表現される識別子(組織単位で使用される事を想定)
major
16bit 同一 proximity UUIDを持つiBeaconの識別子として使用
minor
16bit 同一 proximity UUIDとmajorを持つiBeaconの識別子として使用
リージョン(UUID)監視
Beacon領域の出入をチェック
Ranging(レンジング)
エリア内のBeacon情報を取得(距離測定)

major,minorは省略可能だがこれらを使ってグルーピング、カテゴライズする事が出来る

iBeaconsで出来る事

  • リージョン iBeaconの領域観測(範囲に入ったか出たか)
  • Beaconより情報を取得(プッシュ通知)
  • Ranging 距離観測

OS X を Beacon端末に

まずはOS XでiBeacon端末化します。本当は端末を買いたかったですが、それなりに値段もしますし(というか売り切れ。。)簡単に出来るようなのでやってみます。

とは言っても、OSXをiBeacon端末化する素晴らしい記事があったので、そちらを参考にOSXアプリを作るだけです。

Mac を Beacon にしたい!@dev.classmethod.jp

アプリを作ったら、proximity UUIDを固有の物にしないといけませんので、ターミナルでUUIDを生成してコードに埋め込みます。

% uuidgen
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// UUIDを適当に作成(uuidgenコマンドで生成する)
        NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"];
        
        // アドバタイズ用のデータを作成
        MBCBeaconAdvertisementData *beaconData
        = [[MBCBeaconAdvertisementData alloc] initWithProximityUUID:proximityUUID
                                                              major:1
                                                              minor:1
                                                      measuredPower:-58];
        
        // アドバタイズ開始
        [peripheral startAdvertising:beaconData.beaconAdvertisement];

iOSをBeacon端末に

次にiOSをiBeacon端末化してみます。githubにソースをおいておくので、よかったら参考にして下さい。 atsu666/iOS-iBeaconSenderApp

iOSアプリでは、テスト用に使えるようにUUIDなどを固定せずにアプリ側で自由に設定できるようにしてみました。

CBPeripheralManagerDelegateインターフェイスを実装していきます。RequireなメソッドはperipheralManagerDidUpdateState:になります。

CBPeripheralManagerDelegate Protocol Reference


//----------------------------------------------------
// ViewController.h
//----------------------------------------------------

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface ViewController : UIViewController <CBPeripheralManagerDelegate>
@end

//----------------------------------------------------
// ViewController.m
//----------------------------------------------------

- (void)viewDidLoad
{
    [super viewDidLoad];
    // CBPeripheralManagerを作成
    self.manager    = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
    
    // アドバタイズを開始
    [self beginAdvertising];
}

- (void)beginAdvertising
{
    NSUUID      *uuid  = [[NSUUID alloc]initWithUUIDString:self.uuidField.text];
    uint16_t    major  = (uint16_t)[self.majorField.text integerValue];
    uint16_t    manor  = (uint16_t)[self.minorField.text integerValue];
    
    // uuid, major, minor, identifierを指定して BeconRegionを作成
    CLBeaconRegion *beacon      = [[CLBeaconRegion alloc]initWithProximityUUID:uuid
                                                                         major:major
                                                                         minor:manor
                                                                    identifier:self.identifierField.text];
    
    NSDictionary *beaconData    = [beacon peripheralDataWithMeasuredPower:nil];
    
    [self.manager stopAdvertising];
    [self.manager startAdvertising:beaconData];
}

//----------------------------------------------------------------
// Required Method ペリフェラルデバイスの状態を監視
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    switch (peripheral.state) {
        case CBPeripheralManagerStatePoweredOn:
            self.statusLabel.text = @"CBPeripheralManagerStatePoweredOn";
            break;
        case CBPeripheralManagerStatePoweredOff:
            self.statusLabel.text = @"CBPeripheralManagerStatePoweredOff";
            break;
        case CBPeripheralManagerStateResetting:
            self.statusLabel.text = @"CBPeripheralManagerStateResetting";
            break;
        case CBPeripheralManagerStateUnauthorized:
            self.statusLabel.text = @"CBPeripheralManagerStateUnauthorized";
            break;
        case CBPeripheralManagerStateUnknown:
            self.statusLabel.text = @"CBPeripheralManagerStateUnknown";
            break;
        case CBPeripheralManagerStateUnsupported:
            self.statusLabel.text = @"CBPeripheralManagerStateUnsupported";
            break;
    }
}

受信側アプリをiOSで作る

iBeacon端末が出来たので受信側のiOSアプリを作ります。機能的にはiOSの通知機能を使う事と取得情報を出力する 簡単なものになります。

こちらもgithubに公開しときます。 atsu666/iOS-BeaconReceiverApp

CLLocationManagerDelegateインターフェイスを実装していきます。 重要なデリゲートメソッドはlocationManagerDidPauseLocationUpdates:になります。 ここで、ビーコンを監視して処理を書いていきます。

CLLocationManagerDelegate Protocol Reference

//----------------------------------------------------
// ViewController.h
//----------------------------------------------------

#import <UIKit/UIKit.h>
@import CoreLocation;

@interface ViewController : UIViewController <CLLocationManagerDelegate>
@end

//----------------------------------------------------
// ViewController.m
//----------------------------------------------------

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    if ( [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]] ) {
        
        // Managerを作成
        self.manager            = [CLLocationManager new];
        self.manager.delegate   = self; // デリゲートを設定
        
        // Beacon情報を設定(ここで設定した情報のビーコンを監視します)
        self.proximityUUID      = [[NSUUID alloc]initWithUUIDString:UUID];
        self.identifier         = IDENTIFIER;
        self.major              = (uint16_t)[MAJOR integerValue];
        self.minor              = (uint16_t)[MINOR integerValue];
        
        // Beacon情報を元にリージョンを作成
        self.region = [[CLBeaconRegion alloc]initWithProximityUUID:self.proximityUUID
                                                             major:self.major
                                                             minor:self.minor
                                                        identifier:self.identifier];
        self.region.notifyOnEntry               = YES; // 領域に入った事を監視
        self.region.notifyOnExit                = YES; // 領域を出た事を監視
        self.region.notifyEntryStateOnDisplay   = NO; // デバイスのディスプレイがオンのとき、ビーコン通知が送信されないように設定
        
        [self.manager startMonitoringForRegion:self.region]; // 領域監視を開始
        [self.manager startRangingBeaconsInRegion:self.region]; // iBeaconとの距離測定を開始
    }
}

#pragma mark CLLocationManagerDelegate

//-------------------------------------
// 領域に入った時
- (void)locationManager:(CLLocationManager *)manager
         didEnterRegion:(CLRegion *)region
{
    [self sendNotification:@"ようこそ!"];
}

//-------------------------------------
// 領域から出た時
- (void)locationManager:(CLLocationManager *)manager
          didExitRegion:(CLRegion *)region
{
    [self sendNotification:@"さようなら!"];
}

//--------------------------------------------------------------
// iBeaconを監視
- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region
{
    // init
    NSString *uuid                          = @"unknown";
    CLProximity proximity                   = CLProximityUnknown;
    CLLocationAccuracy accuracy             = 0.0;
    NSInteger rssi                          = 0;
    NSNumber *major                         = @0;
    NSNumber *minor                         = @0;
    
    // near beacon
    CLBeacon *beacon    = beacons.firstObject;
    
    uuid                = beacon.proximityUUID.UUIDString;
    proximity           = beacon.proximity;
    accuracy            = beacon.accuracy;
    rssi                = beacon.rssi;
    major               = beacon.major;
    minor               = beacon.minor;
    
    // update view
    self.uuidLabel.text         = beacon.proximityUUID.UUIDString;
    self.majorLabel.text        = [NSString stringWithFormat:@"%@", major];
    self.minorLabel.text        = [NSString stringWithFormat:@"%@", minor];
    self.accuracyLabel.text = [NSString stringWithFormat:@"%f", accuracy];
    self.rssiLabel.text = [NSString stringWithFormat:@"%ld", (long)rssi];
    
    switch (proximity) {
        case CLProximityUnknown:
            self.proximityLabel.text    = @"CLProximityUnknown";
            break;
        case CLProximityImmediate:
            self.proximityLabel.text    = @"CLProximityImmediate";
            break;
        case CLProximityNear:
            self.proximityLabel.text    = @"CLProximityNear";
            break;
        case CLProximityFar:
            self.proximityLabel.text    = @"CLProximityFar";
            break;
        default:
            break;
    }
    
    if ( proximity == CLProximityUnknown ) {
        self.beconStateLabel.text   = @"UNKNOWN";
    } else {
        self.beconStateLabel.text   = @"ENTER";
    }
    
    //-------------------------------------------------------------
    // iBeaconの電波強度を調べて、近距離に来た場合 
    if ( proximity == CLProximityImmediate && rssi > -40 ) {
        self.beconStateLabel.text   = @"TOUCH";
    }
}

実際に動かしてみて

結構、精度高く動いてくれている印象でした。 領域に入った時には瞬時に反応して、電波強度(RSSI)や精度(Accuracy)から距離も大体とれるので、 タッチした時など出来そうです。 ただ、やはり領域から出たときの反応は30-40秒ほどタイムラグがあって通知がされました。

また、アプリを完全に落とした状態でもiOS7.1だとちゃんと通知がされましたので、 サービスとして問題なく使えそうです。

という事でiBeaconを触ってみました。 なんかiBeaconを利用したアプリ作りたいですね。


参考にした記事


tag : iBeacon iOS