Skip to content

Commit

Permalink
Resolve serial flash issues for boards with reset circuit bugs
Browse files Browse the repository at this point in the history
Improves connection compatibility - Attempts normal connection, then
attempts connection with an extended reset delay and a delay that triggers
a watchdog timeout (esp32r0-only) to more reliably enter download mode.

Fallback design - initial connect is without workarounds - if that fails, the
extra delays are used.

Resolves issue espressif#136
  • Loading branch information
MartyMacGyver committed Jan 30, 2017
1 parent a936522 commit 940a248
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 40 deletions.
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -291,7 +291,6 @@ The `--before` argument allows you to specify whether the chip needs resetting i

* `--before default_reset` is the default, which uses DTR & RTS serial control lines (see [Entering The Bootloader](#entering-the-bootloader)) to try and reset the chip into bootloader mode.
* `--before no_reset` will skip any DTR/RTS control signals and just start sending a serial synchronisation command to the chip. This is useful if your chip doesn't have DTR/RTS, or for some serial interfaces (like Arduino board onboard serial) which behave differently when DTR/RTS are toggled.
* `--before esp32r0` is a special reset sequence that can work around an automatic reset bug when using Windows with first generation ESP32 chips.

#### Reset After Operation

Expand Down
94 changes: 55 additions & 39 deletions esptool.py
Expand Up @@ -281,6 +281,54 @@ def sync(self):
for i in range(7):
self.command()

def _connect_attempt(self, mode='default_reset', latency_delay=False, esp32r0_delay=False):
""" A single connection attempt, with esp32r0 workaround options """
# esp32r0_delay and latency_delay are workarounds for a bug with
# the most common auto reset circuit and Windows, if the EN
# pin on the dev board does not have enough capacitance.
# Details: https://github.com/espressif/esptool/issues/136
last_error = None

# issue reset-to-bootloader:
# RTS = either CH_PD/EN or nRESET (both active low = chip in reset
# DTR = GPIO0 (active low = boot to flasher)
#
# DTR & RTS are active low signals,
# ie True = pin @ 0V, False = pin @ VCC.
if mode != 'no_reset':
self._port.setDTR(False) # IO0=HIGH
self._port.setRTS(True) # EN=LOW, chip in reset
time.sleep(0.1)
if latency_delay:
time.sleep(1.0) # Sleep longer during reset
self._port.setDTR(True) # IO0=LOW
self._port.setRTS(False) # EN=HIGH, chip out of reset
if esp32r0_delay:
# Sleep longer after reset.
# This workaround only works on revision 0 ESP32 chips,
# it exploits a silicon bug spurious watchdog reset.
time.sleep(0.4) # allow watchdog reset to occur
time.sleep(0.05)
self._port.setDTR(False) # IO0=HIGH, done

self._port.timeout = 0.1
for _ in range(5):
try:
self.flush_input()
self._port.flushOutput()
self.sync()
self._port.timeout = 5
return None
except FatalError as e:
if latency_delay or esp32r0_delay:
print('_', end='')
else:
print('.', end='')
sys.stdout.flush()
time.sleep(0.05)
last_error = e
return last_error

def connect(self, mode='default_reset'):
""" Try connecting repeatedly until successful, or giving up """
print('Connecting...', end='')
Expand All @@ -289,44 +337,12 @@ def connect(self, mode='default_reset'):

try:
for _ in range(10):
# issue reset-to-bootloader:
# RTS = either CH_PD/EN or nRESET (both active low = chip in reset
# DTR = GPIO0 (active low = boot to flasher)
#
# DTR & RTS are active low signals,
# ie True = pin @ 0V, False = pin @ VCC.
if mode != 'no_reset':
self._port.setDTR(False) # IO0=HIGH
self._port.setRTS(True) # EN=LOW, chip in reset
time.sleep(0.05)
self._port.setDTR(True) # IO0=LOW
self._port.setRTS(False) # EN=HIGH, chip out of reset
if mode == 'esp32r0':
# this is a workaround for a bug with the most
# common auto reset circuit and Windows, if the EN
# pin on the dev board does not have enough
# capacitance. This workaround only works on
# revision 0 ESP32 chips, it exploits a silicon
# bug spurious watchdog reset.
#
# Details: https://github.com/espressif/esptool/issues/136
time.sleep(0.4) # allow watchdog reset to occur
time.sleep(0.05)
self._port.setDTR(False) # IO0=HIGH, done

self._port.timeout = 0.1
for _ in range(4):
try:
self.flush_input()
self._port.flushOutput()
self.sync()
self._port.timeout = 5
return
except FatalError as e:
print('.', end='')
sys.stdout.flush()
time.sleep(0.05)
last_error = e
last_error = self._connect_attempt(mode=mode, latency_delay=False, esp32r0_delay=False)
if last_error is None:
return
last_error = self._connect_attempt(mode=mode, latency_delay=True, esp32r0_delay=True)
if last_error is None:
return
finally:
print('') # end 'Connecting...' line
raise FatalError('Failed to connect to %s: %s' % (self.CHIP_NAME, last_error))
Expand Down Expand Up @@ -1850,7 +1866,7 @@ def main():
parser.add_argument(
'--before',
help='What to do before connecting to the chip',
choices=['default_reset', 'no_reset', 'esp32r0'],
choices=['default_reset', 'no_reset'],
default=os.environ.get('ESPTOOL_BEFORE', 'default_reset'))

parser.add_argument(
Expand Down

0 comments on commit 940a248

Please sign in to comment.