Reputation: 191
I am working on an Android application that should automatically connect to the BLE device with a specified MAC-address. Basically a scan should be running 24/7, and as soon as the device is discovered, the service should connect to it, and ideally stop scanning.
What I managed to do is the following:
public void startBluetoothScan() {
if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (!scanning) {
service.startScan();
}
}
}
with this being the overall method:
private void configBluetooth() {
BluetoothConfiguration config = new BluetoothConfiguration();
config.context = getApplicationContext();
config.bluetoothServiceClass = BluetoothLeService.class; // BluetoothClassicService.class or BluetoothLeService.class
config.bufferSize = 1024;
config.characterDelimiter = '\n';
config.deviceName = "Your App Name";
config.callListenersInMainThread = true;
// Bluetooth Classic
config.uuid = null; // Set null to find all devices on scan.
// Bluetooth LE
config.uuidService = UUID_HRS;
config.uuidCharacteristic = UUID_HRD;
config.transport = BluetoothDevice.TRANSPORT_LE; // Only for dual-mode devices
BluetoothLeService.init(config);
service = (BluetoothLeService) BluetoothLeService.getDefaultInstance();
service.setOnScanCallback(new BluetoothLeService.OnBluetoothScanCallback() {
@Override
public void onDeviceDiscovered(BluetoothDevice device, int rssi) {
Log.d("Found device address", device.getAddress());
if (device.getAddress().equals(UserHRMAddress)){
if (!deviceList.contains(device)) {
Log.d("Connecting to", device.getAddress());
connectDevice(device);
deviceList.add(device);
}
}
}
@Override
public void onStartScan() {
Log.d("Started Scan", "now");
scanning = true;
}
@Override
public void onStopScan() {
Log.d("Stopped Scan", "now");
scanning = false;
startBluetoothScan();
}
});
service.startScan();
}
The method above is the usage of this library that should make BLE easier (I tried it raw and liked it equally little) Basically the scan returns the same device about a thousand times in a few seconds, without having the chance to connect to it first. Is there a possibility to let every device only be discovered once while scanning? Is there a more appropriate way to implement a 24/7 search with automated connecting?
What I also found out is, that the BluetoothGattCallback onConnectionStateChange method does e.g. not get called when you simply turn the BLE device off, or if you enter flight mode. If it did that, it would make my life a lot easier.
The relevant parts of my BluetoothGattCallback method look like this:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.d("onConnectionStateChange", gatt.getDevice().getName());
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
//mBluetoothAdapter.startLeScan(mLeScanCallback);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("Status", "success");
heartRateService = gatt.getService(UUID_HRS);
batteryLevelService = gatt.getService(Battery_Service_UUID);
if (batteryLevelService != null) {
batteryLevelCharacteristic =
batteryLevelService.getCharacteristic(Battery_Level_UUID);
}
if (heartRateService != null) {
heartRateCharacteristic = heartRateService.getCharacteristic(UUID_HRD);
boolean res = gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER);
gatt.setCharacteristicNotification(heartRateCharacteristic, true);
try {
BluetoothGattDescriptor descriptor = heartRateCharacteristic.getDescriptor(
UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
} catch (Exception ex) {
Log.e(TAG, "wuuuuut?");
}
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
Upvotes: 2
Views: 2755
Reputation: 18497
Just use connectGatt with autoConnect = true. That works on all Android versions and there are no restriction on usage compared to scanning, as introduced in Nougat. Doing that will tell the Bluetooth controller to connect to the device as soon as it detects an advertisement. Note that since the android api unfortunately lacks "address type" parameter when connecting to a specific BD address, you either need to be bonded with the device OR it must have been discovered by some Bluetooth scan since the Bluetooth was last time turned on.
Unfortunately you still need a Bluetooth state change broadcast receiver to restart everything when Bluetooth is turned on.
Upvotes: 4