はじめに
iOSでBluetooth接続処理を実装したときの備忘録です。
環境
- Xcode10.3
- Swift5
##概要
import CoreBluetooth
端末検索〜接続、操作までCoreBluetoothで全て実装することができました。
##主役となるManagerクラス
-
CBCentralManager・・・アドバタイズされた周辺機器のスキャン、検出、接続や、検出または接続されたリモート周辺機器の管理に使用されます。
-
CBPeripheralManager・・・ローカル周辺機器のGeneric Attribute Profile(GATT)データベース内で公開されたサービスを管理し、これらのサービスをアドバタイズするために使用されます。
なお、Bluetoothの操作は、主に以下のデリゲートメソッドを使用して行います。
##実装フロー
①セントラルマネージャーを起動する。
②ペリフェラルを検出する。
③ペリフェラルと接続する。
④ペリフェラルのサービスを検出する。
⑤サービス特性を検出する。
⑥サービス特性の値を取得する。
引用:iOS SwiftでBLEのサンプルを動かしてみる
BluetoothServiceクラスの実装
今回は、Constに使用するサービスのUUIDを定義し、
スキャン時には、タイムアウトエラー実装用に、タイマーを設定するようにしました。
なお、対象のペリフェラルの検出には、ペリフェラルの名前を指定するようにしています。
struct Const {
struct Bluetooth {
/// サービスのUUID
struct Service {
static let kUUID: String = "cd000000-1234-1234-1234-hogehogehoge"
}
/// サービスのキャラクタリスティックのUUID
struct Characteristic {
static let kUUID01 = "cd100001-1234-1234-1234-hogehogehoge"
static let kUUID02 = "cd100002-1234-1234-1234-hogehogehoge"
}
static let kPeripheralName = "Hoge Bluetooth"
}
}
final class BluetoothService: NSObject {
/// 接続先の機器
private var connectPeripheral: CBPeripheral? = nil
/// 対象のキャラクタリスティック
private var writeCharacteristic: CBCharacteristic? = nil
override init() {
self.centralManager = CBCentralManager()
self.peripheralManager = CBPeripheralManager()
}
// MARK: - Public Methods
/// Bluetooth接続のセットアップ
func setupBluetoothService() {
self.centralManager = CBCentralManager(delegate: self, queue: nil)
self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}
/// スキャン開始
func startBluetoothScan() {
print("スキャン開始")
// タイマーを設定する
self.scanTimer = Timer.scheduledTimer(timeInterval: TimeInterval(10),
target: self,
selector: #selector(self.timeOutScanning),
userInfo: nil,
repeats: false)
// 機器を検出
if self.centralManager.isScanning == false {
self.centralManager.scanForPeripherals(withServices: nil, options: nil)
}
}
/// スキャン停止
func stopBluetoothScan() {
self.centralManager.stopScan()
// Timerを削除
self.scanTimer?.invalidate()
self.scanTimer = nil
}
/// 機器に接続
func connectPeripheral() {
guard let connectPeripheral = self.connectPeripheral else {
// 失敗処理
return
}
self.centralManager.connect(connectPeripheral, options: nil)
}
}
CBCentralManagerDelegate
// MARK: CBCentralManagerDelegate
extension BluetoothService: CBCentralManagerDelegate {
/// Bluetoothのステータスを取得する(CBCentralManagerの状態が変わる度に呼び出される)
///
/// - Parameter central: CBCentralManager
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOff:
print("Bluetooth PoweredOff")
break
case .poweredOn:
print("Bluetooth poweredOn")
break
case .resetting:
print("Bluetooth resetting")
break
case .unauthorized:
print("Bluetooth unauthorized")
break
case .unknown:
print("Bluetooth unknown")
break
case .unsupported:
print("Bluetooth unsupported")
break
}
}
/// スキャン結果取得
///
/// - Parameters:
/// - central: CBCentralManager
/// - peripheral: CBPeripheral
/// - advertisementData: アドバタイズしたデータを含む辞書型
/// - RSSI: 周辺機器の現在の受信信号強度インジケータ(RSSI)(デシベル単位)
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// 対象機器のみ保持する
if let peripheralName = peripheral.name,
peripheralName.contains(Const.Bluetooth.kPeripheralName) {
// 対象機器のみ保持する
self.connectPeripheral = peripheral
// 機器に接続
print("機器に接続:\(String(describing: peripheral.name))")
self.centralManager.connect(peripheral, options: nil)
}
}
/// 接続成功時
///
/// - Parameters:
/// - central: CBCentralManager
/// - peripheral: CBPeripheral
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("接続成功")
self.connectPeripheral = peripheral
self.connectPeripheral?.delegate = self
// 指定のサービスを探索
if let peripheral = self.connectPeripheral {
peripheral.discoverServices([CBUUID(string: Const.Bluetooth.Service.kUUID)])
}
// スキャン停止処理
self.stopBluetoothScan()
}
/// 接続失敗時
///
/// - Parameters:
/// - central: CBCentralManager
/// - peripheral: CBPeripheral
/// - error: Error
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("接続失敗:\(String(describing: error))")
}
/// 接続切断時
///
/// - Parameters:
/// - central: CBCentralManager
/// - peripheral: CBPeripheral
/// - error: Error
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("接続切断:\(String(describing: error))")
}
}
CBPeripheralDelegate
// MARK: CBPeripheralDelegate
extension WearBluetoothService: CBPeripheralDelegate {
/// キャラクタリスティック探索時(機器接続直後に呼ばれる)
///
/// - Parameters:
/// - peripheral: CBPeripheral
/// - error: Error
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard error == nil else {
// スキャン停止処理
self.stopScan()
// 失敗処理
return
}
if let peripheralServices = peripheral.services {
for service in peripheralServices where service.uuid == CBUUID(string: Const.Bluetooth.Service.kUUID) {
print("キャラクタリスティック探索")
// キャラクタリスティック探索開始
let characteristicUUIDArray: [CBUUID] = [CBUUID(string: Const.Bluetooth.Characteristic.kUUID01),
CBUUID(string: Const.Bluetooth.Characteristic.kUUID02)]
peripheral.discoverCharacteristics(characteristicUUIDArray, for: service)
}
}
}
}
CBPeripheralManagerDelegate
// MARK: CBPeripheralManagerDelegate
extension WearBluetoothService: CBPeripheralManagerDelegate {
/// 端末のBluetooth設定を取得(WearBluetoothServiceの使用開始時、端末のBluetooth設定変更時に呼ばれる)
///
/// - Parameter peripheral: CBPeripheralManager
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
// サービスを登録
let service = CBMutableService(type: CBUUID(string: Const.Bluetooth.Service.kUUID), primary: true)
self.peripheralManager.add(service)
}
}
/// キャラクタリスティック発見時(機器接続直後に呼ばれる)
///
/// - Parameters:
/// - peripheral: CBPeripheral
/// - service: CBService
/// - error: Error
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard error == nil else {
// スキャン停止処理
self.stopScan()
print("キャラクタリスティック発見時:\(String(describing: error))")
// エラー処理
return
}
guard let serviceCharacteristics = service.characteristics else {
// スキャン停止処理
self.stopScan()
// エラー処理
return
}
// キャラクタリスティック別の処理
for characreristic in serviceCharacteristics {
if characreristic.uuid == CBUUID(string: Const.Bluetooth.Characteristic.kUUID01) {
// データ書き込み用のキャラクタリスティックを保持
self.writeCharacteristic = characreristic
print("Write 01")
}
if characreristic.uuid == CBUUID(string: Const.Bluetooth.Characteristic.kUUID02) {
isNotDiscoverCharacteristic02 = false
peripheral.setNotifyValue(true, for: characreristic)
print("Notify 02")
}
}
}
/// キャラクタリスティックにデータ書き込み時(コマンド送信時に呼ばれる)
///
/// - Parameters:
/// - peripheral: CBPeripheral
/// - characteristic: CBCharacteristic
/// - error: Error
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("キャラクタリスティックデータ書き込み時エラー:\(String(describing: error))")
// 失敗処理
return
}
// 読み込み開始
peripheral.readValue(for: characteristic)
}
/// キャラクタリスティック値取得・変更時(コマンド送信後、受信時に呼ばれる)
///
/// - Parameters:
/// - peripheral: CBPeripheral
/// - characteristic: CBCharacteristic
/// - error: Error
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("キャラクタリスティック値取得・変更時エラー:\(String(describing: error))")
// 失敗処理
return
}
guard let data = characteristic.value else {
// 失敗処理
return
}
// データが渡ってくる
print(data)
}
}
##まとめ
Dalegateメソッドの呼ばれ順(実際処理のフロー)は、docになるべく記載するようにしました。
CoreBluetoothを扱うに当たって、Dataの変換周りに関しても勉強になったので、近々まとめられたらと思います。
#参考