Bluetooth in iOS

박성민·2023년 4월 24일
1

iOS

목록 보기
30/30
post-custom-banner

친한 후배 덕분에 블루투스 기기와 연동하여 데이터를 표시하는 프로젝트를 진행했었습니다. 학습했던 것을 간단하게 정리해보려고 합니다.

CoreBluetooth

Core Bluetooth 프레임 워크는 BLE(Bluetooth Low Energy)와 BR/EDR 무선 통신 기술을 장비한 블루투스와 애플리케이션이 통신하는데 필요한 다양한 클래스를 제공합니다.

Central과 Peripheral

블루투스 통신에서 중요한 것은 Central(중앙장치)Peripheral(주변장치)입니다.

데이터를 받아오고 처리하는 기기가 Cental의 역할을 맡게되고 데이터를 측정하고 Central로 데이터를 전송하는 기기를 Peripheral 이라 합니다.

예를 들어 블루투스 안마기와 안마기 관련 앱이 있다고 합시다. 데이터를 측정하여 앱에다가 정보를 보내는 안마기는 Peripheral의 역할이고 그 안마기에서 데이터를 받아 처리하는 앱은 central입니다.

Central은 광고(Advertising)하고 있는 Peripheral을 검색하고 연결합니다.

Central은 위의 그림에 표시된 것처럼 광고하고 있는 모든 peripheral을 스캔하고 수신할 수 있습니다. 그리고 peripheral에게 연결을 요청할 수 있습니다.

CBCentralManager

  • 스캐닝, Advertising Peripherals와 같은 GAP Central 절차 초기화를 위한 API 제공합니다.

centralManagerDidUpdateState

각 central의 상태에 따라 CBCentralManagerDelegatecentralManagerDidUpdateState가 호출되게 됩니다. 블루투스가 .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")
        }
    }

scanning & connecting

.poweredOn 상태에서 CBCentralManager의 scanForPeripherals(withServices:options:)를 통해 주변기기들을 검색할 수 있습니다.
이 때 CBCentralManagerDelegatecentralManager(_: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) // 디바이스 연결 수행
        }
    }

위와 같이 연결에 성공하면 CBPeripheralDelegatedidConnect 호출됩니다.
이 때 주변기기가 제공하는 서비스를 다시 검색해야합니다.

 // [장치와 연결 되었을 경우 발생하는 이벤트]
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("블루투스 연결 [UUID] :: \(String(peripheral.identifier.uuidString))")
        self.devicePeripheral?.discoverServices(nil)
    }

확인된 서비스는
func peripheral(_:didDiscoverServices:)에서 확인 할 수 있습니다.

Service's Characteristics

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:)로 데이터가 넘어오게 됩니다.

결과물

압력 센서를 가진 블루투스 기기를 연결하여 압력센서에 가해진 압력 따라 색상으로 표시하였습니다.

참고자료

profile
iOS시작~
post-custom-banner

0개의 댓글