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
:
var bluetoothGatt: BluetoothGatt? = null
...
bluetoothGatt = device.connectGatt(this, false, gattCallback)
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.
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
}
}
}
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.
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)
}
}
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.
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
}
...
}
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.
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
}
}
...
}
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
.
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
}
}
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.
// 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
}
}
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.
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
}
}
}
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.
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
}
}
}
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.
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
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
.
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
}
}
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.
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)
}
}
}
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.
class BluetoothLeService : Service() {
...
override fun onUnbind(intent: Intent?): Boolean {
close()
return super.onUnbind(intent)
}
private fun close() {
bluetoothGatt?.let { gatt ->
gatt.close()
bluetoothGatt = null
}
}
}
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;
}
}