Kotlin

BLE 통신

flutter 개발하자 2021. 1. 5. 09:19

BLE란 BluetoothLowEnergy의 약자로 저전력 블루투스를 의미한다. BLE와 일반 블루투스(클래식 블루투스)와의 차이는 전력도 있지만, 보낼 수 있는 데이터의 양도 있다. BLE는 블루투스에 비해 적은 양의 데이터를 보낼 수 있지만, 속도는 일반 블루투스에 비해 뒤쳐지지않는다. 또한 BLE와 블루투스 통신 방법에도 크게 차이가 있다. BLE는 GATT이라는 특성을 이용해 통신을 하게 된다. 이를 자세히 알아보자. 

ATT란?

ATT란 AttributeProtocol의 약자로 서버와 클라이언트 사이의 데이터 교환 규칙을 정의하는 역할을 한다.

GATT이란?

GATT란 GenericAttributeProfile의 약자로 BLE의 데이터 교환을 관리하는 역할을 한다. 디바이스들이 데이터를 발견하고,읽고, 쓰는 것을 가능하게 하는 데이터 모델과 절차를 정의한다. 즉, 데이터가 어린이면, BLE와 디바이스는 유치원과 집, GATT은 유치원 선생님이라고 생각하면 된다. 통학과정에서 어린이가 집에서 유치원을 갈때와 돌아올때, 유치원 선생님이 어린이를 통제하듯이 GATT가 데이터를 통제한다고 보면 된다. 

 

GATT의 구조는 아래와 같다.

Services           - 1개이상의 Characteristic 갖고 있음(최상위 구조체)

                     - 1개의 서비스마다 고유의 uuid를 갖고 있음

Characterisctic - 개별 항목의 상태 정보, 타입, 관련값, 추가적인 작동등에대한 설정

                    - Descriptor와 value를 갖고 있음

                    - 고유의 uuid가 할당됨

Descriptor      - 특정 Characteristic에 속하며,Characteristict을 설명하는 메타데이터를 갖고 있음.

                   - 즉, value를 설명하는 정보를 갖고 있음

 

*uuid - 블루투스 장치에서 제공하는 서비스를 식별

*profile - 기기에 어떻게 연결하는 지에대한 것

 

 

이제 안드로이드와 BLE 통신의 연결 및 데이터 통신 과정 순서를 알아보자.

1.BLE 스캔

 1.1)안드로이드 위치 권한, 블루투스 권한 설정

ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION, BLUETOOTH, BLUETOOTH_ADMIN

 1.2)Manifest에 ble만 스캔하게 하는 설정

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

 1.3)블루투스 검색

 블루투스 어댑터를 정의하고, 블루투스 어댑터를 이용해 블루투스 기기를 스캔한다.(startLeScan)

 startLeScan의 인자로 스캔콜백이 들어가야함.(LeScanCallback)

2. BLE 연결 및 데이터 통신

 2.1)블루투스 어댑터를 정의하고, 어댑터에서 블루투스 기기의 하드웨어 주소를 갖고 온다.(getRemoteAddress)

 2.2)위의 주소 객체에 connectGatt을 하면 기기와 블루투스가 연결이 된다.

 connectGatt의 3번째 인자로 BluetoothGattCallback이 들어가는데, 이는 연결하기 전에 정의를 해놔야한다. 

****

BluetoothGattCallback

기기와 블루투스 사이의 통신에 관련된 콜백으로 다음과 같은 함수들을 오버라이드함.

onConnectionStateChange - 통신 상태(연결 상태) 관리

onServicesDiscovered - 서비스가 발견됐을 때

onCharacteristicRead - Characteristic을 읽었을 때

onCharacteristicChanged - Characteristict이 변화했을 때

 

onConnectionStateChange는 연결이 됐을 때, 연결중일때, 연결이 끊겼을 때와 관련된 콜백 함수이다.

 

onServicesDiscovered는 말그대로 서비스가 발견 됐을 때와 관련된 콜백함수이다.

onServicesDiscovered에서는 발견된 서비스에서 기기에서 찾는 서비스를 찾고(uuid로 구분), 그 서비스 내에서 기기에서 사용할 Characterisc(BluetoothGattCharacteristic)들을 찾는다(마찬가지로 uuid로 구분).

그리고 readCharacteristic으로 블루투스 기기의 Characteristic을 읽고, setCharacteristicNotification으로 이 characteristic의 변화에대한 콜백을 설정한다.

또한, writeDescriptor로 Characteristic의 descriptor에 원하는 값을 쓸 수 있게 설정한다. (이 부분이 안드로이드에서 블루투스 기기에 데이터를 보내기 위한 세팅이라고 생각하면 된다.)

그리고 writeCharacteristic으로 Characteristic의 value를 넣고, 덮어쓰면 블루투스 기기에 value에 해당하는 데이터가 전달된다. 

블루투스 기기에서 이에대한 response로 데이터를 보내면, 여기서 위의 readCharacteristic에서 설정한 콜백이 발생하고, onCharacteristicRead와 onCharacteristicChanged가 발생한다.

onCharacteristicRead는 최초 한 번만 콜백하고 이후에는  onCharacteristicChanged가 콜백한다.

**** 

************************************************************************************************************

Characteristict과 관련된 함수들은 모두 동기화로 진행해줘야한다. 위에 써놨듯이 characteristic과 관련된 순서가 있는데, 비동기로 하면 위의 순서를 무시하게 되기 때문에 오류가 발생한다.

kotlin에서는 코루틴을 이용해 runblocking을 하는 등의 처리를 해줘야한다.(동기화로 만들기 위해서)

*************************************************************************************************************