최근 BLE 통신을 하는 앱을 개발하게 되었는데, 기존 안드로이드에서 제공하는 BLE API가 은근 사용 불편해서 괜찮은 라이브러리가 없나 찾아보았다.
2개정도 발견했는데 RxAndroidBle와 Android-BLE-Liblrary이다.
둘다 써보긴했는데 뭔가 Rx는 내손에 안맞아서 Nordic사의 Android-BLE-Liblrary를 채택하게 되었다.
근데 막상 써보려고하니 제대로된 API문서 같은게 하나도 안나와있어서, 여기다가 작성해보려고 한다.
(내 기준) 단계는 다음과 같다.
기기 스캔 -> 연결 -> service discover -> characteristic discover -> 연결완료 -> notift enable -> 데이터 수신
아쉽게도 해당 라이브러리에서는 스캔기능을 지원하지 않고, Android-Scanner-Compat-Library 라는 라이브러리를 따로 사용해야 한다. 기존 안드로이드에서 제공하는 BLE 스캐너와 용법은 동일한데, legacy 버전까지 지원하기 위함으로 보인다.
해당 라이브러리는 API 문서가 나름 잘 나와있으므로, 해당 포스팅에서는 다루지 않아도 될것 같다.
아래 주소를 통해 라이브러리 implement 및 사용법을 확인할 수 있다.
https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library
스캔을 하게 되면 Advertising 하고 잇는 주변 BLE 기기를 스캔해 콜백으로 넘겨준다.
이때 콜백으로 받은 Device 인자를 저장해두고 있다가 이후 연결할 때 사용하면 된다.
스캔 단계에서 스캔한 Device를 연결한다.
처음에 좀 햇갈렸었는데, 연결한다고 바로 데이터 송수신을 하는게 아니라, service, characteristic 까지 모두 찾아낸 이후에 송수신이 가능하다.
말 그대로 연결만 하는듯?
기기와 연결하기 위해선 BleManager를 상속받은 클래스를 새로 생성해 줘야 한다.
class MyBleManager(context: Context): BleManager(context) {
...
}
그리고 몇가지 메소드들을 override 해줘야 하는데, 빼먹고 안했더니 Device not supported였나? 그런 경고 뜨면서 바로 disconnect 되더라.
해당 메소드는 연결 과정에서 반드시 포함되어야 하는 Service 혹은 Characteristic 을 지정하는 메소드이다.
해당 메소드를 override 하지 않으면, 리턴값이 false로 넘어가서 모든 기기와 연결이 거부된다.
그냥 return true 해버리면 모든 기기와 연결 가능하려나? ㅋㅋ
다음과 같이 사용할 수 있다.
override fun isRequiredServiceSupported(gatt: BluetoothGatt) {
val myService = gatt.getService(UUID.fromString("my-uuid")) ?: return false
myRxCharacteristic = myService.getCharacteristic(UUID.fromString("my-rx-uuid")) ?: return false
myTxCharacteristic = myService.getCharacteristic(UUID.fromString("my-tx-uuid")) ?: return false
return true
}
위와같이 작성하게 되면, my-uuid 라는 uuid를 가진 서비스가 있는 기기,
RxCharacteristic 의 UUID가 my-rx-uuid 인 기기,
TxCharacteristic 의 UUID가 my-tx-uuid 인 기기에 한해서만 연결 가능하도록 지정할 수 있다.
위 메소드에 있는 myRx(Tx)Characteristic 변수는 멤버변수로 선언했다고 가정하자.
통신 전 초기화 로직들을 넣어놓는 함수로 보인다.
정확한 호출 시기는 로그를 안찍어봐서 모르겠는데, 여기서 notify enable,
notification callback, request mtu 등을 작성하면 된다.
override fun initialize() {
setNotificationCallback(myTxCharacteristic).with { device, data ->
// data 인자를 통해 값 수신 가능하다.
}
beginAtomicRequestQueue()
.add(enableNotifications(myTxCharacteristic))
.add(requestMtu(517))
.enqueue()
}
기기와 disconnect 될때 호출되는 메소드로 보인다.
characteristic 멤버변수 등 자원 해제할 때 쓰면 된다.
override fun onServicesInvalidated() {
myRxCharacteristic = null
myTxCharacteristic = null
...
}
이제 데이터를 수신받을 차례인데, 아무리 찾아도 onCharacteristicChange()와 비슷하게 생긴 메소드가 보이질 않아서 찾는데만 몇시간 쓴것같다 ㅡㅡ
API 문서좀 만들어놓지;;
아무튼 데이터를 수신하려면 위 initlalize()에서 setNotificationCallback() 메소드 콜백을 통해 데이터를 수신할 수 있다..
setNotificationCallback 메소드를 통해 notify callback을 받고싶은 characteristic을 넘겨주고, with 람다를 통해 넘어오는 device와 data를 통해 변경된 값을 수신할 수 있다.
특정 characteristic에 데이터를 쓰고싶다면, writeCharacteristic 메소드를 사용하면 된다.
MyBleManager 내부에 래핑하는 메소드로 따로 만들어도 되고, 외부에서 직접 호출해서 써도 된다.
API 문서가 하나도 없어서 답답해서 적어봤다. 추가로 더 적을거 생기면 추가할 예정.