-
-
Notifications
You must be signed in to change notification settings - Fork 8.7k
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
mpremote: Workaround ESP reset quirk at disconnect time. #18001
Conversation
|
@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.39% 98.39%
=======================================
Files 171 171
Lines 22289 22289
=======================================
Hits 21931 21931
Misses 358 358 ☔ 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 ? |
Indeed! In fact, this is the wiring convention (and problem) that inspired the first reset circuit for ESP8266, on the original NodeMCU board circa 2014/15! I agree it'd be useful to have this configurable for mpremote. However, that's somewhat separate to this problem (already mpremote will assert DTR and RTS from a MacOS or Linux host, Windows is the only one that doesn't due to the existing workaround). FWIW, over the years I was the esptool maintainer I don't remember any complete dev boards with USB but no transistor logic, although I'm sure you're right that there are some out there (I do remember some with crappy implementations of the transistor logic). I've seen the "no reset circuit" pattern crop up a lot in consumer products or module breakouts where there might be a header that exposes RX, TX, IO0 and RST pins directly.
Sorry about that, I've edited the table to explain better. A bunch of the "todos" are a bit vague because they're prompts for me to go rummage through my embarassingly large stock of ESP boards and find examples with all the possible interface chips! Once I find one (or someone else reports a result) then I'll update the table with the actual board details.
If/when you have time then that'd be great, thank you! I'll do some testing with other boards (and maybe other host configs) next week, as well. |
assert DTR and RTS means that the level at the output of the USB/UART bridge is low. So these "non-conforming" board do not work with mpremote and Linux. |
Yes indeed, that's why I'm saying that it's a separate concern to the problem that this PR is addressing. (To be clear: If this PR is merged then those configurations may stop working on Windows, but they already don't work from Linux or MacOS. This PR would at least make the behaviour consistent across different hosts, and then if necessary we can look at adding a config setting or an option that allows those particular boards to work from all hosts.) |
|
You have to be careful messing around with setting the states of DTR and RTS as they control the ESP32's boot mode and restarting. I know that this is hard wired into the hardware USB-CDC on the ESP32's that support native USB. I do not know how this plays into the software stack that MicroPython is using but my guess is going to be that the software stack needs to watch those signals and to perform reboots and or setting the boot mode as needed depending on the state of DTR and RTS. at the very bottom. and then if you read here it will shed some more insight on those lines. Now I know the above link is outlined for using a UART to USB bridge IC but the same applies when using the native USB. I also think that this might be a tripping up point... |
I appreciate your concern, but I'm well aware. Without putting too fine a point on this, I was the maintainer of esptool from 2015 to 2021 (including five years employed by Espressif). I wrote the sentence from the esptool documentation that you've quoted from (although that was a wiki page, back then). I'm not saying this PR is infallible (hence marking as draft and asking for testers), but I am saying that I'm across the issue of DTR and RTS on Espressif boards. |
|
I improved my Windows VM test setup by passing through the USB controller PCIe device to the VM - this should give equivalent USB behaviour to a native host. This revealed a quirk in the Silicon Labs driver that needs a workaround to avoid triggering a hard reset the first time the port is opened after power on (but the port was OK if opened additional times after that, very odd). Was able to do a bunch more testing with various boards, have updated the table at the top. So far so good. If anyone has unusual boards, additional USB/Serial chips, or unusual Windows setups then I'd greatly appreciate any test results you can provide. 🙏 |
|
Sharing my test results
Updated with WLS2 tests No longer relevant
FAIL-1:FAIL-2:mpremote.transport.TransportError: could not enter raw repl FAIL-3:works on UART interface |
|
Thanks @Josverl, I really appreciate your help with this. That's interesting that all three failures you have are connecting to the Espressif USB/JTAG hardware peripheral. I'll do some more testing with different MP versions on my board, try to reproduce.
Just to check: I don't have a commit 5a7200217b in my repo anywhere - did you perhaps rebase this PR branch? (I don't expect it's the difference, but worth ruling it out.) |
|
Thanks for catching my mistake. I repeated the Windows tests with the mpremote version from this PR |
Well, that's good news at least, I was really not looking forward to trying to figure out what was different between my Windows test setup and yours! Thanks for testing twice. 😁 Do you have any outstanding concerns with this approach (aside from the desirability of adding a manual override option, which I fully agree we should do)? (For my own 2c: I'm not super happy that we turned out to need a SiLabs CP210x driver workaround on Windows hosts, although on the plus side the workaround should lead to consistent CP210x behaviour on all host OSes - and the behaviour is valid whether or not the CP210x is connected to an Espressif SoC or something else that might expect DTR or RTS to be used in a conventional way.) |
LGTM and then we can add the option to apply rts/dtr tweaks from options/environment/config files separately. ( I do have one board ( ESP32-PICO-KIT-V4 - 4517) that needs a hard reset before mpremote will connect - but that was also with mpremote 1.25.0 - so I think that is a board defect and should not hold this back |
|
This can now be rebased. |
2416c6f to
868c5f9
Compare
|
Rebased, did a couple of quick re-tests (although the only mpremote change after rebase is moving the import block to the top of the file). |
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. One additional workaround is needed to prevent a spurious reset first time a Silicon Labs CP210x-based ESP board is opened by mpremote after enumeration. Signed-off-by: Angus Gratton <angus@redyak.com.au>
868c5f9 to
3dd8073
Compare
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 mostly use the default behaviour (RTS & DTR both set). So far have only found one exception: on Windows the Silicon Labs CP210x driver toggles DTR and RTS with a long delay in between the first time the port is opened after a reset, which triggers a hard reset the first time mpremote tries to connect - have worked around this specific case on connection.
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 & WSL 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
Follow Up Work