[BLE] ⚡️ Peripheral 시뮬레이션 (2)

2025. 10. 12. 15:26도메인/Bluetooth

시뮬레이션

시뮬레이션 - Act as a Peripheral

실제 대부분의 기기는 자체 펌웨어로 Peripheral 역할을 한다. Apple platform에서는 CBPeripheralManager를 통해 peripheral을 시뮬레이션할 수 있다. 해당 기능은 foreground에서만 동작한다는 제약이 있지만 학습해보는데는 충분하다.

CBPeripheralManager

let cbPeripheralManager = CBPeripheralManager(delegate: self, queue: nil)

 

  • 서비스와 Charateristics를 광고
  • central devices에게 데이터를 전송
  • CBPeripheralManagerDelegate: device state + connected devices
  • iOS, iPadOS, madOS can act as peripherals
WatchOS의 경우, 보안을 위해  Central 역할만 하며, 외부 BLE가 Watch Characteristic을 읽을 수 없다. 모든 외부 통신은 막혀있고, iPhone을 통해서만 간접적으로 정보를 얻을 수 있다. 이 때 iPhone과 통신을 위해 Watch Connectivity의 WCSession라는 기술을 이용하여 실시간 데이터 통신한다.

Adversitsing

let heartRateCharacteristic = CBMutableCharacteristic(
    type: Gatt.Characteristic.heartRateMeasurement,
    properties: [.notify],
    value: nil,
    permissions: [.readable]
)

func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
    if peripheral.state == .poweredOn {
        setupHeartRateService()
        startAdvertising()
    }
}

 

전원이 들어오면 서비스를 활성화하고, 광고를 시작합니다.

서비스가 만들어지면 매니저의 스택에 쌓습니다.

func setupHeartRateService() {
    let heartRateService = CBMutableService(
        type: Gatt.Service.heartRate,
        primary: true
    )
    heartRateService.characteristics = [heartRateCharacteristic]
    cbPeripheralManager.add(heartRateService)
}

func startAdvertising() {
    isAdvertising = true
    let advertisementData: [String: Any] = [
        CBAdvertisementDataServiceUUIDsKey: [Gatt.Service.heartRate]
    ]
    cbPeripheralManager.startAdvertising(advertisementData)
}

Sending Data

정보를 주고 받은 이후 Central이 characteristic을 구독하면 광고를 중단하고, 값을 보냅니다.

func peripheralManager(
    _ peripheral: CBPeripheralManager,
    central: CBCentral,
    didSubscribeTo characteristic: CBCharacteristic
) {
    if characteristic.uuid == Gatt.Characteristic.heartRateMeasurement {
        stopAdvertising()
        repeatedlySendHeartRateValues()
    }
}

func repeatedlySendHeartRateValues() {
    guard sendHeartRateTask == nil else {
        return
    }
    sendHeartRateTask = Task {
        while !Task.isCancelled {
            sendHeartRateMeasurement()
            try? await Task.sleep(for: .seconds(1))
        }
    }
}

 

1초 주기로 값을 보내고는 Task를 생성하여 값을 방출합니다.

func sendHeartRateMeasurement() {
    let variableHeartRate = UInt8.random(
        in: (heartRateValue - 1) ... (heartRateValue + 1)
    )
    // Flags (0x00) + Heart Rate Value
    let heartRateData = Data([0x00, variableHeartRate])
    cbPeripheralManager.updateValue(
        heartRateData,
        for: heartRateCharacteristic,
        onSubscribedCentrals: nil
    )
}

 

임의로 만든 heartrate 식별 flag값과 임의로 생성한 심박수를 이용하여 패킷을 생성합니다. 생성한 peripheralManager를 통해 갱신합니다.

모든 디바이스가 아니라 특정 디바이스에게만 전송하고 싶으면, onSubscribedCentrals: nil 대신 위에서 얻은 Central 정보를 넣으면 됩니다.

'도메인 > Bluetooth' 카테고리의 다른 글

[BLE] ⚡️ BLE Write와 State Restore (3)  (0) 2025.10.12
[BLE] ⚡️ BLE란? Core Bluetooth (1)  (0) 2025.10.12