티스토리 뷰

2023.06.12 - [iOS개발/Swift 통신] - Swift 블루투스 통신 1편 (CBCentralManagerDelegate)

 

Swift 블루투스 통신 1편 (CBCentralManagerDelegate)

이번 포스트에서는 실생활에서 많이 쓰이는 블루투스 통신의 기본을 알아볼까합니다. 블루투스의 경우에는 내용이 많기도 하고 두가지로 나누어 설명하는게 좋을것 같아서 1편 CBCentralManagerDeleg

world-of-larooly.tistory.com

이번에는 저번에 말씀드렸다시피 

블루투스 2탄 CBPeripheralDelegate 에 대해 알아봅시다.

2탄! 입니다

 

앞에서는 기기와 앱을 연결했죠? 

이제 연결한 기기와 데이터를 주고 받아 봅시다.

 

* 미리 알아두면 좋은 개념

CBPeripheral : 원격 주변 장치

CBCharacteristic : 원격 주변 장치 서비스의 특성(각 서비스들의 고유 ID라 생각하시면 될듯합니다.)

CBService : 장치의 기능 또는 기능을 수행하는 데이터 및 관련 동작

 

해당 부분에 대해 아직 감이 안오시는 분들을 위해

예시를 들자면 

블루투스 기기에 음악 정보를 보내주는 것,

블루투스 체중계로부터 몸무게를 받아오는 것

과 동일하다고 생각하시면 됩니다. 

 

* 이번 글은 이미 기기가 연결되어있다는 전제하에 작성되었습니다.

* 기기 연결은 1편을 참고해주세요.

 

이미 기기를 연결하셨다면 아래 부분이 있으실꺼에요.

여기에 delegate 를 추가해서 송수신을 받아볼께요

(didDiscover 랑 didConnect 는 연결 전 / 연결 후 차이 입니다.)

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    ....
    // peripheral 가 원하는 기기가 맞는 경우에만
    self.bluefruitPeripheral = peripheral
    self.bluefruitPeripheral.delegate = self 
    // 송수신 받기 delegate 시작
    ....
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    print("[ViewController] 블루투스 연결 완료")
    ....
    self.bluefruitPeripheral.discoverServices(self.ServiceUUid)
    // 저는 UUID를 가지고 있었지만 없으신 분들은 nil 적어주시면 됩니다
    ....
}

 

이제 아래처럼 선언이 가능해졌습니다. 

// MARK: - CBPeripheralDelegate - 블루투스 기기 관련
//
extension ViewController : CBPeripheralDelegate{
    ....
    ....
}

 

통신의 경우에는 기기마다 차이가 있을 수 있습니다.

 

제가 연결한 기기의 경우 프로토콜 문서를 제공해 주셔서 그걸 기반으로 제작하였지만

기기마다 방식이 다르므로 간단히만 이야기 할께요

  • 데이터를 보내는 방법
  • 어떤 데이터인지는 모르겠지만 수신 이벤트를 받는 방법

이 두가지만 이야기하도록 하겠습니다. 

(저는 그냥 이벤트 받는 부분에서 브레이크 포인트로 내부 값을 다 뜯어 봤습니다;;)

 

보내는 법부터 보도록 합시다 

보내는 법은 역시 정해진 규정에 따라 보내야하지만

일단 그냥 데이터(Data)를 보낸다는 가정하에 진행하겠습니다. 

 

참고로 시작전에 편안한 데이터 시청(?)을 위해 미리 추가해 두는걸 권장드립니다. 

import Foundation
extension Data {
    var bytes: [UInt8] {
        return [UInt8](self)
    }
}
extension Array where Element == UInt8 {
    var data: Data {
        return Data(self)
    }
}

 

블루투스 데이터 전송

사실 모양 자체는 어려울게 없긴합니다.

let sendData = Data(data) // 보내고 싶은 데이터를 넣어주세요.
if let device = self.connectedPeripheral{
	// 기기마다 약속된 값이 제공되있는경우도 있고 아니면 찾아서 넣어주셔도 됩니다. 
    if let charistic = self.connectedTXChar{
        device.writeValue(sendData, for: charistic,type: .withResponse)
        // type : 이 값을 보낸후 오는 값을 받을지 말지를 의미합니다.
    }
}

문제는 저 connectedTXChar 가 무엇이냐인데

이거는 기본적으로 기기랑 통신할때 받을수있어요

이는 아래를 참고하시면 편할것같아요

 

블루투스 데이터 수신

길이는 길지만 자세히보면 어렵지 않습니다.

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    print("[ViewController] 블루투스 기기 didDiscoverCharacteristicsFor")
    guard let characteristics = service.characteristics else {
        return
    }
    for characteristic in characteristics {
        if self.RXCharUUid.contains(characteristic.uuid){
            rxCharacteristic = characteristic
            self.connectedRXChar = rxCharacteristic

            peripheral.setNotifyValue(true, for: rxCharacteristic!)
            peripheral.readValue(for: characteristic)

            print(characteristic.value)
            // 여기서부터는 기기마다 값을 보내는 방식이 다르니 브레이크걸고 확인 바랍니다. 
            // 저희꺼는 좀 특이해서 아래 방식을 사용했습니다.
            if let services = characteristic.service?.characteristics{
                for i in services {
                    if(i.value?.count ?? 0 > 0 ){
                        print("[ViewController] Received : " + (i.value?.bytes.description)!)
                    }
                }
            }
      }
      // 일반적으로 회사에서 특정 UUID를 주는데 
      // 해당 UUID인 경우 아래처럼 UUID를 받아서 데이터를 보낼수있습니다.
        if(self.TXCharUUid.contains(characteristic.uuid)){
            txCharacteristic = characteristic
            self.connectedTXChar = txCharacteristic
            let sendData = Data(self.setTimeWithCallback())
            // 보낼 데이터는 알맞게 만들어서 보내주세요
            bluefruitPeripheral.writeValue(sendData, for: self.connectedTXChar!,type: .withResponse)
      }
    }
}

참고로 기기에 따라 아래처럼 오는 경우도 있으니 자신의 기기에 맞는 방법을 추천드립니다.

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    print("[ViewController] 블루투스 기기 didDiscoverServices")
    guard let services = peripheral.services else { return }
    for service in services {
      peripheral.discoverCharacteristics(nil, for: service)
    }
    self.connectedService = services[0]
    // 이부분은 연결된 서비스를 확인하는 부분입니다.
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
    print("[ViewController] 블루투스 기기 didWriteValueFor")
    guard error == nil else {
        print("[ViewController] Error discovering services: error")
        return
    }
    // 위와 동일하게 이건 우리쪽 기기가 값을 보내는 방법인거고요
    // 맞는 방법 찾아주시면 됩니다.
    print(characteristic.value)
    if let services = characteristic.service?.characteristics{
        for i in services {
            if(i.value?.count ?? 0 > 0 ){
                // 블루투스 기기에서 받은 값 분석 
                print( i.value!.bytes)
            }
        }
    }
}

사실 이 부분들의 경우에는 

기기마다 다르게 작동하기때문에 

반응하는 함수들의 내부 값을 직접 보시고 하시는걸 권장드립니다. 

 

여기서는 흐름만 보여드리는 것이기 때문에 

직접 하시면서 본인의 기기에 맞게 만드시는건 어떨까요? 

 

오늘도 파이팅입니다~

댓글