Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ManufacturerData in Peripheral mode #323

Closed
voicubabiciu opened this issue Feb 13, 2021 · 7 comments
Closed

ManufacturerData in Peripheral mode #323

voicubabiciu opened this issue Feb 13, 2021 · 7 comments

Comments

@voicubabiciu
Copy link

Hi, I'm trying to identify my ble devices using the manufacturer data present in advertisement packet, but that's not supported yet. If anyone knows a workaround or when this feature will be supported would be awesome. I need this for my bachelor's degree project.
Thanks in advance!

@ukBaz
Copy link
Owner

ukBaz commented Feb 14, 2021

There are no immediate plans to add manufacturer data to the advertisements.

Manufacturer data has always been a low priority because they require company identifiers and my opinion has been that if people have the money and knowledgeable to be getting a Company Identifier Code, then they are likely not be using Bluezero. My concern is that if I add support for manufacturer data then people who don't have them will be tempted to use values that they haven't registered.

If people are creating beacons then I steer them towards using Eddystone beacons which uses service data. If they are creating a peripheral then I encourage them to use a combination of device name and advertised service UUIDs to identify their device. Of course a peripheral can also use service data if they have a custom service UUID.

Technically, adding support for manufacturer data is not particularly challenging. It will look very similar to the getter/setter above used for service data using the property documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/advertising-api.txt#n48

@property
def service_UUIDs(self): # pylint: disable=invalid-name
"""List of UUIDs that represent available services."""
return self.Get(constants.LE_ADVERTISEMENT_IFACE,
'ServiceUUIDs')
@service_UUIDs.setter
def service_UUIDs(self, UUID): # pylint: disable=invalid-name
self.Set(constants.LE_ADVERTISEMENT_IFACE,
'ServiceUUIDs',
UUID)
def manufacturer_data(self):
"""Manufacturer Data to be broadcast (Currently not supported)"""
pass

Then in peripheral, add the property to the advert in the same way that the local_name and appearance properties are added

def _create_advertisement(self):
self.advert.service_UUIDs = self.primary_services
if self.local_name:
self.advert.local_name = self.local_name
if self.appearance:
self.advert.appearance = self.appearance

@voicubabiciu
Copy link
Author

Hi, thank you for your response. I just modified the advertisement.py and peripheral.py and now my codo looks like this:

In the advertisement.py file:

@property
   def manufacturer_data(self):
      """Manufacturer Data to be broadcast (Currently not supported)"""
      return self.Get(constants.LE_ADVERTISEMENT_IFACE,
                     'ManufacturerData')

   @manufacturer_data.setter
   def manufacturer_data(self, data):  # pylint: disable=invalid-name
       for manuf_code in data:
           self.Set(constants.LE_ADVERTISEMENT_IFACE,
                    'ManufacturerData',
                    {manuf_code: dbus.Array(data[manuf_code], signature='y')})

in the peripheral.py file:

    def _create_advertisement(self):
        self.advert.service_UUIDs = self.primary_services
        if self.local_name:
            self.advert.local_name = self.local_name
        if self.appearance:
            self.advert.appearance = self.appearance
        self.advert.manufacturer_data = {0xFFFF: [0x00, 0x01, 0x02, 0x03]}

Now i getting the following error when it's trying to register the advertisement:
Failed to register advertisement: org.bluez.Error.Failed: Failed to parse advertisement.

NOTE:
I also tried the code from the service_UUIDs setter and getter I had the same result

@ukBaz
Copy link
Owner

ukBaz commented Feb 14, 2021

What you did seemed correct to me so I made the same changes as you in advertisement.py. I didn't make a change in peripheral.py as I accessed the advert directly from example/cpu_temperature.py with:

cpu_monitor.advert.manufacturer_data = {0xffff: [0x00, 0x01]}

And it worked correctly. If I then extended the data to be the same length of data as you had then I could reproduce the error:

Failed to register advertisement: org.bluez.Error.Failed: Failed to parse advertisement.

I used sudo btmon to get more visibility:

= bluetoothd: Advertising data too long or couldn't be generat..   13224.695468

Although the same information is available with:

$ service bluetooth status
[snip]
Feb 14 14:23:09 raspberrypi bluetoothd[13258]: Advertising data too long or couldn't be generated.

Seems like you are bumping up against the maximum amount of data you can have in an advertisement...

@voicubabiciu
Copy link
Author

I tried to change the manufacturer data and I got the same result.
I tried {0xffff: [0x00, 0x01]}, {0xffff: [0x00]} and {0xffff: []}.
I created the Peripheral with the following parameters:

peripheral.Peripheral(list(adapter.Adapter.available())[0].address,
                 local_name='H',
                 appearance=128)

Then I added two services, one with eight characteristics and one with two

@voicubabiciu
Copy link
Author

Okay so I check may services UUID's:

DEVICE_INFO_SERVICE = '180A'
CONFIG_SERVICE = 'be468f4a-6cbd-11eb-9439-0242ac130002'

After I remove the seccond one I could run the script and create the advertisement packet. May you explain me why?

@ukBaz
Copy link
Owner

ukBaz commented Feb 14, 2021

For each advertisement you only get a few (~28) bytes of data. As you will have seen from _create_advertisement, lots of data is being stuffed in there. Using custom UUIDs at 16 bytes take a lot of space especially compared to officially adopted UUIDs that only take 2 bytes.

There are various trade-offs you can make. I believe that BlueZ allows you to have up to 4 advertisements interleaved so you might be able to split your information over multiple adverts. You possibly don't need to advertise all of the services you device has to offer. This can be done by not marking them as primary in the Bluezero add_service API. Do you need to send appearance information?

Remember, an advertisement is about sharing enough information so that a client can identify the correct peripheral.

@voicubabiciu
Copy link
Author

Ok, in this case I think I will keep only the DEVICE_INFO_SERVICE local_name and manufacturer_data it's enough to identify my device. I don't think I need appearance because I am trying to create a smarthome hub and I am using BLE only for configuration. Thank you very much. I think we can close this issue now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants