From 092d0f31bf8fbb173a6eef434560639faf9731cd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 27 Aug 2025 17:40:55 +1000 Subject: [PATCH] mpremote: Don't apply Espressif DTR/RTS quirk to TinyUSB CDC device. The DTR quirk workaround from dea949e86 is needed for the Espressif Serial/JTAG device, but not for TinyUSB - in fact DTR must be set for TinyUSB to correctly determine if the serial port is open (and leads to issues with lost bytes otherwise). This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- tools/mpremote/mpremote/transport_serial.py | 36 +++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index e2490a7caf8be..a26183680cae8 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -36,11 +36,37 @@ # as a command line tool and a library for interacting with devices. import ast, io, os, re, struct, sys, time +import serial +import serial.tools.list_ports from errno import EPERM from .console import VT_ENABLED from .transport import TransportError, TransportExecError, Transport VID_ESPRESSIF = 0x303A # Espressif Incorporated +PID_ESPRESSIF_SERIAL_JTAG = 0x1001 # Serial/JTAG peripheral of ESP32-S3,C3,C6 + + +def has_espressif_dtr_quirk(devicename): + """ESP8266 and ESP32 dev boards use the DTR and RTS lines to trigger reset & + reset into bootloader mode. This can causes spurious reset issues on Windows. + + Apply the quirk to any USB/Serial chip on Windows that isn't using the + Microsoft CDC-ACM driver, or to the integrated Espressif Serial/JTAG device. + + Don't apply it to Espressif boards running TinyUSB, as TinyUSB uses DTR + to determine if the CDC port is open (and there's no spurious reset issue). + """ + portinfo = list(serial.tools.list_ports.grep(devicename)) # type: ignore + if not portinfo: + return False + + def port_attr(name): + return getattr(portinfo[0], name, None) + + return (port_attr("vid"), port_attr("pid")) == ( + VID_ESPRESSIF, + PID_ESPRESSIF_SERIAL_JTAG, + ) or port_attr("manufacturer") != "Microsoft" class SerialTransport(Transport): @@ -52,9 +78,6 @@ def __init__(self, device, baudrate=115200, wait=0, exclusive=True, timeout=None self.device_name = device self.mounted = False - import serial - import serial.tools.list_ports - # Set options, and exclusive if pyserial supports it serial_kwargs = { "baudrate": baudrate, @@ -72,12 +95,7 @@ def __init__(self, device, baudrate=115200, wait=0, exclusive=True, timeout=None elif os.name == "nt": self.serial = serial.Serial(**serial_kwargs) self.serial.port = device - portinfo = list(serial.tools.list_ports.grep(device)) # type: ignore - if portinfo and ( - getattr(portinfo[0], "vid", 0) == VID_ESPRESSIF - or getattr(portinfo[0], "manufacturer", "") != "Microsoft" - ): - # ESP8266/ESP32 boards use RTS/CTS for flashing and boot mode selection. + if has_espressif_dtr_quirk(device): # DTR False: to avoid using the reset button will hang the MCU in bootloader mode # RTS False: to prevent pulses on rts on serial.close() that would POWERON_RESET an ESPxx self.serial.dtr = False # DTR False = gpio0 High = Normal boot