I'm currently developing a react native app in combination with a device. The device and the app communicate via BLE. So far everything works as expected but I'm having issues with the connection stability of the iOS app and the device. What would happen is that the device would connect and I can update some characteristics but it would regularly either disconnect with a CBErrorDomain 7 or the response for a write would timeout. The implementation on the app or device side does not seem to be the problem as Android works stable and the device also disconnects when connecting with the LightBlue app.
I've already updated the BLE connection parameters as suggested here:
https://developer.apple.com/library/archive/qa/qa1931/_index.html.
This has increased the stability but did not resolve the problems completely. I've tried playing around with the values but so far no luck.
The current set of parameters we are using are:
conn_min_interval: 15
conn_max_interval: 15
conn_latency: 0
supervision_timeout: 2000
adv_min_interval: 1285
adv_max_interval: 1285
My question now would be if somebody has an idea what other things I could check or which parameter to tune?
Are you checking the maximumWriteValueLength and making sure your writes are smaller than this? A likely cause of your problems is overwhelming the device and it fails to keep up with sending ACKs. What version of Bluetooth does your device support, and does it implement DLE (Data Length Extension)?
Your conn_min_interval and conn_max_interval are suspicious. Asking for 15ms with no leeway is likely to negotiate to 30ms instead. (See 41.6 Connection Parameters) Is your device comfortable with being re-negotiated to something other than 15ms? Can your device actually keep up with 15ms and no connection latency if it does get that? I'm betting it can't. Try setting your connection interval to 30ms (or even a bit slower), and you might even try setting your connection latency to 1 to make the connection a bit more forgiving (though I'd focus more on slowing the CI than increasing latency; increasing latency would be more of a hack in this case).
All my suspicions are around your peripheral not keeping up with its side of the connection. If you have any synchronous activities in response to the data, you need to make sure that it's not blocking your BLE stack from sending the required responses.
Finally, I've found the answer to my problem. The problem was that the pairing procedure of the BLE server was faulty and thus iOS was unable to have a stable connection. Now that this is fixed the connection is very stable.
I'm still unsure why iOS was able to have any communication at all without the pairing but I hope that this helps some people in the future.
Related
I know the answer is nominally "no", but I mean really—what if the app goes into the background (with BTLE background processing enabled)? For 24 hours? Across an app update?
Under the heading "Reconnecting to Peripherals", this Apple documentation describes a reconnection workflow that first tries to reconnect to previously paired peripherals found via retrievePeripheralsWithIdentifiers: but then starts scanning again if you fail to connect. How do you know when to give up on connect-ing to a previously found peripheral if there is no formal timeout? How do you know when to start/keep scanning if the idea is to re-connect to a previously found BTLE device whenever you move back into proximity to it, without the user necessarily interacting with your app?
Also, a note further down that page says that some BTLE devices might invent a random identifier for themselves every time they're powered on, so even though you find some previously paired peripherals from retrievePeripheralsWithIdentifiers: you might not be able to connect to them as their names have changed. Do any BTLE devices do that in practice? That's nuts!
This is a tricky one to answer. The CoreBluetooth framework itself does not have an official timeout on connect requests. In fact it will try to connect the peripheral for as long as possible. But how long is that?
Well, unfortunately this is not something that is very well defined. You can be pretty confident that the connection will not time out while the app is in the foreground, but as soon as you involve connections in the background then things are not so funny any more. Obviously, like you mention, the pending connection will not remain after a phone reboot, etc.. which is fine since no user would expect the app to still be running after a reboot anyway. Regarding long running pending connections, you will find in Apple’s documentation that they tell you to opt-in for State Preservation and Restoration in order to make sure that the pending connections are properly kept while the app is suspended and eventually terminated. This would be good if it worked as advertised, but unfortunately it does not. After many years of working with this I have found that it is nearly impossible to get a reliable background pending connection on iOS. I have reported many bugs on this topic but so far none have been resolved.
There are a few issues in particular that I think you should pay extra attention to:
State Preservation and Restoration will completely stop working if a Bluetooth-state-change event happens while your app is in the terminated state. This essentially means that if the bluetooth chip gets reset for any reason (ex by toggling bluetooth/flight mode/etc..) then your app will never be relaunched again by Core Bluetooth whenever the peripheral is advertising within range. The reason for this is because all pending connections that have been set by your app will be cleared whenever the bluetooth chip is restarted. The problem with this is that your app will not be relaunched to be notified of this change, so the pending connections will never be recovered. So your app will think that the peripherals will connect, while in fact they will not. To me this one is the most serious issue and it alone makes CoreBluetooth extremely unreliable.
Sometimes the framework gets ”stuck” in a bad state (possibly by an internal race-condition or similar). This can happen randomly, but you can pretty easily reproduce this by calling connectPeripheral immediately in the didFailToConnect or didDisconnect callback. When this happen the ”connection state” property is set to “connecting” when a pending connection is in fact not set. To avoid this I have found that you should wait at least around 20ms before connecting, for example using a dispatch_after or something.
The framework internally uses XPC connections for interprocess communication in order to deliver bluetooth event. On some occasions this will break for whatever reason and the connection will be lost. I don’t know why this happens, but whenever it happens state preservation will stop working and you will manually have to relaunch the app to recover from it. Sometimes I manages to catch this in the device sysdiagnose logs...
Using an iPhone 7 and at the same time having an Apple Watch (paired the phone) will completely break all reconnects from behind the lock screen in case the Watch is not currently connected (out of range/flight mode/low battery/or any other reason). This is particularly bad since it was introduced recently! But it looks like the Apple Watch for some reason has "priority" over other bluetooth peripherals.
These are from the top of my head, but there are other issues as well. Regarding random addresses, most often these peripheral use so called ”random resolvable” addresses. This means that they appear random but in fact they can be resolved using an IRK (Identity Resolving Key) which is usually shared during initial bluetooth bonding. Devices that use completely random addresses are to my knowledge not very common.
I am using Multipeer Connectivity Framework in my APP and its working absolutely fine but when APP is invoking any API or downloading any file from server then its delaying the response. Sometimes its failed and sometimes I am getting Time out error.
I have also checked "Network" status on xCode debug navigator and found that speed is going up and down instantly and never going beyond 6kb/s and sometimes its showing as 0 kb/s but when I disable Multipeer Connectivity then it works fine and speed going at 70kb/s too.
I have also noticed that if bluetooth is ON then its happening not with the Wifi.
Any suggestion would be really appreciable.
This is happening because when advertising, Multipeer always advertises on the wifi (there's no way to say bluetooth only). The way it advertises is by switching the wifi access mode into adhoc multiple times per second to find other peers, then switching back to AP mode (ie. resuming its connection to your wifi router). Doing this is highly disruptive to large transfers.
You have two options to fix this:
1) as soon as you can, after getting connected, call stopAdvertising(). This will stop the wifi mode from being constantly changed, and your throughput will resume at its normal rate. Warning: you cannot micro-manage this, because it takes up to 30 seconds after calling stopAdvertising() until it takes effect
2) switch from Multipeer to an alternative framework that allows you to specify bluetooth-only, and only use bluetooth. I wrote one called BluePeer which I use in my apps. It is unicast (not multicast) and supports roles like Client/Server (as well as role-less like Multipeer)
Original question:
The iOS Developer Library mentions in the Game Kit Programming Guide that
The maximum size of a client-server game is 16 players.
Does this limitation also apply to P2P GKsessions?
This was already kind-of asked here (but from a different angle).
Updated question:Is GKSession reliable when more than 4 peers are connected?
note:The question was updated based on the response by saulobrito below.
Reference: One example of an app that uses a large group of peers connected is Seedio. You will notie that they are not using GKSession as bluetooth is not an option for connectivity (with GKSession, you can't even chose bluetooth or wi-fi.. GKSession does that for you.) Check out their FAQ for some interesting information about why they chose Wi-fi rather than bluetooth.
What I can assure you is that GKSession is very unstable and you should not trust these docs. In fact, Apple recently decided to remove the documentation altogether.
I did a lot of testing and I would suggest that the practical limit is 4 connected devices (one acting as a server and 3 clients). Of course it's better that you do your own testing scenario.
I also couldn't find any game that allows more than 4 players. The only that I knew was Apple own Texas Hold'em - which allowed 8 players, but they decided to remove it.
And last, but not least, Game Center imposes a 4 player limit to peer-to-peer games.
Yes I'm developing a game that should support 10 players but, in our tests, it became unstable/unusable when there were more than 4 devices. By unstable I mean: sometimes you can't find a peer and connection drops in less than a minute. To make matters worse, updating to iOS 6 brought some strange behaviors like freezing (no error, no stack trace, no nothing) while trying to send a message. Other odd thing: when a player loses connection, all other players get disconnected.
EDIT: did a lot of testing since that response and have more information to share:
Using iOS 6 I was able to play reliably with 9 devices using either wi-fi or bluetooth. There's stil a huge issue although: You can't connect devices using iOS 6 with devices using iOS 5 cause you will face unexplainable freezing-with-no-stack-trace time to time if any of the devices is with its wi-fi enabled. You either set iOS 6 as the minimum supported version for your App or you will have to ask users to disable their wi-fi and use bluetooth.
On iOS 6.0 I can confirm that GKSession allows at least 6 players. I have found that you must make sure not to send messages to a peer until the state changes to connected though.
Overall, I think GKSession is a wonderful and simple API. It will use bluetooth or WiFi, meaning that you don't even have to think about whether the phone is connected to a WiFi network. The latency is nice and low ( I measured it at 4ms for unreliable and around 10ms for reliable networking ).
My main caveats are:
Use unreliable messaging if you can.
Make sure you set .available to NO on application exit ( if you're terminated while advertising, you may leave phantom sessions open ).
Do not try and send messages to a peer until the peer in question changes to the connected state.
Don't try to re-use a gksession once you've been disconnected.
You may need to retry a connection attempt ( I automatically retry 3 times if a connection attempt fails ). My timeout is 10 seconds.
Throttle your reliable messaging. I haven't quite figured out what the limit is, but eventually it seems like you will blow a transmission buffer and then your latency will balloon. Again, this just isn't a problem when you use unreliable messaging.
I've encountered a pretty large issue and have been trying to find a solution for 2 months now with no luck. I've submitted it as a bug, ( https://bugzilla.xamarin.com/show_bug.cgi?id=4910 ) but was hoping maybe someone here could shed some light on the cause of the problem, or suggest a work-around.
In a nutshell, to encounter the error:
Create a basic .Net socket connection between two devices
Create and initialize a GameKit.GKSession object on a least one device.
What occurs is the transfer of the data on the .NET socket becomes erratic and too slow to be usable. I've performed many tests across different devices (see link below) and it affects all of them (iPad 3 affected the least). I've tested it between an iPhone and a Windows PC and it still occurs. MonoTouch's GameKit code is somehow affecting the Socket code.
As you can see from the spreadsheet, speed drop from a few milliseconds to send 1 MB to several minutes to forever.
As soon as the GameKit.GKSession is set to null, any backedlogged data on the socket flows freely again and the sockets act normally once more.
Sample Windows and iOS/MonoTouch Apps demonstrating problem: https://dl.dropbox.com/u/8617393/SocketBug/SocketBug.zip
Test results across different devices (PDF Spreadsheet):
https://dl.dropbox.com/u/8617393/SocketBug/SocketBugTestResults.pdf
This issue seems so hard even Apple decided to circumvent it: http://developer.apple.com/library/ios/#qa/qa1753/_index.html#//apple_ref/doc/uid/DTS40011315
The revealing sentence is this: "This change was made to reduce interference with Wi-Fi."
To assist with anyone that comes across this problem, the issue only manifests itself when the GKSession.Available is set to true. This is required for the device to be discoverable, but not to maintain a connection. With this in mind, a temporary work-around can be used where the GKSession.Available is only set to true when required, and set to false as soon as the connection has been made.
In my scenario, I have a socket connection to the server on both devices. I use this connection to send a message from device A to device B, asking it to become discoverable via bluetooth for the next 10 seconds. As soon as the message is sent, device A begins looking for device B and connects as soon as it finds it. Once the connection is established (or 10 seconds elapses without a connection), device B turns off discoverability and the socket behaves as normal.
In my circumstances, this is an acceptable (pending no real fix) workaround to the problem.
I have the following problem with GKSessions:
Sometimes there is a huge delay when sending data (3-4 seconds sometimes up to 10 seconds) in a client/server application.
If the server sends let's say 10 packets during the delay, those 10 packets are received all at once on the client. The weird thing is that the server still receives packets from the clients during the delay.
This delay seems to be worse in WiFi networks but also happens in bluetooth networks.
Did anyone else encounter such delay spikes when using gamekit?
What could be the source of this issue?
I've been working a lot with gamekit and gksession. I always use the Peer2Peer mode even though some people discourage it. I never experience any kind of delay like the one you describe.
Do you send with GKSendDataReliable or GKSendDataUnreliable? Try switching to the other and see if it changes anything. If you're using GKSendDataReliable then the sending device will wait for a "Received" message from the receiver before sending the next message. This might be the problem.
On your testing devices: Is anything running which might flood the network?
I was testing a multiplayer gamekit based game, and if I had several active devices then suddenly some would stall completely, and I needed to hard reboot (shut down completely and restart) them to make them work again.
If you keep getting in trouble you might want to try a much more low level api: dns-sd https://developer.apple.com/library/mac/#documentation/Networking/Conceptual/dns_discovery_api/Introduction.html
I hope you make it work, good luck!