1# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton,
2# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
3#
4# SPDX-License-Identifier: GPL-2.0-or-later
5# PYTHON_ARGCOMPLETE_OK
6__all__ = [
7    "chip_id",
8    "detect_chip",
9    "dump_mem",
10    "elf2image",
11    "erase_flash",
12    "erase_region",
13    "flash_id",
14    "get_security_info",
15    "image_info",
16    "load_ram",
17    "make_image",
18    "merge_bin",
19    "read_flash",
20    "read_flash_status",
21    "read_mac",
22    "read_mem",
23    "run",
24    "verify_flash",
25    "version",
26    "write_flash",
27    "write_flash_status",
28    "write_mem",
29]
30
31__version__ = "4.8.1"
32
33import argparse
34import inspect
35import os
36import shlex
37import sys
38import time
39import traceback
40
41from esptool.bin_image import intel_hex_to_bin
42from esptool.cmds import (
43    DETECTED_FLASH_SIZES,
44    chip_id,
45    detect_chip,
46    detect_flash_size,
47    dump_mem,
48    elf2image,
49    erase_flash,
50    erase_region,
51    flash_id,
52    read_flash_sfdp,
53    get_security_info,
54    image_info,
55    load_ram,
56    make_image,
57    merge_bin,
58    read_flash,
59    read_flash_status,
60    read_mac,
61    read_mem,
62    run,
63    verify_flash,
64    version,
65    write_flash,
66    write_flash_status,
67    write_mem,
68)
69from esptool.config import load_config_file
70from esptool.loader import (
71    DEFAULT_CONNECT_ATTEMPTS,
72    DEFAULT_OPEN_PORT_ATTEMPTS,
73    StubFlasher,
74    ESPLoader,
75    list_ports,
76)
77from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM
78from esptool.util import (
79    FatalError,
80    NotImplementedInROMError,
81    flash_size_bytes,
82    strip_chip_name,
83)
84from itertools import chain, cycle, repeat
85
86import serial
87
88
89def main(argv=None, esp=None):
90    """
91    Main function for esptool
92
93    argv - Optional override for default arguments parsing (that uses sys.argv),
94    can be a list of custom arguments as strings. Arguments and their values
95    need to be added as individual items to the list
96    e.g. "-b 115200" thus becomes ['-b', '115200'].
97
98    esp - Optional override of the connected device previously
99    returned by get_default_connected_device()
100    """
101
102    external_esp = esp is not None
103
104    parser = argparse.ArgumentParser(
105        description="esptool.py v%s - Espressif chips ROM Bootloader Utility"
106        % __version__,
107        prog="esptool",
108    )
109
110    parser.add_argument(
111        "--chip",
112        "-c",
113        help="Target chip type",
114        type=strip_chip_name,
115        choices=["auto"] + CHIP_LIST,
116        default=os.environ.get("ESPTOOL_CHIP", "auto"),
117    )
118
119    parser.add_argument(
120        "--port",
121        "-p",
122        help="Serial port device",
123        default=os.environ.get("ESPTOOL_PORT", None),
124    )
125
126    parser.add_argument(
127        "--baud",
128        "-b",
129        help="Serial port baud rate used when flashing/reading",
130        type=arg_auto_int,
131        default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD),
132    )
133
134    parser.add_argument(
135        "--port-filter",
136        action="append",
137        help="Serial port device filter, can be vid=NUMBER, pid=NUMBER, name=SUBSTRING",
138        type=str,
139        default=[],
140    )
141
142    parser.add_argument(
143        "--before",
144        help="What to do before connecting to the chip",
145        choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"],
146        default=os.environ.get("ESPTOOL_BEFORE", "default_reset"),
147    )
148
149    parser.add_argument(
150        "--after",
151        "-a",
152        help="What to do after esptool.py is finished",
153        choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"],
154        default=os.environ.get("ESPTOOL_AFTER", "hard_reset"),
155    )
156
157    parser.add_argument(
158        "--no-stub",
159        help="Disable launching the flasher stub, only talk to ROM bootloader. "
160        "Some features will not be available.",
161        action="store_true",
162    )
163
164    # --stub-version can be set with --no-stub so the tests wouldn't fail if this option is implied globally
165    parser.add_argument(
166        "--stub-version",
167        default=os.environ.get("ESPTOOL_STUB_VERSION", StubFlasher.STUB_SUBDIRS[0]),
168        choices=StubFlasher.STUB_SUBDIRS,
169        # not a public option and is not subject to the semantic versioning policy
170        help=argparse.SUPPRESS,
171    )
172
173    parser.add_argument(
174        "--trace",
175        "-t",
176        help="Enable trace-level output of esptool.py interactions.",
177        action="store_true",
178    )
179
180    parser.add_argument(
181        "--override-vddsdio",
182        help="Override ESP32 VDDSDIO internal voltage regulator (use with care)",
183        choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES,
184        nargs="?",
185    )
186
187    parser.add_argument(
188        "--connect-attempts",
189        help=(
190            "Number of attempts to connect, negative or 0 for infinite. "
191            "Default: %d." % DEFAULT_CONNECT_ATTEMPTS
192        ),
193        type=int,
194        default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS),
195    )
196
197    subparsers = parser.add_subparsers(
198        dest="operation", help="Run esptool.py {command} -h for additional help"
199    )
200
201    def add_spi_connection_arg(parent):
202        parent.add_argument(
203            "--spi-connection",
204            "-sc",
205            help="Override default SPI Flash connection. "
206            "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers "
207            "to use for SPI flash (CLK,Q,D,HD,CS). Not supported with ESP8266.",
208            action=SpiConnectionAction,
209        )
210
211    parser_load_ram = subparsers.add_parser(
212        "load_ram", help="Download an image to RAM and execute"
213    )
214    parser_load_ram.add_argument(
215        "filename", help="Firmware image", action=AutoHex2BinAction
216    )
217
218    parser_dump_mem = subparsers.add_parser(
219        "dump_mem", help="Dump arbitrary memory to disk"
220    )
221    parser_dump_mem.add_argument("address", help="Base address", type=arg_auto_int)
222    parser_dump_mem.add_argument(
223        "size", help="Size of region to dump", type=arg_auto_int
224    )
225    parser_dump_mem.add_argument("filename", help="Name of binary dump")
226
227    parser_read_mem = subparsers.add_parser(
228        "read_mem", help="Read arbitrary memory location"
229    )
230    parser_read_mem.add_argument("address", help="Address to read", type=arg_auto_int)
231
232    parser_write_mem = subparsers.add_parser(
233        "write_mem", help="Read-modify-write to arbitrary memory location"
234    )
235    parser_write_mem.add_argument("address", help="Address to write", type=arg_auto_int)
236    parser_write_mem.add_argument("value", help="Value", type=arg_auto_int)
237    parser_write_mem.add_argument(
238        "mask",
239        help="Mask of bits to write",
240        type=arg_auto_int,
241        nargs="?",
242        default="0xFFFFFFFF",
243    )
244
245    def add_spi_flash_subparsers(
246        parent: argparse.ArgumentParser,
247        allow_keep: bool,
248        auto_detect: bool,
249        size_only: bool = False,
250    ):
251        """Add common parser arguments for SPI flash properties"""
252        extra_keep_args = ["keep"] if allow_keep else []
253
254        if auto_detect and allow_keep:
255            extra_fs_message = ", detect, or keep"
256            flash_sizes = ["detect", "keep"]
257        elif auto_detect:
258            extra_fs_message = ", or detect"
259            flash_sizes = ["detect"]
260        elif allow_keep:
261            extra_fs_message = ", or keep"
262            flash_sizes = ["keep"]
263        else:
264            extra_fs_message = ""
265            flash_sizes = []
266
267        if not size_only:
268            parent.add_argument(
269                "--flash_freq",
270                "-ff",
271                help="SPI Flash frequency",
272                choices=extra_keep_args
273                + [
274                    "80m",
275                    "60m",
276                    "48m",
277                    "40m",
278                    "30m",
279                    "26m",
280                    "24m",
281                    "20m",
282                    "16m",
283                    "15m",
284                    "12m",
285                ],
286                default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None),
287            )
288            parent.add_argument(
289                "--flash_mode",
290                "-fm",
291                help="SPI Flash mode",
292                choices=extra_keep_args + ["qio", "qout", "dio", "dout"],
293                default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"),
294            )
295
296        parent.add_argument(
297            "--flash_size",
298            "-fs",
299            help="SPI Flash size in MegaBytes "
300            "(1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB) "
301            "plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)" + extra_fs_message,
302            choices=flash_sizes
303            + [
304                "256KB",
305                "512KB",
306                "1MB",
307                "2MB",
308                "2MB-c1",
309                "4MB",
310                "4MB-c1",
311                "8MB",
312                "16MB",
313                "32MB",
314                "64MB",
315                "128MB",
316            ],
317            default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"),
318        )
319        add_spi_connection_arg(parent)
320
321    parser_write_flash = subparsers.add_parser(
322        "write_flash", help="Write a binary blob to flash"
323    )
324
325    parser_write_flash.add_argument(
326        "addr_filename",
327        metavar="<address> <filename>",
328        help="Address followed by binary filename, separated by space",
329        action=AddrFilenamePairAction,
330    )
331    parser_write_flash.add_argument(
332        "--erase-all",
333        "-e",
334        help="Erase all regions of flash (not just write areas) before programming",
335        action="store_true",
336    )
337
338    add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True)
339    parser_write_flash.add_argument(
340        "--no-progress", "-p", help="Suppress progress output", action="store_true"
341    )
342    parser_write_flash.add_argument(
343        "--verify",
344        help="Verify just-written data on flash "
345        "(mostly superfluous, data is read back during flashing)",
346        action="store_true",
347    )
348    parser_write_flash.add_argument(
349        "--encrypt",
350        help="Apply flash encryption when writing data "
351        "(required correct efuse settings)",
352        action="store_true",
353    )
354    # In order to not break backward compatibility,
355    # our list of encrypted files to flash is a new parameter
356    parser_write_flash.add_argument(
357        "--encrypt-files",
358        metavar="<address> <filename>",
359        help="Files to be encrypted on the flash. "
360        "Address followed by binary filename, separated by space.",
361        action=AddrFilenamePairAction,
362    )
363    parser_write_flash.add_argument(
364        "--ignore-flash-encryption-efuse-setting",
365        help="Ignore flash encryption efuse settings ",
366        action="store_true",
367    )
368    parser_write_flash.add_argument(
369        "--force",
370        help="Force write, skip security and compatibility checks. Use with caution!",
371        action="store_true",
372    )
373
374    compress_args = parser_write_flash.add_mutually_exclusive_group(required=False)
375    compress_args.add_argument(
376        "--compress",
377        "-z",
378        help="Compress data in transfer (default unless --no-stub is specified)",
379        action="store_true",
380        default=None,
381    )
382    compress_args.add_argument(
383        "--no-compress",
384        "-u",
385        help="Disable data compression during transfer "
386        "(default if --no-stub is specified)",
387        action="store_true",
388    )
389
390    subparsers.add_parser("run", help="Run application code in flash")
391
392    parser_image_info = subparsers.add_parser(
393        "image_info", help="Dump headers from a binary file (bootloader or application)"
394    )
395    parser_image_info.add_argument(
396        "filename", help="Image file to parse", action=AutoHex2BinAction
397    )
398    parser_image_info.add_argument(
399        "--version",
400        "-v",
401        help="Output format version (1 - legacy, 2 - extended)",
402        choices=["1", "2"],
403        default="1",
404    )
405
406    parser_make_image = subparsers.add_parser(
407        "make_image", help="Create an application image from binary files"
408    )
409    parser_make_image.add_argument("output", help="Output image file")
410    parser_make_image.add_argument(
411        "--segfile", "-f", action="append", help="Segment input file"
412    )
413    parser_make_image.add_argument(
414        "--segaddr",
415        "-a",
416        action="append",
417        help="Segment base address",
418        type=arg_auto_int,
419    )
420    parser_make_image.add_argument(
421        "--entrypoint",
422        "-e",
423        help="Address of entry point",
424        type=arg_auto_int,
425        default=0,
426    )
427
428    parser_elf2image = subparsers.add_parser(
429        "elf2image", help="Create an application image from ELF file"
430    )
431    parser_elf2image.add_argument("input", help="Input ELF file")
432    parser_elf2image.add_argument(
433        "--output",
434        "-o",
435        help="Output filename prefix (for version 1 image), "
436        "or filename (for version 2 single image)",
437        type=str,
438    )
439    parser_elf2image.add_argument(
440        "--version",
441        "-e",
442        help="Output image version",
443        choices=["1", "2", "3"],
444        default="1",
445    )
446    parser_elf2image.add_argument(
447        # it kept for compatibility
448        # Minimum chip revision (deprecated, consider using --min-rev-full)
449        "--min-rev",
450        "-r",
451        help=argparse.SUPPRESS,
452        type=int,
453        choices=range(256),
454        metavar="{0, ... 255}",
455        default=0,
456    )
457    parser_elf2image.add_argument(
458        "--min-rev-full",
459        help="Minimal chip revision (in format: major * 100 + minor)",
460        type=int,
461        choices=range(65536),
462        metavar="{0, ... 65535}",
463        default=0,
464    )
465    parser_elf2image.add_argument(
466        "--max-rev-full",
467        help="Maximal chip revision (in format: major * 100 + minor)",
468        type=int,
469        choices=range(65536),
470        metavar="{0, ... 65535}",
471        default=65535,
472    )
473    parser_elf2image.add_argument(
474        "--secure-pad",
475        action="store_true",
476        help="Pad image so once signed it will end on a 64KB boundary. "
477        "For Secure Boot v1 images only.",
478    )
479    parser_elf2image.add_argument(
480        "--secure-pad-v2",
481        action="store_true",
482        help="Pad image to 64KB, so once signed its signature sector will"
483        "start at the next 64K block. For Secure Boot v2 images only.",
484    )
485    parser_elf2image.add_argument(
486        "--elf-sha256-offset",
487        help="If set, insert SHA256 hash (32 bytes) of the input ELF file "
488        "at specified offset in the binary.",
489        type=arg_auto_int,
490        default=None,
491    )
492    parser_elf2image.add_argument(
493        "--dont-append-digest",
494        dest="append_digest",
495        help="Don't append a SHA256 digest of the entire image after the checksum. "
496        "This argument is not supported and ignored for ESP8266.",
497        action="store_false",
498        default=True,
499    )
500    parser_elf2image.add_argument(
501        "--use_segments",
502        help="If set, ELF segments will be used instead of ELF sections "
503        "to generate the image.",
504        action="store_true",
505    )
506    parser_elf2image.add_argument(
507        "--flash-mmu-page-size",
508        help="Change flash MMU page size.",
509        choices=["64KB", "32KB", "16KB", "8KB"],
510    )
511    parser_elf2image.add_argument(
512        "--pad-to-size",
513        help="The block size with which the final binary image after padding "
514        "must be aligned to. Value 0xFF is used for padding, similar to erase_flash",
515        default=None,
516    )
517    parser_elf2image.add_argument(
518        "--ram-only-header",
519        help="Order segments of the output so IRAM and DRAM are placed at the "
520        "beginning and force the main header segment number to RAM segments "
521        "quantity. This will make the other segments invisible to the ROM "
522        "loader. Use this argument with care because the ROM loader will load "
523        "only the RAM segments although the other segments being present in "
524        "the output. Implies --dont-append-digest",
525        action="store_true",
526        default=None,
527    )
528
529    add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False)
530
531    subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM")
532
533    subparsers.add_parser("chip_id", help="Read Chip ID from OTP ROM")
534
535    parser_flash_id = subparsers.add_parser(
536        "flash_id", help="Read SPI flash manufacturer and device ID"
537    )
538    add_spi_connection_arg(parser_flash_id)
539
540    parser_read_status = subparsers.add_parser(
541        "read_flash_status", help="Read SPI flash status register"
542    )
543
544    add_spi_connection_arg(parser_read_status)
545    parser_read_status.add_argument(
546        "--bytes",
547        help="Number of bytes to read (1-3)",
548        type=int,
549        choices=[1, 2, 3],
550        default=2,
551    )
552
553    parser_write_status = subparsers.add_parser(
554        "write_flash_status", help="Write SPI flash status register"
555    )
556
557    add_spi_connection_arg(parser_write_status)
558    parser_write_status.add_argument(
559        "--non-volatile",
560        help="Write non-volatile bits (use with caution)",
561        action="store_true",
562    )
563    parser_write_status.add_argument(
564        "--bytes",
565        help="Number of status bytes to write (1-3)",
566        type=int,
567        choices=[1, 2, 3],
568        default=2,
569    )
570    parser_write_status.add_argument("value", help="New value", type=arg_auto_int)
571
572    parser_read_flash = subparsers.add_parser(
573        "read_flash", help="Read SPI flash content"
574    )
575    add_spi_flash_subparsers(
576        parser_read_flash, allow_keep=True, auto_detect=True, size_only=True
577    )
578    parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int)
579    parser_read_flash.add_argument(
580        "size",
581        help="Size of region to dump. Use `ALL` to read to the end of flash.",
582        type=arg_auto_size,
583    )
584    parser_read_flash.add_argument("filename", help="Name of binary dump")
585    parser_read_flash.add_argument(
586        "--no-progress", "-p", help="Suppress progress output", action="store_true"
587    )
588
589    parser_verify_flash = subparsers.add_parser(
590        "verify_flash", help="Verify a binary blob against flash"
591    )
592    parser_verify_flash.add_argument(
593        "addr_filename",
594        help="Address and binary file to verify there, separated by space",
595        action=AddrFilenamePairAction,
596    )
597    parser_verify_flash.add_argument(
598        "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no"
599    )
600    add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True)
601
602    parser_erase_flash = subparsers.add_parser(
603        "erase_flash", help="Perform Chip Erase on SPI flash"
604    )
605    parser_erase_flash.add_argument(
606        "--force",
607        help="Erase flash even if security features are enabled. Use with caution!",
608        action="store_true",
609    )
610    add_spi_connection_arg(parser_erase_flash)
611
612    parser_erase_region = subparsers.add_parser(
613        "erase_region", help="Erase a region of the flash"
614    )
615    parser_erase_region.add_argument(
616        "--force",
617        help="Erase region even if security features are enabled. Use with caution!",
618        action="store_true",
619    )
620    add_spi_connection_arg(parser_erase_region)
621    parser_erase_region.add_argument(
622        "address", help="Start address (must be multiple of 4096)", type=arg_auto_int
623    )
624    parser_erase_region.add_argument(
625        "size",
626        help="Size of region to erase (must be multiple of 4096). "
627        "Use `ALL` to erase to the end of flash.",
628        type=arg_auto_size,
629    )
630
631    parser_read_flash_sfdp = subparsers.add_parser(
632        "read_flash_sfdp",
633        help="Read SPI flash SFDP (Serial Flash Discoverable Parameters)",
634    )
635    add_spi_flash_subparsers(parser_read_flash_sfdp, allow_keep=True, auto_detect=True)
636    parser_read_flash_sfdp.add_argument("addr", type=arg_auto_int)
637    parser_read_flash_sfdp.add_argument("bytes", type=int)
638
639    parser_merge_bin = subparsers.add_parser(
640        "merge_bin",
641        help="Merge multiple raw binary files into a single file for later flashing",
642    )
643
644    parser_merge_bin.add_argument(
645        "--output", "-o", help="Output filename", type=str, required=True
646    )
647    parser_merge_bin.add_argument(
648        "--format",
649        "-f",
650        help="Format of the output file",
651        choices=["raw", "uf2", "hex"],
652        default="raw",
653    )
654    uf2_group = parser_merge_bin.add_argument_group("UF2 format")
655    uf2_group.add_argument(
656        "--chunk-size",
657        help="Specify the used data part of the 512 byte UF2 block. "
658        "A common value is 256. By default the largest possible value will be used.",
659        default=None,
660        type=arg_auto_chunk_size,
661    )
662    uf2_group.add_argument(
663        "--md5-disable",
664        help="Disable MD5 checksum in UF2 output",
665        action="store_true",
666    )
667    add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False)
668
669    raw_group = parser_merge_bin.add_argument_group("RAW format")
670    raw_group.add_argument(
671        "--target-offset",
672        "-t",
673        help="Target offset where the output file will be flashed",
674        type=arg_auto_int,
675        default=0,
676    )
677    raw_group.add_argument(
678        "--fill-flash-size",
679        help="If set, the final binary file will be padded with FF "
680        "bytes up to this flash size.",
681        choices=[
682            "256KB",
683            "512KB",
684            "1MB",
685            "2MB",
686            "4MB",
687            "8MB",
688            "16MB",
689            "32MB",
690            "64MB",
691            "128MB",
692        ],
693    )
694    parser_merge_bin.add_argument(
695        "addr_filename",
696        metavar="<address> <filename>",
697        help="Address followed by binary filename, separated by space",
698        action=AddrFilenamePairAction,
699    )
700
701    subparsers.add_parser("get_security_info", help="Get some security-related data")
702
703    subparsers.add_parser("version", help="Print esptool version")
704
705    # internal sanity check - every operation matches a module function of the same name
706    for operation in subparsers.choices.keys():
707        assert operation in globals(), "%s should be a module function" % operation
708
709    # Enable argcomplete only on Unix-like systems
710    if sys.platform != "win32":
711        try:
712            import argcomplete
713
714            argcomplete.autocomplete(parser)
715        except ImportError:
716            pass
717
718    argv = expand_file_arguments(argv or sys.argv[1:])
719
720    args = parser.parse_args(argv)
721    print("esptool.py v%s" % __version__)
722    load_config_file(verbose=True)
723
724    StubFlasher.set_preferred_stub_subdir(args.stub_version)
725
726    # Parse filter arguments into separate lists
727    args.filterVids = []
728    args.filterPids = []
729    args.filterNames = []
730    for f in args.port_filter:
731        kvp = f.split("=")
732        if len(kvp) != 2:
733            raise FatalError("Option --port-filter argument must consist of key=value")
734        if kvp[0] == "vid":
735            args.filterVids.append(arg_auto_int(kvp[1]))
736        elif kvp[0] == "pid":
737            args.filterPids.append(arg_auto_int(kvp[1]))
738        elif kvp[0] == "name":
739            args.filterNames.append(kvp[1])
740        else:
741            raise FatalError("Option --port-filter argument key not recognized")
742
743    # operation function can take 1 arg (args), 2 args (esp, arg)
744    # or be a member function of the ESPLoader class.
745
746    if args.operation is None:
747        parser.print_help()
748        sys.exit(1)
749
750    # Forbid the usage of both --encrypt, which means encrypt all the given files,
751    # and --encrypt-files, which represents the list of files to encrypt.
752    # The reason is that allowing both at the same time increases the chances of
753    # having contradictory lists (e.g. one file not available in one of list).
754    if (
755        args.operation == "write_flash"
756        and args.encrypt
757        and args.encrypt_files is not None
758    ):
759        raise FatalError(
760            "Options --encrypt and --encrypt-files "
761            "must not be specified at the same time."
762        )
763
764    operation_func = globals()[args.operation]
765    operation_args = inspect.getfullargspec(operation_func).args
766
767    if (
768        operation_args[0] == "esp"
769    ):  # operation function takes an ESPLoader connection object
770        if args.before != "no_reset_no_sync":
771            initial_baud = min(
772                ESPLoader.ESP_ROM_BAUD, args.baud
773            )  # don't sync faster than the default baud rate
774        else:
775            initial_baud = args.baud
776
777        if args.port is None:
778            ser_list = get_port_list(args.filterVids, args.filterPids, args.filterNames)
779            print("Found %d serial ports" % len(ser_list))
780        else:
781            ser_list = [args.port]
782        open_port_attempts = os.environ.get(
783            "ESPTOOL_OPEN_PORT_ATTEMPTS", DEFAULT_OPEN_PORT_ATTEMPTS
784        )
785        try:
786            open_port_attempts = int(open_port_attempts)
787        except ValueError:
788            raise SystemExit("Invalid value for ESPTOOL_OPEN_PORT_ATTEMPTS")
789        if open_port_attempts != 1:
790            if args.port is None or args.chip == "auto":
791                print(
792                    "WARNING: The ESPTOOL_OPEN_PORT_ATTEMPTS (open_port_attempts) option can only be used with --port and --chip arguments."
793                )
794            else:
795                esp = esp or connect_loop(
796                    args.port,
797                    initial_baud,
798                    args.chip,
799                    open_port_attempts,
800                    args.trace,
801                    args.before,
802                )
803        esp = esp or get_default_connected_device(
804            ser_list,
805            port=args.port,
806            connect_attempts=args.connect_attempts,
807            initial_baud=initial_baud,
808            chip=args.chip,
809            trace=args.trace,
810            before=args.before,
811        )
812
813        if esp is None:
814            raise FatalError(
815                "Could not connect to an Espressif device "
816                "on any of the %d available serial ports." % len(ser_list)
817            )
818
819        if esp.secure_download_mode:
820            print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME)
821        else:
822            print("Chip is %s" % (esp.get_chip_description()))
823            print("Features: %s" % ", ".join(esp.get_chip_features()))
824            print("Crystal is %dMHz" % esp.get_crystal_freq())
825            read_mac(esp, args)
826
827        if not args.no_stub:
828            if esp.secure_download_mode:
829                print(
830                    "WARNING: Stub loader is not supported in Secure Download Mode, "
831                    "setting --no-stub"
832                )
833                args.no_stub = True
834            elif not esp.IS_STUB and esp.stub_is_disabled:
835                print(
836                    "WARNING: Stub loader has been disabled for compatibility, "
837                    "setting --no-stub"
838                )
839                args.no_stub = True
840            else:
841                try:
842                    esp = esp.run_stub()
843                except Exception:
844                    # The CH9102 bridge (PID: 0x55D4) can have issues on MacOS
845                    if sys.platform == "darwin" and esp._get_pid() == 0x55D4:
846                        print(
847                            "\nNote: If issues persist, "
848                            "try installing the WCH USB-to-Serial MacOS driver."
849                        )
850                    raise
851
852        if args.override_vddsdio:
853            esp.override_vddsdio(args.override_vddsdio)
854
855        if args.baud > initial_baud:
856            try:
857                esp.change_baud(args.baud)
858            except NotImplementedInROMError:
859                print(
860                    "WARNING: ROM doesn't support changing baud rate. "
861                    "Keeping initial baud rate %d" % initial_baud
862                )
863
864        def _define_spi_conn(spi_connection):
865            """Prepare SPI configuration string and value for flash_spi_attach()"""
866            clk, q, d, hd, cs = spi_connection
867            spi_config_txt = f"CLK:{clk}, Q:{q}, D:{d}, HD:{hd}, CS:{cs}"
868            value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk
869            return spi_config_txt, value
870
871        # Override the common SPI flash parameter stuff if configured to do so
872        if hasattr(args, "spi_connection") and args.spi_connection is not None:
873            spi_config = args.spi_connection
874            if args.spi_connection == "SPI":
875                value = 0
876            elif args.spi_connection == "HSPI":
877                value = 1
878            else:
879                esp.check_spi_connection(args.spi_connection)
880                # Encode the pin numbers as a 32-bit integer with packed 6-bit values,
881                # the same way the ESP ROM takes them
882                spi_config, value = _define_spi_conn(args.spi_connection)
883            print(f"Configuring SPI flash mode ({spi_config})...")
884            esp.flash_spi_attach(value)
885        elif args.no_stub:
886            if esp.CHIP_NAME != "ESP32" or esp.secure_download_mode:
887                print("Enabling default SPI flash mode...")
888                # ROM loader doesn't enable flash unless we explicitly do it
889                esp.flash_spi_attach(0)
890            else:
891                # ROM doesn't attach in-package flash chips
892                spi_chip_pads = esp.get_chip_spi_pads()
893                spi_config_txt, value = _define_spi_conn(spi_chip_pads)
894                if spi_chip_pads != (0, 0, 0, 0, 0):
895                    print(
896                        "Attaching flash from eFuses' SPI pads configuration"
897                        f"({spi_config_txt})..."
898                    )
899                else:
900                    print("Enabling default SPI flash mode...")
901                esp.flash_spi_attach(value)
902
903        # XMC chip startup sequence
904        XMC_VENDOR_ID = 0x20
905
906        def is_xmc_chip_strict():
907            id = esp.flash_id()
908            rdid = ((id & 0xFF) << 16) | ((id >> 16) & 0xFF) | (id & 0xFF00)
909
910            vendor_id = (rdid >> 16) & 0xFF
911            mfid = (rdid >> 8) & 0xFF
912            cpid = rdid & 0xFF
913
914            if vendor_id != XMC_VENDOR_ID:
915                return False
916
917            matched = False
918            if mfid == 0x40:
919                if cpid >= 0x13 and cpid <= 0x20:
920                    matched = True
921            elif mfid == 0x41:
922                if cpid >= 0x17 and cpid <= 0x20:
923                    matched = True
924            elif mfid == 0x50:
925                if cpid >= 0x15 and cpid <= 0x16:
926                    matched = True
927            return matched
928
929        def flash_xmc_startup():
930            # If the RDID value is a valid XMC one, may skip the flow
931            fast_check = True
932            if fast_check and is_xmc_chip_strict():
933                return  # Successful XMC flash chip boot-up detected by RDID, skipping.
934
935            sfdp_mfid_addr = 0x10
936            mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8)
937            if mf_id != XMC_VENDOR_ID:  # Non-XMC chip detected by SFDP Read, skipping.
938                return
939
940            print(
941                "WARNING: XMC flash chip boot-up failure detected! "
942                "Running XMC25QHxxC startup flow"
943            )
944            esp.run_spiflash_command(0xB9)  # Enter DPD
945            esp.run_spiflash_command(0x79)  # Enter UDPD
946            esp.run_spiflash_command(0xFF)  # Exit UDPD
947            time.sleep(0.002)  # Delay tXUDPD
948            esp.run_spiflash_command(0xAB)  # Release Power-Down
949            time.sleep(0.00002)
950            # Check for success
951            if not is_xmc_chip_strict():
952                print("WARNING: XMC flash boot-up fix failed.")
953            print("XMC flash chip boot-up fix successful!")
954
955        # Check flash chip connection
956        if not esp.secure_download_mode:
957            try:
958                flash_id = esp.flash_id()
959                if flash_id in (0xFFFFFF, 0x000000):
960                    print(
961                        "WARNING: Failed to communicate with the flash chip, "
962                        "read/write operations will fail. "
963                        "Try checking the chip connections or removing "
964                        "any other hardware connected to IOs."
965                    )
966                    if (
967                        hasattr(args, "spi_connection")
968                        and args.spi_connection is not None
969                    ):
970                        print(
971                            "Some GPIO pins might be used by other peripherals, "
972                            "try using another --spi-connection combination."
973                        )
974
975            except FatalError as e:
976                raise FatalError(f"Unable to verify flash chip connection ({e}).")
977
978        # Check if XMC SPI flash chip booted-up successfully, fix if not
979        if not esp.secure_download_mode:
980            try:
981                flash_xmc_startup()
982            except FatalError as e:
983                esp.trace(f"Unable to perform XMC flash chip startup sequence ({e}).")
984
985        if hasattr(args, "flash_size"):
986            print("Configuring flash size...")
987            if args.flash_size == "detect":
988                flash_size = detect_flash_size(esp, args)
989            elif args.flash_size == "keep":
990                flash_size = detect_flash_size(esp, args=None)
991                if not esp.IS_STUB:
992                    print(
993                        "WARNING: In case of failure, please set a specific --flash_size."
994                    )
995            else:
996                flash_size = args.flash_size
997
998            if flash_size is not None:  # Secure download mode
999                esp.flash_set_parameters(flash_size_bytes(flash_size))
1000                # Check if stub supports chosen flash size
1001                if (
1002                    esp.IS_STUB
1003                    and esp.CHIP_NAME != "ESP32-S3"
1004                    and flash_size_bytes(flash_size) > 16 * 1024 * 1024
1005                ):
1006                    print(
1007                        "WARNING: Flasher stub doesn't fully support flash size larger "
1008                        "than 16MB, in case of failure use --no-stub."
1009                    )
1010
1011        if getattr(args, "size", "") == "all":
1012            if esp.secure_download_mode:
1013                raise FatalError(
1014                    "Detecting flash size is not supported in secure download mode. "
1015                    "Set an exact size value."
1016                )
1017            # detect flash size
1018            flash_id = esp.flash_id()
1019            size_id = flash_id >> 16
1020            size_str = DETECTED_FLASH_SIZES.get(size_id)
1021            if size_str is None:
1022                raise FatalError(
1023                    "Detecting flash size failed. Set an exact size value."
1024                )
1025            print(f"Detected flash size: {size_str}")
1026            args.size = flash_size_bytes(size_str)
1027
1028        if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"):
1029            if esp.CHIP_NAME != "ESP32-S3" and args.address + args.size > 0x1000000:
1030                print(
1031                    "WARNING: Flasher stub doesn't fully support flash size larger "
1032                    "than 16MB, in case of failure use --no-stub."
1033                )
1034
1035        try:
1036            operation_func(esp, args)
1037        finally:
1038            try:  # Clean up AddrFilenamePairAction files
1039                for address, argfile in args.addr_filename:
1040                    argfile.close()
1041            except AttributeError:
1042                pass
1043
1044        # Handle post-operation behaviour (reset or other)
1045        if operation_func == load_ram:
1046            # the ESP is now running the loaded image, so let it run
1047            print("Exiting immediately.")
1048        elif args.after == "hard_reset":
1049            esp.hard_reset()
1050        elif args.after == "soft_reset":
1051            print("Soft resetting...")
1052            # flash_finish will trigger a soft reset
1053            esp.soft_reset(False)
1054        elif args.after == "no_reset_stub":
1055            print("Staying in flasher stub.")
1056        else:  # args.after == 'no_reset'
1057            print("Staying in bootloader.")
1058            if esp.IS_STUB:
1059                esp.soft_reset(True)  # exit stub back to ROM loader
1060
1061        if not external_esp:
1062            esp._port.close()
1063
1064    else:
1065        operation_func(args)
1066
1067
1068def arg_auto_int(x):
1069    return int(x, 0)
1070
1071
1072def arg_auto_size(x):
1073    x = x.lower()
1074    return x if x == "all" else arg_auto_int(x)
1075
1076
1077def arg_auto_chunk_size(string: str) -> int:
1078    num = int(string, 0)
1079    if num & 3 != 0:
1080        raise argparse.ArgumentTypeError("Chunk size should be a 4-byte aligned number")
1081    return num
1082
1083
1084def get_port_list(vids=[], pids=[], names=[]):
1085    if list_ports is None:
1086        raise FatalError(
1087            "Listing all serial ports is currently not available. "
1088            "Please try to specify the port when running esptool.py or update "
1089            "the pyserial package to the latest version"
1090        )
1091    ports = []
1092    for port in list_ports.comports():
1093        if sys.platform == "darwin" and port.device.endswith(
1094            ("Bluetooth-Incoming-Port", "wlan-debug")
1095        ):
1096            continue
1097        if vids and (port.vid is None or port.vid not in vids):
1098            continue
1099        if pids and (port.pid is None or port.pid not in pids):
1100            continue
1101        if names and (
1102            port.name is None or all(name not in port.name for name in names)
1103        ):
1104            continue
1105        ports.append(port.device)
1106    return sorted(ports)
1107
1108
1109def expand_file_arguments(argv):
1110    """
1111    Any argument starting with "@" gets replaced with all values read from a text file.
1112    Text file arguments can be split by newline or by space.
1113    Values are added "as-is", as if they were specified in this order
1114    on the command line.
1115    """
1116    new_args = []
1117    expanded = False
1118    for arg in argv:
1119        if arg.startswith("@"):
1120            expanded = True
1121            with open(arg[1:], "r") as f:
1122                for line in f.readlines():
1123                    new_args += shlex.split(line)
1124        else:
1125            new_args.append(arg)
1126    if expanded:
1127        print(f"esptool.py {' '.join(new_args)}")
1128        return new_args
1129    return argv
1130
1131
1132def connect_loop(
1133    port: str,
1134    initial_baud: int,
1135    chip: str,
1136    max_retries: int,
1137    trace: bool = False,
1138    before: str = "default_reset",
1139):
1140    chip_class = CHIP_DEFS[chip]
1141    esp = None
1142    print(f"Serial port {port}")
1143
1144    first = True
1145    ten_cycle = cycle(chain(repeat(False, 9), (True,)))
1146    retry_loop = chain(
1147        repeat(False, max_retries - 1), (True,) if max_retries else cycle((False,))
1148    )
1149
1150    for last, every_tenth in zip(retry_loop, ten_cycle):
1151        try:
1152            esp = chip_class(port, initial_baud, trace)
1153            if not first:
1154                # break the retrying line
1155                print("")
1156            esp.connect(before)
1157            return esp
1158        except (
1159            FatalError,
1160            serial.serialutil.SerialException,
1161            IOError,
1162            OSError,
1163        ) as err:
1164            if esp and esp._port:
1165                esp._port.close()
1166            esp = None
1167            if first:
1168                print(err)
1169                print("Retrying failed connection", end="", flush=True)
1170                first = False
1171            if last:
1172                raise err
1173            if every_tenth:
1174                # print a dot every second
1175                print(".", end="", flush=True)
1176            time.sleep(0.1)
1177
1178
1179def get_default_connected_device(
1180    serial_list,
1181    port,
1182    connect_attempts,
1183    initial_baud,
1184    chip="auto",
1185    trace=False,
1186    before="default_reset",
1187):
1188    _esp = None
1189    for each_port in reversed(serial_list):
1190        print("Serial port %s" % each_port)
1191        try:
1192            if chip == "auto":
1193                _esp = detect_chip(
1194                    each_port, initial_baud, before, trace, connect_attempts
1195                )
1196            else:
1197                chip_class = CHIP_DEFS[chip]
1198                _esp = chip_class(each_port, initial_baud, trace)
1199                _esp.connect(before, connect_attempts)
1200            break
1201        except (FatalError, OSError) as err:
1202            if port is not None:
1203                raise
1204            print("%s failed to connect: %s" % (each_port, err))
1205            if _esp and _esp._port:
1206                _esp._port.close()
1207            _esp = None
1208    return _esp
1209
1210
1211class SpiConnectionAction(argparse.Action):
1212    """
1213    Custom action to parse 'spi connection' override.
1214    Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas.
1215    """
1216
1217    def __call__(self, parser, namespace, value, option_string=None):
1218        if value.upper() in ["SPI", "HSPI"]:
1219            values = value.upper()
1220        elif "," in value:
1221            values = value.split(",")
1222            if len(values) != 5:
1223                raise argparse.ArgumentError(
1224                    self,
1225                    f"{value} is not a valid list of comma-separate pin numbers. "
1226                    "Must be 5 numbers - CLK,Q,D,HD,CS.",
1227                )
1228            try:
1229                values = tuple(int(v, 0) for v in values)
1230            except ValueError:
1231                raise argparse.ArgumentError(
1232                    self,
1233                    f"{values} is not a valid argument. "
1234                    "All pins must be numeric values",
1235                )
1236        else:
1237            raise argparse.ArgumentError(
1238                self,
1239                f"{value} is not a valid spi-connection value. "
1240                "Values are SPI, HSPI, or a sequence of 5 pin numbers - CLK,Q,D,HD,CS.",
1241            )
1242        setattr(namespace, self.dest, values)
1243
1244
1245class AutoHex2BinAction(argparse.Action):
1246    """Custom parser class for auto conversion of input files from hex to bin"""
1247
1248    def __call__(self, parser, namespace, value, option_string=None):
1249        try:
1250            with open(value, "rb") as f:
1251                # if hex file was detected replace hex file with converted temp bin
1252                # otherwise keep the original file
1253                value = intel_hex_to_bin(f).name
1254        except IOError as e:
1255            raise argparse.ArgumentError(self, e)
1256        setattr(namespace, self.dest, value)
1257
1258
1259class AddrFilenamePairAction(argparse.Action):
1260    """Custom parser class for the address/filename pairs passed as arguments"""
1261
1262    def __init__(self, option_strings, dest, nargs="+", **kwargs):
1263        super(AddrFilenamePairAction, self).__init__(
1264            option_strings, dest, nargs, **kwargs
1265        )
1266
1267    def __call__(self, parser, namespace, values, option_string=None):
1268        # validate pair arguments
1269        pairs = []
1270        for i in range(0, len(values), 2):
1271            try:
1272                address = int(values[i], 0)
1273            except ValueError:
1274                raise argparse.ArgumentError(
1275                    self, 'Address "%s" must be a number' % values[i]
1276                )
1277            try:
1278                argfile = open(values[i + 1], "rb")
1279            except IOError as e:
1280                raise argparse.ArgumentError(self, e)
1281            except IndexError:
1282                raise argparse.ArgumentError(
1283                    self,
1284                    "Must be pairs of an address "
1285                    "and the binary filename to write there",
1286                )
1287            # check for intel hex files and convert them to bin
1288            argfile = intel_hex_to_bin(argfile, address)
1289            pairs.append((address, argfile))
1290
1291        # Sort the addresses and check for overlapping
1292        end = 0
1293        for address, argfile in sorted(pairs, key=lambda x: x[0]):
1294            argfile.seek(0, 2)  # seek to end
1295            size = argfile.tell()
1296            argfile.seek(0)
1297            sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)
1298            sector_end = (
1299                (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1)
1300                & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)
1301            ) - 1
1302            if sector_start < end:
1303                message = "Detected overlap at address: 0x%x for file: %s" % (
1304                    address,
1305                    argfile.name,
1306                )
1307                raise argparse.ArgumentError(self, message)
1308            end = sector_end
1309        setattr(namespace, self.dest, pairs)
1310
1311
1312def _main():
1313    try:
1314        main()
1315    except FatalError as e:
1316        print(f"\nA fatal error occurred: {e}")
1317        sys.exit(2)
1318    except serial.serialutil.SerialException as e:
1319        print(f"\nA serial exception error occurred: {e}")
1320        print(
1321            "Note: This error originates from pySerial. "
1322            "It is likely not a problem with esptool, "
1323            "but with the hardware connection or drivers."
1324        )
1325        print(
1326            "For troubleshooting steps visit: "
1327            "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html"
1328        )
1329        sys.exit(1)
1330    except StopIteration:
1331        print(traceback.format_exc())
1332        print("A fatal error occurred: The chip stopped responding.")
1333        sys.exit(2)
1334
1335
1336if __name__ == "__main__":
1337    _main()
1338