Dbus
bluetooth
BLE
Bluez

BluezをD-busから使う LE advertise

More than 2 years have passed since last update.

BlueZをD-Busから使う方法について

LEのAdvertiseする方法を調べました。

Bluezのバージョンについて

  • D-Bus から LE advertisetをするには5.30以降が必要です。下記は5.40で確認しました。

leadv.py

  • Bluezのexample-advertisementを参考にしました。
leadv.py
#!/usr/bin/python

import dbus
import dbus.mainloop.glib
import dbus.service
import gobject as GObject

mainloop = None

BLUEZ_SERVICE_NAME = 'org.bluez'
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'

LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'

class FixedAdvertisement(dbus.service.Object):
    PATH_BASE = '/org/bluez/example/fixedadvertisement'

    def __init__(self, bus, index, advertising_type):
        self.path = self.PATH_BASE + str(index)
        self.bus = bus
        self.ad_type = advertising_type
        dbus.service.Object.__init__(self, bus, self.path)

    def get_path(self):
        return dbus.ObjectPath(self.path)

    @dbus.service.method(DBUS_PROP_IFACE,
                         in_signature='s',
                         out_signature='a{sv}')
    def GetAll(self, interface):
        if interface != LE_ADVERTISEMENT_IFACE:
            raise InvalidArgsException()

        properties = dict()
        properties['Type'] = self.ad_type
        return properties

    @dbus.service.method(LE_ADVERTISEMENT_IFACE,
                         in_signature='',
                         out_signature='')
    def Release(self):
        print '%s: Released!' % self.path

def register_ad_cb():
    print 'Advertisement registered'

def register_ad_error_cb(error):
    print 'Failed to register advertisement: ' + str(error)
    mainloop.quit()

def find_adapter(bus):
    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
                               DBUS_OM_IFACE)
    objects = remote_om.GetManagedObjects()

    for o, props in objects.iteritems():
        if LE_ADVERTISING_MANAGER_IFACE in props:
            return o

    return None

def main():
    global mainloop

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SystemBus()

    adapter = find_adapter(bus)
    if not adapter:
        print 'LEAdvertisingManager1 interface not found'
        return

    ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
                                LE_ADVERTISING_MANAGER_IFACE)

    ad_manager.RegisterAdvertisement(FixedAdvertisement(bus, 0, 'broadcast').get_path(), {},
                                     reply_handler=register_ad_cb,
                                     error_handler=register_ad_error_cb)

    mainloop = GObject.MainLoop()
    mainloop.run()

if __name__ == '__main__':
    main()
  • FixedAdvertisementクラスはアドバタイズデータのクラスです。
    • サービスデーモンから呼ばれるので、GetAll()とRemove()のメソッドをクラスに用意する必要があります。
    • GetAll()では、アドバタイズデータをpropertisとして返します。
    • Remove()はアドバタイズを停止するときに呼ばれれます。
    • GetAll()とRemove()は定義する際に@dbus.service.methodのおまじないが必要です。
  • register_ad_cb()とregister_ad_error_cb()はRegisterAdvertisement()で登録するコールバック関数です。
  • find_adapter()はAdapterを探して、LE_ADVERTISING_MANAGER_IFACEの有無からLE advertiseが使えるか判定します。
  • メイン
    • mainloopのおまじない
    • busとadapterの取得
    • アドバタイズマネージャの取得
    • FixedAdvertisementを生成して、RegisterAdvertisement()に登録する
    • メインループ

動作例

  • アドバタイズはスマートフォン等のアプリで確認してください。
pi@raspberrypi: $ ./leadv.py
Advertisement registered

アドバタイズで指定できるパラメータ

  • Type : broadcast or peripheral
  • ServiceUUIDs
  • ManufacturerData
  • SolicitUUIDs
  • ServiceData
  • IncludeTxPower

参考