信标 + 蓝牙通信(GATT 配置文件)无法正常工作 [BLE]
Beacons + Bluetooth communication (GATT profile) isn't working fine [BLE]
问题
我有一个外围设备正在发射具有特定 ID 的信标。我的智能手机(具有核心作用)正在搜索外围设备的信标。当它找到正确的信标时,它会通过 GATT 配置文件开始蓝牙通信。
第一次连接设备时,数据正确地从一个设备发送到另一个设备,但第二次(我有一个计时器每 20 秒尝试连接两个设备),连接失败我不知道为什么...我必须终止应用程序进程并重新打开它以使其再次运行。
我做了什么
对于信标,我使用的是 altbeacon,所以中央设备中的信标检测代码是这样的:
@Override
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.i(TAG, "Beacon detected!");
if (beacons.size() > 0 && Utils.getUserData(getApplicationContext(), "id").compareTo("-1") != 0) {
if(region.getId1().compareTo(Identifier.parse(identifier))==0) {
Beacon beacon = beacons.iterator().next();
Log.i(TAG, "The first beacon I see is about " + beacon.getDistance() + " meters away. "
+ region.getId1());
String mac = beacon.getBluetoothAddress();
Log.i("MAC", "MAC: "+mac);
CustomBluetoothManager customBluetoothManager = CustomBluetoothManager.getInstance(getApplicationContext());
if(!customBluetoothManager.isScanning()) {
customBluetoothManager.scanLeDevice(true, mac);
}
}
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", Identifier.parse(identifier),
null, null));
} catch (RemoteException e) { }
}
找到信标后,将调用 scanLeDevice:
public void scanLeDevice(final boolean enable, String mac) {
target_mac = mac;
mHandler = new Handler();
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
这是 mLeScanCallback 代码:
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
long epoch = System.currentTimeMillis() / 1000;
SharedPreferences prefs = context.getSharedPreferences(
"epoch", Context.MODE_PRIVATE);
long epochStored = prefs.getLong("epoch", 0);
if (device.getAddress().compareTo(MAC) == 0 && after20Seconds) {
prefs.edit().putLong("epoch", epoch).apply();
Log.d("epoch", Long.toString(epoch));
mDeviceAddress = device.getAddress();
final Intent gattServiceIntent = new Intent(context, BluetoothLeService.class);
context.bindService(gattServiceIntent, mServiceConnection, context.BIND_AUTO_CREATE);
}
}
};
我不得不对 MAC 地址进行硬编码,因为信标包含在数据包中的 MAC 地址与真实设备 MAC 不同(来自BLE 定义,但我不知道它到底是如何工作的。
最后,当数据发送后,我关闭并断开了蓝牙通道,我也尝试解除绑定服务,但没有任何效果:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
disconnect();
close();
unbindService(mServiceConnection);
}
因此,使用此代码,设备第一次连接时数据发送正确,但 20 秒后,在第二次尝试中,永远不会建立通信。
问题
- 知道问题出在哪里吗?
- BLE 是如何工作的?在信标数据包中,出于安全原因,MAC 是伪造的。但是,如果我从信标扫描应用程序中获取它并将其硬编码到该应用程序中,则通信有效...我认为它不应该有效。
- 如何从信标数据包中获取正确的设备MAC?
- 你知道任何使用蓝牙通信的信标的示例吗?
- 如果我通过"mBluetoothAdapter.stopLeScan(mLeScanCallback)"方法停止扫描,好像扫描并没有真正停止...这正常吗?日志不断显示 "BluetoothLeScanner: startRegisteration: mLeScanClients = {org.altbeacon.beacon.service.scanner.CycledLeScannerForLollipop $ 4 @ 5445b45 = android.bluetooth.le.BluetoothLeScanner......"
您无法使用 Android BLE API 读取 MAC 地址。如您所知,它被 MAC 映射到真实 OS 的假 MAC 所欺骗。所以这条线是不可靠的:
device.getAddress().compareTo(MAC) == 0
这可能是导致第二次连接失败的原因。它在首次启动时运行的事实令人惊讶,并且可能只是由于 Android 的 MAC 欺骗轮换算法的确定性实施。它在第二次连接时不起作用的事实
您需要找到一种不同的方式来找到要连接的正确设备,也许是通过信标标识符。
很难找到用于连接到信标的好的示例代码,因为那不是典型的用例。通常,它们在设计时被视为仅传输设备。这就是 Android Beacon Library 不提供连接到 BLE 服务的工具的原因——所有信标制造商都没有标准实施。也就是说,您的自定义方法是合理的。
了解 Android 信标库自己进行蓝牙扫描来检测信标。这就是为什么即使您停止扫描,您仍然会在日志中看到扫描结果。这是来自图书馆。
完全披露:我是 Android Beacon Library 开源项目的首席开发人员。
问题
我有一个外围设备正在发射具有特定 ID 的信标。我的智能手机(具有核心作用)正在搜索外围设备的信标。当它找到正确的信标时,它会通过 GATT 配置文件开始蓝牙通信。 第一次连接设备时,数据正确地从一个设备发送到另一个设备,但第二次(我有一个计时器每 20 秒尝试连接两个设备),连接失败我不知道为什么...我必须终止应用程序进程并重新打开它以使其再次运行。
我做了什么
对于信标,我使用的是 altbeacon,所以中央设备中的信标检测代码是这样的:
@Override
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.i(TAG, "Beacon detected!");
if (beacons.size() > 0 && Utils.getUserData(getApplicationContext(), "id").compareTo("-1") != 0) {
if(region.getId1().compareTo(Identifier.parse(identifier))==0) {
Beacon beacon = beacons.iterator().next();
Log.i(TAG, "The first beacon I see is about " + beacon.getDistance() + " meters away. "
+ region.getId1());
String mac = beacon.getBluetoothAddress();
Log.i("MAC", "MAC: "+mac);
CustomBluetoothManager customBluetoothManager = CustomBluetoothManager.getInstance(getApplicationContext());
if(!customBluetoothManager.isScanning()) {
customBluetoothManager.scanLeDevice(true, mac);
}
}
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", Identifier.parse(identifier),
null, null));
} catch (RemoteException e) { }
}
找到信标后,将调用 scanLeDevice:
public void scanLeDevice(final boolean enable, String mac) {
target_mac = mac;
mHandler = new Handler();
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
这是 mLeScanCallback 代码:
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
long epoch = System.currentTimeMillis() / 1000;
SharedPreferences prefs = context.getSharedPreferences(
"epoch", Context.MODE_PRIVATE);
long epochStored = prefs.getLong("epoch", 0);
if (device.getAddress().compareTo(MAC) == 0 && after20Seconds) {
prefs.edit().putLong("epoch", epoch).apply();
Log.d("epoch", Long.toString(epoch));
mDeviceAddress = device.getAddress();
final Intent gattServiceIntent = new Intent(context, BluetoothLeService.class);
context.bindService(gattServiceIntent, mServiceConnection, context.BIND_AUTO_CREATE);
}
}
};
我不得不对 MAC 地址进行硬编码,因为信标包含在数据包中的 MAC 地址与真实设备 MAC 不同(来自BLE 定义,但我不知道它到底是如何工作的。
最后,当数据发送后,我关闭并断开了蓝牙通道,我也尝试解除绑定服务,但没有任何效果:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
disconnect();
close();
unbindService(mServiceConnection);
}
因此,使用此代码,设备第一次连接时数据发送正确,但 20 秒后,在第二次尝试中,永远不会建立通信。
问题
- 知道问题出在哪里吗?
- BLE 是如何工作的?在信标数据包中,出于安全原因,MAC 是伪造的。但是,如果我从信标扫描应用程序中获取它并将其硬编码到该应用程序中,则通信有效...我认为它不应该有效。
- 如何从信标数据包中获取正确的设备MAC?
- 你知道任何使用蓝牙通信的信标的示例吗?
- 如果我通过"mBluetoothAdapter.stopLeScan(mLeScanCallback)"方法停止扫描,好像扫描并没有真正停止...这正常吗?日志不断显示 "BluetoothLeScanner: startRegisteration: mLeScanClients = {org.altbeacon.beacon.service.scanner.CycledLeScannerForLollipop $ 4 @ 5445b45 = android.bluetooth.le.BluetoothLeScanner......"
您无法使用 Android BLE API 读取 MAC 地址。如您所知,它被 MAC 映射到真实 OS 的假 MAC 所欺骗。所以这条线是不可靠的:
device.getAddress().compareTo(MAC) == 0
这可能是导致第二次连接失败的原因。它在首次启动时运行的事实令人惊讶,并且可能只是由于 Android 的 MAC 欺骗轮换算法的确定性实施。它在第二次连接时不起作用的事实
您需要找到一种不同的方式来找到要连接的正确设备,也许是通过信标标识符。
很难找到用于连接到信标的好的示例代码,因为那不是典型的用例。通常,它们在设计时被视为仅传输设备。这就是 Android Beacon Library 不提供连接到 BLE 服务的工具的原因——所有信标制造商都没有标准实施。也就是说,您的自定义方法是合理的。
了解 Android 信标库自己进行蓝牙扫描来检测信标。这就是为什么即使您停止扫描,您仍然会在日志中看到扫描结果。这是来自图书馆。
完全披露:我是 Android Beacon Library 开源项目的首席开发人员。