Repository navigation
mpremote: on ESPxxx all mpremote commands are followed by a hard reboot #9659
Description
Activity
if you want to take a look at a trace :
three_device_session.zipopen with : https://www.saleae.com/downloads/
@Josverl This is a hard reset, not a soft reset. A soft reset would not print out the esp boot sequence.
The only hint I have is that on many ESP32 boards, they wire up RTS/CTS from the on-board cp210x / ftdi in a way such that esptool.py can trigger the board to reset into the bootloader. I wonder if somehow the way the serial port is being opened on Windows is also triggering this?
you are correct , there is both a soft and a hard reset. I was blinded by the first one.
but im sure that I and students in bootcamps have been able to connect to and disconnect many times to a board without it rebooting.my esp32s all use a CP210x ,
while my esp8266 all have an CH340but that is about the most common UART/USB interface used,
Could it be that the state of the DTR/RTS signals have an effect ? ( duh , yes - you were also thinking that 🤦♂️)
One fix that I have needed to add to a few terminal tools is to compensate for the fact that that windows and linux actually handle the DTR/RTS signals differently. (neither is wrong, just different)
(I've not looked into how pyserial handles this yet)Reacted by Christoph Rackwitz- changed the title
[-]mpremote: all mpremote commands are followed by a soft reboot[/-][+]mpremote: all mpremote commands are followed by a hard reboot[/+]on Oct 18, 2022 I encountered this recently with a new-to-me board, SparkFun MicroMod ESP32. The schematic documents the DTR/RTS behavior for resetting:
- If DTR is LOW, toggling RTS from HIGH to LOW resets to run mode
- If RTS is HIGH, toggling DTR from LOW to HIGH resets to bootloader
As a test, I changed the Pyboard constructor in
pyboard.pyto assert the DTR and RTS pins:@@ -275,7 +275,11 @@ class Pyboard: delayed = False for attempt in range(wait + 1): try: - self.serial = serial.Serial(device, **serial_kwargs) + self.serial = serial.Serial(**serial_kwargs) + self.serial.dtr = True + self.serial.rts = False + self.serial.port = device + self.serial.open() break except (OSError, IOError): # Py2 and Py3 have different errors if wait == 0:
This fixed the problem on Linux and Windows, but not macOS (Big Sur, Monterrey).
On Linux, using
stty -F /dev/ttyUSB0 -hup
also prevented the hard reset.
Reacted by Jos VerlindeThanks @mgsb -- From what I've read elsewhere this seems like the right approach, although frustrating about macOS.
I haven't had much chance to look into this in detail but one thing I'd like to understand -- is this a new issue, or has it always been a problem? i.e. was this always causing DTR/RTS to bounce quickly, but there was enough capacitance and/or pull strength that it was never noticed? Are we seeing it now because just a handful of boards have less capacitance on these lines?
@Josverl does this solve it for your board?
Following long debugging session on Discord today (22Dec2022) I can confirm I had this issue on an ESP32-C3FH4 (ESP32-C3 with RBG matrix on board) and that the fix above fixed my issue
Who do I see about getting it incorporated into standard code base? :)
Reacted by Christoph RackwitzI'll give this a test, but the .py fix is like what I've been doing in .js for pymakr, but with the added condition/constraint to only manipulate dtr rts on win32/64
Following long debugging session on Discord today (22Dec2022) I can confirm I had this issue on an ESP32-C3FH4 (ESP32-C3 with RBG matrix on board) and that the fix above fixed my issue
Some more details -- this was Windows, and the behaviour was that there was a reset on disconnect.
The C3 is interesting because it's on-chip USB/UART, rather than external, but clearly it's internally wired up the same way with the DTR/RTS pins.
Who do I see about getting it incorporated into standard code base? :)
I will send a PR. I think it sounds like this change doesn't solve everything, but overall only improves things and doesn't make anything break. But will need some more testing.
- added a commit that references this issue
on Feb 10, 2023 I will send a PR. I think it sounds like this change doesn't solve everything, but overall only improves things and doesn't make anything break. But will need some more testing.
Lucky me found the case where it does break :-D on windows and stm32 with "custom" driver installed (with zadig) to give the ports a personalised name! So yeah not a common situation, though common to every device I make at work...
I'm not sure how to handle it better here though really, other than adding a extra bit of extra injected code on connection to query
os.uname()to enable stuff like this per-port. Of course this would fail if the device isn't at repl on connection (though much of mpremote fails in this circumstance).In theory I might be able to update my driver creation / installation to keep the manufacturer as "Microsoft" to bypass this bit of mpremote code too, though that doesn't help devices / drivers already in the field...
8 remaining items
@andrewleech,
did you find the root cause for your problem ?
if it was indeed the RTS held high then perhaps its also possible to prevent that if there is a common VID for STM devices ?VID 303A, PID 1001.
These VID:PID are same as for the ESP32-C3 board I have.
and for a ESP32-C2 - single Connector board:
idVendor : 0x303A (Espressif Incorporated) idProduct : 0x4001 bcdDevice : 0x0100So checking for the VID should work, unless board manufacturers replace it with their own VID's.
then we would need to add a config option / file.I did not try other DTR/RTS combinations since the first one I tried worked already
@crackwitz, the challenge is not only fixing this for your board,
but doing that without breaking mpremote + other boards across a number use different usecases.I am aware of that, which is why I strive to make perfectly clear what I didn't try.
Reacted by Jos Verlinde@andrewleech,
did you find the root cause for your problem ?
if it was indeed the RTS held high then perhaps its also possible to prevent that if there is a common VID for STM devices ?My particular issue was from having a custom vid pid and separately loaded driver that did not have Microsoft as the listed manufacturer so it didn't match the checks in code.
I tried making changes to this block of code to resolve the problems better but every option I tried had other subtle negative outcomes too, they're in a PR here somewhere I think.
did not have Microsoft as the listed manufacturer so it didn't match the checks in code.
Thanks for clarifying. The only way that we can avoid that is by having a list of regexes to match the known driver names/vendors for the uart drivers. That is the approach we ended up using for Pymakr. Simpler than gathering all possible VIDs for the serial UART drivers.
[ "Pycom.*", "FTDI", "Microsoft", "Microchip Technology, Inc.", "Silicon Labs", "Espressif.*", .... ]
I think that should be a separate PR though
This is probably a silly question as I'm not across the whole thing, but in the limit is it reasonable to patch the opposite case? (i.e. don't clear DTR/RTS for drivers/configs where we know that will cause a problem, do it for all other boards which should include most/all ESP32 boards plus a bunch of boards which don't care either way.
Alternatively, if it's too hard to get all of the custom cases then maybe this needs to be an option on
mpremote resume(i.e.mpremote resume --esp ...or similar.) - in addition to detecting the most obvious cases.not silly , but hard to parse your inverse logic, but I think we may see the same way ahead.
let me try to recap my experience, and intents
- pyboard/pyserial defaults are OK on linux / macOS, and were mostly broken on Windows. ( different world visions between the OS ecosystems)
- this mostly surfaces as spikes in the DTR/CTSlines at either connection , but more often at disconnection
- this is most problematic in boards that use these to do special things, such as the ESPxxx boards that connect these lines to some logic and !EN and other strapping Pins
- it is not always simple to see / trace these signals - but on the ESP32 / ESP8266 chips it often is, so I used there for my testing.
- most of these boards have UART-USB convertors on board - with multiple different drivers and driver versions for each of these.
Naming of these drivers is - not quite consistent, and new drivers are released .
To avoid ESPxx board to reset on disconnect , or hang in bootloader on connect we need the set DTR/CTS to False/False Only for these boards, both for UART/USB as well as for USB/CDC , but there is no authoritative list of VIDs / vendor names to do so.
With Pycom I was able to iterate quickly on user feedback - and updates were done in weeks, so we were able to add regexes for vendor strings that were reported quickly.
with mpremote some of the same updates have been stalled for months, or longer. That is the main reason I have chosen for the if not "Microsoft" driver approach.Now with the modern ESPxxx boards switching to native USB , and less reliance on 3rd party UART drivers I think it is a good time to switch tactics
- The default mpremote config should have a list of VIDs-PIDs that require "ESP-Handling" on Windows
- this config should be extendible/overridable though the mpremote configuration file (currently still broken on windows )
- when connecting to a port - mpremote can check if it needs ESP-Handling - and then act accordingly
- if / when needed new vids/pids can be added to the default list via PRs, and included in the next release
- personal / interim changes can be done by editing the mpremote config file.
- personal / interim changes can be done by editing the mpremote config file.
I think that would be good, to be able to customise/configure/override the default behaviour based on user configuration.
Reacted by Jos VerlindeOk , I can create a draft PR for that, so we can iterate over the details.
then we can move the conversation there as well.
- added a commit that references this issue
on Sep 12, 2025 - added a commit that references this issue
on May 23, 2026 - added 2 commits that reference this issue
on Sep 21, 2026
according to @jimmo mpremote should be able to resume a repl session on a connected mcu without a reboot by using the following command pattern:
where the
resumeis the important part.see Discord discussion
however when I try this :
mpremotecommands are lostraw REPL(observed through logic analyzer)
So even though the 2nd mpremote command does not force a reset, by that time a soft reboot has already occurred.
I also see a
soft resetafter:Actual result on windows :
Expected
the MCU not to reset between two mpremote commands similar to the behavour on linux
results as seen by a logic analyzer hooked up to the RX/TX pins of the esp32

other observations
Versions
firmwares
{'ver': 'v1.19.1', 'build': '', 'platform': 'esp32', 'port': 'esp32', 'machine': 'ESP32 module (spiram) with ESP32', 'release': '1.19.1', 'nodename': 'esp32', 'name': 'micropython', 'family': 'micropython', 'sysname': 'esp32', 'version': '1.19.1'}
{'ver': 'v1.19.1', 'build': '', 'platform': 'esp32', 'port': 'esp32', 'machine': '4MB/OTA module with ESP32', 'release': '1.19.1', 'nodename': 'esp32', 'name': 'micropython', 'family': 'micropython', 'sysname': 'esp32', 'version': '1.19.1'}
{'ver': 'v1.19.1', 'build': '', 'platform': 'esp8266', 'port': 'esp8266', 'machine': 'ESP module (1M) with ESP8266', 'release': '1.19.1', 'nodename': 'esp8266', 'name': 'micropython', 'family': 'micropython', 'sysname': 'esp8266', 'version': '1.19.1'}
oldest firmware tested with same behavior:
{'ver': 'v1.10', 'build': '', 'platform': 'esp32', 'port': 'esp32', 'machine': 'ESP32 module with ESP32', 'release': '1.10.0', 'nodename': 'esp32', 'name': 'micropython', 'family': 'micropython', 'sysname': 'esp32', 'version': '1.10.0'}