I'm working on BLE advertisement. I'd like to know if it's possible to have 2 advertisements in BLE. I need to have both service data and manufacturer data. I'm using Python. The code is based on https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/example-advertisement.
I need to support EddyStone Beacon and some manufacturer data. But I don't know how to implement it.
Thanks.
The key item when wanting to do multiple advertisements, is that each advertisement must be created with its own unique D-Bus object path that it is published on.
In the BlueZ example they do this by having a PATH_BASE
and appending the index
value to it to make it unique:
class Advertisement(dbus.service.Object):PATH_BASE = '/org/bluez/example/advertisement'def __init__(self, bus, index, advertising_type):self.path = self.PATH_BASE + str(index)self.bus = busself.ad_type = advertising_typeself.service_uuids = Noneself.manufacturer_data = Noneself.solicit_uuids = Noneself.service_data = Noneself.local_name = Noneself.include_tx_power = Falseself.data = Nonedbus.service.Object.__init__(self, bus, self.path)
They then use this unique path when calling RegisterAdvertisement
:
ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},reply_handler=register_ad_cb,error_handler=register_ad_error_cb)
To make something that ran, I modified the BlueZ example. These modifications focused on getting something to run with minimal changes rather than this is how I would do it in production.
First, I changed the TestAdvertisement
to do a different advertisement depending if it was called with index 0
or index 1
:
class TestAdvertisement(Advertisement):def __init__(self, bus, index):Advertisement.__init__(self, bus, index, 'broadcast')self.add_service_uuid('FEAA')frame_type = [0x10] # Frame Type = 0x10power = [0x00] # Power = 0x00if index == 0:prefix = [0x02] # URL scheme = 0x02 (http://)url = [0x73, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x07]elif index == 1:prefix = [0x01] # URL scheme = https://www.url = [0x62, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x00]eddystone_data = frame_type + power + prefix + urlself.add_service_data('FEAA', eddystone_data)
I then modified where TestAdvertisement
was called so it was called twice, once with index=0
and once with index=1
:
ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),LE_ADVERTISING_MANAGER_IFACE)mainloop = GLib.MainLoop()test_advertisement= []for ad_id in range(2):test_advertisement.append(TestAdvertisement(bus=bus, index=ad_id))print(f'{ad_id}: Registering advert path: {test_advertisement[ad_id].get_path()}')ad_manager.RegisterAdvertisement(test_advertisement[ad_id].get_path(), {},reply_handler=register_ad_cb,error_handler=register_ad_error_cb)if timeout > 0:threading.Thread(target=shutdown, args=(timeout,)).start()else:print('Advertising forever...')try:mainloop.run() # blocks until mainloop.quit() is calledexcept KeyboardInterrupt:print('Cleaning up advertisements')for this_ad in test_advertisement:ad_manager.UnregisterAdvertisement(this_ad)print('Advertisement unregistered')dbus.service.Object.remove_from_connection(this_ad)
I also modified the code to unregister both advertisements to clean up at the end.
The example should show two Eddystone URL beacons with different URL's.