: 안드로이드 블루투스 통신을 마지막단에서 반대로 분석을 해 봤습니다.
Bluetooth LE 장치와 스마트폰사이에 마지막 단인 Gatt 통신을 하기위한 클래스는 BluetoothGatt 이고
이부분을 직접 넣으면서 추가로 필요한 사항을 찾아 보겠습니다.
▶ 안드로이드 BluetoothGatt 관련 사이트 링크
https://developer.android.com/reference/android/bluetooth/BluetoothGatt
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 객체도 추가가 필요합니다.
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 주소를 넣어줍니다.
→ BluetoothAdapter 구하기
: BluetoothManager.getAdapter()를 통해서 구하면 됩니다.
→ 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' 카테고리의 다른 글
[Android App] BLE GATT:: [3] Characteristic /Descriptor 출력하기 (0) | 2023.04.27 |
---|---|
[Android App] BLE GATT:: [2] 서비스 리스트 출력하기 (0) | 2023.04.25 |
[Android App] build Error :: This version (1.2.0) of the Compose Compiler requires Kotlin version 1.7.0 (0) | 2023.04.12 |
[Android App] Composable Scaffold 에서 SnackBar 사용하기 (0) | 2023.04.10 |
[Android app] startActivityForResult () 사용 및 대체하기 (0) | 2023.04.04 |