Conectar-se ao servidor GATT

A primeira etapa na interação com um dispositivo BLE é a conexão com ele. Mais especificamente, conectar-se ao servidor GATT no dispositivo. Para se conectar a um GATT servidor em um dispositivo BLE, use o connectGatt() . Esse método usa três parâmetros: Objeto Context, autoConnect (um booleano indicando se ele deve se conectar automaticamente ao dispositivo BLE assim que estiver disponível) e uma referência a um BluetoothGattCallback:

Kotlin

 var bluetoothGatt: BluetoothGatt? = null ...  bluetoothGatt = device.connectGatt(this, false, gattCallback) 

Java

 bluetoothGatt = device.connectGatt(this, false, gattCallback); 

Ela se conecta ao servidor GATT hospedado pelo dispositivo BLE e retorna uma a instância BluetoothGatt, que que podem ser usados para conduzir operações do cliente GATT. O autor da chamada (o app Android) é o cliente GATT. A BluetoothGattCallback é usado para entregar resultados ao cliente, como o status da conexão, bem como qualquer outra operação do cliente GATT.

Configurar um serviço vinculado

No exemplo a seguir, o app BLE fornece uma atividade (DeviceControlActivity) para se conectar a dispositivos Bluetooth, exibir dados do dispositivo, e exibe os serviços e as características do GATT compatíveis com o dispositivo. Baseado em na entrada do usuário, essa atividade se comunica Service chamado BluetoothLeService, que interage com o dispositivo BLE por meio da API BLE. A comunicação é realizada usando um serviço vinculado que permite a atividade para se conectar ao BluetoothLeService e chamar funções para e se conectar aos dispositivos. O BluetoothLeService precisa de um implementação do Binder que fornece acesso a o serviço para a atividade.

Kotlin

 class BluetoothLeService : Service() {      private val binder = LocalBinder()      override fun onBind(intent: Intent): IBinder? {         return binder     }      inner class LocalBinder : Binder() {         fun getService() : BluetoothLeService {             return this@BluetoothLeService         }     } } 

Java

 class BluetoothLeService extends Service {      private Binder binder = new LocalBinder();      @Nullable     @Override     public IBinder onBind(Intent intent) {         return binder;     }      class LocalBinder extends Binder {         public BluetoothLeService getService() {             return BluetoothLeService.this;         }     } } 

A atividade pode iniciar o serviço usando bindService(), transmitindo um Intent para iniciar o serviço, um ServiceConnection para detectar eventos de conexão e desconexão, além de uma sinalização para especificar outras opções de conexão.

Kotlin

 class DeviceControlActivity : AppCompatActivity() {      private var bluetoothService : BluetoothLeService? = null      // Code to manage Service lifecycle.     private val serviceConnection: ServiceConnection = object : ServiceConnection {         override fun onServiceConnected(             componentName: ComponentName,             service: IBinder         ) {             bluetoothService = (service as LocalBinder).getService()             bluetoothService?.let { bluetooth ->                 // call functions on service to check connection and connect to devices             }         }          override fun onServiceDisconnected(componentName: ComponentName) {             bluetoothService = null         }     }      override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)         setContentView(R.layout.gatt_services_characteristics)          val gattServiceIntent = Intent(this, BluetoothLeService::class.java)         bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE)     } } 

Java

 class DeviceControlActivity extends AppCompatActivity {      private BluetoothLeService bluetoothService;      private ServiceConnection serviceConnection = new ServiceConnection() {         @Override         public void onServiceConnected(ComponentName name, IBinder service) {             bluetoothService = ((LocalBinder) service).getService();             if (bluetoothService != null) {                 // call functions on service to check connection and connect to devices             }         }          @Override         public void onServiceDisconnected(ComponentName name) {             bluetoothService = null;         }     };      @Override     protected void onCreate(@Nullable Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.gatt_services_characteristics);          Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);         bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);     } } 

Configurar o BluetoothAdapter

Depois que o serviço é vinculado, ele precisa acessar o BluetoothAdapter Ele deveria verifique se o adaptador está disponível no dispositivo. Leia Configuração Bluetooth para mais informações sobre o BluetoothAdapter. O exemplo a seguir encapsula esse código de configuração Função initialize() que retorna um valor Boolean indicando sucesso.

Kotlin

 private const val TAG = "BluetoothLeService"  class BluetoothLeService : Service() {      private var bluetoothAdapter: BluetoothAdapter? = null      fun initialize(): Boolean {         bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()         if (bluetoothAdapter == null) {             Log.e(TAG, "Unable to obtain a BluetoothAdapter.")             return false         }         return true     }      ... } 

Java

 class BluetoothLeService extends Service {      public static final String TAG = "BluetoothLeService";      private BluetoothAdapter bluetoothAdapter;      public boolean initialize() {         bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();         if (bluetoothAdapter == null) {             Log.e(TAG, "Unable to obtain a BluetoothAdapter.");             return false;         }         return true;     }      ... } 

A atividade chama essa função dentro da implementação de ServiceConnection. O processamento de um valor de retorno falso da função initialize() depende das suas para o aplicativo. Você pode mostrar uma mensagem de erro ao usuário indicando que o o dispositivo atual não é compatível com a operação de Bluetooth nem desativa recursos que precisam do Bluetooth para funcionar. No exemplo a seguir, finish() é chamado na atividade. para retornar o usuário à tela anterior.

Kotlin

 class DeviceControlActivity : AppCompatActivity() {      // Code to manage Service lifecycle.     private val serviceConnection: ServiceConnection = object : ServiceConnection {         override fun onServiceConnected(             componentName: ComponentName,             service: IBinder         ) {             bluetoothService = (service as LocalBinder).getService()             bluetoothService?.let { bluetooth ->                 if (!bluetooth.initialize()) {                     Log.e(TAG, "Unable to initialize Bluetooth")                     finish()                 }                 // perform device connection             }         }          override fun onServiceDisconnected(componentName: ComponentName) {             bluetoothService = null         }     }      ... } 

Java

 class DeviceControlsActivity extends AppCompatActivity {      private ServiceConnection serviceConnection = new ServiceConnection() {         @Override         public void onServiceConnected(ComponentName name, IBinder service) {             bluetoothService = ((LocalBinder) service).getService();             if (bluetoothService != null) {                 if (!bluetoothService.initialize()) {                     Log.e(TAG, "Unable to initialize Bluetooth");                     finish();                 }                 // perform device connection             }         }          @Override         public void onServiceDisconnected(ComponentName name) {             bluetoothService = null;         }     };      ... } 

Conectar a um dispositivo

Depois que a instância BluetoothLeService for inicializada, ela poderá se conectar ao BLE dispositivo. A atividade precisa enviar o endereço do dispositivo ao serviço para que ele possa inicie a conexão. O serviço vai chamar pela primeira vez getRemoteDevice() no BluetoothAdapter para acessar o dispositivo. Se o adaptador não conseguir encontrar um dispositivo com esse endereço, getRemoteDevice() gera uma IllegalArgumentException.

Kotlin

 fun connect(address: String): Boolean {     bluetoothAdapter?.let { adapter ->         try {             val device = adapter.getRemoteDevice(address)         } catch (exception: IllegalArgumentException) {             Log.w(TAG, "Device not found with provided address.")             return false         }     // connect to the GATT server on the device     } ?: run {         Log.w(TAG, "BluetoothAdapter not initialized")         return false     } } 

Java

 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);     } catch (IllegalArgumentException exception) {         Log.w(TAG, "Device not found with provided address.");         return false;     }     // connect to the GATT server on the device } 

O DeviceControlActivity chama essa função connect() quando o serviço é inicializado. A atividade precisa transmitir o endereço do dispositivo BLE. Em no exemplo a seguir, o endereço do dispositivo é transmitido para a atividade como uma intent extra.

Kotlin

 // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection {     override fun onServiceConnected(     componentName: ComponentName,     service: IBinder     ) {         bluetoothService = (service as LocalBinder).getService()         bluetoothService?.let { bluetooth ->             if (!bluetooth.initialize()) {                 Log.e(TAG, "Unable to initialize Bluetooth")                 finish()             }             // perform device connection             bluetooth.connect(deviceAddress)         }     }      override fun onServiceDisconnected(componentName: ComponentName) {         bluetoothService = null     } } 

Java

 private ServiceConnection serviceConnection = new ServiceConnection() {     @Override     public void onServiceConnected(ComponentName name, IBinder service) {         bluetoothService = ((LocalBinder) service).getService();         if (bluetoothService != null) {             if (!bluetoothService.initialize()) {                 Log.e(TAG, "Unable to initialize Bluetooth");                 finish();             }             // perform device connection             bluetoothService.connect(deviceAddress);         }     }      @Override     public void onServiceDisconnected(ComponentName name) {         bluetoothService = null;     } }; 

Declarar callback GATT

Depois que a atividade informa ao serviço a que dispositivo se conectar e o serviço conecta ao dispositivo, o serviço precisa se conectar ao servidor GATT na dispositivo BLE. Esta conexão exige um BluetoothGattCallback para receber notificações sobre o estado da conexão, descoberta de serviços, leituras e notificações características.

Este tópico se concentra nas notificações do estado da conexão. Consulte Transferir BLE dados sobre como realizar descoberta de serviços, leituras de características e característica da solicitação notificações.

A onConnectionStateChange() é acionada quando a conexão com o servidor GATT do dispositivo é alterada. No exemplo a seguir, o callback é definido na classe Service pode ser usada com o BluetoothDevice assim que o ou serviço se conecta a ele.

Kotlin

 private val bluetoothGattCallback = object : BluetoothGattCallback() {     override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {         if (newState == BluetoothProfile.STATE_CONNECTED) {             // successfully connected to the GATT Server         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {             // disconnected from the GATT Server         }     } } 

Java

 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         }     } }; 

Conectar-se ao serviço GATT

Depois que a BluetoothGattCallback for declarada, o serviço poderá usar a Objeto BluetoothDevice da função connect() para conexão com o GATT serviço no dispositivo.

A connectGatt() é usada. Isso exige um objeto Context, um booleano autoConnect e a sinalização BluetoothGattCallback. Neste exemplo, o app é usado diretamente conectando-se ao dispositivo BLE, então false é transmitido para autoConnect.

Uma propriedade BluetoothGatt também foi adicionada. Isso permite que o serviço feche o padrão quando não há mais necessário.

Kotlin

 class BluetoothLeService : Service() {  ...      private var bluetoothGatt: BluetoothGatt? = null      ...      fun connect(address: String): Boolean {         bluetoothAdapter?.let { adapter ->             try {                 val device = adapter.getRemoteDevice(address)                 // connect to the GATT server on the device                 bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)                 return true             } catch (exception: IllegalArgumentException) {                 Log.w(TAG, "Device not found with provided address.  Unable to connect.")                 return false             }         } ?: run {             Log.w(TAG, "BluetoothAdapter not initialized")             return false         }     } } 

Java

 class BluetoothLeService 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;         }     } } 

Atualizações de transmissão

Quando o servidor se conecta ou desconecta do servidor GATT, ele precisa notificar a atividade do novo estado. Há várias maneiras de fazer isso. A exemplo a seguir usa transmissões para enviar as informações do serviço para a atividade.

O serviço declara uma função para transmitir o novo estado. Essa função usa em uma string de ação transmitida para um objeto Intent antes de ser transmitida ao sistema.

Kotlin

 private fun broadcastUpdate(action: String) {     val intent = Intent(action)     sendBroadcast(intent) } 

Java

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

Depois que a função de transmissão está no lugar, ela é usada no BluetoothGattCallback para enviar informações sobre o estado da conexão com o servidor GATT. As constantes e o estado atual da conexão do serviço são declarados no serviço que representa as ações Intent.

Kotlin

 class BluetoothLeService : Service() {      private var connectionState = STATE_DISCONNECTED      private val bluetoothGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {         override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {             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)             }         }     }      ...      companion object {         const val ACTION_GATT_CONNECTED =             "com.example.bluetooth.le.ACTION_GATT_CONNECTED"         const val ACTION_GATT_DISCONNECTED =             "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"          private const val STATE_DISCONNECTED = 0         private const val STATE_CONNECTED = 2      } } 

Java

  class BluetoothLeService 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;     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);             }         }     };      … } 

Detectar atualizações na atividade

Depois que o serviço transmitir as atualizações da conexão, a atividade precisará implementar um BroadcastReceiver. Registre este receptor ao configurar a atividade e cancele o registro quando o a atividade está saindo da tela. Ao detectar eventos no serviço, a atividade poderá atualizar a interface do usuário com base o estado da conexão com o dispositivo BLE.

Kotlin

 class DeviceControlActivity : AppCompatActivity() {  ...      private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() {         override fun onReceive(context: Context, intent: Intent) {             when (intent.action) {                 BluetoothLeService.ACTION_GATT_CONNECTED -> {                     connected = true                     updateConnectionState(R.string.connected)                 }                 BluetoothLeService.ACTION_GATT_DISCONNECTED -> {                     connected = false                     updateConnectionState(R.string.disconnected)                 }             }         }     }      override fun onResume() {         super.onResume()         registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter())         if (bluetoothService != null) {             val result = bluetoothService!!.connect(deviceAddress)             Log.d(DeviceControlsActivity.TAG, "Connect request result=$result")         }     }      override fun onPause() {         super.onPause()         unregisterReceiver(gattUpdateReceiver)     }      private fun makeGattUpdateIntentFilter(): IntentFilter? {         return IntentFilter().apply {             addAction(BluetoothLeService.ACTION_GATT_CONNECTED)             addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED)         }     } } 

Java

 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;     } } 

Em Transferir dados de BLE, faça o seguinte: BroadcastReceiver também é usado para comunicar a descoberta do serviço, e os dados característicos do dispositivo.

Fechar conexão GATT

Um passo importante ao lidar com conexões Bluetooth é fechar o quando você terminar de usá-lo. Para fazer isso, chame o método close() no objeto BluetoothGatt. No exemplo a seguir, o serviço contém a referência ao BluetoothGatt. Quando a atividade se desvincula do serviço, a conexão será encerrada para evitar o descarregamento da bateria do dispositivo.

Kotlin

 class BluetoothLeService : Service() {  ...      override fun onUnbind(intent: Intent?): Boolean {         close()         return super.onUnbind(intent)     }      private fun close() {         bluetoothGatt?.let { gatt ->             gatt.close()             bluetoothGatt = null         }     } } 

Java

 class BluetoothLeService extends Service {  ...        @Override       public boolean onUnbind(Intent intent) {           close();           return super.onUnbind(intent);       }        private void close() {           if (bluetoothGatt == null) {               Return;           }           bluetoothGatt.close();           bluetoothGatt = null;       } }