[안드로이드]BLE SPP와 Bonding

코저씨 ㅣ 2022. 10. 14. 14:05

블​루투스 관련 제품을 개발하기는 하지만 아직도 블루투스 스택에 대해 많은 공부가 필요한 것 같다.

GATT라던가 A2DP라던가 사용하면서도 누군가에게 설명해 줄 그런 지식이 없어 가끔 한심하기도 한다.

최근 개발하는 장비는 BLE 통신을 사용하며 SPP(Serial Port Profile)를 지원하지 않기에 이를 GATT로 구현한 SPP 에뮬레이터(?)를 사용한다. Nordic CPU가 이쪽으로는 선점을 하고 있으며, 최근에는 Ambiq Apollo 시리즈와 Silicon Labs의 EFR32 시리즈도 사용되는 추세이다.


SPP를 검색해 보다가 Bluetooth 구 버전의 SPP와 BLE로 구현한 SPP의 차이에 대한 글을 발견했다.

https://devzone.nordicsemi.com/f/nordic-q-a/48187/regarding-serial-port-emulation-over-ble-vs-spp-over-classic-bt

정리하면, BLE SPP는 Nordic UART 서비스이며 GATT 프로토콜을 사용하여 UUID 변경을 통해 사용자 정의 서비스를 만들 수 있다는 것이다. 또한 BLE 통신이기 때문에 저전력에, 통신속도도 구 버전보다 빠르다.

 


S​PP와 상관없이 Bonding 관련해서도 알아야 할 사항이 있다. 블루투스, BLE를 이용하는 결제 장비에는 최초 기기 연결 시 본딩 창으로 기기의 제품번호를 확인해야 하는 기능이 있어야 한다. 결제 사고가 몇 년 전에 발생해서 그렇다.

그래서 본딩을 하기 전에는 단순히 APP과 기기와의 통신은 Connect와 Disconnect만 있으면 되었다.

하지만 위에서 이야기한 대로 본딩이 추가되었는데 "연결 성공"이라는 판단이 필요해졌다.

왜 연결 성공을 판단해야 하는가?라는 의문에 대해 이야기하려면 위에서 얘기한 BLE를 지원하는 CPU부터 다시 이야기를 해야 하는데 펌웨어 엔지니어 들은 BLE를 지원하는 CPU를 사용하여 BLE 통신을 하거나 선전된 CPU가 BLE 기능이 없으면 BLE 통신 모듈을 사용해야 한다 Microchip 사의 RN4871 이라던가..

그런데 모듈별로 통신의 차이점이 있다. 어느 칩은 본딩 하기 전에 연결 성공 이벤트가 뜨기도 하고 어느 칩은 본딩이 성공해야 연결성공이 뜬다. (사실 이 부분은 구두로만 전달된 사항이라, 확인은 못했다.)

그래서 안드로이드 APP에서 BLE 프로그램을 짤 때 본딩 여부에 따라 연결 성공/실패를 지정해야 한다.

절차

  1. 본딩이 된 경우, Connected 이벤트가 뜨면 연결 성공으로 처리
  2. 본딩이 되지 않은 경우, createbond()를 실행하고 BluetoothDevice.ACTION_BOND_STATE_CHANGED 이벤트를 처리

스택오버플러우 다음으로 개발에 도움이 되는 github에 기본적인 예제가 있다.

https://github.com/guavabot/ble-bond-test

소스 설명을 하자면 다음과 같다.

MainActivity.java의 Oncreate에서 ACTION_BOND_STATE_CHANGED를 registerReceiver로 등록
registerReceiver(mBondStateReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
 
본딩이 요청되면 BOND_BONDING, 성공하면 BOND_BONDED, 본딩이 삭제되거나 하면 BOND_NONE
private final BroadcastReceiver mBondStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
            int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
            String msg = "Bond state change: state " + printBondState(state) +
                    ", previous state " + printBondState(prevState);
            Log.w("Bond state receiver", msg);
            mLogTv.append("\n\n" + mTimeFormat.format(new Date()) + ": " + msg);
        }

        private String printBondState(int state) {
            switch (state) {
                case BluetoothDevice.BOND_NONE:
                    return "BOND_NONE";
                case BluetoothDevice.BOND_BONDING:
                    return "BOND_BONDING";
                case BluetoothDevice.BOND_BONDED:
                    return "BOND_BONDED";
                default:
                    return String.valueOf(state);
            }
        }
    };

connect 여부와 본딩 처리 여부를 크로스로 체크하면 된다.

참고로 본딩이 된 상태에서는 아무 이벤트도 발생하지 않는다.