본문으로 바로가기

:  안드로이드 블루투스 통신을 마지막단에서 반대로 분석을 해 봤습니다.

  Bluetooth LE 장치와 스마트폰사이에  마지막 단인 Gatt 통신을 하기위한 클래스는 BluetoothGatt 이고

 이부분을 직접 넣으면서 추가로 필요한 사항을 찾아 보겠습니다.

  

 

  안드로이드 BluetoothGatt 관련 사이트 링크

https://developer.android.com/reference/android/bluetooth/BluetoothGatt

 

BluetoothGatt  |  Android Developers

 

developer.android.com

 

 1> BLE 장치 (GATT Server)  연결하기

  ▶ BluetoothGatt 객체가 이미 존재한다면 BluetoothGatt 클래스안의  connect() 메소드를 사용하면 됩니다.

      가장 쉬운방법이나 처음 한번은 객체를 만들어야 합니다.

  

  ▶ 간단히 소스코드를 만든다면 다음처럼 작성하면 됩니다.

     :  연결관련 코드는 시간이 소요되는 부분이라 서비스 컴퍼넌트에 함수로 만드면 좋습니다.

class BluetoothLeService : Service() {
    var mBluetoothGatt : BluetoothGatt ?= null
    ~~ 중략 ~~

    fun connect(address : String): Boolean{
        if( mBluetoothGatt != null){
            // 1> BluetoothGatt 객체가 있는지 체킹후 connect()통해 연결하기
             if(mBluetoothGatt!!.connect()){
             }
        }else{
            // 2> BluetoothGatt 객체가 없을경우 연결하기
        }
    }
}

 

 

  BluetoothGatt 객체가  없다면 BluetoothDevice 객체의 connectGatt() 메소드를 이용해  연결하라고 문서에 적혀있네요.

     BluetoothGattCallback 객체도 추가가 필요합니다.

BluetoothGatt 클래스
BluetoothDevice 클래스

class BluetoothLeService : Service() {
    var mBluetoothGatt : BluetoothGatt ?= null
    private val mGattCallback = object : BluetoothGattCallback(){
	    ~~ 중략 ~~
    }
    ~~ 중략 ~~

    fun connect(address : String): Boolean{
        if( mBluetoothGatt != null){
            ~~ 중략 ~~
        }else{
            // 2> BluetoothGatt 객체가 없을경우 연결하기
            //  2-1> BluetoothDevice 객체 얻기
            ~~ 중략 ~~
            //  2-2> BluetoothDevice.connectGatt() 메소드 사용하기
            mBluetoothGatt = device.connectGatt(this,false, mGattCallback)
            // 리턴되는 BluetoothGatt 객체를 mBluetoothGatt에 넣어주면 
            // 다음부터는 BluetoothGatt.connect() 를 사용하게 됩니다.
        }
    }
}

 

  BluetoothDevice.connectGatt(...) 를 사용하기위해 BluetoothDevice 객체 얻기

    : BluetoothAdapter 의 getRemoteDevice() 를 사용하라고 적혀 있네요. 인자 String 에는 MAC 주소를 넣어줍니다.

BluetoothDevice 클래스

  → BluetoothAdapter  구하기

   :  BluetoothManager.getAdapter()를 통해서 구하면 됩니다.

BluetoothAdapter 클래스

  → BluetoothManager 구하기

BluetoothManager 클래스

  ==> 여기까지 내용을 코드로 호출하면 다음처럼 하면 됩니다.

btAdapter = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
val device : BluetoothDevice = btAdapter!!.getRemoteDevice(address)

 

    MAC address 구하기

   : BluetoothDevice 문서에 보면  BluetoothAdapter.getBondedDevices() 메소드를 사용하라고 나옵니다.

    Set<BluetoothDevice> 를 리턴하는데  리턴값으로 들어온 Set 타입에서 우리가 연결하려는

   BluetoothDevice를 찾고 .. MAC 주소도 얻을수 있습니다.

  ==> 코드로 구현하면 다음과 같습니다. (길게 적었지만 3줄로 끝)

val devices: Set<BluetoothDevice>? = btAdapter?.bondedDevices
devices!!.forEach {
    Log.d(TAG, "bonded device name = ${it.name}, address = ${it.address}")
}

 

 2> 미처리 사항 정리 

  ▶ BluetoothGattCallback

     : Bluetoothdevice.connectGatt(...,callback) 메소드의 마지막 인자로 GATT Server 장치와

       연결후 들어노는 callback 객체입니다.

 private val mGattCallback = object : BluetoothGattCallback(){
    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
        var intentAction:String
        if(newState == BluetoothProfile.STATE_CONNECTED){
               Log.d(TAG,"Attempting to start service discovery:"+mBluetoothGatt?.discoverServices())
        }else if(newState == BluetoothProfile.STATE_DISCONNECTED){
            Log.i(TAG,"Disconnected from GATT server.")
        }
    }

    override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
        if(status == BluetoothGatt.GATT_SUCCESS){
            Log.w(TAG, "onServiceDiscovered()")
        }else {
            Log.w(TAG, "onServiceDiscovered received: $status")
        }
    }
    
    override fun onCharacteristicRead( gatt: BluetoothGatt?,
        Log.d(TAG,"GATT Callback --> onCharacteristicRead()")
    }

    override fun onCharacteristicChanged( gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic? ) {
        Log.d(TAG,"GATT Callback --> onCharacteristicChanged()")
    }
}

 

  ▶  화면에 gatt server  연결상태 "connect /disconnect" 표시하기    

  : 여기서는 2가지 사항만 알면 됩니다.

    → MyService에서 연결상태 변경시 broadcast로 메시지를 전달하기 / Activity 에서 BroadcastReceiver 통해 받기

  

 

1-1> Gatt Server 연결상태 변경사항 체킹하기
    BluetoothDevice.connectGatt(...) 의 마지막 인자에 사용된 BluetoothGattCallback 에서 수신됩니다.

class BluetoothLeService : Service() {
    ~~ 중략 ~~
    mBtDevice.connectGatt(this,false,mGattCallback)
    private val mGattCallback = object :BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            super.onConnectionStateChange(gatt, status, newState)
            when(newState){
                BluetoothProfile.STATE_CONNECTED -> {  
                    // connectGatt가 호출되고 gatt server 와연결 완료시 나옴.
                    <--- sendBroadcast(...) 넣기
                }
                BluetoothProfile.STATE_DISCONNECTED ->{ 
                    // gatt server와 연결이 끊어지면 나옴.
                    <--- sendBroadcast(...) 넣기
                }
            }
        }
    }
    companion object{
        val ACTION_GATT_CONNECTED = "com.example.test.ACTION_GATT_CONNECTED"
        val ACTION_GATT_DISCONNECTED = "com.example.test.ACTION_GATT_DISCONNECTED"
    }
}

1-2> 연결상태 변경시 Broadcast 메시지 전송하기
    val intent = Intent(ACTION_GATT_CONNECTED)
    sendBroadcast(intent)

1-3> sendBroadcast 메시지를 받기위해 Activity에  BroadcastReceiver 만들기/ 등록하기
class DeviceControlActivity : ComponentActivity() {
    private val mGattUpdateReceiver = object :BroadcastReceiver() {
        override fun onReceive(p0: Context?, p1: Intent?) {
	        ~~ 중략 ~~
        }
    }
    
    override fun onResume() {
        ~~ 중략 ~~
        val filter = IntentFilter()
        filter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED)
        filter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED)
        registerReceiver(mGattUpdateReceiver,filter)
    }
}

 

  → Composable 메서드에서  LiveData 의 observeAsState() 변수  적용시키기 

2-1> ViewModel 에 변수/메서드 생성하기
class MainViewModel: ViewModel() {
    val connState =  MutableLiveData<Int>(0)
    fun setConnectionState(value:Int){
        connState.value = value
    }
}

2-2> Activity 화면 표시하기
  2-2-1> BroadcastReceiver 를 통해 들어온 연결상태 변경 적용하기
class DeviceControlActivity : ComponentActivity() {
	val mainViewModel: MainViewModel by viewModels()
     private val mGattUpdateReceiver = object :BroadcastReceiver() {
        override fun onReceive(p0: Context?, p1: Intent?) {
            val action =p1!!.action
            when(action){
                BluetoothLeService.ACTION_GATT_CONNECTED -> {
                    mainViewModel.setConnectionState(1)
                }
                BluetoothLeService.ACTION_GATT_DISCONNECTED -> {
                    mainViewModel.setConnectionState(0)
                }
            }
        }
    }
}

  2-2-2> Composable 함수 수정하기
class DeviceControlActivity : ComponentActivity() {
    ~~ 중략 ~~
    @Composable
    fun DisplayConnectionState(){
        val connState = mainViewModel.connState.observeAsState()
        Row() {
            Text(text = "State : ")
            Spacer(modifier = Modifier.size(30.dp))
            if (connState.value == 1){
                Text(text = "connected ", color = Color.Green,)
            }else {
                Text(text = "disconnected ",color = Color.Red,)
            }
        }
    }
}

 

 

  ▶ BLE GATT 2번 글 링크

https://leevisual.tistory.com/225

 

[Android App] BLE GATT:: [2] 서비스 리스트 출력하기

BLE GATT:: [1] 번 글 연속 강의 ==> [1]번 글 링크는 마지막에 넣었습니다. : 이번에는 Gatt Server 와 연결이 수립된후 서비스 리스트를 검색해 화면에 출력을 해보겠습니다. 연결이 완료된지는 BluetoothGa

leevisual.tistory.com

 

 

오늘도 수고하세요.

 

반응형