|
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