Bluetooth LEとiBeaconを使った、すれ違い通信

20
BLUETOOTH LE を使った、 すれ違い通信 2014.2.25 Bitz Co., Ltd.

description

第1回 MOSA Tech Meetingの発表資料。 GitHub https://github.com/murakami/workbook/tree/master/ios/Wibree Cocoa練習帳 http://www.bitz.co.jp/weblog/

Transcript of Bluetooth LEとiBeaconを使った、すれ違い通信

Page 1: Bluetooth LEとiBeaconを使った、すれ違い通信

BLUETOOTH LEを使った、 すれ違い通信

2014.2.25 Bitz Co., Ltd.

Page 2: Bluetooth LEとiBeaconを使った、すれ違い通信

•村上幸雄 •@m_yukio •ビッツ有限会社http://www.bitz.co.jp/

今回のサンプルコードは以下のURL. https://github.com/murakami/workbook/tree/master/ios/Wibree

Page 3: Bluetooth LEとiBeaconを使った、すれ違い通信

Bluetoothは、省電力な電波を使った無線通信

で、最新の4.xでは対応機器は次の3つに分類されます。

分類 説明

Bluetooth Smart4.0で追加されたBluetooth Low Energyのみ対応。

Bluetooth Smart ReadyBluetooth LEと従来のBluetoothの両方に対応。

Bluetooth 従来のBluetoothのみ対応。

Page 4: Bluetooth LEとiBeaconを使った、すれ違い通信

iOSでBluetoothに対応する方法を整理してみます。

Bluetoothの種類 説明

従来のBLuetooth

MFi機器に対してExternal Accessory Frameworkで通信。

Game Kit

Bluetooth LE Core Bluetooth Framework

iOSでは、無関係の機器と自由に通信したいのならBluetooth LEという事になるかと思います。

Page 5: Bluetooth LEとiBeaconを使った、すれ違い通信

すれ違い通信

識別子 識別子

識別子を交換

Bluetooth LE

Page 6: Bluetooth LEとiBeaconを使った、すれ違い通信

Peripheral

Advertise

Central 発見

Service UUID

Peripheralが対応しているService UUIDをAdvertiseする。

Centralは探しているService UUIDがないか調査する。

Centralが見つけられたら、Peripheralに対して接続要求を出し、受けいれるとデータ通信が可能になる。

サンプルコードでは、識別子のCharacteristic UUIDを問い合わせて、識別子を受け取っている。

Page 7: Bluetooth LEとiBeaconを使った、すれ違い通信

SAMPLE CODE

Page 8: Bluetooth LEとiBeaconを使った、すれ違い通信

Central Managerの用意self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];

Peripheralを探す

- (void)centralManagerDidUpdateState:(CBCentralManager *)central { if (central.state != CBCentralManagerStatePoweredOn) { return; } [self scan]; } !- (void)scan { [self.centralManager scanForPeripheralsWithServices: @[[CBUUID UUIDWithString:WIBREE_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }]; }

探すサービスUUID

Page 9: Bluetooth LEとiBeaconを使った、すれ違い通信

Peripheralが見つかったので接続する- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { if (self.discoveredPeripheral != peripheral) { self.discoveredPeripheral = peripheral; [self.centralManager connectPeripheral:peripheral options:nil]; } }

サービスUUIDで検索- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { [self.centralManager stopScan]; [self.data setLength:0]; peripheral.delegate = self; [peripheral discoverServices:@[[CBUUID UUIDWithString:WIBREE_SERVICE_UUID]]]; }

Page 10: Bluetooth LEとiBeaconを使った、すれ違い通信

キャラクタリスティックUUIDで検索- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error

{ if (error) { [self cleanup]; return; } for (CBService *service in peripheral.services) { [peripheral discoverCharacteristics: @[[CBUUID UUIDWithString:WIBREE_CHARACTERISTIC_UUID]] forService:service]; } } !- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if (error) { [self cleanup]; return; } for (CBCharacteristic *characteristic in service.characteristics) { if ([characteristic.UUID isEqual:[CBUUID UUIDWithString: WIBREE_CHARACTERISTIC_UUID]]) { [peripheral setNotifyValue:YES forCharacteristic:characteristic]; } } }

Page 11: Bluetooth LEとiBeaconを使った、すれ違い通信

識別子を受け取る- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding]; if ([stringFromData isEqualToString:@"EOM"]) { NSString *uniqueIdentifier = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ [self _notifyParserDidDiscoverUUID:uniqueIdentifier]; }); [peripheral setNotifyValue:NO forCharacteristic:characteristic]; [self.centralManager cancelPeripheralConnection:peripheral]; } [self.data appendData:characteristic.value]; } !- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:WIBREE_CHARACTERISTIC_UUID]]) { return; } if (! characteristic.isNotifying) { [self.centralManager cancelPeripheralConnection:peripheral]; } }

Page 12: Bluetooth LEとiBeaconを使った、すれ違い通信

Peripheral Managerの用意self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; [self.peripheralManager startAdvertising: @{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:WIBREE_SERVICE_UUID]] }];

Peripheralが利用可能

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { if (peripheral.state != CBPeripheralManagerStatePoweredOn) { return; } self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:WIBREE_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:WIBREE_SERVICE_UUID] primary:YES]; transferService.characteristics = @[self.transferCharacteristic]; [self.peripheralManager addService:transferService]; }

見つけてもらうサービスUUID

Page 13: Bluetooth LEとiBeaconを使った、すれ違い通信

識別子の送信- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { self.dataToSend = [[Document sharedDocument].uniqueIdentifier dataUsingEncoding:NSUTF8StringEncoding]; self.sendDataIndex = 0; [self sendData]; } !- (void)sendData { static BOOL sendingEOM = NO; if (sendingEOM) { BOOL didSend = [self.peripheralManager updateValue:[@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; if (didSend) sendingEOM = NO; return; } if (self.sendDataIndex >= self.dataToSend.length) return; BOOL didSend = YES; while (didSend) { NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex; if (amountToSend > NOTIFY_MTU) amountToSend = NOTIFY_MTU; NSData *chunk = [NSData dataWithBytes:self.dataToSend.bytes+self.sendDataIndex length:amountToSend]; didSend = [self.peripheralManager updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; if (!didSend) return; NSString *stringFromData = [[NSString alloc] initWithData:chunk encoding:NSUTF8StringEncoding]; self.sendDataIndex += amountToSend; if (self.sendDataIndex >= self.dataToSend.length) { sendingEOM = YES; BOOL eomSent = [self.peripheralManager updateValue: [@"EOM" dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; if (eomSent) sendingEOM = NO; return; } } } !- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral { [self sendData]; }

Page 14: Bluetooth LEとiBeaconを使った、すれ違い通信

バックグラウンドで動かすには

Info.plistのRequired background modesでApp

communicates using CoreBluetoothを設定。

Info.plistのRequired background modesでApp

shares data using CoreBluetoothを設定。

Page 15: Bluetooth LEとiBeaconを使った、すれ違い通信

Core Bluetooth使用時の課題

数分単位の周期でしたバックグラウンドで動かないし、動いても数秒。

なので、実際のすれ違い通信は難しい。

Page 16: Bluetooth LEとiBeaconを使った、すれ違い通信

iBeaconを試してみる。

CoreBluetoothでビーコンを実装。

CoreLocationでビーコンを検出。

常に検出できるみたい。

Page 17: Bluetooth LEとiBeaconを使った、すれ違い通信

/* CLLocationManagerを生成 */ self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; if (! self.locationManager) { /* CLLocationManagerの初期化失敗 */ self.state = kBeaconCentralStateError; self.error = [self _errorWithCode:kBeaconCentralResponseParserGenericError localizedDescription:@"CLLocationManagerの初期化に失敗しました。"]; return; } !/* ビーコン領域を生成 */ NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:BEACON_SERVICE_UUID]; self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid                 identifier:@"demo.Wibree.BeaconCentralResponseParser"]; if (! self.beaconRegion) { /* ビーコン領域の初期化失敗 */ self.state = kBeaconCentralStateError; self.error = [self _errorWithCode:kBeaconCentralResponseParserGenericError localizedDescription:@"ビーコン領域の初期化に失敗しました。"]; self.locationManager = nil; return; } !/* ビーコン領域の出入りを監視 */ [self.locationManager startMonitoringForRegion:self.beaconRegion]; !/* 距離を監視 */ [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];

iBeaconを探す

探すビーコンのUUID

Page 18: Bluetooth LEとiBeaconを使った、すれ違い通信

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { DBGMSG(@"%s", __func__); if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didEnterRegion:)]) { [self.delegate beaconCentralResponseParser:self didEnterRegion:region]; } } !- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { DBGMSG(@"%s", __func__); if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didExitRegion:)]) { [self.delegate beaconCentralResponseParser:self didExitRegion:region]; } } !-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region

{ DBGMSG(@"%s", __func__); if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didRangeBeacons:inRegion:)]) { [self.delegate beaconCentralResponseParser:self didRangeBeacons:beacons inRegion:region]; } } !- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error { DBGMSG(@"%s region:%@", __func__, region); DBGMSG(@"%s error:%@", __func__, error); }

検出 領域に入る

領域から外れる

距離を監視

Page 19: Bluetooth LEとiBeaconを使った、すれ違い通信

/* CBPeripheralManagerを生成 */ self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; if (! self.peripheralManager) { /* CBPeripheralManagerの初期化失敗 */ self.state = kBeaconPeripheralStateError; self.error = [self _errorWithCode:kBeaconPeripheralResponseParserGenericError localizedDescription:@"CBPeripheralManagerの初期化に失敗しました。"]; return; } !/* ビーコン領域を生成 */ NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:BEACON_SERVICE_UUID]; self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid major:12345 minor:67890 identifier:@"demo.Wibree.BeaconCentralResponseParser"]; if (! self.beaconRegion) { /* ビーコン領域の初期化失敗 */ self.state = kBeaconPeripheralStateError; self.error = [self _errorWithCode:kBeaconPeripheralResponseParserGenericError localizedDescription:@"ビーコン領域の初期化に失敗しました。"]; self.peripheralManager = nil; return; } !/* 告知開始 */ NSDictionary *dictionary = [self.beaconRegion peripheralDataWithMeasuredPower:nil]; [self.peripheralManager startAdvertising:dictionary];

見つけてほしいUUIDを告知

見つけてほしいビーコンのUUID

Page 20: Bluetooth LEとiBeaconを使った、すれ違い通信

iBeacon使用時の課題

個体の識別子はmajorとminorの番号。

ただし、Sample Codeでは取得できていない。