kevincar / bless

Cross-platform Bluetooth Low Energy Server Python Library
MIT License
86 stars 28 forks source link

Cannot get macOS to advertise reliably #47

Closed omizrahi99 closed 3 years ago

omizrahi99 commented 3 years ago

The problem After running examples/server.py on macOS version 11.0.1, I cannot get bless to advertise its service UUIDs reliably. Using a BLE scanner tool such as nRF connect for iOS, I am only able to see "Test Service" advertise its service UUID's maybe 5% of the time. The rest of the time, "Test Service" fails to show up with the service UUID that it is advertising.

Reproduction Run examples/server.py on macOS. Use a BLE scanner app such as nRF Connect to find the advertised service, but it won't show up.

Expected behavior We should see the "Test Service" and its service advertised services show up in a BLE scanner.

Desktop:

Additional context Running a simple peripheral using Objective-C with Xcode and advertising a service works every time (the advertisement always shows up when scanning), which makes me think this could be an issue with the interface between python and objective-c/xcode protocols? It's also odd that bless does work some of the times, but most of the time it doesn't, so maybe it could be encountering a race condition?

kevincar commented 3 years ago

Thanks for reporting this @omizrahi99. Though, I'm having difficulty reproducing the behavior (albeit I just tested it on OS 11.4). A couple follow up questions that may help us pin point the issue:

omizrahi99 commented 3 years ago
  1. Yes, using bless version 0.2.0 installed through pip
  2. Yes, the logs are definitely indicating that the peripheral is advertising the name and service
omizrahi99 commented 3 years ago

I just updated my OS to 11.4, and still encountering the same issue.

kevincar commented 3 years ago

@omizrahi99 Can you confirm that you can at least connect to your macOS machine, or does your computer not even show up during advertising?

kevincar commented 3 years ago

@omizrahi99 Any feedback? I hoping we can resolve this. Perhaps you could provide a screenshot of from the iOS app when using your Xcode version vs bless?

omizrahi99 commented 3 years ago

Hey, I apologize for the late reply (was on vacation).

Yes, I can connect to my mac and see all its services and characteristics.

On the left you can see a screenshot from the nRF Connect app while running a simple peripheral in Xcode on my mac. Here's the link to the source code I'm using.

As you can see, we can see the advertised service under "ORI", which is the name in the advertisement dict.

On the right you can see a screenshot from the nRF Connect app while running the bless example. Here, we can only see my mac and cannot see the name or the service in the advertisement dict.

omizrahi99 commented 3 years ago

@kevincar Any insight to why I am experiencing this behavior?

kevincar commented 3 years ago

@omizrahi99 Apologies for the delay. I had a short break myself.

The code for the ObjC-based peripheral helped reproduce this problem. Unfortunately I haven't yet pinned down the cause. You mention that you could, at times, observe the advertised service name, but I haven't yet been able to get even that. I'm going to work on stripping down some of the macOS code to tease the issue apart. I suspect, as you've suggested, that this may be related to the pyobcj interface and/or the way bless uses asynchio/dispatch queue.

I'll continue to post updates about this as I chip away at it.

omizrahi99 commented 3 years ago

For sure, sounds good.

I've found that in this file here if we place await self.peripheral_manager_delegate.startAdvertising_(advertisement_data) in a loop and call this line every .5 seconds or so, I am able to see the peripheral advertising on the app much more reliably. This doesn't seem like a correct fix for the issue, but maybe it could offer us some insight.

kevincar commented 3 years ago

Thanks for that tip @omizrahi99.

You're right, it's likely to do with the way bless is utilizing asyncio and allowing underlying dispatch queues / threads to allow the advertising data to be broadcast. Hence the loop will likely force the advertisement.

To begin teasing this apart I converted the project you linked earlier into python scripts that essentially do the same thing. This will hopefully be a starting point to start dissecting out where the block is occurring.

Interestingly, this really only affects the advertising data since all the other BLE functions on CoreBluetooth still work.

kevincar commented 3 years ago

@omizrahi99 can you checkout branch 47-macos-reliably-advertise and see if you're still seeing the same issue?

Make sure to shut down the nRF connect app to clear its cache too.

omizrahi99 commented 3 years ago

I am still seeing the same issue using the example on that branch.

kevincar commented 3 years ago

Can you confirm on your end if the issue persists when using the python scripts linked above?

omizrahi99 commented 3 years ago

Yes, it looks like the issue is gone with those python scripts! So I guess it seems like the advertising was being blocked by asyncio?

kevincar commented 3 years ago

That's my thought. However, it's not asyncio exactly. Wrapping the code up in an asyncio coroutine still allows the code to advertise the service name. I believe that because the peripheral manager is initialize on a separate thread or dispatch queue, we need to use threading to wait and ensure that the bluetooth module is turned on before calling any CoreBluetooth functions.

When you tested the branch, did you test it in a fresh environment? I.e., ensure that bless wasn't being loaded from a preinstalled version? I ask because the branch makes a change to ensure that the CoreBluetooth module is able to load before moving forward. It seems—on my end— that when this is the case, advertising the service name works.

I'll create a series of other scripts just in case to really isolate the issue if you are in fact unable to use the branch successfully.

omizrahi99 commented 3 years ago

I just tested out that branch using a fresh virtual environment and it was working with no issue! So you're right, it was probably being loaded from a previous version.

kevincar commented 3 years ago

Thanks for confirming. Closing this since I've merged the branch into the develop branch for the next release. Thanks for spotting this!