我有一个使用CoreBluetooth的应用程序在iPad(中央)和iPhone(外围设备)之间进行通信。我有一项服务有两个特点。我有一台运行最新Android 4.3并支持BTLE的Nexus 7。 Android有点迟到了BTLE的潮流,但看起来他们正在接近它,就像iOS的做法一样,最初他们只支持在后续版本中作为外设模式的中心。我可以加载示例Android BTLE应用程序并浏览附近的外围设备。将我的iPhone广告作为外围设备,我可以在Android端附近的外围设备列表中看到CBAdvertisementDataLocalNameKey的值。我可以连接到iPhone,蓝牙符号在连接时从浅灰色变为黑色。连接始终持续10秒,然后断开连接。在Android方面,我应该会在连接时看到可用服务和特征列表。我已经证明Android代码设置正确,因为我可以将它连接到我所拥有的TI CC2541DK-SENSOR硬件,并在连接时列出所有服务和特性。
我花了最后几天对问题进行排查,但没有成功。问题是我无法确定哪个设备出现错误并因此导致断开连接。在连接阶段或服务发现阶段没有来自CBPeripheralManagerDelegate的回调,所以我不知道错误发生在什么时候(如果错误发生在iOS端)。在Android方面,调用一个方法来启动服务发现,但是从不调用它们的回调“onServicesDiscovered”,这是令人困惑的。有什么方法可以深入了解iOS端BTLE通信的内容,看看发生了什么,并确定发生了什么错误?
答案 0 :(得分:27)
我已经经历了至少一周的同样问题。我已经在这里问了一个问题,我已经自己回答了。主要问题是Android BUG问题。它在固定的L2CAP channnel上发送一个非允许的命令。
但是当Android与普通的外围BLE设备通信时,它的效果非常好。事实上,BLE样本就像一个魅力。问题是当与iOS设备通信时,例如:在建立连接之后,他们就开始协商他们的连接参数(这个阶段不会发生在正常的BLE外设上),这就是出现问题的时候。 Android向iOS发送错误命令,iOS断开连接。这基本上就是它的运作方式
已经向Google报告了一些问题,其中一个问题已经被接受,我希望他们很快就会开始研究。
不幸的是,你可以做的是等到下一个Android版本。无论如何,如果你想对这个问题有所了解,我强烈建议你查看我的问题报告以及我的所有测试文件。
以下是链接:https://code.google.com/p/android/issues/detail?id=58725
答案 1 :(得分:14)
我写了一个简单的工作示例,相对简单,并在Github上包含它开源:https://github.com/GitGarage。到目前为止,它仅使用Android Nexus 9和iPhone 5s进行了测试,但我认为它也适用于Nexus 6和各种iPhone类型。到目前为止,它已明确设置为在一个Android和一个iPhone之间进行通信,但我认为可以进行更多的调整。
以下是关键方法......
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();
}
@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++;
}
}
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)]])
}
}
}
}
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)
}
}
}
}
}
答案 2 :(得分:4)
我想在这个线程中添加一些信息,作为跨平台之间关于BLE主题的RnD的一部分。
外围模式与Xiomi Mi A1(OS版Oreo,Android 8.0)没有任何问题。
我们在iPhone 8和Xiomi Mi A1上的RnD中发现了很少的吞吐量观察,但它仍然需要与最新的三星S8中使用的其他自定义Android操作系统一起成熟。以下数据基于write_with_response。
iPhone 8(BLE 5.0)作为中央和Linux桌面(带有BLE加密狗4.0的Ubuntu 16.04):MTU = 2048:吞吐量 - 每秒2.5 KiloBytes。
iPhone 8(BLE 5.0)作为中央和Android操作系统,BLE版本4.2为外围设备(Xiomi Mi A1):MTU = 180:吞吐量 - 每秒2.5 KiloBytes。
iPhone 8(BLE 5.0)作为Central和iPhone 7 plus(BLE 4.2)作为外围设备:MTU = 512:吞吐量 - 每秒7.1 KiloBytes。
iPhone 8(BLE 5.0)作为中环和三星S8(BLE 5.0)作为外设:三星S8无法作为外设工作
iPhone 8(BLE 5.0)作为Central和iPhone 8 plus(BLE 5.0)作为外围设备:MTU = 512:吞吐量 - 每秒15.5 KiloBytes。
答案 3 :(得分:1)
我正在使用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设备在Android设备上执行正常的BLE扫描(startLeScan),但是使用广播接收器启动BT Classic扫描解决了问题(startDiscovery)
答案 4 :(得分:-1)
我只是想分享我的知识,因为我在前一段时间处理过这个问题,因为谷歌没有支持我就退出了。上面提到的代码,我感谢很多,不起作用。您可以在合理的时间内编写iOS到iOS或android到android蓝牙应用程序的代码但是当您尝试在iOS和Android之间进行通信时会出现问题。有一个记录良好的谷歌问题(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)我合作,但谷歌根本没有发音,似乎他们关闭了这个问题,没有任何改变在Android M,因为我一直在调查代码,不能再看到差异。当Android尝试连接并且特别是在“if else”句子中时,问题就出现了;这段代码基本上拒绝传输并削减通信,因此无法正常工作。目前,没有解决方案。你可以做一个WiFi直接解决方案,但这是一个限制,还有其他问题。如果你想用外部硬件(覆盆子,传感器等)实现BLE,这个问题就不存在了,但它在iOS和android之间不起作用。这两种平台的技术完全相同,但在Android中没有很好地实现,或谷歌没有打开幽灵在两个平台之间进行通信。
答案 5 :(得分:-2)
也许有点延迟,但也许你的痛苦可以稍微缓解;)
我们一直在尝试跨平台BLE连接(iOS&lt; - &gt; Android),并了解到仍然存在许多不兼容性和连接问题。
如果您的用例是功能驱动的,并且您只需要基本的数据交换,我建议您查看可以为您实现跨平台通信的框架和库,而无需您从头开始构建。
例如:http://p2pkit.io或谷歌附近
免责声明:我为Uepaa工作,为Android和iOS开发p2pkit.io。