|
7 | 7 | # i.e. upload.py tools/pyserial tools/esptool write_flash file 0x0
|
8 | 8 |
|
9 | 9 | import os
|
| 10 | +import atexit |
| 11 | +import pathlib |
10 | 12 | import sys
|
11 | 13 | import tempfile
|
| 14 | +import traceback |
12 | 15 |
|
13 |
| -sys.argv.pop(0) # Remove executable name |
14 |
| -toolspath = os.path.dirname(os.path.realpath(__file__)) |
| 16 | +from typing import List |
| 17 | + |
| 18 | +# Add neighbouring pyserial & esptool to search path |
| 19 | +MODULES = [ |
| 20 | + "pyserial", |
| 21 | + "esptool", |
| 22 | +] |
| 23 | + |
| 24 | +PWD = pathlib.Path(__file__).resolve().parent |
| 25 | +for m in MODULES: |
| 26 | + sys.path.insert(0, (PWD / m).as_posix()) |
| 27 | + |
| 28 | +# If this fails, we can't continue and will bomb below |
15 | 29 | try:
|
16 |
| - sys.path.insert(0, os.path.join(toolspath, "pyserial")) # Add pyserial dir to search path |
17 |
| - sys.path.insert(0, os.path.join(toolspath, "esptool")) # Add esptool dir to search path |
18 |
| - import esptool # If this fails, we can't continue and will bomb below |
19 |
| -except ImportError: |
20 |
| - sys.stderr.write("pyserial or esptool directories not found next to this upload.py tool.\n") |
| 30 | + import esptool |
| 31 | +except (ImportError, ModuleNotFoundError) as e: |
| 32 | + sys.stderr.write( |
| 33 | + "\n*** pyserial or esptool directories not found next to upload.py tool (this script) ***\n" |
| 34 | + ) |
| 35 | + traceback.print_exc(file=sys.stderr) |
| 36 | + sys.stderr.flush() |
| 37 | + |
21 | 38 | sys.exit(1)
|
22 | 39 |
|
23 |
| -cmdline = [] |
24 |
| -write_option = '' |
25 |
| -write_addr = '0x0' |
26 |
| -erase_addr = '' |
27 |
| -erase_len = '' |
28 | 40 |
|
29 |
| -while sys.argv: |
30 |
| - thisarg = sys.argv.pop(0) |
| 41 | +def make_erase_pair(addr: str, dest_size: int, block_size=2**16): |
| 42 | + dest, path = tempfile.mkstemp() |
| 43 | + |
| 44 | + buffer = b"\xff" * block_size |
| 45 | + while dest_size: |
| 46 | + unaligned = dest_size % block_size |
| 47 | + |
| 48 | + src_size = block_size |
| 49 | + if unaligned: |
| 50 | + src = buffer[unaligned:] |
| 51 | + src_size = unaligned |
| 52 | + else: |
| 53 | + src = buffer |
| 54 | + |
| 55 | + os.write(dest, src) |
| 56 | + dest_size -= src_size |
| 57 | + |
| 58 | + os.close(dest) |
| 59 | + |
| 60 | + def maybe_remove(path): |
| 61 | + try: |
| 62 | + os.remove(path) |
| 63 | + except: |
| 64 | + pass |
| 65 | + |
| 66 | + atexit.register(maybe_remove, path) |
| 67 | + return [addr, path] |
31 | 68 |
|
32 |
| - # We silently replace the 921kbaud setting with 460k to enable backward |
| 69 | + |
| 70 | +argv = sys.argv[1:] # Remove executable name |
| 71 | + |
| 72 | +cmdline = [] # type: List[str] |
| 73 | +write_options = ["--flash_size", "detect"] # type: List[str] |
| 74 | +erase_options = [] |
| 75 | + |
| 76 | +thisarg = "" |
| 77 | +lastarg = "" |
| 78 | +while argv: |
| 79 | + lastarg = thisarg |
| 80 | + thisarg = argv.pop(0) |
| 81 | + |
| 82 | + # We silently replace the high-speed setting with 460k to enable backward |
33 | 83 | # compatibility with the old esptool-ck.exe. Esptool.py doesn't seem
|
34 |
| - # work reliably at 921k, but is still significantly faster at 460kbaud. |
35 |
| - if thisarg == "921600": |
| 84 | + # work reliably, but 460kbaud is still plenty fast. |
| 85 | + if lastarg == "--baud" and thisarg in ("921600", "3000000"): |
36 | 86 | thisarg = "460800"
|
37 | 87 |
|
38 | 88 | # 'erase_flash' command is translated to the write_flash --erase-all option
|
39 | 89 | # https://github.com/esp8266/Arduino/issues/6755#issuecomment-553208688
|
40 | 90 | if thisarg == "erase_flash":
|
41 |
| - write_option = '--erase-all' |
42 |
| - # 'erase_region' is using a temporary file filled with 0xff |
43 |
| - elif thisarg == 'erase_region': |
44 |
| - erase_addr = sys.argv.pop(0) |
45 |
| - erase_len = sys.argv.pop(0) |
46 |
| - # 'write_flash' and everything else is used as-is |
47 |
| - elif thisarg == 'write_flash': |
48 |
| - write_addr = sys.argv.pop(0) |
49 |
| - binary = sys.argv.pop(0) |
50 |
| - elif thisarg: |
51 |
| - cmdline = cmdline + [thisarg] |
| 91 | + write_options.append("--erase-all") |
52 | 92 |
|
53 |
| -cmdline = cmdline + ['write_flash'] |
54 |
| -if write_option: |
55 |
| - cmdline = cmdline + [write_option] |
56 |
| -cmdline = cmdline + ['--flash_size', 'detect'] |
57 |
| -cmdline = cmdline + [write_addr, binary] |
| 93 | + # instead of providing esptool with separate targets, |
| 94 | + # everything below becomes 'write_flash' [<addr> <path>] pairs |
| 95 | + |
| 96 | + # 'erase_region' becomes a temporary file filled with 0xff |
| 97 | + # this pair is appended *after* 'write_flash' pairs |
| 98 | + elif thisarg == "erase_region": |
| 99 | + addr = argv.pop(0) |
| 100 | + size = int(argv.pop(0), 0) |
| 101 | + erase_options.extend(make_erase_pair(addr, size)) |
| 102 | + |
| 103 | + # 'write_flash' pair taken in order it was specified |
| 104 | + elif thisarg == "write_flash": |
| 105 | + addr = argv.pop(0) |
| 106 | + path = argv.pop(0) |
| 107 | + write_options.extend([addr, path]) |
| 108 | + |
| 109 | + # everything else is used as-is |
| 110 | + elif thisarg: |
| 111 | + cmdline.append(thisarg) |
58 | 112 |
|
59 |
| -erase_file = '' |
60 |
| -if erase_addr: |
61 |
| - erase_fd, erase_file = tempfile.mkstemp() |
62 |
| - os.write(erase_fd, b"\xff" * int(erase_len, 0)) |
63 |
| - os.close(erase_fd) |
64 |
| - cmdline = cmdline + [erase_addr, erase_file] |
65 | 113 |
|
66 |
| -exit_code = 0 |
| 114 | +cmdline.append("write_flash") |
| 115 | +for opts in (write_options, erase_options): |
| 116 | + if opts: |
| 117 | + cmdline.extend(opts) |
67 | 118 |
|
68 | 119 | try:
|
69 | 120 | esptool.main(cmdline)
|
70 |
| -except Exception as e: |
71 |
| - sys.stderr.write(f"\nA fatal upload.py error occurred: {repr(e)}\n") |
72 |
| - exit_code = 2 |
| 121 | +except: |
| 122 | + etype, evalue, _ = sys.exc_info() |
| 123 | + estring = "\n".join(traceback.format_exception_only(etype, value=evalue)) |
73 | 124 |
|
74 |
| -if erase_file: |
75 |
| - try: |
76 |
| - os.remove(erase_file) |
77 |
| - except: |
78 |
| - pass |
| 125 | + sys.stderr.write(f"\n*** A fatal upload.py error occurred ***\n") |
| 126 | + sys.stderr.write(estring) |
| 127 | + sys.stderr.flush() |
79 | 128 |
|
80 |
| -sys.exit(exit_code) |
| 129 | + sys.exit(2) |
0 commit comments