I am writing an Android application that uses bluetooth LE to connect to our device.
One of the notifications that will come in from our device is whether it is on a charger or not. This is a notification that fires once, and won't fire again until the status changes.
On iOS, this is all hunky-dory.
On Android, however, when connected to Android studio anyway, I will sometimes when running see a packet come into "onCharacteristicChanged" that is at time X, followed by the exact same packet at the exact same time to the exact same characteristic.
Sometimes when running, i'll only see one (expected). Sometimes 2 copies of it, and sometimes 4 copies of it.
It's completely insane and I have no idea why it is happening. I am not running my bluetooth stack as a service or anything.
Is this a "real" problem, or just a logging problem? I ask because there are other packets we get in other characteristics that have sequence numbers (haven't enabled this yet), and I'm concerned that these packets will fail because we'll get duplicate sequence numbers!
I think I found the problem. I wasn't checking the the status in the onConnectionStateChange callback. If I saw that the state was connected, i just blindly assigned the gatt and started the rest of my process to send commands to the device.
I think I also had an issue where i was always calling connectGatt instead of connect if the gatt already existed.
I have to say, while this is user error on my part, Android APIs are really idiotic in this regard. You don't have to worry about which "connect" to call and monitor every state change callback, worrying about the thread you are on and whether certain objects are already allocated.
Related
I am trying to send 10 bytes of data, every 30milisecond,s between 2 android devices using bluetooth low enegry notifications. The goal is to achieve low latency communication and there is no interest in low energy.
You can check the results in the Delay graph(Y field = delta time between packets). The first 10seconds the communication is being perfomed good enough. (The interval between the packets is max 30-40ms). However after the 10th second, the intervals are either extremly high or extremely low.
This is probably happenning due to non strict connection interval parameters. You can check a sample of hci_events that shows clearly the slow down is caused by the hci_events.
I have tried to configure the parameters of the connection, calling from the master the function
requestConnectionPriority
However, it didn't change anything on the result graph. I also check the packets using wireshark and findout that the master didn't even send a request for configuring the connection interval. I also tried another configuration request with
requestMtu
with exactly the same results, no update packets was send from the master.
So, there are 2 questions that show up from this observations.
Has ble a timeout in the 10th second in order to ensure low energy consumption? If so, how can I disable it?
Is some thing wrong calling requestConnectionPriority, from the android 6.0.1 with the role of the central(master)? Can you give me a simple example on how is being called properly?
Note that requestConnectionPriority is called right after the connection being establish in the onConnectionStateChange of the BluetoothGattCallback.
The device that has role of a slave(server) is running (Android 5.1.1) and the master(client) is running (Android 6.0.1).
Thanks a lot!
IMPORTANT UPDATE: It's crucial to mention the way the connection is being initiated between the devices.
The central device calls device.connectGatt, where device is BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address). Address is a hardcoded string, the peripheral's public mac address. You can check logcat logs and also hci logs.
I have also tried connecting with the legit method (peripheral advertising and connect to scanned random address), but I get an error 'initiate direct connection fail, topology limitation'. You can check logcat logs and also hci logs.
Has ble a timeout in the 10th second in order to ensure low energy consumption? If so, how can I disable it?
No
Is some thing wrong calling requestConnectionPriority, from the android 6.0.1 with the role of the central(master)? Can you give me a simple example on how is being called properly?
I'm not aware of any bugs regarding requestConnectionPriority. But you could try to execute that method after the onServicesDiscovered callback. Android temporarily changes connection interval to 7.5 ms during the GATT service discovery so it might be confused if you don't wait until the service discovery is completed.
Regarding requestMtu, make sure you execute that method while there are no other GATT operations pending (otherwise it won't work).
One additional tip is to use an air sniffer and not only look at the hci logs.
I'm working on an Android app that communicates with a custom bluetooth device. After calling BluetoothGatt.Disconnect() I am seeing that the OnConnectionStateChange callback is called, and the new state is Disconnected, however, there seems to be a lag between when that happens and when the Device itself is actually disconnected. For example, if I call BluetoothManager.GetConnectionState(...) with the device that was connected, it still returns Connected. Sometimes it takes several seconds before GetConnectionState returns Disconnected. Is this normal? Is it possible that I am doing something wrong in my application that could be causing this? e.g. disconnecting from a non-UI tread, or something like that? Or, is it possible that the physical bluetooth device itself is not handling the disconnect properly and maybe not completing the disconnect event promptly?
Android's BLE system is so messed up. I've seen what you've described, except much worse - where you disconnect from Android, but under the hood it maintains a persistent connection with your peripheral.
Usually takes 30 seconds to finally disconnect, sometimes takes minutes! All depending on which phone you were using at the time.
If you have the ability, I highly recommend adding a disconnection characteristic to the peripheral, so that you actually disconnect by writing a disconnection request, and letting the peripheral force the disconnection - and then Android will pick it up.
The benefit I've seen is that it ALWAYS works (because a 'hard' disconnection is always picked up by Android, whereas a 'soft' disconnection request can cause some issues on certain phones). Typically 'good' phones don't exhibit this behaviour (especially Marshmallow and on), but back in those KitKat days.... Wow....
Another benefit... If you're using iOS, you can scan for or re-connect to disconnected peripherals much faster.
When you call "disconnect()" you only disconnect your client object (BluetoothGatt object). You can have multiple BluetoothGatt objects connected to the same physical device. Multiple apps can also have own BluetoothGatt objects connected to the same device.
As soon as you call "disconnect()" the request is processed in the Bluetooth stack in the system and it immediately then calls the onConnectionStateChange callback in your app when it has completed processing the request. However, it will not disconnect the link until all other clients have disconnected. Newer versions of Android also delay the physical disconnection a few seconds (not sure why). Also, once the disconnect request has been sent to the Bluetooth controller it may take some time to actually disconnect since the remote device needs to acknowledge the disconnection (or time out). The default time out was 20 seconds until it was recently changed to 5 seconds in the latest Android version.
My goal is to get the Android device to reconnect to a BLE device that it has previously connected to without user intervention in the same way it does for a classic BT paired device does (even works through power cycles).
One of the ideas of BTLE devices is that one saves service, bonding, and enabling states such that a reconnect is VERY fast and consumes very little power on the peripheral.
What I have done seems to work but it works poorly.
The first step is to connect or pair and connect to a new device setting the 'autoconnect' parameter to 'true'.
When the device disconnects, do not call gatt.close(). Everywhere I look I see that one should call gatt.close(). But if I do call gatt.close() the Android central app never reconnects. I have tested this many times.
If I have not called gatt.close() and have not power cycled the Android, the auto-connection usually happens. Sometimes it can take a long time, especially after version 5.0. It is, however, unreliable and it may be unreliable due to a very low-duty scan cycle and the device quitting advertising before a scan cycle actually detects the advertisement. I am not sure because there is no way to detect the scanning operation like there is advertisements! It is also possible the scanning stops after a certain amount of time but there is no documentation on that.
So what I think I need to do is to somehow set the background scan rate used by the Android to a higher duty cycle (only possible in 5.0 and up) when auto-connect has been set but I do not know how to do this. I do not want to start my own scan but somehow set the background scanning rate used by Android for the reconnect. Does anyone know how to do this? Does anyone really know how autoconnect and gatt.close() are to work?
Maybe the auto-connect was NOT meant to re-connect as I indicated above?
Well after many trials and tribulations this is how I best get the Android to auto connect with the only user action being to first select the device (if using the settings menu then first pairing).
You have to trap the paired event in a BroadcastReceiver and do a BluetoothDevice.connectGatt() setting the autoconnect to true. Then when the device disconnects, invoke a gatt.connect().
Update: While the above approach works in general, it is sometimes agonizingly slow probably because the pending connection uses extremely conservative scan rates. The other downside is that for each device you want to auto-reconnect to you have to keep a BluetoothGatt object performing a pending connection. In the embedded world this is insane. Instead what one does is continuously scan and connect to a desired device by inspecting its advertisement. One saves only the minimal amount of data about the device (the services, its paired state and keys, etc.). When an advertisement is captured you see if it is one of your known devices and connect to if it is.
I tried the equivalent on Android. Scan all the time (low power rate) and connect to advertisements of interest, and maintain a class representing a known device. There are some annoying details in this approach (like turning off scanning while connecting and restarting after connected) but it basically works without the overhead of maintaining connections. BUT there is one exception I do not understand. One pre-paired device's advertisements are never seen by the scanner. However, if I invoke a pending connection to this device, I re-connect. I do not understand this at all. On my embedded platforms it works as it should.
If anyone else has tried this approach for auto-reconnecting, please share your experiences!
I have discovered the reason the pre-paired device is not seen by Android. Android only reports scan results IF the device responds to a scan request. Once paired, this device only emits advertisements and ignores scan requests, so the Android system does not pass up its advertisements in the ScanCallback. Thus in order to work using the scan approach, I have to use the pending connect approach for those specific devices. It just seems like you can't win!
============= UPDATE 2020
Many years have passed and I have a lot more experience with the background scan approach. If one keeps the supported platforms 5 and up, one can use only the newest scanner APIs and use filters, eliminating the need to decode the raw advertisements yourself. I have also found that connection and re-connection is snappier if you DONT turn off scanning while connecting. I know it goes against all documentation, but it works and on some platforms allowed connections to happen that otherwise did not. Also, to date, I have found only one (health) device that needs pending connects. Disclaimer: All I have ever worked with is health devices.
This is how I was able to do it for my application.
I first stored the address of the device in a SharedPreference then in gattClientCallback funtion of my BluetoothLeService
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_DISCONNECTED;
DeviceActivity.runOnUI(() -> {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String name_dev_1 = sharedPreferences.getString("Dev_1", null) ;
connectToDevice(name_dev_1);
disconnectGattServer();
});
}
}
What this does is if your device is disconnected it will keep on trying to connect to it until a connection is established.
I have developed an Android app that connects to a CC2540 BLE peripheral.
When I do a Characteristic write of type no response (WRITE_TYPE_NO_RESPONSE), I still get the callback onCharacteristicWrite at the app level. Is this behavior correct?
I understand there is probably a low level acknowledgement that occurs between the Android device and the peripheral.
But the reason I am asking is because this is causing an issue where I can only send a write once I have received this callback, which is slowing things down in the app.
Any light on this behavior would be appreciated it.
Thanks,
I did run into the same problem when I was trying to do some performance testing and found that when I used the WRITE_TYPE_DEFAULT specifically I stopped getting a response. There might be a bug with the android constants that is causing inverse behavior, but I am not quite not sure.
you can only make 1 transfer at a time on the low level so you need a callback to tell when the stack is ready to send another command.
If you try to send several after each other without waiting for the air-interface to be ready you can seriously crash the BLE stack! This was happening a lot on earlier iOS CoreBluetooth.
If one app crashes the BLE Stack then the phone need a reset or Bluetooth must be turned off and on again to reset the stack.
The callback just tells you that the stack has send a request over the air-interface, not that it was acknowledged by the recipient. For that you would use the other api which will make the BLE Stack re-transmit several times (depending on how the connection parameters has been negotiated).
It's stated clearly in the BLE specification that only 1 transfer can be made at a time.
If you just bang the API with write requ
I am working on a fitness app that is to supports two BLE sensors (heart rate, stride). I ended up with two bounded services (one for each sensor) called and bounded by my main activity. This is running on Nexus 7 II, with KitKat. I have spent days trying to get the app to stable state. The Gatt connections, and notifications work perfectly right after a clean reboot. The problem I am having is getting things back to a working state after one or both of the devices get disconnected and needs to be reconnected. Since similar issues have been reported by others, I have developed and tested various scenarios which included keeping the service and the connections going when the activity is restarted, closing the connections and the server and then starting fresh, closing only and not disconnecting, waiting before reconnecting, etc. Currently, with one sensor, I can get the app to reconnect most of the time. With two sensors, the connections do not get re-established most of the time. It is such a shame that android BLE API is so fragile considering that there are many new BLE devices every week. I am wondering if anyone else has had much luck with multiple BLE sensors and if they would share their approach to get it working.
This isn't a very question-y question but your comment indicates knowing what 0x85 means would help. The Android BLE API was taken from Samsung's second BLE API, but not all of the constants Samsung had defined made it over.
0x85 = 133 (which you also see in the logs) is GATT_ERROR. Basically this means that something went wrong, it could be the peripheral went out of range of the device, or the Bluetooth chip just messed up. I've found calling connect() on the BluetoothGatt in onConnectionStateChange is a decent solution since that will wait for things to get sorted out and connect when it can.
0x8D = 141 GATT_ALREADY_OPEN This one is pretty self-explanatory.
The other thing to watch out for is making sure anything happening to Bluetooth happens sequentially. Multiple threads sending commands to a BluetoothGatt before the result of the previous action happens tends to not be a good thing.