Communiquer entre iOS et Android avec Bluetooth LE
j'ai une application qui fonctionne avec CoreBluetooth pour communiquer entre un iPad (central) et un iPhone (périphérique). J'ai un service qui a deux caractéristiques. J'ai un Nexus 7 qui exécute le dernier Android 4.3 avec le soutien de BTLE. Android est un peu en retard pour prendre le train en marche, mais il semble qu'ils l'approchent de la même façon que iOS, où initialement ils ne supportent agir comme un central avec le mode périphérique à venir dans une version ultérieure. Je peux charger L'échantillon Android BTLE appliquer et rechercher des périphériques à proximité. Avec ma publicité iPhone comme un périphérique, je peux voir la valeur de CBAdvertisementDataLocalNameKey dans la liste des périphériques à proximité sur le côté Android. Je peux me connecter à l'iPhone et le symbole Bluetooth passe du gris clair au noir quand la connexion est faite. La connexion dure toujours exactement 10 secondes puis se déconnecte. Du côté Android je suis censé voir une liste des services disponibles et les caractéristiques apparaissent immédiatement après connexion. J'ai prouvé que le code Android est configuré correctement parce que je peux le connecter au matériel TI CC2541DK-SENSOR que j'ai et tous les services et caractéristiques sont énumérés lors de la connexion à celui-ci.
j'ai passé les derniers jours de résoudre le problème, sans succès. Le problème est que je ne peux pas déterminer quel appareil est victime d'une erreur et donc de provoquer la déconnexion. Il n'y a pas de callbacks de CBPeripheralManagerDelegate pendant la phase de connexion ou phase de découverte du service donc je n'ai aucune idée à quel moment une erreur se produit (si l'erreur est du côté iOS). Du côté Android, une méthode est appelée pour initier la découverte de service, mais leur callback "onservesdovered" n'est jamais appelé, ce qui est déconcertant. Y a-t-il un moyen que je puisse creuser dans les entrailles de la communication de BTLE du côté de iOS pour voir ce qui se passe et déterminer quelle erreur se produit?
6 réponses
je suis déjà passé par là depuis au moins une semaine avec ce même numéro. J'ai déjà posé une question ici, et j'ai déjà répondu sur mon propre. Le problème principal est un problème de bogue Android. Il envoie une commande non autorisée sur un canal fixe L2CAP.
mais quand Android est en communication avec les périphériques périphériques normaux, il fonctionne assez bien. En fait, L'échantillon fonctionne comme un charme. Le problème est quand est comuniquer avec un dispositif iOS pour exemple: juste après la connexion est faite, ils commencent à négocier leurs paramètres de connexion (cette phase ne se produit pas avec le périphérique BLE normal), et c'est là que le problème apparaît. Android envoie une mauvaise commande à iOS, iOS coupe la connexion. C'est comme ça que ça marche
certains problèmes ont déjà été signalés à Google, et l'un d'eux a déjà été accepté et j'espère qu'ils vont commencer à travailler dessus bientôt.
malheureusement, quoi vous pouvez attendre la prochaine version D'Android. Quoi qu'il en soit, je vous suggère fortement de jeter un oeil à mon rapport de question avec tous mes documents de test si vous voulez faire un peu de lumière sur ce problème.
voici le lien: https://code.google.com/p/android/issues/detail?id=58725
j'ai écrit un exemple de travail simple, bien relativement simple, et l'ai inclus open-source sur Github: https://github.com/GitGarage . Jusqu'à présent, il a seulement été testé avec un Android Nexus 9 et un iPhone 5s, mais je présume qu'il fonctionnerait également avec un Nexus 6 et divers types d'iPhone. Jusqu'à présent, il est mis en place explicitement pour communiquer entre un Android et un iPhone, mais je présume qu'il est tweakable de faire beaucoup plus.
Voici la clé méthode...
DROID SIDE-Sending to 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-Receiving from 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 CÔTÉ - Envoi d'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 SIDE - Receiving de Android:
func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
delegate?.didDiscoverPeripheral(peripheral)
var splitUp = split("\(advertisementData)") {"151930920" == "\n"}
if (splitUp.count > 1)
{
var chop = splitUp[1]
chop = chop[0...chop.length-2]
var chopSplit = split("\(chop)") {"151930920" == "\""}
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)
}
}
}
}
}
je voudrais ajouter peu d'informations à ce fil dans le cadre de notre RnD sur le sujet BLE entre plateforme cross.
Le mode périphériquefonctionne sans problème avec Xiomi Mi A1 (version OS Oreo, Android 8.0).
voici quelques observations sur le débit que nous avons trouvé lors de notre RnD sur iPhone 8 et Xiomi Mi A1, mais il doit encore être mûri avec D'autres OS Android personnalisé utilisé dans la dernière Samsung S8. Les données ci-dessous est basé sur write_with_response.
-
iPhone 8 (BLE5. 0) comme bureau Central et Linux (Ubuntu 16.04 avec ble6. 0): MTU = 2048 : débit - 2,5 kilo-octets par seconde.
-
iPhone 8 (BLE5. 0) comme OS central et Android avec la version 4.2 comme périphérique (Xiomi Mi A1): MTU = 180: débit - 2,5 kilo-octets par seconde.
-
iPhone 8 (BLE5. 0) comme Central et iPhone 7 plus (BLE4. 2) comme périphérique: MTU = 512: débit-7,1 kilo-octets par seconde.
-
iPhone 8 (BLE5. 0) comme Central et Samsung S8 (BLE5. 0) comme périphérique : Samsung S8 n'a pas fonctionné comme périphérique
-
iPhone 8 (BLE5. 0) comme Central et iPhone 8 plus (BLE5. 0) comme périphérique : MTU = 512 : débit - 15.5 kilo-octets par seconde.
je fais quelque chose de similaire avec un Android central et un périphérique iOS. J'ai trouvé qu'ils déconnecter si rien souscrit à l'un des périphériques de services.
n'oubliez pas de mettre à jour le descripteur lors de l'abonnement sinon il ne fait rien (par exemple, appeler la méthode delegate du côté 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);
}
il est peut-être aussi à noter que je ne pouvais même pas voir l'appareil iOS faire un BLESCAN normal sur L'Android un appareil (startLeScan), mais le fait de lancer un scan BT Classic avec un récepteur de diffusion a résolu le problème (startDiscovery).
je voulais juste partager mes connaissances à ce sujet comme je l'ai traité il ya un certain temps et je démissionne car il n'y a pas de soutien par google. Le code susmentionné, que je remercie beaucoup, ne fonctionne pas. Vous pouvez coder dans un délai raisonnable un iOS à iOS ou android pour android bluetooth LE application, mais le problème vient lorsque vous essayez de communiquer entre iOS et android. Il ya un problème bien documenté 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 ) j'ai collaboré, mais google ne s'est pas prononcé du tout et il semble qu'ils ont clos le problème et rien n'a changé dans android J'ai été à la recherche dans le code et ne peut pas voir plus de différences. Le problème vient Quand Android essaie de se connecter et spécifiquement dans une phrase "SI AUTREMENT"; ce code rejette fondamentalement la transmission et ça coupe la communication et ça ne marche pas. Pour le moment, il n'y a pas de solution pour cela. Vous pouvez faire une solution WiFi direct, mais c'est une limitation et il ya d'autres problèmes à le faire. Le problème n'existe pas si vous souhaitez mettre en œuvre la fonction BLE avec du matériel externe (framboise, capteurs, etc.).,) mais il ne fonctionne pas entre iOS et android. La technologie est tout à fait la même dans les deux plates-formes, mais il n'est pas bien mis en œuvre dans Android ou est expressément inséré pitfall par google pour ne pas ouvrir le spectre pour communiquer entre les deux plateformes.
peut-être un peu retardé, mais peut-être que votre douleur peut être légèrement soulagée;)
nous avons fait beaucoup d'expériences avec les connexions multiplateformes (iOS<-> Android) et avons appris qu'il y a encore beaucoup d'incompatibilités et de problèmes de connexion.
si votre cas d'utilisation est axée sur les fonctionnalités et que vous n'avez besoin que d'un échange de données de base, je suggérerais d'examiner les cadres et les bibliothèques qui peuvent réaliser une communication multiplateforme pour vous, sans vous avoir besoin de le construire à partir de zéro.
par exemple: http://p2pkit.io ou google à proximité
avertissement: je travaille pour L'Uepaa, développer p2pkit.io pour Android et iOS.