친한 후배 덕분에 블루투스 기기와 연동하여 데이터를 표시하는 프로젝트를 진행했었습니다. 학습했던 것을 간단하게 정리해보려고 합니다.
Core Bluetooth 프레임 워크는 BLE(Bluetooth Low Energy)와 BR/EDR 무선 통신 기술을 장비한 블루투스와 애플리케이션이 통신하는데 필요한 다양한 클래스를 제공합니다.
블루투스 통신에서 중요한 것은 Central(중앙장치)
와 Peripheral(주변장치)
입니다.
데이터를 받아오고 처리하는 기기가 Cental
의 역할을 맡게되고 데이터를 측정하고 Central
로 데이터를 전송하는 기기를 Peripheral
이라 합니다.
예를 들어 블루투스 안마기와 안마기 관련 앱이 있다고 합시다. 데이터를 측정하여 앱에다가 정보를 보내는 안마기는 Peripheral
의 역할이고 그 안마기에서 데이터를 받아 처리하는 앱은 central
입니다.
Central은 위의 그림에 표시된 것처럼 광고하고 있는 모든 peripheral을 스캔하고 수신할 수 있습니다. 그리고 peripheral에게 연결을 요청할 수 있습니다.
각 central의 상태에 따라 CBCentralManagerDelegate
의 centralManagerDidUpdateState
가 호출되게 됩니다. 블루투스가 .poweredOff 비활성 상태이면 자동으로 팝업창이 나옵니다.
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case . unknown:
print("unkwon case")
case .resetting:
print("reseting case")
case .unsupprted:
print("unsupported case")
case .unauthorized:
print("블루투스 사용 권한 확인 필요")
// [앱 블루투스 권한 사용 설정창 이동]
delegate?.buttonState(isValid: false)
delegate?.intentAppSettings(content: "Please allow Bluetooth permission to use this app.")
case .poweredOff:
delegate?.buttonState(isValid: false)
print("블루투스 비활성 상태")
// [자동으로 시스템에서 비활성 상태 알림 및 팝업창 호출 실시]
case .poweredOn:
print("CBCentralManager : 블루투스 활성 상태")
delegate?.buttonState(isValid: true)
@unknown default:
delegate?.buttonState(isValid: false)
print(" 블루투스 CASE DEFAULT")
}
}
.poweredOn 상태에서 CBCentralManager의 scanForPeripherals(withServices:options:)
를 통해 주변기기들을 검색할 수 있습니다.
이 때 CBCentralManagerDelegate
에 centralManager(_:didDiscover peripheral:advertisementData:rssi:)
통해서 검색된 블루투스 기기를 확인 할 수 있습니다.
//장치를 찾았을 때 실행되는 이벤트
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
// 특정 이름을 찾은 경우 : 스캔 중지
if peripheral.name?.contains(SungMin_Name) ?? false {
// 블루투스 스캔 종료
centralManager?.stopScan()
delegate?.connected()
// 블루투스 연결
self.devicePeripheral = peripheral // 주변기기 등록
self.devicePeripheral?.delegate = self // 딜리게이트 지정
self.centralManager?.connect(peripheral) // 디바이스 연결 수행
}
}
위와 같이 연결에 성공하면 CBPeripheralDelegate
의 didConnect
호출됩니다.
이 때 주변기기가 제공하는 서비스를 다시 검색해야합니다.
// [장치와 연결 되었을 경우 발생하는 이벤트]
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("블루투스 연결 [UUID] :: \(String(peripheral.identifier.uuidString))")
self.devicePeripheral?.discoverServices(nil)
}
확인된 서비스는
func peripheral(_:didDiscoverServices:)
에서 확인 할 수 있습니다.
peripheral에는 characteristic이 있는데 peripheral의 특성을 전달합니다. 데이터 전송이나 기기 세팅 등 다양한 것들이 있는데, 보통 각 블루투스 기기의 스펙문서를 보시고 작업해야합니다.
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
if "\(characteristic.uuid)".contains(Rx_UUID) {
RxChar = characteristic
peripheral.setNotifyValue(true, for: RxChar!)
peripheral.readValue(for: characteristic)
}
}
}
위와 같이 readValue를 하게 되면
func centralManager(_:didDisconnectPeripheral: error:)
로 데이터가 넘어오게 됩니다.
압력 센서를 가진 블루투스 기기를 연결하여 압력센서에 가해진 압력 따라 색상으로 표시하였습니다.