python icon indicating copy to clipboard operation
python copied to clipboard

CLI hangs on BLE connected keyboard interrupt

Open bkt0031 opened this issue 6 months ago • 2 comments

I'm looking at integrating Bluetooth connectivity into the TC2-BBS software. I created a simple Python script that connects, waits, and disconnects from a RAK 4631 radio. It connected fine, but when I issue a keyboard interrupt it hung requiring a second Ctrl-C to be entered. I thought that maybe it was my script so I decided to run a listen command from the meshtastic CLI script and it does the same thing:

# meshtastic --ble xx:xx:xx:xx:xx:xx --listen --debug

[... all kinds of good debug connection information displayed ...]
DEBUG file:client.py read_gatt_char line:763 Read Characteristic 2c55e69e-4993-11ed-b878-0242ac120002 | /org/bluez/hci0/dev_F5_1B_B9_6D_B9_81/service0025/char0029: bytearray(b'')
DEBUG file:manager.py _parse_msg line:872 received D-Bus signal: org.freedesktop.DBus.Properties.PropertiesChanged (/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81/service0025/char0029): ['org.bluez.GattCharacteristic1', {'Value': <dbus_fast.signature.Variant ('ay', b'')>}, []]
DEBUG file:client.py read_gatt_char line:763 Read Characteristic 2c55e69e-4993-11ed-b878-0242ac120002 | /org/bluez/hci0/dev_F5_1B_B9_6D_B9_81/service0025/char0029: bytearray(b'')

I then hit Ctrl-C after a few seconds with the following result:

^CINFO file:__main__.py common line:1323 Exiting due to keyboard interrupt
DEBUG file:client.py disconnect line:391 Disconnecting ({/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81})
DEBUG file:manager.py _parse_msg line:872 received D-Bus signal: org.freedesktop.DBus.ObjectManager.InterfacesRemoved (/): ['/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81', ['org.bluez.Battery1']]
DEBUG file:manager.py _parse_msg line:872 received D-Bus signal: org.freedesktop.DBus.Properties.PropertiesChanged (/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81): ['org.bluez.Device1', {'ServicesResolved': <dbus_fast.signature.Variant ('b', False)>}, []]
DEBUG file:manager.py _parse_msg line:872 received D-Bus signal: org.freedesktop.DBus.Properties.PropertiesChanged (/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81): ['org.bluez.Device1', {'Connected': <dbus_fast.signature.Variant ('b', False)>}, []]
DEBUG file:client.py on_connected_changed line:163 Device disconnected (/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81)
DEBUG file:client.py _cleanup_all line:353 _cleanup_all(/org/bluez/hci0/dev_F5_1B_B9_6D_B9_81)
DEBUG file:ble_interface.py _sendToRadioImpl line:208 TORADIO write: 2001

It hung here with not further output. Hitting Ctrl-C again after 10 minutes or so I get:

^CException ignored in atexit callback: <bound method BLEClient.disconnect of <meshtastic.ble_interface.BLEClient object at 0x7f9ba41f10>>
Traceback (most recent call last):
  File "/home/brian/TC2-BBS-mesh/venv/lib/python3.11/site-packages/meshtastic/ble_interface.py", line 270, in disconnect
    self.async_await(self.bleak_client.disconnect(**kwargs))
  File "/home/brian/TC2-BBS-mesh/venv/lib/python3.11/site-packages/meshtastic/ble_interface.py", line 296, in async_await
    return self.async_run(coro).result(timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 451, in result
    self._condition.wait(timeout)
  File "/usr/lib/python3.11/threading.py", line 320, in wait
    waiter.acquire()
KeyboardInterrupt:
#

I'm not sure why the software is writing TORADIO 2001 after the connection has been close, but then again I'm not 100% sure how to read these logs with threading and async stuff thrown in there.

Normally if I were just dealing with this on the CLI I would not care, but it adds an undesired complication when I want to run the TC2-BBS as a service and desire to stop it.

I'm running this software on Raspberry Pi OS "Debian GNU/Linux 12 (bookworm)", on a 2 W device, Python version 3.11.2, meshtastic firmware firmware-rak4631-2.6.10.9ce4455, and the Python meshtastic library version 2.6.3.

I will keep digging into this, but quite frankly I suck at reading/figuring out someone else's code...

Thanks in advance

bkt0031 avatar Jun 19 '25 01:06 bkt0031

I am seeing very similar behavior. Environment is a simple Python script (Python 3.13.5) using the meshtastic package 2.6.4 on Mac OS 15.5. Device is a RAK 4631 running firmware 2.6.11 (firmware-rak4631-2.6.11.60ec05e.uf2 firmware file).

The connection works fine, and I see plenty of packets coming in. When I hit ^c, the keyboard interrupt is registered and close() is called. Here's the debug output:

^CKeyboard interrupt
Closing connection to Meshtastic BLE interface
DEBUG: TORADIO write: 2001
DEBUG: peripheral_didWriteValueForCharacteristic_error_
DEBUG: Write Characteristic Value
DEBUG: Write Characteristic f75c76d2-129e-4dad-a1dd-7866124401e7 : b' \x01'
DEBUG: centralManager_didDisconnectPeripheral_error_
DEBUG: Peripheral Device disconnected!
DEBUG: TORADIO write: 2001

and the close() hangs. If I hit ^C again, I get this stack trace:

^CTraceback (most recent call last):
  File "/Users/ethierbach/PycharmProjects/meshmisc/scaffold.py", line 156, in <module>
    somewhereland.close()
    ~~~~~~~~~~~~~~~~~~~^^
  File "/Users/ethierbach/PycharmProjects/meshmisc/.venv/lib/python3.13/site-packages/meshtastic/ble_interface.py", line 249, in close
    self.client.disconnect()
    ~~~~~~~~~~~~~~~~~~~~~~^^
  File "/Users/ethierbach/PycharmProjects/meshmisc/.venv/lib/python3.13/site-packages/meshtastic/ble_interface.py", line 281, in disconnect
    self.async_await(self.bleak_client.disconnect(**kwargs))
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ethierbach/PycharmProjects/meshmisc/.venv/lib/python3.13/site-packages/meshtastic/ble_interface.py", line 307, in async_await
    return self.async_run(coro).result(timeout)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/Users/ethierbach/.pyenv/versions/3.13.5/lib/python3.13/concurrent/futures/_base.py", line 451, in result
    self._condition.wait(timeout)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/Users/ethierbach/.pyenv/versions/3.13.5/lib/python3.13/threading.py", line 359, in wait
    waiter.acquire()
    ~~~~~~~~~~~~~~^^
KeyboardInterrupt

Process finished with exit code 130 (interrupted by signal 2:SIGINT)

Here's the relevant code section. There are some pub.subscribes and associated plumbing above it, let me know if you want to see that as well.

somewhereland = None
try:
    print("Connecting to Meshtastic BLE interface")
    somewhereland = ble.make_connection_and_return(None)
    print("busy-waiting while async events happen")
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    print("Keyboard interrupt")
finally:
    if somewhereland:
        print("Closing connection to Meshtastic BLE interface")
        somewhereland.close()
        print("Connection closed")
    print(TOPIC_COUNTS)
    print("Done")

and here is ble.make_connection_and_return:

def make_connection_and_return(address: Optional[str]):
    # This relies on the caller to keep the script alive, and to close the interface
    # if address = None, the meshtastic BLE interface will hunt for one (and I think only one)
    interface = None
    try:
        interface = meshtastic.ble_interface.BLEInterface(address)
        print(f"BLEInterface to {interface.getShortName()} initialized")
    except meshtastic.ble_interface.BLEInterface.BLEError as e:
        print(f"\nERROR: Could not connect via BLE. {e}")
    except Exception as e:
        print(f"\nAn unexpected error occurred: {e}")

    return interface

Adding my advance thanks!

ab8oj avatar Jul 08 '25 21:07 ab8oj

I am not sure if this is related, but my meshtastic CLI version 2.6.4 does not disconnect when doing --ble --export-config anymore either.

ulab avatar Jul 09 '25 13:07 ulab