Zenn
📶

Core Bluetooth入門(Peripheral)

2025/02/18に公開

概要

Core Bluetoothは、Bluetooth Low Energy(BLE)通信を行うためのフレームワークです。このフレームワークを活用することで、デバイス間のデータ通信を実現できます。
BLEデバイスは「Central」と「Peripheral」という2つの役割を持つことができ、それぞれの役割に応じた実装を行います。
Peripheralは、BLE通信においてデータを提供する側として機能します。たとえば、センサーやデータ送信デバイスとしての役割を担い、Centralデバイスからのスキャンや接続リクエストに応答し、必要なデータを提供します。

本記事では、Core Bluetoothを用いてPeripheralとして動作するデバイスの基本的な実装方法について解説していきます。

Centralに関して知りたい方は以下の記事をどうぞ。
https://zenn.dev/oka_yuuji/articles/828a3de7486016

Peripheralの基本的な実装手順

Peripheralの基本的な実装手順は以下の通りです。

  1. 使用許諾の実装
  2. CBPeripheralManagerの初期化
  3. ServiceとCharacteristicの定義
  4. Serviceの登録
  5. アドバタイズの開始
  6. アドバタイズの停止
  7. Readリクエストへの応答
  8. Writeリクエストの処理
  9. Notifyの実装
  10. Peripheral Managerの停止

1. 使用許諾の実装

使用許諾の実装はinfo.plistに以下を追加します。
Privacy - Bluetooth Always Usage Description

2. CBPeripheralManagerの初期化

CBPeripheralManagerを初期化するには、以下ののようにデリゲートを設定してインスタンスを作成します。

class PeripheralManager: NSObject {
    private var peripheralManager: CBPeripheralManager!
    
    override init() {
        super.init()
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
    }
}

queueはnilを渡すと、main queueで処理されます。

CBPeripheralManagerDelegateへの準拠

CBPeripheralManagerを使用するためには、CBPeripheralManagerDelegateプロトコルに準拠する必要があります。
そのため、次のようにextensionを用いてCBPeripheralManagerDelegateに準拠させます。
またperipheralManagerDidUpdateStateにてステータスが取れます。

extension PeripheralManager: CBPeripheralManagerDelegate {
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        // Peripheralの状態が変化した際に呼び出される
        switch peripheral.state {
        case .poweredOn:
            print("Bluetooth is powered on and ready.")
            // アドバタイズなどの処理を開始できる
        case .poweredOff:
            print("powered off")
        case .resetting:
            print("resetting")
        case .unauthorized:
            print("authorized")
        case .unsupported:
            print("unsupported")
        case .unknown:
            print("unknown")
        @unknown default:
            print("default")
        }
    }
}

Stateの詳細

CBPeripheralManagerDelegateのperipheralManagerDidUpdateState(_:)メソッドは、Peripheralの状態を監視するために使用されます。このメソッドで渡されるstateプロパティは、以下のような状態を表します。

状態 (CBManagerState) 説明
unknown 状態が不明です。
resetting Bluetoothがリセット中です。
unsupported デバイスがBluetoothをサポートしていません。
unauthorized アプリがBluetoothの使用を許可されていません。
poweredOff Bluetoothがオフになっています。
poweredOn Bluetoothがオンになっています。Peripheralとして動作可能です。

3. ServiceとCharacteristicの定義

Peripheralは、BLE通信でデータを提供するために「Service」と「Characteristic」という概念を使用します。

ServiceとCharacteristicとは?

  • Service
    Serviceは、データのグループを表します。たとえば「心拍数センサー」や「温度計」など、デバイスが提供する機能ごとにServiceを定義します。ServiceはユニークなUUID(Universally Unique Identifier)で識別されます。

  • Characteristic
    Characteristicは、Serviceの中で個々のデータポイントを表します。たとえば、温度計Serviceであれば「現在の温度」や「最小温度・最大温度の履歴」がCharacteristicとして定義されます。それぞれのCharacteristicもUUIDで識別され、読み取り(Read)、書き込み(Write)、通知(Notify)といったプロパティを設定できます。

ServiceとCharacteristicの定義方法

  1. UUIDの生成
    ServiceやCharacteristicを定義するためには、ユニークなUUIDを生成します。UUIDは公式に定められたもの(標準UUID)を使うか、自分で新規に生成することもできます。以下の例では独自のUUIDを使用します。

  2. CBMutableServiceとCBMutableCharacteristicの作成
    ServiceにはCBMutableService、CharacteristicにはCBMutableCharacteristicを使用します。

  3. プロパティの設定
    Characteristicには、以下のようなプロパティを設定できます。
    ・CBCharacteristicProperties: データの操作(Read, Write, Notifyなど)を指定します。
    ・CBAttributePermissions: 読み取りや書き込みの権限(許可、セキュリティ)を設定します。

実装例

以下の例では、シンプルな「温度計Service」を作成し、現在の温度を提供するためのCharacteristicを定義しています。

class PeripheralManager: NSObject {
    ...
    // ServiceとCharacteristicを定義するメソッド
    private func setupServiceAndCharacteristics() {
        // ServiceのUUIDを作成
        let serviceUUID = CBUUID(string: "12345678-1234-5678-1234-56789ABCDEFF")
        let characteristicUUID = CBUUID(string: "87654321-4321-8765-4321-ABCDEFFEDCBA")

        // Characteristicを作成
        let temperatureCharacteristic = CBMutableCharacteristic(
            type: characteristicUUID,
            properties: [.read, .notify], // ReadとNotifyをサポート
            value: nil,                  // 初期値は設定しない
            permissions: [.readable]     // 読み取りを許可
        )

        // Serviceを作成し、Characteristicを追加
        let temperatureService = CBMutableService(type: serviceUUID, primary: true)
        temperatureService.characteristics = [temperatureCharacteristic]
        
        // Peripheral ManagerにServiceを登録
        peripheralManager.add(temperatureService)
    }
}

※Characteristicのプロパティ(Read, Write, Notifyなど)は、提供するデータや機能に応じて慎重に設定してください。たとえば、通知(Notify)を使用する場合は、Centralに通知が送られる仕組みを別途実装する必要があります。

4. Serviceの登録

ServiceとCharacteristicを定義したら、Peripheral ManagerにServiceを登録する必要があります。登録を行うと、アドバタイズ時にCentralにService情報を公開できます。

extension PeripheralManager: CBPeripheralManagerDelegate {
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            // ServiceをPeripheral Managerに登録
            peripheralManager.add(temperatureService)
        } else {
            print("Peripheral is not ready.")
        }
    }
}

5. アドバタイズの開始

Peripheralはアドバタイズ(Advertise)を通じて自身の情報を公開し、Centralに見つけてもらいます。以下ではアドバタイズの設定と開始方法を解説します。

実装方法

アドバタイズにはCBPeripheralManagerのstartAdvertising(_:)メソッドを使用します。このメソッドにアドバタイズ情報を渡すことで、Peripheralが自身を公開します。

private func startAdvertising() {
    let advertisingData: [String: Any] = [
        CBAdvertisementDataLocalNameKey: "MyPeripheral", // デバイス名
        CBAdvertisementDataServiceUUIDsKey: [serviceUUID] // アドバタイズするサービスのUUID
    ]
    peripheralManager.startAdvertising(advertisingData)
}

6. アドバタイズの停止

必要に応じてアドバタイズを停止する場合は、stopAdvertising()を使用します。

private func stopAdvertising() {
    peripheralManager.stopAdvertising()
}

7. Readリクエストへの応答

CentralがCharacteristicの値を読み取る際、Peripheralはリクエストに応答する必要があります。CBPeripheralManagerDelegateのperipheralManager(_:didReceiveRead:)メソッドで処理を行います。

実装方法

extension PeripheralManager: CBPeripheralManagerDelegate {
    func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
        guard let characteristic = request.characteristic as? CBMutableCharacteristic else {
            peripheral.respond(to: request, withResult: .unlikelyError)
            return
        }
        request.value = characteristic.value
        peripheral.respond(to: request, withResult: .success)
    }
}

8. Writeリクエストの処理

CentralがCharacteristicに値を書き込む場合も、Peripheralはそれを処理します。CBPeripheralManagerDelegateのperipheralManager(_:didReceiveWrite:)メソッドを使用します。

実装方法

extension PeripheralManager: CBPeripheralManagerDelegate {
    func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
        for request in requests {
            guard let characteristic = request.characteristic as? CBMutableCharacteristic else {
                peripheral.respond(to: request, withResult: .unlikelyError)
                return
            }
            
            // 書き込まれた値をCharacteristicに反映
            characteristic.value = request.value
        }
        peripheral.respond(to: requests.first!, withResult: .success)
    }
}

9. Notifyの実装

Notifyを使用すると、Characteristicの値が変化した際にCentralに通知を送ることができます。

実装方法

updateValue(_:for:onSubscribedCentrals:)メソッドを使用して通知を送信します。

func sendNotification(value: Data) {
    if let characteristic = temperatureService?.characteristics?.first as? CBMutableCharacteristic {
        let didSend = peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: nil)
        if didSend {
            print("success")
        } else {
            print("Failed")
        }
    }
}

10. Peripheral Managerの停止

アプリ終了時や不要になった場合、CBPeripheralManagerを停止します。

実装方法

deinit {
    peripheralManager.stopAdvertising()
}

まとめ

以上、Core Bluetoothを用いてiOSデバイスをPeripheralとして動作させる基本的な手順でした。

参考記事

https://developer.apple.com/documentation/corebluetooth/

Discussion

ログインするとコメントできます