Core Bluetooth란 BLE 기기들과 통신을 하기 위한 프레임워크입니다.
Core Bluetooth 프레임워크는 앱와 저전력 블루투스 장비들과 통신할 수 있도록 해줍니다. Core Bluetooth를 사용할 때 Core Bluetooth 프레임워크의 서브 클래스들을 오버라이딩하면 에러가 발생할 수 있으므로 하지 않습니다.
중요
Info.plist에서 설정을 해주지 않는다면 앱은 에러가 발생할 것입니다. Core Bluetooth API를 사용하기 위해서는
iOS 13 이후로는 NSBluetoothAlwaysUsageDescription 키를
iOS 12 이전으로는 NSBluetoothPeripheralUsageDescription 을 설정하여
블루투스의 peripheral 데이터에 접근할 수 있습니다.
알아야할 용어, 클래스 및 프로토콜
Central
Centrals
Centrals는 블루투스(Bluetooth) 통신에서 중앙(Central) 장치를 가리킵니다. 중앙 장치는 데이터를 요청하고 수신하는 기기로, 일반적으로 스마트폰, 태블릿, 노트북 등이 해당됩니다. 중앙 장치는 주변 장치(Peripheral)와 통신하여 데이터를 송수신하거나 제어하는 역할을 담당합니다. 중앙 장치는 주변 장치와 연결하고 데이터를 주고받을 수 있는 능력을 가지며, Bluetooth Low Energy (BLE)와 같은 센서, 웨어러블 장치, 헬스케어 장치 등에서 사용됩니다. 중앙 장치는 다수의 주변 장치와 연결할 수 있으며, 데이터를 수집하거나 제어하기 위해 주변 장치와 지속적으로 통신합니다. Bluetooth 통신에서 중앙 장치를 지칭하는 용어입니다.
- 블루투스 통신을 하기 위해서는 Central 과 Peripheral 이 존재해야합니다.
- Central 은 Peripheral 로부터 통신할 수 있는 주체가 되며 데이터를 송수신 및 Peripheral 를 제어할 수 있습니다.
- Central 는 Peripheral 과 ( 1 : 다 ) 통신이 가능합니다.
- Central는 Bluetooth 통신을 관리하는 중앙 장치라고 불립니다.
CBCentral
Central 은 Peripheral 들을 관리할 수 있는 중앙장치라 했습니다.
즉 Central은 개념적 추상적인 의미이며 Swift 에서는 CBCentral 로 사용됩니다.
class CBCentral: 주변 장치 역할을 하는 로컬 앱에 연결된 peripheral를 다루는 원격 장치(master)입니다.
- 원격 central은 식별당할 수 있도록 NSUUID 객체로 대표되는 보편적으로 고유한 식별자(UUID)를 사용합니다.
var maximunUpdateValueLenght: Int: 중앙이 단일 알림이나 표시로 받을 수 있는 최대 데이터 양(바이트)
주로 iOS 의 경우 아이폰이 CBCentral 이 되기 때문에 직접 CBCentral 를 사용할 일은 없을 것 같습니다.
대신해서 CBCentralManager를 구현하여 Peripheral 들과 데이터 송수신을 할 수 있겠습니다.
CBCentralManager
class CBCentralManager: 블루투스 기기를 탐색하고 연결 및 관리하는 객체입니다.
- CBCentralManager 메서드를 호출하기 전에 블루투스가 켜진 상태여야합니다.
Central Manager 객체 생성하기
init( )
central manager를 delegate 없이 생성
init(delegate: (any CBCentralManagerDelegate)?, queue: dispatch_queue_t?)
central manager를 명시된 delegate와 dispatch queue와 함께 생성
init(delegate: (any CBCentralManagerDelegate)?, queue: dispatch_queue_t?, options: [String : Any]?)
central manager를 명시된 delegate와 disptach queue와 초기화 options를 사용하여 생성
central manager를 초기화할 때 사용하는 options 종류
let CBCentralManagerOptionShowPowerAlertKey: String : 블루투스 서비스를 사용할 수 없을 때 앱이 central manager를 인스턴스화하는 경우 시스템이 사용자에게 경고하는지 여부를 지정하는 bool 값.
let CBCentralManagerOptionRestoreIdentifierKey: String : central manager가 인스턴스화할 수 있는 고유 식별자(UID)를 포함하는 문자열.
central manager를 초기화할 때 사용하는 상태 복원 options 종류
let CBCentralManagerRestoredStatePeripheralsKey: String
: central manager의 상태를 복구할 때 peripheral들이 포함된 배열
let CBCentralManagerRestoredStateScanServicesKey: String
: 상태를 복구할 때 사용되는 service ID들이 포함된 배열
let CBCentralManagerRestoredStateScanOptionsKey: String
: 상태를 복구할 때 사용할 수 있는 주변 스캔 option인 딕셔너리.
CentralManager 생성 코드
옵션과 함께 만들어본 코드를 살펴보겠습니다.
import CoreBluetooth
class BluetoothController: NSObject, CBCentralManagerDelegate {
var centralManager: CBCentralManager?
override init() {
super.init()
self.centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: [CBCentralManagerOptionShowPowerAlertKey:true])
}
...
}
- NSObject 와 CBCentralManagerDelegate 를 먼저 상속시켜줍니다.
- NSObject는 Object-C 기반 클래스이며, CoreBluetooth와 같은 Object-C 프레임워크를 사용할 때 필요합니다.
- CBCentralManagerDelegate 는 아래서 설명하겠지만 대충 CBCentralManager 의 상태 관리를 할 수 있도록 해줍니다.
- override init 과 super.init 을 사용한 이유도 NSObject 를 상속받았기 때문입니다.
- NSObject 는 init 메서드를 포함하고 있어서 NSObject 를 상속받으면 NSObject의 init을 먼저 초기화해야 합니다.
- 이후 super.init() 을 사용하여 BluetoothController 클래스를 초기화시켜줍니다.
기기와 연결, 연결 헤제
func connect(CBPeripheral, options: [String : Any]?)
: peripheral 기기와 연결
- peripheral: 연결하고자하는 peripheral
- options: 연결할 때 설정하는 옵션
func startConnection() {
guard let peripheral = discoverdPeripheral else { return }
centralManager?.connect(peripheral, options: [CBConnectPeripheralOptionEnableAutoReconnect:true])
}
peripheral 연결할 때 사용하는 option 키값 종류
let CBConnectPeripheralOptionEnableAutoReconnect: String
: 시스템이 peripheral과 자동으로 재연결 할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionEnableTransportBridgingKey: String
: BLE를 통해 이미 연결된 경우, 고전적인 블루투스 기술 프로필을 연결하는 옵션.
let CBConnectPeripheralOptionNotifyOnConnectionKey: String
: 백그라운드에서 peripheral과 연결하고 있을 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionNotifyOnDisconnectionKey: String
: 백그라운드에서 peripheral과 연결이 끊어질 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionNotifyOnNotificationKey: String
: peripheral으로부터 알림을 받았을 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionRequiresANCS: String
: 기기와 연결할 때 Apple Notification Center Service (ANCS) 옵션이 필요할 때 사용
let CBConnectPeripheralOptionStartDelayKey: String
: 시스템이 연결을 하기 전에 딜레이를 명시하도록 하는 옵션 (해당 값은 지연 기간을 초 단위로 나타내는 NSNumber입니다)
func cancelPeripheralConnection(CBPeripheral)
: 연결된 기기와 연결을 해제
func CancelConnect() {
guard let peripheral = discoverdPeripheral else { return }
centralManager?.cancelPeripheralConnection(peripheral)
}
탐색된 Peripheral 들 리스트로 받아오기
func retrieveConnectedPeripherals(withServices: [CBUUID]) -> [CBPeripheral]
- 연결된 peripheral들의 리스트를 제공합니다.
- Peripheral의 서비스의 CBUUID 를 사용하여 연결된 peripheral 들을 받아옵니다.
func retrieveConnectedPeripherals() {
guard let peripherals = centralManager?.retrieveConnectedPeripherals(withServices:[CBUUID]) else { return }
}
func retrievePeripherals(withIdentifiers: [UUID]) -> [CBPeripheral]
- peripheral들의 UUID 리스트를 반환
- 연결하려고자 하는 peripheral의 UUID 를 사용하여 해당 peripheral 들을 반환받습니다.
func getPeripherals() {
guard let peripherals = centralManager?.retrievePeripherals(withIdentifiers: [UUID]) else { return }
}
기기 탐색 알아보기 전 Service 란?
CoreBlutooth에 대해 더 알아보기 전에 Bluetooth 의 Service 에 대해서 알아보겠습니다.
Service 란?
Bluetooth Low Energy (BLE)에서 제공하는 데이터와 기능이 담긴 그룹입니다.
Service는 BLE 장치의 특정 기능을 설명하는 하나 이상의 Characteristic (특성)을 포함합니다.
여기서 Characteristic 은 특정 데이터 (예를들어 심박수 특성 또는 배터리 상태 특성)을 등을 포함할 수 있습니다.
이런 1개 이상의 Characteristic 을 포함하고 있는 Service 를 사용하기 식별 가능한 UUID 가 지정됩니다.
func scanForPeripherals(withServices: [CBUUID]?, options: [String : Any]?)
- 서비스를 advertising하고 있는 기기들을 탐색
- withServices: nil로 설정하면 모든 기기를 탐색, 또는 특정 기기를 탐색할 때 사용
- options: 탐색할 때 설정하는 옵션
기기 탐색할 때 사용하는 옵션
let CBCentralManagerScanOptionAllowDuplicatesKey: String
: 중복 필터링 없이 스캔이 수행될 것인지를 결정하는 Bool 값
let CBCentralManagerScanOptionSolicitedServiceUUIDsKey: String
: 스캔으로부터 찾고싶은 service UUID들이 포함된 배열
func startScan() {
centralManager?.scanForPeripherals(
withServices: [CBUUID],
options: [CBCentralManagerScanOptionAllowDuplicatesKey:true] // 중복 탐색을 방지
)
}
기기 탐색 중단
func stopScan()
- central manager가 기기 탐색을 중단
func stopScan() {
centralManager?.stopScan()
}
var isScanning: Bool
- 현재 기기를 탐색하는지를 나타내는 Bool 값
func stopScan() {
if centralManager?.isScanning == true {
centralManager?.stopScan()
}
}
특성 지원 확인
class func supports(CBCentralManager.Feature) -> Bool
- 기기가 특정 특성의 set을 지원하는지에 대한 Bool 값
struct CBCentralManager.Feature
기기의 특정 특성 옵션 종류는 1가지입니다.
- extendedScanAndConnect
let supportsExtendedScan = CBCentralManager.supports(.extendedScanAndConnect)
if supportsExtendedScan {
print("장치가 확장된 스캔 및 연결 기능을 지원합니다.")
} else {
print("장치가 확장된 스캔 및 연결 기능을 지원하지 않습니다.")
}
- extendedScanAndConnect 란?
- 백그라운드 상태에서도 BLE 장치를 스캔하고 연결할 수 있습니다.
- 앱이 백그라운드 상태일 때도 Bluetooth LE 작업을 이어가도록 설계된 기능입니다.
- extendedScanAndConnect 를 지원하는 기기에서 블루투스 백그라운드 작업을 사용하려면 백그라운드 모드를 활성화합니다.
- 에어팟이 이런 기능을 사용하는 것 같습니다.
프로퍼티 모니터링
var delegate: (any CBCentralManagerDelegate)?
: central manager에 이벤트를 수신받고자 하는 delegate 를 나타냅니다.
centralManager.delegate
연결 이벤트 수신
func registerForConnectionEvents(options: [CBConnectionEventMatchingOption : Any]?)
- 설정한 옵션으로 기기와 연결됐을 때 이벤트 알림을 등록
enum CBConnectionEvent
- 피어의 연결 상태에 대한 변화로 Delegate 메서드와 함께 사용되며 밑에서 다시 나오겠습니다.
- peerConnected: Central 과 Periheral 가 연결된 상태
- peerDisconnected: Central 과 Peripheral 이 연결되지 않은 상태
struct CBConnectionEventMatchingOption
- 연결 이벤트에 등록할 때 사용할 옵션 세트
다음과 같은 키값들이 기기와의 연결에 사용되었을 때 이벤트가 발생하도록 등록할 수 있습니다.
peripheral 연결할 때 사용하는 option 키값 종류
let CBConnectPeripheralOptionEnableAutoReconnect: String
: 시스템이 peripheral과 자동으로 재연결 할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionEnableTransportBridgingKey: String
: BLE를 통해 이미 연결된 경우, 고전적인 블루투스 기술 프로필을 연결하는 옵션.
let CBConnectPeripheralOptionNotifyOnConnectionKey: String
: 백그라운드에서 peripheral과 연결하고 있을 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionNotifyOnDisconnectionKey: String
: 백그라운드에서 peripheral과 연결이 끊어질 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionNotifyOnNotificationKey: String
: peripheral으로부터 알림을 받았을 때 시스템이 화면에 alert를 표시할 것인지를 결정하는 Bool 값
let CBConnectPeripheralOptionRequiresANCS: String
: 기기와 연결할 때 Apple Notification Center Service (ANCS) 옵션이 필요할 때 사용
let CBConnectPeripheralOptionStartDelayKey: String
: 시스템이 연결을 하기 전에 딜레이를 명시하도록 하는 옵션 (해당 값은 지연 기간을 초 단위로 나타내는 NSNumber입니다)
func receivingConnectionEvents() {
centralManager?.registerForConnectionEvents(
options: [.init(rawValue: CBConnectPeripheralOptionNotifyOnConnectionKey):true]
)
}
CBCentralManagerDelegate
protocol CBCentralManagerDelegate: 블루투스 기기를 발견, 연결 및 검색에 관한 delegate 함수들을 제공합니다.
- delegate 함수는 이벤트가 발생하면 자동으로 콜백되는 함수입니다.
- CBCentralManagerDelegate를 사용하기 위해서는 centralManagerDidUpdateState(_:)를 완성해야 합니다.
- centralManagerDidUpdateState는 현재 central manager의 가용 상태를 의미합니다.
기기와의 연결을 모니터링
func centralManager(CBCentralManager, didConnect: CBPeripheral)
- central manager가 기기와 연결되었을 때 실행되는 delegate 메서드
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Peripheral과 연결되었을 때 호출")
}
func centralManager(CBCentralManager, didDisconnectPeripheral: CBPeripheral, error: (any Error)?)
- central manager가 기기와 연결해제되었다고 delegate에 전달
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?) {
print("Peripheral과 연결이 끊겼을 때 호출")
}
func centralManager(CBCentralManager, didFailToConnect: CBPeripheral, error: (any Error)?)
- 기기와 연결이 실패하였다고 delegate에 전달
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) {
print("Peripheral과 연결이 실패했을 때 호출")
}
func centralManager(CBCentralManager, connectionEventDidOccur: CBConnectionEvent, for: CBPeripheral)
- 등록한 옵션과 일치하는 연결 이벤트 발생을 delegate에 전달
func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
print("Peripheral과 연결되었을 때 호출")
switch event {
case .peerDisconnected:
print("Peripheral과 연결 성공, \(peripheral.name ?? "알수없음")")
case .peerConnected:
print("Peripheral과 연결 실패, \(peripheral.name ?? "알수없음")")
@unknown default:
print("알수없는 연결 상태 발생")
}
}
기기를 발견, 불러오기
func centralManager(CBCentralManager, didDiscover: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber)
func centralManager(CBCentralManager, didDiscover: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber)
- central manager가 기기를 찾았음을 delegate에 전달
- advertisementData 는 peripheral 이 central 이 자신을 찾을 수 있도록 주변에 방송하는 정보입니다.
advertisementData 키값 종류
let CBAdvertisementDataLocalNameKey: String
: peripheral 의 로컬 이름을 나타냅니다.
if let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
print("발견된 장치 이름: \(localName)")
}
let CBAdvertisementDataManufacturerDataKey: String
: 제조업체에서 제공하는 장치에 설정된 데이터입니다, 제조업체에서 고유 데이터를 사용해 장치를 식별하거나 맞춤 데이터를 처리합니다.
if let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data {
print("제조사 데이터: \(manufacturerData)")
}
let CBAdvertisementDataServiceDataKey: String
: 서비스별 광고 데이터를 포함한 딕셔너리 값을 value 값으로 반환합니다.
if let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] {
print("서비스 데이터: \(serviceData)")
}
let CBAdvertisementDataServiceUUIDsKey: String
: peripheral이 제공하는 서비스의 UUID 목록입니다.
if let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] {
print("서비스 UUIDs: \(serviceUUIDs)")
}
let CBAdvertisementDataOverflowServiceUUIDsKey: String
: 광고 데이터의 오버플로우 영역에 포함된 서비스 UUID 목록입니다.
if let overflowServiceUUIDs = advertisementData[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID] {
print("오버플로우 서비스 UUIDs: \(overflowServiceUUIDs)")
}
let CBAdvertisementDataTxPowerLevelKey: String
: 주변 장치의 송신 전력 수준을 나타냅니다,
: rssi = 또한 마찬가지로 수신 신호 강도를 의미하며 서로 비슷하지만 다릅니다.
if let txPowerLevel = advertisementData[CBAdvertisementDataTxPowerLevelKey] as? NSNumber {
print("송신 전력 수준: \(txPowerLevel)")
}
let CBAdvertisementDataIsConnectable: String
: peripheral이 연결 가능한 상태인지 나타냅니다.
if let isConnectable = advertisementData[CBAdvertisementDataIsConnectable] as? NSNumber {
print("연결 가능 여부: \(isConnectable.boolValue)")
}
let CBAdvertisementDataSolicitedServiceUUIDsKey: String
: peripheral이 요청한 서비스 UUID 목록입니다.
if let solicitedServiceUUIDs = advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID] {
print("요청된 서비스 UUIDs: \(solicitedServiceUUIDs)")
}
Central Manager의 상태 모니터링
func centralManagerDidUpdateState(CBCentralManager)
: central manager의 상태가 업데이트 되었음을 delegate에 전달
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
break
case .poweredOff:
break
case .resetting:
break
case .unauthorized:
break
case .unknown:
break
case .unsupported:
break
@unknown default:
break
}
}
func centralManager(CBCentralManager, willRestoreState: [String : Any])
- 시스템이 앱을 백그라운드로 재실행하여 central manager를 복구될 때 호출
- willRestoreState 의 옵션으로 BLE 연결, 검색 및 스캔과 관련된 상태를 복원하 데 필요한 정보를 제공합니다.
willRestoreState 종류
let CBCentralManagerRestoredStatePeripheralsKey: String
: 복원된 central manager와 연결된 peripheral의 배열입니다.
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) {
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] {
for peripheral in peripherals {
print("복원된 주변 장치: \(peripheral.name ?? "알 수 없음")")
}
}
}
let CBCentralManagerRestoredStateScanServicesKey: String
: 복원 시 central manager 가 검색 중이였던 서비스 UUID 배열을 반환합니다.
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) {
if let scanServices = dict[CBCentralManagerRestoredStateScanServicesKey] as? [CBUUID] {
print("복원된 스캔 서비스: \(scanServices)")
}
}
let CBCentralManagerRestoredStateScanOptionsKey: String
: 복원 시 사용된 스캔 옵션 딕셔너리를 반환합니다.
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String: Any]) {
if let scanOptions = dict[CBCentralManagerRestoredStateScanOptionsKey] as? [String: Any] {
print("복원된 스캔 옵션: \(scanOptions)")
}
}
Central Manager의 권한 모니터링
func centralManager(CBCentralManager, didUpdateANCSAuthorizationFor: CBPeripheral)
- ANCS는 BLE Peripheral 이 Apple 기기에서 알림을 수신할 수 있도록 지원하는 서비스입니다.
- 이를 통해서 peripheral은 iOS 의 알림을 확인하고 처리하거나 알림을 전달할 수 있습니다.
- 이 메서드는 peripheral이 해당 ANCS 서비스 권한을 갖게 되거나 잃을 때 호출됩니다.
func centralManager(_ central: CBCentralManager, didUpdateANCSAuthorizationFor peripheral: CBPeripheral) {
if peripheral.ancsAuthorized {
print("Peripheral \(peripheral.name ?? "Unknown") has ANCS authorization.")
// 권한이 부여되었으므로 알림 데이터 처리 가능
} else {
print("Peripheral \(peripheral.name ?? "Unknown") lost ANCS authorization.")
// 권한이 제거되었으므로 관련 작업 중단
}
}
Peripherals
Peripherals
Peripherals(페리퍼럴)란 Bluetooth 통신에서 중앙(Central) 장치와 통신하기 위해 대기하고 있는 외부 장치를 의미합니다. 이러한 외부 장치들은 주로 센서, 웨어러블 기기, 헬스케어 장치, 스마트 홈 장치 등 다양한 유형의 무선 기기입니다. 페리퍼럴 장치는 일반적으로 광고 패킷(Advertisement Packet)을 통해 자신의 존재를 알리며, Central 이 해당 광고를 감지하면 연결을 시도합니다. 예를 들어, 스마트폰이 중앙 장치로 동작하여 주변에 있는 Peripheral 장치들을 찾고 연결할 수 있습니다. 이후에는 중앙 장치에서 Peripheral 장치로 데이터를 요청하고 Peripheral 이 응답하는 방식으로 통신이 이루어집니다. Peripheral 장치는 Central 과의 연결을 기다리는 동안 저전력 상태로 대기하며, 필요할 때까지 광고를 계속합니다. 이는 Peripheral 가 배터리 수명을 효율적으로 관리할 수 있도록 합니다. 요약하면, Peripheral은 Bluetooth 통신에서 중앙 장치와 통신하기 위해 대기하고 있는 외부 장치로, 주변에 있는 다양한 무선 기기를 나타냅니다.
CBPeripheral
Peripheral 은 Central 과 연결되는 주변 장치라고 했습니다.
즉 Peripheral은 개념적 추상적인 의미이며 Swift 에서는 CBPeripheral 로 사용됩니다.
class CBPeripheral: CBCentralManager 인스턴스로 발견하는 원격 블루투스 장치를 나타냅니다.
- peripheral는 한개 또는 여러개의 서비스들을 포함하여 peripheral에 관한 유용한 정보를 제공하는데 CBPeripheral 클래스는 이런 서비스들을 발견, 탐색, 상호작용할 수 있게 합니다.
- 예를 들어 심박수 모니터 서비스는 심장 관련 데이터를 센서로 받아오는데 여기서 서비스는 한개 또는 여러개의 특성값을 가질 수 있습니다.
peripheral 식별
var name: String?
: peripheral 이름
discoverdPeripheral?.name
var delegate: (any CBPeripheralDelegate)?
: peripheral의 이벤트를 받도록 명시된 delegate 객체
discoverdPeripheral?.delegate
서비스 탐색
func discoverServices([CBUUID]?)
- peripheral의 명시된 서비스들을 탐색합니다.
func discoverIncludedServices([CBUUID]?, for: CBService)
- 이전에 발견된 서비스의 지정된 포함된 서비스를 발견
var services: [CBService]?
- peripheral들의 서비스들의 리스트
Characteristic
Characteristic 들과 서술자 발견
func discoverCharacteristics([CBUUID]?, for: CBService)
- 서비스의 명시된 Characteristic을 발견
func discoverDescriptors(for: CBCharacteristic)
- Characteristic의 Descriptor를 발견
Descriptor
Characteristic 에 대한 추가 정보를 제공하는 메타 데이터로 예를 들어, Characteristic 값의 데이터 형식, 단위, 범위 등에 대한 정보를 포함할 수 있습니다.
Characteristic 값과 Descriptor는 Peripheral 와 데이터를 주고받는데 중요한 역할을 합니다.
특성값과 서술자 읽기
func readValue(for: CBCharacteristic)
- Peripheral 의 명시된 Characteristic의 값 가져오기
func readValue(for: CBDescriptor)
- Peripheral 의 명시된Descriptor(메타 데이터) 읽기
- 특정 Characteristic 의 값이 심박수를 나타낸다면 Descriptor 는 값의 단위 (bpm) 등을 나타낼 수 있습니다.
특성과 서술자 값 쓰기
func writeValue(Data, for: CBCharacteristic, type: CBCharacteristicWriteType)
- Peripheral 의 Characteristic 값을 변경하기
- Characteristic 을 변경하여 장치의 값을 설정할 수 있습니다. (조명 장치의 밝기 값을 설정, 슴마트 전구의 색상을 변경)
func writeValue(Data, for: CBDescriptor)
- Peripheral 의 Descriptor 값 쓰기
- Peripheral 의 Descriptor 를 설정할 수 있으며, 특성 값의 알림 활성화/비활성화 등을 설정할 수 있겠습니다.
func maximumWriteValueLength(for: CBCharacteristicWriteType) -> Int
- 특성에 전송할 수 있는 싱글 쓰기 타입의 데이터의 최대 크기(바이트로)
enum CBCharacteristicWriteType
- 특성의 값에 대한 가능한 쓰기 유형을 나타내는 값
- withResponse: 특성값을 쓸 때 peripheral으로부터 성공적으로 특성값이 쓰였는지를 알려주는 응답을 받을 것으로 설정
- withoutResponse: 특성값을 쓸 때 peripheral으로부터 성공적으로 특성값이 쓰였는지를 알려주는 응답을 받지 않을 것으로 설정
- 특성의 값을 쓰기 위해 이 메서드를 호출할 때, peripheral는 쓰기 유형을 CBCharacteristicWriteType.withResponse로 지정한 경우에만 delegate 객체의 peripheral(_:didWriteValueFor:error:) 메서드를 호출합니다.
특성의 값을 위한 알림 설정
func setNotifyValue(_ enabled: Bool, for: CBCharacteristic)
- 특성에 명시된 값에 알림 또는 표시를 설정, 구독
- enabled를 true로 설정하면 characteristic 값이 변화할 때 알림 또는 표시, false로 설정하면 값이 변화해도 무시
- 만일 true로 설정하고 값이 변했을 때 peripheral(_:didUpdateNotificationStateFor:error:) 를 delegate 객체를 통해 실행합니다
- 만일 성공적으로 위에 메서드가 실행되었다면 peripheral(_:didUpdateValueFor:error:)를 delegate 객체를 통해 값이 변화할 때마다 콜백합니다.
Peripheral의 연결 상태를 모니터링
var state: CBPeripheralState
- peripheral의 연결 상태
enum CBPeripheralState
- peripheral의 연결 상태를 나타내는 값 (disconnected, connecting, connected, disconnecting)
var canSendWriteWithoutResponse: Bool
- 원격 기기가 쓰기 전송을 응답없이 수행할 수 있는지에 대한 값
peripheral.canSendWriteWithoutResponse
Peripheral의 연결 신호에 접근
func readRSSI()
- 연결 신호 강도를 나타내는 RSSI 값을 반환
peripheral.readRSSI()
L2CAP 채널 작업하기
L2CAP (Logical Link Control and Adaptation Protocol)은 BLE 장치 간의 저수준 데이터 통신을 관리하는 프로토콜입니다. CoreBluetooth에서 L2CAP 채널은 BLE Peripheral과 Central 간의 대량 데이터 전송을 지원하기 위해 사용됩니다. 이를 통해 장치 간에 더 효율적이고 유연한 데이터 교환이 가능합니다.
func openL2CAPChannel(CBL2CAPPSM)
- Peripheral 장치의 지정된 PSM(Protocol//Service Multiplexer) 값을 사용하여 L2CAP 채널을 여는 메서드입니다.
- peripheral 과의 대량 데이터를 전송이 가능하며 커스텀 서비스 또는 데이터 스트리밍을 구현할 수 있습니다.
func connectL2CAPChannel(to peripheral: CBPeripheral, psm: CBL2CAPPSM) {
peripheral.openL2CAPChannel(psm)
}
class CBL2CAPChannel
- L2CAP 채널의 활성 연결을 나타내는 클래스입니다.
- 데이터를 읽고 쓰는데 사용됩니다.
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?) {
guard let channel = channel else {
print("L2CAP 채널 열기 실패: \(error?.localizedDescription ?? "알 수 없음")")
return
}
// 입력 및 출력 스트림 설정
let inputStream = channel.inputStream
let outputStream = channel.outputStream
print("L2CAP 채널 연결 성공")
}
typealias CBL2CAPPSM
- peripheral 이 제공하는 L2CAP 서비스의 PSM 값입니다. Peripheral 이 이를 통해 방송하거나 특정 서비스에 연결할 때 제공합니다.
CBPeripheralDelegate
protocol CBPeripheralDelegate: 연결된 블루투스 장비의 서비스들에 대한 delegate 함수들을 제공하는 프로토콜입니다.
- delegate 함수는 이벤트가 발생하면 자동으로 콜백되는 함수입니다.
- 추가적인 필수 메서드는 존재하지 않습니다.
서비스 찾기
func peripheral(CBPeripheral, didDiscoverServices: (any Error)?)
- peripheral 서비스를 성공적으로 찾았을 때 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: (any Error)?) {
print("서비스 찾았다")
}
func peripheral(CBPeripheral, didDiscoverIncludedServicesFor: CBService, error: (any Error)?)
- 표시된 Descritpor 를 발견하는 것이 완료되었다고 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor service: CBService, error: (any Error)?) {
print("표시된 Descriptor를 발견했다")
}
특성과 서술자들 발견
func peripheral(CBPeripheral, didDiscoverCharacteristicsFor: CBService, error: (any Error)?)
- peripheral이 서비스의 특성을 발견했다고 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: (any Error)?) {
print("peripheral이 서비스의 특성을 찾았다")
}
func peripheral(CBPeripheral, didDiscoverDescriptorsFor: CBCharacteristic, error: (any Error)?)
- peripheral이 특성과 서술자를 발견했다고 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: (any Error)?) {
print("Descriptor를 찾았다 \(characteristic.descriptors?.first)")
}
특성과 서술자 값을 가져오기
func peripheral(CBPeripheral, didUpdateValueFor: CBCharacteristic, error: (any Error)?)
- 명시한 특성의 값을 성공적으로 가져오거나 특성의 값이 바뀌었을 때 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: (any Error)?) {
print("특성의 값이 바뀌었을 때 호출되는 메서드")
}
func peripheral(CBPeripheral,didUpdateValueFor: CBDescriptor,error: (any Error)?)
- 명시한 특성 기술자의 값을 성공적으로 가져왔을 때 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: (any Error)?) {
print("Descriptor의 값을 설공적으로 가져왔을 때 호출되는 메서드")
}
특성과 서술자 값 쓰기
func peripheral(CBPeripheral, didWriteValueFor: CBCharacteristic, error: (any Error)?)
- peripheral이 성공적으로 특성의 값을 설정했을 때 delegate에 전달
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: (any Error)?) {
print("peripheral 이 성공적으로 특성의 값을 설정했을 때 호출")
}
func peripheral(CBPeripheral, didWriteValueFor: CBDescriptor, error: (any Error)?)
- perpheral이 성공적으로 Descriptor의 값을 설정했을 때 delegate에 전달되어 호출되는 메서드
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: (any Error)?) {
print("peripheral이 성공적으로 Descriptor의 값을 설정했을 때 호출")
}
func peripheralIsReady(toSendWriteWithoutResponse: CBPeripheral)
- peripheral이 특성의 값을 업데이트할 수 있을 때 해당 peripheral를 delegate에 전달되어 호출되는 메서드
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
print("peripheral이 특성의 값을 반환값 없이 업데이트할 수 있을 때 해당 peripheral")
}
특성 값에 대한 알림 관리
func peripheral(CBPeripheral, didUpdateNotificationStateFor: CBCharacteristic, error: (any Error)?)
- 이 메서드는 BLE Peripheral에서 특정 Characteristic에 대해 알림 Notification이 활성화되거나 비활성화된 상태가 변경되었을 때 호출됩니다. 특성의 알림 상태 변경을 처리하는 역할을 합니다.
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: (any Error)?) {
print("이 메서드는 BLE Peripheral에서 특정 Characteristic에 대해 알림 Notification이 활성화되거나 비활성화된 상태가 변경되었을 때 호출")
}
Peripheral의 RSSI 데이터 가져오기
func peripheral(CBPeripheral, didReadRSSI: NSNumber, error: (any Error)?)
func peripheralDidUpdateRSSI(CBPeripheral, error: (any Error)?) (Deprecated)
- peripheral의 현재 수신 신호 강도 표시기(RSSI)의 값을 읽었을 때 성공했다고 delegate에 전달되어 호출되는 메서드
- Peripheral과 Central 간의 신호 세기를 측정하고, 장치의 거리 추정이나 연결 상태를 판단하는 데 사용됩니다.
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: (any Error)?) {
print("Peripheral 의 RSSI를 검색하는 것이 성공했을 때")
}
Peripheral의 이름 또는 서비스의 변화 모니터링
func peripheralDidUpdateName(CBPeripheral)
- peripheral의 이름이 바꼈을 때 delegate에 전달되어 호출되는 메서드
func peripheralDidUpdateName(_ peripheral: CBPeripheral) {
print("peripheral의 이름이 변경되었을 때")
}
func peripheral(CBPeripheral, didModifyServices: [CBService])
- peripheral의 서비스가 바꼈을 때 delegate에 전달되어 호출되는 메서드
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
print("peripheral의 서비스가 바꼈을 때")
}
CBPeripheralManager
class CBPeripheralManager: 앱에 의해 노출된 블루투스 기기의 서비스를 관리하고 advertises하는 객체입니다.
- CBPeripheralManager 메서드를 호출하기 전에 블루투스가 켜져있어야 합니다
- 앱이 Peripheral 로 작동하여 서비스와 특성을 Central 에 노출하고 광고하는 데 사용됩니다.
CBPeripheralManagerDelegate
protocol CBPeripheralManagerDelegate: 연결된 블루투스 기기의 상태와 상호 작용에 대한 정보를 제공하는 프로토콜입니다.
- peripheral 장치의 서비스 및 특성 탐색, 데이터 읽기/ 쓰기, 알림 상태 변경과 같은 이벤트를 처리할 수 있도록 합니다.
CBAttribute
class CBAttribute: 블루투스 기기로부터 제공받은 서비스에 대한 공통적인 속성을 표현하는데 사용됩니다.
- peripheral 이 제공하는 service, characteristic, descriptor 를 나타내는 공통적인 속성을 정의한 기본 클래스입니다.
- CBAttribute 는 BLE 구조에서 Peripheral 이 제공하는 다양한 데이터를 표현하는 데 사용됩니다.
- 하위 클래스로가 바로 CBService, CBCharacteristic, CBDescriptor 입니다.
CBAttributePermissions
struct CBAttributePermissions: 특성의 값을 읽고 쓰는 권한 및 암호화에 대한 값을 나타내는 열거형(Enum)입니다.
- 이는 peripheral 에서 central 의 요청을 허용하거나 제한하기 위해 사용됩니다.
Permissions
readable
- Peripheral 이 특성 값을 읽을 수 있도록 허용
writable
- peripheral 이 특성 값을 쓸 수 있도록 허용
readEncryptionRequired
- peripheral 이 암호화된 연결에서만 특성 값을 읽을 수 있도록 허용
- 민감한 데이터를 보호할 때 사용될 수 있습니다.
writeEncryptionRequired
- peripheral 이 암호화된 연결에서만 특성 값을 쓸 수 있도록 허용
- 안전한 장치 제어(IoT 장치 설정) 등에 사용될 수 있습니다.
기본 읽기 / 쓰기 Characteristic
let characteristic = CBMutableCharacteristic(
type: CBUUID(string: "2A37"),
properties: [.read, .write],
value: nil,
permissions: [.readable, .writeable]
)
- Peripheral 로서 Central 에게 Chracteristic 값을 보내기 위해 만든 코드입니다.
- CBMutableCharacteristic 구조체를 사용하여 특성값을 만드는 데 Central 이 읽기와 쓰기를 모두 할 수 있도록 허용 설정을 합니다
암호화된 연결에서만 읽기 / 쓰기
let characteristic = CBMutableCharacteristic(
type: CBUUID(string: "2A37"),
properties: [.read, .write],
value: nil,
permissions: [.readEncryptionRequired, .writeEncryptionRequired]
)
- Peripheral 로서 Central 에게 Characteristic 값을 보내기 위해 만든 코드입니다.
- 암호화된 연결에서만 읽기 및 쓰기가 가능하도록 설정합니다.
그렇다면 암호화는 누가 처리하는가?
암호화는 Central 과 Peripheral 이 함께 협업을 통해 이루어집니다.
Peripheral 역할
Peripheral 은 .readEncryptionRequired, writeEncryptionRequired 를 통해 암호화 조건을 결정합니다.
암호화되지 않은 연결에서 요청이 들어오면 요청을 거부합니다.
Central 역할
Central은 Peripheral 의 암호화 요구에 따라서 연결을 설정합니다.
Central과 Peripheral 이 페어링 과정을 통해 보안 연결을 설정합니다.
pairing: Central 과 Peripheral 이 인증 정보를 교환합니다. (키 교환, 인증 절차 등등)
Encryption: 페어링 후 Central 과 Peripheral 간의 데이터가 암호호됩니다.
암호화 과정
암호화는 BLE 프로토콜의 Security Manager Protocol (SMP) 을 통해 처리됩니다.
* Central 이 연결 초기화
* Peripheral 이 암호화 요구 전달
* 서로 암호화된 키를 교환하여 보안 연결 설정
암호화 연결 작업 흐름 정리
1. Peripheral 이 특성을 정의
- permissions 에 암호화 요구를 설정
2. Central 이 Peripheral 에 연결
- Peripheral 의 특성을 탐색하고 요청 작업(읽기, 쓰기..)을 시도
3. Peripheral 이 요청 확인
- 암호화 요구 조건을 확인
- 조건 충족: 요청 승인
- 조건 미충족: 요청 거부
4. Central 이 암호화 작업 수행
- Peripheral 이 암호화를 요구하면 Central 은 암호화된 연결을 설정하고 요청을 다시 시도
암호화는 나중에 따로 더 다루겠습니다.
Service
Services
Services는 Bluetooth Low Energy (BLE) 통신에서 사용되는 중요한 개념 중 하나입니다. BLE 장치는 서비스(Service)를 제공하여 특정 기능이나 특성을 나타냅니다. 이 서비스는 주로 특정한 데이터나 동작을 캡슐화하고 제공합니다. 여러 서비스가 하나의 BLE 장치에 존재할 수 있으며, 각 서비스는 고유한 서비스 UUID(범용 고유 식별자)를 가지고 있습니다. 이러한 서비스는 다양한 BLE 프로파일(profile)에 따라 정의될 수 있습니다. 예를 들어, 심박수 모니터 장치는 심박수 측정 서비스를 제공하고, 환경 센서 장치는 온도 및 습도 측정 서비스를 제공할 수 있습니다. 각 서비스는 하나 이상의 특성(Characteristic)으로 구성됩니다. 이러한 특성은 해당 서비스의 특정 데이터를 나타냅니다. 서비스의 특성은 읽을 수 있거나 쓸 수 있을 뿐만 아니라 알림(Notification)이나 인디케이션(Indication)을 통해 변경 사항을 알리는 데 사용될 수도 있습니다. 따라서 서비스는 BLE 장치가 제공하는 기능이나 특성을 정의하고, 특성은 해당 서비스의 데이터를 나타냅니다. 서비스와 특성을 통해 BLE 장치 간의 효율적인 통신 및 데이터 교환을 가능하게 합니다.
CBService
class CBService: 클래스는 디바이스의 기능 또는 특성을 수행하는 데이터와 관련된 동작의 모음입니다.
서비스 식별
var peripheral: CBPeripheral?
- 서비스의 주체가 되는 peripheral
var isPrimary: Bool
- 서비스가 우선순위인지 아닌지 Bool 값
서비스 데이터에 접근
var characteristics: [CBCharacteristic]?
- 서비스에서 발견된 특성들 리스트
var includedServices: [CBService]?
- 서비스에 안에 포함되어 있는 서비스들
CBMutableService
class CBMutableService: 특성 값을 변경 가능한 서비스를 나타냅니다.
새로운 변경 가능한 서비스 만들기
init(type: CBUUID, primary: Bool)
- UUID와 서비스 유형으로 지정된 새로 초기화된 변경 가능한 서비스를 만듭니다.
변경 가능한 서비스 관리
var characteristics: [CBCharacteristic]?
- 서비스의 특성들을 나타내는 리스트
var includedServices: [CBService]?
- 포함되어 있는 서비스들
CBCharacteristic
class CBCharacteristic: 블루투스 기기의 서비스에 속한 특성을 나타냅니다. 특성은 특정한 데이터 값을 나타내며, 해당 데이터의 읽기 및 쓰기 권한 설정을 포함합니다. 예를 들어, 심박수 모니터의 서비스에는 심박수 측정값을 나타내는 특성이 있을 수 있습니다.
- 특성은 하나의 값과 서술자를 나타내는 숫자를 포함하고 있습니다
- CBChrarcteristic 프로퍼티들은 특성의 값을 어떻게 사용하고 서술자 값에 접근하는지 결정합니다.
특성 식별
var service: CBService?
- 특성이 포함되어 있는 서비스
특성 데이터에 접근
var value: Data?
- 특성의 값
var descriptors: [CBDescriptor]?
- 특성에서 발견된 서술자들을 포함한 리스트
var properties: CBCharacteristicProperties
- 특성의 프로퍼티들
struct CBCharacteristicProperties
- 가능한 특성의 프로퍼티들을 나타내는 값
var isNotifying: Bool
- 특성이 현재 구독된 Central에 그 값을 알리고 있는지 여부를 나타내는 Bool 값
var isBroadcasted: Bool
- 특성을 서비스가 홍보하고 있는지를 나타내는 Bool 값
CBMutableCharacteristic
class CBMutableCharacteristic: 블루투스 기기의 변경 가능한 특성을 나타냅니다.
- peripheral 서비스의 특성을 나타냅니다
- 이 클래스를 사용하여 특성을 만들고 원하는 대로 속성과 권한을 설정합니다.
- 로컬 서비스에 특성을 만들고 추가한 후, CBPeripheralManager 클래스의 add(_:) 메서드를 사용하여 peripheral의 로컬 데이터베이스에 게시할 수 있습니다.
- 특성을 게시한 후에는 코어 블루투스는 특성을 캐시하고 변경할 수 없습니다.
- 변경 가능한 특성 관리
var value: Data?
- 특성의 값
var descriptors: [CBDescriptor]?
- 특성의 서술자들을 포함한 배열
var properties: CBCharacteristicProperties
- 특성의 프로퍼티들
var permissions: CBAttributePermissions
- 특성 값에 허가 (readable, writeable, readEncryptionRequired, writeEncryptionRequired)
struct CBAttrbutePermissions
- 특성의 값에 대한 읽기, 쓰기 및 암호화 권한을 나타내는 값
var subscribedCentrals: [CBCentral]?
- 현재 구독되어 있는 특성의 값의 central들 리스트
CBDescriptor
class CBDescriptor: 블루투스 기기의 특성에 대한 추가 정보를 제공합니다. 이 정보는 특성 값의 형식, 단위, 범위 등과 같은 특성에 대한 세부 정보를 제공합니다.
- CBDescriptor 객체는 perihperal의 특성의 추가적인 정보를 나타냅니다
- CBDescriptor는 CBCharacteristic에 여러개가 배열로 포함되어 있을 수 있습니다.
- CBCharacteristic는 하나의 value와 여러개의 CBDescriptor를 포함할 수 있어 특성의 추가적인 정보를 내포할 수 있습니다.
Descritpor 식별하기
var characteristic: CBCharacteristic?
- descriptor가 포함되어 있는 특성
Descriptor 데이터에 접근하기
var value: Any?
- descriptor의 값
CBMutableDescriptor
class CBMutableDescriptor: 블루투스 기기의 특성에 추가적인 정보를 제공합니다.
지원하는 타입
class CBManager
- central과 peripheral 객체들을 관리하는 추상 기본 클래스
class CBATTRequest
- Attribute Protocol (ATT)를 사용하는 요청
class CBPeer
- 원격 기기를 나타내는 객체
class CBUUID
- 블루투스 표준에 의해 정의된 보편적으로 고유한 식별자.
에러
struct CBError
- 블루투스 통신 중에 코어 블루투스가 반환하는 오류
let CBErrorDomain: String
- Core Bluetooth 에러를 위한 domain
enum CBError.Code
- 블루투스 통신 중에 발생하는 Core Bluetooth 에러 코드
struct CBATTError
- Attribute Protocol (ATT)를 사용하는 도중 생기는 Core Bluetooth 에러
let CBATTErrorDomain: String
- Core Bluetooth ATT 에러를 위한 domain
enum CBATTError.code
- BLE ATT 통신 중 GATT 서버에 의해 반환받는 가능한 에러
'SwiftUI' 카테고리의 다른 글
SwiftUI - 접근제어자 (0) | 2024.11.17 |
---|---|
SwiftUI - 소켓 통신으로 영상 전송받기 (0) | 2024.11.13 |
SwiftUI - 소켓 통신 (5) | 2024.11.12 |
SwiftUI - 커스텀 카메라 (0) | 2024.11.11 |
WidgetKit - UserDefault 로 위젯과 앱 데이터 공유하기 (0) | 2024.10.23 |