ESP32 Reset To Bootloader Issues on Windows #136

Open
negativekelvin opened this Issue Oct 18, 2016 · 22 comments

Projects

None yet

6 participants

@negativekelvin

On windows, it seems like there are timing issues with the reset to bootloader functions using the DTR and RTS circuit for esp32 because setDTR and setRTS are sent separately. Possible fix:

            # issue reset-to-bootloader:
            # RTS = either CH_PD or nRESET (both active low = chip in reset)
            # DTR = GPIO0 (active low = boot to flasher)
            self._port._dtr_state = False
            self._port._rts_state = True
            self._port._reconfigure_port()
            time.sleep(0.05)
            self._port._dtr_state = True
            self._port._rts_state = False
            self._port._reconfigure_port()
            time.sleep(0.05)
            self._port.setDTR(False)

I haven't scoped this or tested it because my v1 board has the transistor bug.

@negativekelvin negativekelvin changed the title from Reset To Bootloader Issues on Windows to ESP32 Reset To Bootloader Issues on Windows Oct 18, 2016
@projectgus
Collaborator

Thanks @negativekelvin. I don't know if you're following the forum thread, but I'm guessing you are.
http://esp32.com/viewtopic.php?f=2&t=334&p=1503#p1497

I considered this solution as well (although I didn't test it either.) The problem is, I'm not comfortable relying on the private interface of pyserial. At minimum, it's necessary to check it hasn't changed over any previous supported pyserial versions. But even if it works with all those, if the private interface changes in the future then it could break esptool without warning.

A hacky but somewhat safer way would be to check all these properties exist with hasattr() before calling them, and fail back to the public API instead. But this is still super hacky.

The safest, most maintainable, fix that I can think of is probably to call the win32 GetCommState/SetCommState API functions directly. This is a stable public API, and the only private part of pyserial it needs to touch is the port handle. I still don't like that idea, but it seems least likely to suddenly break.

@projectgus
Collaborator

Or to submit a patch to pyserial, some variant of the existing apply_settings() that applies all settings as an "atomic" operation.

@negativekelvin

Yes, that makes sense. I don't know if those properties and methods are very likely to change, so yes I like the check and fall back strategy until pyserial can support it.

@negativekelvin
negativekelvin commented Oct 18, 2016 edited

However it also looks like _reconfigure_port() only updates the RTS/DTR lines on windows, not other platforms. So would have to add a platform check before calling.

@bvernoux
bvernoux commented Oct 18, 2016 edited

For full details on this issue I have done a full analysis of the problem on Win7 see
http://esp32.com/viewtopic.php?f=2&p=1511#p1511

I also confirm that by modifying esptool.py
from

            self._port.setDTR(False)
            self._port.setRTS(True)
            time.sleep(0.05)
            self._port.setDTR(True)
            self._port.setRTS(False)
            time.sleep(0.05)
            self._port.setDTR(False)

to

            self._port.setDTR(False)    # GPIO0 -> 1
            self._port.setRTS(True  )   # RST -> 0
            self._port.setDTR(True  )   # GPIO0 -> 0
            time.sleep(0.05)
            self._port.setRTS(False )   # RST -> 1
            time.sleep(0.1) 

That fix the problem on win7 (thanks to rudi post http://esp32.com/viewtopic.php?f=13&p=1512#p1512)

@bvernoux bvernoux referenced this issue Oct 19, 2016
Closed

DTR #130

@bvernoux
bvernoux commented Oct 20, 2016 edited

In fact after analysis of rudi patch it is pure luck (thanks to watchdog and glitch on EN signal)
For more details on Rudi patch see Saleae Logic 1.2.10 capture:

I have simplified it like that
esptool.py modifications (MyPatch):
from

            self._port.setRTS(True)
            time.sleep(0.05)
            self._port.setDTR(True)
            self._port.setRTS(False)
            time.sleep(0.05)
            self._port.setDTR(False)

to

            self._port.setDTR(False) # EN=0 and IO0=0
            time.sleep(0.05)
            self._port.setRTS(False) # EN=1 and IO0=1(glitch on IO0 during 1ms)
            self._port.setDTR(True)  # EN=1 and IO0=0

The main problem is the glitch on IO0 during 1.1ms which does not allow to enter download mode as IO0 is sampled by ESP32 during this glitch but we are lucky that we have a reset by watchdog after 0.385s (with message RTCWDT_RTC_RESET) which then boot in download mode because EN=1 and IO0=0

For more details see Saleae Logic 1.2.10 capture of MyPatch:

For information MyPatch is a bit faster (300ms) compared to Rudi version and keep EN to 0 during at least 50ms(and not 3.5ms like Rudi which in fact is a glitch) and after multiple test my patch never fail with Win7 64bits and also under VirtualBox with Xubuntu 14.04
If other can give feedback it will be great

@negativekelvin

@bvernoux are you able to capture the waveforms for the method using reconfigure_port in the first post on windows?

@projectgus
Collaborator

I just tried a different fix for this issue (1f5d9d2) which should be equivalent to calling _reconfigure_port, and I thought would definitely correct the issue.

Unfortunately the timing seems to be the same (trace attached), even when both DTR & RTS are set in the same Windows API call. Which I think means the CP2102 Windows driver is doing this, and there's no way to change that pulse in software.

2016-10-21-143743_1680x981_scrot

@negativekelvin

That is disappointing. Time to ask silicon labs?

@negativekelvin

we are lucky that we have a reset by watchdog after 0.385s

What is the cause of this?

@projectgus
Collaborator
projectgus commented Oct 21, 2016 edited

we are lucky that we have a reset by watchdog after 0.385s
What is the cause of this?

It's a silicon bug, that will be fixed at some point.

A solution that seems to work reliably is to add a ~2.2uF capacitor (I think 470nF-2.2uF range should be suitable) to the EN pin (between EN and GND), so it rises slower (there is a ~12K pullup on this pin.) This is not super desirable, as it requires a hardware change, but it works.

@bvernoux

I confirm adding a 2.2uF capacitor on EN (connected between EN and GND) fix the problem
esp32-devkitc_fix_boot_download_esptool_with_2_2uf_capacitor_on_en

@krzychb
krzychb commented Nov 5, 2016

@projectgus and all - thank you guys!
No more fiddling with EN / Boot buttons.
Life is much easier with this simple hack 😄
Krzysztof

img_2448

@ladyada
ladyada commented Nov 13, 2016

hiya - got bit by this bug, and found a few things helped. capacitor helps but easier to change the second time.sleep(0.05) to time.sleep(0.5) e.g. https://github.com/adafruit/arduino-esp32/blob/master/tools/esptool.py#L279
that along with a formal 'reset' procedure to swat the ESP32 back into 'user mode'
https://github.com/adafruit/arduino-esp32/blob/master/tools/esptool.py#L296
has made developing code work great on win7 + CP210x... plz re-consider the wontfix? there's a lot of those DevKitC's in the world now :) i can test on mac/linux too

@bvernoux

For information it is already fixed in actual branch even without the capacitor => thanks to an ESP32 silicon bug with reset by watchdog after 0.385s
I do not see why this modification will improve anything.
Do you have a sigrok capture to show the difference with this patch ?

@projectgus
Collaborator
projectgus commented Nov 14, 2016 edited

Hi @bvernoux,

For information it is already fixed in actual branch even without the capacitor

I don't believe this is fixed in any branch at the moment, without adding either a capacitor or the time.sleep(0.5) modification that @ladyada mentions.

Hi @ladyada,

plz re-consider the wontfix? there's a lot of those DevKitC's in the world now :)

The problem I see is because the 0.5s delay happens to rely on a silicon bug, which is already fixed in the forthcoming chip revision. When the updated chips come out, the bug will bite everyone again (if the capacitor on EN hasn't been changed in the board design) and it will also take longer any time esptool.py fails to connect, due to the extra delays.

There are perhaps some other ways to work around this, either a command line option like --variant or --board to let people specify they have the gen1 silicon, or a long-standing suggestion (#27) to read back the "header" message that the chip prints in order to detect boot mode failures earlier.

@ladyada
ladyada commented Nov 14, 2016 edited

yah ok - i didn't realize it was a silicon issue on the esp32 (thought it was cp210x) in which case... i agree y'all should keep as wontfix.
i will update the devkitc product page to tell people to add a capacitor if they're having upload probs on windows

@projectgus
Collaborator

yah ok - i didn't realize it was a silicon issue on the esp32 (thought it was cp210x) in which case... i agree y'all should keep as wontfix.

Just to be sure we're on the same page, the silicon issue on esp32 is what allows the extra time.sleep(0.5) delay to work as a workaround (the chip resets twice due to an unexpected WDT), rather than the cause of this bug. The cause of this bug is an interaction between the reset circuit timing and Windows driver behaviour (probably CP210x-specific but I haven't confirmed this.)

i will update the devkitc product page to tell people to add a capacitor if they're having upload probs on windows

That would be awesome, thanks very much. :)

@ladyada
ladyada commented Nov 14, 2016

ahh ok yes reading thru more carefully i understand the double-bug interaction! too funny - ok thanks!

@rojer
Contributor
rojer commented Dec 23, 2016

just found my way to this issue. not before spending some time banging my head against the wall, though :)

the workaround seems fine, i just want to confirm: devkit hardware will be revised to increase the capacitor value, right? hopefully, in time for new chip revision which fixes the glitch.

@projectgus
Collaborator

Yes, newer Espressif DevKits will fix this issue via increased capacitance on the EN pin.

@projectgus
Collaborator

esptool v2.0 (current master, and the version in esp-idf) has a new --after esp32r0 option which will work around this issue for ESP32 original revision chips.

The option can also be selected under "make menuconfig" in esp-idf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment