Core BluetoothでBLE通信の2回目です。前回はBLE通信の仕様について簡単にですが確認しました。今回はそれを踏まえて実際にCore BluetoothでHello World的なペリフェラルを実装してみます。ここではペリフェラルをiPhone4S上で起動してセントラルはiPad miniで前回のBLExplrを使います。
Core Bluetoothの仕様詳細等は以下のマニュアル等を参照して下さい。
Core Bluetooth プログラミングガイド
今回のペリフェラルのアプリの画面は以下です。ペリフェラルの内容としてはサービスがひとつあり、そのサービスにキャラクタリスティックがひとつあります。そのキャラクタリスティックでNotifyを実行します。このNotityで単純な通信を確認するための処理のみ実行出来ます。細かい実装は行っていないテスト的なプログラムということでご了承下さい。
ソース一式は以下からダウンロード出来ます。(※テスト的なプログラムです。一切無保証ということをご了承下さい。)
ソース一式
実際のプログラムを見ながら動作も見ていきます。ViewControllerのヘッダファイルです。
#import <UIKit/UIKit.h> #import <CoreBluetooth/CoreBluetooth.h> @interface ViewController : UIViewController <CBPeripheralManagerDelegate,UITextFieldDelegate> { CBPeripheralManager *manager; CBMutableCharacteristic *characteristic; CBMutableService *services; NSString* UUIDService; NSString* UUIDCharacteristics; } @property (weak, nonatomic) IBOutlet UIButton *btnAdvertising; @property (weak, nonatomic) IBOutlet UIButton *btnNotify; @property (weak, nonatomic) IBOutlet UITextField *txtNotify; @property (weak, nonatomic) IBOutlet UITextField *txtStatus; - (IBAction)onAdvertising:(id)sender; - (IBAction)onNotify:(id)sender; @end
CoreBluetoothに関係するクラスは、CBPeripheralManager、CBMutableCharacteristic、CBMutableServiceです。
初期処理の実装です。
- (void)viewDidLoad { [super viewDidLoad]; //ペリフェラルを初期化 ここでは、queueは使わない //ここでは初期化だけでサービスの追加はステート変更後に行う //このmanagerオブジェクトをペリフェラルとして以降使う manager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil]; //独自のサービスとキャラクタリスティスクを一つずつ定義 UUIDService = @"C52D11BC-C2EE-422D-955A-D834930CF567"; UUIDCharacteristics = @"F13167B2-1FAE-4E7D-9200-B2DFA35510F8"; btnNotify.enabled = false; btnAdvertising.enabled = false; txtNotify.delegate = self; }
コメントの通りですが、ここではペリフェラルの初期化だけ行います。このmanagerオブジェクトを既定のペリフェラルとして利用します。あと、uuidgenで生成したuuidを文字列で設定しておきます。
次にステート変更時のデリゲートでの処理です。
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{ switch (manager.state) { case CBPeripheralManagerStatePoweredOn: { CBUUID *sUDID = [CBUUID UUIDWithString:UUIDService]; CBUUID *cUDID = [CBUUID UUIDWithString:UUIDCharacteristics]; services = [[CBMutableService alloc]initWithType:sUDID primary:YES]; characteristic = [[CBMutableCharacteristic alloc]initWithType:cUDID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; services.characteristics = @[characteristic]; [peripheral addService:services]; } default: break; } }
実際にはステート変更時にはいろいろな処理が必要となってくると思いますが、ここではBluetoothが有効の時にサービスとキャラクタリスティスクの追加処理のみを行っています。Bluetoothをオンにしておくと、このデリゲートが呼ばれます。Bluetoothがオフの状態では「Bluetoothがをオンにして下さい」といったメッセージが表示されます。
このサービスの登録が完了すると、以下のサービス登録完了のデリゲートが呼ばれます。
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{ txtStatus.text = @"Service追加完了"; btnAdvertising.enabled = true; }
これで最初のスクリーンショットの状態になります。
ここでアドバータイズのボタンを有効します。このボタンでアドバータイズを開始するとセントラル(ここではiPad miniのBLExplr)が見つけることが出来ます。以下がアドバータイズ開始の処理とデリゲートです。
- (IBAction)onAdvertising:(id)sender { NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey : @"ISYJP", CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:UUIDService]]}; [manager startAdvertising:advertisingData]; btnAdvertising.enabled = false; } - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{ txtStatus.text = @"Advertis開始"; }
以下の画面のようになります。
今回のキャラクタリスティスクUUIDがあります。それを選択します。
ここでペリフェラルのSubscribe時のデリゲートが呼ばれます。以下です。
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic12{ [manager stopAdvertising]; NSString *stra = @"Init"; NSData *dataa = [stra dataUsingEncoding:NSUTF8StringEncoding]; bool isOK = [peripheral updateValue:dataa forCharacteristic:characteristic onSubscribedCentrals:nil]; if(isOK){ txtStatus.text = @"初回通知完了"; } else{ txtStatus.text = @"初回通知エラー"; } btnNotify.enabled = true; }
BLExplrに以下のように表示されます。”Init”という文字が表示されました。
ここでペリフェラルのNotify実行ボタンが有効になるので実行します。
Valueのところに何か文字を入力して送信することが出来ます。以下がペリフェラルのNotifyの処理です。
- (IBAction)onNotify:(id)sender { NSData *dataa = [txtNotify.text dataUsingEncoding:NSUTF8StringEncoding]; bool isOK = [manager updateValue:dataa forCharacteristic:characteristic onSubscribedCentrals:nil]; if(isOK){ txtStatus.text = @"通知処理完了"; } else{ txtStatus.text = @"通知処理エラー"; } }
ここではNotifyの通知が届いたかどうかは判定出来ないです。isOKでの判定は送信キューがフルの場合等にNOが返るようなので、その場合は以下が呼ばれるようです。ここでは何もしていないです。
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { //送信が再度可能になった場合に呼ばれる }
これでNotifyだけですが、ペリフェラルの通信が出来ました。次回はこのペリフェラルからのNotifyを受け取れるセントラルの方を実装してみます。