randdusing / cordova-plugin-bluetoothle

Bluetooth Low Energy Phonegap Plugin
804 stars 353 forks source link

AutoConnect #333

Open alexhisen opened 8 years ago

alexhisen commented 8 years ago

Looking at #214 and the docs, it seems that the way connect() is expected to operate is it asks the OS to connect to the device and it keeps looking for it indefinitely until it comes in range and then connects and invokes the callback.

However, in practice, at least on a Samsung Galaxy S7 (Android 6.0.1, Cordova 5.1.1), connect() times out within about 5 seconds and calls the error callback and after that nothing happens when device comes back in range.

Sure, we can keep calling connect() every time it times out but that doesn't seem very optimal. It seems like the autoConnect=true parameter in the native connectGatt method is exactly what we need to auto-reconnect to a device when it's back in range.

Rand, do you see any issues with changing the plugin's reconnect() method to set autoConnect=true (or for connect() to take an autoConnect:true option)? That way on first disconnect callback, we just call reconnect() or connect({autoConnect:true}).

The autoConnect parameter determines whether to actively connect to the remote device, or rather passively scan and finalize the connection when the remote device is in range/available. Generally, the first ever connection to a device should be direct (autoConnect set to false) and subsequent connections to known devices should be invoked with the autoConnect parameter set to true.

randdusing commented 8 years ago

I'm all for adding an optional parameter that will enable autoConnect. It's pretty straight forward to add, but I probably won't be able to get to it until sometime next week since I'm busy this weekend. PR's are welcome!

alexhisen commented 8 years ago

Rand, If you can add it next week, it would be greatly appreciated. I doubt I can do it, esp on the iOS side.

randdusing commented 8 years ago

iOS doesn't have that option, so we don't have to worry about iOS.

alexhisen commented 8 years ago

So, iOS behaves the way the docs describe, right? i.e. connect() never times out and will finally call your callback when device is back in range and re-connected.

Would I be correct in assuming that every time BLE is disabled in the OS, pending connects are cancelled and when BLE is re-enabled, we'd want to call connect() again on both iOS and Android?

Also, on Android, looking at the docs for BluetoothGatt.connect() (https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#connect()) which is what reconnect() calls, it apparently never times out, so we could call it upon disconnect. However, this would not survive ble/app/phone restarts, so we'd still need a way to call connect() with autoReconnect=true. Once that facility is in place, is there any reason to ever use reconnect()?

randdusing commented 8 years ago

The plugin itself will close out all connections whenever Bluetooth is disabled. I haven't actually verified, but I assumed the connections are closed out at a lower level automatically as well.

Yah, once autoReconnect is added to the main connect call, reconnect is probably unnecessary. I never use reconnect either way.

alexhisen commented 8 years ago

Hey, Rand. Do you think you'll be able to add the autoConnect param this week? Thanks.

randdusing commented 8 years ago

Yep, I'll be spending a few hours later today or tomorrow.

randdusing commented 8 years ago

Sorry for the delay. Girlfriend came into town last weekend, and mostly limited to working on the weekends. I just committed this change to master. I want to address a couple other issues before creating an official release, but you install from master in the mean time. Please re-open if it still doesn't work!

alexhisen commented 8 years ago

Thanks!

alexhisen commented 7 years ago

Hi, Rand. After implementing the autoConnect=true using 4.3 version of the plugin I am seeing the following behavior on Android: BLE device connection is lost - this triggers the connect() callback with status=disconnected as expected. Checking isConnected confirms as disconnected. Android re-connects the device automatically (takes about a minute - I know it reconnects because I have a USB connection to device and it tells me it's connected) - but no callback is triggered! Checking isConnected tells me it's connected now. But I am unable to write to characteristics If I now disconnect again, no callback is triggered but checking isConnected tells me it's disconnected now.

So, it sounds like the connection is re-established at the OS level but the plugin isn't really 'in touch' with that connection any more.

I am not able to call reconnect() - says it's not disconnected.

If I close and connect (with autoConnect:true), it'll eventually truly re-connect, so this is a potential work around but it requires at the least the callback to get triggered with some new status that tells me I should call connect now since the OS has re-detected it.

randdusing commented 7 years ago

So after you receive the disconnected event, it's automatically reconnecting after a minute or so? Does this happen with and without the autoConnect flag? The Android documentation never mentions auto-reconnecting... And is this only happening on 4.3? I don't have any devices where I can easily see if it's actually connected without relying on the app itself. Although I guess I can start scanning and see if it's still scanning.

alexhisen commented 7 years ago

The autoConnect=true is working the way I would expect at the OS level. Android re-connects to my device when it's back in range (though it takes it about a minute to detect that but that's ok). Without autoConnect=true, it stays disconnected as expected. The issue is that the plugin isn't fully re-reconnect to the device when it's re-connected at the OS level.

alexhisen commented 7 years ago

So, I may have not been using the API correctly. I have now changed our code to where we first try to connect without autoConnect:true and when we get disconnected, we then use close() and connect(autoConnect:true) to basically request the OS to reconnect when it can. This appears to work correctly for the most part.

The issue that I am seeing now is that if I turn off my device (as opposed to move it out of range), it doesn't always detect that it got disconnected. This is sporadic, maybe 10% of the time I get no disconnected callback and status remains connected if I check. It's only when the device turns back on, the disconnect callback occurs at which point we try to re-connect and re-connect.

Not sure if that's an issue the plugin can really detect ...

randdusing commented 7 years ago

Yah, I'm not sure if I can really detect the disconnect not being triggered. Android can take a long time to detect a disconnected device.

alexhisen commented 7 years ago

As a followup, when our device is turned off, not only does Android not think we disconnected but if we do a disconnect()/close() and then try to connect to it, Android will 'pretend' like it can see it and connect to is and discover services and everything. So, there is some kind of BLE cache in Android that needs to be purged.

alexhisen commented 7 years ago

And in fact, the plugin behaves the same way on iOS! Perhaps there is an issue in the plugin itself?

randdusing commented 7 years ago

So Android and iOS both connect when the device is turned off??

alexhisen commented 7 years ago

That's correct. Both connect, discover and subscribe but then obviously can't really communicate.

randdusing commented 7 years ago

That's really strange. I've never noticed that before with a few brands of heart rate monitors and a few other proprietary devices. Do you have any other devices to test?

alexhisen commented 7 years ago

Only our own and they all do this. :-(

randdusing commented 7 years ago

Perhaps some strange settings with your devices connection settings. Next time I take out my devices, I'll see if I can recreate.