The android documentation states:
Note: You can only scan for Bluetooth LE devices or scan for Classic Bluetooth devices, as described in Bluetooth. You cannot scan for both Bluetooth LE and classic devices at the same time.
However I am noticing that calling mBtAdapter.startDiscovery(); is returning both classic and btle devices. Does anybody know what is correct here?
From my understanding what the documentation means is that you cannot have a startLeScan() and a startDiscovery() running at the same time. The reason might be that there is only one BluetoothAdapter object (the object that represents the local Bluetooth hardware) therefor it cannot do two different operations that use the BluetoothAdapter at the same time.(If someone knows anything different as to how it works in the background, let us know)
startLeScan() -> scans only for BLE devices
startDiscovery() -> discovers all Bluetooth devices, also it only scans for 12 seconds and this cannot be changed (have a read through the method description)
Note: After doing a startDiscovery() inquiry scan when a BT device is found you can get the device type to identify what each device is, For example:
int deviceType = device.getType();
if (deviceType == BluetoothDevice.DEVICE_TYPE_CLASSIC) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_LE) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_DUAL) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_UNKNOWN) {
}
device: BluetoothDevice
when (device.type) {
1 -> edt.text = "DEVICE_TYPE_CLASSIC"
2 -> edt.text = "DEVICE_TYPE_LE"
3 -> edt.text = "DEVICE_TYPE_DUAL"
else -> edt.text = "DEVICE_TYPE_UNKNOWN"
}
Related
I'm trying to receive data in mobile(android) devices from a BLE device based on nRF52840 and custom firmware.
Here is my setting
sampling rate : 250Hz
mobile devices : Galaxy flip z 3, Galaxy S22
Used library : RxBleAndroid
To check whether the BLE device sends data correctly or not, I tested it using nRF Connect for Desktop. There were about 250 samples per second. (nRF52840 dongle was equipped with the Desktop)
However, in mobile devices, there were about 20~30 samples per second. I already checked these mobile devices could receive 250 samples per second using a commercial device.
So I think it is not a problem of mobile devices, but code.
Here is my code
fun connectDevice(){
rxBleDevice = rxBleClient.getBleDevice(lxDeviceAddress)
connectSubscription = rxBleDevice.establishConnection(false)
.subscribe(
{ rxBleConnection ->
this.rxBleConnection = rxBleConnection
Log.v(TAG, "success to connect")
}
) { throwable ->
throwable.printStackTrace()
}
}
fun bleNotification() = rxBleConnection
.setupNotification(lxDeviceUUID)
?.doOnNext { notificationObservable->
}
fun readDataFromDevice(){
scanSubscription.dispose()
bleNotification()
?.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
?.flatMap { notificationObservable -> notificationObservable }
?.subscribe({ bytes ->
Log.v(TAG, byteArrayToHex(bytes))
}, { throwable ->
throwable.printStackTrace()
})
}
I called connectDevice() to connect the BLE device, and called readDataFromDevice() to read data.
Could you give me some solutions?
It all depends on the Bluetooth chip in the device (how good its radio scheduler is) and the circumstances such as connection interval, number of concurrent connections, if BLE antenna is shared with WiFi antenna and so on. So, not that much depends on your code. You just seem to set up a regular notification listener.
the BLE connection performance mainly depends on three connection parameters:
connection interval, latency, and timeout. These are negotiated between peripheral and central after connecting. The peripheral can request for parameters by giving their range, but it is the central that decides the final parameters in our case the mobile and PC applications.
So, there will be a change in the values of these parameters between mobile and pc apps, also between different OS's. Hence the change in throughput.
As #Emil covered it depends on hardware as well.
I am making an application that scans the nearby BLE devices in Xamarin, using the Bluetooth LE plugin library. I would like to know how I can find out what type of protocol the scanned device uses so that I can differentiate between iBeacon, Eddystone, etc.
I have seen applications that are capable of doing this like Beacon Simulator or BLE Scanner.
Beacon Simulator scan result
To scan devices I use this function, similar to the one explained in Bluetooth LE plugin for Xamarin documentation.
async void ScanDevices()
{
if (Ble.State == BluetoothState.On)
{
if (!Adapter.IsScanning)
{
deviceList.Clear();//clear list
Adapter.ScanTimeout = Constants.Scann_time_ms;
Adapter.ScanMode = ScanMode.Balanced;
Adapter.DeviceDiscovered += (s, a) =>
{
//scan and search for devices
if (!deviceList.Contains(a.Device))
{
deviceList.Add(a.Device);
}
};
id_devicesCollection.ItemsSource = deviceList;
await Adapter.StartScanningForDevicesAsync();
System.Diagnostics.Debug.WriteLine("[INFO] Scanned");
}
else
{
System.Diagnostics.Debug.WriteLine("[WARNING] device is scaning");
}
}
else
{
System.Diagnostics.Debug.WriteLine("[WARNING] BLE State is OFF ");
}
}
Could I get the information I'm looking for from <Plugin.BLE.Abstractions.AdvertisementRecord> by interpreting raw data?
I am developing a solution to check BLE devices and I used the native API that comes with Android to check BluetoothLeScanner.
Wanted to understand a little better operation, I take the location permission and bluetooth.
After the scan starts, I turn Bluetooth on my phone to off, on Moto G2 Android 6.0 Scan still keeps giving me the expected result when I test on a Samsung S8 Android 9 and Sony Xperia T2 Ultra Android 5.1 in the log I get which was bluetooth disabled and the scan was stopped.
I can only perform the test when I purchase it as follows
bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
bluetoothScanner = bluetoothAdapter.bluetoothLeScanner
}
#TargetApi(Build.VERSION_CODES.M)
class BleScanCallback(resultMap: MutableMap) : ScanCallback() {
var resultOfScan = resultMap
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
#TargetApi(Build.VERSION_CODES.M)
override fun onScanResult(callbackType: Int, result: ScanResult?) {
addScanResult(result)
Log.v("Main Activity", "I found a ble device ${result}")
Log.v("Main Activity", "I found a ble device ${result?.device?.address}")
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
results?.forEach { result -> addScanResult(result) }
}
override fun onScanFailed(errorCode: Int) {
Log.v("Main Activity","Bluetooth LE scan failed. Error code: $errorCode")
}
fun addScanResult(scanResult: ScanResult?) {
val bleDevice = scanResult?.device
val deviceAddress = bleDevice?.address
resultOfScan.put(deviceAddress, bleDevice)
}
scanResult is bringing the necessary information when bluetooth is online, I already set it up as the image below
https://i.stack.imgur.com/o9jGRm.png
I see that this makes scanning even off
On some Android devices including Pixel phones, Android One devices, and unmodified AOSP builds, turning off bluetooth in the quick settings panel doesn't really turn off bluetooth. Instead, it merely blocks bluetooth connections and pairing in software, yet allows Bluetooth LE scans to continue unaffected. As #Jorgesys correctly notes, it is impossible to detect BLE devices if the Bluetooth radio is really turned off, so let me say again: despite what the quick settings panel says, bluetooth is not necessarily powered off.
On supported devices, this happens only if two things are true:
Bluetooth is turned on in the full settings menu (On Android 9: Settings -> Connected Devices -> Connection preferences -> Bluetooth ON)
The user has selected to "Allow apps and services to scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services." (Settings -> Security & Location -> Location -> Advanced -> Scanning -> Bluetooth scanning ON)
There is no way to detect BLE devices with bluetooth off
Bluetooth must be enabled
Set up BLE
Before your application can communicate over BLE, you need
to verify that BLE is supported on the device, and if so, ensure that
it is enabled.
I am developing an Android app which searches for Classic and Low Energy Bluetooth devices such that when I press "search" button it will show me all Bluetooth devices (low energy and classic) in range. Since classic BT discovery and LE scanning are different things, I have to implement them separately and combine them in one function such that
searchFirstLowEnergyThenClassic() or searchFirstClassicThenLowEnergy()
In order to implement this, I have to know when the discovery/scanning ends so that I immediately start scan/discovery for other technology.
Here is my implementation:
Started Classic BT discovery
Received BluetoothAdapter.ACTION_DISCOVERY_FINISHED
Started BLE Scaning -> onReceive action equals(ACTION_DISCOVERY_FINISHED)
Stop search when BLE Scan ended
This looks ok but there is a problem when I extend the behavior. When I want search, I start searching first with LE scan or Classic discovery based on the last connected technology. For example if last time the device is connected to a Classic BT device, searchFirstClassicThenLowEnergy() is run. Otherwise, searchFirstLowEnergyThenClassic().
So as you might guess, it gets more complicated. For example, when the Classic BT discovery ends, the app should know whether the search ended or it should proceed with LE scan.
There is also this issue. When the user stops search during the scan/discovery of first technology, it will recieve BluetoothAdapter.ACTION_DISCOVERY_FINISHED but it shouldn't start LE scan since the search is terminated by user.
I implemented this using some flags (not working properly, though) but my code looks very dirty.
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// Classic Bluetooth discovery ended
lastOpenedType = getLastOpenedType();
if (lastOpenedType == BT_CLASSIC && !isSearchStoppedByUser()) {
// Search should continue with low energy scan
startBtLeScanning();
} else if (lastOpenedType != BT_CLASSIC && !isSearchStoppedByUser()){
// Search ended
searchProgressLayout.setVisibility(View.INVISIBLE);
} else {
// Search ended by user
searchProgressLayout.setVisibility(View.INVISIBLE);
}
}
In short, I am asking if someone has a more brilliant and simple solution on this?
PS. A solution without broadcast intent is much appreciated if possible.
BluetoothAdapter's startDiscovery() method searches for both classic and BLE devices. Once you get the result from the scan, you can separate them based on the type of the device. For example:
int deviceType = device.getType();
if(deviceType == BluetoothDevice.DEVICE_TYPE_CLASSIC)
{
}
else if(deviceType == BluetoothDevice.DEVICE_TYPE_LE)
{
}
else if(deviceType == BluetoothDevice.DEVICE_TYPE_DUAL)
{
}
So, it's not needed to search separately.
startLESCan is more advanced and better method of scanning bluetooth devices with some advancements like take less energy consumption.
While running on Nexus 7 with Android 4.4, the onScanResult throws a NullPointerExceptionas as seen in the log below:
03-18 17:59:34.170: D/BluetoothAdapter(5092): onScanResult() - Device=78:4B:08:02:7C:91 RSSI=-77
03-18 17:59:34.170: W/BluetoothAdapter(5092): Unhandled exception: java.lang.NullPointerException
However, other devices have no such problems.
I've found this on googlesource
public void onScanResult(String address, int rssi, byte[] advData) {
if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
if (mLeHandle <= 0) return;
}
try {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter == null) {
Log.d(TAG, "onScanResult, BluetoothAdapter null");
return;
}
mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
}
Which has a number of potential culprits, but of course I cannot set values for these variables.
Any why does this fail for Nexus 7 but not for other devices? Any ideas for workarounds?
Android 4.3, 4.4: BLE filtering in startLeScan(UUIDs, callback) doesn't work for 128-bit UUIDs
It works on Samsung s5, tested with Android 4.4.2 but for some reason, it fails on Nexus. Waiting for this fix from Google stack.
In case you want to search for specific address only, you could use this solution. Basically, you would have to use scanRec[], take an extra effort to parse it and then add device with matching address into a list adapter.
[I know, .... wish that simple API would work ! :P ]
Turning WIFI OFF:
I can confirm too, that turning WIFI OFF makes Bluetooth 4.0 more stable especially on Google Nexus (I have a Nexus 7).
The problem
is that the application I am developing needs both WIFI and continous Bluetooth LE scanning. So turning WIFI OFF was no option for me.
Moreover I have realised is that continous Bluetooth LE scanning can actually kill WIFI connection and make the WIFI adapter unable to re-connect to any WIFI network until BLE scan is ON. (I'm not sure about mobile networks and mobile internet).
This definitely happened on the following devices:
Nexus 7
Motorola Moto G
However BLE scanning with WIFI on seemed pretty stable on:
Samsung S4
HTC One
My workaround
I scan BLE for a short period of time 3-4 seconds then I turn scan OFF for 3-4 seconds. Then ON again.
Obviously I always turn BLE scan OFF when I'm connecting to a BLE device.
When I disconnect from a device I restart BLE (turn adapter OFF and then ON) to reset the stack before starting scan again.
I also reset BLE when discovering services or characteristics fails.
When I get advertisement data from a device that the app should connect to (lets say 500 times without being able to connect - thats about 5-10 seconds of advertising) I reset BLE again.