如何模仿 nRF Connect (for Android) 对 Pygatt 脚本的操作?

How to Mimic nRF Connect (for Android) Actions to Pygatt Script?

我正在使用 nRF Connect for Android 来测试 BLE 外围设备。外围设备是 BSX Insight 肌肉残余氧监测器,其软件应用程序不再运行或不受制造商支持。因此,我使用我的设备 (BSX) 的唯一选择是编写我自己的控制软件。我在我的 64 位 Win 10 笔记本电脑上的 tkinter 例程中编写了一个 Python 3.7 脚本 运行。另外,我正在使用 Pygatt 库和 BLED112 BT 加密狗。

我可以连接到外围设备,读取和写入值正好符合特性,但我确信来自 nRF Connect 中使用的进程和我的脚本的 "conversion" 不完整且效率低下。所以我想确认的第一件事是使用了 Pygatt 中正确的相应函数。一旦我使用了 Pygatt 的正确函数,我就可以比较我想要捕获和存储的两个数据(特征值)流的各自输出。

nRF Connect中的基本流程:
1.扫描
2. select/connect BSX Insight
3. 暴露感兴趣的服务和特性
4.启用CCCD
5. 写入 "start data" 值 (04-02)

这些是来自 nRF Connect 日志文件的处理命令结果。从第四位开始:
4.
D 09:04:54.491 gatt.setCharacteristicNotification(00002a37-0000-1000-8000-00805f9b34fb, 真) 11
D 09:04:54.496 gatt.setCharacteristicNotification(2e4ee00b-d9f0-5490-ff4b-d17374c433ef, 真) 20x
D 09:04:54.499 gatt.setCharacteristicNotification(2e4ee00d-d9f0-5490-ff4b-d17374c433ef, 真) 25x
D 09:04:54.516 gatt.setCharacteristicNotification(2e4ee00e-d9f0-5490-ff4b-d17374c433ef, 真) 32x
D 09:04:54.519 gatt.setCharacteristicNotification(00002a63-0000-1000-8000-00805f9b34fb, 真) 36
D 09:04:54.523 gatt.setCharacteristicNotification(00002a53-0000-1000-8000-00805f9b34fb, 真) 40

以上是使用 nRF 命令的结果 "Enable CCCDs." 基本上每个可以启用的特性都已启用,这很好。 'x' 是我需要启用的三个。其他都是额外的。请注意,我在该行的末尾注释了这些 UUID 的相应句柄。

  1. V 09:05:39.211 写入命令到特性 2e4ee00a-d9f0-5490-ff4b-d17374c433ef
    D 09:05:39.211 gatt.writeCharacteristic(2e4ee00a-d9f0-5490-ff4b-d17374c433ef, 值=0x0402)
    I 09:05:39.214数据写入2e4ee00a-d9f0-5490-ff4b-d17374c433ef,值:(0x)04-02
    A 09:05:39.214 "(0x) 04-02" 已发送

第五个是我将0402写入上面的UUID的地方。此操作从以下位置发送 data/value 流:

一旦我在 nRF Connect 中完成上述基本步骤,两个特征值流就会激活,我可以立即在我的 Garmin Edge 810 主机中看到转换后的值。

因此尝试在我的 tkinter 片段中复制相同的过程:

# this function fires from the 'On' button click event
def powerON():
    powerON_buttonevent = 1
    print(f"\tpowerON_buttonevent OK {powerON_buttonevent}")

    # Connect to the BSX Insight
    try:
        adapter = pygatt.BGAPIBackend()       # serial_port='COM3'
        adapter.start()
        device = adapter.connect('0C:EF:AF:81:0B:76', address_type=pygatt.BLEAddressType.public)
        print(f"\tConnected: {device}")
    except:
        print(f"BSX Insight connection failure")
    finally:
        # adapter.stop()
        pass

    # Enable only these CCCDs
    try:
        device.char_write_handle(21, bytearray([0x01, 0x00]), wait_for_response=True)
        device.char_write_handle(26, bytearray([0x01, 0x00]), wait_for_response=True)
        device.char_write_handle(33, bytearray([0x01, 0x00]), wait_for_response=True)

        print(f"\te00b DESC: {device.char_read_long_handle(21)}")       # notifiy e00b
        print(f"\te00d DESC: {device.char_read_long_handle(26)}")       # notify e00d SmO2
        print(f"\te00e DESC: {device.char_read_long_handle(33)}")       # notify e00e tHb

        # Here's where I tested functions from Pygatt...
        # print(f"\t{device.get_handle('UUID_here')}")     # function works
        # print(f"\tvalue_handle/characteristic_config_handle:            {device._notification_handles('UUID_here')}")       # function works
        # print(f"{device.char_read('UUID_here')}")
        # print(f"{device.char_read_long_handle(handle_here)}")        # function works
    except:
        print(f"CCCD write value failure")
    finally:
        # adapter.stop()
        pass

    # Enable the data streams
    try:
        device.char_write('2e4ee00a-d9f0-5490-ff4b-d17374c433ef', bytearray([0x04, 0x02]), wait_for_response=True)    # function works
        print(f"\te00a Power ON: {device.char_read('2e4ee00e-d9f0-5490-ff4b-d17374c433ef')}")
    except:
        print(f"e00a Power ON write failure")
    finally:
        # adapter.stop()
        pass

    # Subscribe to SmO2 and tHb UUIDs
    try:
        def data_handler(handle, value):
            """
            Indication and notification come asynchronously, we use this function to
            handle them either one at the time as they come.
            :param handle:
            :param value:
            :return:
            """
            if handle == 25:
                print(f"\tSmO2: {value} Handle: {handle}")
            elif handle == 32:
                print(f"\ttHb: {value} Handle: {handle}")
            else:
                print(f"\tvalue: {value}, handle: {handle}")

        device.subscribe("2e4ee00d-d9f0-5490-ff4b-d17374c433ef", callback=data_handler, indication=False, wait_for_response=True)
        device.subscribe("2e4ee00e-d9f0-5490-ff4b-d17374c433ef", callback=data_handler, indication=False, wait_for_response=True)
        print(f"\tSuccess 2e4ee00d: {device.char_read('2e4ee00d-d9f0-5490-ff4b-d17374c433ef')}")
        print(f"\tSuccess 2e4ee00e: {device.char_read('2e4ee00e-d9f0-5490-ff4b-d17374c433ef')}")

        # this statement causes a run-on continuity when enabled
        # while True:
        #     sleep(1)

    except:
        print("e00d/e00e subscribe failure")
    finally:
        adapter.stop()
        # pass  

问题:在我的 Atom 编辑器的输出 window 中,两个数据流按预期启动。例如:

I 09:05:39.983 从 2e4ee00d-d9f0-5490-ff4b-d17374c433ef 收到通知,值:(0x) 00-00-00-00-C0-FF-00-00-C0-FF -84-65-B4-3B-9E-AB-83-3C-FF-03

和...

I 09:05:39.984 从 2e4ee00e-d9f0-5490-ff4b-d17374c433ef 收到通知,值:(0x) 1C-00-00-FF-03-FF-0F-63-00-00 -00-00-00-00-16-32-00-00-00-00

在 "stream" 停止之前,我会看到大约七到十行数据。将有大约 20 秒的间隔,然后是大量的值转储。这与 nRF Connect 的输出不同,后者是即时且连续的。

我有来自 nRF Connect 的日志和 Python...但我不确定哪个日志条目指向停止的原因。这个问题可能与外设首选连接参数有关吗? nRF Connect 属性 读取显示:

Python 日志条目显示:

信息:pygatt.backends.bgapi.bgapi:连接状态:句柄=0x0,标志=5,地址=0xb'760b81afef0c',连接间隔=75.000000ms,超时=1000,延迟=0 间隔,绑定=0xff

有人有想法吗? (真的,提前致谢。)

我已经回答了我的问题。我现在必须解决新问题,即为什么我的 tKinter 对话框是 "not responding" 作为一个单独的问题。 谢谢大家 编辑 2020 年 3 月 31 日:我使用 pyQt 重新编写了脚本,现在有了一个功能性应用程序。