Difference BLE throughput between PC and mobile(android) - android

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.

Related

Unlock Linux with Bluetooth low energy

Have you ever seen the apple watch unlock a Mac? The idea is amazing, but I don't want a smart watch because I already have a phone which has similar capabilities AFAIK.
Also, I moved from OSX to Linux recently :)
I don't know how the Apple watch manages to unlock the Mac. But I know what would be desirable from a user experience point of view:
Needs to unlock quicker than I type my password
Should work in absence of wifi/4G
Should be power efficient
RFID
RFID would be nice, but no laptops that I know embed RFID readers.
Bluetooth based proximity detection
The challenge with classic Bluetooth is the requirement to constantly scan for near devices to measure the signal strenght (RSSI) from which we can infer the proximity.
Unfortunately discovery hops and listens 40 channels. And anyway the phones stop broadcasting when screen is off for a while. This is not good enough. I know because I tried:
import collection.JavaConverters._
import tinyb._
object Listener extends App {
var running = true
val BT_ADDR = sys.env.getOrElse("BT_ADDR", "XX:XX:XX:XX:XX:XX")
val BT_RSSI_DBM_THRESHOLD = Integer.parseInt(sys.env.getOrElse("BT_RSSI_DBM_THRESHOLD", "-65")).toShort
val manager = BluetoothManager.getBluetoothManager
val lock = new Object
while (true) {
manager.getAdapters.forEach(a => {
a.setRssiDiscoveryFilter(BT_RSSI_DBM_THRESHOLD)
a.removeDevices()
})
System.err.println("scanning for " + BT_ADDR + " at minimum " + BT_RSSI_DBM_THRESHOLD + " dBm RSSI...")
manager.startNearbyDiscovery(
(device: BluetoothDevice) => {
if (BT_ADDR.equals(device.getAddress)) {
onProximity(device)
manager.stopNearbyDiscovery()
lock.synchronized(lock.notify())
}
else println(device.getName)
}
, 1000
, false
)
lock.synchronized(lock.wait())
}
}
I was looking at BTLE (Bluetooth Low Energy), and I'm having difficulty to understand the following:
Is there a way to establish from Linux a single low energy bluetooth connection to the Android phone which we can leave dormant all the time, and use it to wake the phone up and make it transmit some packets (so we can measure its RSSI power and infer proximity) on demand, only when strictly needed.
I.e. we'd limit transmissions to only these rare occasions:
Check when the user is away if we detect inactive mouse & keyboard for 1 minute,
Check if the user is near enough when GDM is active
No BT activity whatsoever otherwise
This approach is quick, energy efficient, and does not require network protocol, only some rare BT transmission.
But is this possible with Bluetooth LE? Any pointers to examples?
Yes this should be possible with Bluetooth Low Energy (with some caveats) as follows:-
You need a BlueZ script/C program to constantly scan for your Android device.
You need your phone's Bluetooth to always be turned on.
You will need to pair at least once so that your Linux machine recognizes the changeable Bluetooth address of your Android device (see referenced links).
The BlueZ script program should be written so that as soon as your Linux system goes to standby, the program is launched as a daemon or background process that just starts scanning for Android devices and read their RSSI values. If your device is found and the RSSI value indicates that it is within range, this process will signal the Linux OS to wake up.
The caveats:-
BLE is not ideal for positioning/locationing; you can probably detect
if you're a few metres away but it would be challenging to get an
accuracy of a few centimeters.
Your BlueZ script needs to be
constantly running as a daemon or background process, so if it is
somehow killed or is inactive when the device goes to sleep, this
will not work.
Bluetooth on your phone should be always on, which
shouldn't have a big impact on the battery life but is also not
recommended.
Some resources for you:-
Running Bluetooth applications in the background in Linux
Bluetooth Low Energy: A Primer
Getting Started with Bluetooth Low Energy
Introduction to BLE
Bluetooth LE Signal Strength in Linux
Should One Create a Bond with a Bluetooth LE Device
How to Detect Whether System is Going to Standby in Linux
Android Bluetooth Low Energy Overview
Using Bluetooth Low Energy in Linux Command Line
It will not be a straight forward process and you'll probably have to try and fail along the way, but it will be a learning experience and you should be able to achieve what you want in the end.
I hope this helps.

identify BLE devices on Android / Kotlin with bluetooth off

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.

BLE Device dissonect after receive Notifications (BLEGattException Status = 0x8)

My BLE server permanently measures a sensor value and sends a notification with 20 byte user data after each measurement. The goal is to generate as much throughput as possible.
On the client side, the value sent by the server is received and processed.
rxBleConnection.setupNotification(setDescriptorEnableNotification(characteristic))
.flatMap(notificationObservable -> notificationObservable)
.observeOn(Schedulers.newThread())
.buffer(1)
.subscribe(bytes -> {
onNotificationReceived(bytes, buffer);
} , throwable -> {
// Handle an error here.
onNotificationSetupFailure(throwable);
}
);
If I set the Connection intervall to 11.25ms, I receive all values. However, if I set the connection interval to 30ms, I receive a few values ​​and then the connection is closed.
In the Android Log i see the followed message:
BleGattException status=8 (0x8),
bleGattOperationType=BleGattOperation{description='CONNECTION_STATE'
Why is the connection interrupted and what is the trigger?
With the help of a BLE Sniffer this is not recognizable. The set connection parameters are accepted and the transfer begins. Suddenly the transmission ends and the error message appears.
Update:
BLE Sniffer screenshot has been added:
30ms, this is connection interval you set in server or android?
Btw, on android you can set speed mode
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
mBluetoothGatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
}
Error 8 means the connection timed out. There is nothing wrong on the Android side. The problem is with the communication between the two Bluetooth controllers. If you have a sniffer then you should be able to see who is the one that fails to send packets.
Here is an Image from BLE Sniffer.
BLE Sniffer
I have the similar problem. On Android 7.0 there were two ways to keep connection:
1) If devices are bonded and there is a charachteristic reading callback with constant thread of packets. If there are no packets by some time, then connection fails.
2) If devides are not bonded, but I do TXCharacheristic.read every few seconds. If don't do that some time, then connection fails.
But now in Android 7.1.2 this way doesn't work.
May be the first way will work for you.
On your Android device you should make bonding, on your kit you should handle this bonding.
On Nexus 6P and Samsung S7 it doesn't work anymore, but I didn't try it on the other devices.
I suppose, you have min connection interval 7.5 on your BLE kit, but now it is deprecated on Android.
Try to set min connection interval to 11.25 on your BLE kit and set connection priority
gatt.requestConnectionPriority(CONNECTION_PRIORITY_HIGH);
where CONNECTION_PRIORITY_HIGH = 1
in onConnectionStateChange.
It had worked for me when I had changed min connection interval in my nordic from 7.5 to 11.25.

Android BLE reconnection very slow

Background:
I have a BLE peripheral with two modes: "Application" and "Bootloader". In both modes, the device advertises with the same MAC address.
To switch from one mode to the other, the BLE peripheral must reboot itself. In doing so, it has to disconnect any active BLE connection.
The BLE peripheral only stays in Bootloader mode for about 5 seconds. If nobody connects to it within that window, it switches to Application mode.
The Problem:
Android takes a very long time to reconnect to the BLE device, long enough that I'm missing the 5 second window. The raw code has a few layers down to the BluetoothGATT and BluetoothAdapter layers, but the sequence of calls boils down to:
BluetoothGattCharacteristic c = mCharacteristics.get(POWER_STATE_UUID);
c.setValue(SHUTDOWN_VALUE);
mBluetoothGatt.writeCharacteristic(c);
// Signalled by BluetoothGattCallback.onCharacteristicWrite
bleWriteCondition.await();
mBluetoothGatt.disconnect();
// Wait for the underlying layer to confirm we're disconnected
while( mConnectionState != BluetoothProfile.STATE_DISCONNECTED ) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
}
mBluetoothGatt.connect();
while (mConnectionState != BluetoothProfile.STATE_CONNECTED) {
// Signalled by BluetoothGattCallback.onConnectionStateChange
bleStateCondition.await();
if (bleStateCondition.stat != 0) {
break;
}
}
Am I going about this entirely the wrong way? I've tried calling close() on the BluetoothGatt instance, then generating a new one with BluetoothDevice.connectGatt, but I get the same extremely slow behavior.
I'm testing on a Samsung Galaxy S4, API level 21.
The problem here is that the gatt connect call issues a background connection request. It can take quite a long time for this call to result in a connection. A description of the two types of connection request is here : Direct vs Background connections
The absolute fastest way to get a connection is to do a scan and upon finding your device issue a direct connection request to it. As the scan has just found it, you know it is there and the connection will complete quickly. This is more complicated than your example code, but will be most effective given your small window. A scan is the most aggressive way to find a device. However, if you already have the device object, you could just call a direct connection request on the device.
Scans are issued using code like this :
scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
ScanFilter uuidFilter = new ScanFilter.Builder()
.setServiceUuid(YOUR_SERVICE_UUID).build();
filters.add(uuidFilter);
scanner.startScan(filters, settings, myScanCallback);
Upon finding your device (using the scan callback), issue a direct connection request via this method call :
myGatt = myDevice.connectGatt(this, false, myGattCallback);
The key part being the parameter of false. The connection request will time out in around 30s if the device is not found.

Android Bluetooth Low Energy Motorola API pairing

I am working on using the BT 4.0 API that Motorola has provided with the RAZR. In one of their documents it states to use the Android API to pair before connecting and using their framework. Per their instructions I have been pairing with OS Bluetooth settings application, but it never prompts me for a key. It will pair but doesn't appear to bond, and this is critical for me.
My question is, when they say "using the Android API" is this referring to simply using the OS Bluetooth utility to pair before hand (like I have been doing), or is there some way to do it with code in my application. They reference the "createBond()" function which, to my knowledge, is not an accessible function (at least not without some squirrely libraries or reflection).
Any advice is greatly appreciated, especially anyone who has used the API successfully, if they could give an account of their process. I'm just looking for some clarity at this point :)
Lloyd,
You are correct, follow the instructions in the link you posted.
Outside of coding, when they say use the standard android api for "non-le" operations, they mean go ahead and pair the ble device the same way you would any bluetooth classic devices inside android settings -> wireless & network -> bluetooth -> scan for devices.
If the device you are using is a motorola le compatible device the ble device will be paired but not connected.
Now, in the code, you can detect this paired device through the same method of
BluetoothAdapter.getDefaultAdapter().getBondedDevices()
To double check if your Android Phone is LE compatible, run this code:
public static boolean checkBLESupport() {
boolean deviceSupportsLE;
try {
#SuppressWarnings({ "unused", "rawtypes" })
Class object = Class.forName("android.server.BluetoothGattService");
deviceSupportsLE = true;
} catch (Exception e) {
deviceSupportsLE = false;
}
return deviceSupportsLE;
}
And to double check if the bluetooth device you paired is LE, when you are looping through the bonded devices.
Check the device with this code.
if (device.getBluetoothClass() == null) {
Log.i(TAG, "This device is BLE compatible");
b = true;
} else {
Log.i(TAG, "This device is not BLE");
b = false;
}
Now for establishing connection from your LE compatible phone to your LE compatible bluetooth device, follow the Gatt service instructions under the link you posted. http://developer.motorola.com/docs/bluetooth-low-energy-api/
Take note that under this example it is connecting to a bluetooth low energy heart rate monitor.
If you are not trying to connect to the heart rate monitor with LE heart rate profile, here is a link to another Motorola document that details creating your own LE Profile to use with the GATT framework. http://developer.motorola.com/docs/bluetooth-low-energy-gatt-framework-api/
If the instructions are not clear enough at any point in either of these documents, motorola offers sample android applications using the frameworks in those documents.
I guess motorola stack has BLE support. But what i feel is that it does not pair with the devices that require bonding though It does work some sensors. I have tried with a proximity sensor that require bonding. It never gets paired though the devices is discovered with Razr which even does not with S3.
There's a helpful video here.
Late to the game, but can confirm -
If your BLE Peripheral requires bonding, Moto X - and some other older Motorola devices - MUST be paired via Bluetooth Settings prior to programmatic connection via the Android GATT interface.
If you bond via the createBond method, or reading of an encrypted characteristic, your connection will be dropped typically in under 60 seconds, despite DDMS logs that show a good bond may be established.

Categories

Resources