Comunicación entre iOS y Android con Bluetooth LE


Tengo una aplicación que funciona usando CoreBluetooth para comunicarse entre un iPad (central) y un iPhone (periférico). Tengo un servicio que tiene dos características. Tengo un Nexus 7 ejecutando el último Android 4.3 con soporte BTLE. Android es un poco tarde para subirse al carro de BTLE, pero parece que se están acercando de manera similar a como lo hizo iOS, donde inicialmente solo admiten actuar como una central con el modo periférico que viene en una versión posterior. Puedo cargar la aplicación Android BTLE de muestra y busque periféricos cercanos. Con mi iPhone publicidad como un periférico puedo ver el valor de CBAdvertisementDataLocalNameKey en la lista de periféricos cercanos en el lado Android. Puedo conectarme al iPhone y el símbolo Bluetooth cambia de gris claro a negro cuando se realiza la conexión. La conexión siempre dura exactamente 10 segundos y luego se desconecta. En el lado de Android, se supone que debo ver una lista de servicios y características disponibles que aparecen inmediatamente después de la conexión. He demostrado que el código de Android está configurado correctamente porque puedo conectarlo al hardware TI CC2541DK-SENSOR que tengo y todos los servicios y características se enumeran al conectarse a él.

He pasado los últimos días solucionando el problema sin éxito. El problema es que no puedo determinar qué dispositivo está experimentando un error y, por lo tanto, causando la desconexión. No hay devoluciones de llamada de CBPeripheralManagerDelegate durante la fase de conexión o la fase de descubrimiento de servicio, así que no tenga idea en qué momento se produce un error (si el error está en el lado de iOS). En el lado de Android, se llama a un método para iniciar el descubrimiento de servicios, sin embargo, su devolución de llamada "ervervicesdiscovered "nunca se llama, lo que es desconcertante. ¿Hay alguna manera de que pueda profundizar en las entrañas de la comunicación BTLE en el lado de iOS para ver lo que está pasando y determinar qué error está teniendo lugar?

Author: afrederick, 2013-08-23

6 answers

Ya he pasado por esto durante al menos una semana teniendo este mismo problema. Ya he hecho una pregunta aquí y ya he respondido por mi cuenta. El problema principal es un problema de error de Android. Está enviando un comando no permitido en un canal fijo L2CAP.

Pero cuando Android se comunica con dispositivos periféricos BLE normales, funciona bastante bien. De hecho, la muestra BLE funciona como un encanto. El problema es cuando se comunica con un dispositivo iOS por ejemplo: Justo después de la se realiza la conexión, comienzan a negociar sus parámetros de conexión (esta fase no sucede con el periférico BLE normal), y es entonces cuando surge el problema. Android envía un comando incorrecto a iOS, iOS elimina la conexión. Así es básicamente como funciona

Algunos problemas ya han sido reportados a Google, y uno de ellos ya ha sido aceptado y espero que comiencen a trabajar en él pronto.

Desafortunadamente, lo que puede hacer, es esperar hasta la próxima versión de Android. De todos modos, le sugiero que eche un vistazo a mi informe de problemas con todos mis documentos de prueba si desea obtener algo de luz sobre este problema.

Aquí está el enlace: https://code.google.com/p/android/issues/detail?id=58725

 24
Author: edoardotognoni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-24 03:01:29

He escrito un ejemplo de trabajo simple, relativamente simple, y lo he incluido de código abierto en Github: https://github.com/GitGarage. Hasta ahora solo se ha probado con un Android Nexus 9 y un iPhone 5s, pero supongo que también funcionaría con un Nexus 6 y varios tipos de iPhone. Hasta ahora está configurado explícitamente para comunicarse entre un Android y un iPhone, pero supongo que es modificable para hacer mucho más.

Aquí están los métodos clave...

Envío LATERAL de DROIDES para iOS:

private void sendMessage() {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            if (mBTAdapter == null) {
                return;
            }
            if (mBTAdvertiser == null) {
                mBTAdvertiser = mBTAdapter.getBluetoothLeAdvertiser();
            }
               // get the full message from the UI
            String textMessage = mEditText.getText().toString(); 
            if (textMessage.length() > 0)
            {
                   // add 'Android' as the user name
                String message = "Android: " + textMessage; 

                while (message.length() > 0) {
                    String subMessage;
                    if(message.length() > 8)
                    {    // add dash to unfinished messages
                        subMessage = message.substring(0,8) + "-"; 
                        message = message.substring(8);
                        for (int i = 0; i < 20; i++) // twenty times (better safe than sorry) send this part of the message. duplicate parts will be ignored
                        {
                            AdvertiseData ad = BleUtil.makeAdvertiseData(subMessage);
                            mBTAdvertiser.startAdvertising(BleUtil.createAdvSettings(true, 100), ad, mAdvCallback);
                            mBTAdvertiser.stopAdvertising(mAdvCallback);
                        }
                    }
                    else
                    {  // otherwise, send the last part
                        subMessage = message;
                        message = "";
                        for (int i = 0; i < 5; i++)
                        {
                            AdvertiseData ad = BleUtil.makeAdvertiseData(subMessage);
                            mBTAdvertiser.startAdvertising(
                                    BleUtil.createAdvSettings(true, 40), ad,
                                    mAdvCallback);
                            mBTAdvertiser.stopAdvertising(mAdvCallback);
                        }
                    }
                }
                threadHandler.post(updateRunnable);
            }
        }
    });
    thread.start();
}

LADO DROID-Recepción de iOS:

@Override
public void onLeScan(final BluetoothDevice newDevice, final int newRssi,
                     final byte[] newScanRecord) {

    int startByte = 0;
    String hex = asHex(newScanRecord).substring(0,29);
       // check five times, startByte was used for something else before
    while (startByte <= 5) {
       // check if this is a repeat message
        if (!Arrays.asList(used).contains(hex)) {
            used[ui] = hex;

            String message = new String(newScanRecord);
            String firstChar = message.substring(5, 6);
            Pattern pattern = Pattern.compile("[ a-zA-Z0-9~!@#$%^&*()_+{}|:\"<>?`\\-=;',\\./\\[\\]\\\\]", Pattern.DOTALL);
               // if the message is comprised of standard characters...
            Matcher matcher = pattern.matcher(firstChar);
            if (firstChar.equals("L"))
            {
                firstChar = message.substring(6, 7);
                pattern = Pattern.compile("[ a-zA-Z0-9~!@#$%^&*()_+{}|:\"<>?`\\-=;',\\./\\[\\]\\\\]", Pattern.DOTALL);
                matcher = pattern.matcher(firstChar);
            }

            if(matcher.matches())
            {
                TextView textViewToChange = (TextView) findViewById(R.id.textView);
                String oldText = textViewToChange.getText().toString();
                int len = 0;
                String subMessage = "";
                   // add this portion to our final message
                while (matcher.matches())  
                {
                    subMessage = message.substring(5, 6+len);
                    matcher = pattern.matcher(message.substring(5+len, 6+len));
                    len++;
                }
                subMessage = subMessage.substring(0,subMessage.length()-1);

                Log.e("Address",newDevice.getAddress());
                Log.e("Data",asHex(newScanRecord));
                boolean enter = subMessage.length() == 16;
                enter = enter && !subMessage.substring(15).equals("-");
                enter = enter || subMessage.length() < 16;
                textViewToChange.setText(oldText + subMessage.substring(0, subMessage.length() - 1) + (enter ? "\n" : ""));
                ui = ui == 2 ? -1 : ui;
                ui++;

                Log.e("String", subMessage);
            }
            break;
        }
        startByte++;
    }
}

LADO IOS-Envío a Android:

func startAdvertisingToPeripheral() {
    var allTime:UInt64 = 0;
    if (dataToSend != nil)
    {
        datastring = NSString(data:dataToSend, encoding:NSUTF8StringEncoding) as String
        datastring = "iPhone: " + datastring
        if (datastring.length > 15)
        {
            for (var i:Double = 0; i < Double(datastring.length)/15.000; i++)
            {
                let delay = i/10.000 * Double(NSEC_PER_SEC)
                let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
                allTime = time
                dispatch_after(time, dispatch_get_main_queue(), { () -> Void in self.sendPart() });
            }
        }
        else
        {
            var messageUUID = StringToUUID(datastring)
            if !peripheralManager.isAdvertising {
                peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: messageUUID)]])
            }
        }
    }
}

IOS LADO de recepción de Android:

func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {

    delegate?.didDiscoverPeripheral(peripheral)
    var splitUp = split("\(advertisementData)") {$0 == "\n"}
    if (splitUp.count > 1)
    {
        var chop = splitUp[1]
        chop = chop[0...chop.length-2]
        var chopSplit = split("\(chop)") {$0 == "\""}

        if !(chopSplit.count > 1 && chopSplit[1] == "Device Information")
        {
            var hexString = chop[4...7] + chop[12...19] + chop[21...26]
            var datas = hexString.dataFromHexadecimalString()
            var string = NSString(data: datas!, encoding: NSUTF8StringEncoding) as String
            if (!contains(usedList,string))
            {
                usedList.append(string)
                if (string.length == 9 && string[string.length-1...string.length-1] == "-")
                {
                    finalString = finalString + string[0...string.length-2]
                }
                else
                {
                    lastString = finalString + string + "\n"
                    println(lastString)
                    finalString = ""
                    usedList = newList
                    usedList.append(string)
                }
            }
        }
    }
}
 13
Author: oMiKeY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-21 15:14:33

Me gustaría agregar poca información a este hilo como parte de nuestro RnD sobre el tema BLE entre plataformas cruzadas.

El modo periférico funciona sin problemas con Xiomi Mi A1 (versión del sistema operativo Oreo, Android 8.0).

Aquí hay algunas observaciones sobre el rendimiento que encontramos durante nuestro RnD en el iPhone 8 y Xiomi Mi A1, pero todavía tiene que madurar con otro sistema operativo Android personalizado utilizado en el último Samsung S8. Los siguientes datos se basan en write_with_response.

  1. IPhone 8 (BLE 5.0) como escritorio central y Linux (Ubuntu 16.04 con BLE dongle 4.0): MTU = 2048 : Rendimiento - 2.5 KiloBytes por segundo.

  2. IPhone 8 (BLE 5.0) como sistema operativo Central y Android con BLE versión 4.2 como Periférico (Xiomi Mi A1): MTU = 180: Rendimiento-2.5 KiloBytes por segundo.

  3. IPhone 8 (BLE 5.0) como Central y iPhone 7 plus (BLE 4.2) como Periférico : MTU = 512 : Rendimiento - 7.1 KiloBytes por segundo.

  4. IPhone 8 (BLE 5.0) como central y Samsung S8 (BLE 5.0) como periférico : Samsung S8 no pudo funcionar como periférico

  5. IPhone 8 (BLE 5.0) como Central y iPhone 8 plus (BLE 5.0) como Periférico : MTU = 512 : Rendimiento - 15.5 KiloBytes por segundo.

 2
Author: Sudhin Philip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-01 13:04:00

Estoy haciendo algo similar con una central Android y un periférico iOS. Descubrí que se desconectarían si nada se suscribía a ninguno de los servicios del periférico.

No se olvide de actualizar el descriptor cuando se suscriba a otra cosa que en realidad no hace nada (es decir, llamar al método delegado en el lado de iOS).

public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.v(TAG, "BluetoothAdapter not initialized");
        return;
    }

    UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");    // UUID for client config desc
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
}

También podría ser de notar que ni siquiera pude ver el dispositivo iOS haciendo un análisis BLE normal en el dispositivo Android (startLeScan), pero iniciando un BT El escaneo clásico con un receptor de difusión resolvió el problema (startDiscovery).

 1
Author: Charlie Scott-Skinner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-25 12:49:40

Solo quería compartir mi conocimiento sobre eso, ya que me ocupé de él hace algún tiempo y renuncié ya que no hay soporte por parte de Google. El código antes mencionado, al que agradezco mucho, no funciona. Puede codificar en un tiempo razonable una aplicación iOS a iOS o android a android bluetooth le, pero el problema viene cuando intenta comunicarse entre iOS y Android. Hay un problema bien documentado de Google (https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=58725) Colaboré pero Google no pronunció en absoluto y parece que cerraron el problema y nada ha cambiado en Android M como he estado buscando en el código y no puedo ver más diferencias. El problema viene cuando Android intenta conectarse y específicamente en una oración "if else" ; este código básicamente rechaza la transmisión y corta la comunicación para que no funcione. Por el momento, no hay solución para eso. Puede hacer una solución directa WiFi, pero es una limitación y hay más problemas para hacerlo. El problema no existe si se quiere implementar BLE con hardware externo(raspberry, sensores, etc.,) pero no funciona entre iOS y Android. La tecnología es bastante la misma en ambas plataformas pero no está bien implementada en Android o se inserta propósito trampa por Google para no abrir el espectro a comunicarse entre ambas plataformas.

 -1
Author: AlfuryDB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-24 11:30:59

Tal vez un poco retrasado, pero tal vez su dolor se puede aliviar ligeramente ;)

Hemos estado experimentando mucho con conexiones BLE multiplataforma (iOS Android) y hemos aprendido que todavía hay muchas incompatibilidades y problemas de conexión.

Si su caso de uso está basado en características y solo necesita intercambio de datos básicos, le sugeriría que busque Marcos y Bibliotecas que puedan lograr la comunicación multiplataforma para usted, sin necesidad de construirla desde cero.

Por ejemplo: http://p2pkit.io o google cerca

Descargo de responsabilidad: Trabajo para Uepaa, desarrollando p2pkit.io para Android e iOS.

 -2
Author: p2pkit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 16:17:30