Skip to content

Commit d37c38a

Browse files
committed
feat(watchdog_reset): Add a new watchdog_reset option working even in USB modes
1 parent ef407ed commit d37c38a

File tree

14 files changed

+71
-57
lines changed

14 files changed

+71
-57
lines changed

docs/en/esptool/advanced-options.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The ``--after`` argument allows you to specify whether the chip should be reset
3535
:esp8266: * ``--after soft_reset`` runs the user firmware, but any subsequent reset will return to the serial bootloader. This was the reset behaviour in esptool v1.x.
3636
* ``--after no_reset`` leaves the chip in the serial bootloader, no reset is performed.
3737
* ``--after no_reset_stub`` leaves the chip in the stub bootloader, no reset is performed.
38+
:not esp8266 and not esp32 and not esp32h2 and not esp32c6: * ``--after watchdog_reset`` hard-resets the chip by triggering an internal watchdog reset. This is useful when the RTS control line is not available, especially in the USB-OTG and USB-Serial/JTAG modes. Use this if a chip is getting stuck in download mode when using the default reset method in USB-Serial/JTAG mode. Using this may cause the port to re-enumerate on Linux (e.g. ``/dev/ttyACM0`` -> ``/dev/ttyACM1``).
3839

3940

4041
Connect Loop

esptool/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,13 @@ def main(argv=None, esp=None):
151151
"--after",
152152
"-a",
153153
help="What to do after esptool.py is finished",
154-
choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"],
154+
choices=[
155+
"hard_reset",
156+
"soft_reset",
157+
"no_reset",
158+
"no_reset_stub",
159+
"watchdog_reset",
160+
],
155161
default=os.environ.get("ESPTOOL_AFTER", "hard_reset"),
156162
)
157163

@@ -1065,6 +1071,15 @@ def flash_xmc_startup():
10651071
esp.soft_reset(False)
10661072
elif args.after == "no_reset_stub":
10671073
print("Staying in flasher stub.")
1074+
elif args.after == "watchdog_reset":
1075+
if esp.secure_download_mode:
1076+
print(
1077+
"WARNING: Watchdog hard reset is not supported in Secure Download "
1078+
"Mode, attempting classic hard reset instead."
1079+
)
1080+
esp.hard_reset()
1081+
else:
1082+
esp.watchdog_reset()
10681083
else: # args.after == 'no_reset'
10691084
print("Staying in bootloader.")
10701085
if esp.IS_STUB:

esptool/loader.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,13 @@ def soft_reset(self, stay_in_bootloader):
15811581
# in the stub loader
15821582
self.command(self.ESP_RUN_USER_CODE, wait_response=False)
15831583

1584+
def watchdog_reset(self):
1585+
print(
1586+
f"WARNING: Watchdog hard reset is not supported on {self.CHIP_NAME}, "
1587+
"attempting classic hard reset instead."
1588+
)
1589+
self.hard_reset()
1590+
15841591

15851592
def slip_reader(port, trace_function):
15861593
"""Generator to read SLIP packets from a serial port.

esptool/targets/esp32c2.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,6 @@ def _post_connect(self):
133133
self.stub_is_disabled = True
134134
self.IS_STUB = False
135135

136-
def hard_reset(self):
137-
ESPLoader.hard_reset(self)
138-
139136
""" Try to read (encryption key) and check if it is valid """
140137

141138
def is_flash_encryption_key_valid(self):

esptool/targets/esp32c3.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -253,21 +253,15 @@ def _post_connect(self):
253253
if not self.sync_stub_detected: # Don't run if stub is reused
254254
self.disable_watchdogs()
255255

256-
def hard_reset(self):
257-
if self.uses_usb_jtag_serial():
258-
self.rtc_wdt_reset()
259-
sleep(0.5) # wait for reset to take effect
260-
else:
261-
ESPLoader.hard_reset(self)
262-
263-
def rtc_wdt_reset(self):
264-
print("Hard resetting with RTC WDT...")
256+
def watchdog_reset(self):
257+
print("Hard resetting with a watchdog...")
265258
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, self.RTC_CNTL_WDT_WKEY) # unlock
266259
self.write_reg(self.RTC_CNTL_WDTCONFIG1_REG, 2000) # set WDT timeout
267260
self.write_reg(
268261
self.RTC_CNTL_WDTCONFIG0_REG, (1 << 31) | (5 << 28) | (1 << 8) | 2
269262
) # enable WDT
270263
self.write_reg(self.RTC_CNTL_WDTWPROTECT_REG, 0) # lock
264+
sleep(0.5) # wait for reset to take effect
271265

272266
def check_spi_connection(self, spi_connection):
273267
if not set(spi_connection).issubset(set(range(0, 22))):

esptool/targets/esp32c5.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import time
77
from typing import Dict
88

9+
from .esp32c3 import ESP32C3ROM
910
from .esp32c6 import ESP32C6ROM
1011
from ..loader import ESPLoader
1112
from ..util import FatalError
@@ -162,6 +163,10 @@ def check_spi_connection(self, spi_connection):
162163
"consider using other pins for SPI flash connection."
163164
)
164165

166+
def watchdog_reset(self):
167+
# Watchdog reset disabled in parent (ESP32-C6) ROM, re-enable it
168+
ESP32C3ROM.watchdog_reset(self)
169+
165170

166171
class ESP32C5StubLoader(ESP32C5ROM):
167172
"""Access class for ESP32C5 stub loader, runs on top of ROM.

esptool/targets/esp32c6.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ def check_spi_connection(self, spi_connection):
192192
"consider using other pins for SPI flash connection."
193193
)
194194

195-
def hard_reset(self):
195+
def watchdog_reset(self):
196196
# Bug in the USB-Serial/JTAG controller can cause the port to disappear
197-
# if the chip is reset with RTC WDT, do a classic reset
198-
ESPLoader.hard_reset(self)
197+
# if watchdog reset happens, disable it on ESP32-C6
198+
ESPLoader.watchdog_reset(self)
199199

200200

201201
class ESP32C6StubLoader(ESP32C6ROM):

esptool/targets/esp32c61.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import struct
66
from typing import Dict
77

8+
from .esp32c3 import ESP32C3ROM
89
from .esp32c6 import ESP32C6ROM
910

1011

@@ -118,6 +119,10 @@ def read_mac(self, mac_type="BASE_MAC"):
118119
}
119120
return macs.get(mac_type, None)
120121

122+
def watchdog_reset(self):
123+
# Watchdog reset disabled in parent (ESP32-C6) ROM, re-enable it
124+
ESP32C3ROM.watchdog_reset(self)
125+
121126

122127
class ESP32C61StubLoader(ESP32C61ROM):
123128
"""Access class for ESP32C61 stub loader, runs on top of ROM.

esptool/targets/esp32h2.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ def get_crystal_freq(self):
7676
# ESP32H2 XTAL is fixed to 32MHz
7777
return 32
7878

79-
def hard_reset(self):
80-
# RTC WDT reset not available, do a classic reset
81-
ESPLoader.hard_reset(self)
79+
# Watchdog reset is not supported on ESP32-H2
80+
def watchdog_reset(self):
81+
ESPLoader.watchdog_reset(self)
8282

8383
def check_spi_connection(self, spi_connection):
8484
if not set(spi_connection).issubset(set(range(0, 28))):

esptool/targets/esp32h21.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66

77
from .esp32h2 import ESP32H2ROM
8-
from ..loader import ESPLoader
98
from ..util import FatalError
109

1110

@@ -46,10 +45,6 @@ def get_crystal_freq(self):
4645
# ESP32H21 XTAL is fixed to 32MHz
4746
return 32
4847

49-
def hard_reset(self):
50-
# RTC WDT reset not available, do a classic reset
51-
ESPLoader.hard_reset(self)
52-
5348
def check_spi_connection(self, spi_connection):
5449
if not set(spi_connection).issubset(set(range(0, 28))):
5550
raise FatalError("SPI Pin numbers must be in the range 0-27.")

0 commit comments

Comments
 (0)