CoreBluetoothを使ってBLE Central側を実装する
最終更新日:2022-05-11
CoreBluetoothを使って、Peripheralの、特定のサービスの特定のCharacteristicの値を読み取るには、次の6ステップで実装します。実装済みのサンプルはこちら
- CBCentralManagerを作る
- サービスのUUIDを指定してPeripheralを検索する
- Peripheralに接続する
- PeripheralのサービスをUUID指定で検索する
- サービスのCharacteristicをUUID指定で検索する
- Characteristicから値を読み取る
順にみていきましょう。
CBCentralManagerを作る
まずはviewDidLoad()
のあたりでCBCentralManagerオブジェクトを作ります。今回はdelegateにself(=ViewController)を指定したので、ViewControllerにCBCentralManagerDelegateプロトコルをつけます。また、サービスとCharacteristicのUUIDを表すオブジェクトも作っておきます。
class CentralViewController : UIViewController, CBCentralManagerDelegate {
var serviceUUID : CBUUID!
var characteristicUUID : CBUUID!
var manager : CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// UUIDを表すオブジェクト生成。
self.serviceUUID = CBUUID(string: Constants.SERVICE_UUID)
self.characteristicUUID = CBUUID(string: Constants.CHARACTERISTIC_UUID)
self.manager = CBCentralManager(delegate : self, queue : nil)
// その他処理
}
}
オブジェクトを作ると、早速CBCentralManagerDelegateのcentralManagerDidUpdateState()
が呼ばれます。端末のBluetoothがOFFの場合は次の図のようにダイアログが出ます。
<img src="./bluetooth_off.webp"/>
BluetoothがONの時のみ、後続の処理を行うようcentralManagerDidUpdateState()
を実装します。
func centralManagerDidUpdateState(central: CBCentralManager!) {
if central.state == CBCentralManagerState.PoweredOn {
self.addMessage("BLE Power On")
// ONの時だけ開始ボタンが押せるようにする
self.startButton?.enabled = true
}
}
サービスのUUIDを指定してPeripheralを検索する
BluetoothがONになっているのを確認したら、次はPeripheralの検索です。検索はCBCentralManagerのscanForPeripheralsWithServices()
を使います。第1引数には検索対象とするサービスのUUIDを配列で指定します。nilを指定すると見つかるもの全部を返すので、デバッグ時はnilを指定して、ちゃんとPeripheralが見つかるか確認することもできます。第2引数は検索オプションですが、詳しくは別の回で紹介します。
// 開始ボタンをタップしたら検索を始めるようにする
@IBAction func startClicked(sender: AnyObject) {
self.startButton?.enabled = false
self.stopButton?.enabled = true
self.manager.scanForPeripheralsWithServices([self.serviceUUID], options: nil)
self.addMessage("Start scan")
}
見つかると、CBCentralManagerDelegateのcentralManager:didDiscoverPeripheral:advertisementData:RSSI()
が呼ばれます。引数に見つかったperipheralが渡されますが、後続の処理実行中にGCされてしまうことがあるので強参照で捕まえておきます。
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!,
advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
self.addMessage("Peripheral discovered")
self.peripheral = peripheral
self.peripheral.delegate = self
}
また、delegateも設定するので、対象クラス(ここではViewController)にCBPeripheralDelegateをつけておきます。
class CentralViewController : UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
// 中身は省略
}
Peripheralに接続する
Peripheralが見つかったら、次は接続します。接続するにはCBCentralManagerのconnectPeripheral
を使います。advertisementのデータだけ使うのであれば接続は必要ありません。また、省電力のため他のPeripheralのスキャンは止めておきます。止めるにはCBCentralManagerのstopScan()
を使います(出典:Appleのドキュメント)。
self.manager.connectPeripheral(peripheral, options: nil)
self.manager.stopScan()
接続が完了すると、CBCentralManagerDelegateのcentralManager:didConnectPeripheral
が呼ばれます。
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
self.addMessage("Connected to peripheral")
}
PeripheralのサービスをUUID指定で検索する
Peripheralに接続できたら、次はPeripheralが提供しているサービスをUUID指定で検索します。検索するにはCBPeripheralのdiscoverServices()
を使います。引数には検索したいサービスのUUIDを配列で渡します。Peripheralの検索と同様、デバッグ時はnilを渡すことですべてのサービスを検索することができます。
self.peripheral.discoverServices([self.serviceUUID])
サービスが見つかると、CBPeripheralDelegateのperipheral:didDiscoverServices:error
が呼ばれます。エラー時は第3引数に何か値が入っているのでそこで確認します。
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
if (error != nil) {
self.addMessage("Failed to discover services " + error!.localizedDescription)
return
}
}
サービスのCharacteristicをUUID指定で検索する
Peripheralのサービスが見つかったら、次はサービスに紐づくCharacteristicを検索します。まずは見つかったサービスの中から、目的のサービスを次のようにして見つけます。見つかったらCBPeripheralのdiscoverCharacteristics:forService:service()
で検索を開始します。第1引数には検索対象となるCharacteristicのUUIDを配列で指定します。nilを指定した場合の挙動はこれまでと同様、すべてを検索対象とします。
let services : NSArray = peripheral.services
self.addMessage("Service discovered count=\(services.count) services=\(services)")
for service in services as! [CBService] {
if service.UUID.isEqual(self.serviceUUID) {
self.peripheral.discoverCharacteristics(nil, forService:service)
}
}
Characteristicが見つかると、CBPeripheralDelegateのperipheral:didDiscoverCharacteristicsForService:error()
が呼ばれます。
func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
if error != nil {
self.addMessage("Failed to discover characteristics " + error!.localizedDescription)
return
}
// 見つかった時の処理
}
Characteristicから値を読み取る
Characteristicまで見つけたら、ようやくBLEデバイス側の値を読み取ることができます。値を読み取るにはCBPeripheralのreadValueForCharacteristic()
を使います。
self.peripheral.readValueForCharacteristic(c)
値を最後まで読み取ると、CBPeripheralDelegateのperipheral:didUpdateValueForCharacteristic:error()
が呼ばれます。読み取った値はCBCharacteristicのvalue
プロパティにNSDataの形で入っています。
func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
if error != nil {
self.addMessage("Failed to read value " + error!.localizedDescription)
return
}
var data = NSString(data: characteristic.value, encoding: NSUTF8StringEncoding)
self.addMessage("value=\(data!)")
}