diff --git a/docs/en/esptool/configuration-file.rst b/docs/en/esptool/configuration-file.rst index c538c608a..d93037495 100644 --- a/docs/en/esptool/configuration-file.rst +++ b/docs/en/esptool/configuration-file.rst @@ -109,6 +109,8 @@ Complete list configurable options: +------------------------------+-----------------------------------------------------------+----------+ | custom_reset_sequence | Custom reset sequence for resetting into the bootloader | | +------------------------------+-----------------------------------------------------------+----------+ +| retry_open_serial | Retry opening the serial port indefinitely | False | ++------------------------------+-----------------------------------------------------------+----------+ Custom Reset Sequence --------------------- diff --git a/esptool/__init__.py b/esptool/__init__.py index 46dad3582..146e9eec9 100644 --- a/esptool/__init__.py +++ b/esptool/__init__.py @@ -35,6 +35,7 @@ import os import shlex import sys +import termios import time import traceback @@ -66,7 +67,13 @@ write_mem, ) from esptool.config import load_config_file -from esptool.loader import DEFAULT_CONNECT_ATTEMPTS, StubFlasher, ESPLoader, list_ports +from esptool.loader import ( + DEFAULT_CONNECT_ATTEMPTS, + StubFlasher, + ESPLoader, + list_ports, + cfg, +) from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM from esptool.util import ( FatalError, @@ -77,6 +84,9 @@ import serial +# Retry opening the serial port indefinitely +DEFAULT_RETRY_OPEN_SERIAL = cfg.getboolean("retry_open_serial", False) + def main(argv=None, esp=None): """ @@ -768,6 +778,9 @@ def add_spi_flash_subparsers( port=args.port, connect_attempts=args.connect_attempts, initial_baud=initial_baud, + retry_open_serial=os.environ.get( + "ESPTOOL_RETRY_OPEN_SERIAL", DEFAULT_RETRY_OPEN_SERIAL + ), chip=args.chip, trace=args.trace, before=args.before, @@ -1092,11 +1105,49 @@ def expand_file_arguments(argv): return argv +def get_default_specific_connected_device( + chip, each_port, initial_baud, trace, before, connect_attempts, retry_open_serial +): + retry_attempts = 0 + _esp = None + while True: + try: + chip_class = CHIP_DEFS[chip] + _esp = chip_class(each_port, initial_baud, trace) + _esp.connect(before, connect_attempts) + if retry_attempts > 0: + # break the retrying line + print("") + return _esp + except ( + FatalError, + serial.serialutil.SerialException, + IOError, + OSError, + termios.error, + ) as e: + if not retry_open_serial: + raise + if _esp and _esp._port: + _esp._port.close() + _esp = None + if retry_attempts == 0: + print(e) + print("Retrying failed connection ", end="", flush=True) + else: + if retry_attempts % 9 == 0: + # print a dot every second + print(".", end="", flush=True) + time.sleep(0.1) + retry_attempts += 1 + + def get_default_connected_device( serial_list, port, connect_attempts, initial_baud, + retry_open_serial=False, chip="auto", trace=False, before="default_reset", @@ -1110,9 +1161,15 @@ def get_default_connected_device( each_port, initial_baud, before, trace, connect_attempts ) else: - chip_class = CHIP_DEFS[chip] - _esp = chip_class(each_port, initial_baud, trace) - _esp.connect(before, connect_attempts) + _esp = get_default_specific_connected_device( + chip, + each_port, + initial_baud, + trace, + before, + connect_attempts, + retry_open_serial and port is not None, + ) break except (FatalError, OSError) as err: if port is not None: @@ -1229,6 +1286,7 @@ def _main(): try: main() except FatalError as e: + print(traceback.format_exc()) print(f"\nA fatal error occurred: {e}") sys.exit(2) except serial.serialutil.SerialException as e: diff --git a/esptool/config.py b/esptool/config.py index 5566becca..56f27d5e3 100644 --- a/esptool/config.py +++ b/esptool/config.py @@ -20,6 +20,7 @@ "write_block_attempts", "reset_delay", "custom_reset_sequence", + "retry_open_serial", ]