-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
mpremote: Workaround ESP reset quirk at disconnect time. #18001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
mpremote: Workaround ESP reset quirk at disconnect time. #18001
Conversation
- The problem with ESP board spurious reset happens at disconnect time on Windows (clearing DTR before RTS triggers a reset). - Previous workarounds tried to detect possible ESP boards and apply the correct DTR and RTS settings when opening the port. - Instead, we can manually clear RTS before closing the port and thereby avoid the reset issue. Opening the port can keep the default behaviour (RTS & DTR both set). - close() is called from a finally block in the mpremote main module (via do_disconnect()) - so this should always happen provided the Python process isn't terminated by the OS. Signed-off-by: Angus Gratton <angus@redyak.com.au> Signed-off-by: Angus Gratton <angus@redyak.com.au>
@Josverl you've looked into this a lot more recently than I have, so I'm very curious what you think. I noticed that in your results table here you didn't get a REPL on non-ESP devices when setting DTR=True and RTS=True. I wasn't able to reproduce this (my understanding is that as long as DTR is set, RTS should be ignored) but it did give me some concern that I've missed something (or behaviour is different in some other Windows configuration). |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #18001 +/- ##
=======================================
Coverage 98.38% 98.38%
=======================================
Files 171 171
Lines 22296 22296
=======================================
Hits 21937 21937
Misses 359 359 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Code size report:
|
Some board vendors (ESP32 and others) wanted to save a few pennies and omitted the two transistor logic for RTS and DTR. Instead RTS and DTR are directly connected to the respective pins, e.g. Reset and IO0. Then it matters whether RTS and DTR are both low or both high. Such board required having control over RTS and DTR. That is possible e.g. with picocom. I faintly recall that there were similar discussion for mpremote. |
I do expect differences, mainly as I also see differences in when running Ubuntu/ WSL2 in Windows. Could be it's just timing, but we know it only needs a spike to cause a reset. |
So would it be better to set RTS/DTR defaults (for ESPxxx) that can be changed by cmdline/config, rather than hard coding ? Also, how can I determine on a board if the dual transistor logic you mention is in place? I do not want to pollute the tests, and I have both cheap and non-cheap boards (And my electronics skills are basic) |
Yes. The default can be that both DTR and RTS are set (levels at the board low), which is the actual state.
You can try the various settings, whether the board responds, or just assume that the majority of boards behave well with RTS and DTR both set and not use untypical boards for firmware testing. The special cases are then left as support for users with untypical boards. P.S.: Not all cheap boards omit the reset/firmware_load logic. |
@projectgus Can you clarify that in the table please ? |
Summary
This is a possible alternative fix for #9659 which uses a different approach to #17776 and #17800.
Background
There are two conflicting problems with use of DTR and RTS in mpremote (and other serial host programs):
USB-CDC implementations tend to use "DTR is set" as a signal for "host is connected" (a reasonable thing to do), and therefore don't send any data to the host if DTR is cleared.
ESP8266 and ESP32 boards use a convention where setting
(DTR&&!RTS)
means "put IO0 low for bootloader mode" and(!DTR&&RTS)
means "trigger reset", allowing the host to reset the chip to bootloader mode. Traditionally this is implemented in a small circuit - the circuit mostly exists so that default behaviour of an open serial port(DTR&&RTS)
doesn't trigger a reset.With the Espressif integrated "USB Serial & JTAG" peripheral and Espressif TinyUSB native USB stack the same reset logic is implemented by looking for a sequence of line state transition packets (implemented in hardware and software, respectively).
#9659 describes a problem where Windows (and/or pyserial on Windows) clears DTR before RTS when closing the port, and therefore triggers a hard reset each time mpremote exits.
@Josverl has done a lot of hard work and testing to find a DTR & RTS setting which works for (2) without causing problems due to (1), and has come up with a heuristic for detecting possible Espressif boards. The challenge is that pretty much any USB/Serial chip on the market has been connected to an ESP8266 or ESP32 at some point, and there's no way to tell what MCU is connected from the USB side.
There also cases like #17999 where an Espressif chip with a native USB implementation needs (1) in order to function correctly.
Approach in this PR
Instead, we can manually clear RTS before DTR when closing the port, to avoid the reset issue. Opening the port can use the default behaviour (RTS & DTR both set).
In mpremote, the transport close() is called from a finally block in the mpremote main module (via do_disconnect()) - so this should always happen provided the Python process isn't terminated by the OS.
Testing
On Linux my test process is:
If the resume works correctly then this should print
53
. If a reset occurs when mpremote exits then it will printNameError: name 'a' isn't defined
.Windows allocates a new COM port for each device, so made a batch file to only type the port name once:
Results:
Trade-offs and Alternatives