투케이2K

618. (ios/swift5) [간단 소스] 블루투스 GATT 클라이언트 , Remote 원격 서버 연결 소스 코드 - Bluetooth GATT Client 본문

IOS

618. (ios/swift5) [간단 소스] 블루투스 GATT 클라이언트 , Remote 원격 서버 연결 소스 코드 - Bluetooth GATT Client

투케이2K 2025. 1. 2. 18:52

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : SWIFT5


 

[소스 코드]

// --------------------------------------------------------------------------------------
[개발 및 테스트 환경]
// --------------------------------------------------------------------------------------

- 언어 : Swift

- 개발 툴 : Xcode

- 기술 구분 : Bluetooth / Gatt / Client

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[사전) 권한 설정 및 import 추가 관련]
// --------------------------------------------------------------------------------------

/**
* // ------------------------------------------------------
* 1. 필요 퍼미션 권한 설정 : 블루투스 사용 권한
*
* - Privacy - Bluetooth Always Usage Description
* - Privacy - Bluetooth Peripheral Usage Description
* // ------------------------------------------------------
* 2. 필요 import 호출 선언 :
*
* - import CoreBluetooth
* - import UIKit
* // ------------------------------------------------------
* 3. 참고 사항 :
*
* - 블루투스 GATT 연결을 하기 위해서는 사전 페어링이 되어 있어야합니다 (클라이언트 To 서버)
* - 서버 연결 시 블루투스 Name 값을 기준으로 찾는다
* - 클라이언트와 서버 연결을 위해서는 ServiceUUID , CharacteristicUUID 값이 일치해야합니다
* // ------------------------------------------------------
* */

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[소스 코드]
// --------------------------------------------------------------------------------------

// -----------------------------------------------------------------------
// MARK: - [전역 변수 선언]
// -----------------------------------------------------------------------
public static let ACTIVITY_NAME = "C_Bluetooth_Gatt_Client_Module"

let SEARCH_BLUETOOTH_NAME = "TWOK-BLUETOOTH" // MARK: [찾으려는 블루투스 이름] : 연결할 Gatt 서버 이름

static var scanBluetoothCentralManager: CBCentralManager! // [블루투스 활성 상태 확인 및 실시간 블루투스 신호 스캔]
static var discoveredPeripheral: CBPeripheral? // [블루투스 연결에 필요한 객체]
var scanBluetoothList: Array<Dictionary<String, String>> = [] // [블루투스 스캔 리스트를 담을 변수]

// -----------------------------------------------------
// MARK: [UUIDs for the GATT service and characteristic] : 서버 및 클라이언트 UUID 값이 일치해야합니다
// -----------------------------------------------------
// MARK: [Android , Ios 간에는 UUID 형식이 다르게 표현 될 수 있습니다]
// -----------------------------------------------------
let targetServiceUUID = CBUUID(string: "0000180F-0000-1000-8000-00805F9B34FB") // 대상 서비스 UUID
let targetCharacteristicUUID = CBUUID(string: "00002A19-0000-1000-8000-00805F9B34FB") // 대상 특성 UUID



// -----------------------------------------------------------------------
// MARK: - [블루투스 권한 확인 요청]
// -----------------------------------------------------------------------
DispatchQueue.main.async {
         
    // [블루투스 권한 확인]
    C_Bluetooth_Gatt_Client_Module.scanBluetoothCentralManager = CBCentralManager(delegate: self, queue: nil)
    
}



// -----------------------------------------------------------------------
// MARK: - [블루투스 권한 부여 상태 확인 함수] : CBCentralManagerDelegate : Delegate
// -----------------------------------------------------------------------

func centralManagerDidUpdateState(_ central: CBCentralManager) {
        
    switch central.state {

    case .unknown:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unknown :: 블루투스 상태 알수 없음", data: nil)


    case .resetting:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: resetting :: 블루투스 서비스 리셋", data: nil)
        

    case .unsupported:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unsupported :: 기기가 블루투스를 지원하지 않습니다", data: nil)
        

    case .unauthorized:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unauthorized :: 블루투스 사용 권한 확인 필요", data: nil)
        
        // MARK: [앱 블루투스 권한 사용 설정창 이동 및 확인 필요]


    case .poweredOff:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: poweredOff :: 블루투스 비활성 상태", data: nil)
        
        // MARK: [자동으로 시스템에서 비활성 상태 알림 및 팝업창 호출 실시]


    case .poweredOn:
        S_Log._W_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: poweredOn :: 블루투스 활성 상태", data: nil)
        
        // ---------------------------------------------------------
        // MARK: [블루투스 실시간 스캔 시작] : centralManager : didDiscover : 함수에서 확인
        // ---------------------------------------------------------
        if C_Bluetooth_Gatt_Client_Module.scanBluetoothCentralManager != nil {
            
            // ----------------------------------
            // MARK: [전체 블루투스 목록 스캔]
            // ----------------------------------
            C_Bluetooth_Gatt_Client_Module.scanBluetoothCentralManager?.stopScan() // [사전 블루투스 스캔 종료]
            C_Bluetooth_Gatt_Client_Module.scanBluetoothCentralManager?.scanForPeripherals(withServices: nil, options: nil)
            
        }
        else {
            S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: poweredOn :: 실시간 블루투스 리스트 스캔 실패", data: ["scanBluetoothCentralManager is null"])
        }


    @unknown default:
        S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: 블루투스 권한 :: default :: 상태", data: nil)
        
        self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
    }

}



// -----------------------------------------------------------------------
// MARK: - [실시간 블루투스 스캔 리스트 확인 및 GATT 연결 수행 함수]
// -----------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
    if peripheral.name == self.SEARCH_BLUETOOTH_NAME {
        print("🔵 :: didDiscover :: 실시간 블루투스 스캔 정보 >> NAME - \(String(peripheral.name ?? "")) / UUID - \(String(peripheral.identifier.uuidString)) / RSSI - \(String(RSSI.intValue))")
    }
    else {
        print("🔴 :: didDiscover :: 실시간 블루투스 스캔 정보 >> NAME - \(String(peripheral.name ?? "")) / UUID - \(String(peripheral.identifier.uuidString)) / RSSI - \(String(RSSI.intValue))")
    }
    

    // -------------------------------------------------
    // MARK: [스캔 리스트에 저장할 포맷 지정]
    // -------------------------------------------------
    var bleScanDic : Dictionary<String, String> = [String : String]()
    bleScanDic["NAME"] = "\(String(peripheral.name ?? ""))"
    bleScanDic["UUID"] = "\(String(peripheral.identifier.uuidString))"
    //bleScanDic["RSSI"] = "\(String(RSSI.intValue))"

    
    // -------------------------------------------------
    // MARK: [블루투스 GATT 연결할 서버 이름을 찾은 경우 >> 연결 수행 실시]
    // -------------------------------------------------
    if self.scanBluetoothList != nil
        && self.scanBluetoothList.contains(bleScanDic) == false
        && peripheral.name == self.SEARCH_BLUETOOTH_NAME
        && self.scanBluetoothList.count < 1 { // MARK: [배열에 하나만 저장 수행]
        
        
        // MARK: [배열에 추가]
        self.scanBluetoothList.append(bleScanDic)
        
        
        // MARK: [서버 연결에 필요한 peripheral 삽입]
        peripheral.delegate = self // [딜리게이트 지정]
        C_Bluetooth_Gatt_Client_Module.discoveredPeripheral = peripheral // [static 객체에 추가]
        
        
        // MARK: [연결 nil 옵션 지정]
        C_Bluetooth_Gatt_Client_Module.scanBluetoothCentralManager?.connect(peripheral, options: nil)
        
        
        // MARK: [로그 출력]
        S_Log._W_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: didDiscover :: 블루투스 연결 정보 확인 및 연결 수행", data: [
            "NAME :: \(C_Bluetooth_Gatt_Client_Module.discoveredPeripheral?.name ?? "")",
            "UUID :: \(C_Bluetooth_Gatt_Client_Module.discoveredPeripheral?.identifier.uuidString ?? "")"
        ])
    }

}



// -----------------------------------------------------------------------
// MARK: - [블루투스 주변 장치 연결 성공] : CBCentralManagerDelegate : didConnect
// -----------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    S_Log._W_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: didConnect :: 실시간 블루투스 GATT 서버 연결 성공", data: ["Connected Name :: \(peripheral.name ?? "Unknown")"])

}





// -----------------------------------------------------------------------
// MARK: - [블루투스 주변 장치 연결 실패] : CBCentralManagerDelegate : didFailToConnect
// -----------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
    S_Log._E_(description: "\(C_Bluetooth_Gatt_Client_Module.ACTIVITY_NAME) :: didFailToConnect :: 실시간 블루투스 GATT 서버 연결 실패", data: ["Error :: \(error?.localizedDescription ?? "Unknown error")"])
    
}

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------

[IOS - 실시간 블루투스 목록 스캔]

https://blog.naver.com/kkh0977/223704822567


[CLLocationManager 간단 설명 및 사용 옵션 정리 - 위치 이벤트]

https://blog.naver.com/kkh0977/223690092317

// --------------------------------------------------------------------------------------
 
반응형
Comments