如何从 BLE 服务器清除已注册的广告?

How to clean registered advertisements from a BLE server?

我正在尝试 运行 以下 python 脚本:https://github.com/Jumperr-labs/python-gatt-server (gatt-server-example.py) 这是一个低功耗蓝牙服务器。

它在我的电脑上工作得很好,但是当我尝试 运行 它在英特尔爱迪生上时,我收到以下错误:

Failed to register advertisement: org.bluez.Error.NotPermitted: Maximum        
advertisements reached

Python-dbus 和 Bluez 已安装在此设备上 (Bluez v5.50)。我正在寻找一种方法来清除已注册的广告(如果有的话)并在这个英特尔爱迪生上启动我的服务器。

如果您 运行 来自 github.com/edison-fw 的最新 Yocto 映像并切换到最新 python3 example gatt server 服务器 运行s没有错误。 Bluetoothctl show 显示心率服务在杀死gatt服务器后自动注销

BlueZ API docs 对错误提供了一些解释:

Methods     RegisterAdvertisement(object advertisement, dict options)

            Registers an advertisement object to be sent over the LE
            Advertising channel.  The service must be exported
            under interface LEAdvertisement1.

            ...

            If the maximum number of advertisement instances is
            reached it will result in NotPermitted error.

因此,错误来自 RegisterAdvertisement 调用:

ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
                                 reply_handler=register_ad_cb, 
                                 error_handler=functools.partial(register_ad_error_cb, mainloop))

“brute-force”清除以前注册的广告的方法是始终在 运行 您的 BLE 服务器之前重新启动蓝牙服务。

# Restart bluetooth
$ systemctl restart bluetooth
$ systemctl status bluetooth
● bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset
   Active: active (running) since Thu 2020-08-20 18:07:55 JST; 4s ago
...

# Start BLE server
$ python3 gatt-server.py

一种更“优雅”的方法是尝试确保您的 BLE 服务器实现具有正确关闭或清理的处理能力。尝试在服务器进程 ends/quits:

时调用 BlueZ 广告管理器和 GATT 管理器 API Unregister* 方法

您可能还需要 quit 创建的 GLib.MainLoop

example-gatt-server mentioned in the does not do this, but you can also check example-advertisement 代码(也来自 BlueZ) 这样做:

def shutdown(timeout):
    print('Advertising for {} seconds...'.format(timeout))
    time.sleep(timeout)
    mainloop.quit()


def main(timeout=0):
    
    ...

    mainloop = GObject.MainLoop()

    ad_manager.RegisterAdvertisement(test_advertisement.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...')

    mainloop.run()  # blocks until mainloop.quit() is called

    ad_manager.UnregisterAdvertisement(test_advertisement)
    print('Advertisement unregistered')
    dbus.service.Object.remove_from_connection(test_advertisement)

请注意,当 mainloop 退出时,main 函数以清理调用结束。

https://github.com/Jumperr-labs/python-gatt-server 中的代码只是 BlueZ 代码的 ported/re-organized 版本。一项改进是修改 advertisinggatt-server 模块以添加适当的清理。

一种方法是使用 SIGTERM 信号优雅地终止 BLE 服务器进程,然后 catching that SIGTERM within the app

# ----------------------------------------------------------------------
def terminate(signum, frame):
    adv_manager.UnregisterAdvertisement(...)
    gatt_manager.UnregisterApplication(...)
    main_loop.quit()
    return True
# ----------------------------------------------------------------------
signal.signal(signal.SIGTERM, terminate)