気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
iOS7で「iBeacon」が発表されたことで、ますます注目が集まっているBluetooth Low Energyですが、
iOSでは、iOS5から「Core Blutooth Framework」が提供され、BLE機器と接続するアプリを開発することが
出来るようになっています。
また、iOS6からはiOSのデバイスをBluetooth対応機器として、動作させるためのAPIも提供されています。
そこで本日は、iOSのデバイスをBluetooth対応機器として、動作させるサンプルコードを紹介します。

ペリファレルとセントラルについて


コードの紹介に入る前にBLEの基本について少しお話します。BLEは、クライアント-サーバ型の形式になっていて、
主にデータを発信するサーバの役割を担う端末をペリファレル(Peripherel)、ペリファレルからデータを提供される側を
セントラル(Central)と言います。

iPhoneアプリはは体温計などのペリファレルからデータを受信するセントラルとして機能するケースが多いと
思いますが、今回はデータを提供する側のペリファレルとして動作するアプリを開発してみます。

サービスとキャラクタリスティック


ペリファレルは、セントラルに対して、サービスと呼ばれる一連のデータと機能をまとめたものを提供します。
例えば、心拍系であれば心拍を計測し、そのデータを提供するサービスを提供します。一つのペリファレルが、複数のサービスを提供することも可能です。

サービスはキャラクタリスティックと呼ばれるサービスの詳細情報を持つ属性を持っています。例えば心拍系の場合、心拍や、心拍系の場所などを
キャラクタリスティックとして、持つことになると思います。

つまり、ペリファレルは、ペリファレル→サービス→キャラクタリスティックという階層構造を持っていることになります。

Core Bluetoothを利用したペリファレルの実装


最初に、「Link Binary with Libraries」で「CoreBluetooth.framework」を追加します。
そして、ペリファレルの機能を提供するクラスのヘッダで、CoreBluetooth/CoreBluetooth.hをインポートします。

今回は、ViewControllerに必要な機能を実装してみます。

#define SERVICE_UUID @"9EA08FC0-FFCF-46D1-ABE3-A1A726699681"
#define CHARACTERISTIC_UUID @"1E222259-9612-4139-B3CB-19BA1DFB9C0E"

@implementation ViewController
{
    CBPeripheralManager *_peripheralManager;
    CBMutableService *_service;
    CBCharacteristic *_characteristic;
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if(self){
        _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
        
        //サービスとキャラクタリスティックを構築する
        CBUUID *characteristicUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID];
        _characteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID
                                                                 properties:CBCharacteristicPropertyRead
                                                                 value:nil permissions:CBAttributePermissionsReadable];
        CBUUID *serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID];
        _service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
        _service.characteristics = @[_characteristic];
    }
    return self;
}

//Bluetoothの状態が変更されるとコールされる
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    NSLog(@"State Updated");
    
    if(peripheral.state == CBPeripheralManagerStatePoweredOn){
        NSLog(@"ready");        

        [_peripheralManager addService:_service];
    }
}

//サービスがペリファレルに追加されるとコールされる。
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
    if(error) {
        NSLog(@"Error adding Service:%@",[error localizedDescription]);
    }
    else{
        NSLog(@"Adding Service Successful");
    }
}

ペリファレルを実装するにあたり、中心的な役割を担うのが、CBPeripheralManagerです。initでこのクラスのインスタンスを作成しています。
ペリファレルに対して発生する様々なイベントを処理するCBPeripheralManagerDelegateプロトコルをViewControllerに採用して、
initのdelegateに指定しています。

続いて、サービスとキャラクタリスティックを構築します。サービスとキャラクタリスティックにはそれぞれUUIDを持たせる必要があります。
MACのターミナルでuuidgenを実行し、それを割り当てています。キャラクタリスティックのvalueにはnilを指定していますが、
もし、値を指定した場合、それがキャッシュされ、自動的にその値をセントラルに返すような挙動をします。
状況に応じて動的に値を返したい場合、必ずnilを指定しておく必要があります。

CBPeripheralManagerのインスタンスを生成すると、Bluetoothが利用可能な状態になり、 peripheralManagerDidUpdateState:が
コールされます。もし、端末がBLEをサポートしていないケースなどを考慮し、ここで、状態をチェックしてから、後続の処理を
継続する必要があります。
ここでは、Bluetoothが利用可能な状態になっていることを確認し、サービスをCBPeripheralManagerに追加しています。

サービスとキャラクタリスティックが準備できたところで、次に「アドバタイズ」を開始します。
アドバタイズとは、セントラルに対して、ペリファレルの存在と公開しているサービスの情報を送信する行為のことを言います。

このサンプルアプリでは、2つのボタンを設置して、アドバタイズの開始と終了をボタンが押されたタイミングで
行うようにしました。



-(IBAction)startAdvertisingTouched:(id)sender
{  
    //アドバタイズを開始する
    [_peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[_service.UUID],
         CBAdvertisementDataLocalNameKey:@"HelloPeripheral"}];
}

-(IBAction)stopAdvertisingTouched:(id)sender
{
    //アドバタイズを停止する
    [_peripheralManager stopAdvertising]; 
}

//アドバタイズが開始されるとコールされる
-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
    if(error){
        NSLog(@"Error Advertising:%@",[error localizedDescription]);
    }
    else{
        NSLog(@"Starting Advertising Successful");
    }
}

そして、最後にセントラルからの接続と要求を受けて、結果をセントラルに返す処理です。
セントラルから要求を受けると、peripheralManager:didReceiveReadRequest:がコールされます。

-(void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
{
    NSLog(@"Request received: %@ request = %@",request.characteristic.UUID.description,request.central.UUID);
    //キャラクタリスティックのUUIDが一致する場合
    if([request.characteristic.UUID isEqual:_characteristic.UUID]) {
        request.value = [@"Hello!" dataUsingEncoding:NSUTF8StringEncoding];
        [_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
        return;
    }
    
    [_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound];
}

今回は単純に「Hello!」という文字列をセントラルに返すだけの処理になっています。
要求されたキャラクタリスティックが自身が提供するものと一致していることを確認してから処理を行っています。
前述しましたがCBCharacteristicのインスタンスを生成する際、valueを指定しているとこのメソッドは
コールされず、自動的にそこで指定した値が返されることになります。

peripheralManager:didReceiveReadRequest:がコールされると必ずCBPeripheralManagerの
respondToRequest:withResult:をコールして、応答をセントラルに返す必要があります。

今回はセントラルに対してキャラクタリスティックの値を返却するサンプルでしたが、逆にセントラルからキャラクタリスティックの値を
更新する要求を処理することも可能です。また、セントラルの要求に応じて、継続的にキャラクタリスティックの値の変化をセントラルに
通知する処理を書くことも可能です。


コメント

コメントの投稿

管理者にだけ表示を許可する

トラックバック

http://csfun.blog49.fc2.com/tb.php/121-d45cbf07

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。