Posts

Post marked as solved
6 Replies
It's not 100% clear from your description but it sounds like you're trying to take readings from a series of advertisements. You may be running into this restriction imposed by iOS: In particular, when your app is scanning for device while in the background: The CBCentralManagerScanOptionAllowDuplicatesKey scan option key is ignored, and multiple discoveries of an advertising peripheral are coalesced into a single discovery event. In my testing, actual behaviour seems to vary—sometimes I really do get just one event, sometimes I get an occasional trickle. This may be what you're experiencing with the difference between "backgrounded" and "backgrounded and locked". Either way the rule has been laid down: we can't rely on scanning more than one advertisement when the app is in the background. I am aware of no workarounds. The solution supported by iOS is to use that single advertisement to connect to the device and then subscribe to the relevant changing characteristic. If you have the relevant background mode configured, iOS is quite willing to allow this. This does require your peripheral to support a different mode of operation of course.
Post not yet marked as solved
3 Replies
One cause of this is that Linux tries to read the battery state from the iPhone, which triggers the pairing request. You can configure bluez to prevent this. One of my colleagues at Ditto wrote some docs to fix it, which the forum won't let me link directly, but I will copy-paste the main instructions here. To disable auto Battery reading Open the bluetooth service file /usr/lib/systemd/system/bluetooth.service, or /etc/systemd/system/bluetooth.target.wants/bluetooth.service in a text editor. You may need sudo permission to write to this file. Add -P battery to the end of the ExecStart line to disable the Battery feature in bluetoothd. Your ExecStart should look something like ExecStart=/usr/lib/bluetooth/bluetoothd -P battery now. Save the file. Run systemctl daemon-reload and systemctl restart bluetooth to apply the changes to the Bluetooth service
Post marked as solved
3 Replies
It's not so much a solution as a workaround, but I can confirm that using nw_listener_t to set up the equivalent peer-to-peer TCP listener works. It and nw_connection_t have a configurable dispatch queue instead of a runloop. Provided I stay away from dispatch_get_main_queue() everything looks like it's working fine in this environment. In this situation I can tolerate supporting only macOS 10.14+ so I'll get away with it.
Post marked as solved
1 Replies
Strictly speaking you could do it either way. The second option you mentioned—iOS central and Mac peripheral—is the most naturally aligned with Core Bluetooth. Imagine you have two or more iOS devices. All the Mac has to do is sit there and keep its characteristic up-to-date. iOS devices can come and go freely, and they will read and subscribe for as long as iOS allows them to do so. The first way you mentioned, the Mac has to keep track of all connected devices, write to them in turn, and handle errors if the writes did not succeed. Apart from that, being a peripheral means transmitting advertisements constantly. This uses more energy than passively receiving data. It would be kinder to your users' batteries to have the Mac perform the more energy-intensive role.
Post not yet marked as solved
2 Replies
It depends, but a flexible approach might look like this. I'll focus on your protocol here—the rest is in the Core Bluetooth docs. The peripheral publishes an app-specific service, most likely on a UUID that you generated randomly. Underneath that service it publishes a read/notify characteristic, which will provide an asynchronous response to requests (a different random UUID). The peripheral also publishes a writeable characteristic for requests. When the central connects, it discovers both characteristics, subscribes to the "result" characteristic, and writes to the "request" characteristic when it needs to. These writes and notifications have a small binary data payload of your choice. Typically iOS devices will let you do ~185 bytes at a time so if your requests and responses are short you can insert them directly. If they're bigger you'll have a bit more work to split them up. Possible simplifications if your app allows it: If requests can be completed instantly, the central could write a request and then immediately read a response from a second characteristic, without messing about with notifications. If you have a small fixed variety of requests, you can add a characteristic for each one. Then you know what type of request it is already without parsing the data. If your requests don't need any parameters or data payload, simply trying to read a specific characteristic can be treated as a request. If the peripheral can provide the response synchronously, it can calculate and return it immediately. This is a very simple workflow to implement.
Post not yet marked as solved
1 Replies
Yes, all of the Core Bluetooth functions (GATT and L2CAP channels) have compatible equivalents on Android and they can interoperate freely. Note that all of this functionality is Bluetooth LE, not Bluetooth "classic". Although Bluetooth LE is not optimised for sending large amounts of data like images you can certainly do so. Apple provides a sample project which demonstrates sending arbitrary quantities of data over GATT. A more efficient way is to establish the connection then have the central ("client") open an L2CAP channel. This gives you an NSStream interface which is somewhat easier to work with and provides much greater throughput.
Post not yet marked as solved
11 Replies
As suggested by Quinn, I have tried to use allowLocalEndpointReuse with my TCP server but I can't make it work for some reason. I'm using the following code to test it out: 				let parameters = NWParameters.tcp 				parameters.allowLocalEndpointReuse = true 				listener = try! NWListener(using: parameters, on: 15000) 				listener?.newConnectionHandler = { conn in 						// ... Run app Accept incoming TCP connection; all is well Stop and restart app Expected behaviour: It rebinds, like SO_REUSEADDR would allow. (I use BSD sockets elsewhere in the same app and they have no problem rebinding to the same ports.) Actual behaviour: Target[2669:2271422] [] nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error [48: Address already in use] Target[2669:2271422] [] nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed Target[2669:2271422] [] nw_listener_start_locked [L1] nw_path_create_evaluator_for_listener failed ...but if I wait a minute before restarting the app then everything is fine. Am I using allowLocalEndpointReuse wrong?
Post not yet marked as solved
23 Replies
I ran into this error too - in my case I was accidentally calling the method to get the InputStream and OutputStream from a listening NetService that I had published myself. Obviously this doesn't make sense - I was meant to implement the delegate method for when incoming connections are accepted. Once I fixed that faulty logic this error went away.It could be something else but I'll post this solution just in case. Unfortunately there are only a couple of pages found by Google that mention this error, and the stack trace doesn't clarify anything. 🙂