モケラ

Tech Sheets

mokelab

CoreBluetoothを使ってBLE Peripheral側を実装する

最終更新日:2022-05-11

CoreBluetoothのAPIを使うと、データを提供するPeripheral側も実装することができます。次の4ステップで実装します。

  • CBPeripheralManagerを作る
  • サービスを追加する
  • Advertiseを開始する
  • Readリクエストが来た時の処理を実装する

順にみていきましょう。

CBPeripheralManagerを作る

まずはviewDidLoad()のあたりでCBPeripheralManagerオブジェクトを作ります。今回はdelegateにself(=ViewController)を指定したので、ViewControllerにCBPeripheralManagerDelegateプロトコルをつけます。また、サービスとCharacteristicのUUIDを表すオブジェクトも作っておきます。

class PeripheralViewController : UIViewController, CBPeripheralManagerDelegate {
    var serviceUUID : CBUUID!
    var characteristicUUID : CBUUID!
    
    var manager : CBPeripheralManager!
    var service : CBMutableService!
    var characteristic : CBMutableCharacteristic!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.serviceUUID = CBUUID(string: Constants.SERVICE_UUID)
        self.characteristicUUID = CBUUID(string: Constants.CHARACTERISTIC_UUID)
        
        self.manager = CBPeripheralManager(delegate : self, queue : nil)
    }
}

オブジェクトを作ると、早速CBPeripheralManagerDelegateのperipheralManagerDidUpdateState() が呼ばれます。端末のBluetoothがOFFの場合は次の図のようにダイアログが出ます。

<img src="./bluetooth_off.webp"/>

BluetoothがONの時のみ、後続の処理を行うようperipheralManagerDidUpdateState() を実装します。

func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) {
    if (peripheral.state == CBPeripheralManagerState.PoweredOn) {
        self.addMessage("BLE Power On")
        // この時点でサービスを登録する(次節参照)
        self.addService()
    }
}

サービスを追加する

次にサービスを追加します。今回はCharacteristicを1つだけ持ったサービスを作ってみます。

func addService() {
    self.service = CBMutableService(type: self.serviceUUID, primary: true)
    self.characteristic = CBMutableCharacteristic(type: self.characteristicUUID,
        properties: CBCharacteristicProperties.Read,
        value: nil, permissions:
        CBAttributePermissions.Readable)
    self.service.characteristics = [self.characteristic]
        
    self.manager.addService(self.service)
}

CBMutableCharacteristicのvalueの部分は、Readリクエストが来た時に動的に値を返したい場合はnilを指定します。

Advertiseを開始する

サービスの追加まで完了したら、次はAdvertiseを開始します。Advertiseを開始するには、CBPeripheralManagerのstartAdvertising() を使います。

self.manager.startAdvertising([CBAdvertisementDataServiceUUIDsKey : [self.service.UUID]])

引数には、CBAdvertisementDataServiceUUIDsKeyキーを持つDictionaryを指定します。このキーの値はサービスのUUIDを配列で指定します。型チェックの効かない部分なので不用意に[]を抜いたりしてはまらないよう注意します。

このメソッドを呼ぶと、CBPeripheralManagerDelegateのperipheralManagerDidStartAdvertising:error() が呼ばれます。errorがnilであればAdvertiseは開始されています。

func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager!, error: NSError!) {
    if (error == nil) {
        self.addMessage("Start advertisement")
    } else {
        self.addMessage("Failed to start advertisement " + error!.localizedDescription)
    }
}

Readリクエストが来た時の処理を実装する

Central側がReadリクエストを投げると、CBPeripheralManagerDelegateのperipheralManager:didReceiveReadRequest() が呼ばれます。ここでCentral側に値を通知します。

func peripheralManager(peripheral: CBPeripheralManager!, didReceiveReadRequest request: CBATTRequest!) {
    self.addMessage("Receive request")
    if (!request.characteristic.UUID!.isEqual(self.characteristic.UUID)) {
        return
    }
    if (request.offset > data.length) {
        self.manager!.respondToRequest(request, withResult: CBATTError.InvalidOffset)
        return;
    }
    // create data
    request.value = self.data!.subdataWithRange(NSMakeRange(request.offset, data.length - request.offset))
    self.manager!.respondToRequest(request, withResult: CBATTError.Success);
    self.addMessage("Respond to request");
}

データの長さとオフセットの関係がおかしい場合はCBATTError.InvalidOffset を返しておきます。正常な場合はrequest.value にデータをセットし、ResultにCBATTError.Success をセットし返します。

一覧に戻る