: 이번에는 Characteristic 에 대한 read/write/notify 하는 방법에 대해 알아 보겠습니다.
아시겠지만 블루투스 코드는 성능이 느리므로 UI 가 아닌 service class 에 메서드를 만들고
호출하게 구현했습니다. GATT callback에 대한 처리는 broadcast 로 메시지를 전송하고
Activity 에서 이를 받아서 처리했습니다.
▶ Characteristic read
: Characteristic read는 BluetoothGatt 클래스의 readCharacteristic() 메서드를 이용합니다.
다음처럼 선언이 되어 있습니다.
Characteristic 속성 값에 아래처럼 READ 가 존재하는 특성에 대해서 체킹후 호출해야 합니다.
→ Characteristic Read 버튼 UI 코드 (Jetpack Compose) 작성하기
: 아래코드는 Activity 안의 UI 코드 입니다.
private var bluetoothLeService :BluetoothLeService?= null
private var mBtNordicBtnCharacteristic : BluetoothGattCharacteristic? = null
~~ 중략 ~~
Button(onClick = {
bluetoothLeService?.readCharacteristic(mBtNordicBtnCharacteristic!!)
}){
Text("READ BTN ")
}
→ BluetoothGatt::readCharacteristic() 메소드 호출코드를 포함한 메서드 만들기
: BluetoothLeService 클래스 안에 readCharacteristic() 메소드 선언 및 내용 작성
// 1> BluetoothGatt::readCharacteristic() 사용할 메서드 만들기
class BluetoothLeService : Service() {
private var mBtGatt :BluetoothGatt? = null
fun readCharacteristic(characteristic: BluetoothGattCharacteristic){
if( mBtGatt == null){
Log.w(TAG,"BluetoothGatt is not initialized")
}else {
mBtGatt?.readCharacteristic(characteristic)
Log.d(TAG, "readCharacteristic() --> ${characteristic.uuid}")
}
}
}
→ readCharacteristic() 의 결과를 리턴 받을 BluetoothGattCallback() 객체의
onCharacteristicRead() 메서드 생성/구현 하기
// 2> BluetoothGattCallback :: onCharacteristicRead() 추가
private val mGattCallback = object :BluetoothGattCallback() {
override fun onCharacteristicRead( gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?, status: Int ) {
if(status == BluetoothGatt.GATT_SUCCESS){
Log.d(TAG,"GATT Callback --> onCharacteristicRead")
}
}
}
→ 들어온데이타를 Activity 로 전달하기위한 Broadcast 코드 작성
: BluetoothGattCallback() 객체안에서 boradcast 메시지를 전송 및 Activity 에서 BroadcastReceiver()를
통한 데이타 파싱하기
// 3> Activity 로 읽은 데이타 전송하기
3-0> broadcastUpdate 메서드 추가
private fun broadcastUpdate(action:String, characteristic: BluetoothGattCharacteristic){
val intent = Intent(action)
// For all other profiles, writes the data formated in HEX.
val data = characteristic.value
if(data != null && data.isNotEmpty()){
val stringBuilder = StringBuilder(data.size)
data.forEach {
stringBuilder.append(String.format("%02X ", it))
}
intent.putExtra(EXTRA_DATA, stringBuilder.toString())
}
sendBroadcast(intent)
}
3-1> onCharacteristicRead()에 broadcastUpdate 메서드 호출하기
override fun onCharacteristicRead( gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?, status: Int ) {
if(status == BluetoothGatt.GATT_SUCCESS){
broadcastUpdate(ACTION_DATA_AVAILABLE,characteristic)
}
}
3-2> BroadcastReceiver 에 broadcastUpate() 에서 보낸 데이타 읽는 코드 추가
private val mGattUpdateReceiver = object :BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
val action =p1!!.action
when(action){
BluetoothLeService.ACTION_DATA_AVAILABLE -> {
val data=p1.getStringExtra(BluetoothLeService.EXTRA_DATA)
}
}
~~ 중략 ~~
}
▶ Characteristic write
: write 시 사용되는 메서드는 BluetoothGatt::writeCharacteristic() 함수로 다음처럼 사용합니다.
// 1> Service class에 writeCharacteristic() 함수 작성하기
class BluetoothLeService : Service() {
private var mBtGatt :BluetoothGatt? = null
~~ 중략 ~~
fun writeCharacteristic(charac:BluetoothGattCharacteristic, data:ByteArray)
{
if( mBtGatt == null){
Log.w(TAG,"BluetoothGatt is not initialized")
}else {
charac.value = data
mBtGatt?.writeCharacteristic(charac)
}
}
}
// 2> Activity 에서 함수 호출하기
private var bluetoothLeService :BluetoothLeService?= null
~~ 중략 ~~
val value = byteArrayOf(0x31,0x32,0x33)
bluetoothLeService!!.writeCharacteristic(mBtNordicUartRxCharac!!,value)
// 3> BluetoothGattCallback 에 onCharacteristicWrite() 추가
private val mGattCallback = object :BluetoothGattCallback() {
override fun onCharacteristicWrite( gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
if(status == BluetoothGatt.GATT_SUCCESS){
Log.d(TAG,"GATT Callback --> onCharacteristicWrite OK")
}
}
}
==> Write or Write No Response 두가지 경우 모두 BluetoothGattCallback() 객체의
onCharacteristicWrite() 메서드가 호출됩니다.
▶ Characteristic notify
: notify 는 read 처럼 호출 후 응답하는 방식이 아닌 peripheral 장치에서 직접 값을 넣어 보내줄 경우
호출되는 방식입니다.
BluetoothGatt::setCharacteristicNotification()을 사용해 특성에 대한 notify를 enable 한후
BluetoothGattCallback() 의 onCharacteristicChanged() 메서드를 추가해 줍니다.
→ Nordic UART Service 찾기
: BluetoothGattCallback() 내부 메서드중 onServiceDiscovered 에서 작성해도 됩니다.
저는 BroadcastReceiver() 사용함.
private val mBtNordicUartServiceUuid = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
private var mBtNordicUartService : BluetoothGattService? = null
private val mGattUpdateReceiver = object :BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
val action =p1!!.action
when(action){
~~ 중략 ~~
BluetoothLeService.ACTION_GATT_SERVICE_DISCOVERED -> {
mBtGattServices= bluetoothLeService!!.getSupportedGattServices()
mBtGattServices!!.forEach {
if(it.uuid.toString() == mBtNordicUartServiceUuid){
mBtNordicUartService=it
}
}
}
~~ 중략 ~~
}
~~ 중략 ~~
}
→ NOTIFY 속성 characteristic 에 notify enable 하기
fun setCharateristicNotification(characteristic: BluetoothGattCharacteristic, enabled: Boolean){
if(btAdapter == null || mBtGatt == null){
Log.w(TAG,"BluetoothAdapter not initialized")
}
mBtGatt?.setCharacteristicNotification(characteristic, enabled)
}
→ CCCD 에 값 notification enable 쓰기
{
~~ 중략 ~~
val uuid: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
val descriptor = characteristic.getDescriptor(uuid)
descriptor.value= BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
mBtGatt!!.writeDescriptor(descriptor)
}
→ 데이타를 받기위한 BluetoothGattCallback() 객체 에 onCharacteristicChanged() 메서드 오버라이드하기
private val mGattCallback = object :BluetoothGattCallback() {
~~ 중략 ~~
override fun onCharacteristicChanged( gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?
) {
Log.d(TAG,"GATT Callback --> onCharacteristicChanged OK => ${String(characteristic!!.value)}")
}
}
→ 이후 Activity 로 데이타 전송하는 과정은 read 와 동일합니다.
<이전 강좌>
https://leevisual.tistory.com/229
그럼 오늘도 수고하세요.
'Android_app' 카테고리의 다른 글
[Jetpack Compose] AlertDialog 사용하기 (0) | 2023.07.12 |
---|---|
[Android_App] Runtime error "cannot be cast to java.lang.Byte " (0) | 2023.06.21 |
[Android app] Build Error : Duplicate class found (0) | 2023.05.19 |
[Android App] BLE GATT:: [3] Characteristic /Descriptor 출력하기 (0) | 2023.04.27 |
[Android App] BLE GATT:: [2] 서비스 리스트 출력하기 (0) | 2023.04.25 |