Bluetooth LEとiBeaconを使った、すれ違い通信
-
Upload
yukio-murakami -
Category
Technology
-
view
10.331 -
download
3
description
Transcript of Bluetooth LEとiBeaconを使った、すれ違い通信
• 村上幸雄 • @m_yukio • ビッツ有限会社���http://www.bitz.co.jp/
今回のサンプルコードは以下のURL. https://github.com/murakami/workbook/tree/master/ios/Wibree
" Bluetoothは、省電力な電波を使った無線通信で、最新の4.xでは対応機器は次の3つに分類されます。
分類 説明
Bluetooth Smart 4.0で追加されたBluetooth Low Energyのみ対応。
Bluetooth Smart Ready Bluetooth LEと従来のBluetoothの両方に対応。
Bluetooth 従来のBluetoothのみ対応。
" iOSでBluetoothに対応する方法を整理してみます。
Bluetoothの種類 説明
従来のBLuetooth
MFi機器に対してExternal Accessory Frameworkで通信。
Game Kit
Bluetooth LE Core Bluetooth Framework
iOSでは、無関係の機器と自由に通信したいのならBluetooth LEという事になるかと思います。
Peripheral
Advertise
Central 発見
Service UUID
" Peripheralが対応しているService UUIDをAdvertiseする。 " Centralは探しているService UUIDがないか調査する。 " Centralが見つけられたら、Peripheralに対して接続要求を出し、受けいれるとデータ通信が可能になる。
" サンプルコードでは、識別子のCharacteristic UUIDを問い合わせて、識別子を受け取っている。
Central Managerの用意 self.centralManager = [[CBCentralManager alloc] initWithDelegate:self ! queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
Centralが使えるようになったので、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
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]]]; !} !
利用するサービスUUID
キャラクタリスティックス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]; ! } ! } !}
利用するキャラクタリスティックスUUID
見つけたキャラクタリスティックスの値の通知を設定
識別子を受け取る
- (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]; ! } !}
状態は、通知解除
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
利用可能なサービスUUIDとキャラクタリスティックスUUID
識別子の送信 - (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]; !} !
サンプルコードで決めた終端データを送る
" バックグラウンドで動かすには
" Info.plistのRequired background modesでApp communicates using CoreBluetoothを設定。
" Info.plistのRequired background modesでApp shares data using CoreBluetoothを設定。
" iBeaconを試してみる。
" CoreBluetoothでビーコンを実装。
" CoreLocationでビーコンを検出。
" iBeaconはアドバタイズのパケットにiBeaconの情報を付与したもの。
" ビーコンUUIDというのを定義するので、これをサンプルのサービスUUIDとした。
" ペリフェラルにメジャー番号とマイナー番号を与えられるので、サンプルではこれを識別子とした。
/* 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
- (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); !} !
検出 領域に入る
領域から外れる
距離を監視
/* 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
メジャー番号とマイナー番号