Takahiro Octopress Blog

-1から始める情弱プログラミング

iOS端末同士でBluetoothペアリングするときの注意点

iOS端末同士でのBluetoothペアリング方法について

さて、本日はiOS端末同士でBluetoothペアリングするときの注意点について書きたいと思います。
筆者は仕様に気づかずにだいぶ苦しめられました笑。

始めにおさらいの意味も兼ねて、Bluetoothでのペアリングについて書いていきます。
今回はSwiftで書きます。

まずはBluetoothの発信側から説明します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBPeripheralManagerDelegate {
  
  // 必要なプロパティの宣言
  var pm:CBPeripheralManager!
  let sUUID:CBUUID = CBUUID(string:"47EC089D-2FBB-410D-BEDF-C88730DBBD3A")
  var characteristic:CBMutableCharacteristic!

  override func viewDidLoad() {
      super.viewDidLoad()

      // CBPeripheralManagerの初期化
      self.pm = CBPeripheralManager(delegate: self, queue: nil, options: nil)
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
  }

  // ボタンをタップしたときに実行
  @IBAction func peripheralStartAction(sender: AnyObject) {
      // サービスの追加
      let service = CBMutableService(type: self.sUUID, primary:true)
      self.characteristic = CBMutableCharacteristic(type: sUUID, properties: CBCharacteristicProperties.Read, value: nil, permissions: CBAttributePermissions.ReadEncryptionRequired)
      service.characteristics = [self.characteristic]
      self.pm.addService(service)

      // アドバタイズ開始
      let advertisementData:[String:AnyObject] = [CBAdvertisementDataServiceUUIDsKey: [self.sUUID], CBAdvertisementDataLocalNameKey:"MyBlog"]
      self.pm.startAdvertising(advertisementData)
  }

  // MARK: - CBPeripheralManagerDelegate -
  // Bluetoothの状態が変化したときに通る処理
  func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) {
      print("state: \(peripheral.state)")
  }

  // アドバタイズの開始結果を取得したときに通る処理
  func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?) {
      if(error != nil) {
          print("アドバタイズ開始失敗。error: \(error)")
          return
      }
                                                      
      print("アドバタイズ開始成功!")
  }

  // サービスの追加結果を取得したときに通る処理
  func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?) {
      if(error != nil) {
          print("サービスの追加に失敗しました。error: \(error)")
          return
      }
                                                      
      print("サービス追加成功")
  }

  // Readリクエストを取得したときに通る処理
  func peripheralManager(peripheral: CBPeripheralManager, didReceiveReadRequest request: CBATTRequest) {
                  
      if request.characteristic.UUID.isEqual(self.characteristic.UUID) {
          // プロパティで保持しているキャラクタリスティックへのReadリクエストかどうかを判定
          // CBMutableCharacteristicのvalueをCBATTRequestのvalueにセット
          request.value = self.characteristic.value
                                                                          
          // リクエストに応答
          self.pm.respondToRequest(request, withResult: CBATTError.Success)
      }
  }
}

ここで注目して頂きたいのが、
self.characteristic = CBMutableCharacteristic(type: sUUID, properties: CBCharacteristicProperties.Read, value: nil, permissions: CBAttributePermissions.ReadEncryptionRequired)の部分です。
permissionsCBAttributePermissions.ReadEncryptionRequiredを設定することで下図のようなペアリングアラートが表示されます。

Bluetoothペアリングアラート

続いて、Bluetoothの受信側の処理を書きます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
  
  // 必要なプロパティの宣言
  var cm:CBCentralManager!
  var connectPeripheral:CBPeripheral!
  let sUUID:CBUUID = CBUUID(string:"47EC089D-2FBB-410D-BEDF-C88730DBBD3A")

  override func viewDidLoad() {
      super.viewDidLoad()

      // CBCentralManagerの初期化
      self.cm = CBCentralManager(delegate: self, queue: nil)
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
  }

  @IBAction func centralStartAction(sender: AnyObject) {
      // スキャン開始
      self.cm.scanForPeripheralsWithServices([self.sUUID], options: nil)
  }

  // MARK: - CBCentralManagerDelegate -
  // Bluetoothの状態が変化したときに通る処理
  func centralManagerDidUpdateState(central: CBCentralManager) {
      print("state: \(central.state)")
  }

  // 周辺にBLEデバイスが見つかったときに通る処
  func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
      if let uuidArray:[CBUUID] = advertisementData["kCBAdvDataServiceUUIDs"] as? [CBUUID] {
          let uuid:CBUUID = uuidArray.first!
          if uuid == self.sUUID {
              // Peripheralに接続
              self.connectPeripheral = peripheral
              self.cm.connectPeripheral(connectPeripheral, options: nil)
          }
      }
  }

  // 接続が成功したときに通る処理
  func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
      peripheral.delegate = self
      // サービス探索開始
      peripheral.discoverServices([self.sUUID])
  }

  // 接続に失敗したときに通る処理
  func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
      print("接続失敗: \(error)")
  }
  
  // MARK: - CBPeripheralDelegate -
  // サービスの探索結果を受け取るときに通る処理
  func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
      let services:[CBService] = peripheral.services!
                              
      for service in services {
          peripheral.discoverCharacteristics([self.sUUID], forService: service)
      }
  }

  // キャラクタリスティックの探索結果を受け取るときに通る処理
  func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
      let characteristics:[CBCharacteristic] = service.characteristics!
                          
      for characteristic in characteristics {
          print("properties: \(characteristic.properties)")
          peripheral.readValueForCharacteristic(characteristic)
      }
  }

  // データの読込み結果を取得したときに通る処理
  func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
      if(error != nil) {
          print("読込みに失敗しました。error: \(error)")
          return
      }
                                                      
      print("読込みに成功しました。service.uuid: \(characteristic.service.UUID), characteristic uuid: \(characteristic.UUID), value: \(characteristic.value)")
  }
}

さあ、これでペアリングアラートが出るはず!ということで2台のiOS端末で試してみました。
結果、 『アラートが表示されません!』 でした。
どこか書き方が間違っているのかと思い、試行錯誤を繰り返したり、ググり続けたりしたところ、理由が判明しました。
2台共にiOS8端末かつiCloudに同じApple IDを設定している場合はセキュアと見なされてペアリングアラートが表示されない ということでした。

なるほど、出ないわけだ…ということで皆さんも気をつけてみてください。
本日はここまで。

Comments