Skip to content

Commit 28a5880

Browse files
committed
fix: Address BLE interface race conditions and improve error handling
- Fix race condition in _receiveFromRadioImpl and _sendToRadioImpl by using local client variables - Update reconnect_example.py to include interface info in logs and re-raise exceptions - Move close() invocation off Bleak loop thread to prevent blocking disconnects - Fix import order and missing final newline in reconnect_example.py
1 parent cb53e4b commit 28a5880

2 files changed

Lines changed: 16 additions & 11 deletions

File tree

examples/reconnect_example.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@
1010
The application can then listen for this event and attempt to create a new
1111
BLEInterface instance to re-establish the connection, as shown in this example.
1212
"""
13+
import threading
1314
import time
15+
16+
from pubsub import pub
17+
1418
import meshtastic
1519
import meshtastic.ble_interface
16-
from pubsub import pub
17-
import threading
1820

1921
# A thread-safe flag to signal disconnection
2022
disconnected_event = threading.Event()
2123

2224
def on_connection_change(interface, connected):
2325
"""Callback for connection changes."""
24-
print(f"Connection changed: {'Connected' if connected else 'Disconnected'}")
26+
iface_label = getattr(interface, "address", repr(interface))
27+
print(f"Connection changed for {iface_label}: {'Connected' if connected else 'Disconnected'}")
2528
if not connected:
2629
# Signal the main loop that we've been disconnected
2730
disconnected_event.set()
@@ -71,7 +74,7 @@ def main():
7174
print(f"An unexpected error occurred: {e}")
7275
if iface:
7376
iface.close()
74-
break
77+
raise
7578

7679
if __name__ == "__main__":
77-
main()
80+
main()

meshtastic/ble_interface.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ def __repr__(self):
108108

109109
def _on_ble_disconnect(self, client: "BLEClient") -> None:
110110
"""Disconnected callback from Bleak."""
111-
logger.debug(f"BLE client {client.address} disconnected.")
111+
logger.debug(f"BLE client {client.bleak_client.address} disconnected.")
112112
if self.auto_reconnect:
113113
# We only cleanup the client, but do not call close()
114114
# This allows the application to handle reconnection.
115115
self.client = None
116116
self._disconnected()
117117
else:
118-
self.close()
118+
Thread(target=self.close, name="BLEClose", daemon=True).start()
119119

120120
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
121121
"""Handle callbacks for fromnum notify.
@@ -208,12 +208,13 @@ def _receiveFromRadioImpl(self) -> None:
208208
self.should_read = False
209209
retries: int = 0
210210
while self._want_receive:
211-
if self.client is None:
211+
client = self.client
212+
if client is None:
212213
logger.debug(f"BLE client is None, shutting down")
213214
self._want_receive = False
214215
continue
215216
try:
216-
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
217+
b = bytes(client.read_gatt_char(FROMRADIO_UUID))
217218
except BleakDBusError as e:
218219
# Device disconnected probably, so end our read loop immediately
219220
logger.debug(f"Device disconnected, shutting down {e}")
@@ -238,10 +239,11 @@ def _receiveFromRadioImpl(self) -> None:
238239

239240
def _sendToRadioImpl(self, toRadio) -> None:
240241
b: bytes = toRadio.SerializeToString()
241-
if b and self.client: # we silently ignore writes while we are shutting down
242+
client = self.client
243+
if b and client: # we silently ignore writes while we are shutting down
242244
logger.debug(f"TORADIO write: {b.hex()}")
243245
try:
244-
self.client.write_gatt_char(
246+
client.write_gatt_char(
245247
TORADIO_UUID, b, response=True
246248
) # FIXME: or False?
247249
# search Bleak src for org.bluez.Error.InProgress

0 commit comments

Comments
 (0)