株式会社インデペンデンスシステムズ横浜

システム開発エンジニアの西田五郎が運営しております。新規開発案件のご依頼をお待ちしております。

*

Core BluetoothでBLE通信(その3)セントラル

      2015/03/03

Core BluetoothでBLE通信(Bluetooth Low Energy通信)の3回目です。前回はペリフェラルを実装してみました。今回は前回のペリフェラルと通信をするためのセントラルを実装してみます。今回はまずはいわゆるHello World的なプログラムのレベルです。ペリフェラルからのNotifyを受信するのみの機能です。試してみたところ前回のペリフェラルと両方を1台のiOSで動作させることは出来ませんでした。このあたりはご了承ください。

Core Bluetoothの仕様詳細等は以下のマニュアル等を参照して下さい。
Core Bluetooth プログラミングガイド

ソース一式は以下からダウンロード出来ます。(※テスト的なプログラムです。一切無保証ということをご了承下さい。)
ソース一式

今回のアプリの画面は以下です。iPad miniを使っています。
IMG_0185

画面と合わせてプログラムを順番に書いていきます。

ViewControllerのヘッダファイルです。

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

@interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>
{
    //セントラル
    CBCentralManager *centralManager;
    
    //ペリフェラル
    CBPeripheral *targetPeripheral;
      
    //ターゲットペリフェラル
    NSString* UUIDService;
    NSString* UUIDCharacteristics;
}

//画面関連
@property (weak, nonatomic) IBOutlet UIButton *btnScan;

@property (weak, nonatomic) IBOutlet UIButton *btnConnect;

@property (weak, nonatomic) IBOutlet UIButton *btnClose;

@property (weak, nonatomic) IBOutlet UITextField *txtNotifyData;

@property (weak, nonatomic) IBOutlet UITextField *txtStatus;

- (IBAction)OnBtnScan:(id)sender;

- (IBAction)OnBtnClose:(id)sender;

- (IBAction)OnBtnConnect:(id)sender;

@end

上記画像の初期化完了までの処理です。
まずは初期化です。CBCentralManagerの初期化ですが、バックグラウンドキューにはnilを指定しています。これでmainキューを使うことになります。ここではBLE通信を確認することが目的ということでmainキューを使うことにしました。uuidの文字列は前回のペリフェラルで実装したサービスとキャラクタリスティスクの文字列です。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //Centralの初期処理
    centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    [centralManager setDelegate:self];
    
    //画面の初期化
    btnScan.enabled = false;
    btnConnect.enabled = false;
    btnClose.enabled = false;
    
    txtStatus.text = @"";
    txtNotifyData.text = @"";
    
    //ペリフェラルUUIDの設定
    UUIDService = @"C52D11BC-C2EE-422D-955A-D834930CF567";
    UUIDCharacteristics = @"F13167B2-1FAE-4E7D-9200-B2DFA35510F8";
}

次にステート変更時の処理です。実際にはいろいろと考慮することがあると思いますが、有効になったらscanボタンを使えるようにします。このステート変更後(CBCentralManagerStatePoweredOn)に実際のセントラルの処理が可能になります。

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{ 
    if(central.state == CBCentralManagerStatePoweredOn) {
        //scanボタンを有効にする
        btnScan.enabled = true;
        txtStatus.text = @"初期化完了";
    }
}

ここで、ペリフェラルでアドバータイズを実行します。(※プログラムは前回を参照して下さい。)

セントラルでScan実行を開始します。以下のように検知出来ると思います。
Scan実行ボタンでScan開始
IMG_0185(1)

Scan完了の状態です。
IMG_0193

Scan処理のセントラルのプログラムは以下です。
Scan実行時の処理です。サービスを指定してスキャンを開始しています。

- (IBAction)OnBtnScan:(id)sender {
    
    btnScan.enabled = false;
    txtStatus.text = @"スキャン中";
    
    CBUUID *uuid = [CBUUID UUIDWithString:UUIDService];
    NSArray *services = [NSArray arrayWithObjects:uuid,nil];
    
    [centralManager scanForPeripheralsWithServices:services
        options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:YES]}];
}

スキャン完了時の処理です。ペリフェラルを保持して、GUIを更新します。これで接続実行ボタンが有効になります。

- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    //スキャンの終了
    [centralManager stopScan];
    
    //ペリフェラルを保持する(ここではペリフェラルは単独)
    targetPeripheral = aPeripheral;
    targetPeripheral.delegate = self;
    
    btnConnect.enabled = true;
    txtStatus.text = @"ペリフェラル検知";
}

接続を実行します。問題がなければ以下の画像のようになります。

IMG_0194

接続処理と接続完了後の処理です。まず接続処理です。

- (IBAction)OnBtnConnect:(id)sender {
    btnConnect.enabled = false;
    txtStatus.text = @"サービススキャン中";
    
    [centralManager connectPeripheral:targetPeripheral options:nil];
}

接続完了後の処理です。ここではとりあえず全てのサービスを検索しています。

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
  //サービスを探す
  [peripheral discoverServices:nil];
}

サービス検知時の処理です。ここではキャラクタリスティスクを指定して検索しています。

- (void) peripheral:(CBPeripheral *)aPeripheral didDiscoverServices:(NSError *)error {
    
    for (CBService *aService in aPeripheral.services){
        if ([aService.UUID isEqual:[CBUUID UUIDWithString:UUIDService]]) {
            [aPeripheral discoverCharacteristics:@[[CBUUID UUIDWithString:UUIDCharacteristics]]
                                      forService:aService];
        }
    }
}

キャラクタリスティスク検知時の処理です。

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service
                    error:(NSError *)error
{
    /// Characteristic に対して Notify を受け取れるようにする
    for (CBService *service in peripheral.services) {
        for (CBCharacteristic *characteristic in service.characteristics) {
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
    
    txtStatus.text = @"Notify受付中";
    btnClose.enabled = true;
}

Notifyを受け取る処理です。初回はすぐにNotifyが返ってきます。

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error
{
    txtStatus.text = @"Notify完了";
    
    NSString *stringFromData =
        [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
    
    txtNotifyData.text = stringFromData;
}

ここでペリフェラルからNotifyを実行してみます。そうするとセントラルで更新されます。

以下はペリフェラルの画面です。Notifyを実行ボタンでNotifyが実行されます。
IMG_0320

こちらがセントラルの画面です。上のペリフェラルのValueという文字列を受け取っています。
IMG_0196

最後にペリフェラルとの接続の切断処理です。

- (IBAction)OnBtnClose:(id)sender {
    [centralManager cancelPeripheralConnection:targetPeripheral];
    btnClose.enabled = false;
}

これで一通りの処理が完了です。再度実行する場合は強制終了して起動します。

今回はNotifyだけですが、BLEの通信の確認が出来ました。次の段階としては、Read、Writeの処理もありますし、別のプラットフォーム(Android、Windows等)でのBLEでの通信プログラムということもあると思います。今回のシリーズとしてはここまでですが、また出来たら公開したいと思います。


 - Bluetooth Low Energy, iOS

AdSense

AdSense

  関連記事

SHARP製スマホSHL23でBLE通信を試してみた

SHARP製のスマホSHL23でBLE(Bluetooth Low Energy …

Core BluetoothでBLE通信(その1)BLEの仕様概要

iOSでCore Bluetoothを使ってBLE(Bluetooth Low …

Core BluetoothでBLE通信(その2)ペリフェラル

Core BluetoothでBLE通信の2回目です。前回はBLE通信の仕様につ …