Skip to content

Commit cb53e4b

Browse files
fix: Robustly handle BLE shutdown and add reconnect hook
This commit addresses two primary issues with the BLE interface: 1. The application would hang on exit, requiring multiple interrupts. 2. There was no mechanism for long-running applications to handle disconnections without the entire interface shutting down. Changes: - The `BLEInterface.close()` method is now idempotent and includes a timeout for the disconnect operation to prevent hangs. - The `atexit` handler has been simplified to call `close()` directly. - A new `auto_reconnect` parameter has been added to `BLEInterface.__init__`. - If `True` (default), on disconnect, the interface will clean up the client but remain active, allowing the application to handle reconnection. - If `False`, the interface will call `close()` as before. - A new example file, `examples/reconnect_example.py`, demonstrates how to implement a client-side reconnection loop.
1 parent 4ef6302 commit cb53e4b

5 files changed

Lines changed: 526 additions & 277 deletions

File tree

examples/reconnect_example.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
This example shows how to implement a robust client-side reconnection loop for a
3+
long-running application that uses the BLE interface.
4+
5+
The key is to instantiate the BLEInterface with `auto_reconnect=True` (the default).
6+
This prevents the library from calling `close()` on the entire interface when a
7+
disconnect occurs. Instead, it cleans up the underlying BLE client and notifies
8+
listeners via the `onConnection` event with a `connected=False` payload.
9+
10+
The application can then listen for this event and attempt to create a new
11+
BLEInterface instance to re-establish the connection, as shown in this example.
12+
"""
13+
import time
14+
import meshtastic
15+
import meshtastic.ble_interface
16+
from pubsub import pub
17+
import threading
18+
19+
# A thread-safe flag to signal disconnection
20+
disconnected_event = threading.Event()
21+
22+
def on_connection_change(interface, connected):
23+
"""Callback for connection changes."""
24+
print(f"Connection changed: {'Connected' if connected else 'Disconnected'}")
25+
if not connected:
26+
# Signal the main loop that we've been disconnected
27+
disconnected_event.set()
28+
29+
def main():
30+
"""Main function"""
31+
# Subscribe to the connection change event
32+
pub.subscribe(on_connection_change, "meshtastic.connection.status")
33+
34+
# The address of the device to connect to.
35+
# Replace with your device's address.
36+
address = "DD:DD:13:27:74:29" # TODO: Replace with your device's address
37+
38+
iface = None
39+
while True:
40+
try:
41+
disconnected_event.clear()
42+
print(f"Attempting to connect to {address}...")
43+
# Set auto_reconnect=True to prevent the interface from closing on disconnect.
44+
# This allows us to handle the reconnection here.
45+
iface = meshtastic.ble_interface.BLEInterface(
46+
address,
47+
noProto=True, # Set to False in a real application
48+
auto_reconnect=True
49+
)
50+
51+
print("Connection successful. Waiting for disconnection event...")
52+
# Wait until the on_connection_change callback signals a disconnect
53+
disconnected_event.wait()
54+
55+
# We must explicitly close the old interface before creating a new one
56+
iface.close()
57+
print("Interface closed. Reconnecting in 5 seconds...")
58+
time.sleep(5)
59+
60+
except meshtastic.ble_interface.BLEInterface.BLEError as e:
61+
print(f"Connection failed: {e}")
62+
print("Retrying in 5 seconds...")
63+
time.sleep(5)
64+
except KeyboardInterrupt:
65+
print("Exiting...")
66+
# Make sure to close the interface on exit
67+
if iface:
68+
iface.close()
69+
break
70+
except Exception as e:
71+
print(f"An unexpected error occurred: {e}")
72+
if iface:
73+
iface.close()
74+
break
75+
76+
if __name__ == "__main__":
77+
main()

0 commit comments

Comments
 (0)