Skip to content

Commit ad0f34e

Browse files
committed
mpremote: Fix disconnect handling on Windows and Linux.
- Handle SerialException on Windows when device disconnects - Print clean 'device disconnected' message instead of stack trace - Fix terminal formatting issues on Linux after disconnect - Return disconnected state after console cleanup to avoid terminal issues This ensures proper disconnect messages on both platforms without showing confusing error traces to users. Signed-off-by: Andrew Leech <[email protected]>
1 parent 45e4deb commit ad0f34e

File tree

3 files changed

+44
-7
lines changed

3 files changed

+44
-7
lines changed

tools/mpremote/mpremote/console.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,19 @@ def inWaiting(self):
9696
return 1 if self.ctrl_c or msvcrt.kbhit() else 0
9797

9898
def waitchar(self, pyb_serial):
99-
while not (self.inWaiting() or pyb_serial.inWaiting()):
100-
time.sleep(0.01)
99+
import serial
100+
101+
while True:
102+
try:
103+
if self.inWaiting() or pyb_serial.inWaiting():
104+
break
105+
time.sleep(0.01)
106+
except serial.SerialException as e:
107+
if "ClearCommError failed" in str(e):
108+
# Device disconnected on Windows
109+
raise serial.SerialException("Device disconnected")
110+
else:
111+
raise
101112

102113
def readchar(self):
103114
if self.ctrl_c:

tools/mpremote/mpremote/main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,11 @@ def main():
574574
# If no commands were "actions" then implicitly finish with the REPL
575575
# using default args.
576576
if state.run_repl_on_completion():
577-
do_repl(state, argparse_repl().parse_args([]))
577+
disconnected = do_repl(state, argparse_repl().parse_args([]))
578+
579+
# Handle disconnection message
580+
if disconnected:
581+
print("\ndevice disconnected")
578582

579583
return 0
580584
except CommandError as e:

tools/mpremote/mpremote/repl.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@
22

33
from .transport import TransportError
44

5+
import serial
6+
57

68
def do_repl_main_loop(
79
state, console_in, console_out_write, *, escape_non_printable, code_to_inject, file_to_inject
810
):
11+
disconnected = False
912
while True:
10-
console_in.waitchar(state.transport.serial)
11-
c = console_in.readchar()
13+
try:
14+
console_in.waitchar(state.transport.serial)
15+
c = console_in.readchar()
16+
except serial.SerialException as e:
17+
if "Device disconnected" in str(e) or "ClearCommError failed" in str(e):
18+
disconnected = True
19+
break
20+
else:
21+
raise
1222
if c:
1323
if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit
1424
break
@@ -35,8 +45,15 @@ def do_repl_main_loop(
3545
n = state.transport.serial.inWaiting()
3646
except OSError as er:
3747
if er.args[0] == 5: # IO error, device disappeared
38-
print("device disconnected")
48+
disconnected = True
3949
break
50+
except Exception as er:
51+
# Handle Windows serial exception
52+
if isinstance(er, serial.SerialException) and "ClearCommError failed" in str(er):
53+
disconnected = True
54+
break
55+
else:
56+
raise
4057

4158
if n > 0:
4259
dev_data_in = state.transport.serial.read(n)
@@ -53,6 +70,8 @@ def do_repl_main_loop(
5370
console_data_out = dev_data_in
5471
console_out_write(console_data_out)
5572

73+
return disconnected
74+
5675

5776
def do_repl(state, args):
5877
state.ensure_friendly_repl()
@@ -85,8 +104,9 @@ def console_out_write(b):
85104
capture_file.write(b)
86105
capture_file.flush()
87106

107+
disconnected = False
88108
try:
89-
do_repl_main_loop(
109+
disconnected = do_repl_main_loop(
90110
state,
91111
console,
92112
console_out_write,
@@ -98,3 +118,5 @@ def console_out_write(b):
98118
console.exit()
99119
if capture_file is not None:
100120
capture_file.close()
121+
122+
return disconnected

0 commit comments

Comments
 (0)