studio low example ble java android android-bluetooth bluetooth-lowenergy

java - low - BLE Android-onConnectionStateChange no se llama



bluetooth low energy android studio (3)

Tengo un problema al intentar conectarme a un periférico. A veces, la devolución de llamada onConnectionStateChange(...) no se llama después de BluetoothDevice#connectGatt(...) . Lo que estoy tratando de lograr es conexiones rápidas y cortas activadas por la acción del usuario.

Esta situación ocurre aproximadamente 1 de cada 10 veces sin una acción previa específica. Dura unos 20 a 30 segundos o hasta que la aplicación se cancela y vuelve a abrir. La secuencia normal de pasos que sigo es:

  1. Escanear dispositivos para encontrar el periférico.
  2. Llame a BluetoothDevice#connectGatt(...) . Si tarda más de 1 segundo en conectarse, significa que la conexión está "bloqueada" y, por lo tanto, no se conectará, por lo que BluetoothDevice#connectGatt(...) se vuelve a llamar. Esto se hace con un límite de 5 intentos.
  3. onConnectionStateChange(...) se llama con newState CONNECTED y comienza el descubrimiento de servicios.
  4. El resto de las operaciones se realizan sin problemas.
  5. Después de la desconexión se llama a BluetoothGatt#close() .

El problema que se produce está en el punto 3. A veces no se llama a onConnectionStateChange(...) . He notado que la mayoría de las veces el problema comienza con un comportamiento específico. Después de BluetoothDevice#connectGatt(...) , se onConnectionStateChange(...) con newState CONNECTED, pero casi inmediatamente después (~ 40 milisegundos) se vuelve a llamar con newStatus DESCONECTADO. Debido al poco tiempo del cambio de estado, puedo deducir que el dispositivo ni siquiera intentó establecer la conexión y cambió el estado a DESCONECTADO. El problema termina cuando:

  1. Han pasado 20-30 segundos. Durante este tiempo nunca se llama a onConnectionStateChange(...) . Cuando el problema termina, onConnectionStateChange(...) se llama el número de veces que la aplicación intentó conectarse. Por ejemplo, si se llama 15 veces a BluetoothDevice#connectGatt(...) , onConnectionStateChange(...) se llama 15 veces con newState igual a DESCONECTADO. Esto es curioso porque nunca en ninguno de esos intentos de conexión el estado cambió a CONECTADO.
  2. La aplicación se mata y se inicia de nuevo.

Este error ocurre en SDK18 y SDK 21.

@Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { String deviceName = device.getName(); if (deviceName == null) return; Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName()); if (mMode == SCAN_MODE) { mListener.deviceFound(device, rssi, scanRecord); } else { mDevices.put(device.hashCode(), device); stopScan(); // Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread. mHandler.post(new Runnable() { @Override public void run() { Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()"); BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback); retryIfNecessary(device, gatt); mTryingToConnect = true; } }); } }

private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) { if (isRetryLimitReached()) { Log.d("BLUETOOTH CONNECTION", "Try count limit reached"); finishConnection(gatt); mRetryCount = 0; mListener.error(TIMEOUT); return; } mRetryCount++; mHandler.postDelayed(new Runnable() { @Override public void run() { Log.d("BLUETOOTH CONNECTION", "Check if it is frozen."); if (isWorking()) { Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection."); BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback); retryIfNecessary(device, gatt); } } }, RETRY_INTERVAL_MS); }

@Override public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) { Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress()); if (!mConnected && BluetoothGatt.STATE_CONNECTED == newState) { Log.d("BLUETOOTH CONNECTION", "Connected"); mTryingToConnect = false; mTryingToDiscoverServices = true; mConnected = true; gatt.discoverServices(); } else if(BluetoothGatt.STATE_DISCONNECTED == newState) { Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt."); mConnected = false; gatt.close(); if (!mConnectionFinished && mRetryCount == 0) { finishConnection(gatt); } } }

Creo que el periférico no es relevante, porque la aplicación iOS siempre puede conectarse sin este problema.

¿Algunas ideas? Gracias por adelantado.

¡Editar!

This respuesta dice que:

La conexión directa tiene un intervalo de 60 ms y una ventana de 30 ms, por lo que las conexiones se completan mucho más rápido. Además, solo puede haber una solicitud de conexión directa pendiente a la vez y se agota después de 30 segundos. onConnectionStateChange () recibe una llamada con el estado = 2, estado = 133 para indicar este tiempo de espera.

Entonces, en este intervalo de 30 segundos hay una solicitud de conexión pendiente y se agota el tiempo de espera en el segundo 30. Es poco probable, pero, ¿hay algo que pueda hacer para acortar este tiempo? O tal vez hay una explicación para el fallo de conexión que no estoy viendo. Gracias.

EDITAR 02/03/2016

Nueva cosa que puede ayudar. Cuando el problema comienza, eso es cuando se onConnectionStateChange(...) con newState igual a DESCONECTADO después de ~ 40ms que se llama con newState igual a CONNECTED, el estado es 62 = 0x03E. Mirando here ese código de estado significa GATT_CONN_FAIL_ESTABLISH. Cuando detecto este estado, estoy cerrando la conexión de Gatt, pero el problema persiste. También intenté desconectar y cerrar. Ideas? Gracias.


Android Bluetooth necesita ser reciclado de vez en cuando, ¿ha intentado reiniciar el BLE en el dispositivo cuando se encuentra con esta cantidad de tiempo?

Aquí hay un fragmento que he usado para reiniciar el BLE cuando comienzan a suceder cosas extrañas.

static Handler mHandler = new Handler(); public static void restartBle() { final BluetoothManager mgr = (BluetoothManager) ApplicationBase.getAppContext().getSystemService(Context.BLUETOOTH_SERVICE); final BluetoothAdapter adp = mgr.getAdapter(); if (null != adp) { if (adp.isEnabled()) { adp.disable(); // TODO: display some kind of UI about restarting BLE mHandler.postDelayed(new Runnable() { @Override public void run() { if (!adp.isEnabled()) { adp.enable(); } else { mHandler.postDelayed(this, 2500); } } }, 2500); } } }


No estoy seguro de si todavía está buscando una respuesta para esta pregunta. Personalmente, no recomendaría hacer "conexiones rápidas y cortas activadas por la acción del usuario" para dispositivos de baja energía. En su lugar, podría establecer la opción autoConnect en "true" en su método connectGatt.

device.connectGatt (mContext, true, mGattCallback); [en lugar de falso]

¡Espero eso ayude!


Si alguien tiene un problema similar, el problema finalmente se resolvió cambiando el chip BLE utilizado por el periférico (arduino). Antes de ese cambio, una solución que encontré estaba apagando y encendiendo el BLE después de cada conexión. La solución no fue perfecta, pero mejoró mucho la velocidad de conexión.