vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

MacとiOSをBLEで連携 - Central編

次はメインのコントローラー側となるCentralについてのメモ。

実行すること

  • Peripheral端末を探索して接続する。
  • 接続したPeripheral端末が値を更新した場合、通知(notify)を受けられるようにする。
  • 接続したPeripheral端末にWriteリクエストを送ってデータを渡す。

Central

iOSでBLEを利用するには、CoreBluetooth.frameworkをインポートします。

Peripheralと同様、UI部分はViewControllerで、BLEの使用部分はCentralControllerで実装しています。

CentralController.h

#import < Foundation / Foundation.h >

@interface CentralController : NSObject
- (void) initCentralController;
- (void) sendValue:(int)intSendValue;
- (NSString *) getPeripheralValue;
- (BOOL) getIsValueWrote;
@end

CentralController.m

#import "CentralController.h"
@import CoreBluetooth;

#define CHARACTERISTIC_UUID @"7F855F82-9378-4508-A3D2-CD989104AF22"
#define SERVICE_UUID @"2B1DA6DE-9C29-4D6C-A930-B990EA2F12BB"

@interface CentralController() 
@property (strong, nonatomic) CBCentralManager      *ccmCentralManager;
@property (strong, nonatomic) CBPeripheral          *prpDiscovered;
@property (strong, nonatomic) CBCharacteristic      *chrDiscoveredChacteristic;
@property (strong, nonatomic) NSMutableData         *mdtSendValue;
@property (strong, nonatomic) NSString              *strGotValue;
@property (nonatomic) BOOL                          isValueWrote;
@end
@implementation CentralController
- (void) initCentralController
{
    // CentralManagerの初期化.
    _ccmCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    
    _strGotValue = @"";
    _isValueWrote = NO;
}
- (void)sendValue:(int)intSendValue
{
    // Peripheralへの書き込みリクエスト.
    _mdtSendValue = (NSMutableData *)[[NSString stringWithFormat:@"%d", intSendValue] dataUsingEncoding:NSUTF8StringEncoding];
    [_prpDiscovered writeValue:_mdtSendValue forCharacteristic:_chrDiscoveredChacteristic type:CBCharacteristicWriteWithResponse];
}
- (NSString *) getPeripheralValue
{
    // Peripheralから受け取った値を返す.
    return _strGotValue;
}
- (BOOL) getIsValueWrote
{
    // 書き込みに成功したかを返す.
    return _isValueWrote;
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    // BluetoothがOffならリターン.
    if (central.state != CBCentralManagerStatePoweredOn)
    {
        return;
    }
    [self scanNewDevice];
}
- (void) scanNewDevice
{
    // Scanの開始. 重複したAdvertisingを受け取らないようにする.
    [_ccmCentralManager scanForPeripheralsWithServices:@CBUUID UUIDWithString:SERVICE_UUID
                                                options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @NO}];
}
// Peripheralが見つかったら実行.
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    // 未接続のPeripheralのみ追加.
    if (_prpDiscovered != peripheral)
    {
        _prpDiscovered = peripheral;
        // Peripheralに接続する.
        [_ccmCentralManager connectPeripheral:peripheral options:nil];
    }
}
// Peripheralに接続されたら実行.
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    // 接続されたらScanをストップ.
    [_ccmCentralManager stopScan];
    
    peripheral.delegate = self;
    
    // Serviceを探索する.
    [peripheral discoverServices:@CBUUID UUIDWithString:SERVICE_UUID];
}
// 接続したPeripheralでServiceが見つかったら実行.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error)
    {
        // Errorならリターン.
        NSLog(@"Service Error: %@", [error localizedDescription]);
        return;
    }
    
    // Characteristicの探索.
    for (CBService *service in peripheral.services)
    {
        [peripheral discoverCharacteristics:@CBUUID UUIDWithString:CHARACTERISTIC_UUID forService:service];
    }
}
// 見つかったServiceからCharacteristicが見つかったら実行.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error)
    {
        // Errorならリターン.
        NSLog(@"Characteristics Error: %@", [error localizedDescription]);
        return;
    }
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        // 見つかったCharacteristicからUUIDの合致しているものを検出.
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]])
        {
            _chrDiscoveredChacteristic = characteristic;
            // Peripheralで値が更新されたら、通知が届くようにする.
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
}
// 書き込みリクエストを送信して、返答があれば実行.
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic*)characteristic error:(NSError*)error
{
    if (error)
    {
        NSLog(@"WriteValue Error: %@", [error localizedDescription]);
        _isValueWrote = NO;
    }
    else
    {
        _isValueWrote = YES;
    }
}
// Peripheralからデータ更新の通知が届いたら実行.
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        NSLog(@"UpdateValue Error: %@", [error localizedDescription]);
        return;
    }
    _strGotValue = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
}
// Peripheralとの接続が切れたら実行.
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    // 接続したPeripheralを破棄してScanの再実行.
    _prpDiscovered = nil;
    [self scanNewDevice];
}
@end

CBCentralManagerの初期化時に、オプションにCBCentralManagerOptionRestoreIdentifierKeyを設定してできるようですが、Peripheral端末であるMacでその設定ができなかったため、今回は行っていません。

Characteristic, Serviceを検出するのに使用するUUIDは、前々回のPeripheral端末と同じものを使用します。

ViewControllerから呼び出しが必要なのは、初期化、書き込みリクエスト、Peripheralの値などを取得するメソッドです。

UI部分は次回。

参考

BLE

参考書籍

上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編