Связь между iOS и Android с помощью Bluetooth LE

У меня есть рабочее приложение, использующее CoreBluetooth для связи между iPad (центральным) и iPhone (периферийным). У меня есть одна услуга, которая имеет две характеристики. У меня Nexus 7 под управлением последней версии Android 4.3 с поддержкой BTLE. Android немного опоздал, чтобы запрыгнуть на подножку BTLE, но, похоже, они приближаются к этому так же, как это сделала iOS, где изначально они поддерживают только работу в качестве центра с периферийным режимом, который появится в более поздней версии. Я могу загрузить образец приложения BTLE для Android и просмотреть ближайшие периферийные устройства. Когда мой iPhone рекламируется как периферийное устройство, я вижу значение из CBAdvertisementDataLocalNameKey в списке ближайших периферийных устройств на стороне Android. Я могу подключиться к iPhone, и символ Bluetooth меняет цвет со светло-серого на черный, когда соединение установлено. Соединение всегда длится ровно 10 секунд, а затем отключается. На стороне Android я должен увидеть список доступных сервисов и характеристик, которые появляются сразу после подключения. Я доказал, что код Android настроен правильно, потому что я могу подключить его к имеющемуся у меня оборудованию TI CC2541DK-SENSOR, и все службы и характеристики будут перечислены при подключении к нему.

Я потратил последние несколько дней на устранение проблемы, но безуспешно. Проблема в том, что я не могу определить, какое устройство испытывает ошибку и, следовательно, вызывает отключение. Обратных вызовов от CBPeripheralManagerDelegate на этапе подключения или обнаружения службы нет, поэтому я понятия не имею, в какой момент возникает ошибка (если ошибка на стороне iOS). На стороне Android вызывается метод для инициирования обнаружения службы, однако их обратный вызов «onServicesDiscovered» никогда не вызывается, что вызывает недоумение. Есть ли способ, которым я могу покопаться в внутренностях коммуникации BTLE на стороне iOS, чтобы увидеть, что происходит, и определить, какая ошибка происходит?


person afrederick    schedule 23.08.2013    source источник
comment
Вы используете последнюю версию iOS, верно?   -  person www.jensolsson.se    schedule 23.08.2013
comment
@www.jensolsson.se Да   -  person afrederick    schedule 23.08.2013
comment
Вам следует купить ключ BLE и использовать решение для анализа пакетов TI: ti.com/tool/packet-sniffer То, что вы упомянули, звучит странно. Также попробуйте это приложение: itunes.apple.com/tr/ app/ble-utility/id606210918?mt=8 Это упрощает имитацию служб на устройстве i*. Если это удастся, то проблема, скорее всего, в вашем коде. Если это не удается, то требуется более глубокая проверка.   -  person allprog    schedule 23.08.2013
comment
@allprog Я загрузил приложение BLE Utility, а также LightBlue, оба приложения показали те же результаты, что и мое пользовательское приложение. Я нашел другую ветку со ссылкой здесь: code.google.com/p/android/issues/ Похоже, что это может быть ошибка реализации низкого уровня на стороне Android, но это еще не подтверждено.   -  person afrederick    schedule 24.08.2013
comment
Это очень вероятно. Выкатили только сейчас и начали с нуля. Наверняка есть проблемы. К сожалению, Google, кажется, более или менее игнорирует BLE. Они даже не потратили много времени на Java-переработку API. Держите этот пост в курсе. Я думаю, что вы представите ответ в ближайшее время. :)   -  person allprog    schedule 24.08.2013
comment
@allprog Подойдет! Это поможет, если люди пойдут и отметят эту проблему на сайте code.google.com. Надеюсь, это будет решено в следующей версии Android 4.3, я бы очень хотел, чтобы iOS и Android общались друг с другом!   -  person afrederick    schedule 24.08.2013
comment
Всем привет. Я разработчик, который сообщил об ошибке, на которую ссылается @afrederick. Я думаю, что allprog сказал правду. В любом случае, я даю вам, ребята, обновление. На странице AOSP есть еще один отчет о проблеме, которым владеет пользователь Android. Проблема та же, что и у меня. Так что будем надеяться, что они возьмутся за дело! Это ссылка на проблему:code.google.com/p/android/ Issues/detail?id=58896 Следите за этим!   -  person edoardotognoni    schedule 12.09.2013
comment
Я столкнулся с той же проблемой: Nexus 4 на 4.3 подключался к iPhone 5, выступая в качестве периферийного устройства с помощью LightBlue. Удалось подключиться, но обнаружение устройства так и не завершилось и отключилось. Nexus 4 обновлен до версии 4.4 (номер сборки KRT16S), и службы подключения и обнаружения работают. Умеет читать и записывать характеристики.   -  person vee    schedule 19.12.2013


Ответы (6)


Я уже прошел через это в течение как минимум одной недели с такой же проблемой. Я уже задавал вопрос здесь, и я уже ответил самостоятельно. Основная проблема - проблема с Android BUG. Он отправляет неразрешенную команду по фиксированному каналу L2CAP.

Но когда Android взаимодействует с обычными периферийными устройствами BLE, все работает очень хорошо. Фактически, образец BLE работает как шарм. Например, проблема заключается в том, что при общении с устройством iOS: сразу после установления соединения они начинают согласовывать свои параметры соединения (эта фаза не происходит с обычным периферийным устройством BLE), и именно здесь возникает проблема. Android отправляет iOS неверную команду, iOS разрывает соединение. Вот как это работает

О некоторых проблемах уже сообщили в Google, и одна из них уже принята, и я надеюсь, что они скоро начнут над ней работать.

К сожалению, что вы можете сделать, так это дождаться следующего выпуска Android. В любом случае, я настоятельно рекомендую вам взглянуть на мой отчет о проблеме со всеми моими тестовыми документами, если вы хотите пролить свет на эту проблему.

Вот ссылка: https://code.google.com/p/android/issues/detail?id=58725

person edoardotognoni    schedule 12.09.2013
comment
Спасибо за объяснение. Ваша проблема была обнаружена и ОП. Будем надеяться, что это будет решено в следующем выпуске. - person allprog; 12.09.2013
comment
Привет, ребята, есть новости по этому поводу? Команда Android работает над этой проблемой? Я не вижу обновлений в их отслеживании проблем... - person andresmafra; 16.06.2014
comment
Он был отмечен Google как устаревший 7 декабря 2014 года. Однако люди сообщают, что это все еще проблема. - person ThomasW; 24.04.2015

Я написал простой рабочий пример, относительно простой, и включил его с открытым исходным кодом на Github: https://github.com/GitGarage. Пока что он был протестирован только с Android Nexus 9 и iPhone 5s, но я предполагаю, что он также будет работать с Nexus 6 и различными типами iPhone. Пока что он явно настроен для связи между одним Android и одним iPhone, но я полагаю, что его можно настроить для гораздо большего.

Вот основные методы...

DROID SIDE — Отправка на 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();
}

DROID SIDE - Получение от 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++;
    }
}

СТОРОНА iOS — отправка на 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 — получение с 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)
                }
            }
        }
    }
}
person omikes    schedule 21.06.2015
comment
Спасибо, что поделились. Приятно знать, что это работает в теории, и хорошо иметь пример кода. Однако, когда я попытался проверить ваш пример git, BLEMingle.swift и BLECentralDelegate.swift отсутствовали. - person domenukk; 22.06.2015
comment
Пытался скомпилировать ваш код ios, используя xcode 6.3.2, он не компилируется после устранения ошибок компилятора. Я запускаю код, но не могу отправить текст. Постараюсь решить проблему. - person Inder Kumar Rathore; 24.06.2015
comment
Только что попытался скомпилировать, но BLEMingle.swift выдает 17 ошибок... Не могли бы вы проверить свой статус git? Большое спасибо за ваши усилия. Даже в нерабочем состоянии это огромная помощь. - person domenukk; 29.06.2015
comment
Xcode 6.3, 17 ошибок на BLEMingle.swift. Вы уверены, что отправили на гитхаб последнюю версию в основной ветке? - person idiogo; 30.06.2015
comment
Пробовали все почистить и восстановить? Или проверить на другом аккаунте? Может быть, он даже не удосужился перекомпилировать этот файл, так как вы ничего не меняли? - person domenukk; 10.07.2015
comment
При компиляции или запуске? В настоящее время я не дома, но попробую позже на этой неделе и отчитаюсь. ;) - person domenukk; 14.07.2015
comment
Проверил на другом компе, компилируется нормально. Я просто удалил ссылки на банку, которые все еще были в сборке. Отправка файлов на iOS работает безупречно! Отличная работа! Однако Nexus 6 иногда получает только части сообщений. Я немного отлаживаю сейчас. - person domenukk; 16.07.2015
comment
ОК, круто. Я думаю, что сложная часть сделана, для отправки сообщений на Android я думал об отправке одной и той же части сообщения несколько раз, а затем на принимающей стороне он просто будет вести журнал примерно из двадцати последних частей сообщения и отменять любые дубликаты, которые приходят через. Прогресс! - person omikes; 16.07.2015
comment
В любом случае, мы столкнемся с серьезными проблемами с этим решением, как только будет задействовано более 2 устройств... - person domenukk; 17.07.2015
comment
Я пробовал этот код, и он не работает, потому что в основном это ошибка Android, говоря внутренне. Что касается компиляции, использование XCode 7 не работает и не компилируется, но компилируется в Android. Я написал код iOS в Objective-C, чтобы сделать его компилируемым, но он тоже не работает. - person AlfuryDB; 15.10.2015
comment
У меня он снова работает, обновлен до последних версий iOS и Android. Попробуйте. - person omikes; 04.10.2016

Я хотел бы добавить немного информации в эту тему как часть нашей RnD по теме BLE между кроссплатформенными.

Периферийный режим работает без проблем с Xiomi Mi A1 (версия ОС Oreo, Android 8.0).

Вот несколько наблюдений о пропускной способности, которые мы обнаружили во время RnD на iPhone 8 и Xiomi Mi A1, но ее еще предстоит довести до совершенства с другими пользовательскими ОС Android, используемыми в последней версии Samsung S8. Приведенные ниже данные основаны на write_with_response.

  1. iPhone 8 (BLE 5.0) в качестве рабочего стола Central и Linux (Ubuntu 16.04 с ключом BLE 4.0): MTU = 2048: пропускная способность — 2,5 килобайта в секунду.

  2. iPhone 8 (BLE 5.0) в качестве центрального устройства и ОС Android с BLE версии 4.2 в качестве периферийного устройства (Xiomi Mi A1): MTU = 180 : Пропускная способность — 2,5 килобайта в секунду.

  3. iPhone 8 (BLE 5.0) в качестве центрального устройства и iPhone 7 plus (BLE 4.2) в качестве периферийного устройства: MTU = 512: Пропускная способность — 7,1 килобайт в секунду.

  4. iPhone 8 (BLE 5.0) в качестве центрального устройства и Samsung S8 (BLE 5.0) в качестве периферийного устройства: Samsung S8 не работал в качестве периферийного устройства.

  5. iPhone 8 (BLE 5.0) в качестве центрального устройства и iPhone 8 plus (BLE 5.0) в качестве периферийного устройства: MTU = 512: Пропускная способность — 15,5 килобайт в секунду.

person Sudhin Philip    schedule 01.03.2018
comment
Это действительно полезная информация. У вас есть идеи, почему он может выйти из строя с S8? - person elslooo; 26.05.2018
comment
Я полагаю, что это может быть связано с настраиваемой версией ОС Samsung Android. Но я не могу найти никакой официальной документации по этому поводу. - person Sudhin Philip; 26.05.2018

Я делаю что-то подобное с центральным Android и периферийным устройством iOS. Я обнаружил, что они отключатся, если никто не подписан ни на одну из периферийных услуг.

Не забудьте обновить дескриптор при подписке, иначе он ничего не делает (т.е. вызывает метод делегата на стороне 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);
}

Также следует отметить, что я даже не мог видеть, как устройство iOS выполняет обычное сканирование BLE на устройстве Android (startLeScan), но запуск сканирования BT Classic с помощью широковещательного приемника решил проблему (startDiscovery).

person Charlie Scott-Skinner    schedule 25.06.2015

Я просто хотел поделиться своими знаниями об этом, поскольку я имел дело с этим некоторое время назад, и я ушел, так как Google не поддерживает. Вышеупомянутый код, за что большое спасибо, не работает. Вы можете в разумные сроки закодировать приложение Bluetooth для iOS для iOS или Android для Android, но проблема возникает, когда вы пытаетесь установить связь между iOS и Android. Существует хорошо задокументированная проблема с 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) Я сотрудничал, но Google вообще ничего не произносил, и, кажется, они закрыли проблему, и в Android M ничего не изменилось, так как я просматривал код и не вижу ничего дальнейшие отличия. Проблема возникает, когда Android пытается подключиться, особенно в предложении «если еще»; этот код в основном отклоняет передачу и обрывает связь, поэтому он не работает. На данный момент решения для этого нет. Вы можете сделать прямое решение WiFi, но это ограничение, и при этом возникают дополнительные проблемы. Проблема не существует, если вы хотите внедрить BLE с внешним оборудованием (малина, датчики и т. д.), но это не работает между iOS и Android. Эта технология одинакова на обеих платформах, но она плохо реализована в Android или специально вставлена ​​в ловушку Google, чтобы не открывать призрак для связи между обеими платформами.

person AlfuryDB    schedule 24.11.2015

Мы много экспериментировали с кроссплатформенными соединениями BLE (iOS‹-›Android) и узнали, что по-прежнему существует много несовместимостей и проблем с подключением.

Если ваш вариант использования основан на функциях и вам нужен только базовый обмен данными, я бы посоветовал взглянуть на фреймворки и библиотеки, которые могут обеспечить вам межплатформенное взаимодействие без необходимости создавать его с нуля.

Например, http://p2pkit.io или поищите поблизости

Отказ от ответственности: я работаю в Uepaa, разрабатываю p2pkit.io для Android и iOS.

person p2pkit    schedule 03.05.2016