본문 바로가기
프로그래밍/Android, iOS

안드로이드 BLE 통신(공식문서 번역) 2

by YuminK 2023. 7. 23.

GATT callback 선언

일단 서비스가 기기에 연결되면 BluetoothGattCallback을 통해 연결 상태에 대한 알림을 받는다. 서비스 회복, Characters read, Characters notifications

 

onConnectionStateChanged 메소드는 디바이스의 GATT 서버 연결이 변경되었을 때 호출된다. 서비스 클래스에 선언되어 bluetoothAdapter 와 함께 사용될 수 있다. (서비스와 연결된 이후에)

private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // successfully connected to the GATT Server
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // disconnected from the GATT Server
        }
    }
};

 

GATT 서비스 연결

BluetoothGattcallback이 선언되면, 서비스는 connect 함수로부터 BluetoothDevice 객체를 사용할 수 있다.

예제에서는 바로 BLE Device에 연결한다.

class BluetoothService extends Service {

...

    private BluetoothGatt bluetoothGatt;

    ...

    public boolean connect(final String address) {
        if (bluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        try {
            final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
            // connect to the GATT server on the device
            bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
            return true;
        } catch (IllegalArgumentException exception) {
            Log.w(TAG, "Device not found with provided address.  Unable to connect.");
            return false;
        }
    }
}

 

Broadcast 업데이트

GATT 서버로부터 연결이나 실패 상황시, 액티비티에 데이터를 보낼 필요가 있다. 다양한 방법이 있으나 예제에서는 broadcast를 사용한다. Service to activity

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

 

BluetoothGattCallback에서 사용된다. GATT서버 연결 상태를 처리하기 위함이다.

class BluetoothService extends Service {

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";

    private static final int STATE_DISCONNECTED = 0;p
    private static final int STATE_CONNECTED = 2;

    private int connectionState;
    ...

    private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // successfully connected to the GATT Server
                connectionState = STATE_CONNECTED;
                broadcastUpdate(ACTION_GATT_CONNECTED);
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // disconnected from the GATT Server
                connectionState = STATE_DISCONNECTED;
                broadcastUpdate(ACTION_GATT_DISCONNECTED);
            }
        }
    };

    …
}

 

Broadcast를 받는 Activity에서는 BroadcastReceiver를 등록한다. 액티비티의 생명주기에 맞춰 register하고 release한다. 현재 BLE 연결 상태에 따라 UI를 변경할 수 있다. BoradcastReceiver는 서비스 회복이나 디바이스로부터 Characteristics 정보를 받을 때도 사용된다.

class DeviceControlsActivity extends AppCompatActivity {

...

    private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                connected = true;
                updateConnectionState(R.string.connected);
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                connected = false;
                updateConnectionState(R.string.disconnected);
            }
        }
    };

    @Override
    protected void onResume() {
        super.onResume();

        registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
        if (bluetoothService != null) {
            final boolean result = bluetoothService.connect(deviceAddress);
            Log.d(TAG, "Connect request result=" + result);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(gattUpdateReceiver);
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        return intentFilter;
    }
}

 

GATT 연결 닫기

연결 작업이 끝나면 닫아라. 액티비티가 서비스로부터 unbound될 때, 배터리 고갈을 방지할 수 있다.

class BluetoothService extends Service {

...

      @Override
      public boolean onUnbind(Intent intent) {
          close();
          return super.onUnbind(intent);
      }

      private void close() {
          if (bluetoothGatt == null) {
              Return;
          }
          bluetoothGatt.close();
          bluetoothGatt = null;
      }
}

 

BLE 데이터 보내기

일단 BLE GATT 서버와 연결하면, 어떤 서비스가 디바이스에서 가능한지 알아내는데 연결을 사용할 수 있다. (디바이스에 데이터를 요청, GATT 캐릭터 정보가 변경될 때, 알람을 요청 등)

 

서비스 찾기

GATT 서버를 연결했을 때 가장 먼저할 것은 서비스를 찾는 것이다. 이는 가능한 remote 디바이스에 대한 정보를 비롯하여 서비스 characteristics 그리고 descriptors 정보를 제공한다. 이 예제에서는 일단 서비스가 기기에 성공적으로 연결되면 discoverServices 함수가 BLE 디바이스에 정보를 요청한다. (BluetoothGattCallback onConnectionStarteChange 메소드로 지정된)

 

이 서비스는 onServiceDiscovered 메소드를 재정의할 필요가 있다. 가능한 서비스들에서 디바이스가 리포트 될 때 호출된다.

class BluetoothService extends Service {

    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";

    ...

    private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // successfully connected to the GATT Server
                connectionState = STATE_CONNECTED;
                broadcastUpdate(ACTION_GATT_CONNECTED);
                // Attempts to discover services after successful connection.
                bluetoothGatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // disconnected from the GATT Server
                connectionState = STATE_DISCONNECTED;
                broadcastUpdate(ACTION_GATT_DISCONNECTED);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }
    };
}

서비스는 broadcast를 사용하여 액티비티에 알린다. 일단 서비스가 찾아지면, 서비스는 getServices 메소드를 통해 보내진 데이터를 받을 수 있다. 

 

class BluetoothService extends Service {

...

    public List<BluetoothGattService> getSupportedGattServices() {
        if (bluetoothGatt == null) return null;
        return bluetoothGatt.getServices();
    }
}

 

액티비티는 브로드캐스트 인텐트를 받을 때, 다음 처리를 한다. 

class DeviceControlsActivity extends AppCompatActivity {

...

    private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                connected = true;
                updateConnectionState(R.string.connected);
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                connected = false;
                updateConnectionState(R.string.disconnected);
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the user interface.
                displayGattServices(bluetoothService.getSupportedGattServices());
            }
        }
    };
}

 

댓글