[iOS] CoreBluetooth를 이용한 Bluetooth Scan

김범수·2022년 6월 2일
0

iOS

목록 보기
4/8

BLE

BLE는 Bluetooth Low Energy의 약자이며, 블루투스 통신의 단점이었던 전력소비를 보완한 저전력 블루투스.
통신 속도보다 전력 소모를 줄이는 것에 초점을 맞춘 Bluetooth 4.0에 특화된 기술

CoreBluetooth

Core Bluetooth는 이 기술을 쉽게 사용할 수 있도록 애플에서 제공하는 프레임워크

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

Central, Peripheral

Peripheral는 흔히 우리가 iOS에 연결하는 주변 기기이고, Central은 이를 제어하는 iOS 애플리케이션을 의미.

Central은 Peripheral을 discover(검색)하고 connect(연결)을 요청함. 그리고 연결한 Peripheral에 대한 exploring(탐색)을 시작하고, Peripheral의 데이터와 interacting(상호작용)을 합니다. 이 때, Peripheral은 Central이 discover(검색)할 수 있도록 advertising(광고)함.

Characteristic

Peripheral의 데이터는 1개 이상의 Service로 이루어져있습니다. 또, Service는 1개 이상의 Characteristic을 가질 수 있음. Service는 Peripheral이 가지고 있는 하나의 기능. Service는 보통 Characteristic을 갖고 있는데, Characteristic은 Peripheral이 가지고 있는 실질적인 데이터를 나타냄.

CBPeripheral : CBService : CBCharacteristic = 1 : n : n * m 의 구조

  • CBPeripheral: 주변 연결 기기
    • CBService: 기기가 보유하고 있는 서비스 특정 서비스에 대한 UUID 가지고 있음 ex) FFE0: HM-10모듈이 기본적으로 갖고있는 serviceUUID, FFE0는 원래 온도 관련 서비스라고 함
      • CBCharacteristic: 서비스가 보유하고 있는 특성 특정 특성에 대한 UUID 가지고 있음
        ex) 심박수 서비스에는 기기의 심박수 센서의 의도된 신체 위치를 설명하는 특성이 포함될 수 있으며 다른 특성에는 심박수 측정 데이터가 전송

Info plist

iOS 13 이상에서는 NSBluetoothAlwaysUsageDescription를,

iOS 12 이하 버전에서는 NSBluetoothPeripheralUsageDescription를 추가

CBCentralManagerDelegate

// MARK: Bluetooth Central
extension FindDeviceViewController: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
         switch central.state {

         case .unknown:
             print("central.state is unknown")
         case .resetting:
             print("central.state is resetting")
         case .unsupported:
             print("central.state is unsupported")
         case .unauthorized:
             print("central.state is unauthorized")
         case .poweredOff:
             print("central.state is poweredOff")
         case .poweredOn:
             print("central.state is poweredOn")
             centralManager.scanForPeripherals(withServices: nil) // 스캔 시작
         @unknown default:
             print("central.state default case")
         }
     }
    
    // Bluetooth 감지
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        viewModel.findPeripheral.accept((peripheral, CGFloat(RSSI)))
    }
    
    // Bluetooth 연결 후
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("Connected")
        // 아래 파라미터가 nil이면 모든 서비스를 검색.
        devicePeripheral.discoverServices(nil)
        // 연결 끊기
        // centralManager.cancelPeripheralConnection(peripheral)
    }
    
}

CoreBluetooth를 import 후 CBCentralManager 생성, CNCentralManagerDelegate 를 받는 extension을 하나 만들어줌.

Bluetooth 감지된 peripheral를 viewModel로 RSSI (신호 세기)와 함께 날려준 후, viewModel에서 우리 Device인지, 신호가 가장 센 N개의 Device로 Filtering 후 Device 정보를 가져올 예정.

CBPeripheralDelegate

// MARK: Bluetooth Peripheral
extension FindDeviceViewController: CBPeripheralDelegate {
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard let services = devicePeripheral.services else {return}
        for service in services {
            print(service)
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        guard let characteristics = service.characteristics else {return}
        for characteristic in characteristics {
            print("characteristic: \(characteristic)")
            if characteristic.properties.contains(.read) {
                print("readable")
                peripheral.readValue(for: characteristic)
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        print("didUpdateValueFor characteristic")
        print(characteristic.value ?? "can't get value")
    }
}

Peripheral 즉, 주변 기기의 Service - Characteristic 정보를 사용하게 되면 쓸 Method들

Bluetooth Scan으로 받아올 수 있는 정보


  1. 기기 검색 시

    특정 [serviceUUID]를 가지고 있는 기기로 검색 가능
    ⇒ 충전기들만의 공통된 serviceUUID or characteristicsUUID가 있다면 filtering하여 검색 가능
    (전체 UUID로 검색 시 기기 꽤나 많이뜸..)

    But, 검색된 기기의 CBService, CBCharacteristic 읽기 불가

    // Peripheral (기기) 정보
    - <CBPeripheral: 0x283541040, identifier = CECD15FC-1BD8-09DF-6DA4-6621449EA8E2, name = (null), state = disconnected> #0
      - super: CBPeer
        - super: NSObject
    
    // 신호세기
    - DDSI: -87
    
    // 서비스 검색 시 에러 메세지 (연결 상태에서만 검색 가능)
    2022-02-22 16:15:12.913562+0900 BluetoothSwitch[6774:3089124] [CoreBluetooth] API MISUSE: Discovering services for peripheral <CBPeripheral: 0x281570f00, identifier = 11E2F4EB-D633-4FE9-54F9-2616D3AA6D4B, name = OLULO_DEV, state = disconnected> while delegate is either nil or does not implement peripheral:didDiscoverServices:
    
    // 서비스 정보
    nil
    
    // 특성 정보
    nil
  2. 기기 연결 시

연결된 기기의 CBService, CBCharacteristic 읽기 가능

// Peripheral (기기) 정보
- <CBPeripheral: 0x28022d040, identifier = DACF66FC-88AF-030E-BB68-F24847158E2C, name = ESP32, state = connected> #0
  - super: CBPeer
    - super: NSObject

// 서비스 정보Optional([<CBService: 0x28266fe80, isPrimary = YES, UUID = 0000FFE0-0000-1000-8000-00805F9B34FB>])
  ▿ some: 1 element
    - <CBService: 0x28266fe80, isPrimary = YES, UUID = 0000FFE0-0000-1000-8000-00805F9B34FB> #0
      - super: CBAttribute
        - super: NSObject

// 특성 정보Optional([<CBCharacteristic: 0x28172fcc0, UUID = 0000FFE1-0000-1000-8000-00805F9B34FB, properties = 0x1E, value = (null), notifying = NO>])
  ▿ some: 1 element
    - <CBCharacteristic: 0x28172fcc0, UUID = 0000FFE1-0000-1000-8000-00805F9B34FB, properties = 0x1E, value = (null), notifying = NO> #0
      - super: CBAttribute
        - super: NSObject

결론

충전기 블루투스 기기들이 공통적으로 보유하고 있는 serviceUUID가 있다면 식별하여 검색 가능함

주변 블루투스 기기들의 모든 service, characteristic를 탐지는 연결한 상태에서 가능함

profile
iOS Developer

0개의 댓글