bluetooth leとibeaconを使った、すれ違い通信
DESCRIPTION
第1回 MOSA Tech Meetingの発表資料。 GitHub https://github.com/murakami/workbook/tree/master/ios/Wibree Cocoa練習帳 http://www.bitz.co.jp/weblog/TRANSCRIPT
![Page 1: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/1.jpg)
BLUETOOTH LEを使った、 すれ違い通信
2014.2.25 Bitz Co., Ltd.
![Page 2: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/2.jpg)
•村上幸雄 •@m_yukio •ビッツ有限会社http://www.bitz.co.jp/
今回のサンプルコードは以下のURL. https://github.com/murakami/workbook/tree/master/ios/Wibree
![Page 3: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/3.jpg)
Bluetoothは、省電力な電波を使った無線通信
で、最新の4.xでは対応機器は次の3つに分類されます。
分類 説明
Bluetooth Smart4.0で追加されたBluetooth Low Energyのみ対応。
Bluetooth Smart ReadyBluetooth LEと従来のBluetoothの両方に対応。
Bluetooth 従来のBluetoothのみ対応。
![Page 4: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/4.jpg)
iOSでBluetoothに対応する方法を整理してみます。
Bluetoothの種類 説明
従来のBLuetooth
MFi機器に対してExternal Accessory Frameworkで通信。
Game Kit
Bluetooth LE Core Bluetooth Framework
iOSでは、無関係の機器と自由に通信したいのならBluetooth LEという事になるかと思います。
![Page 5: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/5.jpg)
すれ違い通信
識別子 識別子
識別子を交換
Bluetooth LE
![Page 6: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/6.jpg)
Peripheral
Advertise
Central 発見
Service UUID
Peripheralが対応しているService UUIDをAdvertiseする。
Centralは探しているService UUIDがないか調査する。
Centralが見つけられたら、Peripheralに対して接続要求を出し、受けいれるとデータ通信が可能になる。
サンプルコードでは、識別子のCharacteristic UUIDを問い合わせて、識別子を受け取っている。
![Page 7: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/7.jpg)
SAMPLE CODE
![Page 8: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/8.jpg)
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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/9.jpg)
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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/10.jpg)
キャラクタリスティック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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/11.jpg)
識別子を受け取る- (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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/12.jpg)
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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/13.jpg)
識別子の送信- (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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/14.jpg)
バックグラウンドで動かすには
Info.plistのRequired background modesでApp
communicates using CoreBluetoothを設定。
Info.plistのRequired background modesでApp
shares data using CoreBluetoothを設定。
![Page 15: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/15.jpg)
Core Bluetooth使用時の課題
数分単位の周期でしたバックグラウンドで動かないし、動いても数秒。
なので、実際のすれ違い通信は難しい。
![Page 16: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/16.jpg)
iBeaconを試してみる。
CoreBluetoothでビーコンを実装。
CoreLocationでビーコンを検出。
常に検出できるみたい。
![Page 17: Bluetooth LEとiBeaconを使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/17.jpg)
/* 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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/18.jpg)
- (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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/19.jpg)
/* 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を使った、すれ違い通信](https://reader031.vdocuments.mx/reader031/viewer/2022013111/556607f0d8b42a06318b45eb/html5/thumbnails/20.jpg)
iBeacon使用時の課題
個体の識別子はmajorとminorの番号。
ただし、Sample Codeでは取得できていない。