BLE는 Bluetooth Low Energy의 약자이며, 블루투스 통신의 단점이었던 전력소비를 보완한 저전력 블루투스.
통신 속도보다 전력 소모를 줄이는 것에 초점을 맞춘 Bluetooth 4.0에 특화된 기술
Core Bluetooth는 이 기술을 쉽게 사용할 수 있도록 애플에서 제공하는 프레임워크
Core Bluetooth 프레임 워크는 BLE와 BR/EDR 무선 통신 기술을 장비한 블루투스와 애플리케이션이 통신하는데 필요한 다양한 클래스를 제공.
Peripheral는 흔히 우리가 iOS에 연결하는 주변 기기이고, Central은 이를 제어하는 iOS 애플리케이션을 의미.
Central은 Peripheral을 discover(검색)하고 connect(연결)을 요청함. 그리고 연결한 Peripheral에 대한 exploring(탐색)을 시작하고, Peripheral의 데이터와 interacting(상호작용)을 합니다. 이 때, Peripheral은 Central이 discover(검색)할 수 있도록 advertising(광고)함.
Peripheral의 데이터는 1개 이상의 Service로 이루어져있습니다. 또, Service는 1개 이상의 Characteristic을 가질 수 있음. Service는 Peripheral이 가지고 있는 하나의 기능. Service는 보통 Characteristic을 갖고 있는데, Characteristic은 Peripheral이 가지고 있는 실질적인 데이터를 나타냄.
CBPeripheral : CBService : CBCharacteristic = 1 : n : n * m 의 구조
iOS 13 이상에서는 NSBluetoothAlwaysUsageDescription를,
iOS 12 이하 버전에서는 NSBluetoothPeripheralUsageDescription를 추가
// 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 정보를 가져올 예정.
// 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들
기기 검색 시
특정 [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
기기 연결 시
연결된 기기의 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를 탐지는 연결한 상태에서 가능함