esptool.py
1 #!/usr/bin/env python 2 # 3 # SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) CO LTD, other contributors as noted. 4 # 5 # SPDX-License-Identifier: GPL-2.0-or-later 6 7 from __future__ import division, print_function 8 9 import argparse 10 import base64 11 import binascii 12 import copy 13 import hashlib 14 import inspect 15 import io 16 import itertools 17 import os 18 import re 19 import shlex 20 import string 21 import struct 22 import sys 23 import time 24 import zlib 25 26 try: 27 import serial 28 except ImportError: 29 print("Pyserial is not installed for %s. Check the README for installation instructions." % (sys.executable)) 30 raise 31 32 # check 'serial' is 'pyserial' and not 'serial' https://github.com/espressif/esptool/issues/269 33 try: 34 if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: 35 raise ImportError(""" 36 esptool.py depends on pyserial, but there is a conflict with a currently installed package named 'serial'. 37 38 You may be able to work around this by 'pip uninstall serial; pip install pyserial' \ 39 but this may break other installed Python software that depends on 'serial'. 40 41 There is no good fix for this right now, apart from configuring virtualenvs. \ 42 See https://github.com/espressif/esptool/issues/269#issuecomment-385298196 for discussion of the underlying issue(s).""") 43 except TypeError: 44 pass # __doc__ returns None for pyserial 45 46 try: 47 import serial.tools.list_ports as list_ports 48 except ImportError: 49 print("The installed version (%s) of pyserial appears to be too old for esptool.py (Python interpreter %s). " 50 "Check the README for installation instructions." % (sys.VERSION, sys.executable)) 51 raise 52 except Exception: 53 if sys.platform == "darwin": 54 # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview ref https://github.com/espressif/esptool/issues/540 55 list_ports = None 56 else: 57 raise 58 59 60 __version__ = "3.3.3" 61 62 MAX_UINT32 = 0xffffffff 63 MAX_UINT24 = 0xffffff 64 65 DEFAULT_TIMEOUT = 3 # timeout for most flash operations 66 START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase) 67 CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase 68 MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run 69 SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader 70 MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum 71 ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region 72 ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data 73 MEM_END_ROM_TIMEOUT = 0.05 # special short timeout for ESP_MEM_END, as it may never respond 74 DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write 75 DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection 76 WRITE_BLOCK_ATTEMPTS = 3 # number of times to try writing a data block 77 78 SUPPORTED_CHIPS = ['esp8266', 'esp32', 'esp32s2', 'esp32s3beta2', 'esp32s3', 'esp32c3', 'esp32c6beta', 'esp32h2beta1', 'esp32h2beta2', 'esp32c2'] 79 80 81 def timeout_per_mb(seconds_per_mb, size_bytes): 82 """ Scales timeouts which are size-specific """ 83 result = seconds_per_mb * (size_bytes / 1e6) 84 if result < DEFAULT_TIMEOUT: 85 return DEFAULT_TIMEOUT 86 return result 87 88 89 def _chip_to_rom_loader(chip): 90 return { 91 'esp8266': ESP8266ROM, 92 'esp32': ESP32ROM, 93 'esp32s2': ESP32S2ROM, 94 'esp32s3beta2': ESP32S3BETA2ROM, 95 'esp32s3': ESP32S3ROM, 96 'esp32c3': ESP32C3ROM, 97 'esp32c6beta': ESP32C6BETAROM, 98 'esp32h2beta1': ESP32H2BETA1ROM, 99 'esp32h2beta2': ESP32H2BETA2ROM, 100 'esp32c2': ESP32C2ROM, 101 }[chip] 102 103 104 def get_default_connected_device(serial_list, port, connect_attempts, initial_baud, chip='auto', trace=False, 105 before='default_reset'): 106 _esp = None 107 for each_port in reversed(serial_list): 108 print("Serial port %s" % each_port) 109 try: 110 if chip == 'auto': 111 _esp = ESPLoader.detect_chip(each_port, initial_baud, before, trace, 112 connect_attempts) 113 else: 114 chip_class = _chip_to_rom_loader(chip) 115 _esp = chip_class(each_port, initial_baud, trace) 116 _esp.connect(before, connect_attempts) 117 break 118 except (FatalError, OSError) as err: 119 if port is not None: 120 raise 121 print("%s failed to connect: %s" % (each_port, err)) 122 if _esp and _esp._port: 123 _esp._port.close() 124 _esp = None 125 return _esp 126 127 128 DETECTED_FLASH_SIZES = { 129 0x12: "256KB", 130 0x13: "512KB", 131 0x14: "1MB", 132 0x15: "2MB", 133 0x16: "4MB", 134 0x17: "8MB", 135 0x18: "16MB", 136 0x19: "32MB", 137 0x1A: "64MB", 138 0x1B: "128MB", 139 0x1C: "256MB", 140 0x20: "64MB", 141 0x21: "128MB", 142 0x22: "256MB", 143 0x32: "256KB", 144 0x33: "512KB", 145 0x34: "1MB", 146 0x35: "2MB", 147 0x36: "4MB", 148 0x37: "8MB", 149 0x38: "16MB", 150 0x39: "32MB", 151 0x3A: "64MB", 152 } 153 154 155 def check_supported_function(func, check_func): 156 """ 157 Decorator implementation that wraps a check around an ESPLoader 158 bootloader function to check if it's supported. 159 160 This is used to capture the multidimensional differences in 161 functionality between the ESP8266 & ESP32 (and later chips) ROM loaders, and the 162 software stub that runs on these. Not possible to do this cleanly 163 via inheritance alone. 164 """ 165 def inner(*args, **kwargs): 166 obj = args[0] 167 if check_func(obj): 168 return func(*args, **kwargs) 169 else: 170 raise NotImplementedInROMError(obj, func) 171 return inner 172 173 174 def esp8266_function_only(func): 175 """ Attribute for a function only supported on ESP8266 """ 176 return check_supported_function(func, lambda o: o.CHIP_NAME == "ESP8266") 177 178 179 def stub_function_only(func): 180 """ Attribute for a function only supported in the software stub loader """ 181 return check_supported_function(func, lambda o: o.IS_STUB) 182 183 184 def stub_and_esp32_function_only(func): 185 """ Attribute for a function only supported by software stubs or ESP32 and later chips ROM """ 186 return check_supported_function(func, lambda o: o.IS_STUB or isinstance(o, ESP32ROM)) 187 188 189 def esp32s3_or_newer_function_only(func): 190 """ Attribute for a function only supported by ESP32S3 and later chips ROM """ 191 return check_supported_function(func, lambda o: isinstance(o, ESP32S3ROM) or isinstance(o, ESP32C3ROM)) 192 193 194 PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3 195 196 # Function to return nth byte of a bitstring 197 # Different behaviour on Python 2 vs 3 198 if PYTHON2: 199 def byte(bitstr, index): 200 return ord(bitstr[index]) 201 else: 202 def byte(bitstr, index): 203 return bitstr[index] 204 205 # Provide a 'basestring' class on Python 3 206 try: 207 basestring 208 except NameError: 209 basestring = str 210 211 212 def print_overwrite(message, last_line=False): 213 """ Print a message, overwriting the currently printed line. 214 215 If last_line is False, don't append a newline at the end (expecting another subsequent call will overwrite this one.) 216 217 After a sequence of calls with last_line=False, call once with last_line=True. 218 219 If output is not a TTY (for example redirected a pipe), no overwriting happens and this function is the same as print(). 220 """ 221 if sys.stdout.isatty(): 222 print("\r%s" % message, end='\n' if last_line else '') 223 else: 224 print(message) 225 226 227 def _mask_to_shift(mask): 228 """ Return the index of the least significant bit in the mask """ 229 shift = 0 230 while mask & 0x1 == 0: 231 shift += 1 232 mask >>= 1 233 return shift 234 235 236 class ESPLoader(object): 237 """ Base class providing access to ESP ROM & software stub bootloaders. 238 Subclasses provide ESP8266 & ESP32 Family specific functionality. 239 240 Don't instantiate this base class directly, either instantiate a subclass or 241 call ESPLoader.detect_chip() which will interrogate the chip and return the 242 appropriate subclass instance. 243 244 """ 245 CHIP_NAME = "Espressif device" 246 IS_STUB = False 247 248 FPGA_SLOW_BOOT = False 249 250 DEFAULT_PORT = "/dev/ttyUSB0" 251 252 USES_RFC2217 = False 253 254 # Commands supported by ESP8266 ROM bootloader 255 ESP_FLASH_BEGIN = 0x02 256 ESP_FLASH_DATA = 0x03 257 ESP_FLASH_END = 0x04 258 ESP_MEM_BEGIN = 0x05 259 ESP_MEM_END = 0x06 260 ESP_MEM_DATA = 0x07 261 ESP_SYNC = 0x08 262 ESP_WRITE_REG = 0x09 263 ESP_READ_REG = 0x0a 264 265 # Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub) 266 ESP_SPI_SET_PARAMS = 0x0B 267 ESP_SPI_ATTACH = 0x0D 268 ESP_READ_FLASH_SLOW = 0x0e # ROM only, much slower than the stub flash read 269 ESP_CHANGE_BAUDRATE = 0x0F 270 ESP_FLASH_DEFL_BEGIN = 0x10 271 ESP_FLASH_DEFL_DATA = 0x11 272 ESP_FLASH_DEFL_END = 0x12 273 ESP_SPI_FLASH_MD5 = 0x13 274 275 # Commands supported by ESP32-S2 and later chips ROM bootloader only 276 ESP_GET_SECURITY_INFO = 0x14 277 278 # Some commands supported by stub only 279 ESP_ERASE_FLASH = 0xD0 280 ESP_ERASE_REGION = 0xD1 281 ESP_READ_FLASH = 0xD2 282 ESP_RUN_USER_CODE = 0xD3 283 284 # Flash encryption encrypted data command 285 ESP_FLASH_ENCRYPT_DATA = 0xD4 286 287 # Response code(s) sent by ROM 288 ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received 289 290 # Maximum block sized for RAM and Flash writes, respectively. 291 ESP_RAM_BLOCK = 0x1800 292 293 FLASH_WRITE_SIZE = 0x400 294 295 # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. 296 ESP_ROM_BAUD = 115200 297 298 # First byte of the application image 299 ESP_IMAGE_MAGIC = 0xe9 300 301 # Initial state for the checksum routine 302 ESP_CHECKSUM_MAGIC = 0xef 303 304 # Flash sector size, minimum unit of erase. 305 FLASH_SECTOR_SIZE = 0x1000 306 307 UART_DATE_REG_ADDR = 0x60000078 308 309 CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 # This ROM address has a different value on each chip model 310 311 UART_CLKDIV_MASK = 0xFFFFF 312 313 # Memory addresses 314 IROM_MAP_START = 0x40200000 315 IROM_MAP_END = 0x40300000 316 317 # The number of bytes in the UART response that signify command status 318 STATUS_BYTES_LENGTH = 2 319 320 # Response to ESP_SYNC might indicate that flasher stub is running instead of the ROM bootloader 321 sync_stub_detected = False 322 323 # Device PIDs 324 USB_JTAG_SERIAL_PID = 0x1001 325 326 # Chip IDs that are no longer supported by esptool 327 UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"} 328 329 def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): 330 """Base constructor for ESPLoader bootloader interaction 331 332 Don't call this constructor, either instantiate ESP8266ROM 333 or ESP32ROM, or use ESPLoader.detect_chip(). 334 335 This base class has all of the instance methods for bootloader 336 functionality supported across various chips & stub 337 loaders. Subclasses replace the functions they don't support 338 with ones which throw NotImplementedInROMError(). 339 340 """ 341 self.secure_download_mode = False # flag is set to True if esptool detects the ROM is in Secure Download Mode 342 self.stub_is_disabled = False # flag is set to True if esptool detects conditions which require the stub to be disabled 343 344 if isinstance(port, basestring): 345 self._port = serial.serial_for_url(port) 346 else: 347 self._port = port 348 self._slip_reader = slip_reader(self._port, self.trace) 349 # setting baud rate in a separate step is a workaround for 350 # CH341 driver on some Linux versions (this opens at 9600 then 351 # sets), shouldn't matter for other platforms/drivers. See 352 # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 353 self._set_port_baudrate(baud) 354 self._trace_enabled = trace_enabled 355 # set write timeout, to prevent esptool blocked at write forever. 356 try: 357 self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT 358 except NotImplementedError: 359 # no write timeout for RFC2217 ports 360 # need to set the property back to None or it will continue to fail 361 self._port.write_timeout = None 362 363 @property 364 def serial_port(self): 365 return self._port.port 366 367 def _set_port_baudrate(self, baud): 368 try: 369 self._port.baudrate = baud 370 except IOError: 371 raise FatalError("Failed to set baud rate %d. The driver may not support this rate." % baud) 372 373 @staticmethod 374 def detect_chip(port=DEFAULT_PORT, baud=ESP_ROM_BAUD, connect_mode='default_reset', trace_enabled=False, 375 connect_attempts=DEFAULT_CONNECT_ATTEMPTS): 376 """ Use serial access to detect the chip type. 377 378 First, get_security_info command is sent to detect the ID of the chip 379 (supported only by ESP32-C3 and later, works even in the Secure Download Mode). 380 If this fails, we reconnect and fall-back to reading the magic number. 381 It's mapped at a specific ROM address and has a different value on each chip model. 382 This way we can use one memory read and compare it to the magic number for each chip type. 383 384 This routine automatically performs ESPLoader.connect() (passing 385 connect_mode parameter) as part of querying the chip. 386 """ 387 inst = None 388 detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) 389 if detect_port.serial_port.startswith("rfc2217:"): 390 detect_port.USES_RFC2217 = True 391 detect_port.connect(connect_mode, connect_attempts, detecting=True) 392 try: 393 print('Detecting chip type...', end='') 394 res = detect_port.check_command('get security info', ESPLoader.ESP_GET_SECURITY_INFO, b'') 395 res = struct.unpack("<IBBBBBBBBI", res[:16]) # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id 396 chip_id = res[9] # 2/4 status bytes invariant 397 398 for cls in [ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM, ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM]: 399 if chip_id == cls.IMAGE_CHIP_ID: 400 inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) 401 inst._post_connect() 402 try: 403 inst.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) # Dummy read to check Secure Download mode 404 except UnsupportedCommandError: 405 inst.secure_download_mode = True 406 except (UnsupportedCommandError, struct.error, FatalError) as e: 407 # UnsupportedCmdErr: ESP8266/ESP32 ROM | struct.err: ESP32-S2 | FatalErr: ESP8266/ESP32 STUB 408 print(" Unsupported detection protocol, switching and trying again...") 409 try: 410 # ESP32/ESP8266 are reset after an unsupported command, need to connect again (not needed on ESP32-S2) 411 if not isinstance(e, struct.error): 412 detect_port.connect(connect_mode, connect_attempts, detecting=True, warnings=False) 413 print('Detecting chip type...', end='') 414 sys.stdout.flush() 415 chip_magic_value = detect_port.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) 416 417 for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, 418 ESP32C3ROM, ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM]: 419 if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: 420 inst = cls(detect_port._port, baud, trace_enabled=trace_enabled) 421 inst._post_connect() 422 inst.check_chip_id() 423 except UnsupportedCommandError: 424 raise FatalError("Unsupported Command Error received. Probably this means Secure Download Mode is enabled, " 425 "autodetection will not work. Need to manually specify the chip.") 426 finally: 427 if inst is not None: 428 print(' %s' % inst.CHIP_NAME, end='') 429 if detect_port.sync_stub_detected: 430 inst = inst.STUB_CLASS(inst) 431 inst.sync_stub_detected = True 432 print('') # end line 433 return inst 434 raise FatalError("Unexpected CHIP magic value 0x%08x. Failed to autodetect chip type." % (chip_magic_value)) 435 436 """ Read a SLIP packet from the serial port """ 437 def read(self): 438 return next(self._slip_reader) 439 440 """ Write bytes to the serial port while performing SLIP escaping """ 441 def write(self, packet): 442 buf = b'\xc0' \ 443 + (packet.replace(b'\xdb', b'\xdb\xdd').replace(b'\xc0', b'\xdb\xdc')) \ 444 + b'\xc0' 445 self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) 446 self._port.write(buf) 447 448 def trace(self, message, *format_args): 449 if self._trace_enabled: 450 now = time.time() 451 try: 452 453 delta = now - self._last_trace 454 except AttributeError: 455 delta = 0.0 456 self._last_trace = now 457 prefix = "TRACE +%.3f " % delta 458 print(prefix + (message % format_args)) 459 460 """ Calculate checksum of a blob, as it is defined by the ROM """ 461 @staticmethod 462 def checksum(data, state=ESP_CHECKSUM_MAGIC): 463 for b in data: 464 if type(b) is int: # python 2/3 compat 465 state ^= b 466 else: 467 state ^= ord(b) 468 469 return state 470 471 """ Send a request and read the response """ 472 def command(self, op=None, data=b"", chk=0, wait_response=True, timeout=DEFAULT_TIMEOUT): 473 saved_timeout = self._port.timeout 474 new_timeout = min(timeout, MAX_TIMEOUT) 475 if new_timeout != saved_timeout: 476 self._port.timeout = new_timeout 477 478 try: 479 if op is not None: 480 self.trace("command op=0x%02x data len=%s wait_response=%d timeout=%.3f data=%s", 481 op, len(data), 1 if wait_response else 0, timeout, HexFormatter(data)) 482 pkt = struct.pack(b'<BBHI', 0x00, op, len(data), chk) + data 483 self.write(pkt) 484 485 if not wait_response: 486 return 487 488 # tries to get a response until that response has the 489 # same operation as the request or a retries limit has 490 # exceeded. This is needed for some esp8266s that 491 # reply with more sync responses than expected. 492 for retry in range(100): 493 p = self.read() 494 if len(p) < 8: 495 continue 496 (resp, op_ret, len_ret, val) = struct.unpack('<BBHI', p[:8]) 497 if resp != 1: 498 continue 499 data = p[8:] 500 501 if op is None or op_ret == op: 502 return val, data 503 if byte(data, 0) != 0 and byte(data, 1) == self.ROM_INVALID_RECV_MSG: 504 self.flush_input() # Unsupported read_reg can result in more than one error response for some reason 505 raise UnsupportedCommandError(self, op) 506 507 finally: 508 if new_timeout != saved_timeout: 509 self._port.timeout = saved_timeout 510 511 raise FatalError("Response doesn't match request") 512 513 def check_command(self, op_description, op=None, data=b'', chk=0, timeout=DEFAULT_TIMEOUT): 514 """ 515 Execute a command with 'command', check the result code and throw an appropriate 516 FatalError if it fails. 517 518 Returns the "result" of a successful command. 519 """ 520 val, data = self.command(op, data, chk, timeout=timeout) 521 522 # things are a bit weird here, bear with us 523 524 # the status bytes are the last 2/4 bytes in the data (depending on chip) 525 if len(data) < self.STATUS_BYTES_LENGTH: 526 raise FatalError("Failed to %s. Only got %d byte status response." % (op_description, len(data))) 527 status_bytes = data[-self.STATUS_BYTES_LENGTH:] 528 # we only care if the first one is non-zero. If it is, the second byte is a reason. 529 if byte(status_bytes, 0) != 0: 530 raise FatalError.WithResult('Failed to %s' % op_description, status_bytes) 531 532 # if we had more data than just the status bytes, return it as the result 533 # (this is used by the md5sum command, maybe other commands?) 534 if len(data) > self.STATUS_BYTES_LENGTH: 535 return data[:-self.STATUS_BYTES_LENGTH] 536 else: # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg) 537 return val 538 539 def flush_input(self): 540 self._port.flushInput() 541 self._slip_reader = slip_reader(self._port, self.trace) 542 543 def sync(self): 544 val, _ = self.command(self.ESP_SYNC, b'\x07\x07\x12\x20' + 32 * b'\x55', 545 timeout=SYNC_TIMEOUT) 546 547 # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. If we receive 0 then it 548 # probably indicates that the chip wasn't or couldn't be reseted properly and esptool is talking to the 549 # flasher stub. 550 self.sync_stub_detected = val == 0 551 552 for _ in range(7): 553 val, _ = self.command() 554 self.sync_stub_detected &= val == 0 555 556 def _setDTR(self, state): 557 self._port.setDTR(state) 558 559 def _setRTS(self, state): 560 self._port.setRTS(state) 561 # Work-around for adapters on Windows using the usbser.sys driver: 562 # generate a dummy change to DTR so that the set-control-line-state 563 # request is sent with the updated RTS state and the same DTR state 564 self._port.setDTR(self._port.dtr) 565 566 def _get_pid(self): 567 if list_ports is None: 568 print("\nListing all serial ports is currently not available. Can't get device PID.") 569 return 570 active_port = self._port.port 571 572 # Pyserial only identifies regular ports, URL handlers are not supported 573 if not active_port.lower().startswith(("com", "/dev/")): 574 print("\nDevice PID identification is only supported on COM and /dev/ serial ports.") 575 return 576 # Return the real path if the active port is a symlink 577 if active_port.startswith("/dev/") and os.path.islink(active_port): 578 active_port = os.path.realpath(active_port) 579 580 # The "cu" (call-up) device has to be used for outgoing communication on MacOS 581 if sys.platform == "darwin" and "tty" in active_port: 582 active_port = [active_port, active_port.replace("tty", "cu")] 583 ports = list_ports.comports() 584 for p in ports: 585 if p.device in active_port: 586 return p.pid 587 print("\nFailed to get PID of a device on {}, using standard reset sequence.".format(active_port)) 588 589 def bootloader_reset(self, usb_jtag_serial=False, extra_delay=False): 590 """ Issue a reset-to-bootloader, with USB-JTAG-Serial custom reset sequence option 591 """ 592 # RTS = either CH_PD/EN or nRESET (both active low = chip in reset) 593 # DTR = GPIO0 (active low = boot to flasher) 594 # 595 # DTR & RTS are active low signals, 596 # ie True = pin @ 0V, False = pin @ VCC. 597 if usb_jtag_serial: 598 # Custom reset sequence, which is required when the device 599 # is connecting via its USB-JTAG-Serial peripheral 600 self._setRTS(False) 601 self._setDTR(False) # Idle 602 time.sleep(0.1) 603 self._setDTR(True) # Set IO0 604 self._setRTS(False) 605 time.sleep(0.1) 606 self._setRTS(True) # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0) 607 self._setDTR(False) 608 self._setRTS(True) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting 609 time.sleep(0.1) 610 self._setDTR(False) 611 self._setRTS(False) 612 else: 613 # This fpga delay is for Espressif internal use 614 fpga_delay = True if self.FPGA_SLOW_BOOT and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" else False 615 delay = 7 if fpga_delay else 0.5 if extra_delay else 0.05 # 0.5 needed for ESP32 rev0 and rev1 616 617 self._setDTR(False) # IO0=HIGH 618 self._setRTS(True) # EN=LOW, chip in reset 619 time.sleep(0.1) 620 self._setDTR(True) # IO0=LOW 621 self._setRTS(False) # EN=HIGH, chip out of reset 622 time.sleep(delay) 623 self._setDTR(False) # IO0=HIGH, done 624 625 def _connect_attempt(self, mode='default_reset', usb_jtag_serial=False, extra_delay=False): 626 """ A single connection attempt """ 627 last_error = None 628 boot_log_detected = False 629 download_mode = False 630 631 # If we're doing no_sync, we're likely communicating as a pass through 632 # with an intermediate device to the ESP32 633 if mode == "no_reset_no_sync": 634 return last_error 635 636 if mode != 'no_reset': 637 if not self.USES_RFC2217: # Might block on rfc2217 ports 638 self._port.reset_input_buffer() # Empty serial buffer to isolate boot log 639 self.bootloader_reset(usb_jtag_serial, extra_delay) 640 641 # Detect the ROM boot log and check actual boot mode (ESP32 and later only) 642 waiting = self._port.inWaiting() 643 read_bytes = self._port.read(waiting) 644 data = re.search(b'boot:(0x[0-9a-fA-F]+)(.*waiting for download)?', read_bytes, re.DOTALL) 645 if data is not None: 646 boot_log_detected = True 647 boot_mode = data.group(1) 648 download_mode = data.group(2) is not None 649 650 for _ in range(5): 651 try: 652 self.flush_input() 653 self._port.flushOutput() 654 self.sync() 655 return None 656 except FatalError as e: 657 print('.', end='') 658 sys.stdout.flush() 659 time.sleep(0.05) 660 last_error = e 661 662 if boot_log_detected: 663 last_error = FatalError("Wrong boot mode detected ({})! The chip needs to be in download mode.".format(boot_mode.decode("utf-8"))) 664 if download_mode: 665 last_error = FatalError("Download mode successfully detected, but getting no sync reply: The serial TX path seems to be down.") 666 return last_error 667 668 def get_memory_region(self, name): 669 """ Returns a tuple of (start, end) for the memory map entry with the given name, or None if it doesn't exist 670 """ 671 try: 672 return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] 673 except IndexError: 674 return None 675 676 def connect(self, mode='default_reset', attempts=DEFAULT_CONNECT_ATTEMPTS, detecting=False, warnings=True): 677 """ Try connecting repeatedly until successful, or giving up """ 678 if warnings and mode in ['no_reset', 'no_reset_no_sync']: 679 print('WARNING: Pre-connection option "{}" was selected.'.format(mode), 680 'Connection may fail if the chip is not in bootloader or flasher stub mode.') 681 print('Connecting...', end='') 682 sys.stdout.flush() 683 last_error = None 684 685 usb_jtag_serial = (mode == 'usb_reset') or (self._get_pid() == self.USB_JTAG_SERIAL_PID) 686 687 try: 688 for _, extra_delay in zip(range(attempts) if attempts > 0 else itertools.count(), itertools.cycle((False, True))): 689 last_error = self._connect_attempt(mode=mode, usb_jtag_serial=usb_jtag_serial, extra_delay=extra_delay) 690 if last_error is None: 691 break 692 finally: 693 print('') # end 'Connecting...' line 694 695 if last_error is not None: 696 raise FatalError('Failed to connect to {}: {}' 697 '\nFor troubleshooting steps visit: ' 698 'https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html'.format(self.CHIP_NAME, last_error)) 699 700 if not detecting: 701 try: 702 # check the date code registers match what we expect to see 703 chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) 704 if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: 705 actually = None 706 for cls in [ESP8266ROM, ESP32ROM, ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, 707 ESP32C3ROM, ESP32H2BETA1ROM, ESP32H2BETA2ROM, ESP32C2ROM, ESP32C6BETAROM]: 708 if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: 709 actually = cls 710 break 711 if warnings and actually is None: 712 print(("WARNING: This chip doesn't appear to be a %s (chip magic value 0x%08x). " 713 "Probably it is unsupported by this version of esptool.") % (self.CHIP_NAME, chip_magic_value)) 714 else: 715 raise FatalError("This chip is %s not %s. Wrong --chip argument?" % (actually.CHIP_NAME, self.CHIP_NAME)) 716 except UnsupportedCommandError: 717 self.secure_download_mode = True 718 self._post_connect() 719 self.check_chip_id() 720 721 def _post_connect(self): 722 """ 723 Additional initialization hook, may be overridden by the chip-specific class. 724 Gets called after connect, and after auto-detection. 725 """ 726 pass 727 728 def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): 729 """ Read memory address in target """ 730 # we don't call check_command here because read_reg() function is called 731 # when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different 732 # for different chip types (!) 733 val, data = self.command(self.ESP_READ_REG, struct.pack('<I', addr), timeout=timeout) 734 if byte(data, 0) != 0: 735 raise FatalError.WithResult("Failed to read register address %08x" % addr, data) 736 return val 737 738 """ Write to memory address in target """ 739 def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): 740 command = struct.pack('<IIII', addr, value, mask, delay_us) 741 if delay_after_us > 0: 742 # add a dummy write to a date register as an excuse to have a delay 743 command += struct.pack('<IIII', self.UART_DATE_REG_ADDR, 0, 0, delay_after_us) 744 745 return self.check_command("write target memory", self.ESP_WRITE_REG, command) 746 747 def update_reg(self, addr, mask, new_val): 748 """ Update register at 'addr', replace the bits masked out by 'mask' 749 with new_val. new_val is shifted left to match the LSB of 'mask' 750 751 Returns just-written value of register. 752 """ 753 shift = _mask_to_shift(mask) 754 val = self.read_reg(addr) 755 val &= ~mask 756 val |= (new_val << shift) & mask 757 self.write_reg(addr, val) 758 759 return val 760 761 """ Start downloading an application image to RAM """ 762 def mem_begin(self, size, blocks, blocksize, offset): 763 if self.IS_STUB: # check we're not going to overwrite a running stub with this data 764 stub = self.STUB_CODE 765 load_start = offset 766 load_end = offset + size 767 for (start, end) in [(stub["data_start"], stub["data_start"] + len(stub["data"])), 768 (stub["text_start"], stub["text_start"] + len(stub["text"]))]: 769 if load_start < end and load_end > start: 770 raise FatalError(("Software loader is resident at 0x%08x-0x%08x. " 771 "Can't load binary at overlapping address range 0x%08x-0x%08x. " 772 "Either change binary loading address, or use the --no-stub " 773 "option to disable the software loader.") % (start, end, load_start, load_end)) 774 775 return self.check_command("enter RAM download mode", self.ESP_MEM_BEGIN, 776 struct.pack('<IIII', size, blocks, blocksize, offset)) 777 778 """ Send a block of an image to RAM """ 779 def mem_block(self, data, seq): 780 return self.check_command("write to target RAM", self.ESP_MEM_DATA, 781 struct.pack('<IIII', len(data), seq, 0, 0) + data, 782 self.checksum(data)) 783 784 """ Leave download mode and run the application """ 785 def mem_finish(self, entrypoint=0): 786 # Sending ESP_MEM_END usually sends a correct response back, however sometimes 787 # (with ROM loader) the executed code may reset the UART or change the baud rate 788 # before the transmit FIFO is empty. So in these cases we set a short timeout and 789 # ignore errors. 790 timeout = DEFAULT_TIMEOUT if self.IS_STUB else MEM_END_ROM_TIMEOUT 791 data = struct.pack('<II', int(entrypoint == 0), entrypoint) 792 try: 793 return self.check_command("leave RAM download mode", self.ESP_MEM_END, 794 data=data, timeout=timeout) 795 except FatalError: 796 if self.IS_STUB: 797 raise 798 pass 799 800 """ Start downloading to Flash (performs an erase) 801 802 Returns number of blocks (of size self.FLASH_WRITE_SIZE) to write. 803 """ 804 def flash_begin(self, size, offset, begin_rom_encrypted=False): 805 num_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE 806 erase_size = self.get_erase_size(offset, size) 807 808 t = time.time() 809 if self.IS_STUB: 810 timeout = DEFAULT_TIMEOUT 811 else: 812 timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size) # ROM performs the erase up front 813 814 params = struct.pack('<IIII', erase_size, num_blocks, self.FLASH_WRITE_SIZE, offset) 815 if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM, 816 ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB: 817 params += struct.pack('<I', 1 if begin_rom_encrypted else 0) 818 self.check_command("enter Flash download mode", self.ESP_FLASH_BEGIN, 819 params, timeout=timeout) 820 if size != 0 and not self.IS_STUB: 821 print("Took %.2fs to erase flash block" % (time.time() - t)) 822 return num_blocks 823 824 def flash_block(self, data, seq, timeout=DEFAULT_TIMEOUT): 825 """Write block to flash, retry if fail""" 826 for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1): 827 try: 828 self.check_command( 829 "write to target Flash after seq %d" % seq, 830 self.ESP_FLASH_DATA, 831 struct.pack("<IIII", len(data), seq, 0, 0) + data, 832 self.checksum(data), 833 timeout=timeout, 834 ) 835 break 836 except FatalError: 837 if attempts_left: 838 self.trace( 839 "Block write failed, " 840 "retrying with {} attempts left".format(attempts_left) 841 ) 842 else: 843 raise 844 845 def flash_encrypt_block(self, data, seq, timeout=DEFAULT_TIMEOUT): 846 """Encrypt, write block to flash, retry if fail""" 847 if isinstance(self, (ESP32S2ROM, ESP32C3ROM, ESP32S3ROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB: 848 # ROM support performs the encrypted writes via the normal write command, 849 # triggered by flash_begin(begin_rom_encrypted=True) 850 return self.flash_block(data, seq, timeout) 851 852 for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1): 853 try: 854 self.check_command( 855 "Write encrypted to target Flash after seq %d" % seq, 856 self.ESP_FLASH_ENCRYPT_DATA, 857 struct.pack("<IIII", len(data), seq, 0, 0) + data, 858 self.checksum(data), 859 timeout=timeout, 860 ) 861 break 862 except FatalError: 863 if attempts_left: 864 self.trace( 865 "Encrypted block write failed, " 866 "retrying with {} attempts left".format(attempts_left) 867 ) 868 else: 869 raise 870 871 """ Leave flash mode and run/reboot """ 872 def flash_finish(self, reboot=False): 873 pkt = struct.pack('<I', int(not reboot)) 874 # stub sends a reply to this command 875 self.check_command("leave Flash mode", self.ESP_FLASH_END, pkt) 876 877 """ Run application code in flash """ 878 def run(self, reboot=False): 879 # Fake flash begin immediately followed by flash end 880 self.flash_begin(0, 0) 881 self.flash_finish(reboot) 882 883 """ Read SPI flash manufacturer and device id """ 884 def flash_id(self): 885 SPIFLASH_RDID = 0x9F 886 return self.run_spiflash_command(SPIFLASH_RDID, b"", 24) 887 888 def get_security_info(self): 889 res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'') 890 esp32s2 = True if len(res) == 12 else False 891 res = struct.unpack("<IBBBBBBBB" if esp32s2 else "<IBBBBBBBBII", res) 892 return { 893 "flags": res[0], 894 "flash_crypt_cnt": res[1], 895 "key_purposes": res[2:9], 896 "chip_id": None if esp32s2 else res[9], 897 "api_version": None if esp32s2 else res[10], 898 } 899 900 @esp32s3_or_newer_function_only 901 def get_chip_id(self): 902 res = self.check_command('get security info', self.ESP_GET_SECURITY_INFO, b'') 903 res = struct.unpack("<IBBBBBBBBI", res[:16]) # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id 904 chip_id = res[9] # 2/4 status bytes invariant 905 return chip_id 906 907 @classmethod 908 def parse_flash_size_arg(cls, arg): 909 try: 910 return cls.FLASH_SIZES[arg] 911 except KeyError: 912 raise FatalError("Flash size '%s' is not supported by this chip type. Supported sizes: %s" 913 % (arg, ", ".join(cls.FLASH_SIZES.keys()))) 914 915 @classmethod 916 def parse_flash_freq_arg(cls, arg): 917 try: 918 return cls.FLASH_FREQUENCY[arg] 919 except KeyError: 920 raise FatalError("Flash frequency '%s' is not supported by this chip type. Supported frequencies: %s" 921 % (arg, ", ".join(cls.FLASH_FREQUENCY.keys()))) 922 923 def run_stub(self, stub=None): 924 if stub is None: 925 stub = self.STUB_CODE 926 927 if self.sync_stub_detected: 928 print("Stub is already running. No upload is necessary.") 929 return self.STUB_CLASS(self) 930 931 # Upload 932 print("Uploading stub...") 933 for field in ['text', 'data']: 934 if field in stub: 935 offs = stub[field + "_start"] 936 length = len(stub[field]) 937 blocks = (length + self.ESP_RAM_BLOCK - 1) // self.ESP_RAM_BLOCK 938 self.mem_begin(length, blocks, self.ESP_RAM_BLOCK, offs) 939 for seq in range(blocks): 940 from_offs = seq * self.ESP_RAM_BLOCK 941 to_offs = from_offs + self.ESP_RAM_BLOCK 942 self.mem_block(stub[field][from_offs:to_offs], seq) 943 print("Running stub...") 944 self.mem_finish(stub['entry']) 945 946 p = self.read() 947 if p != b'OHAI': 948 raise FatalError("Failed to start stub. Unexpected response: %s" % p) 949 print("Stub running...") 950 return self.STUB_CLASS(self) 951 952 @stub_and_esp32_function_only 953 def flash_defl_begin(self, size, compsize, offset): 954 """ Start downloading compressed data to Flash (performs an erase) 955 956 Returns number of blocks (size self.FLASH_WRITE_SIZE) to write. 957 """ 958 num_blocks = (compsize + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE 959 erase_blocks = (size + self.FLASH_WRITE_SIZE - 1) // self.FLASH_WRITE_SIZE 960 961 t = time.time() 962 if self.IS_STUB: 963 write_size = size # stub expects number of bytes here, manages erasing internally 964 timeout = DEFAULT_TIMEOUT 965 else: 966 write_size = erase_blocks * self.FLASH_WRITE_SIZE # ROM expects rounded up to erase block size 967 timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, write_size) # ROM performs the erase up front 968 print("Compressed %d bytes to %d..." % (size, compsize)) 969 params = struct.pack('<IIII', write_size, num_blocks, self.FLASH_WRITE_SIZE, offset) 970 if isinstance(self, (ESP32S2ROM, ESP32S3BETA2ROM, ESP32S3ROM, ESP32C3ROM, 971 ESP32C6BETAROM, ESP32H2BETA1ROM, ESP32C2ROM, ESP32H2BETA2ROM)) and not self.IS_STUB: 972 params += struct.pack('<I', 0) # extra param is to enter encrypted flash mode via ROM (not supported currently) 973 self.check_command("enter compressed flash mode", self.ESP_FLASH_DEFL_BEGIN, params, timeout=timeout) 974 if size != 0 and not self.IS_STUB: 975 # (stub erases as it writes, but ROM loaders erase on begin) 976 print("Took %.2fs to erase flash block" % (time.time() - t)) 977 return num_blocks 978 979 @stub_and_esp32_function_only 980 def flash_defl_block(self, data, seq, timeout=DEFAULT_TIMEOUT): 981 """Write block to flash, send compressed, retry if fail""" 982 for attempts_left in range(WRITE_BLOCK_ATTEMPTS - 1, -1, -1): 983 try: 984 self.check_command( 985 "write compressed data to flash after seq %d" % seq, 986 self.ESP_FLASH_DEFL_DATA, 987 struct.pack("<IIII", len(data), seq, 0, 0) + data, 988 self.checksum(data), 989 timeout=timeout, 990 ) 991 break 992 except FatalError: 993 if attempts_left: 994 self.trace( 995 "Compressed block write failed, " 996 "retrying with {} attempts left".format(attempts_left) 997 ) 998 else: 999 raise 1000 1001 """ Leave compressed flash mode and run/reboot """ 1002 @stub_and_esp32_function_only 1003 def flash_defl_finish(self, reboot=False): 1004 if not reboot and not self.IS_STUB: 1005 # skip sending flash_finish to ROM loader, as this 1006 # exits the bootloader. Stub doesn't do this. 1007 return 1008 pkt = struct.pack('<I', int(not reboot)) 1009 self.check_command("leave compressed flash mode", self.ESP_FLASH_DEFL_END, pkt) 1010 self.in_bootloader = False 1011 1012 @stub_and_esp32_function_only 1013 def flash_md5sum(self, addr, size): 1014 # the MD5 command returns additional bytes in the standard 1015 # command reply slot 1016 timeout = timeout_per_mb(MD5_TIMEOUT_PER_MB, size) 1017 res = self.check_command('calculate md5sum', self.ESP_SPI_FLASH_MD5, struct.pack('<IIII', addr, size, 0, 0), 1018 timeout=timeout) 1019 1020 if len(res) == 32: 1021 return res.decode("utf-8") # already hex formatted 1022 elif len(res) == 16: 1023 return hexify(res).lower() 1024 else: 1025 raise FatalError("MD5Sum command returned unexpected result: %r" % res) 1026 1027 @stub_and_esp32_function_only 1028 def change_baud(self, baud): 1029 print("Changing baud rate to %d" % baud) 1030 # stub takes the new baud rate and the old one 1031 second_arg = self._port.baudrate if self.IS_STUB else 0 1032 self.command(self.ESP_CHANGE_BAUDRATE, struct.pack('<II', baud, second_arg)) 1033 print("Changed.") 1034 self._set_port_baudrate(baud) 1035 time.sleep(0.05) # get rid of crap sent during baud rate change 1036 self.flush_input() 1037 1038 @stub_function_only 1039 def erase_flash(self): 1040 # depending on flash chip model the erase may take this long (maybe longer!) 1041 self.check_command("erase flash", self.ESP_ERASE_FLASH, 1042 timeout=CHIP_ERASE_TIMEOUT) 1043 1044 @stub_function_only 1045 def erase_region(self, offset, size): 1046 if offset % self.FLASH_SECTOR_SIZE != 0: 1047 raise FatalError("Offset to erase from must be a multiple of 4096") 1048 if size % self.FLASH_SECTOR_SIZE != 0: 1049 raise FatalError("Size of data to erase must be a multiple of 4096") 1050 timeout = timeout_per_mb(ERASE_REGION_TIMEOUT_PER_MB, size) 1051 self.check_command("erase region", self.ESP_ERASE_REGION, struct.pack('<II', offset, size), timeout=timeout) 1052 1053 def read_flash_slow(self, offset, length, progress_fn): 1054 raise NotImplementedInROMError(self, self.read_flash_slow) 1055 1056 def read_flash(self, offset, length, progress_fn=None): 1057 if not self.IS_STUB: 1058 return self.read_flash_slow(offset, length, progress_fn) # ROM-only routine 1059 1060 # issue a standard bootloader command to trigger the read 1061 self.check_command("read flash", self.ESP_READ_FLASH, 1062 struct.pack('<IIII', 1063 offset, 1064 length, 1065 self.FLASH_SECTOR_SIZE, 1066 64)) 1067 # now we expect (length // block_size) SLIP frames with the data 1068 data = b'' 1069 while len(data) < length: 1070 p = self.read() 1071 data += p 1072 if len(data) < length and len(p) < self.FLASH_SECTOR_SIZE: 1073 raise FatalError('Corrupt data, expected 0x%x bytes but received 0x%x bytes' % (self.FLASH_SECTOR_SIZE, len(p))) 1074 self.write(struct.pack('<I', len(data))) 1075 if progress_fn and (len(data) % 1024 == 0 or len(data) == length): 1076 progress_fn(len(data), length) 1077 if progress_fn: 1078 progress_fn(len(data), length) 1079 if len(data) > length: 1080 raise FatalError('Read more than expected') 1081 1082 digest_frame = self.read() 1083 if len(digest_frame) != 16: 1084 raise FatalError('Expected digest, got: %s' % hexify(digest_frame)) 1085 expected_digest = hexify(digest_frame).upper() 1086 digest = hashlib.md5(data).hexdigest().upper() 1087 if digest != expected_digest: 1088 raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) 1089 return data 1090 1091 def flash_spi_attach(self, hspi_arg): 1092 """Send SPI attach command to enable the SPI flash pins 1093 1094 ESP8266 ROM does this when you send flash_begin, ESP32 ROM 1095 has it as a SPI command. 1096 """ 1097 # last 3 bytes in ESP_SPI_ATTACH argument are reserved values 1098 arg = struct.pack('<I', hspi_arg) 1099 if not self.IS_STUB: 1100 # ESP32 ROM loader takes additional 'is legacy' arg, which is not 1101 # currently supported in the stub loader or esptool.py (as it's not usually needed.) 1102 is_legacy = 0 1103 arg += struct.pack('BBBB', is_legacy, 0, 0, 0) 1104 self.check_command("configure SPI flash pins", ESP32ROM.ESP_SPI_ATTACH, arg) 1105 1106 def flash_set_parameters(self, size): 1107 """Tell the ESP bootloader the parameters of the chip 1108 1109 Corresponds to the "flashchip" data structure that the ROM 1110 has in RAM. 1111 1112 'size' is in bytes. 1113 1114 All other flash parameters are currently hardcoded (on ESP8266 1115 these are mostly ignored by ROM code, on ESP32 I'm not sure.) 1116 """ 1117 fl_id = 0 1118 total_size = size 1119 block_size = 64 * 1024 1120 sector_size = 4 * 1024 1121 page_size = 256 1122 status_mask = 0xffff 1123 self.check_command("set SPI params", ESP32ROM.ESP_SPI_SET_PARAMS, 1124 struct.pack('<IIIIII', fl_id, total_size, block_size, sector_size, page_size, status_mask)) 1125 1126 def run_spiflash_command(self, spiflash_command, data=b"", read_bits=0, addr=None, addr_len=0, dummy_len=0): 1127 """Run an arbitrary SPI flash command. 1128 1129 This function uses the "USR_COMMAND" functionality in the ESP 1130 SPI hardware, rather than the precanned commands supported by 1131 hardware. So the value of spiflash_command is an actual command 1132 byte, sent over the wire. 1133 1134 After writing command byte, writes 'data' to MOSI and then 1135 reads back 'read_bits' of reply on MISO. Result is a number. 1136 """ 1137 1138 # SPI_USR register flags 1139 SPI_USR_COMMAND = (1 << 31) 1140 SPI_USR_ADDR = (1 << 30) 1141 SPI_USR_DUMMY = (1 << 29) 1142 SPI_USR_MISO = (1 << 28) 1143 SPI_USR_MOSI = (1 << 27) 1144 1145 # SPI registers, base address differs ESP32* vs 8266 1146 base = self.SPI_REG_BASE 1147 SPI_CMD_REG = base + 0x00 1148 SPI_ADDR_REG = base + 0x04 1149 SPI_USR_REG = base + self.SPI_USR_OFFS 1150 SPI_USR1_REG = base + self.SPI_USR1_OFFS 1151 SPI_USR2_REG = base + self.SPI_USR2_OFFS 1152 SPI_W0_REG = base + self.SPI_W0_OFFS 1153 1154 # following two registers are ESP32 and later chips only 1155 if self.SPI_MOSI_DLEN_OFFS is not None: 1156 # ESP32 and later chips have a more sophisticated way to set up "user" commands 1157 def set_data_lengths(mosi_bits, miso_bits): 1158 SPI_MOSI_DLEN_REG = base + self.SPI_MOSI_DLEN_OFFS 1159 SPI_MISO_DLEN_REG = base + self.SPI_MISO_DLEN_OFFS 1160 if mosi_bits > 0: 1161 self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) 1162 if miso_bits > 0: 1163 self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) 1164 flags = 0 1165 if dummy_len > 0: 1166 flags |= (dummy_len - 1) 1167 if addr_len > 0: 1168 flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT 1169 if flags: 1170 self.write_reg(SPI_USR1_REG, flags) 1171 else: 1172 def set_data_lengths(mosi_bits, miso_bits): 1173 SPI_DATA_LEN_REG = SPI_USR1_REG 1174 SPI_MOSI_BITLEN_S = 17 1175 SPI_MISO_BITLEN_S = 8 1176 mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) 1177 miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) 1178 flags = (miso_mask << SPI_MISO_BITLEN_S) | (mosi_mask << SPI_MOSI_BITLEN_S) 1179 if dummy_len > 0: 1180 flags |= (dummy_len - 1) 1181 if addr_len > 0: 1182 flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT 1183 self.write_reg(SPI_DATA_LEN_REG, flags) 1184 1185 # SPI peripheral "command" bitmasks for SPI_CMD_REG 1186 SPI_CMD_USR = (1 << 18) 1187 1188 # shift values 1189 SPI_USR2_COMMAND_LEN_SHIFT = 28 1190 SPI_USR_ADDR_LEN_SHIFT = 26 1191 1192 if read_bits > 32: 1193 raise FatalError("Reading more than 32 bits back from a SPI flash operation is unsupported") 1194 if len(data) > 64: 1195 raise FatalError("Writing more than 64 bytes of data with one SPI command is unsupported") 1196 1197 data_bits = len(data) * 8 1198 old_spi_usr = self.read_reg(SPI_USR_REG) 1199 old_spi_usr2 = self.read_reg(SPI_USR2_REG) 1200 flags = SPI_USR_COMMAND 1201 if read_bits > 0: 1202 flags |= SPI_USR_MISO 1203 if data_bits > 0: 1204 flags |= SPI_USR_MOSI 1205 if addr_len > 0: 1206 flags |= SPI_USR_ADDR 1207 if dummy_len > 0: 1208 flags |= SPI_USR_DUMMY 1209 set_data_lengths(data_bits, read_bits) 1210 self.write_reg(SPI_USR_REG, flags) 1211 self.write_reg(SPI_USR2_REG, 1212 (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command) 1213 if addr and addr_len > 0: 1214 self.write_reg(SPI_ADDR_REG, addr) 1215 if data_bits == 0: 1216 self.write_reg(SPI_W0_REG, 0) # clear data register before we read it 1217 else: 1218 data = pad_to(data, 4, b'\00') # pad to 32-bit multiple 1219 words = struct.unpack("I" * (len(data) // 4), data) 1220 next_reg = SPI_W0_REG 1221 for word in words: 1222 self.write_reg(next_reg, word) 1223 next_reg += 4 1224 self.write_reg(SPI_CMD_REG, SPI_CMD_USR) 1225 1226 def wait_done(): 1227 for _ in range(10): 1228 if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: 1229 return 1230 raise FatalError("SPI command did not complete in time") 1231 wait_done() 1232 1233 status = self.read_reg(SPI_W0_REG) 1234 # restore some SPI controller registers 1235 self.write_reg(SPI_USR_REG, old_spi_usr) 1236 self.write_reg(SPI_USR2_REG, old_spi_usr2) 1237 return status 1238 1239 def read_spiflash_sfdp(self, addr, read_bits): 1240 CMD_RDSFDP = 0x5A 1241 return self.run_spiflash_command(CMD_RDSFDP, read_bits=read_bits, addr=addr, addr_len=24, dummy_len=8) 1242 1243 def read_status(self, num_bytes=2): 1244 """Read up to 24 bits (num_bytes) of SPI flash status register contents 1245 via RDSR, RDSR2, RDSR3 commands 1246 1247 Not all SPI flash supports all three commands. The upper 1 or 2 1248 bytes may be 0xFF. 1249 """ 1250 SPIFLASH_RDSR = 0x05 1251 SPIFLASH_RDSR2 = 0x35 1252 SPIFLASH_RDSR3 = 0x15 1253 1254 status = 0 1255 shift = 0 1256 for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: 1257 status += self.run_spiflash_command(cmd, read_bits=8) << shift 1258 shift += 8 1259 return status 1260 1261 def write_status(self, new_status, num_bytes=2, set_non_volatile=False): 1262 """Write up to 24 bits (num_bytes) of new status register 1263 1264 num_bytes can be 1, 2 or 3. 1265 1266 Not all flash supports the additional commands to write the 1267 second and third byte of the status register. When writing 2 1268 bytes, esptool also sends a 16-byte WRSR command (as some 1269 flash types use this instead of WRSR2.) 1270 1271 If the set_non_volatile flag is set, non-volatile bits will 1272 be set as well as volatile ones (WREN used instead of WEVSR). 1273 1274 """ 1275 SPIFLASH_WRSR = 0x01 1276 SPIFLASH_WRSR2 = 0x31 1277 SPIFLASH_WRSR3 = 0x11 1278 SPIFLASH_WEVSR = 0x50 1279 SPIFLASH_WREN = 0x06 1280 SPIFLASH_WRDI = 0x04 1281 1282 enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR 1283 1284 # try using a 16-bit WRSR (not supported by all chips) 1285 # this may be redundant, but shouldn't hurt 1286 if num_bytes == 2: 1287 self.run_spiflash_command(enable_cmd) 1288 self.run_spiflash_command(SPIFLASH_WRSR, struct.pack("<H", new_status)) 1289 1290 # also try using individual commands (also not supported by all chips for num_bytes 2 & 3) 1291 for cmd in [SPIFLASH_WRSR, SPIFLASH_WRSR2, SPIFLASH_WRSR3][0:num_bytes]: 1292 self.run_spiflash_command(enable_cmd) 1293 self.run_spiflash_command(cmd, struct.pack("B", new_status & 0xFF)) 1294 new_status >>= 8 1295 1296 self.run_spiflash_command(SPIFLASH_WRDI) 1297 1298 def get_crystal_freq(self): 1299 # Figure out the crystal frequency from the UART clock divider 1300 # Returns a normalized value in integer MHz (40 or 26 are the only supported values) 1301 # 1302 # The logic here is: 1303 # - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate 1304 # - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency 1305 # - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32) 1306 # or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor. 1307 uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK 1308 est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER 1309 norm_xtal = 40 if est_xtal > 33 else 26 1310 if abs(norm_xtal - est_xtal) > 1: 1311 print("WARNING: Detected crystal freq %.2fMHz is quite different to normalized freq %dMHz. Unsupported crystal in use?" % (est_xtal, norm_xtal)) 1312 return norm_xtal 1313 1314 def hard_reset(self): 1315 print('Hard resetting via RTS pin...') 1316 self._setRTS(True) # EN->LOW 1317 time.sleep(0.1) 1318 self._setRTS(False) 1319 1320 def soft_reset(self, stay_in_bootloader): 1321 if not self.IS_STUB: 1322 if stay_in_bootloader: 1323 return # ROM bootloader is already in bootloader! 1324 else: 1325 # 'run user code' is as close to a soft reset as we can do 1326 self.flash_begin(0, 0) 1327 self.flash_finish(False) 1328 else: 1329 if stay_in_bootloader: 1330 # soft resetting from the stub loader 1331 # will re-load the ROM bootloader 1332 self.flash_begin(0, 0) 1333 self.flash_finish(True) 1334 elif self.CHIP_NAME != "ESP8266": 1335 raise FatalError("Soft resetting is currently only supported on ESP8266") 1336 else: 1337 # running user code from stub loader requires some hacks 1338 # in the stub loader 1339 self.command(self.ESP_RUN_USER_CODE, wait_response=False) 1340 1341 def check_chip_id(self): 1342 try: 1343 chip_id = self.get_chip_id() 1344 if chip_id != self.IMAGE_CHIP_ID: 1345 print("WARNING: Chip ID {} ({}) doesn't match expected Chip ID {}. esptool may not work correctly." 1346 .format(chip_id, self.UNSUPPORTED_CHIPS.get(chip_id, 'Unknown'), self.IMAGE_CHIP_ID)) 1347 # Try to flash anyways by disabling stub 1348 self.stub_is_disabled = True 1349 except NotImplementedInROMError: 1350 pass 1351 1352 1353 class ESP8266ROM(ESPLoader): 1354 """ Access class for ESP8266 ROM bootloader 1355 """ 1356 CHIP_NAME = "ESP8266" 1357 IS_STUB = False 1358 1359 CHIP_DETECT_MAGIC_VALUE = [0xfff0c101] 1360 1361 # OTP ROM addresses 1362 ESP_OTP_MAC0 = 0x3ff00050 1363 ESP_OTP_MAC1 = 0x3ff00054 1364 ESP_OTP_MAC3 = 0x3ff0005c 1365 1366 SPI_REG_BASE = 0x60000200 1367 SPI_USR_OFFS = 0x1c 1368 SPI_USR1_OFFS = 0x20 1369 SPI_USR2_OFFS = 0x24 1370 SPI_MOSI_DLEN_OFFS = None 1371 SPI_MISO_DLEN_OFFS = None 1372 SPI_W0_OFFS = 0x40 1373 1374 UART_CLKDIV_REG = 0x60000014 1375 1376 XTAL_CLK_DIVIDER = 2 1377 1378 FLASH_SIZES = { 1379 '512KB': 0x00, 1380 '256KB': 0x10, 1381 '1MB': 0x20, 1382 '2MB': 0x30, 1383 '4MB': 0x40, 1384 '2MB-c1': 0x50, 1385 '4MB-c1': 0x60, 1386 '8MB': 0x80, 1387 '16MB': 0x90, 1388 } 1389 1390 FLASH_FREQUENCY = { 1391 '80m': 0xf, 1392 '40m': 0x0, 1393 '26m': 0x1, 1394 '20m': 0x2, 1395 } 1396 1397 BOOTLOADER_FLASH_OFFSET = 0 1398 1399 MEMORY_MAP = [[0x3FF00000, 0x3FF00010, "DPORT"], 1400 [0x3FFE8000, 0x40000000, "DRAM"], 1401 [0x40100000, 0x40108000, "IRAM"], 1402 [0x40201010, 0x402E1010, "IROM"]] 1403 1404 def get_efuses(self): 1405 # Return the 128 bits of ESP8266 efuse as a single Python integer 1406 result = self.read_reg(0x3ff0005c) << 96 1407 result |= self.read_reg(0x3ff00058) << 64 1408 result |= self.read_reg(0x3ff00054) << 32 1409 result |= self.read_reg(0x3ff00050) 1410 return result 1411 1412 def _get_flash_size(self, efuses): 1413 # rX_Y = EFUSE_DATA_OUTX[Y] 1414 r0_4 = (efuses & (1 << 4)) != 0 1415 r3_25 = (efuses & (1 << 121)) != 0 1416 r3_26 = (efuses & (1 << 122)) != 0 1417 r3_27 = (efuses & (1 << 123)) != 0 1418 1419 if r0_4 and not r3_25: 1420 if not r3_27 and not r3_26: 1421 return 1 1422 elif not r3_27 and r3_26: 1423 return 2 1424 if not r0_4 and r3_25: 1425 if not r3_27 and not r3_26: 1426 return 2 1427 elif not r3_27 and r3_26: 1428 return 4 1429 return -1 1430 1431 def get_chip_description(self): 1432 efuses = self.get_efuses() 1433 is_8285 = (efuses & ((1 << 4) | 1 << 80)) != 0 # One or the other efuse bit is set for ESP8285 1434 if is_8285: 1435 flash_size = self._get_flash_size(efuses) 1436 max_temp = (efuses & (1 << 5)) != 0 # This efuse bit identifies the max flash temperature 1437 chip_name = { 1438 1: "ESP8285H08" if max_temp else "ESP8285N08", 1439 2: "ESP8285H16" if max_temp else "ESP8285N16" 1440 }.get(flash_size, "ESP8285") 1441 return chip_name 1442 return "ESP8266EX" 1443 1444 def get_chip_features(self): 1445 features = ["WiFi"] 1446 if "ESP8285" in self.get_chip_description(): 1447 features += ["Embedded Flash"] 1448 return features 1449 1450 def flash_spi_attach(self, hspi_arg): 1451 if self.IS_STUB: 1452 super(ESP8266ROM, self).flash_spi_attach(hspi_arg) 1453 else: 1454 # ESP8266 ROM has no flash_spi_attach command in serial protocol, 1455 # but flash_begin will do it 1456 self.flash_begin(0, 0) 1457 1458 def flash_set_parameters(self, size): 1459 # not implemented in ROM, but OK to silently skip for ROM 1460 if self.IS_STUB: 1461 super(ESP8266ROM, self).flash_set_parameters(size) 1462 1463 def chip_id(self): 1464 """ Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function """ 1465 id0 = self.read_reg(self.ESP_OTP_MAC0) 1466 id1 = self.read_reg(self.ESP_OTP_MAC1) 1467 return (id0 >> 24) | ((id1 & MAX_UINT24) << 8) 1468 1469 def read_mac(self): 1470 """ Read MAC from OTP ROM """ 1471 mac0 = self.read_reg(self.ESP_OTP_MAC0) 1472 mac1 = self.read_reg(self.ESP_OTP_MAC1) 1473 mac3 = self.read_reg(self.ESP_OTP_MAC3) 1474 if (mac3 != 0): 1475 oui = ((mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff) 1476 elif ((mac1 >> 16) & 0xff) == 0: 1477 oui = (0x18, 0xfe, 0x34) 1478 elif ((mac1 >> 16) & 0xff) == 1: 1479 oui = (0xac, 0xd0, 0x74) 1480 else: 1481 raise FatalError("Unknown OUI") 1482 return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) 1483 1484 def get_erase_size(self, offset, size): 1485 """ Calculate an erase size given a specific size in bytes. 1486 1487 Provides a workaround for the bootloader erase bug.""" 1488 1489 sectors_per_block = 16 1490 sector_size = self.FLASH_SECTOR_SIZE 1491 num_sectors = (size + sector_size - 1) // sector_size 1492 start_sector = offset // sector_size 1493 1494 head_sectors = sectors_per_block - (start_sector % sectors_per_block) 1495 if num_sectors < head_sectors: 1496 head_sectors = num_sectors 1497 1498 if num_sectors < 2 * head_sectors: 1499 return (num_sectors + 1) // 2 * sector_size 1500 else: 1501 return (num_sectors - head_sectors) * sector_size 1502 1503 def override_vddsdio(self, new_voltage): 1504 raise NotImplementedInROMError("Overriding VDDSDIO setting only applies to ESP32") 1505 1506 1507 class ESP8266StubLoader(ESP8266ROM): 1508 """ Access class for ESP8266 stub loader, runs on top of ROM. 1509 """ 1510 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 1511 IS_STUB = True 1512 1513 def __init__(self, rom_loader): 1514 self.secure_download_mode = rom_loader.secure_download_mode 1515 self._port = rom_loader._port 1516 self._trace_enabled = rom_loader._trace_enabled 1517 self.flush_input() # resets _slip_reader 1518 1519 def get_erase_size(self, offset, size): 1520 return size # stub doesn't have same size bug as ROM loader 1521 1522 1523 ESP8266ROM.STUB_CLASS = ESP8266StubLoader 1524 1525 1526 class ESP32ROM(ESPLoader): 1527 """Access class for ESP32 ROM bootloader 1528 1529 """ 1530 CHIP_NAME = "ESP32" 1531 IMAGE_CHIP_ID = 0 1532 IS_STUB = False 1533 1534 FPGA_SLOW_BOOT = True 1535 1536 CHIP_DETECT_MAGIC_VALUE = [0x00f01d83] 1537 1538 IROM_MAP_START = 0x400d0000 1539 IROM_MAP_END = 0x40400000 1540 1541 DROM_MAP_START = 0x3F400000 1542 DROM_MAP_END = 0x3F800000 1543 1544 # ESP32 uses a 4 byte status reply 1545 STATUS_BYTES_LENGTH = 4 1546 1547 SPI_REG_BASE = 0x3ff42000 1548 SPI_USR_OFFS = 0x1c 1549 SPI_USR1_OFFS = 0x20 1550 SPI_USR2_OFFS = 0x24 1551 SPI_MOSI_DLEN_OFFS = 0x28 1552 SPI_MISO_DLEN_OFFS = 0x2c 1553 EFUSE_RD_REG_BASE = 0x3ff5a000 1554 1555 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 1556 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = (1 << 7) # EFUSE_RD_DISABLE_DL_ENCRYPT 1557 1558 DR_REG_SYSCON_BASE = 0x3ff66000 1559 APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C 1560 APB_CTL_DATE_V = 0x1 1561 APB_CTL_DATE_S = 31 1562 1563 SPI_W0_OFFS = 0x80 1564 1565 UART_CLKDIV_REG = 0x3ff40014 1566 1567 XTAL_CLK_DIVIDER = 1 1568 1569 FLASH_SIZES = { 1570 '1MB': 0x00, 1571 '2MB': 0x10, 1572 '4MB': 0x20, 1573 '8MB': 0x30, 1574 '16MB': 0x40, 1575 '32MB': 0x50, 1576 '64MB': 0x60, 1577 '128MB': 0x70 1578 } 1579 1580 FLASH_FREQUENCY = { 1581 '80m': 0xf, 1582 '40m': 0x0, 1583 '26m': 0x1, 1584 '20m': 0x2, 1585 } 1586 1587 BOOTLOADER_FLASH_OFFSET = 0x1000 1588 1589 OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] 1590 1591 MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], 1592 [0x3F400000, 0x3F800000, "DROM"], 1593 [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], 1594 [0x3FF80000, 0x3FF82000, "RTC_DRAM"], 1595 [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], 1596 [0x3FFAE000, 0x40000000, "DRAM"], 1597 [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], 1598 [0x40000000, 0x40070000, "IROM"], 1599 [0x40070000, 0x40078000, "CACHE_PRO"], 1600 [0x40078000, 0x40080000, "CACHE_APP"], 1601 [0x40080000, 0x400A0000, "IRAM"], 1602 [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], 1603 [0x400C0000, 0x400C2000, "RTC_IRAM"], 1604 [0x400D0000, 0x40400000, "IROM"], 1605 [0x50000000, 0x50002000, "RTC_DATA"]] 1606 1607 FLASH_ENCRYPTED_WRITE_ALIGN = 32 1608 1609 """ Try to read the BLOCK1 (encryption key) and check if it is valid """ 1610 1611 def is_flash_encryption_key_valid(self): 1612 1613 """ Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 1614 this bit is at position 16 in EFUSE_BLK0_RDATA0_REG """ 1615 word0 = self.read_efuse(0) 1616 rd_disable = (word0 >> 16) & 0x1 1617 1618 # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed 1619 if rd_disable: 1620 return True 1621 else: 1622 # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. 1623 # When ESP32 has not generated AES/encryption key in BLOCK1, the contents will be readable and 0. 1624 # If the flash encryption is enabled it is expected to have a valid non-zero key. We break out on 1625 # first occurance of non-zero value 1626 key_word = [0] * 7 1627 for i in range(len(key_word)): 1628 key_word[i] = self.read_efuse(14 + i) 1629 # key is non-zero so break & return 1630 if key_word[i] != 0: 1631 return True 1632 return False 1633 1634 def get_flash_crypt_config(self): 1635 """ For flash encryption related commands we need to make sure 1636 user has programmed all the relevant efuse correctly so before 1637 writing encrypted write_flash_encrypt esptool will verify the values 1638 of flash_crypt_config to be non zero if they are not read 1639 protected. If the values are zero a warning will be printed 1640 1641 bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config 1642 this bit is at position 19 in EFUSE_BLK0_RDATA0_REG """ 1643 word0 = self.read_efuse(0) 1644 rd_disable = (word0 >> 19) & 0x1 1645 1646 if rd_disable == 0: 1647 """ we can read the flash_crypt_config efuse value 1648 so go & read it (EFUSE_BLK0_RDATA5_REG[31:28]) """ 1649 word5 = self.read_efuse(5) 1650 word5 = (word5 >> 28) & 0xF 1651 return word5 1652 else: 1653 # if read of the efuse is disabled we assume it is set correctly 1654 return 0xF 1655 1656 def get_encrypted_download_disabled(self): 1657 if self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT: 1658 return True 1659 else: 1660 return False 1661 1662 def get_pkg_version(self): 1663 word3 = self.read_efuse(3) 1664 pkg_version = (word3 >> 9) & 0x07 1665 pkg_version += ((word3 >> 2) & 0x1) << 3 1666 return pkg_version 1667 1668 # Returns new version format based on major and minor versions 1669 def get_chip_full_revision(self): 1670 return self.get_major_chip_version() * 100 + self.get_minor_chip_version() 1671 1672 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 1673 def get_chip_revision(self): 1674 return self.get_major_chip_version() 1675 1676 def get_minor_chip_version(self): 1677 return (self.read_efuse(5) >> 24) & 0x3 1678 1679 def get_major_chip_version(self): 1680 rev_bit0 = (self.read_efuse(3) >> 15) & 0x1 1681 rev_bit1 = (self.read_efuse(5) >> 20) & 0x1 1682 apb_ctl_date = self.read_reg(self.APB_CTL_DATE_ADDR) 1683 rev_bit2 = (apb_ctl_date >> self.APB_CTL_DATE_S) & self.APB_CTL_DATE_V 1684 combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 1685 1686 revision = { 1687 0: 0, 1688 1: 1, 1689 3: 2, 1690 7: 3, 1691 }.get(combine_value, 0) 1692 return revision 1693 1694 def get_chip_description(self): 1695 pkg_version = self.get_pkg_version() 1696 major_rev = self.get_major_chip_version() 1697 minor_rev = self.get_minor_chip_version() 1698 rev3 = major_rev == 3 1699 single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU 1700 1701 chip_name = { 1702 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", 1703 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", 1704 2: "ESP32-D2WD", 1705 4: "ESP32-U4WDH", 1706 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", 1707 6: "ESP32-PICO-V3-02", 1708 7: "ESP32-D0WDR2-V3", 1709 }.get(pkg_version, "unknown ESP32") 1710 1711 # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 1712 if chip_name.startswith("ESP32-D0WD") and rev3: 1713 chip_name += "-V3" 1714 1715 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 1716 1717 def get_chip_features(self): 1718 features = ["WiFi"] 1719 word3 = self.read_efuse(3) 1720 1721 # names of variables in this section are lowercase 1722 # versions of EFUSE names as documented in TRM and 1723 # ESP-IDF efuse_reg.h 1724 1725 chip_ver_dis_bt = word3 & (1 << 1) 1726 if chip_ver_dis_bt == 0: 1727 features += ["BT"] 1728 1729 chip_ver_dis_app_cpu = word3 & (1 << 0) 1730 if chip_ver_dis_app_cpu: 1731 features += ["Single Core"] 1732 else: 1733 features += ["Dual Core"] 1734 1735 chip_cpu_freq_rated = word3 & (1 << 13) 1736 if chip_cpu_freq_rated: 1737 chip_cpu_freq_low = word3 & (1 << 12) 1738 if chip_cpu_freq_low: 1739 features += ["160MHz"] 1740 else: 1741 features += ["240MHz"] 1742 1743 pkg_version = self.get_pkg_version() 1744 if pkg_version in [2, 4, 5, 6]: 1745 features += ["Embedded Flash"] 1746 1747 if pkg_version == 6: 1748 features += ["Embedded PSRAM"] 1749 1750 word4 = self.read_efuse(4) 1751 adc_vref = (word4 >> 8) & 0x1F 1752 if adc_vref: 1753 features += ["VRef calibration in efuse"] 1754 1755 blk3_part_res = word3 >> 14 & 0x1 1756 if blk3_part_res: 1757 features += ["BLK3 partially reserved"] 1758 1759 word6 = self.read_efuse(6) 1760 coding_scheme = word6 & 0x3 1761 features += ["Coding Scheme %s" % { 1762 0: "None", 1763 1: "3/4", 1764 2: "Repeat (UNSUPPORTED)", 1765 3: "Invalid"}[coding_scheme]] 1766 1767 return features 1768 1769 def read_efuse(self, n): 1770 """ Read the nth word of the ESP3x EFUSE region. """ 1771 return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) 1772 1773 def chip_id(self): 1774 raise NotSupportedError(self, "chip_id") 1775 1776 def read_mac(self): 1777 """ Read MAC from EFUSE region """ 1778 words = [self.read_efuse(2), self.read_efuse(1)] 1779 bitstring = struct.pack(">II", *words) 1780 bitstring = bitstring[2:8] # trim the 2 byte CRC 1781 try: 1782 return tuple(ord(b) for b in bitstring) 1783 except TypeError: # Python 3, bitstring elements are already bytes 1784 return tuple(bitstring) 1785 1786 def get_erase_size(self, offset, size): 1787 return size 1788 1789 def override_vddsdio(self, new_voltage): 1790 new_voltage = new_voltage.upper() 1791 if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: 1792 raise FatalError("The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'") 1793 RTC_CNTL_SDIO_CONF_REG = 0x3ff48074 1794 RTC_CNTL_XPD_SDIO_REG = (1 << 31) 1795 RTC_CNTL_DREFH_SDIO_M = (3 << 29) 1796 RTC_CNTL_DREFM_SDIO_M = (3 << 27) 1797 RTC_CNTL_DREFL_SDIO_M = (3 << 25) 1798 # RTC_CNTL_SDIO_TIEH = (1 << 23) # not used here, setting TIEH=1 would set 3.3V output, not safe for esptool.py to do 1799 RTC_CNTL_SDIO_FORCE = (1 << 22) 1800 RTC_CNTL_SDIO_PD_EN = (1 << 21) 1801 1802 reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting 1803 reg_val |= RTC_CNTL_SDIO_PD_EN 1804 if new_voltage != "OFF": 1805 reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO 1806 if new_voltage == "1.9V": 1807 reg_val |= (RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M) # boost voltage 1808 self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) 1809 print("VDDSDIO regulator set to %s" % new_voltage) 1810 1811 def read_flash_slow(self, offset, length, progress_fn): 1812 BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) 1813 1814 data = b'' 1815 while len(data) < length: 1816 block_len = min(BLOCK_LEN, length - len(data)) 1817 r = self.check_command("read flash block", self.ESP_READ_FLASH_SLOW, 1818 struct.pack('<II', offset + len(data), block_len)) 1819 if len(r) < block_len: 1820 raise FatalError("Expected %d byte block, got %d bytes. Serial errors?" % (block_len, len(r))) 1821 data += r[:block_len] # command always returns 64 byte buffer, regardless of how many bytes were actually read from flash 1822 if progress_fn and (len(data) % 1024 == 0 or len(data) == length): 1823 progress_fn(len(data), length) 1824 return data 1825 1826 1827 class ESP32S2ROM(ESP32ROM): 1828 CHIP_NAME = "ESP32-S2" 1829 IMAGE_CHIP_ID = 2 1830 1831 FPGA_SLOW_BOOT = False 1832 1833 IROM_MAP_START = 0x40080000 1834 IROM_MAP_END = 0x40b80000 1835 DROM_MAP_START = 0x3F000000 1836 DROM_MAP_END = 0x3F3F0000 1837 1838 CHIP_DETECT_MAGIC_VALUE = [0x000007c6] 1839 1840 SPI_REG_BASE = 0x3f402000 1841 SPI_USR_OFFS = 0x18 1842 SPI_USR1_OFFS = 0x1c 1843 SPI_USR2_OFFS = 0x20 1844 SPI_MOSI_DLEN_OFFS = 0x24 1845 SPI_MISO_DLEN_OFFS = 0x28 1846 SPI_W0_OFFS = 0x58 1847 1848 MAC_EFUSE_REG = 0x3f41A044 # ESP32-S2 has special block for MAC efuses 1849 1850 UART_CLKDIV_REG = 0x3f400014 1851 1852 FLASH_ENCRYPTED_WRITE_ALIGN = 16 1853 1854 # todo: use espefuse APIs to get this info 1855 EFUSE_BASE = 0x3f41A000 1856 EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address 1857 EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 1858 EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x05C 1859 1860 EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 1861 EFUSE_PURPOSE_KEY0_SHIFT = 24 1862 EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 1863 EFUSE_PURPOSE_KEY1_SHIFT = 28 1864 EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 1865 EFUSE_PURPOSE_KEY2_SHIFT = 0 1866 EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 1867 EFUSE_PURPOSE_KEY3_SHIFT = 4 1868 EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 1869 EFUSE_PURPOSE_KEY4_SHIFT = 8 1870 EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 1871 EFUSE_PURPOSE_KEY5_SHIFT = 12 1872 1873 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE 1874 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19 1875 1876 PURPOSE_VAL_XTS_AES256_KEY_1 = 2 1877 PURPOSE_VAL_XTS_AES256_KEY_2 = 3 1878 PURPOSE_VAL_XTS_AES128_KEY = 4 1879 1880 UARTDEV_BUF_NO = 0x3ffffd14 # Variable in ROM .bss which indicates the port in use 1881 UARTDEV_BUF_NO_USB = 2 # Value of the above variable indicating that USB is in use 1882 1883 USB_RAM_BLOCK = 0x800 # Max block size USB CDC is used 1884 1885 GPIO_STRAP_REG = 0x3f404038 1886 GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode 1887 RTC_CNTL_OPTION1_REG = 0x3f408128 1888 RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? 1889 1890 MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], 1891 [0x3F000000, 0x3FF80000, "DROM"], 1892 [0x3F500000, 0x3FF80000, "EXTRAM_DATA"], 1893 [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"], 1894 [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"], 1895 [0x3FF9E000, 0x40072000, "MEM_INTERNAL"], 1896 [0x3FFB0000, 0x40000000, "DRAM"], 1897 [0x40000000, 0x4001A100, "IROM_MASK"], 1898 [0x40020000, 0x40070000, "IRAM"], 1899 [0x40070000, 0x40072000, "RTC_IRAM"], 1900 [0x40080000, 0x40800000, "IROM"], 1901 [0x50000000, 0x50002000, "RTC_DATA"]] 1902 1903 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 1904 def get_chip_revision(self): 1905 return self.get_major_chip_version() 1906 1907 def get_pkg_version(self): 1908 num_word = 4 1909 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F 1910 1911 def get_minor_chip_version(self): 1912 hi_num_word = 3 1913 hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 20) & 0x01 1914 low_num_word = 4 1915 low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 4) & 0x07 1916 return (hi << 3) + low 1917 1918 def get_major_chip_version(self): 1919 num_word = 3 1920 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x03 1921 1922 def get_flash_version(self): 1923 num_word = 3 1924 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F 1925 1926 def get_psram_version(self): 1927 num_word = 3 1928 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F 1929 1930 def get_block2_version(self): 1931 # BLK_VERSION_MINOR 1932 num_word = 4 1933 return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 4) & 0x07 1934 1935 def get_chip_description(self): 1936 chip_name = { 1937 0: "ESP32-S2", 1938 1: "ESP32-S2FH2", 1939 2: "ESP32-S2FH4", 1940 102: "ESP32-S2FNR2", 1941 100: "ESP32-S2R2", 1942 }.get(self.get_flash_version() + self.get_psram_version() * 100, "unknown ESP32-S2") 1943 1944 major_rev = self.get_major_chip_version() 1945 minor_rev = self.get_minor_chip_version() 1946 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 1947 1948 def get_chip_features(self): 1949 features = ["WiFi"] 1950 1951 if self.secure_download_mode: 1952 features += ["Secure Download Mode Enabled"] 1953 1954 flash_version = { 1955 0: "No Embedded Flash", 1956 1: "Embedded Flash 2MB", 1957 2: "Embedded Flash 4MB", 1958 }.get(self.get_flash_version(), "Unknown Embedded Flash") 1959 features += [flash_version] 1960 1961 psram_version = { 1962 0: "No Embedded PSRAM", 1963 1: "Embedded PSRAM 2MB", 1964 2: "Embedded PSRAM 4MB", 1965 }.get(self.get_psram_version(), "Unknown Embedded PSRAM") 1966 features += [psram_version] 1967 1968 block2_version = { 1969 0: "No calibration in BLK2 of efuse", 1970 1: "ADC and temperature sensor calibration in BLK2 of efuse V1", 1971 2: "ADC and temperature sensor calibration in BLK2 of efuse V2", 1972 }.get(self.get_block2_version(), "Unknown Calibration in BLK2") 1973 features += [block2_version] 1974 1975 return features 1976 1977 def get_crystal_freq(self): 1978 # ESP32-S2 XTAL is fixed to 40MHz 1979 return 40 1980 1981 def override_vddsdio(self, new_voltage): 1982 raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S2") 1983 1984 def read_mac(self): 1985 mac0 = self.read_reg(self.MAC_EFUSE_REG) 1986 mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC 1987 bitstring = struct.pack(">II", mac1, mac0)[2:] 1988 try: 1989 return tuple(ord(b) for b in bitstring) 1990 except TypeError: # Python 3, bitstring elements are already bytes 1991 return tuple(bitstring) 1992 1993 def get_flash_crypt_config(self): 1994 return None # doesn't exist on ESP32-S2 1995 1996 def get_key_block_purpose(self, key_block): 1997 if key_block < 0 or key_block > 5: 1998 raise FatalError("Valid key block numbers must be in range 0-5") 1999 2000 reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), 2001 (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), 2002 (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), 2003 (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), 2004 (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), 2005 (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] 2006 return (self.read_reg(reg) >> shift) & 0xF 2007 2008 def is_flash_encryption_key_valid(self): 2009 # Need to see either an AES-128 key or two AES-256 keys 2010 purposes = [self.get_key_block_purpose(b) for b in range(6)] 2011 2012 if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): 2013 return True 2014 2015 return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ 2016 and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) 2017 2018 def uses_usb(self, _cache=[]): 2019 if self.secure_download_mode: 2020 return False # can't detect native USB in secure download mode 2021 if not _cache: 2022 buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff 2023 _cache.append(buf_no == self.UARTDEV_BUF_NO_USB) 2024 return _cache[0] 2025 2026 def _post_connect(self): 2027 if self.uses_usb(): 2028 self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK 2029 2030 def _check_if_can_reset(self): 2031 """ 2032 Check the strapping register to see if we can reset out of download mode. 2033 """ 2034 if os.getenv("ESPTOOL_TESTING") is not None: 2035 print("ESPTOOL_TESTING is set, ignoring strapping mode check") 2036 # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case. 2037 return 2038 strap_reg = self.read_reg(self.GPIO_STRAP_REG) 2039 force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) 2040 if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0: 2041 print("WARNING: {} chip was placed into download mode using GPIO0.\n" 2042 "esptool.py can not exit the download mode over USB. " 2043 "To run the app, reset the chip manually.\n" 2044 "To suppress this note, set --after option to 'no_reset'.".format(self.get_chip_description())) 2045 raise SystemExit(1) 2046 2047 def hard_reset(self): 2048 if self.uses_usb(): 2049 self._check_if_can_reset() 2050 2051 print('Hard resetting via RTS pin...') 2052 self._setRTS(True) # EN->LOW 2053 if self.uses_usb(): 2054 # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions 2055 time.sleep(0.2) 2056 self._setRTS(False) 2057 time.sleep(0.2) 2058 else: 2059 time.sleep(0.1) 2060 self._setRTS(False) 2061 2062 2063 class ESP32S3ROM(ESP32ROM): 2064 CHIP_NAME = "ESP32-S3" 2065 2066 IMAGE_CHIP_ID = 9 2067 2068 CHIP_DETECT_MAGIC_VALUE = [0x9] 2069 2070 BOOTLOADER_FLASH_OFFSET = 0x0 2071 2072 FPGA_SLOW_BOOT = False 2073 2074 IROM_MAP_START = 0x42000000 2075 IROM_MAP_END = 0x44000000 2076 DROM_MAP_START = 0x3c000000 2077 DROM_MAP_END = 0x3e000000 2078 2079 UART_DATE_REG_ADDR = 0x60000080 2080 2081 SPI_REG_BASE = 0x60002000 2082 SPI_USR_OFFS = 0x18 2083 SPI_USR1_OFFS = 0x1c 2084 SPI_USR2_OFFS = 0x20 2085 SPI_MOSI_DLEN_OFFS = 0x24 2086 SPI_MISO_DLEN_OFFS = 0x28 2087 SPI_W0_OFFS = 0x58 2088 2089 FLASH_ENCRYPTED_WRITE_ALIGN = 16 2090 2091 # todo: use espefuse APIs to get this info 2092 EFUSE_BASE = 0x60007000 # BLOCK0 read base address 2093 MAC_EFUSE_REG = EFUSE_BASE + 0x044 2094 EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44 2095 EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C 2096 EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address 2097 2098 EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 2099 EFUSE_PURPOSE_KEY0_SHIFT = 24 2100 EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 2101 EFUSE_PURPOSE_KEY1_SHIFT = 28 2102 EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 2103 EFUSE_PURPOSE_KEY2_SHIFT = 0 2104 EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 2105 EFUSE_PURPOSE_KEY3_SHIFT = 4 2106 EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 2107 EFUSE_PURPOSE_KEY4_SHIFT = 8 2108 EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 2109 EFUSE_PURPOSE_KEY5_SHIFT = 12 2110 2111 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE 2112 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 2113 2114 PURPOSE_VAL_XTS_AES256_KEY_1 = 2 2115 PURPOSE_VAL_XTS_AES256_KEY_2 = 3 2116 PURPOSE_VAL_XTS_AES128_KEY = 4 2117 2118 UARTDEV_BUF_NO = 0x3fcef14c # Variable in ROM .bss which indicates the port in use 2119 UARTDEV_BUF_NO_USB = 3 # Value of the above variable indicating that USB is in use 2120 2121 USB_RAM_BLOCK = 0x800 # Max block size USB CDC is used 2122 2123 GPIO_STRAP_REG = 0x60004038 2124 GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode 2125 RTC_CNTL_OPTION1_REG = 0x6000812C 2126 RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? 2127 2128 UART_CLKDIV_REG = 0x60000014 2129 2130 MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], 2131 [0x3C000000, 0x3D000000, "DROM"], 2132 [0x3D000000, 0x3E000000, "EXTRAM_DATA"], 2133 [0x600FE000, 0x60100000, "RTC_DRAM"], 2134 [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], 2135 [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], 2136 [0x3FC88000, 0x3FD00000, "DRAM"], 2137 [0x40000000, 0x4001A100, "IROM_MASK"], 2138 [0x40370000, 0x403E0000, "IRAM"], 2139 [0x600FE000, 0x60100000, "RTC_IRAM"], 2140 [0x42000000, 0x42800000, "IROM"], 2141 [0x50000000, 0x50002000, "RTC_DATA"]] 2142 2143 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 2144 def get_chip_revision(self): 2145 return self.get_minor_chip_version() 2146 2147 def get_pkg_version(self): 2148 num_word = 3 2149 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 2150 2151 def is_eco0(self, minor_raw): 2152 # Workaround: The major version field was allocated to other purposes 2153 # when block version is v1.1. 2154 # Luckily only chip v0.0 have this kind of block version and efuse usage. 2155 return ( 2156 (minor_raw & 0x7) == 0 and self.get_blk_version_major() == 1 and self.get_blk_version_minor() == 1 2157 ) 2158 2159 def get_minor_chip_version(self): 2160 minor_raw = self.get_raw_minor_chip_version() 2161 if self.is_eco0(minor_raw): 2162 return 0 2163 return minor_raw 2164 2165 def get_raw_minor_chip_version(self): 2166 hi_num_word = 5 2167 hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 2168 low_num_word = 3 2169 low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 2170 return (hi << 3) + low 2171 2172 def get_blk_version_major(self): 2173 num_word = 4 2174 return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03 2175 2176 def get_blk_version_minor(self): 2177 num_word = 3 2178 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07 2179 2180 def get_major_chip_version(self): 2181 minor_raw = self.get_raw_minor_chip_version() 2182 if self.is_eco0(minor_raw): 2183 return 0 2184 return self.get_raw_major_chip_version() 2185 2186 def get_raw_major_chip_version(self): 2187 num_word = 5 2188 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 2189 2190 def get_chip_description(self): 2191 major_rev = self.get_major_chip_version() 2192 minor_rev = self.get_minor_chip_version() 2193 return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev) 2194 2195 def get_chip_features(self): 2196 return ["WiFi", "BLE"] 2197 2198 def get_crystal_freq(self): 2199 # ESP32S3 XTAL is fixed to 40MHz 2200 return 40 2201 2202 def get_flash_crypt_config(self): 2203 return None # doesn't exist on ESP32-S3 2204 2205 def get_key_block_purpose(self, key_block): 2206 if key_block < 0 or key_block > 5: 2207 raise FatalError("Valid key block numbers must be in range 0-5") 2208 2209 reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), 2210 (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), 2211 (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), 2212 (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), 2213 (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), 2214 (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] 2215 return (self.read_reg(reg) >> shift) & 0xF 2216 2217 def is_flash_encryption_key_valid(self): 2218 # Need to see either an AES-128 key or two AES-256 keys 2219 purposes = [self.get_key_block_purpose(b) for b in range(6)] 2220 2221 if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): 2222 return True 2223 2224 return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) \ 2225 and any(p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes) 2226 2227 def override_vddsdio(self, new_voltage): 2228 raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-S3") 2229 2230 def read_mac(self): 2231 mac0 = self.read_reg(self.MAC_EFUSE_REG) 2232 mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC 2233 bitstring = struct.pack(">II", mac1, mac0)[2:] 2234 try: 2235 return tuple(ord(b) for b in bitstring) 2236 except TypeError: # Python 3, bitstring elements are already bytes 2237 return tuple(bitstring) 2238 2239 def uses_usb(self, _cache=[]): 2240 if self.secure_download_mode: 2241 return False # can't detect native USB in secure download mode 2242 if not _cache: 2243 buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xff 2244 _cache.append(buf_no == self.UARTDEV_BUF_NO_USB) 2245 return _cache[0] 2246 2247 def _post_connect(self): 2248 if self.uses_usb(): 2249 self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK 2250 2251 def _check_if_can_reset(self): 2252 """ 2253 Check the strapping register to see if we can reset out of download mode. 2254 """ 2255 if os.getenv("ESPTOOL_TESTING") is not None: 2256 print("ESPTOOL_TESTING is set, ignoring strapping mode check") 2257 # Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case. 2258 return 2259 strap_reg = self.read_reg(self.GPIO_STRAP_REG) 2260 force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) 2261 if strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0: 2262 print("WARNING: {} chip was placed into download mode using GPIO0.\n" 2263 "esptool.py can not exit the download mode over USB. " 2264 "To run the app, reset the chip manually.\n" 2265 "To suppress this note, set --after option to 'no_reset'.".format(self.get_chip_description())) 2266 raise SystemExit(1) 2267 2268 def hard_reset(self): 2269 if self.uses_usb(): 2270 self._check_if_can_reset() 2271 2272 print('Hard resetting via RTS pin...') 2273 self._setRTS(True) # EN->LOW 2274 if self.uses_usb(): 2275 # Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions 2276 time.sleep(0.2) 2277 self._setRTS(False) 2278 time.sleep(0.2) 2279 else: 2280 time.sleep(0.1) 2281 self._setRTS(False) 2282 2283 2284 class ESP32S3BETA2ROM(ESP32S3ROM): 2285 CHIP_NAME = "ESP32-S3(beta2)" 2286 IMAGE_CHIP_ID = 4 2287 2288 CHIP_DETECT_MAGIC_VALUE = [0xeb004136] 2289 2290 EFUSE_BASE = 0x6001A000 # BLOCK0 read base address 2291 2292 def get_chip_description(self): 2293 major_rev = self.get_major_chip_version() 2294 minor_rev = self.get_minor_chip_version() 2295 return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev) 2296 2297 2298 class ESP32C3ROM(ESP32ROM): 2299 CHIP_NAME = "ESP32-C3" 2300 IMAGE_CHIP_ID = 5 2301 2302 FPGA_SLOW_BOOT = False 2303 2304 IROM_MAP_START = 0x42000000 2305 IROM_MAP_END = 0x42800000 2306 DROM_MAP_START = 0x3c000000 2307 DROM_MAP_END = 0x3c800000 2308 2309 SPI_REG_BASE = 0x60002000 2310 SPI_USR_OFFS = 0x18 2311 SPI_USR1_OFFS = 0x1C 2312 SPI_USR2_OFFS = 0x20 2313 SPI_MOSI_DLEN_OFFS = 0x24 2314 SPI_MISO_DLEN_OFFS = 0x28 2315 SPI_W0_OFFS = 0x58 2316 2317 BOOTLOADER_FLASH_OFFSET = 0x0 2318 2319 # Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly 2320 CHIP_DETECT_MAGIC_VALUE = [0x6921506f, 0x1b31506f] 2321 2322 UART_DATE_REG_ADDR = 0x60000000 + 0x7c 2323 2324 EFUSE_BASE = 0x60008800 2325 EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 2326 MAC_EFUSE_REG = EFUSE_BASE + 0x044 2327 2328 EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address 2329 2330 EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 2331 EFUSE_PURPOSE_KEY0_SHIFT = 24 2332 EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 2333 EFUSE_PURPOSE_KEY1_SHIFT = 28 2334 EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 2335 EFUSE_PURPOSE_KEY2_SHIFT = 0 2336 EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 2337 EFUSE_PURPOSE_KEY3_SHIFT = 4 2338 EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 2339 EFUSE_PURPOSE_KEY4_SHIFT = 8 2340 EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 2341 EFUSE_PURPOSE_KEY5_SHIFT = 12 2342 2343 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE 2344 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 2345 2346 PURPOSE_VAL_XTS_AES128_KEY = 4 2347 2348 GPIO_STRAP_REG = 0x3f404038 2349 2350 FLASH_ENCRYPTED_WRITE_ALIGN = 16 2351 2352 MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], 2353 [0x3C000000, 0x3C800000, "DROM"], 2354 [0x3FC80000, 0x3FCE0000, "DRAM"], 2355 [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], 2356 [0x3FF00000, 0x3FF20000, "DROM_MASK"], 2357 [0x40000000, 0x40060000, "IROM_MASK"], 2358 [0x42000000, 0x42800000, "IROM"], 2359 [0x4037C000, 0x403E0000, "IRAM"], 2360 [0x50000000, 0x50002000, "RTC_IRAM"], 2361 [0x50000000, 0x50002000, "RTC_DRAM"], 2362 [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] 2363 2364 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 2365 def get_chip_revision(self): 2366 return self.get_minor_chip_version() 2367 2368 def get_pkg_version(self): 2369 num_word = 3 2370 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 2371 2372 def get_minor_chip_version(self): 2373 hi_num_word = 5 2374 hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 2375 low_num_word = 3 2376 low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 2377 return (hi << 3) + low 2378 2379 def get_major_chip_version(self): 2380 num_word = 5 2381 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 2382 2383 def get_chip_description(self): 2384 chip_name = { 2385 0: "ESP32-C3", 2386 }.get(self.get_pkg_version(), "unknown ESP32-C3") 2387 major_rev = self.get_major_chip_version() 2388 minor_rev = self.get_minor_chip_version() 2389 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 2390 2391 def get_chip_features(self): 2392 return ["Wi-Fi"] 2393 2394 def get_crystal_freq(self): 2395 # ESP32C3 XTAL is fixed to 40MHz 2396 return 40 2397 2398 def override_vddsdio(self, new_voltage): 2399 raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-C3") 2400 2401 def read_mac(self): 2402 mac0 = self.read_reg(self.MAC_EFUSE_REG) 2403 mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC 2404 bitstring = struct.pack(">II", mac1, mac0)[2:] 2405 try: 2406 return tuple(ord(b) for b in bitstring) 2407 except TypeError: # Python 3, bitstring elements are already bytes 2408 return tuple(bitstring) 2409 2410 def get_flash_crypt_config(self): 2411 return None # doesn't exist on ESP32-C3 2412 2413 def get_key_block_purpose(self, key_block): 2414 if key_block < 0 or key_block > 5: 2415 raise FatalError("Valid key block numbers must be in range 0-5") 2416 2417 reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), 2418 (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), 2419 (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), 2420 (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), 2421 (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), 2422 (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] 2423 return (self.read_reg(reg) >> shift) & 0xF 2424 2425 def is_flash_encryption_key_valid(self): 2426 # Need to see an AES-128 key 2427 purposes = [self.get_key_block_purpose(b) for b in range(6)] 2428 2429 return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) 2430 2431 2432 class ESP32H2BETA1ROM(ESP32ROM): 2433 CHIP_NAME = "ESP32-H2(beta1)" 2434 IMAGE_CHIP_ID = 10 2435 2436 IROM_MAP_START = 0x42000000 2437 IROM_MAP_END = 0x42800000 2438 DROM_MAP_START = 0x3c000000 2439 DROM_MAP_END = 0x3c800000 2440 2441 SPI_REG_BASE = 0x60002000 2442 SPI_USR_OFFS = 0x18 2443 SPI_USR1_OFFS = 0x1C 2444 SPI_USR2_OFFS = 0x20 2445 SPI_MOSI_DLEN_OFFS = 0x24 2446 SPI_MISO_DLEN_OFFS = 0x28 2447 SPI_W0_OFFS = 0x58 2448 2449 BOOTLOADER_FLASH_OFFSET = 0x0 2450 2451 CHIP_DETECT_MAGIC_VALUE = [0xca26cc22] 2452 2453 UART_DATE_REG_ADDR = 0x60000000 + 0x7c 2454 2455 EFUSE_BASE = 0x6001A000 2456 EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 2457 MAC_EFUSE_REG = EFUSE_BASE + 0x044 2458 2459 EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address 2460 2461 EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 2462 EFUSE_PURPOSE_KEY0_SHIFT = 24 2463 EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 2464 EFUSE_PURPOSE_KEY1_SHIFT = 28 2465 EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 2466 EFUSE_PURPOSE_KEY2_SHIFT = 0 2467 EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 2468 EFUSE_PURPOSE_KEY3_SHIFT = 4 2469 EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 2470 EFUSE_PURPOSE_KEY4_SHIFT = 8 2471 EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 2472 EFUSE_PURPOSE_KEY5_SHIFT = 12 2473 2474 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE 2475 EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 2476 2477 PURPOSE_VAL_XTS_AES128_KEY = 4 2478 2479 GPIO_STRAP_REG = 0x3f404038 2480 2481 FLASH_ENCRYPTED_WRITE_ALIGN = 16 2482 2483 MEMORY_MAP = [] 2484 2485 FLASH_FREQUENCY = { 2486 '48m': 0xf, 2487 '24m': 0x0, 2488 '16m': 0x1, 2489 '12m': 0x2, 2490 } 2491 2492 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 2493 def get_chip_revision(self): 2494 return 0 2495 2496 def get_pkg_version(self): 2497 num_word = 4 2498 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x07 2499 2500 def get_minor_chip_version(self): 2501 num_word = 3 2502 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x07 2503 2504 def get_major_chip_version(self): 2505 num_word = 3 2506 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x03 2507 2508 def get_chip_description(self): 2509 chip_name = { 2510 0: "ESP32-H2", 2511 }.get(self.get_pkg_version(), "unknown ESP32-H2") 2512 major_rev = self.get_major_chip_version() 2513 minor_rev = self.get_minor_chip_version() 2514 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 2515 2516 def get_chip_features(self): 2517 return ["BLE/802.15.4"] 2518 2519 def get_crystal_freq(self): 2520 return 32 2521 2522 def override_vddsdio(self, new_voltage): 2523 raise NotImplementedInROMError("VDD_SDIO overrides are not supported for ESP32-H2") 2524 2525 def read_mac(self): 2526 mac0 = self.read_reg(self.MAC_EFUSE_REG) 2527 mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC 2528 bitstring = struct.pack(">II", mac1, mac0)[2:] 2529 try: 2530 return tuple(ord(b) for b in bitstring) 2531 except TypeError: # Python 3, bitstring elements are already bytes 2532 return tuple(bitstring) 2533 2534 def get_flash_crypt_config(self): 2535 return None # doesn't exist on ESP32-H2 2536 2537 def get_key_block_purpose(self, key_block): 2538 if key_block < 0 or key_block > 5: 2539 raise FatalError("Valid key block numbers must be in range 0-5") 2540 2541 reg, shift = [(self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), 2542 (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), 2543 (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), 2544 (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), 2545 (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), 2546 (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT)][key_block] 2547 return (self.read_reg(reg) >> shift) & 0xF 2548 2549 def is_flash_encryption_key_valid(self): 2550 # Need to see an AES-128 key 2551 purposes = [self.get_key_block_purpose(b) for b in range(6)] 2552 2553 return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) 2554 2555 2556 class ESP32H2BETA2ROM(ESP32H2BETA1ROM): 2557 CHIP_NAME = "ESP32-H2(beta2)" 2558 IMAGE_CHIP_ID = 14 2559 2560 def get_chip_description(self): 2561 major_rev = self.get_major_chip_version() 2562 minor_rev = self.get_minor_chip_version() 2563 return "%s (revision v%d.%d)" % (self.CHIP_NAME, major_rev, minor_rev) 2564 2565 2566 class ESP32C2ROM(ESP32C3ROM): 2567 CHIP_NAME = "ESP32-C2" 2568 IMAGE_CHIP_ID = 12 2569 2570 IROM_MAP_START = 0x42000000 2571 IROM_MAP_END = 0x42400000 2572 DROM_MAP_START = 0x3c000000 2573 DROM_MAP_END = 0x3c400000 2574 2575 # Magic value for ESP32C2 ECO0 and ECO1 respectively 2576 CHIP_DETECT_MAGIC_VALUE = [0x6F51306F, 0x7c41a06f] 2577 2578 EFUSE_BASE = 0x60008800 2579 EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x040 2580 MAC_EFUSE_REG = EFUSE_BASE + 0x040 2581 2582 FLASH_FREQUENCY = { 2583 '60m': 0xf, 2584 '30m': 0x0, 2585 '20m': 0x1, 2586 '15m': 0x2, 2587 } 2588 2589 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 2590 def get_chip_revision(self): 2591 return self.get_major_chip_version() 2592 2593 def get_pkg_version(self): 2594 num_word = 1 2595 return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 22) & 0x07 2596 2597 def get_chip_description(self): 2598 chip_name = { 2599 0: "ESP32-C2", 2600 1: "ESP32-C2", 2601 }.get(self.get_pkg_version(), "unknown ESP32-C2") 2602 major_rev = self.get_major_chip_version() 2603 minor_rev = self.get_minor_chip_version() 2604 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 2605 2606 def get_minor_chip_version(self): 2607 num_word = 1 2608 return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF 2609 2610 def get_major_chip_version(self): 2611 num_word = 1 2612 return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3 2613 2614 def _post_connect(self): 2615 # ESP32C2 ECO0 is no longer supported by the flasher stub 2616 if self.get_chip_revision() == 0: 2617 self.stub_is_disabled = True 2618 self.IS_STUB = False 2619 2620 2621 class ESP32C6BETAROM(ESP32C3ROM): 2622 CHIP_NAME = "ESP32-C6(beta)" 2623 IMAGE_CHIP_ID = 7 2624 2625 CHIP_DETECT_MAGIC_VALUE = [0x0da1806f] 2626 2627 UART_DATE_REG_ADDR = 0x00000500 2628 2629 # Returns old version format (ECO number). Use the new format get_chip_full_revision(). 2630 def get_chip_revision(self): 2631 return 0 2632 2633 def get_pkg_version(self): 2634 num_word = 3 2635 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 29) & 0x07 2636 2637 def get_minor_chip_version(self): 2638 num_word = 3 2639 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x0F 2640 2641 def get_major_chip_version(self): 2642 num_word = 3 2643 return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 22) & 0x03 2644 2645 def get_chip_description(self): 2646 chip_name = { 2647 0: "ESP32-C6", 2648 }.get(self.get_pkg_version(), "unknown ESP32-C6") 2649 major_rev = self.get_major_chip_version() 2650 minor_rev = self.get_minor_chip_version() 2651 return "%s (revision v%d.%d)" % (chip_name, major_rev, minor_rev) 2652 2653 2654 class ESP32StubLoader(ESP32ROM): 2655 """ Access class for ESP32 stub loader, runs on top of ROM. 2656 """ 2657 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2658 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2659 IS_STUB = True 2660 2661 def __init__(self, rom_loader): 2662 self.secure_download_mode = rom_loader.secure_download_mode 2663 self._port = rom_loader._port 2664 self._trace_enabled = rom_loader._trace_enabled 2665 self.flush_input() # resets _slip_reader 2666 2667 2668 ESP32ROM.STUB_CLASS = ESP32StubLoader 2669 2670 2671 class ESP32S2StubLoader(ESP32S2ROM): 2672 """ Access class for ESP32-S2 stub loader, runs on top of ROM. 2673 2674 (Basically the same as ESP32StubLoader, but different base class. 2675 Can possibly be made into a mixin.) 2676 """ 2677 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2678 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2679 IS_STUB = True 2680 2681 def __init__(self, rom_loader): 2682 self.secure_download_mode = rom_loader.secure_download_mode 2683 self._port = rom_loader._port 2684 self._trace_enabled = rom_loader._trace_enabled 2685 self.flush_input() # resets _slip_reader 2686 2687 if rom_loader.uses_usb(): 2688 self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK 2689 self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK 2690 2691 2692 ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader 2693 2694 2695 class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): 2696 """ Access class for ESP32S3 stub loader, runs on top of ROM. 2697 2698 (Basically the same as ESP32StubLoader, but different base class. 2699 Can possibly be made into a mixin.) 2700 """ 2701 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2702 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2703 IS_STUB = True 2704 2705 def __init__(self, rom_loader): 2706 self.secure_download_mode = rom_loader.secure_download_mode 2707 self._port = rom_loader._port 2708 self._trace_enabled = rom_loader._trace_enabled 2709 self.flush_input() # resets _slip_reader 2710 2711 2712 ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader 2713 2714 2715 class ESP32S3StubLoader(ESP32S3ROM): 2716 """ Access class for ESP32S3 stub loader, runs on top of ROM. 2717 2718 (Basically the same as ESP32StubLoader, but different base class. 2719 Can possibly be made into a mixin.) 2720 """ 2721 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2722 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2723 IS_STUB = True 2724 2725 def __init__(self, rom_loader): 2726 self.secure_download_mode = rom_loader.secure_download_mode 2727 self._port = rom_loader._port 2728 self._trace_enabled = rom_loader._trace_enabled 2729 self.flush_input() # resets _slip_reader 2730 2731 if rom_loader.uses_usb(): 2732 self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK 2733 self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK 2734 2735 2736 ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader 2737 2738 2739 class ESP32C3StubLoader(ESP32C3ROM): 2740 """ Access class for ESP32C3 stub loader, runs on top of ROM. 2741 2742 (Basically the same as ESP32StubLoader, but different base class. 2743 Can possibly be made into a mixin.) 2744 """ 2745 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2746 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2747 IS_STUB = True 2748 2749 def __init__(self, rom_loader): 2750 self.secure_download_mode = rom_loader.secure_download_mode 2751 self._port = rom_loader._port 2752 self._trace_enabled = rom_loader._trace_enabled 2753 self.flush_input() # resets _slip_reader 2754 2755 2756 ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader 2757 2758 2759 class ESP32H2BETA1StubLoader(ESP32H2BETA1ROM): 2760 """ Access class for ESP32H2BETA1 stub loader, runs on top of ROM. 2761 2762 (Basically the same as ESP32StubLoader, but different base class. 2763 Can possibly be made into a mixin.) 2764 """ 2765 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2766 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2767 IS_STUB = True 2768 2769 def __init__(self, rom_loader): 2770 self.secure_download_mode = rom_loader.secure_download_mode 2771 self._port = rom_loader._port 2772 self._trace_enabled = rom_loader._trace_enabled 2773 self.flush_input() # resets _slip_reader 2774 2775 2776 ESP32H2BETA1ROM.STUB_CLASS = ESP32H2BETA1StubLoader 2777 2778 2779 class ESP32H2BETA2StubLoader(ESP32H2BETA2ROM): 2780 """ Access class for ESP32H2BETA2 stub loader, runs on top of ROM. 2781 2782 (Basically the same as ESP32StubLoader, but different base class. 2783 Can possibly be made into a mixin.) 2784 """ 2785 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2786 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2787 IS_STUB = True 2788 2789 def __init__(self, rom_loader): 2790 self.secure_download_mode = rom_loader.secure_download_mode 2791 self._port = rom_loader._port 2792 self._trace_enabled = rom_loader._trace_enabled 2793 self.flush_input() # resets _slip_reader 2794 2795 2796 ESP32H2BETA2ROM.STUB_CLASS = ESP32H2BETA2StubLoader 2797 2798 2799 class ESP32C2StubLoader(ESP32C2ROM): 2800 """ Access class for ESP32C2 stub loader, runs on top of ROM. 2801 2802 (Basically the same as ESP32StubLoader, but different base class. 2803 Can possibly be made into a mixin.) 2804 """ 2805 FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c 2806 STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM 2807 IS_STUB = True 2808 2809 def __init__(self, rom_loader): 2810 self.secure_download_mode = rom_loader.secure_download_mode 2811 self._port = rom_loader._port 2812 self._trace_enabled = rom_loader._trace_enabled 2813 self.flush_input() # resets _slip_reader 2814 2815 2816 ESP32C2ROM.STUB_CLASS = ESP32C2StubLoader 2817 2818 2819 class ESPBOOTLOADER(object): 2820 """ These are constants related to software ESP8266 bootloader, working with 'v2' image files """ 2821 2822 # First byte of the "v2" application image 2823 IMAGE_V2_MAGIC = 0xea 2824 2825 # First 'segment' value in a "v2" application image, appears to be a constant version value? 2826 IMAGE_V2_SEGMENT = 4 2827 2828 2829 def LoadFirmwareImage(chip, filename): 2830 """ Load a firmware image. Can be for any supported SoC. 2831 2832 ESP8266 images will be examined to determine if they are original ROM firmware images (ESP8266ROMFirmwareImage) 2833 or "v2" OTA bootloader images. 2834 2835 Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) or ESP8266V2FirmwareImage (v2). 2836 """ 2837 chip = re.sub(r"[-()]", "", chip.lower()) 2838 with open(filename, 'rb') as f: 2839 if chip == 'esp32': 2840 return ESP32FirmwareImage(f) 2841 elif chip == "esp32s2": 2842 return ESP32S2FirmwareImage(f) 2843 elif chip == "esp32s3beta2": 2844 return ESP32S3BETA2FirmwareImage(f) 2845 elif chip == "esp32s3": 2846 return ESP32S3FirmwareImage(f) 2847 elif chip == 'esp32c3': 2848 return ESP32C3FirmwareImage(f) 2849 elif chip == 'esp32c6beta': 2850 return ESP32C6BETAFirmwareImage(f) 2851 elif chip == 'esp32h2beta1': 2852 return ESP32H2BETA1FirmwareImage(f) 2853 elif chip == 'esp32h2beta2': 2854 return ESP32H2BETA2FirmwareImage(f) 2855 elif chip == 'esp32c2': 2856 return ESP32C2FirmwareImage(f) 2857 else: # Otherwise, ESP8266 so look at magic to determine the image type 2858 magic = ord(f.read(1)) 2859 f.seek(0) 2860 if magic == ESPLoader.ESP_IMAGE_MAGIC: 2861 return ESP8266ROMFirmwareImage(f) 2862 elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: 2863 return ESP8266V2FirmwareImage(f) 2864 else: 2865 raise FatalError("Invalid image magic number: %d" % magic) 2866 2867 2868 class ImageSegment(object): 2869 """ Wrapper class for a segment in an ESP image 2870 (very similar to a section in an ELFImage also) """ 2871 def __init__(self, addr, data, file_offs=None): 2872 self.addr = addr 2873 self.data = data 2874 self.file_offs = file_offs 2875 self.include_in_checksum = True 2876 if self.addr != 0: 2877 self.pad_to_alignment(4) # pad all "real" ImageSegments 4 byte aligned length 2878 2879 def copy_with_new_addr(self, new_addr): 2880 """ Return a new ImageSegment with same data, but mapped at 2881 a new address. """ 2882 return ImageSegment(new_addr, self.data, 0) 2883 2884 def split_image(self, split_len): 2885 """ Return a new ImageSegment which splits "split_len" bytes 2886 from the beginning of the data. Remaining bytes are kept in 2887 this segment object (and the start address is adjusted to match.) """ 2888 result = copy.copy(self) 2889 result.data = self.data[:split_len] 2890 self.data = self.data[split_len:] 2891 self.addr += split_len 2892 self.file_offs = None 2893 result.file_offs = None 2894 return result 2895 2896 def __repr__(self): 2897 r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) 2898 if self.file_offs is not None: 2899 r += " file_offs 0x%08x" % (self.file_offs) 2900 return r 2901 2902 def get_memory_type(self, image): 2903 """ 2904 Return a list describing the memory type(s) that is covered by this 2905 segment's start address. 2906 """ 2907 return [map_range[2] for map_range in image.ROM_LOADER.MEMORY_MAP if map_range[0] <= self.addr < map_range[1]] 2908 2909 def pad_to_alignment(self, alignment): 2910 self.data = pad_to(self.data, alignment, b'\x00') 2911 2912 2913 class ELFSection(ImageSegment): 2914 """ Wrapper class for a section in an ELF image, has a section 2915 name as well as the common properties of an ImageSegment. """ 2916 def __init__(self, name, addr, data): 2917 super(ELFSection, self).__init__(addr, data) 2918 self.name = name.decode("utf-8") 2919 2920 def __repr__(self): 2921 return "%s %s" % (self.name, super(ELFSection, self).__repr__()) 2922 2923 2924 class BaseFirmwareImage(object): 2925 SEG_HEADER_LEN = 8 2926 SHA256_DIGEST_LEN = 32 2927 2928 """ Base class with common firmware image functions """ 2929 def __init__(self): 2930 self.segments = [] 2931 self.entrypoint = 0 2932 self.elf_sha256 = None 2933 self.elf_sha256_offset = 0 2934 self.pad_to_size = 0 2935 2936 def load_common_header(self, load_file, expected_magic): 2937 (magic, segments, self.flash_mode, self.flash_size_freq, self.entrypoint) = struct.unpack('<BBBBI', load_file.read(8)) 2938 2939 if magic != expected_magic: 2940 raise FatalError('Invalid firmware image magic=0x%x' % (magic)) 2941 return segments 2942 2943 def verify(self): 2944 if len(self.segments) > 16: 2945 raise FatalError('Invalid segment count %d (max 16). Usually this indicates a linker script problem.' % len(self.segments)) 2946 2947 def load_segment(self, f, is_irom_segment=False): 2948 """ Load the next segment from the image file """ 2949 file_offs = f.tell() 2950 (offset, size) = struct.unpack('<II', f.read(8)) 2951 self.warn_if_unusual_segment(offset, size, is_irom_segment) 2952 segment_data = f.read(size) 2953 if len(segment_data) < size: 2954 raise FatalError('End of file reading segment 0x%x, length %d (actual length %d)' % (offset, size, len(segment_data))) 2955 segment = ImageSegment(offset, segment_data, file_offs) 2956 self.segments.append(segment) 2957 return segment 2958 2959 def warn_if_unusual_segment(self, offset, size, is_irom_segment): 2960 if not is_irom_segment: 2961 if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536: 2962 print('WARNING: Suspicious segment 0x%x, length %d' % (offset, size)) 2963 2964 def maybe_patch_segment_data(self, f, segment_data): 2965 """If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data.""" 2966 segment_len = len(segment_data) 2967 file_pos = f.tell() # file_pos is position in the .bin file 2968 if self.elf_sha256_offset >= file_pos and self.elf_sha256_offset < file_pos + segment_len: 2969 # SHA256 digest needs to be patched into this binary segment, 2970 # calculate offset of the digest inside the binary segment. 2971 patch_offset = self.elf_sha256_offset - file_pos 2972 # Sanity checks 2973 if patch_offset < self.SEG_HEADER_LEN or patch_offset + self.SHA256_DIGEST_LEN > segment_len: 2974 raise FatalError('Cannot place SHA256 digest on segment boundary' 2975 '(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)' % 2976 (self.elf_sha256_offset, file_pos, segment_len)) 2977 # offset relative to the data part 2978 patch_offset -= self.SEG_HEADER_LEN 2979 if segment_data[patch_offset:patch_offset + self.SHA256_DIGEST_LEN] != b'\x00' * self.SHA256_DIGEST_LEN: 2980 raise FatalError('Contents of segment at SHA256 digest offset 0x%x are not all zero. Refusing to overwrite.' % 2981 self.elf_sha256_offset) 2982 assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN 2983 segment_data = segment_data[0:patch_offset] + self.elf_sha256 + \ 2984 segment_data[patch_offset + self.SHA256_DIGEST_LEN:] 2985 return segment_data 2986 2987 def save_segment(self, f, segment, checksum=None): 2988 """ Save the next segment to the image file, return next checksum value if provided """ 2989 segment_data = self.maybe_patch_segment_data(f, segment.data) 2990 f.write(struct.pack('<II', segment.addr, len(segment_data))) 2991 f.write(segment_data) 2992 if checksum is not None: 2993 return ESPLoader.checksum(segment_data, checksum) 2994 2995 def read_checksum(self, f): 2996 """ Return ESPLoader checksum from end of just-read image """ 2997 # Skip the padding. The checksum is stored in the last byte so that the 2998 # file is a multiple of 16 bytes. 2999 align_file_position(f, 16) 3000 return ord(f.read(1)) 3001 3002 def calculate_checksum(self): 3003 """ Calculate checksum of loaded image, based on segments in 3004 segment array. 3005 """ 3006 checksum = ESPLoader.ESP_CHECKSUM_MAGIC 3007 for seg in self.segments: 3008 if seg.include_in_checksum: 3009 checksum = ESPLoader.checksum(seg.data, checksum) 3010 return checksum 3011 3012 def append_checksum(self, f, checksum): 3013 """ Append ESPLoader checksum to the just-written image """ 3014 align_file_position(f, 16) 3015 f.write(struct.pack(b'B', checksum)) 3016 3017 def write_common_header(self, f, segments): 3018 f.write(struct.pack('<BBBBI', ESPLoader.ESP_IMAGE_MAGIC, len(segments), 3019 self.flash_mode, self.flash_size_freq, self.entrypoint)) 3020 3021 def is_irom_addr(self, addr): 3022 """ Returns True if an address starts in the irom region. 3023 Valid for ESP8266 only. 3024 """ 3025 return ESP8266ROM.IROM_MAP_START <= addr < ESP8266ROM.IROM_MAP_END 3026 3027 def get_irom_segment(self): 3028 irom_segments = [s for s in self.segments if self.is_irom_addr(s.addr)] 3029 if len(irom_segments) > 0: 3030 if len(irom_segments) != 1: 3031 raise FatalError('Found %d segments that could be irom0. Bad ELF file?' % len(irom_segments)) 3032 return irom_segments[0] 3033 return None 3034 3035 def get_non_irom_segments(self): 3036 irom_segment = self.get_irom_segment() 3037 return [s for s in self.segments if s != irom_segment] 3038 3039 def merge_adjacent_segments(self): 3040 if not self.segments: 3041 return # nothing to merge 3042 3043 segments = [] 3044 # The easiest way to merge the sections is the browse them backward. 3045 for i in range(len(self.segments) - 1, 0, -1): 3046 # elem is the previous section, the one `next_elem` may need to be 3047 # merged in 3048 elem = self.segments[i - 1] 3049 next_elem = self.segments[i] 3050 if all((elem.get_memory_type(self) == next_elem.get_memory_type(self), 3051 elem.include_in_checksum == next_elem.include_in_checksum, 3052 next_elem.addr == elem.addr + len(elem.data))): 3053 # Merge any segment that ends where the next one starts, without spanning memory types 3054 # 3055 # (don't 'pad' any gaps here as they may be excluded from the image due to 'noinit' 3056 # or other reasons.) 3057 elem.data += next_elem.data 3058 else: 3059 # The section next_elem cannot be merged into the previous one, 3060 # which means it needs to be part of the final segments. 3061 # As we are browsing the list backward, the elements need to be 3062 # inserted at the beginning of the final list. 3063 segments.insert(0, next_elem) 3064 3065 # The first segment will always be here as it cannot be merged into any 3066 # "previous" section. 3067 segments.insert(0, self.segments[0]) 3068 3069 # note: we could sort segments here as well, but the ordering of segments is sometimes 3070 # important for other reasons (like embedded ELF SHA-256), so we assume that the linker 3071 # script will have produced any adjacent sections in linear order in the ELF, anyhow. 3072 self.segments = segments 3073 3074 def set_mmu_page_size(self, size): 3075 """ If supported, this should be overridden by the chip-specific class. Gets called in elf2image. """ 3076 print('WARNING: Changing MMU page size is not supported on {}! Defaulting to 64KB.'.format(self.ROM_LOADER.CHIP_NAME)) 3077 3078 3079 class ESP8266ROMFirmwareImage(BaseFirmwareImage): 3080 """ 'Version 1' firmware image, segments loaded directly by the ROM bootloader. """ 3081 3082 ROM_LOADER = ESP8266ROM 3083 3084 def __init__(self, load_file=None): 3085 super(ESP8266ROMFirmwareImage, self).__init__() 3086 self.flash_mode = 0 3087 self.flash_size_freq = 0 3088 self.version = 1 3089 3090 if load_file is not None: 3091 segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) 3092 3093 for _ in range(segments): 3094 self.load_segment(load_file) 3095 self.checksum = self.read_checksum(load_file) 3096 3097 self.verify() 3098 3099 def default_output_name(self, input_file): 3100 """ Derive a default output name from the ELF name. """ 3101 return input_file + '-' 3102 3103 def save(self, basename): 3104 """ Save a set of V1 images for flashing. Parameter is a base filename. """ 3105 # IROM data goes in its own plain binary file 3106 irom_segment = self.get_irom_segment() 3107 if irom_segment is not None: 3108 with open("%s0x%05x.bin" % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), "wb") as f: 3109 f.write(irom_segment.data) 3110 3111 # everything but IROM goes at 0x00000 in an image file 3112 normal_segments = self.get_non_irom_segments() 3113 with open("%s0x00000.bin" % basename, 'wb') as f: 3114 self.write_common_header(f, normal_segments) 3115 checksum = ESPLoader.ESP_CHECKSUM_MAGIC 3116 for segment in normal_segments: 3117 checksum = self.save_segment(f, segment, checksum) 3118 self.append_checksum(f, checksum) 3119 3120 3121 ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage 3122 3123 3124 class ESP8266V2FirmwareImage(BaseFirmwareImage): 3125 """ 'Version 2' firmware image, segments loaded by software bootloader stub 3126 (ie Espressif bootloader or rboot) 3127 """ 3128 3129 ROM_LOADER = ESP8266ROM 3130 3131 def __init__(self, load_file=None): 3132 super(ESP8266V2FirmwareImage, self).__init__() 3133 self.version = 2 3134 if load_file is not None: 3135 segments = self.load_common_header(load_file, ESPBOOTLOADER.IMAGE_V2_MAGIC) 3136 if segments != ESPBOOTLOADER.IMAGE_V2_SEGMENT: 3137 # segment count is not really segment count here, but we expect to see '4' 3138 print('Warning: V2 header has unexpected "segment" count %d (usually 4)' % segments) 3139 3140 # irom segment comes before the second header 3141 # 3142 # the file is saved in the image with a zero load address 3143 # in the header, so we need to calculate a load address 3144 irom_segment = self.load_segment(load_file, True) 3145 irom_segment.addr = 0 # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 3146 irom_segment.include_in_checksum = False 3147 3148 first_flash_mode = self.flash_mode 3149 first_flash_size_freq = self.flash_size_freq 3150 first_entrypoint = self.entrypoint 3151 # load the second header 3152 3153 segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) 3154 3155 if first_flash_mode != self.flash_mode: 3156 print('WARNING: Flash mode value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' 3157 % (first_flash_mode, self.flash_mode)) 3158 if first_flash_size_freq != self.flash_size_freq: 3159 print('WARNING: Flash size/freq value in first header (0x%02x) disagrees with second (0x%02x). Using second value.' 3160 % (first_flash_size_freq, self.flash_size_freq)) 3161 if first_entrypoint != self.entrypoint: 3162 print('WARNING: Entrypoint address in first header (0x%08x) disagrees with second header (0x%08x). Using second value.' 3163 % (first_entrypoint, self.entrypoint)) 3164 3165 # load all the usual segments 3166 for _ in range(segments): 3167 self.load_segment(load_file) 3168 self.checksum = self.read_checksum(load_file) 3169 3170 self.verify() 3171 3172 def default_output_name(self, input_file): 3173 """ Derive a default output name from the ELF name. """ 3174 irom_segment = self.get_irom_segment() 3175 if irom_segment is not None: 3176 irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START 3177 else: 3178 irom_offs = 0 3179 return "%s-0x%05x.bin" % (os.path.splitext(input_file)[0], 3180 irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) 3181 3182 def save(self, filename): 3183 with open(filename, 'wb') as f: 3184 # Save first header for irom0 segment 3185 f.write(struct.pack(b'<BBBBI', ESPBOOTLOADER.IMAGE_V2_MAGIC, ESPBOOTLOADER.IMAGE_V2_SEGMENT, 3186 self.flash_mode, self.flash_size_freq, self.entrypoint)) 3187 3188 irom_segment = self.get_irom_segment() 3189 if irom_segment is not None: 3190 # save irom0 segment, make sure it has load addr 0 in the file 3191 irom_segment = irom_segment.copy_with_new_addr(0) 3192 irom_segment.pad_to_alignment(16) # irom_segment must end on a 16 byte boundary 3193 self.save_segment(f, irom_segment) 3194 3195 # second header, matches V1 header and contains loadable segments 3196 normal_segments = self.get_non_irom_segments() 3197 self.write_common_header(f, normal_segments) 3198 checksum = ESPLoader.ESP_CHECKSUM_MAGIC 3199 for segment in normal_segments: 3200 checksum = self.save_segment(f, segment, checksum) 3201 self.append_checksum(f, checksum) 3202 3203 # calculate a crc32 of entire file and append 3204 # (algorithm used by recent 8266 SDK bootloaders) 3205 with open(filename, 'rb') as f: 3206 crc = esp8266_crc32(f.read()) 3207 with open(filename, 'ab') as f: 3208 f.write(struct.pack(b'<I', crc)) 3209 3210 3211 def esp8266_crc32(data): 3212 """ 3213 CRC32 algorithm used by 8266 SDK bootloader (and gen_appbin.py). 3214 """ 3215 crc = binascii.crc32(data, 0) & 0xFFFFFFFF 3216 if crc & 0x80000000: 3217 return crc ^ 0xFFFFFFFF 3218 else: 3219 return crc + 1 3220 3221 3222 class ESP32FirmwareImage(BaseFirmwareImage): 3223 """ ESP32 firmware image is very similar to V1 ESP8266 image, 3224 except with an additional 16 byte reserved header at top of image, 3225 and because of new flash mapping capabilities the flash-mapped regions 3226 can be placed in the normal image (just @ 64kB padded offsets). 3227 """ 3228 3229 ROM_LOADER = ESP32ROM 3230 3231 # ROM bootloader will read the wp_pin field if SPI flash 3232 # pins are remapped via flash. IDF actually enables QIO only 3233 # from software bootloader, so this can be ignored. But needs 3234 # to be set to this value so ROM bootloader will skip it. 3235 WP_PIN_DISABLED = 0xEE 3236 3237 EXTENDED_HEADER_STRUCT_FMT = "<BBBBHBHH" + ("B" * 4) + "B" 3238 3239 IROM_ALIGN = 65536 3240 3241 def __init__(self, load_file=None): 3242 super(ESP32FirmwareImage, self).__init__() 3243 self.secure_pad = None 3244 self.flash_mode = 0 3245 self.flash_size_freq = 0 3246 self.version = 1 3247 self.wp_pin = self.WP_PIN_DISABLED 3248 # SPI pin drive levels 3249 self.clk_drv = 0 3250 self.q_drv = 0 3251 self.d_drv = 0 3252 self.cs_drv = 0 3253 self.hd_drv = 0 3254 self.wp_drv = 0 3255 self.min_rev = 0 3256 self.min_rev_full = 0 3257 self.max_rev_full = 0 3258 3259 self.append_digest = True 3260 3261 if load_file is not None: 3262 start = load_file.tell() 3263 3264 segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) 3265 self.load_extended_header(load_file) 3266 3267 for _ in range(segments): 3268 self.load_segment(load_file) 3269 self.checksum = self.read_checksum(load_file) 3270 3271 if self.append_digest: 3272 end = load_file.tell() 3273 self.stored_digest = load_file.read(32) 3274 load_file.seek(start) 3275 calc_digest = hashlib.sha256() 3276 calc_digest.update(load_file.read(end - start)) 3277 self.calc_digest = calc_digest.digest() # TODO: decide what to do here? 3278 3279 self.verify() 3280 3281 def is_flash_addr(self, addr): 3282 return (self.ROM_LOADER.IROM_MAP_START <= addr < self.ROM_LOADER.IROM_MAP_END) \ 3283 or (self.ROM_LOADER.DROM_MAP_START <= addr < self.ROM_LOADER.DROM_MAP_END) 3284 3285 def default_output_name(self, input_file): 3286 """ Derive a default output name from the ELF name. """ 3287 return "%s.bin" % (os.path.splitext(input_file)[0]) 3288 3289 def warn_if_unusual_segment(self, offset, size, is_irom_segment): 3290 pass # TODO: add warnings for ESP32 segment offset/size combinations that are wrong 3291 3292 def save(self, filename): 3293 total_segments = 0 3294 with io.BytesIO() as f: # write file to memory first 3295 self.write_common_header(f, self.segments) 3296 3297 # first 4 bytes of header are read by ROM bootloader for SPI 3298 # config, but currently unused 3299 self.save_extended_header(f) 3300 3301 checksum = ESPLoader.ESP_CHECKSUM_MAGIC 3302 3303 # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them 3304 flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr)] 3305 ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr)] 3306 3307 # check for multiple ELF sections that are mapped in the same flash mapping region. 3308 # this is usually a sign of a broken linker script, but if you have a legitimate 3309 # use case then let us know 3310 if len(flash_segments) > 0: 3311 last_addr = flash_segments[0].addr 3312 for segment in flash_segments[1:]: 3313 if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: 3314 raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. " 3315 "Can't generate binary. Suggest changing linker script or ELF to merge sections.") % 3316 (segment.addr, last_addr)) 3317 last_addr = segment.addr 3318 3319 def get_alignment_data_needed(segment): 3320 # Actual alignment (in data bytes) required for a segment header: positioned so that 3321 # after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN 3322 # 3323 # (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned 3324 # IROM_ALIGN+0x18 to account for the binary file header 3325 align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN 3326 pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past 3327 if pad_len == 0 or pad_len == self.IROM_ALIGN: 3328 return 0 # already aligned 3329 3330 # subtract SEG_HEADER_LEN a second time, as the padding block has a header as well 3331 pad_len -= self.SEG_HEADER_LEN 3332 if pad_len < 0: 3333 pad_len += self.IROM_ALIGN 3334 return pad_len 3335 3336 # try to fit each flash segment on a 64kB aligned boundary 3337 # by padding with parts of the non-flash segments... 3338 while len(flash_segments) > 0: 3339 segment = flash_segments[0] 3340 pad_len = get_alignment_data_needed(segment) 3341 if pad_len > 0: # need to pad 3342 if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: 3343 pad_segment = ram_segments[0].split_image(pad_len) 3344 if len(ram_segments[0].data) == 0: 3345 ram_segments.pop(0) 3346 else: 3347 pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) 3348 checksum = self.save_segment(f, pad_segment, checksum) 3349 total_segments += 1 3350 else: 3351 # write the flash segment 3352 assert (f.tell() + 8) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN 3353 checksum = self.save_flash_segment(f, segment, checksum) 3354 flash_segments.pop(0) 3355 total_segments += 1 3356 3357 # flash segments all written, so write any remaining RAM segments 3358 for segment in ram_segments: 3359 checksum = self.save_segment(f, segment, checksum) 3360 total_segments += 1 3361 3362 if self.secure_pad: 3363 # pad the image so that after signing it will end on a a 64KB boundary. 3364 # This ensures all mapped flash content will be verified. 3365 if not self.append_digest: 3366 raise FatalError("secure_pad only applies if a SHA-256 digest is also appended to the image") 3367 align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN 3368 # 16 byte aligned checksum (force the alignment to simplify calculations) 3369 checksum_space = 16 3370 if self.secure_pad == '1': 3371 # after checksum: SHA-256 digest + (to be added by signing process) version, signature + 12 trailing bytes due to alignment 3372 space_after_checksum = 32 + 4 + 64 + 12 3373 elif self.secure_pad == '2': # Secure Boot V2 3374 # after checksum: SHA-256 digest + signature sector, but we place signature sector after the 64KB boundary 3375 space_after_checksum = 32 3376 pad_len = (self.IROM_ALIGN - align_past - checksum_space - space_after_checksum) % self.IROM_ALIGN 3377 pad_segment = ImageSegment(0, b'\x00' * pad_len, f.tell()) 3378 3379 checksum = self.save_segment(f, pad_segment, checksum) 3380 total_segments += 1 3381 3382 # done writing segments 3383 self.append_checksum(f, checksum) 3384 image_length = f.tell() 3385 3386 if self.secure_pad: 3387 assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 3388 3389 # kinda hacky: go back to the initial header and write the new segment count 3390 # that includes padding segments. This header is not checksummed 3391 f.seek(1) 3392 try: 3393 f.write(chr(total_segments)) 3394 except TypeError: # Python 3 3395 f.write(bytes([total_segments])) 3396 3397 if self.append_digest: 3398 # calculate the SHA256 of the whole file and append it 3399 f.seek(0) 3400 digest = hashlib.sha256() 3401 digest.update(f.read(image_length)) 3402 f.write(digest.digest()) 3403 3404 if self.pad_to_size: 3405 image_length = f.tell() 3406 if image_length % self.pad_to_size != 0: 3407 pad_by = self.pad_to_size - (image_length % self.pad_to_size) 3408 f.write(b"\xff" * pad_by) 3409 3410 with open(filename, 'wb') as real_file: 3411 real_file.write(f.getvalue()) 3412 3413 def save_flash_segment(self, f, segment, checksum=None): 3414 """ Save the next segment to the image file, return next checksum value if provided """ 3415 segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN 3416 segment_len_remainder = segment_end_pos % self.IROM_ALIGN 3417 if segment_len_remainder < 0x24: 3418 # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the 3419 # last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page boundary. 3420 segment.data += b'\x00' * (0x24 - segment_len_remainder) 3421 return self.save_segment(f, segment, checksum) 3422 3423 def load_extended_header(self, load_file): 3424 def split_byte(n): 3425 return (n & 0x0F, (n >> 4) & 0x0F) 3426 3427 fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16))) 3428 3429 self.wp_pin = fields[0] 3430 3431 # SPI pin drive stengths are two per byte 3432 self.clk_drv, self.q_drv = split_byte(fields[1]) 3433 self.d_drv, self.cs_drv = split_byte(fields[2]) 3434 self.hd_drv, self.wp_drv = split_byte(fields[3]) 3435 3436 chip_id = fields[4] 3437 if chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: 3438 print(("Unexpected chip id in image. Expected %d but value was %d. " 3439 "Is this image for a different chip model?") % (self.ROM_LOADER.IMAGE_CHIP_ID, chip_id)) 3440 3441 self.min_rev = fields[5] 3442 self.min_rev_full = fields[6] 3443 self.max_rev_full = fields[7] 3444 3445 # reserved fields in the middle should all be zero 3446 if any(f for f in fields[8:-1] if f != 0): 3447 print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?") 3448 3449 append_digest = fields[-1] # last byte is append_digest 3450 if append_digest in [0, 1]: 3451 self.append_digest = (append_digest == 1) 3452 else: 3453 raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", append_digest) 3454 3455 def save_extended_header(self, save_file): 3456 def join_byte(ln, hn): 3457 return (ln & 0x0F) + ((hn & 0x0F) << 4) 3458 3459 append_digest = 1 if self.append_digest else 0 3460 3461 fields = [self.wp_pin, 3462 join_byte(self.clk_drv, self.q_drv), 3463 join_byte(self.d_drv, self.cs_drv), 3464 join_byte(self.hd_drv, self.wp_drv), 3465 self.ROM_LOADER.IMAGE_CHIP_ID, 3466 self.min_rev, 3467 self.min_rev_full, 3468 self.max_rev_full] 3469 fields += [0] * 4 # padding 3470 fields += [append_digest] 3471 3472 packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) 3473 save_file.write(packed) 3474 3475 3476 class ESP8266V3FirmwareImage(ESP32FirmwareImage): 3477 """ ESP8266 V3 firmware image is very similar to ESP32 image 3478 """ 3479 3480 EXTENDED_HEADER_STRUCT_FMT = "B" * 16 3481 3482 def is_flash_addr(self, addr): 3483 return (addr > ESP8266ROM.IROM_MAP_START) 3484 3485 def save(self, filename): 3486 total_segments = 0 3487 with io.BytesIO() as f: # write file to memory first 3488 self.write_common_header(f, self.segments) 3489 3490 checksum = ESPLoader.ESP_CHECKSUM_MAGIC 3491 3492 # split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them 3493 flash_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if self.is_flash_addr(s.addr) and len(s.data)] 3494 ram_segments = [copy.deepcopy(s) for s in sorted(self.segments, key=lambda s:s.addr) if not self.is_flash_addr(s.addr) and len(s.data)] 3495 3496 # check for multiple ELF sections that are mapped in the same flash mapping region. 3497 # this is usually a sign of a broken linker script, but if you have a legitimate 3498 # use case then let us know 3499 if len(flash_segments) > 0: 3500 last_addr = flash_segments[0].addr 3501 for segment in flash_segments[1:]: 3502 if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: 3503 raise FatalError(("Segment loaded at 0x%08x lands in same 64KB flash mapping as segment loaded at 0x%08x. " 3504 "Can't generate binary. Suggest changing linker script or ELF to merge sections.") % 3505 (segment.addr, last_addr)) 3506 last_addr = segment.addr 3507 3508 # try to fit each flash segment on a 64kB aligned boundary 3509 # by padding with parts of the non-flash segments... 3510 while len(flash_segments) > 0: 3511 segment = flash_segments[0] 3512 # remove 8 bytes empty data for insert segment header 3513 if segment.name == '.flash.rodata': 3514 segment.data = segment.data[8:] 3515 # write the flash segment 3516 checksum = self.save_segment(f, segment, checksum) 3517 flash_segments.pop(0) 3518 total_segments += 1 3519 3520 # flash segments all written, so write any remaining RAM segments 3521 for segment in ram_segments: 3522 checksum = self.save_segment(f, segment, checksum) 3523 total_segments += 1 3524 3525 # done writing segments 3526 self.append_checksum(f, checksum) 3527 image_length = f.tell() 3528 3529 # kinda hacky: go back to the initial header and write the new segment count 3530 # that includes padding segments. This header is not checksummed 3531 f.seek(1) 3532 try: 3533 f.write(chr(total_segments)) 3534 except TypeError: # Python 3 3535 f.write(bytes([total_segments])) 3536 3537 if self.append_digest: 3538 # calculate the SHA256 of the whole file and append it 3539 f.seek(0) 3540 digest = hashlib.sha256() 3541 digest.update(f.read(image_length)) 3542 f.write(digest.digest()) 3543 3544 with open(filename, 'wb') as real_file: 3545 real_file.write(f.getvalue()) 3546 3547 def load_extended_header(self, load_file): 3548 def split_byte(n): 3549 return (n & 0x0F, (n >> 4) & 0x0F) 3550 3551 fields = list(struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16))) 3552 3553 self.wp_pin = fields[0] 3554 3555 # SPI pin drive stengths are two per byte 3556 self.clk_drv, self.q_drv = split_byte(fields[1]) 3557 self.d_drv, self.cs_drv = split_byte(fields[2]) 3558 self.hd_drv, self.wp_drv = split_byte(fields[3]) 3559 3560 if fields[15] in [0, 1]: 3561 self.append_digest = (fields[15] == 1) 3562 else: 3563 raise RuntimeError("Invalid value for append_digest field (0x%02x). Should be 0 or 1.", fields[15]) 3564 3565 # remaining fields in the middle should all be zero 3566 if any(f for f in fields[4:15] if f != 0): 3567 print("Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py?") 3568 3569 3570 ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage 3571 3572 3573 class ESP32S2FirmwareImage(ESP32FirmwareImage): 3574 """ ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3575 ROM_LOADER = ESP32S2ROM 3576 3577 3578 ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage 3579 3580 3581 class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): 3582 """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3583 ROM_LOADER = ESP32S3BETA2ROM 3584 3585 3586 ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage 3587 3588 3589 class ESP32S3FirmwareImage(ESP32FirmwareImage): 3590 """ ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3591 ROM_LOADER = ESP32S3ROM 3592 3593 3594 ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage 3595 3596 3597 class ESP32C3FirmwareImage(ESP32FirmwareImage): 3598 """ ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3599 ROM_LOADER = ESP32C3ROM 3600 3601 3602 ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage 3603 3604 3605 class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): 3606 """ ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3607 ROM_LOADER = ESP32C6BETAROM 3608 3609 3610 ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage 3611 3612 3613 class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage): 3614 """ ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3615 ROM_LOADER = ESP32H2BETA1ROM 3616 3617 3618 ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage 3619 3620 3621 class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage): 3622 """ ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3623 ROM_LOADER = ESP32H2BETA2ROM 3624 3625 3626 ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage 3627 3628 3629 class ESP32C2FirmwareImage(ESP32FirmwareImage): 3630 """ ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage """ 3631 ROM_LOADER = ESP32C2ROM 3632 3633 def set_mmu_page_size(self, size): 3634 if size not in [16384, 32768, 65536]: 3635 raise FatalError("{} is not a valid page size.".format(size)) 3636 self.IROM_ALIGN = size 3637 3638 3639 ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage 3640 3641 3642 class ELFFile(object): 3643 SEC_TYPE_PROGBITS = 0x01 3644 SEC_TYPE_STRTAB = 0x03 3645 SEC_TYPE_INITARRAY = 0x0e 3646 SEC_TYPE_FINIARRAY = 0x0f 3647 3648 PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY) 3649 3650 LEN_SEC_HEADER = 0x28 3651 3652 SEG_TYPE_LOAD = 0x01 3653 LEN_SEG_HEADER = 0x20 3654 3655 def __init__(self, name): 3656 # Load sections from the ELF file 3657 self.name = name 3658 with open(self.name, 'rb') as f: 3659 self._read_elf_file(f) 3660 3661 def get_section(self, section_name): 3662 for s in self.sections: 3663 if s.name == section_name: 3664 return s 3665 raise ValueError("No section %s in ELF file" % section_name) 3666 3667 def _read_elf_file(self, f): 3668 # read the ELF file header 3669 LEN_FILE_HEADER = 0x34 3670 try: 3671 (ident, _type, machine, _version, 3672 self.entrypoint, _phoff, shoff, _flags, 3673 _ehsize, _phentsize, _phnum, shentsize, 3674 shnum, shstrndx) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) 3675 except struct.error as e: 3676 raise FatalError("Failed to read a valid ELF header from %s: %s" % (self.name, e)) 3677 3678 if byte(ident, 0) != 0x7f or ident[1:4] != b'ELF': 3679 raise FatalError("%s has invalid ELF magic header" % self.name) 3680 if machine not in [0x5e, 0xf3]: 3681 raise FatalError("%s does not appear to be an Xtensa or an RISCV ELF file. e_machine=%04x" % (self.name, machine)) 3682 if shentsize != self.LEN_SEC_HEADER: 3683 raise FatalError("%s has unexpected section header entry size 0x%x (not 0x%x)" % (self.name, shentsize, self.LEN_SEC_HEADER)) 3684 if shnum == 0: 3685 raise FatalError("%s has 0 section headers" % (self.name)) 3686 self._read_sections(f, shoff, shnum, shstrndx) 3687 self._read_segments(f, _phoff, _phnum, shstrndx) 3688 3689 def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): 3690 f.seek(section_header_offs) 3691 len_bytes = section_header_count * self.LEN_SEC_HEADER 3692 section_header = f.read(len_bytes) 3693 if len(section_header) == 0: 3694 raise FatalError("No section header found at offset %04x in ELF file." % section_header_offs) 3695 if len(section_header) != (len_bytes): 3696 raise FatalError("Only read 0x%x bytes from section header (expected 0x%x.) Truncated ELF file?" % (len(section_header), len_bytes)) 3697 3698 # walk through the section header and extract all sections 3699 section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) 3700 3701 def read_section_header(offs): 3702 name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from("<LLLLLL", section_header[offs:]) 3703 return (name_offs, sec_type, lma, size, sec_offs) 3704 all_sections = [read_section_header(offs) for offs in section_header_offsets] 3705 prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES] 3706 3707 # search for the string table section 3708 if not (shstrndx * self.LEN_SEC_HEADER) in section_header_offsets: 3709 raise FatalError("ELF file has no STRTAB section at shstrndx %d" % shstrndx) 3710 _, sec_type, _, sec_size, sec_offs = read_section_header(shstrndx * self.LEN_SEC_HEADER) 3711 if sec_type != ELFFile.SEC_TYPE_STRTAB: 3712 print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type) 3713 f.seek(sec_offs) 3714 string_table = f.read(sec_size) 3715 3716 # build the real list of ELFSections by reading the actual section names from the 3717 # string table section, and actual data for each section from the ELF file itself 3718 def lookup_string(offs): 3719 raw = string_table[offs:] 3720 return raw[:raw.index(b'\x00')] 3721 3722 def read_data(offs, size): 3723 f.seek(offs) 3724 return f.read(size) 3725 3726 prog_sections = [ELFSection(lookup_string(n_offs), lma, read_data(offs, size)) for (n_offs, _type, lma, size, offs) in prog_sections 3727 if lma != 0 and size > 0] 3728 self.sections = prog_sections 3729 3730 def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): 3731 f.seek(segment_header_offs) 3732 len_bytes = segment_header_count * self.LEN_SEG_HEADER 3733 segment_header = f.read(len_bytes) 3734 if len(segment_header) == 0: 3735 raise FatalError("No segment header found at offset %04x in ELF file." % segment_header_offs) 3736 if len(segment_header) != (len_bytes): 3737 raise FatalError("Only read 0x%x bytes from segment header (expected 0x%x.) Truncated ELF file?" % (len(segment_header), len_bytes)) 3738 3739 # walk through the segment header and extract all segments 3740 segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) 3741 3742 def read_segment_header(offs): 3743 seg_type, seg_offs, _vaddr, lma, size, _memsize, _flags, _align = struct.unpack_from("<LLLLLLLL", segment_header[offs:]) 3744 return (seg_type, lma, size, seg_offs) 3745 all_segments = [read_segment_header(offs) for offs in segment_header_offsets] 3746 prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD] 3747 3748 def read_data(offs, size): 3749 f.seek(offs) 3750 return f.read(size) 3751 3752 prog_segments = [ELFSection(b'PHDR', lma, read_data(offs, size)) for (_type, lma, size, offs) in prog_segments 3753 if lma != 0 and size > 0] 3754 self.segments = prog_segments 3755 3756 def sha256(self): 3757 # return SHA256 hash of the input ELF file 3758 sha256 = hashlib.sha256() 3759 with open(self.name, 'rb') as f: 3760 sha256.update(f.read()) 3761 return sha256.digest() 3762 3763 3764 def slip_reader(port, trace_function): 3765 """Generator to read SLIP packets from a serial port. 3766 Yields one full SLIP packet at a time, raises exception on timeout or invalid data. 3767 3768 Designed to avoid too many calls to serial.read(1), which can bog 3769 down on slow systems. 3770 """ 3771 partial_packet = None 3772 in_escape = False 3773 successful_slip = False 3774 while True: 3775 waiting = port.inWaiting() 3776 read_bytes = port.read(1 if waiting == 0 else waiting) 3777 if read_bytes == b'': 3778 if partial_packet is None: # fail due to no data 3779 msg = "Serial data stream stopped: Possible serial noise or corruption." if successful_slip else "No serial data received." 3780 else: # fail during packet transfer 3781 msg = "Packet content transfer stopped (received {} bytes)".format(len(partial_packet)) 3782 trace_function(msg) 3783 raise FatalError(msg) 3784 trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) 3785 for b in read_bytes: 3786 if type(b) is int: 3787 b = bytes([b]) # python 2/3 compat 3788 3789 if partial_packet is None: # waiting for packet header 3790 if b == b'\xc0': 3791 partial_packet = b"" 3792 else: 3793 trace_function("Read invalid data: %s", HexFormatter(read_bytes)) 3794 trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) 3795 raise FatalError('Invalid head of packet (0x%s): Possible serial noise or corruption.' % hexify(b)) 3796 elif in_escape: # part-way through escape sequence 3797 in_escape = False 3798 if b == b'\xdc': 3799 partial_packet += b'\xc0' 3800 elif b == b'\xdd': 3801 partial_packet += b'\xdb' 3802 else: 3803 trace_function("Read invalid data: %s", HexFormatter(read_bytes)) 3804 trace_function("Remaining data in serial buffer: %s", HexFormatter(port.read(port.inWaiting()))) 3805 raise FatalError('Invalid SLIP escape (0xdb, 0x%s)' % (hexify(b))) 3806 elif b == b'\xdb': # start of escape sequence 3807 in_escape = True 3808 elif b == b'\xc0': # end of packet 3809 trace_function("Received full packet: %s", HexFormatter(partial_packet)) 3810 yield partial_packet 3811 partial_packet = None 3812 successful_slip = True 3813 else: # normal byte in packet 3814 partial_packet += b 3815 3816 3817 def arg_auto_int(x): 3818 return int(x, 0) 3819 3820 3821 def format_chip_name(c): 3822 """ Normalize chip name from user input """ 3823 c = c.lower().replace('-', '') 3824 if c == 'esp8684': # TODO: Delete alias, ESPTOOL-389 3825 print('WARNING: Chip name ESP8684 is deprecated in favor of ESP32-C2 and will be removed in a future release. Using ESP32-C2 instead.') 3826 return 'esp32c2' 3827 return c 3828 3829 3830 def div_roundup(a, b): 3831 """ Return a/b rounded up to nearest integer, 3832 equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only 3833 without possible floating point accuracy errors. 3834 """ 3835 return (int(a) + int(b) - 1) // int(b) 3836 3837 3838 def align_file_position(f, size): 3839 """ Align the position in the file to the next block of specified size """ 3840 align = (size - 1) - (f.tell() % size) 3841 f.seek(align, 1) 3842 3843 3844 def flash_size_bytes(size): 3845 """ Given a flash size of the type passed in args.flash_size 3846 (ie 512KB or 1MB) then return the size in bytes. 3847 """ 3848 if "MB" in size: 3849 return int(size[:size.index("MB")]) * 1024 * 1024 3850 elif "KB" in size: 3851 return int(size[:size.index("KB")]) * 1024 3852 else: 3853 raise FatalError("Unknown size %s" % size) 3854 3855 3856 def hexify(s, uppercase=True): 3857 format_str = '%02X' if uppercase else '%02x' 3858 if not PYTHON2: 3859 return ''.join(format_str % c for c in s) 3860 else: 3861 return ''.join(format_str % ord(c) for c in s) 3862 3863 3864 class HexFormatter(object): 3865 """ 3866 Wrapper class which takes binary data in its constructor 3867 and returns a hex string as it's __str__ method. 3868 3869 This is intended for "lazy formatting" of trace() output 3870 in hex format. Avoids overhead (significant on slow computers) 3871 of generating long hex strings even if tracing is disabled. 3872 3873 Note that this doesn't save any overhead if passed as an 3874 argument to "%", only when passed to trace() 3875 3876 If auto_split is set (default), any long line (> 16 bytes) will be 3877 printed as separately indented lines, with ASCII decoding at the end 3878 of each line. 3879 """ 3880 def __init__(self, binary_string, auto_split=True): 3881 self._s = binary_string 3882 self._auto_split = auto_split 3883 3884 def __str__(self): 3885 if self._auto_split and len(self._s) > 16: 3886 result = "" 3887 s = self._s 3888 while len(s) > 0: 3889 line = s[:16] 3890 ascii_line = "".join(c if (c == ' ' or (c in string.printable and c not in string.whitespace)) 3891 else '.' for c in line.decode('ascii', 'replace')) 3892 s = s[16:] 3893 result += "\n %-16s %-16s | %s" % (hexify(line[:8], False), hexify(line[8:], False), ascii_line) 3894 return result 3895 else: 3896 return hexify(self._s, False) 3897 3898 3899 def pad_to(data, alignment, pad_character=b'\xFF'): 3900 """ Pad to the next alignment boundary """ 3901 pad_mod = len(data) % alignment 3902 if pad_mod != 0: 3903 data += pad_character * (alignment - pad_mod) 3904 return data 3905 3906 3907 class FatalError(RuntimeError): 3908 """ 3909 Wrapper class for runtime errors that aren't caused by internal bugs, but by 3910 ESP ROM responses or input content. 3911 """ 3912 def __init__(self, message): 3913 RuntimeError.__init__(self, message) 3914 3915 @staticmethod 3916 def WithResult(message, result): 3917 """ 3918 Return a fatal error object that appends the hex values of 3919 'result' and its meaning as a string formatted argument. 3920 """ 3921 3922 err_defs = { 3923 0x101: 'Out of memory', 3924 0x102: 'Invalid argument', 3925 0x103: 'Invalid state', 3926 0x104: 'Invalid size', 3927 0x105: 'Requested resource not found', 3928 0x106: 'Operation or feature not supported', 3929 0x107: 'Operation timed out', 3930 0x108: 'Received response was invalid', 3931 0x109: 'CRC or checksum was invalid', 3932 0x10A: 'Version was invalid', 3933 0x10B: 'MAC address was invalid', 3934 # Flasher stub error codes 3935 0xC000: 'Bad data length', 3936 0xC100: 'Bad data checksum', 3937 0xC200: 'Bad blocksize', 3938 0xC300: 'Invalid command', 3939 0xC400: 'Failed SPI operation', 3940 0xC500: 'Failed SPI unlock', 3941 0xC600: 'Not in flash mode', 3942 0xC700: 'Inflate error', 3943 0xC800: 'Not enough data', 3944 0xC900: 'Too much data', 3945 0xFF00: 'Command not implemented', 3946 } 3947 3948 err_code = struct.unpack(">H", result[:2]) 3949 message += " (result was {}: {})".format(hexify(result), err_defs.get(err_code[0], 'Unknown result')) 3950 return FatalError(message) 3951 3952 3953 class NotImplementedInROMError(FatalError): 3954 """ 3955 Wrapper class for the error thrown when a particular ESP bootloader function 3956 is not implemented in the ROM bootloader. 3957 """ 3958 def __init__(self, bootloader, func): 3959 FatalError.__init__(self, "%s ROM does not support function %s." % (bootloader.CHIP_NAME, func.__name__)) 3960 3961 3962 class NotSupportedError(FatalError): 3963 def __init__(self, esp, function_name): 3964 FatalError.__init__(self, "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME)) 3965 3966 # "Operation" commands, executable at command line. One function each 3967 # 3968 # Each function takes either two args (<ESPLoader instance>, <args>) or a single <args> 3969 # argument. 3970 3971 3972 class UnsupportedCommandError(RuntimeError): 3973 """ 3974 Wrapper class for when ROM loader returns an invalid command response. 3975 3976 Usually this indicates the loader is running in Secure Download Mode. 3977 """ 3978 def __init__(self, esp, op): 3979 if esp.secure_download_mode: 3980 msg = "This command (0x%x) is not supported in Secure Download Mode" % op 3981 else: 3982 msg = "Invalid (unsupported) command 0x%x" % op 3983 RuntimeError.__init__(self, msg) 3984 3985 3986 def load_ram(esp, args): 3987 image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) 3988 3989 print('RAM boot...') 3990 for seg in image.segments: 3991 size = len(seg.data) 3992 print('Downloading %d bytes at %08x...' % (size, seg.addr), end=' ') 3993 sys.stdout.flush() 3994 esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr) 3995 3996 seq = 0 3997 while len(seg.data) > 0: 3998 esp.mem_block(seg.data[0:esp.ESP_RAM_BLOCK], seq) 3999 seg.data = seg.data[esp.ESP_RAM_BLOCK:] 4000 seq += 1 4001 print('done!') 4002 4003 print('All segments done, executing at %08x' % image.entrypoint) 4004 esp.mem_finish(image.entrypoint) 4005 4006 4007 def read_mem(esp, args): 4008 print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) 4009 4010 4011 def write_mem(esp, args): 4012 esp.write_reg(args.address, args.value, args.mask, 0) 4013 print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) 4014 4015 4016 def dump_mem(esp, args): 4017 with open(args.filename, 'wb') as f: 4018 for i in range(args.size // 4): 4019 d = esp.read_reg(args.address + (i * 4)) 4020 f.write(struct.pack(b'<I', d)) 4021 if f.tell() % 1024 == 0: 4022 print_overwrite('%d bytes read... (%d %%)' % (f.tell(), 4023 f.tell() * 100 // args.size)) 4024 sys.stdout.flush() 4025 print_overwrite("Read %d bytes" % f.tell(), last_line=True) 4026 print('Done!') 4027 4028 4029 def detect_flash_size(esp, args): 4030 if args.flash_size == 'detect': 4031 if esp.secure_download_mode: 4032 raise FatalError("Detecting flash size is not supported in secure download mode. Need to manually specify flash size.") 4033 flash_id = esp.flash_id() 4034 size_id = flash_id >> 16 4035 args.flash_size = DETECTED_FLASH_SIZES.get(size_id) 4036 if args.flash_size is None: 4037 print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4MB' % (flash_id, size_id)) 4038 args.flash_size = '4MB' 4039 else: 4040 print('Auto-detected Flash size:', args.flash_size) 4041 4042 4043 def _update_image_flash_params(esp, address, args, image): 4044 """ Modify the flash mode & size bytes if this looks like an executable bootloader image """ 4045 if len(image) < 8: 4046 return image # not long enough to be a bootloader image 4047 4048 # unpack the (potential) image header 4049 magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) 4050 if address != esp.BOOTLOADER_FLASH_OFFSET: 4051 return image # not flashing bootloader offset, so don't modify this 4052 4053 if (args.flash_mode, args.flash_freq, args.flash_size) == ('keep',) * 3: 4054 return image # all settings are 'keep', not modifying anything 4055 4056 # easy check if this is an image: does it start with a magic byte? 4057 if magic != esp.ESP_IMAGE_MAGIC: 4058 print("Warning: Image file at 0x%x doesn't look like an image file, so not changing any flash settings." % address) 4059 return image 4060 4061 # make sure this really is an image, and not just data that 4062 # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted 4063 # images that happen to start with a magic byte 4064 try: 4065 test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) 4066 test_image.verify() 4067 except Exception: 4068 print("Warning: Image file at 0x%x is not a valid %s image, so not changing any flash settings." % (address, esp.CHIP_NAME)) 4069 return image 4070 4071 if args.flash_mode != 'keep': 4072 flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] 4073 4074 flash_freq = flash_size_freq & 0x0F 4075 if args.flash_freq != 'keep': 4076 flash_freq = esp.parse_flash_freq_arg(args.flash_freq) 4077 4078 flash_size = flash_size_freq & 0xF0 4079 if args.flash_size != 'keep': 4080 flash_size = esp.parse_flash_size_arg(args.flash_size) 4081 4082 flash_params = struct.pack(b'BB', flash_mode, flash_size + flash_freq) 4083 if flash_params != image[2:4]: 4084 print('Flash params set to 0x%04x' % struct.unpack(">H", flash_params)) 4085 image = image[0:2] + flash_params + image[4:] 4086 return image 4087 4088 4089 def write_flash(esp, args): 4090 # set args.compress based on default behaviour: 4091 # -> if either --compress or --no-compress is set, honour that 4092 # -> otherwise, set --compress unless --no-stub is set 4093 if args.compress is None and not args.no_compress: 4094 args.compress = not args.no_stub 4095 4096 # In case we have encrypted files to write, we first do few sanity checks before actual flash 4097 if args.encrypt or args.encrypt_files is not None: 4098 do_write = True 4099 4100 if not esp.secure_download_mode: 4101 if esp.get_encrypted_download_disabled(): 4102 raise FatalError("This chip has encrypt functionality in UART download mode disabled. " 4103 "This is the Flash Encryption configuration for Production mode instead of Development mode.") 4104 4105 crypt_cfg_efuse = esp.get_flash_crypt_config() 4106 4107 if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: 4108 print('Unexpected FLASH_CRYPT_CONFIG value: 0x%x' % (crypt_cfg_efuse)) 4109 do_write = False 4110 4111 enc_key_valid = esp.is_flash_encryption_key_valid() 4112 4113 if not enc_key_valid: 4114 print('Flash encryption key is not programmed') 4115 do_write = False 4116 4117 # Determine which files list contain the ones to encrypt 4118 files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files 4119 4120 for address, argfile in files_to_encrypt: 4121 if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: 4122 print("File %s address 0x%x is not %d byte aligned, can't flash encrypted" % 4123 (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN)) 4124 do_write = False 4125 4126 if not do_write and not args.ignore_flash_encryption_efuse_setting: 4127 raise FatalError("Can't perform encrypted flash write, consult Flash Encryption documentation for more information") 4128 4129 # verify file sizes fit in flash 4130 if args.flash_size != 'keep': # TODO: check this even with 'keep' 4131 flash_end = flash_size_bytes(args.flash_size) 4132 for address, argfile in args.addr_filename: 4133 argfile.seek(0, os.SEEK_END) 4134 if address + argfile.tell() > flash_end: 4135 raise FatalError(("File %s (length %d) at offset %d will not fit in %d bytes of flash. " 4136 "Use --flash_size argument, or change flashing address.") 4137 % (argfile.name, argfile.tell(), address, flash_end)) 4138 argfile.seek(0) 4139 4140 if args.erase_all: 4141 erase_flash(esp, args) 4142 else: 4143 for address, argfile in args.addr_filename: 4144 argfile.seek(0, os.SEEK_END) 4145 write_end = address + argfile.tell() 4146 argfile.seek(0) 4147 bytes_over = address % esp.FLASH_SECTOR_SIZE 4148 if bytes_over != 0: 4149 print("WARNING: Flash address {:#010x} is not aligned to a {:#x} byte flash sector. " 4150 "{:#x} bytes before this address will be erased." 4151 .format(address, esp.FLASH_SECTOR_SIZE, bytes_over)) 4152 # Print the address range of to-be-erased flash memory region 4153 print("Flash will be erased from {:#010x} to {:#010x}..." 4154 .format(address - bytes_over, div_roundup(write_end, esp.FLASH_SECTOR_SIZE) * esp.FLASH_SECTOR_SIZE - 1)) 4155 4156 """ Create a list describing all the files we have to flash. Each entry holds an "encrypt" flag 4157 marking whether the file needs encryption or not. This list needs to be sorted. 4158 4159 First, append to each entry of our addr_filename list the flag args.encrypt 4160 For example, if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], 4161 all_files will be [(0x1000, "partition.bin", args.encrypt), (0x8000, "bootloader", args.encrypt)], 4162 where, of course, args.encrypt is either True or False 4163 """ 4164 all_files = [(offs, filename, args.encrypt) for (offs, filename) in args.addr_filename] 4165 4166 """Now do the same with encrypt_files list, if defined. 4167 In this case, the flag is True 4168 """ 4169 if args.encrypt_files is not None: 4170 encrypted_files_flag = [(offs, filename, True) for (offs, filename) in args.encrypt_files] 4171 4172 # Concatenate both lists and sort them. 4173 # As both list are already sorted, we could simply do a merge instead, 4174 # but for the sake of simplicity and because the lists are very small, 4175 # let's use sorted. 4176 all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) 4177 4178 for address, argfile, encrypted in all_files: 4179 compress = args.compress 4180 4181 # Check whether we can compress the current file before flashing 4182 if compress and encrypted: 4183 print('\nWARNING: - compress and encrypt options are mutually exclusive ') 4184 print('Will flash %s uncompressed' % argfile.name) 4185 compress = False 4186 4187 if args.no_stub: 4188 print('Erasing flash...') 4189 image = pad_to(argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4) 4190 if len(image) == 0: 4191 print('WARNING: File %s is empty' % argfile.name) 4192 continue 4193 image = _update_image_flash_params(esp, address, args, image) 4194 calcmd5 = hashlib.md5(image).hexdigest() 4195 uncsize = len(image) 4196 if compress: 4197 uncimage = image 4198 image = zlib.compress(uncimage, 9) 4199 # Decompress the compressed binary a block at a time, to dynamically calculate the 4200 # timeout based on the real write size 4201 decompress = zlib.decompressobj() 4202 blocks = esp.flash_defl_begin(uncsize, len(image), address) 4203 else: 4204 blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) 4205 argfile.seek(0) # in case we need it again 4206 seq = 0 4207 bytes_sent = 0 # bytes sent on wire 4208 bytes_written = 0 # bytes written to flash 4209 t = time.time() 4210 4211 timeout = DEFAULT_TIMEOUT 4212 4213 while len(image) > 0: 4214 print_overwrite('Writing at 0x%08x... (%d %%)' % (address + bytes_written, 100 * (seq + 1) // blocks)) 4215 sys.stdout.flush() 4216 block = image[0:esp.FLASH_WRITE_SIZE] 4217 if compress: 4218 # feeding each compressed block into the decompressor lets us see block-by-block how much will be written 4219 block_uncompressed = len(decompress.decompress(block)) 4220 bytes_written += block_uncompressed 4221 block_timeout = max(DEFAULT_TIMEOUT, timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed)) 4222 if not esp.IS_STUB: 4223 timeout = block_timeout # ROM code writes block to flash before ACKing 4224 esp.flash_defl_block(block, seq, timeout=timeout) 4225 if esp.IS_STUB: 4226 timeout = block_timeout # Stub ACKs when block is received, then writes to flash while receiving the block after it 4227 else: 4228 # Pad the last block 4229 block = block + b'\xff' * (esp.FLASH_WRITE_SIZE - len(block)) 4230 if encrypted: 4231 esp.flash_encrypt_block(block, seq) 4232 else: 4233 esp.flash_block(block, seq) 4234 bytes_written += len(block) 4235 bytes_sent += len(block) 4236 image = image[esp.FLASH_WRITE_SIZE:] 4237 seq += 1 4238 4239 if esp.IS_STUB: 4240 # Stub only writes each block to flash after 'ack'ing the receive, so do a final dummy operation which will 4241 # not be 'ack'ed until the last block has actually been written out to flash 4242 esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) 4243 4244 t = time.time() - t 4245 speed_msg = "" 4246 if compress: 4247 if t > 0.0: 4248 speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) 4249 print_overwrite('Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s...' % (uncsize, 4250 bytes_sent, 4251 address, t, speed_msg), last_line=True) 4252 else: 4253 if t > 0.0: 4254 speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) 4255 print_overwrite('Wrote %d bytes at 0x%08x in %.1f seconds%s...' % (bytes_written, address, t, speed_msg), last_line=True) 4256 4257 if not encrypted and not esp.secure_download_mode: 4258 try: 4259 res = esp.flash_md5sum(address, uncsize) 4260 if res != calcmd5: 4261 print('File md5: %s' % calcmd5) 4262 print('Flash md5: %s' % res) 4263 print('MD5 of 0xFF is %s' % (hashlib.md5(b'\xFF' * uncsize).hexdigest())) 4264 raise FatalError("MD5 of file does not match data in flash!") 4265 else: 4266 print('Hash of data verified.') 4267 except NotImplementedInROMError: 4268 pass 4269 4270 print('\nLeaving...') 4271 4272 if esp.IS_STUB: 4273 # skip sending flash_finish to ROM loader here, 4274 # as it causes the loader to exit and run user code 4275 esp.flash_begin(0, 0) 4276 4277 # Get the "encrypted" flag for the last file flashed 4278 # Note: all_files list contains triplets like: 4279 # (address: Integer, filename: String, encrypted: Boolean) 4280 last_file_encrypted = all_files[-1][2] 4281 4282 # Check whether the last file flashed was compressed or not 4283 if args.compress and not last_file_encrypted: 4284 esp.flash_defl_finish(False) 4285 else: 4286 esp.flash_finish(False) 4287 4288 if args.verify: 4289 print('Verifying just-written flash...') 4290 print('(This option is deprecated, flash contents are now always read back after flashing.)') 4291 # If some encrypted files have been flashed print a warning saying that we won't check them 4292 if args.encrypt or args.encrypt_files is not None: 4293 print('WARNING: - cannot verify encrypted files, they will be ignored') 4294 # Call verify_flash function only if there at least one non-encrypted file flashed 4295 if not args.encrypt: 4296 verify_flash(esp, args) 4297 4298 4299 def image_info(args): 4300 if args.chip == "auto": 4301 print("WARNING: --chip not specified, defaulting to ESP8266.") 4302 image = LoadFirmwareImage(args.chip, args.filename) 4303 print('Image version: %d' % image.version) 4304 if args.chip != 'auto' and args.chip != 'esp8266': 4305 print( 4306 "Minimal chip revision:", 4307 "v{}.{},".format(image.min_rev_full // 100, image.min_rev_full % 100), 4308 "(legacy min_rev = {})".format(image.min_rev) 4309 ) 4310 print( 4311 "Maximal chip revision:", 4312 "v{}.{}".format(image.max_rev_full // 100, image.max_rev_full % 100), 4313 ) 4314 print('Entry point: %08x' % image.entrypoint if image.entrypoint != 0 else 'Entry point not set') 4315 print('%d segments' % len(image.segments)) 4316 print() 4317 idx = 0 4318 for seg in image.segments: 4319 idx += 1 4320 segs = seg.get_memory_type(image) 4321 seg_name = ",".join(segs) 4322 print('Segment %d: %r [%s]' % (idx, seg, seg_name)) 4323 calc_checksum = image.calculate_checksum() 4324 print('Checksum: %02x (%s)' % (image.checksum, 4325 'valid' if image.checksum == calc_checksum else 'invalid - calculated %02x' % calc_checksum)) 4326 try: 4327 digest_msg = 'Not appended' 4328 if image.append_digest: 4329 is_valid = image.stored_digest == image.calc_digest 4330 digest_msg = "%s (%s)" % (hexify(image.calc_digest).lower(), 4331 "valid" if is_valid else "invalid") 4332 print('Validation Hash: %s' % digest_msg) 4333 except AttributeError: 4334 pass # ESP8266 image has no append_digest field 4335 4336 4337 def make_image(args): 4338 image = ESP8266ROMFirmwareImage() 4339 if len(args.segfile) == 0: 4340 raise FatalError('No segments specified') 4341 if len(args.segfile) != len(args.segaddr): 4342 raise FatalError('Number of specified files does not match number of specified addresses') 4343 for (seg, addr) in zip(args.segfile, args.segaddr): 4344 with open(seg, 'rb') as f: 4345 data = f.read() 4346 image.segments.append(ImageSegment(addr, data)) 4347 image.entrypoint = args.entrypoint 4348 image.save(args.output) 4349 4350 4351 def elf2image(args): 4352 e = ELFFile(args.input) 4353 if args.chip == 'auto': # Default to ESP8266 for backwards compatibility 4354 args.chip = 'esp8266' 4355 4356 print("Creating {} image...".format(args.chip)) 4357 4358 if args.chip == 'esp32': 4359 image = ESP32FirmwareImage() 4360 if args.secure_pad: 4361 image.secure_pad = '1' 4362 elif args.secure_pad_v2: 4363 image.secure_pad = '2' 4364 elif args.chip == 'esp32s2': 4365 image = ESP32S2FirmwareImage() 4366 if args.secure_pad_v2: 4367 image.secure_pad = '2' 4368 elif args.chip == 'esp32s3beta2': 4369 image = ESP32S3BETA2FirmwareImage() 4370 if args.secure_pad_v2: 4371 image.secure_pad = '2' 4372 elif args.chip == 'esp32s3': 4373 image = ESP32S3FirmwareImage() 4374 if args.secure_pad_v2: 4375 image.secure_pad = '2' 4376 elif args.chip == 'esp32c3': 4377 image = ESP32C3FirmwareImage() 4378 if args.secure_pad_v2: 4379 image.secure_pad = '2' 4380 elif args.chip == 'esp32c6beta': 4381 image = ESP32C6BETAFirmwareImage() 4382 if args.secure_pad_v2: 4383 image.secure_pad = '2' 4384 elif args.chip == 'esp32h2beta1': 4385 image = ESP32H2BETA1FirmwareImage() 4386 if args.secure_pad_v2: 4387 image.secure_pad = '2' 4388 elif args.chip == 'esp32h2beta2': 4389 image = ESP32H2BETA2FirmwareImage() 4390 if args.secure_pad_v2: 4391 image.secure_pad = '2' 4392 elif args.chip == 'esp32c2': 4393 image = ESP32C2FirmwareImage() 4394 if args.secure_pad_v2: 4395 image.secure_pad = '2' 4396 elif args.version == '1': # ESP8266 4397 image = ESP8266ROMFirmwareImage() 4398 elif args.version == '2': 4399 image = ESP8266V2FirmwareImage() 4400 else: 4401 image = ESP8266V3FirmwareImage() 4402 image.entrypoint = e.entrypoint 4403 image.flash_mode = {'qio': 0, 'qout': 1, 'dio': 2, 'dout': 3}[args.flash_mode] 4404 4405 if args.chip != 'esp8266': 4406 image.min_rev = args.min_rev 4407 image.min_rev_full = args.min_rev_full 4408 image.max_rev_full = args.max_rev_full 4409 4410 if args.flash_mmu_page_size: 4411 image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size)) 4412 4413 # ELFSection is a subclass of ImageSegment, so can use interchangeably 4414 image.segments = e.segments if args.use_segments else e.sections 4415 4416 if args.pad_to_size: 4417 image.pad_to_size = flash_size_bytes(args.pad_to_size) 4418 4419 image.flash_size_freq = image.ROM_LOADER.parse_flash_size_arg(args.flash_size) 4420 image.flash_size_freq += image.ROM_LOADER.parse_flash_freq_arg(args.flash_freq) 4421 4422 if args.elf_sha256_offset: 4423 image.elf_sha256 = e.sha256() 4424 image.elf_sha256_offset = args.elf_sha256_offset 4425 4426 before = len(image.segments) 4427 image.merge_adjacent_segments() 4428 if len(image.segments) != before: 4429 delta = before - len(image.segments) 4430 print("Merged %d ELF section%s" % (delta, "s" if delta > 1 else "")) 4431 4432 image.verify() 4433 4434 if args.output is None: 4435 args.output = image.default_output_name(args.input) 4436 image.save(args.output) 4437 4438 print("Successfully created {} image.".format(args.chip)) 4439 4440 4441 def read_mac(esp, args): 4442 mac = esp.read_mac() 4443 4444 def print_mac(label, mac): 4445 print('%s: %s' % (label, ':'.join(map(lambda x: '%02x' % x, mac)))) 4446 print_mac("MAC", mac) 4447 4448 4449 def chip_id(esp, args): 4450 try: 4451 chipid = esp.chip_id() 4452 print('Chip ID: 0x%08x' % chipid) 4453 except NotSupportedError: 4454 print('Warning: %s has no Chip ID. Reading MAC instead.' % esp.CHIP_NAME) 4455 read_mac(esp, args) 4456 4457 4458 def erase_flash(esp, args): 4459 print('Erasing flash (this may take a while)...') 4460 t = time.time() 4461 esp.erase_flash() 4462 print('Chip erase completed successfully in %.1fs' % (time.time() - t)) 4463 4464 4465 def erase_region(esp, args): 4466 print('Erasing region (may be slow depending on size)...') 4467 t = time.time() 4468 esp.erase_region(args.address, args.size) 4469 print('Erase completed successfully in %.1f seconds.' % (time.time() - t)) 4470 4471 4472 def run(esp, args): 4473 esp.run() 4474 4475 4476 def flash_id(esp, args): 4477 flash_id = esp.flash_id() 4478 print('Manufacturer: %02x' % (flash_id & 0xff)) 4479 flid_lowbyte = (flash_id >> 16) & 0xFF 4480 print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, flid_lowbyte)) 4481 print('Detected flash size: %s' % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown"))) 4482 4483 4484 def read_flash(esp, args): 4485 if args.no_progress: 4486 flash_progress = None 4487 else: 4488 def flash_progress(progress, length): 4489 msg = '%d (%d %%)' % (progress, progress * 100.0 / length) 4490 padding = '\b' * len(msg) 4491 if progress == length: 4492 padding = '\n' 4493 sys.stdout.write(msg + padding) 4494 sys.stdout.flush() 4495 t = time.time() 4496 data = esp.read_flash(args.address, args.size, flash_progress) 4497 t = time.time() - t 4498 print_overwrite('Read %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' 4499 % (len(data), args.address, t, len(data) / t * 8 / 1000), last_line=True) 4500 with open(args.filename, 'wb') as f: 4501 f.write(data) 4502 4503 4504 def verify_flash(esp, args): 4505 differences = False 4506 4507 for address, argfile in args.addr_filename: 4508 image = pad_to(argfile.read(), 4) 4509 argfile.seek(0) # rewind in case we need it again 4510 4511 image = _update_image_flash_params(esp, address, args, image) 4512 4513 image_size = len(image) 4514 print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)) 4515 # Try digest first, only read if there are differences. 4516 digest = esp.flash_md5sum(address, image_size) 4517 expected_digest = hashlib.md5(image).hexdigest() 4518 if digest == expected_digest: 4519 print('-- verify OK (digest matched)') 4520 continue 4521 else: 4522 differences = True 4523 if getattr(args, 'diff', 'no') != 'yes': 4524 print('-- verify FAILED (digest mismatch)') 4525 continue 4526 4527 flash = esp.read_flash(address, image_size) 4528 assert flash != image 4529 diff = [i for i in range(image_size) if flash[i] != image[i]] 4530 print('-- verify FAILED: %d differences, first @ 0x%08x' % (len(diff), address + diff[0])) 4531 for d in diff: 4532 flash_byte = flash[d] 4533 image_byte = image[d] 4534 if PYTHON2: 4535 flash_byte = ord(flash_byte) 4536 image_byte = ord(image_byte) 4537 print(' %08x %02x %02x' % (address + d, flash_byte, image_byte)) 4538 if differences: 4539 raise FatalError("Verify failed.") 4540 4541 4542 def read_flash_status(esp, args): 4543 print('Status value: 0x%04x' % esp.read_status(args.bytes)) 4544 4545 4546 def write_flash_status(esp, args): 4547 fmt = "0x%%0%dx" % (args.bytes * 2) 4548 args.value = args.value & ((1 << (args.bytes * 8)) - 1) 4549 print(('Initial flash status: ' + fmt) % esp.read_status(args.bytes)) 4550 print(('Setting flash status: ' + fmt) % args.value) 4551 esp.write_status(args.value, args.bytes, args.non_volatile) 4552 print(('After flash status: ' + fmt) % esp.read_status(args.bytes)) 4553 4554 4555 def get_security_info(esp, args): 4556 si = esp.get_security_info() 4557 # TODO: better display and tests 4558 print('Flags: {:#010x} ({})'.format(si["flags"], bin(si["flags"]))) 4559 print('Flash_Crypt_Cnt: {:#x}'.format(si["flash_crypt_cnt"])) 4560 print('Key_Purposes: {}'.format(si["key_purposes"])) 4561 if si["chip_id"] is not None and si["api_version"] is not None: 4562 print('Chip_ID: {}'.format(si["chip_id"])) 4563 print('Api_Version: {}'.format(si["api_version"])) 4564 4565 4566 def merge_bin(args): 4567 try: 4568 chip_class = _chip_to_rom_loader(args.chip) 4569 except KeyError: 4570 msg = "Please specify the chip argument" if args.chip == "auto" else "Invalid chip choice: '{}'".format(args.chip) 4571 msg = msg + " (choose from {})".format(', '.join(SUPPORTED_CHIPS)) 4572 raise FatalError(msg) 4573 4574 # sort the files by offset. The AddrFilenamePairAction has already checked for overlap 4575 input_files = sorted(args.addr_filename, key=lambda x: x[0]) 4576 if not input_files: 4577 raise FatalError("No input files specified") 4578 first_addr = input_files[0][0] 4579 if first_addr < args.target_offset: 4580 raise FatalError("Output file target offset is 0x%x. Input file offset 0x%x is before this." % (args.target_offset, first_addr)) 4581 4582 if args.format != 'raw': 4583 raise FatalError("This version of esptool only supports the 'raw' output format") 4584 4585 with open(args.output, 'wb') as of: 4586 def pad_to(flash_offs): 4587 # account for output file offset if there is any 4588 of.write(b'\xFF' * (flash_offs - args.target_offset - of.tell())) 4589 for addr, argfile in input_files: 4590 pad_to(addr) 4591 image = argfile.read() 4592 image = _update_image_flash_params(chip_class, addr, args, image) 4593 of.write(image) 4594 if args.fill_flash_size: 4595 pad_to(flash_size_bytes(args.fill_flash_size)) 4596 print("Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" % (of.tell(), args.output, args.target_offset)) 4597 4598 4599 def version(args): 4600 print(__version__) 4601 4602 # 4603 # End of operations functions 4604 # 4605 4606 4607 def main(argv=None, esp=None): 4608 """ 4609 Main function for esptool 4610 4611 argv - Optional override for default arguments parsing (that uses sys.argv), can be a list of custom arguments 4612 as strings. Arguments and their values need to be added as individual items to the list e.g. "-b 115200" thus 4613 becomes ['-b', '115200']. 4614 4615 esp - Optional override of the connected device previously returned by get_default_connected_device() 4616 """ 4617 4618 external_esp = esp is not None 4619 4620 parser = argparse.ArgumentParser(description='esptool.py v%s - Espressif chips ROM Bootloader Utility' % __version__, prog='esptool') 4621 4622 parser.add_argument('--chip', '-c', 4623 help='Target chip type', 4624 type=format_chip_name, # support ESP32-S2, etc. 4625 choices=['auto'] + SUPPORTED_CHIPS, 4626 default=os.environ.get('ESPTOOL_CHIP', 'auto')) 4627 4628 parser.add_argument( 4629 '--port', '-p', 4630 help='Serial port device', 4631 default=os.environ.get('ESPTOOL_PORT', None)) 4632 4633 parser.add_argument( 4634 '--baud', '-b', 4635 help='Serial port baud rate used when flashing/reading', 4636 type=arg_auto_int, 4637 default=os.environ.get('ESPTOOL_BAUD', ESPLoader.ESP_ROM_BAUD)) 4638 4639 parser.add_argument( 4640 '--before', 4641 help='What to do before connecting to the chip', 4642 choices=['default_reset', 'usb_reset', 'no_reset', 'no_reset_no_sync'], 4643 default=os.environ.get('ESPTOOL_BEFORE', 'default_reset')) 4644 4645 parser.add_argument( 4646 '--after', '-a', 4647 help='What to do after esptool.py is finished', 4648 choices=['hard_reset', 'soft_reset', 'no_reset', 'no_reset_stub'], 4649 default=os.environ.get('ESPTOOL_AFTER', 'hard_reset')) 4650 4651 parser.add_argument( 4652 '--no-stub', 4653 help="Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available.", 4654 action='store_true') 4655 4656 parser.add_argument( 4657 '--trace', '-t', 4658 help="Enable trace-level output of esptool.py interactions.", 4659 action='store_true') 4660 4661 parser.add_argument( 4662 '--override-vddsdio', 4663 help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", 4664 choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, 4665 nargs='?') 4666 4667 parser.add_argument( 4668 '--connect-attempts', 4669 help=('Number of attempts to connect, negative or 0 for infinite. ' 4670 'Default: %d.' % DEFAULT_CONNECT_ATTEMPTS), 4671 type=int, 4672 default=os.environ.get('ESPTOOL_CONNECT_ATTEMPTS', DEFAULT_CONNECT_ATTEMPTS)) 4673 4674 subparsers = parser.add_subparsers( 4675 dest='operation', 4676 help='Run esptool {command} -h for additional help') 4677 4678 def add_spi_connection_arg(parent): 4679 parent.add_argument('--spi-connection', '-sc', help='ESP32-only argument. Override default SPI Flash connection. ' 4680 'Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers to use for SPI flash (CLK,Q,D,HD,CS).', 4681 action=SpiConnectionAction) 4682 4683 parser_load_ram = subparsers.add_parser( 4684 'load_ram', 4685 help='Download an image to RAM and execute') 4686 parser_load_ram.add_argument('filename', help='Firmware image') 4687 4688 parser_dump_mem = subparsers.add_parser( 4689 'dump_mem', 4690 help='Dump arbitrary memory to disk') 4691 parser_dump_mem.add_argument('address', help='Base address', type=arg_auto_int) 4692 parser_dump_mem.add_argument('size', help='Size of region to dump', type=arg_auto_int) 4693 parser_dump_mem.add_argument('filename', help='Name of binary dump') 4694 4695 parser_read_mem = subparsers.add_parser( 4696 'read_mem', 4697 help='Read arbitrary memory location') 4698 parser_read_mem.add_argument('address', help='Address to read', type=arg_auto_int) 4699 4700 parser_write_mem = subparsers.add_parser( 4701 'write_mem', 4702 help='Read-modify-write to arbitrary memory location') 4703 parser_write_mem.add_argument('address', help='Address to write', type=arg_auto_int) 4704 parser_write_mem.add_argument('value', help='Value', type=arg_auto_int) 4705 parser_write_mem.add_argument('mask', help='Mask of bits to write', type=arg_auto_int, nargs='?', default='0xFFFFFFFF') 4706 4707 def add_spi_flash_subparsers(parent, allow_keep, auto_detect): 4708 """ Add common parser arguments for SPI flash properties """ 4709 extra_keep_args = ['keep'] if allow_keep else [] 4710 4711 if auto_detect and allow_keep: 4712 extra_fs_message = ", detect, or keep" 4713 elif auto_detect: 4714 extra_fs_message = ", or detect" 4715 elif allow_keep: 4716 extra_fs_message = ", or keep" 4717 else: 4718 extra_fs_message = "" 4719 4720 parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', 4721 choices=extra_keep_args + ['80m', '60m', '48m', '40m', '30m', '26m', '24m', '20m', '16m', '15m', '12m'], 4722 default=os.environ.get('ESPTOOL_FF', 'keep' if allow_keep else '40m')) 4723 parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', 4724 choices=extra_keep_args + ['qio', 'qout', 'dio', 'dout'], 4725 default=os.environ.get('ESPTOOL_FM', 'keep' if allow_keep else 'qio')) 4726 parent.add_argument('--flash_size', '-fs', help='SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB)' 4727 ' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)' + extra_fs_message, 4728 action=FlashSizeAction, auto_detect=auto_detect, 4729 default=os.environ.get('ESPTOOL_FS', 'keep' if allow_keep else '1MB')) 4730 add_spi_connection_arg(parent) 4731 4732 parser_write_flash = subparsers.add_parser( 4733 'write_flash', 4734 help='Write a binary blob to flash') 4735 4736 parser_write_flash.add_argument('addr_filename', metavar='<address> <filename>', help='Address followed by binary filename, separated by space', 4737 action=AddrFilenamePairAction) 4738 parser_write_flash.add_argument('--erase-all', '-e', 4739 help='Erase all regions of flash (not just write areas) before programming', 4740 action="store_true") 4741 4742 add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) 4743 parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") 4744 parser_write_flash.add_argument('--verify', help='Verify just-written data on flash ' 4745 '(mostly superfluous, data is read back during flashing)', action='store_true') 4746 parser_write_flash.add_argument('--encrypt', help='Apply flash encryption when writing data (required correct efuse settings)', 4747 action='store_true') 4748 # In order to not break backward compatibility, our list of encrypted files to flash is a new parameter 4749 parser_write_flash.add_argument('--encrypt-files', metavar='<address> <filename>', 4750 help='Files to be encrypted on the flash. Address followed by binary filename, separated by space.', 4751 action=AddrFilenamePairAction) 4752 parser_write_flash.add_argument('--ignore-flash-encryption-efuse-setting', help='Ignore flash encryption efuse settings ', 4753 action='store_true') 4754 4755 compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) 4756 compress_args.add_argument('--compress', '-z', help='Compress data in transfer (default unless --no-stub is specified)', 4757 action="store_true", default=None) 4758 compress_args.add_argument('--no-compress', '-u', help='Disable data compression during transfer (default if --no-stub is specified)', 4759 action="store_true") 4760 4761 subparsers.add_parser( 4762 'run', 4763 help='Run application code in flash') 4764 4765 parser_image_info = subparsers.add_parser( 4766 'image_info', 4767 help='Dump headers from an application image') 4768 parser_image_info.add_argument('filename', help='Image file to parse') 4769 4770 parser_make_image = subparsers.add_parser( 4771 'make_image', 4772 help='Create an application image from binary files') 4773 parser_make_image.add_argument('output', help='Output image file') 4774 parser_make_image.add_argument('--segfile', '-f', action='append', help='Segment input file') 4775 parser_make_image.add_argument('--segaddr', '-a', action='append', help='Segment base address', type=arg_auto_int) 4776 parser_make_image.add_argument('--entrypoint', '-e', help='Address of entry point', type=arg_auto_int, default=0) 4777 4778 parser_elf2image = subparsers.add_parser( 4779 'elf2image', 4780 help='Create an application image from ELF file') 4781 parser_elf2image.add_argument('input', help='Input ELF file') 4782 parser_elf2image.add_argument('--output', '-o', help='Output filename prefix (for version 1 image), or filename (for version 2 single image)', type=str) 4783 parser_elf2image.add_argument('--version', '-e', help='Output image version', choices=['1', '2', '3'], default='1') 4784 parser_elf2image.add_argument( 4785 # kept for compatibility 4786 # Minimum chip revision (deprecated, consider using --min-rev-full) 4787 "--min-rev", 4788 "-r", 4789 # In v3 we do not do help=argparse.SUPPRESS because 4790 # it should remain visible. 4791 help="Minimal chip revision (ECO version format)", 4792 type=int, 4793 choices=range(256), 4794 metavar="{0, ... 255}", 4795 default=0, 4796 ) 4797 parser_elf2image.add_argument( 4798 "--min-rev-full", 4799 help="Minimal chip revision (in format: major * 100 + minor)", 4800 type=int, 4801 choices=range(65536), 4802 metavar="{0, ... 65535}", 4803 default=0, 4804 ) 4805 parser_elf2image.add_argument( 4806 "--max-rev-full", 4807 help="Maximal chip revision (in format: major * 100 + minor)", 4808 type=int, 4809 choices=range(65536), 4810 metavar="{0, ... 65535}", 4811 default=65535, 4812 ) 4813 parser_elf2image.add_argument('--secure-pad', action='store_true', 4814 help='Pad image so once signed it will end on a 64KB boundary. For Secure Boot v1 images only.') 4815 parser_elf2image.add_argument('--secure-pad-v2', action='store_true', 4816 help='Pad image to 64KB, so once signed its signature sector will start at the next 64K block. ' 4817 'For Secure Boot v2 images only.') 4818 parser_elf2image.add_argument('--elf-sha256-offset', help='If set, insert SHA256 hash (32 bytes) of the input ELF file at specified offset in the binary.', 4819 type=arg_auto_int, default=None) 4820 parser_elf2image.add_argument('--use_segments', help='If set, ELF segments will be used instead of ELF sections to genereate the image.', 4821 action='store_true') 4822 parser_elf2image.add_argument('--flash-mmu-page-size', help="Change flash MMU page size.", choices=['64KB', '32KB', '16KB']) 4823 parser_elf2image.add_argument( 4824 "--pad-to-size", 4825 help="The block size with which the final binary image after padding must be aligned to. Value 0xFF is used for padding, similar to erase_flash", 4826 default=None, 4827 ) 4828 add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) 4829 4830 subparsers.add_parser( 4831 'read_mac', 4832 help='Read MAC address from OTP ROM') 4833 4834 subparsers.add_parser( 4835 'chip_id', 4836 help='Read Chip ID from OTP ROM') 4837 4838 parser_flash_id = subparsers.add_parser( 4839 'flash_id', 4840 help='Read SPI flash manufacturer and device ID') 4841 add_spi_connection_arg(parser_flash_id) 4842 4843 parser_read_status = subparsers.add_parser( 4844 'read_flash_status', 4845 help='Read SPI flash status register') 4846 4847 add_spi_connection_arg(parser_read_status) 4848 parser_read_status.add_argument('--bytes', help='Number of bytes to read (1-3)', type=int, choices=[1, 2, 3], default=2) 4849 4850 parser_write_status = subparsers.add_parser( 4851 'write_flash_status', 4852 help='Write SPI flash status register') 4853 4854 add_spi_connection_arg(parser_write_status) 4855 parser_write_status.add_argument('--non-volatile', help='Write non-volatile bits (use with caution)', action='store_true') 4856 parser_write_status.add_argument('--bytes', help='Number of status bytes to write (1-3)', type=int, choices=[1, 2, 3], default=2) 4857 parser_write_status.add_argument('value', help='New value', type=arg_auto_int) 4858 4859 parser_read_flash = subparsers.add_parser( 4860 'read_flash', 4861 help='Read SPI flash content') 4862 add_spi_connection_arg(parser_read_flash) 4863 parser_read_flash.add_argument('address', help='Start address', type=arg_auto_int) 4864 parser_read_flash.add_argument('size', help='Size of region to dump', type=arg_auto_int) 4865 parser_read_flash.add_argument('filename', help='Name of binary dump') 4866 parser_read_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") 4867 4868 parser_verify_flash = subparsers.add_parser( 4869 'verify_flash', 4870 help='Verify a binary blob against flash') 4871 parser_verify_flash.add_argument('addr_filename', help='Address and binary file to verify there, separated by space', 4872 action=AddrFilenamePairAction) 4873 parser_verify_flash.add_argument('--diff', '-d', help='Show differences', 4874 choices=['no', 'yes'], default='no') 4875 add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) 4876 4877 parser_erase_flash = subparsers.add_parser( 4878 'erase_flash', 4879 help='Perform Chip Erase on SPI flash') 4880 add_spi_connection_arg(parser_erase_flash) 4881 4882 parser_erase_region = subparsers.add_parser( 4883 'erase_region', 4884 help='Erase a region of the flash') 4885 add_spi_connection_arg(parser_erase_region) 4886 parser_erase_region.add_argument('address', help='Start address (must be multiple of 4096)', type=arg_auto_int) 4887 parser_erase_region.add_argument('size', help='Size of region to erase (must be multiple of 4096)', type=arg_auto_int) 4888 4889 parser_merge_bin = subparsers.add_parser( 4890 'merge_bin', 4891 help='Merge multiple raw binary files into a single file for later flashing') 4892 4893 parser_merge_bin.add_argument('--output', '-o', help='Output filename', type=str, required=True) 4894 parser_merge_bin.add_argument('--format', '-f', help='Format of the output file', choices='raw', default='raw') # for future expansion 4895 add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) 4896 4897 parser_merge_bin.add_argument('--target-offset', '-t', help='Target offset where the output file will be flashed', 4898 type=arg_auto_int, default=0) 4899 parser_merge_bin.add_argument('--fill-flash-size', help='If set, the final binary file will be padded with FF ' 4900 'bytes up to this flash size.', action=FlashSizeAction) 4901 parser_merge_bin.add_argument('addr_filename', metavar='<address> <filename>', 4902 help='Address followed by binary filename, separated by space', 4903 action=AddrFilenamePairAction) 4904 4905 subparsers.add_parser('get_security_info', help='Get some security-related data') 4906 4907 subparsers.add_parser('version', help='Print esptool version') 4908 4909 # internal sanity check - every operation matches a module function of the same name 4910 for operation in subparsers.choices.keys(): 4911 assert operation in globals(), "%s should be a module function" % operation 4912 4913 argv = expand_file_arguments(argv or sys.argv[1:]) 4914 4915 args = parser.parse_args(argv) 4916 print('esptool.py v%s' % __version__) 4917 4918 # operation function can take 1 arg (args), 2 args (esp, arg) 4919 # or be a member function of the ESPLoader class. 4920 4921 if args.operation is None: 4922 parser.print_help() 4923 sys.exit(1) 4924 4925 # Forbid the usage of both --encrypt, which means encrypt all the given files, 4926 # and --encrypt-files, which represents the list of files to encrypt. 4927 # The reason is that allowing both at the same time increases the chances of 4928 # having contradictory lists (e.g. one file not available in one of list). 4929 if args.operation == "write_flash" and args.encrypt and args.encrypt_files is not None: 4930 raise FatalError("Options --encrypt and --encrypt-files must not be specified at the same time.") 4931 4932 operation_func = globals()[args.operation] 4933 4934 if PYTHON2: 4935 # This function is depreciated in Python3 4936 operation_args = inspect.getargspec(operation_func).args 4937 else: 4938 operation_args = inspect.getfullargspec(operation_func).args 4939 4940 if operation_args[0] == 'esp': # operation function takes an ESPLoader connection object 4941 if args.before != "no_reset_no_sync": 4942 initial_baud = min(ESPLoader.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate 4943 else: 4944 initial_baud = args.baud 4945 4946 if args.port is None: 4947 ser_list = get_port_list() 4948 print("Found %d serial ports" % len(ser_list)) 4949 else: 4950 ser_list = [args.port] 4951 esp = esp or get_default_connected_device(ser_list, port=args.port, connect_attempts=args.connect_attempts, 4952 initial_baud=initial_baud, chip=args.chip, trace=args.trace, 4953 before=args.before) 4954 4955 if esp is None: 4956 raise FatalError("Could not connect to an Espressif device on any of the %d available serial ports." % len(ser_list)) 4957 4958 if esp.secure_download_mode: 4959 print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) 4960 else: 4961 print("Chip is %s" % (esp.get_chip_description())) 4962 print("Features: %s" % ", ".join(esp.get_chip_features())) 4963 print("Crystal is %dMHz" % esp.get_crystal_freq()) 4964 read_mac(esp, args) 4965 4966 if not args.no_stub: 4967 if esp.secure_download_mode: 4968 print("WARNING: Stub loader is not supported in Secure Download Mode, setting --no-stub") 4969 args.no_stub = True 4970 elif not esp.IS_STUB and esp.stub_is_disabled: 4971 print("WARNING: Stub loader has been disabled for compatibility, setting --no-stub") 4972 args.no_stub = True 4973 else: 4974 esp = esp.run_stub() 4975 4976 if args.override_vddsdio: 4977 esp.override_vddsdio(args.override_vddsdio) 4978 4979 if args.baud > initial_baud: 4980 try: 4981 esp.change_baud(args.baud) 4982 except NotImplementedInROMError: 4983 print("WARNING: ROM doesn't support changing baud rate. Keeping initial baud rate %d" % initial_baud) 4984 4985 # override common SPI flash parameter stuff if configured to do so 4986 if hasattr(args, "spi_connection") and args.spi_connection is not None: 4987 if esp.CHIP_NAME != "ESP32": 4988 raise FatalError("Chip %s does not support --spi-connection option." % esp.CHIP_NAME) 4989 print("Configuring SPI flash mode...") 4990 esp.flash_spi_attach(args.spi_connection) 4991 elif args.no_stub: 4992 print("Enabling default SPI flash mode...") 4993 # ROM loader doesn't enable flash unless we explicitly do it 4994 esp.flash_spi_attach(0) 4995 4996 # XMC chip startup sequence 4997 XMC_VENDOR_ID = 0x20 4998 4999 def is_xmc_chip_strict(): 5000 id = esp.flash_id() 5001 rdid = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00) 5002 5003 vendor_id = ((rdid >> 16) & 0xFF) 5004 mfid = ((rdid >> 8) & 0xFF) 5005 cpid = (rdid & 0xFF) 5006 5007 if vendor_id != XMC_VENDOR_ID: 5008 return False 5009 5010 matched = False 5011 if mfid == 0x40: 5012 if cpid >= 0x13 and cpid <= 0x20: 5013 matched = True 5014 elif mfid == 0x41: 5015 if cpid >= 0x17 and cpid <= 0x20: 5016 matched = True 5017 elif mfid == 0x50: 5018 if cpid >= 0x15 and cpid <= 0x16: 5019 matched = True 5020 return matched 5021 5022 def flash_xmc_startup(): 5023 # If the RDID value is a valid XMC one, may skip the flow 5024 fast_check = True 5025 if fast_check and is_xmc_chip_strict(): 5026 return # Successful XMC flash chip boot-up detected by RDID, skipping. 5027 5028 sfdp_mfid_addr = 0x10 5029 mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) 5030 if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. 5031 return 5032 5033 print("WARNING: XMC flash chip boot-up failure detected! Running XMC25QHxxC startup flow") 5034 esp.run_spiflash_command(0xB9) # Enter DPD 5035 esp.run_spiflash_command(0x79) # Enter UDPD 5036 esp.run_spiflash_command(0xFF) # Exit UDPD 5037 time.sleep(0.002) # Delay tXUDPD 5038 esp.run_spiflash_command(0xAB) # Release Power-Down 5039 time.sleep(0.00002) 5040 # Check for success 5041 if not is_xmc_chip_strict(): 5042 print("WARNING: XMC flash boot-up fix failed.") 5043 print("XMC flash chip boot-up fix successful!") 5044 5045 # Check flash chip connection 5046 if not esp.secure_download_mode: 5047 try: 5048 flash_id = esp.flash_id() 5049 if flash_id in (0xffffff, 0x000000): 5050 print('WARNING: Failed to communicate with the flash chip, read/write operations will fail. ' 5051 'Try checking the chip connections or removing any other hardware connected to IOs.') 5052 except Exception as e: 5053 esp.trace('Unable to verify flash chip connection ({}).'.format(e)) 5054 5055 # Check if XMC SPI flash chip booted-up successfully, fix if not 5056 if not esp.secure_download_mode: 5057 try: 5058 flash_xmc_startup() 5059 except Exception as e: 5060 esp.trace('Unable to perform XMC flash chip startup sequence ({}).'.format(e)) 5061 5062 if hasattr(args, "flash_size"): 5063 print("Configuring flash size...") 5064 detect_flash_size(esp, args) 5065 if args.flash_size != 'keep': # TODO: should set this even with 'keep' 5066 esp.flash_set_parameters(flash_size_bytes(args.flash_size)) 5067 # Check if stub supports chosen flash size 5068 if esp.IS_STUB and args.flash_size in ('32MB', '64MB', '128MB'): 5069 print("WARNING: Flasher stub doesn't fully support flash size larger than 16MB, in case of failure use --no-stub.") 5070 5071 if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): 5072 if args.address + args.size > 0x1000000: 5073 print("WARNING: Flasher stub doesn't fully support flash size larger than 16MB, in case of failure use --no-stub.") 5074 5075 try: 5076 operation_func(esp, args) 5077 finally: 5078 try: # Clean up AddrFilenamePairAction files 5079 for address, argfile in args.addr_filename: 5080 argfile.close() 5081 except AttributeError: 5082 pass 5083 5084 # Handle post-operation behaviour (reset or other) 5085 if operation_func == load_ram: 5086 # the ESP is now running the loaded image, so let it run 5087 print('Exiting immediately.') 5088 elif args.after == 'hard_reset': 5089 esp.hard_reset() 5090 elif args.after == 'soft_reset': 5091 print('Soft resetting...') 5092 # flash_finish will trigger a soft reset 5093 esp.soft_reset(False) 5094 elif args.after == 'no_reset_stub': 5095 print('Staying in flasher stub.') 5096 else: # args.after == 'no_reset' 5097 print('Staying in bootloader.') 5098 if esp.IS_STUB: 5099 esp.soft_reset(True) # exit stub back to ROM loader 5100 5101 if not external_esp: 5102 esp._port.close() 5103 5104 else: 5105 operation_func(args) 5106 5107 5108 def get_port_list(): 5109 if list_ports is None: 5110 raise FatalError("Listing all serial ports is currently not available. Please try to specify the port when " 5111 "running esptool.py or update the pyserial package to the latest version") 5112 return sorted(ports.device for ports in list_ports.comports()) 5113 5114 5115 def expand_file_arguments(argv): 5116 """ Any argument starting with "@" gets replaced with all values read from a text file. 5117 Text file arguments can be split by newline or by space. 5118 Values are added "as-is", as if they were specified in this order on the command line. 5119 """ 5120 new_args = [] 5121 expanded = False 5122 for arg in argv: 5123 if arg.startswith("@"): 5124 expanded = True 5125 with open(arg[1:], "r") as f: 5126 for line in f.readlines(): 5127 new_args += shlex.split(line) 5128 else: 5129 new_args.append(arg) 5130 if expanded: 5131 print("esptool.py %s" % (" ".join(new_args[1:]))) 5132 return new_args 5133 return argv 5134 5135 5136 class FlashSizeAction(argparse.Action): 5137 """ Custom flash size parser class to support backwards compatibility with megabit size arguments. 5138 5139 (At next major relase, remove deprecated sizes and this can become a 'normal' choices= argument again.) 5140 """ 5141 def __init__(self, option_strings, dest, nargs=1, auto_detect=False, **kwargs): 5142 super(FlashSizeAction, self).__init__(option_strings, dest, nargs, **kwargs) 5143 self._auto_detect = auto_detect 5144 5145 def __call__(self, parser, namespace, values, option_string=None): 5146 try: 5147 value = { 5148 '2m': '256KB', 5149 '4m': '512KB', 5150 '8m': '1MB', 5151 '16m': '2MB', 5152 '32m': '4MB', 5153 '16m-c1': '2MB-c1', 5154 '32m-c1': '4MB-c1', 5155 }[values[0]] 5156 print("WARNING: Flash size arguments in megabits like '%s' are deprecated." % (values[0])) 5157 print("Please use the equivalent size '%s'." % (value)) 5158 print("Megabit arguments may be removed in a future release.") 5159 except KeyError: 5160 value = values[0] 5161 5162 known_sizes = dict(ESP8266ROM.FLASH_SIZES) 5163 known_sizes.update(ESP32ROM.FLASH_SIZES) 5164 if self._auto_detect: 5165 known_sizes['detect'] = 'detect' 5166 known_sizes['keep'] = 'keep' 5167 if value not in known_sizes: 5168 raise argparse.ArgumentError(self, '%s is not a known flash size. Known sizes: %s' % (value, ", ".join(known_sizes.keys()))) 5169 setattr(namespace, self.dest, value) 5170 5171 5172 class SpiConnectionAction(argparse.Action): 5173 """ Custom action to parse 'spi connection' override. Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. 5174 """ 5175 def __call__(self, parser, namespace, value, option_string=None): 5176 if value.upper() == "SPI": 5177 value = 0 5178 elif value.upper() == "HSPI": 5179 value = 1 5180 elif "," in value: 5181 values = value.split(",") 5182 if len(values) != 5: 5183 raise argparse.ArgumentError(self, '%s is not a valid list of comma-separate pin numbers. Must be 5 numbers - CLK,Q,D,HD,CS.' % value) 5184 try: 5185 values = tuple(int(v, 0) for v in values) 5186 except ValueError: 5187 raise argparse.ArgumentError(self, '%s is not a valid argument. All pins must be numeric values' % values) 5188 if any([v for v in values if v > 33 or v < 0]): 5189 raise argparse.ArgumentError(self, 'Pin numbers must be in the range 0-33.') 5190 # encode the pin numbers as a 32-bit integer with packed 6-bit values, the same way ESP32 ROM takes them 5191 # TODO: make this less ESP32 ROM specific somehow... 5192 clk, q, d, hd, cs = values 5193 value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk 5194 else: 5195 raise argparse.ArgumentError(self, '%s is not a valid spi-connection value. ' 5196 'Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS).' % value) 5197 setattr(namespace, self.dest, value) 5198 5199 5200 class AddrFilenamePairAction(argparse.Action): 5201 """ Custom parser class for the address/filename pairs passed as arguments """ 5202 def __init__(self, option_strings, dest, nargs='+', **kwargs): 5203 super(AddrFilenamePairAction, self).__init__(option_strings, dest, nargs, **kwargs) 5204 5205 def __call__(self, parser, namespace, values, option_string=None): 5206 # validate pair arguments 5207 pairs = [] 5208 for i in range(0, len(values), 2): 5209 try: 5210 address = int(values[i], 0) 5211 except ValueError: 5212 raise argparse.ArgumentError(self, 'Address "%s" must be a number' % values[i]) 5213 try: 5214 argfile = open(values[i + 1], 'rb') 5215 except IOError as e: 5216 raise argparse.ArgumentError(self, e) 5217 except IndexError: 5218 raise argparse.ArgumentError(self, 'Must be pairs of an address and the binary filename to write there') 5219 pairs.append((address, argfile)) 5220 5221 # Sort the addresses and check for overlapping 5222 end = 0 5223 for address, argfile in sorted(pairs, key=lambda x: x[0]): 5224 argfile.seek(0, 2) # seek to end 5225 size = argfile.tell() 5226 argfile.seek(0) 5227 sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) 5228 sector_end = ((address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) & ~(ESPLoader.FLASH_SECTOR_SIZE - 1)) - 1 5229 if sector_start < end: 5230 message = 'Detected overlap at address: 0x%x for file: %s' % (address, argfile.name) 5231 raise argparse.ArgumentError(self, message) 5232 end = sector_end 5233 setattr(namespace, self.dest, pairs) 5234 5235 5236 # Binary stub code (see flasher_stub dir for source & details) 5237 ESP8266ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5238 eNq9Pftj1DbS/4rthCQbkiLZXq/Mo2w2yQItXCEcKddL28gvelxpwzZXcj34/vbP85Jl7yaB67U/LFl5ZWk0M5q3xH8265/OF//evB1oNUlNmmTjeCfYrOy5bZ8VmycXypxcGH1y0dT328aYP2n7Ue0nbj9J+5lw\ 5239 O+FPQe0iP7mo2t+0mp5c1I3X0FXbMNworGv80PZzfer2cY6Nc/ft5KJUruH3Ni0sleVG03gNfKEYvNB9e9n+Wg6etf9WDb8OC6kVNu64b6sGouUtdWjX1w5Va2y0S6pjfmxbTNUJNtr56xS/tf/W40unWPWtXVmd\ 5240 DZ597c2ew/IrwVLj4d1mbrK2Ufj4K1fiuC7dXPojofv0b5GD43sPoqL2YFWa+U15fOi3k0E7HbTHg/ak1z7vtRb9vnowt879dug3ej33/Ibtj2EGY5bD9en+ms2gjd/jQTsZtNNBOxu0zaBd9tt6AI/u9Q/8Rq/n\ 5241 1G+cDtb1R370Ne34E3noOp66jseG7eya9uSatrmyfX5F66crWk52X9our2wvrto7134+dd9mn4Sj809Y9xDy5hopMIBcDyDRAyzq3nhrfuOm3+gNe8dv7PuN536jR5BfBpJmAKcdtMtBu05W7BL9J+7iP1oK/F4p\ 5242 8XulyO+VMr9XCl3X/sSP5r2hY28HTnDnZbjjxrzTUpYcCe406F0z9pvVOm+JMr2VbrZW63l9cc5WqzWisNhaAIOwaeZ9CYCmERq3zX0GVRpG0cftm9RvT51Se7jHL+SpGwr+zWlKQAMo80YFAcwdWyJxOSZ7WEEH\ 5243 C7C1q88zQEXyrG2l8DoMncEXLU/aQQCBRn3xAsxaVLo/wDuzdiqk3FRRX13sA5DwlfvNXsC/tzP3IEIJEsmbYNAVNAm818qvPL4fkr2HINCXFqgaF3c77oPwTHqcbMJSaO0mW80m/OKIyMitv8Ew824PeY/T3iuJ\ 5244 JoO+BSJybCQd4iPhPN3hX6NAb0To9cR7bnwmUM7d+erh3iPiJFvyrzZ1ja0WBLL0H7cLFyu1k72nwnPL++NaGcXPTNnnQeeN+J9ukumyTUlOW+o12vk3vRHTVeAyyL2V99zAovdLb6eY0WB3Nf4ACTe08howkhst\ 5245 L3k/NPdhhEq2o3GPiWBDfdpp+tNOzT9lqSINJ5TUXQ/MQnnzF6nXqKBhsXHHe6HpSY3ShwyGqj0R4hsV2v8Re8rqrlOoAKH2BHOj+4yV+/TAhpXllELPKSHRNWzXeIlUnB7M8c/OY/xz8dDx1Bf8rUgf8bey/Iy/\ 5246 VQbdn+lDcpiVOJw1Lmn6eEPm5ndDggmgz0H0sWORs9ooVWTXItyhrE5tK6XK2LYCrootCJ/YglyLLeOtZklb+i5YEbPMKhLGVMkKI/OxDSDFX0YT6G1IKBeAZs0QwAZU5f52SHrLsoLAfjCYDt/z5Po3ntCiWNre\ 5247 ceKo/QIYikN6vwMGn2r/6RENXy2tSJOr3jQRYQyBoOGBSkmwHvTlI8If8HDJcDh+Hn/s87eyEVsRn9esBOiLli8FQyYOAZuJZbWCOjkygCZMrTnIDb2ms1/kHUZgxb8MBH3ePdVxtAc8FqFcR+d1HZ+MZ8/2Yxtt\ 5248 ILe1ckGXCQQZ4oBVU88/oLeTWCwSg8pRqyhoQL/qrV039xb0iGzU5yhdRtGzfWIQYhZhJLZ2QOZpGx224BT08+oZ46BF0wSlyoS4WSc8VMlGWp5549c1rLV9OGZgxsQxSs8puNVJCw9FwMUPaB8iItsXcgpmbKb7\ 5249 QFF4WoLBwFKsyRZBw71N9lYezgCRwJvU/xiet/OWGOQA1MnoBp2i5qgb2fLIQIwSbYUNiOT9mywf4+bqegLYuQ82/GhweU1/Lc61yTr+0+W6e3X+nteKGhGQ1B/2c/mZpDhExJbm5SkA9MJ5fA2RrJ3hS5kVVG03\ 5250 8UvGUIGvz4iGlX4AnPkDzogmdOm9YvyeCnt+xSh0Jof8HPegvIVGEf+UTNyI7ReAeNx+sQj6zoy3WI0WXCKYfI29brKxKJIZ2M34PK7UoTyhHZDzd+O+u93A4MD207iJCtAp9JoBttHHzLeVgwlliarnvHfEkCzJ\ 5251 9zbZ97wY3APIRknHZ7njhVP2QfQ/lsVfY2nAvIzoNdOz3oIdiOHpZvjiMVBjDZQSKcEGbYQd6lKWtwC6f4hrFaCBfkxQg9Vf65s0IAokhBMtcATyuxVzVSC9atX94uZqJp+TLeoNRTB/vTTOEcUgVsB8LB7J/BsA\ 5252 mjFpmM37tkrDTAzsVFWeegN6xZ3EMtnF6tcOGC+ZZ5DLi0pvSD/ocYYcuAu6U1OABWar444n3YwxcHDh6I+RHvMtyG8z/hXF4t3O1V05ncmQ9DFOF9KCLp9u4nY8mptb91gtlQFFUExMs5djCMfA5ga5DtQnW+AR\ 5253 DFUlN8GcWQNdnjL7Zt94erNJvHfqmecoCSAZBdHBF6WVv+QO5mhrN6boToPbuBRpgQoN5stBw8Ae0+YJvF6waml8uh91frcuDp62bu1fYYjJDTBKSY2Nj/AP62LYRMCJFC9D7fZ0P5jGAf086dgFdZ8CbRtswJiy\ 5254 JggtA9w57vL9QbAdjSWHk/gKnDhuQLkOqsV1MZx7cKx2l4Nf8czhDx7f7sS5iTvIidTAct0IE6d7wBsZN/ud0kCrezzHFaggXb1hEB4nw6kvCA4waiDhU7E8jN8TXyCJUBNsfR6LjaiWTLmHn21BXq1A8z1YB+M7\ 5255 DoHvkaq1s2+OyG7fV08PKCfTNzAzcsBAVgLOy5qs+GZyl7CJshOdFFr4nOQHSgdQWQhX429uCpmgnw+DZ4NkCUo3R+aHbHYB3seOrWGr1tMbsHPUd/Dv04jAWmKF+Mu3lJtE+VsxlilS1fXOJ+Zz9BrTPmkAeton\ 5256 5CFU4w4cpb7ZRtvwyxcY2/jyQAzUp6QE270ypjl1hgm3R57hBbNnrxg3JulYph53ETNdehyTLTuPqyQUm9PIit+z34TNcvwdNA9/Rp37GB0w30BsyFzWJ5uwh8dizKHdAeRIxUwEKJGa6Ubc9Wlkd2b9PJku1yad\ 5257 jYKjAfbFLqhinPWQLafE7YW5CMG5jBeStENCmnvw6q7nfTDX9L2PWB52Xgc8xuBGsEYsU4Mwwoyyuhcc0p5TGsQ6jl/S+MJCpedCUFaXTZ8iG3ZgLsRFvPz24RNzL2KAy/HrLjFXC78rEqpJ1LkIKE2DJHCM7+T2\ 5258 4SNed+YJRjf1kXQ7suuXuzoRcU07qt1mhkPb5BwV4TsY9C15HzB6EUe4QxmnRnivhXeTRs7jcBt+sKiSef0tPjZzj6Ut7CrqEYPfYsY5IzAOs7dvYPCjcL0It8++ZKvKHrx+QXajSY/sDZxhhz0Mp/1Aj6UtyAUy\ 5259 6fwx/QbbEDagBVJqdQT/jrfeAKh2CwHZOLKju89BlH2A/bRLMgBCBK09uumZPOB3QTTyBCAk63iLJrcuF6BnvugScUCBC1BK8NckI9D/I+aeFgI2fbFcAKByxi+lvBahOC06OQODOX0PWvx0H8Z8g+GF4lvAzyJc\ 5260 7zhKkgsmhqctZW6EbFKAKwBRvQr3pBENf5ETTVFtsUy2Mc+9pl4D/t6T2Y6wpKewV3oAzCmMppIWkqN2wi2Y8Gtyl6riqMvZAXpN/DfaoEbdEicItvw30czqI4i+En+A8oFaCzP+dyerdDIL9VGYEtccs8wgpyMI\ 5261 XgCsx+SNUBAvSIXXTTDmeKSvY8WW1bH9rNtLupnyZrL668xLVzQYb1ckSZDYsEeQQxPVBOBVzLsNpVZI7qolS8hyt97ZZwSyfkCfhx8UYKBAdQzSpRaGAEowNwjkp2tCD7RXEnqFYok8GMw693ygiq22ljtoPxUV\ 5262 l88UZOOrYjfqJ7p0ORXhpwl3JVq+dcCw1imA0Lx1mwjUz/oIuCAI99q/JRfO1Mke0aRE5++Yq2VIfa2fbN7dmne51zyWRUMA8ap1Pw+pdqWp8uIDyDz0CdNhuu1qjFjEiBWM6P8WI7wSipFig0OdU8YB8lDEXpgJ\ 5263 2MlV5KyJagcTKWcuM9kwDf9x7MCv1+xKwboZBX3q1+pj1yprNGwdggI0WNwUCk09+gIvgDghfVhBLNCZ3IgJs7dqcUx52fWO+CQCU1kikLb8hbVhurREIGdItVkAlKNr1c5ZwdatYskSNTb6IhLABBGaRUSTfdWZ\ 5264 QBRrC0nBNNV7zuDFJIw0xBXwSxU06hRyLmlr7uWN2H5UFyeyAAHdwRVwnoXQGbD4aD6O0ncT6l/04oCftPvz/373C/3fEMDI3OpclK2jco8nEAiXCLy7z7hlve5QE/7TKWROp7Y0Bf1b/AxfD4VeL2i3ICNNjhkR\ 5265 hkVh6YUVEy/cGV+FoND2Zra+hryeIDslC6C8n8a5dP9VoKEhJAazV5BuLdVTkJrqFQceNA78itiZWs0Tgggs2Kc3glZ7FnYsChRtK9mObmMV4djpz9dA7tcvTn/GkBDweT6nlCd6lYiGDTYW0A1JVy4E02NJ+LMn\ 5266 e9EZmAxkL+apNpydH3QSJXoFEn+H4zAGJ5/Bs1ik4ZjWtgDbqVseZkx6y1uECa2N1gnW/IRDkrmEQ5SZ7UPQpICdWiQQJqO0izoTJoHwKhr41Ocd9SFWytMFAP0S6PVCwpcXc3/D/Na+lVt0L1Ci7BImdIFhWxCE\ 5267 OnwNr+rXXfzT9HJXMyeKInajmskcw6bhK3qnlVjdLgVdo6v5chGT9RSovVKBss2b/XUgRkM0fWnCIrzxapW1cC/u05rlBcyJrlYdeGEbEh8T1I4hzFbt/dXN4PUTK5pqrzoBNP2K6QjEQZNcS5jYMULSU7QPMaC4\ 5268 iwpoXUE+WkUlG59OxN/3RDxGAsMHyIes8ygz5RmoSv8w5D7ifeRzt8nm/bwu2+aOJuYamtQi2VEyLtymy1F6oE0vBcnFO/xp85N0N1pu6CKAUtTxCt5qR1fr2wehGqj2Fpc9TQ52HaEzJBhb9Rqhb3+fUpS6TCDk\ 5269 COoc2WbCCkOJUkTOf+xFCSiOqQ98yYYyQOn9lVsfI1KwgBLXe8pVPuBHxHtkULdwb+EKhabsImCsJ7Yoau8MB0eigRkDIQliCqwWunwoBVQfDgIhkLKhwcBz2EZWjIgVjSVWbL3T3ch+9oTZqJVkTqqxHE4YM6p5\ 5270 2XGwJuc0joSxCdk6Dj+b3sKoVEzlO5S8HcFvI5ajlZdnBe8UKg9hkBxraw6+oDAPZniaL/a2RxyS4HlG5Dzn4OJbDvnVYyknKqGAx/BebrLtmyEbX+WMJHilpg/YB4XpzEsIJhiW2kW5vY50uYngnWMsdGbziKJ9\ 5271 +x9j6+wknTL25OJNnPAnIS9Yh2hDGHK4NPj5Vl1wyrrggH7uaUfTZcIZH5E1s9CcBRgdt7d4HzTTKDh7u3f8fRcmgNnMZHLn7IIxrd6hQnwHzbMzPQvVAt/H2MlbjjOxJaMNFwJBEYjVgK/0jCDPuXgF8lxaLyja\ 5272 4FJPTizMwlvwdjR72aXF2tc3SZxgAjgLaFflAW0irH6ztJks788CSt5tTpBZHGMqcCyYYUAsl69imKj8QBEpCmrVwWiPDEqEsWTnJ3UxJCQNz67SbiuXLfj5e6IHPVMMWsqVO4Z+sGolMO8IGPWLi69hXCucsGFG\ 5273 gmAyCzl1UZjWg6WpAQmlCZC+wTsA5T0KgDA/+039togE/u13YMiAtCmhW44Ozdcw3Az4DrgpR4v7YGZvLsLPSJSjycTR0oJTbS1cuxtbXpoz58yy8dQkRuhdhgo6dl7UDdhhI45rZ5FouRscKxXc19g93fsAze01\ 5274 OKKRK/ZlyDeLMXwYaA4N4vRqBCIgizHVBkZIa9itd/l+HfftP5gM4UwOKIgCRGnK92Sse5r7FH4G76lF85ba3kB7ysGUi6wqfiFZgrjIbpZo770iuxdUzFJQ6yMsIS0lKNmv/fCqs4g8K6iOxIHUxb3r9a3QCtUf\ 5275 4sKLj1e2S59QfI7j9KxwXcyObCEsrXLTV7Fki1ZAsRa4wEQsoYbwXz2626+I7qeI49ine9KjuyW65xrGNOl8UHHfauAfWVABvVPeproQ3yFF+kIJmcYS2zQ42VQo42102ifwE0JSDkzrCNG37JzdF4GrSemMk02I\ 5276 8I/D2wDHAqORLe2/I6ikJGkxk+xd2tPxWw1FSSFeDZ7+hKx+5836QT3PB6IkBghV4uYMg3kZlPfW+jmhtgu7C5kiu8GWOw9cfyqrQmlVNv4IPl3t3S/Cjesc/HccxWpxs6H52FhOPnjZZfFYIzX0fo4mWtG49AWQ\ 5277 Czw/dBd9n9D0qLndhKxLjCPs9i0xrhrKxqhSElfwsftoZ5W3U+eCoFM89IUfUf+hI/yathzZVkuVzCVHNLFkvDVXIP3bohH+xK8rccVjrn1AN2LCZVuTvsbtscrzOVUMSmCg9QZh/BDYHEVYs8LeHQ8X0BqFYmdT\ 5278 MYTWBGF8+z6f0Oq/4DvGmkIaTbHF50GKaMTE0uT6NsUF2apVxokDKpfSBWgUJ8ik8sqiKVbvsaFSPE3l+RoqhSeSGROB52krk33GfM+db/BhOgy1FRhJBhnV00nFCp1E2QDWSUeeToKJ17tSseFJnhXqiVVS7lkk\ 5279 f6R6+phgru5FOquPCir9j9QTOsXFUD0t1Rua4u6lKuomD/sxummPA+NXkh7tbaiiUkx9quQKRlpKncggLH14kciok4qOsLpY61GXNAsbnTjUhE3kJkGBwaePc47XY3AXiQsNqMZz4rgf8A+ctupy9g2b08Sk0852\ 5280 6vvwUixEnnSP6uE/EFVejLShwCi717p4MaeDH31zEVgqpwB4Z0dI7nktuIxdmJAdtcR17aiFroVatiCJZEojjarMK6YpiRq6WL/MTFC5ug1puwkzhrHJNGFbBQt/0FyI2ThNOAxQefrmElq8A9S+51IorLiYAGR2\ 5281 toSZAwR/Pbh8I/lWmMfOZ8vYIYae+djB4aeBnj4S7MQedjAlKq5ttiSFRq0UwpMHxSpL6g2nxdlubyqJnHu4FsRMPcQojv5igB2RGc+HB+Cg1Fnpm5Cql7LuBkAzaUOpkV7tCOILw2vbECbCeA2iBhhVv4646npD\ 5282 iqOL1kR5XbB/wGVDWAEwBsNEU5h98WlxNV1KqdnfO0HqCVEvtlbxJQErMyOtmbZxfX7EUqSzwL5+SDPz3/GDmaeDYGbWN5K6tHW9pEjZMkCfDyRCz+kryenbS1aLVmBe1Vzh8ilfvc566tWIei2HXt9qzw8SHcWf\ 5283 4vZ9ClMgkNnNS7XrgDH+V9pV/bna1XLdQscCp30WuML7M33vr69dDZY5F3+264fWcyG7R28nBqNMdkNz/TNlgje211i4bKEY+YlT/hMstJh/SpWF5mQJ1r9Nurjc9QZZdZksyT9KluQT9sZY9PjixHavFZQi7STK\ 5284 NnkaP/UQyOWOiMPCbr3lg1aW/OCzf5H0aAHbkVCuRceMxQrpdnU7WtjRPRIGcrYKTaE9PlIEpoFFzOFBF8wSOyERklRo7M5z8JjL7A2mg6GYAwqWG/vhZIGudAnLzX68LtQrxX/WmUOErFGLkkW4c7qNDENJzsae\ 5285 EXLWPDoAdSwe3yEJb0es/VTx8zHzU+UXg/JOpPcL5gnWr/Ic6xyVU+fxnQgfxHdAq2ZcLKhFaLl+ikaBnKkaC4ya9O3rF37VikRj31OA2jivZeGd2ZKgYi1RP9gIqDomJJjcCaGJuI5YxRJ4acWPk6Y7bHXx0ZVu\ 5286 e1xdkTAQr/kfW33zHlbjKm9aPpsPyrUgn+bXLLRstP6ObjLCiD/XPaD3jlFzgIS+PMGzfjb18tXu7JZkLsEZb3341hkPqMpQu5ADMMPOZXU/n0CEPOmXkfyvk4dS+IFO03kfW67sgy8F+Oiw1g5WE8A+s3kxHwLP\ 5287 MS2sLIy7ysIW/r1TznqgJt2SDJ0UvY7oAZXymLkkef2xw29QEq3lRVfdZ1y8T3pheG+D9qsefwkHGcqFfx7nEvYxzD5myD6gOCH6asXmBT6iLxAnuhBmkvoHSlJyYGssR+4kqUixOdYPwGeQMKgxlBGOmCwxn1SO\ 5288 f4AvmF0E5nY56yIcc2E3EBJ7gfeAX8Zcvz0hLYCpmjHXYcs+xvOBNQaCLQ1SGaypPqSFyQlrLIFOn0gGjT/jD4z+8gIBwSz6jWF1mG+Gi1yloBZW3cZdiTg8AyRAfhL2GcJUeyIvod/kg669pVOvrk96xW/jK37L\ 5289 rvht0v8NYKu5bYroNqziQQ6ona5BXA1YuWCU5+q054jFvgaDV7vBtnOSpyp+AHnZRv8FUIAnKWatqbCCqegAlYKyCcAVnZ7YlVMtv1L+ULuk9PQdHwBr+W8PIt6W+EcOXUDsYrLLZa1YSyQl8tny5QmYyQaJqgzP\ 5290 bimmAVSt4o7JqvJYxDIzU83puJpr0DA9mMxXXNgigpbdBSWpR1TuWAduj8LtrbUCaqgrOksFX17wF+hY8bH2xo7WzMniLaGnfRFq6kv7/G8nizPeF+60dEn7p1F4gqWw60FwfixG9YxCO0bR+RYu/4y928LGQjgY\ 5291 b7ztjp4vEPfbBBPWwOYsWwwcU4Nxilh23S0t5wlgJegaMjkwe1qwZ9V4Z0HLyehb72yUUscgQ2sYG5VnPH/GVRBy1KqLpMvDHIbAcv3W7NjtCgJU9phFH25NPhOAMtwuH1zX9fJzkz3gh0YAwqjAocDkHdZA7A/e\ 5292 L/0LrsCtbaycsqHCjvdzPoBiLx+kd2onWXbjSpB/DQDYqJ0AnCfwikpGQsnV342dQxl6gZstkVMjZpx453u72fjQnEFHZIQR+w8/vKETJ1u7HYsrjrE0eR/m2q/6iGnPlLGERm6wK+dhvxkvD+ImyTpni8x62L/5\ 5293 w2/N7iM+fkOnhXLvOJjliwbQ1M9loxg+JOaO7jFceIJCQQwQlU1+r5tdl/iT69iF+QGeXpZVQoOlAFDRHolIBpRcuYjEjsP9/h0WZRziLRUh3lIR4i0V4X0S31r7988MLyzpqlBV74K8U/++mNOQs1q9a4VI9nkX\ 5294 QjA/xyfn8CznI5IoNRszuFihZHMDZS4W5ZYkIyrN2gdqPvsXLbm7iHp3IbRu9QJvOCr9O6zQFpRwFBfJ0sshx6IbvqenA55ZoDQhX1SpvR268jYfpx3kmgiHt9jHaLyEXpjbQ6p/IEyp9eN9BpTuctoTluzfPyFH\ 5295 ebvHj3qXVOD9HMfnSwhzBVVULqKipSWZpYtgpt0dPU4zpv6aPa5Aypf6DME/11uPhQ9I5KOZYQ67NWmJ3xjoyrly+LsAZxmzjOXWjoo6T6QFnZVSQi5zBRxE93/BkTnhNjzniIis5MqQul5GZB//x79w12qyzGtB\ 5296 sH48Qzt+R8vphSlVnPHhLJ11pRoiwSq4ZS9PzOMAqZM/Njvba6Md5MpzySliXQ2eugeLAg07qFys+gBHlcixvPc8LnoXV3krYd5XHuWQ7X0uX8Xdj+XuHfkR5TBszVIfMGviyUUDHm2+d3L+Fgj9rBPgGFdJWEZz\ 5297 cFGzPIb14ewlFZhANBspWe6tOqF0yAmCBljESpgm3dpBFqm47IePtwpT5TF7XBrjds297qa3jg3cDQsgQkwZi2GQjk5O8NWHd1lhN5AJKaFkSZePIORp3gIa8QLhZ+xUNt3NX8FKQdH4pdDPRoRHb/ecy4mydIZB\ 5298 FvV479ENJwCg73g0xpx5+kW0vRbs7I0ORUFFtVww8fcVCtFoUVzjYI206VVCjW58nA6Ac2eRufhby8UaVeEp9GZ4LhUJO19xRaC7HaSQQ7lyE1522ThckNtpgt4ar5PUS4KKRdO5lgzZbVCkPes86gwxcZzlohGR\ 5299 YSArcpFcYFSgHMYv8dXbDD4vfWV71r+pDWp2sUQQaVHitS7T1HuGbmcu9195JFjat7BJcc+6ndrdfSW7Nuo8iFKSlGJiDa8s0Z0zblwwRRwXTKo0fK9TRYdCFrAD9W63DRu1chs25Uw6e/Ao8SIGcFRyCxF/p8jx\ 5300 MR/grEI6w97gdSM8FNYkGxlzsuKiqFp3JytUInfFjAUTu5112RdUyKUlEUJuJWEr7JMERsen3eVL53y3QmH/C9b/0WerH/zGud+48Bvv+6xoBpcI5sO2f8mbKe+s0B/In0xD0h6Vb4U3zKnAnOdvmWN9Jm3J4Gwf\ 5301 lO47nPmwCRo/ex1PVT697YF32Yo6qmQ3v6GKutW8aCVUIWf1sP6ugSsns1d+UesjOkyz8E7eL10Ll4gBgvyzvdfBPP98cOME3emn+XacppsU32GbFC2Jje7GDoIGYqFxYIX37rHxahu4yRIPpdfobPMFbdasuO+t\ 5302 MnzfJ073kuYCv7iQ5Fs6e8O2XOVdnLEUzdgjAuDdNWLr2QO+UxP3khYg693uwiY8kdkcCkUQaF5IpXYZ8tyuoJbYRDVfptSu+6VkXxIBgFL5aK7BmoAzsfyxebojPEaZuu5mHUHnXBYiJ7bU7MmPIV/G0AzESrHq\ 5303 sia6FsXKK9HJAvclGGmo+SA64E6lc/oD7+zJ/iJlbNphEqBJo/0uQVTzvRyEV3e960QOTGz8ZRT+h3eHuhf0AP6/FTJlzCV3TfjrkjQ8lMLgQ5GgO11o03J4tR8PkXuP4NhA0+DlB7a/oyAwZb6+66WfxcrNPK/N\ 5304 yxJ+uniFiFHsLiBCo/4BZ0AAe6UXeFCZd623HE5RUKZVxzSYnBqcbER8WctSWkRuRiuAw8c4FR4ZiUa8sXoRGWcs4T1oY/QOEBrsjTdC9UW7DI/nfWNZSdF7rTPR3KvYe6gkimwNa7PgwpgmwzsFEjgCU4xHTw+8\ 5305 i+zS7hCdIGaCxzAVM5AuInfyRGkw6IuDYw6n1JNMYmmRD4Ch4hd8Msm8EEq8yoyjz8Gj4y6Myb3aRWwB/LEPP8ROLlsCBWEo8tgCO2M4m2Ous2v8DjJLWT6/DpCl66mxzCDD4c+DRP7nBcoULr0T+9jtfpa7pr//\ 5306 5dwuzr3/OyU1/H+n+L8kk1ilxnz4f3giyVw=\ 5307 """))) 5308 ESP32ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5309 eNqVWm1z2zYS/iuyEtmRL+kAFEUCvutEdh3ZTtKp3TaKk1PvSoJkk7uMx3Z0Y8VN/vth3wiQUtO7D7JJEFzsLnaffQF/31vV69XewaDcW66V8T+1XDfp0+Vau+gGLtqbIl2u69LfVDAtPMkO4XLHXxf+1yzXTg1g\ 5310 BKgm/lljO8OP/J90MFgt19YvVSf+NvO/aVhNKXhrSm8Z7f9nHQqeFaDt2TGGuC9gTHmStQriqHLYAAt+NPdTgUYKdIBT3SFoaZqu/KiKpDYDFr0xsaiec3i/6jHlmfEcwEyjHi5O6SnOLP6Xmf3V4afVoN2JQW9P\ 5311 8GeEoxrU5US8kkgqR9oIC7OkyFUZKdj2OLTJO7oII6jqxadNUTzFz340AWmGajCgrdkmjlIz4rcWZv08vy+2CKzUVaQ412fL9gTqcrV9TdJ0f0xpetsptmggID+ckA565o3cmCPg+QFZbQFaN0EQV5AlG5gF2gfF\ 5312 4i5M/KC3woLvjRn65Vl/6Et+UFtgFf5MlFrVwXBwmQnrCN/UpPCmmTEJDfT9q5pVJ2p0QHfCY6zOAq6b/kba5RXtQKX/T607sGYUwLCN2cm39IpXBAvp8NERCnBg7DDsllXRLigz4yvT2ziTxvezmVyd0jC+Y9OW\ 5313 lDhGqWWLBowRoM0yZ0n8ptWyaRHS2Oi6BRfLopvYL8pkHGECb5LsbGemBYhhbLL8A0TS3hGdJcYdj7UvueSS3/Bc2jLGwOSs76LRAuhKReBGFhOt4nUOezvnyWmQvJ5GhsLmjevHRuAQxMroVaSHdjwnmFHqMxGA\ 5314 J9oTqPUcbSXa1p4BBr3a5aol0x2/6r61IqariFFEJTTLSDmsyC5InD8BT2ZY8n9K0E1zmZ5PHLk0bKma+Hd1+van8+XykEIJvV1zRES/PPYKy3gHMDY9ZJ+fkrOCVuvJJupBMNOw5xXhRFmR2KZ18m5ca+3RuIMh\ 5315 3bp0/PMjoHIwHMO/RykQcMrGQGy6EQQd6JoicFM8PX2IioC5Q1JJESJLJchREXCaCJwDa9/CziAOJAQ2tWyFJuMskmD9AnwYkjRpqdaR9yXBBsWtegG0wKtBPJ68F6/cYWVFWIdcq20KfQCYuyUBgFAg4UAJogAZ\ 5316 I96SEBsk4SlMAPncoYTQJE4BcESPDXKYggUM9yfqb4ccI5LxpT2Ng8oThGRQaCEbPe1z+ZiYaEMrKIHnKt6whF0TptW0C/C8Klkl5RaVyBzH5j7p0sZ3haZhOvlX6FQ8J92csxm1SZIDSQ2T8AwtiO91OWSoQgGY\ 5317 myb9o1RKri/jGw9QFeL/DBDkG/YBQLF2GDJikNffZDs7xAPESM22IFE6lsnr7Cpe/yIktOje+vw7xx6fRrukw7StHt+4/fBWyc64wc8GZjwfziZgD4sJpa3oFLzrZfQ2QHRRdPOwDh9RPMA6oQzv4EbkZLVkLhmx\ 5318 F5SANuz+eMODuTgWxZV/Zi4f4s18F99cxzer+GYd34BSf2M8rFTrTLDeO3arnSJkyHG2rIvmjOTUCHRl0CT6c/p4efUGCB01PCXKKoJIF6EeQZklMS5eQcSavvZ7ZNjqM9mIitbF+dvMr93AG3/RsgtM3l8gS/Pd\ 5319 aCZu6ewj6V0zaEttRWZ2vRZ7zbv26vRXIxTw53momqgimf6IEfD+BoW4jbx7KvZ/DSnaIAQcjJ1oDKPgIsKOxjRv8CvE3wFxVW5o+X54f5wT7LqaE12kc77aLrfJQdIJ4V8dFwYOfLtI5h+YjIr1C08eBd1WG/Fm\ 5320 wYlZTclTIVkm7u8HIlOWNUUktM+cc1QwjOzZcvWW8tcieSFA+Irr20nw6aLhNXLKNJxpngELP+7CEqAJqLiT16QSyEHAuy3q+FdQ4IAUi3jAwtgNTKgDUODr2TZwqEPeg1EeDfyxlBul+7qPY0rsnv5wenhGfLY9\ 5321 CFA2pAyqnFEGhSTgRoUmBtYT6dNebdcrBHG3XLfe0GrWqVz7aStyRUjc3niF7UUU0qh7IomHsNCRRJmISBnqoc/veOmaMepSss/Zh32MVSbhkKU92tCVc3T1kv5BKjplMoAtlsRZU6xTZNs+tl222PeS4jxgH/k3\ 5322 lr9V3brw1bBAWOPcR2Ck7m8kvIXgoymc4uttWDiCmKpeDPOEbXxK79IiL8glnB54j69ydn16LjVHvSMXPn5UGPb2H/S7OmVb0u6wl9RsIPh7exawTFAqSHBIKNM0P5KBA+dxH8tb8fkwZKBjTv31l+2YLDVKR0Uu\ 5323 2+iPJbRVZUGu5NIZ1MsQQF1CySHGUehQQFcOkYJdLq7g4+VBaSXLV2nuObloEMHIkHqgJih0DHdxZOpwT+57yOBd708gCqZ/kZS2FeuUsgKdrTrSXnKy4LgGCnqcHB2CzEfcG9Q4aQ8H9e3FcjW+2KWCH0OAy++I\ 5324 gq7ZxpBBeXtyyxfYpToGGtfHgwYvTkcdJtOzi3k3c9Hu4dHFkjsHVRIiGUR8MlLCSrBgh1v89XVvyHF8mLsGdDghlNZJNwFCapMQHWAcfKyIxnEObuA8CrLqtqM0dEKb0w85ySg+KHVnF81vklxQJoDGkt0NGp7s\ 5325 OM+g8RPQbIMol8x/Yx1lGPCaExz0yyDcNK8DXYdIOH/YWhT2lGKWYJUiJ5ZAmc1dGHdEjyoO4l96EBnFioDMjZCchyYPrFd21psLDHL6ZmWje1yp7JxY2m+YHLL0i0xfru5sABPIC2tOjbrqPo7YbY2K1u0uV7cW\ 5326 dxIP/95WFejj0uzByirpK3IWtBhNUzRF53+PBrUMvifmVNqNcu3ESV+i11FVDxPS/oQFdw9QH7di8HMybV+2nvWeOWEwDWaDqacaX0igwYwwGXALL5WmXgIhgm5rDp3hgrMhhqzYW1V5XEumuODStJK8uP3BNuUL\ 5327 CK5GbyuJMM2dsAFX/fRil1oFqFekxH1j3MSjEcoPfyfXgGWD0Ksr0wUZbVOPyDM8WhxThoI9toIoSfe5Tbuh9WD05UbWeQDS3XIoheKo4MTFo/peqCbaVLztEy6opdXU82AfBf8abJtebCy2T0W50X/lukbd8Q7r\ 5328 A07vaXkwU4jgLuox9EQ53SqK3RRlwXqCrc+uGQbJtGQbGPuMi42THThnqCt439CgAF4zF9FSmh+1tDLpOI1IhAIvkprMgVOetum5jUysewsPfPwY8QFEnQxEiuSELIJe+cLLmlcQtkfBviAkFEpa17dinevIni14\ 5329 Rfr49BlFeS1xveOPuFwhy6EA62lY8HpFWSIAeQ1B19acCODBWEXd4lqPqyNMCD49JLqAGGbyBTWS/TtqnRt6S/bEe+oq3sMMm6ZXYPnrN0B8dERxpYHE1nL/PI6fVt3DSYRkMAJVkO6aflupoHoIoALrFkNteRzH\ 5330 ExB1sk+dFazTICBNJcZzVx3maj2Di4E8yiIbo+jPpp22U3JODLCTo44HrTAMeoBWFk8fsBT9ibNSTIQjdWHqnm4qbEWIak0q42ZBTVc8FUowMq1/Bl5fxhkPzYkdROddgHbZKXuRgZ03/4H09hUs8AysZMpmaWRv\ 5331 6y5wetJXrYHOj2K03T2WFZ5KrTTqtcXVJkEM5E1UrhWgj16MWbCD1zPwgENK4ez0++XVZ+poo2XUkWXgcZalQhO8D/YD0oSK2+BYw/VYsVyYQt/G6fWYe/wWCwBuXBfcuHDJzvJ2lwxB1VEvu06eYBvrM1wDfmqE\ 5332 uiLudxcW2ie6PCYDcBz0yzylSkG1xwzNcYAsa7b5u5o/6GxCwCdH6RbUQoyv1rxaj/hgELSIJUz9Ce6+BEyTTwmgssC6pQ6WBGmyKlsXpt6CSmacRmC78hYurmkZ3c8lXLZ4E87tsAqp5REp2m/HybfPZzSm09gG\ 5333 MHp6VuW8omlPmu4pfbXm/Ys7XPmKWYOYBQw4mFK4k5xudTamxBtPdkvqtmE5n92HQkHnDUUgnR9yXKjXDB34uHXOcZQsFIdEtqmvCXfMZNzyTcq4lu8eEmwQ1VTr4+biCYgjw6Q5Uzm3GDHkQtoDdlfatsqS+NRg\ 5334 OTU7QYcn45l/wgS7Col5ju62G0VDPCiq6Vqc0ZvB3h2XajjtI4wDZYRalD96rrIDLBY8MSnWBdOqBLn6QW5TvP0456qs7iWviEHgptgKuxwFRzbS6qp7xb4R/mFSxclEVBuKb89WkBkIpFNlp2YnmMAd8zEJKuqc\ 5335 W44MXDbZRAuMWRM+KG3Z3QQ37LRGMlaYaAAfeJHGOYgZjQjpMOUuXXizlPg1OYpLU+g5cvwqywFNw+47M4gHT6b+V2jiYOJc/0mhC3l1FXs5HhqpYz5chdQPcmFQquZGJdiBlUO0+PzHoo1jAfyznL7ttEdae3yU\ 5336 kol3I+juEdCCFwFQF9OXW85f6WUQ3aUi+pSSAAMO0d8MnT3vnbSBH9vXfKiZP+dGsiWhjMlnp2AtZ5xiWjGyZKvy5IuOazzdPIqYSbi9ICCNKcYgsnj9GijfvAGtX+I3RvbmHFPvT8V6V2Dljk8UsDtwQIxiKcje\ 5337 UWCYq78Px9NO35wTz4Uc1+ofWDEV8V7yqVQjFZahUz/Mq2XMSW3S0egvgGel2YW1bkj6SocoWrBxOjz935VzXx6ggFrA4bvhJjlko5i5lZEXFtxRL20bgdXqOlB27p5gBI82uWKWl7R8JiJnL/kWrU3fslUy3yTH\ 5338 J/k+AcJ/U60tgUxhPE+20echd62yMZ8PYJjNCACLKZ8R6ZR9CS0uP3s1j76NAROxbRXzgTQMXMOA5bYgNEcCBXN2OX8fcMFyryhMsGdv5o1MOB0tUmI1HP6kW7BMDwRAv9l8arTuD87/0a4AVp5xHyEs8vjPAHN3\ 5339 S26polZG9pq10Tk3bL/kk4XawuFR+FoL8xv8iCoJHzYY1qqkSgo/AJp8pq5pyB93abrKd4e0FaZX426cp6vxA1kXfXUohBtoWNGaw/Zcfi98mEWs4ewnfHi17cy+ks/0RLSs8+owsNLV1d7jAX4u+s+Pq+IWPhrV\ 5340 Kk+niVdi6p/UV6vbT+2gnurMD1bFqoi+LuWzjT1+EhOaZGo6TdMv/wXshTKs\ 5341 """))) 5342 ESP32S2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5343 eNqVW3t33TQS/yo3TvNsu0i2ry13YfMALild6AvSwuacxZbtcvb05CRpIEkp+9lX87LG9i2wf9zGlqXRSDP6zUv9bee6u73eebRods5ujQs/A7+fzm6tVy/0wC+1v39223cHoU9sLo7gz0b4UIdff3brzQJagGQa\ 5344 vvXVqHk3/JMvwmOVh1+YqktDSxF+Sz0bDFzSQGfD32JEJLAC5AMF54j7GtrMdSBn1HKapAcuQmsZugKNHOgAs3ZEsKJutg2tAw/fLQ4PvjOHB7TCwC2MaSeMBAbCrA6ezL3TE/qKPeu/0nM8I/wehFnhL/9RPyeM\ 5345 dLAzXlbSECXjaeFxPl4UMtOovawmjFXpz/QQW3BXT+/mKwgUP4TWFBaRGJAjSGG+CvgdEr+dMBv6BRFUdWSla9V++Slb1WRBY67Wz0kbPG0zlkcb1l8gID/skC9EzhvCijsGhjdJO2vYchdX4WvSWAe9YOthV1EE\ 5346 WWgM2lbzu3NJmJs3D49NaLQV8An/ZMZcd1FZcJqMNwhHWtrtvj9kEhboh6GW90320APdjNt4L2t47qdSrM7Oaftb+39uuQcNxgU4VrAq+4yGhI3gRXr8dIwLeOSqJIqqMkoEOAhe2gM5FUqIuX4/PJSnEyKPYwBA\ 5347 mJocjMaKlBYMB7ChXcaLCXLrRG4KVCr1POBIxat3+lw06Z6CApaTCHfUswI0YY2r+AfgY8NB9BWtwnPbMMinr3lE4LJqNNylj6dHVE2AR6mO3Mhksqv4XIJ4V9w5jyvvlkpXWMNxfq0HHrGrUUORHqryimDGmA9E\ 5348 AL7YQKCzK1QXJdaJDsZ9rc6uBzLj9vPxqGtiulWMIiqhZqrN4Y0cg8RPnwAUHIq2oA50RCiajQSmtE4OagJioX6EsAtSfJc9pBFTHEL9w/MHep2nOGVYSk5GJ2gzCEiaUQesQkuN7gpqUjvGHyIAirGpFlNOFwMd\ 5349 6If7WrCpVezq70gjX0PDMa4prY/fV1Pb/QDxBbAv5TFmThOssbHqxCvY0v1ke3sjvkczPDlvlUA34rYhMWbUO9qtmj2FRtlmp0zjyHFgW+pGfsHhfTVbFx0UkEddqy1MmXq6y42eNEpLNkxxPrbH61ZNu7OrJ96K\ 5350 MrDF1yQYPMeVzLoXJYuLt2iN+OTIEef54aBXCYOL4xG4rYOZIlvmMgJHB+DoPR07VB6wTHWdDOeC1tu7LmqsK+LhHilfS+1O2an5NgznNSUWvB5ZIhReMS/hS9uwGRWTKu1+RZs1chbUICaFyowEqgmBdis2jgVE\ 5351 HD4STzYdHy95t00y+EI8V5+vJxVn9YIIxXzi2slJqHN5MgO0dHw6cM1Be2rGaJDwaPuy+6Ah4mFPXHE5+6gP4de0pFEfwxtot2ncOTnj4qj/dVwJzwVZkrZdgbIaA3qbbUdHyzMzDW+WSavIXdfNKVZOfXciiAr1\ 5352 BxnsaUaDHWZBR/r52E+aYr/3l+qkHuEEr/NnmSeQgDWaLJgmm//48tnZ2RGfDMOA4lvx/L4ggdghyrnHXuWS4AU0wi/nTjUswua8MRlLKx3D0kgC4u44/yhhhcv3vtsFKo+SPfizmwMBb6pBVS8oaOtr9OJUnHIo\ 5353 zlvDKljzU1FH09agWBTuWXTcFsBJtmBW6ymr36ACeNItsBONWH5LJ0lsmHa1Eb5tOLU+U55eGv0deZ7EaIhzYOgnnkcjaNpGvV0HW7W5R0A48w1AgfvpUQcyhq1QJZqMyzqBDrAofyRhWqqjS2yxew59JYin62Q/\ 5354 M58e8QlJ915XJwMMPxSzbGsR9XJ9GBWj+9f65e0LFrLzVyxg56XN+JfkxWxskDAguLDmY/JEzT/X0z6PBpUw5tnnnhU5H1sK6bZWkXu/H0cJWM34mR2Fr5NDwBNzmlGMj94wy6RRowEcwNbr6HXEhzKxmEhp4hiM\ 5355 RQyJgjB14gOB8xlW4ddJ5a2WxM/65UK/XOuXW/3CLhSi00Y9SQk0chShwRyjih6zJvKqnWviNqA48h9g3ccImXDIkMhzFcCCo847bevvwSVfvuJThKbSKS+g4P5mvUmhY3YZhUIsvX/+A0esxfY0Fjp8R9bTMlKI\ 5356 BSKluLgV7SrH2uXLP4RJ8MeBDauyLssXCMPvL9l8Dgm0LYlUgoyacsETASK1RQz4W55RuGFAhIRbwYDYzDzj98n7L0o69b7jvUM6z67XL9uVsNCMTI6gFnmBdbp6yySyiMkS09aDG6tSBpGR09BYU4AErnXtle0v\ 5357 WGubhkmF1VYla2PJ0TgAVPElHJUfYZdg6BNxmL4nzRCNol39QFoAXcHu+WUPo92LbVg07Am4L+kr2hwwo3Aoa1SssKcN7CnqcM0RMgPETOhgY/toLCof4aA2YzjAvgIHSxKJqAceDhHgzCK0B09Pjh6DJzhENY5N\ 5358 Qnp48ulgSMmghDYVpbifyNpjpgS/xNStOzw8mOS4JgkxRvqdIcqAaadJR8fhixuZh8NRmm9OHvMzJlcvNld5ZIkHhbO4dlkajmlUuuiMQBtna40KpWsdV7ccbr8WHxjk1OXCZX7Bzcb8IgbM0RElu2Yei+/sloMX\ 5359 /XfpOuwdRPDgHnAHovr2Uoh7eXL5Hj7d/ip9zY2Mz38deKFd/HmgXnAEBhGt4CEc1KQ+O1eg7FTyJurthgCxpTBt8OlxFKwvfZKUKTkF6I8NUPCEY0a7y19QoTvpcnQDotjAf624n/v3Bi8JaW/Qmeg7jJw78kjJ\ 5360 S92Hs7zBkUZHa0TPhHuhTYAKw+AxdPOovPKfklGN8fMckgiJmkpBNP8qNt2+/+NMCfSp+jkLSHcZfW3iOayOU4q+/kj2hI0LgN+Upv4+Nz4ATv2sgpI9S6J7ujd4E8qhQixuBsOC3vOPbEzadZOgnF4Q1EGDnu3B\ 5361 2fmaCYEWuNjTBeEcbo1i1na2kJS0FLx5cJd8Bi6QNZJ1kzQcBAZouzgSBQ3AadbIHjRf4qHWbvOUqrGxG8x0yss1Wp9qv4Z1GhucGwvI3u1/Ac7Q8v6Ks3HTGoTlmKMbFpsKMfLRinfs1+r0HPVMToTI2zXKAnuu\ 5362 PLeqX+8vzdsvCez6ntO0qkxXMl1WPgfCtWZUHCuZbPWR6WbiBoGlC6X1k1G6HBhTmnDsUb7liME8xn7cegetyRFoTcLqj0mr3TuYEj+0kN55TgGX9eUNjAfl8l/h4yZGW1syOrviByptQaHlIln0WAPa3Rzr7B7r\ 5363 vsr1IBtuqgqg0WnCPACk1nnEJBAj4S3pPn5Po49BbFGOijjb6EVA5FJhLQgk0CygEidpfhhsU51CCkpUdeNsVtdtSeNWnBecrbYVyzzaWfAvbzhDltJaPa1zp/weAgiTrrgMBoewuFr0qutQ+imOMc2SYH8sD6z2\ 5364 MbVzzBRqNNeARIbzae1SIsZtZQg0M2DgGsfMgEvZX8UPbcGpI9zIdMUJc1MsuF1T8crWNNMpxPLCMxRyBocIVYp60moCG4akajURU7wfmLS8q/14V58zU80w5I2a1OpJz3ZGpEmj6FOkuXqucv+De3jFKc10NPc/\ 5365 I3O59DCjPVjowXY0eFNqAbnuk0VGoIJipT0fse5V2r1MxFln6Y+r448n3UAHAV8wVQb645EYRNjlHTvS/obQCOKkTqqDAE5w5Jxy2ys7fx5yi258XE1zQ36/w1N5yulQjGVnJehEsjoQLblPyLp5yUjAcca6EnoD\ 5366 CJr7a8Kspv4bY4pGDggyZQ140qq4mTaFYe4h2ekOE59baxDc8cfic830NgePCn1BIA0qYQLyTq/g3+wCztYi1hub/JQ9AAQZNJ0XCX3DmmHNYZ2LMQCG2JDmwgTyzMycPoK9veIgF3IeuAGG694lH4AiEqPC5+ku\ 5367 cLGK0UjNv345zlVPpmpUogjUBo6BN6qc1Q0s3DiOMYo4y5A9V6u6XjtRNV9T2Lya/URf/DI2J0rvRTwMTpXVvR6SDTUKelGgKBXIGRCsDHq5HfMyTiFJg+XFTOqM4NBXjarpuJmCb+sET5QIFm073kykJQgzjPVc\ 5368 j2gqNZzNa+NewtHY3KKy/xBal5LpRBI7UMxY/f5HPHlGYGGCOUMx/xlDvvgVXYAs8rPYJR4RMBC0Lp8yCAwlxDco4ft7GGnzOaox2s5W57I7X6tRmAKh0rlwUtzQOSL5/AfZ+AHcrk0CKDBboJYNFyf8pFxWwXxV\ 5369 FvPL3awq955rd+yoAp5agDg8tLC2poQV9Kk4SkvlmuR8RMgjAbgdPhURY4JLdas/lQwZmFa/4Jy7W7cEG7dmWIorTsUBxYtZw16xZUHd/P3smqvoFSUhKr4uoE6SuyHEIRk/lSsE6Ibvvl59M9hbKigwHyLKaHxg\ 5370 9NdyCeAdeNngNIGmePc56EyGOsNRxiy6/J1LHOcX+yQKP4ZjmXL7RCY7lABycxwVSYQ9Cp4LujKkEzu1QLtClRuG53T1SGWA8iPChCoLXH4gfAwbfSUMVNHCgOxaDr89l9qqds4QhAzQj4zP7h4Xryq5bFJzSI75\ 5371 QyxKb+O6r1RZBjM3fnEfHRUMHMGy17pyI/ewUJdBL1ARDOdDfc4KgzF9f8I+hMYYtTereyM5aFTZwDjxNwY49/J2U24PbLyHxruYKxBsgwAFPTP7q7pfUsqqxgdbtM1xjrjh0wEHoWXkW0jla4QaDnACTlffSXuF\ 5372 ESAUtMp/fEu0MKGZay2o6i8fDkW3frids//u9s2T5A6DZeClX/wXBuxTbruBzBLURSxXLwHzSQR3ePsRIHRISOKaeua6vsSUzy7nwItE458bUtUYjF5iPlBIy2HEBDsWtAfGTRkvgv2LE/HhdQuft/6NjtsLcFiu\ 5373 YqgjoGP4lpMtD8DE22X/1ciL799E41vikrZLFUhwYoTDjBgl7nAKHAkhyN+ksbweZDzq8Bl71ZJNGFm2Fjvb8qm85/T+jh01hJjbDOChXn6Bi90n5Kkts+fst+sSOAfUWMnD+AqoG4J7dPaWDNZ8O3FUgaXo9IKO\ 5374 iks5cEXLar9Zk95IKc+D98js8bwDhmGt3gvYhk0yR15senRbaO2/xHCiruLgGGUnOviX4HoBDws+hw2dQ/TQID3itlAHObuK8XP3x7kEWNGn0xVd0np7Lxkv3Dw0jAnfGsAApOA9LenOjkslNX80vkolKBwckh25\ 5375 TMC4jMpiuCSC9g7NoegboG/Xbi3WXCqgkbAfPpX9yOh8O/twLiS6l2TEeOfVK2R9PwPoLV25wHzKHuWUdtbbKleKnK7WpUIm/kYiHF1xDlVdtDUEzqrW7jiFXfN9rI4qXFf1GV4gx0JwCkYwd9tCp6SCE+VC1pi7\ 5376 Ds2dbB8ZN44gwbHr+FpVrdrkckbFl1YoJrxHUYcEh626a2DtGjOKud0GGAUJVjZKvJbThjcpoYNEMY3cmsebAO4o4g8Us5ohvV4ZSoeCN9JUo5v2irb378mFbrnM16phlu/wGDtc8Ei56leFB/C5l08mmoqbh9NB\ 5377 GQa8KIR1EF+vAAbmbotdiY4zuiQF6R60vIPxKPe+n+fmnH9MpBt0Ul+JtimL4+FKgy++m36qiN4LNFebYD/AX+iWWawVYi2w5RpNyiowLWl383YDORiNbRRuWA6FqnRLXa2KQMkl2AkxfJaCBlRYx4Q/dvOskmL4\ 5378 ED/sxmvtqKt42zxVTknBVtgMF09gxAdaO37tOP7EJEK5nfCaij+5kmf2NmVePImJEO7BTaM5xfh5RDLWUmINe/M9XT3XMEcr/5lBllaMhiaRlfFe7TxY4P+f+fe76/oK/heNNWVWmWVR5OFLd359dTc0lstlGhrb\ 5379 +rqe/Hebvj3Y4S8jQkWaGpP//j8geRE3\ 5380 """))) 5381 ESP32S3BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5382 eNqNW3t31Eay/yozY4wfmF21ZkbqJrnBDslgks1dQ4gxrM9ZWi0pJIf1MWayY3thP/tVvdSlh7n5w/aM1I+q6nr8qqr9n511db3eeTQpds6vzeL82ibn18n8bfMrUV98eHB+HYrHzdc4JjviUeb8ui6an7r5Hp41\ 5383 A5MJvXGu+WubBxlNhGftDEsz2sHe0mB8mdNLD8+TdfOQX1YJ/U2SaTMi6y2Bo9LzhpUKxjYLmbIZksSdk3Y+/DScmaC+xFHwJn4hTqeRTd4Rt2ve1b7zeBe4nTQfXcOzazao0uYJyGCp9ziKHADP1XJMIIvIeyuM\ 5384 SjNRzGpk+rpqZGZhvQWsA8SazoKOhnUl8hJfvX2p+WxohpnlULhrOnub3Ds9prc40v+ZkcOTOJi0Yp8MDgA4FHJQMkG4ajWNdavdlRl0Sh1Rl1yPPJe+ow/xCUr49GZUnT41T1NgZZbAmcKJjGpVckj0VkJsM645\ 5385 DufVEZdKaqFPlusx1KWqv6fjv2SVluQd/GMRvLK2hf5+eCifjmEvnuMW7Woi9aI10gnrnW/Owcybt80b17BWeVJplHKmWMt61uuYG6uFXqR7StvmPJ3F3hnpQG1tZBt+QMtNc8rBMRf8rJ0U0jOe0VDpCm1X6bP+\ 5386 +asN8Jx8pEY2E6ni5xyOa8WDF5FzsWFvyAXhc0/MR8rQPAo1Fddr9MHAmqDDSfKJFoA3xoMvW8EkrXBa8S+0XN35ul2m+/yiO2tNRJeKUFT5AG+UcFiQXQ08YJEtD9ftJ3JNZolfMhIPjg4/ksECKzpcRE/WLJfk\ 5387 5CXNnbNsf9blyyfRE+DQZn4JRC9pHbA4kIR1R/ALFNdNHb1Dh2FgY3YntoR4cMA7e5ia/eZ/e87nXPd37wzsak4c9Gb+ZnbyHSlolVGQLXKmeuCPvyS0aj4kAMNXxj+hE8EGovFd0RDHriOkZoh3z0FIKK4pCQlG\ 5388 l31hhcAkWn7bJY1YBuYj26G8i+1mlQBbg3tlNwpOJ53wwZcjnPcWKDjWtgvw1Ko/dQZmYEQobgaugrYgaU2IaTt/SDN6B6TXzv/k2uHPra1+0HhPyC+G6jEZqauRveNj/EPug5+9PO5wjn6IVdGH39soeBSJBoEX\ 5389 KaOM+V0MsXYvUMPW3Sc4YzkyQ6+46L9fqYHLGJ1jeASa8PRBynP+wWUnHFFT5amr4f4AghLTRZtDu4zCrxMBt0X7Cca5Ssl0GiM2rifYihBnC14LhYfGHV00SttBZCcc8HuHmEYk4b1SPX5u091oP6B4EStVMarC\ 5390 bONl9x/u8qZCyqRHSsVywpV+oMN1KsJZieQ+ZYkYNGaOL1XeIrbGOiyEQzfjEGx5RjBiM3zYjg4elQAgBLgbCE6wha8PQRqz1prI8p3djlwN4XbfqYypDkhPQJrJ9pWa2jEU1jqDlCgNYh0px/T0/IpJbt6UBR8P\ 5391 q3z7PKxIpppWqybxUshPpYhpFyirL1P4SPKPNG4Ak+W7KWYtauW96sVdzPqW2e0I9fRQb8WK/II+vWz91GH7iXApLAR65hnzgC50JDh/aGgUWlvvUMXToOaAPytJ9+7yZa3lVsy/UUDffsGLjUTeim2qLJGuC318\ 5392 wOH8Plv6nAMfaviD1H/ZeTkbWSD7QqyJeoRU1mzKOKCfAPv0STeZGITZsWBjeFvJJuo6/uCAheRnjnFEA+/etZ9IMVrHN9viJAkElIITAJ0tyuiKCs8O0DJEAYsQYACnjkCgAiPY4pyOD8kwWPdMtk32wP4nE85O\ 5393 CsFo8JkNorBb7IfwJShewQAwB1R8wN4DlL54AEGAAyMq1t/Eax7yG86MOsCsuAOYpSvio5U0Lqdn2vHwYJqZSqMQpTNrAV/zs0qlG6afvSLeR3RWkMr/6VQTEwCkQxCLE8TSnNmEziLgHk/gy9b+7t5WVLZKSh5i\ 5394 2I2ClEje2eJkzijdgXiLM0CGb34+OT8/4sCNLG8LcnjXwEif0Yxk8e7er+yO0206pG6WfQ2rNZ8C2n7yChSk+VVk/+IjTRjLqCNIhknFyZq1DN3DyW/Aa7NxMWeplWTVBfsp9Fecu6Dpg6WEDIoFRTq5bAbkkxeN\ 5395 BBgqOUTUj37Z5qS1Pjk7UkhduTjQ+uBA3OI1HKNx1G8+fHRekAqXjHvgs+TQGDvhdI35HQh5S4JoxILuDggF/wRKKhY6TLsSUp8BxlKe+C6ZoPotRjBiW9gLj2YcTxZ7L3fhAB/N9uDP7gIWCknKS3dd2SWpWO0f\ 5396 M65W9SOscDSaVZIOXrefSBsrsu26VsUhg2Y9AarmknQMIONPaJNsf6DkhWTMhhUmjQcn7oqyTFJbn6siSRptVz73KmgIfoC3XtJeCMQqY4gay059co/QUT8K4DHXYymNFSVVTtalkFwgX+FIymeprv3hE7Nn2zzM\ 5397 z/bnydei1unemTtuIcpDdEuoX3Lyy/FMKFZgz/QXmPRv4PyQvCHkxQgoAj8+3OCfbDqlIwHfY5K7TpWVuXXTz3Ux6wIN9El4xFCVUx8CGi2axDgCCq+8UuhlIS1yN1+whjrs9zcZsjCEDT/MDtGKT7cIxXmFugs1\ 5398 GyzaS+hxI3SomhQGsELmbJOo4fQIcQ24w7MMYwf5Xh/eO/3lUn+51l/W3QOHzMZJdWDqY15lfP1aVT+WhTB7AhY3ecFoCbEBqvYHCUVPuQyD0Pixymy5BFMm4/iRbPVDtDh0k/aW7CF2PB5i7PjISYGBzLu+XHcU\ 5399 KVeINUdvshaVAgfvM5XKaOQu+NiA0/yCNrnwRmWbKYbKz/TN6eqjofnCDPAAWZpvs7jGQK5k/4pCRsMMCHDC5JiKcjgBX0GsgPlk/wqtpIz9azEA6Lez219yOqjAZWUKmlf3iTLD/pbw+uUfDELynBwK2HEVeg4M\ 5400 EePqPQOmJLp7J6mmZK8oz4EbPW1meQqAgPvkUNDVZ++JpqKoovMHwA7lBqiU19nTaLfBaoURLm4pPrdFaVNsVuIlPpGZupwATkhAaxNL4shFgTckMihXeFbBuoZAD5KGzdHkmQg3wKoVxTGJRW4Z/YZPtN+oIqZG\ 5401 e3KxEWPgpV+OdyqCf/z346NnAOkoHfwOSMkhFcTAHd5yqEvzwxLSQnjVomH7Foif8jiUZQIDTOxPvpRnsQL61h4yJOgkMr2sZ/BsgevuqBo3VWN7rSnLBRfbCVaHwwXj5iMvsfEibVH8Yhaq/1gnXeJFelE2OKfQ\ 5402 faDa/oRO0lI5cLdNsKEM2Kbe7x/xM8xjDX65pGwSyouG34KxFPTsgNMh+uRk+uL8nD9iV6RQdUle9rrNBIhV/vKO9AwfA2CoqdwEy0GAbrfn5wcvOdxX4R8UBHgD0IsQG4IXM5gcvo2mr7PkTu84ZIwAQ67iNc4C\ 5403 Q0x/nOXgwmxF/LbAGEtcBjFkHt2dt1xdLcH9lFP5YATj799rURwuD5V1OFssq6eQ6lYtNjJUTcimK07bwCpLrlpwvbHNZjgOwMl2MbrysIX6bCSD55p0LB24CCbDGJg0X8EWL1TybR4PEwPcizVXiMUl/R2FYnyf\ 5404 DxfqTB6ECvDExaDpPj+ZRbS8F5GKsSp6WRfTRuxgpib6LiluQZJVqOeqwhJUxsP0FW6MPiNq832XSHgFQK0ap3YdBelCbKQGP4nkSdfV1yMbUy78gvNXM9icCuwjm1tOHgfJnqEYMjAilw6OICWgCBEFVC3MARia\ 5405 RDoh0hqBJMtzcT9hpInbjFTawEotH3bJpRfcVT0v1OeqTd3bmrOh8DhgwOJSz/8LPO9/B+Bx+WDF1Yakj93/0amipLIMgdXsIzk4Lz2nsh05O+61o8q+ZX2OQMyZcdg5fP7hViCC7VxIgfqZ4UqOxcQ56dwB4azc\ 5406 FuMb2cFB66K5Hcka9K0Xll3KfjzQfoq6Rcyg+ekNPJ0d3ZuxBeAJ794AF7OjbYAFzwlMmZBvYCYoVHi6oUWMgD1TywLzK/5AtzbAmC9nk/pbkMTuVldb91hVVDpjwmyGO1ZRSNgrLcT731NeBBwx7UrAnjaevhH5\ 5407 AzjkaiMIuJhAWUjuEMBkY3RJncKJXXS9UQUpX/twSdAR18+kCHilpYcFjx0CjliExNJQsklf179KysNNYLyusdmqZaRn1QfkelEjFOLhVhpI2YNOrfspz8QMOKlf8ddK/GR2XxdA8bBa0ijsoittqIMDqjfxOfal\ 5408 E74f0Sy6JQRMesX2yGcd9ym6IiiW3H0ED0fn/K0mxGZ7RMVfOUgOyLRVJJO9UNpdoWZ6inbWf2KFwUT96q/sanmlaa5FgBrYcsmk7O3sW9IWMiLpjpjq2ab77p40Zhd60FzRAmmjkRcLPTvJrlVbM5/Fbg0oDVQh\ 5409 1I2vZ71hoPCwEAZo1Bpsg0HunN+waYaNOCC/XMt1pHSyjZ+3r+NvhYpsa6oQWjZgVX/AkFPuC5Wr73sCTcwppDdLtlmse0vTOaQUbCSRM2NN5/L84vRWqja57M+TUswHtz9GqOlrXe0/PZ63o3iPsfYVwEYzX70S\ 5410 2u9zEqmcKgg9oNCvt3axlJlP4uWlYnHK930q8jQO4+3pLYe2jCJxp3MOo0uoYoejDWWAhgoXF2MNjXwY37C8NmdZQyW2ThpvVWJVQHU+MdMaKWfglR7OwG0W+7NtGiXhJOOkAspKdGvr9DXVw7QJtTeekCcYttyQ\ 5411 8zKVsBVUr8DrdpMhV9rnsH+/1UlFpcJaKG+ay4MNz1D3GLDbUQ0Zd4rxUt0/QDFW5zsQ+pf7YI+xB4mI2Q1PLbvz1LAyUfZvaeC5jJ1U0k+uRgivfFSpQPbCTqNMVpsRjxFabU6JXTfXox4ykshImKj7WI2AYlH2\ 5412 W8eS+ZUsZEu5cLglCJjbihhr6xhK483gsbUwCNp4wgXnZNSlXFxJ1ikMpaoyLNNZSQr7M/iWrW40xyNRV8M8ufB5I/q8/hJlIfskHoKbr47zl7r6/8gK5L9355GqyS6sVEk34ANXjUp9deVXVIwHe4gQ2AI9YCo7\ 5413 X11GGblMTSSvuNbEZBvScTqr90jJaygMbZE3R83ktqvjq576toWDLUFTpN5X1e3dx1u+O8RIHWKMgVhgca0Ew/QOBHHBTksF4fDG50aQ22aiXmXx0kCDLK/1q5xdLTZoLrl7Y8doN1EmLQ8hOxPDgoykbIXE1z9Q\ 5414 9z6fY7rxmu5ZOr6wqezIbugaDencCymBYhKyewZi/ruqWgXuUDEtcpQxKMP+3ASv7UeIBwAvQVMCtO8LvKUDx4XpQdn3Bp+5Z3ZxuU/HEbLve8kVbnn/WDb7VvL/rW5qaEdcZEKwJlblvMRC5Vc2XPFIV1+r8t3i\ 5415 iCC9mzckfqKOQshiX8zj1SIJyQ4/YOHFs1KY2yE9DiN02obq3T0qZMB5hPaWFF+iwZo4jgNbxuPSTfoyPcDs6wGCOcyiAfn4Sb9oE1ivQUfA7xupiRd8OdRhVK2PlW9N1SX6KKnVvc6RaAeDcp6escezP19vyQW2\ 5416 6St4eMMcBJVfsN8N5meaJkpW2hahtwYuimcZPpVsLKAyBfpBO59IV7XjPSz4C/MTbCHPHcIW6JTm3/wvOQ8s6iy0Tjj//cO2oRu9xv7H619/nN1g2QBoqSf/hQn71OgooCoICAQv5Fm6/kOncIO3prO1QifIE7fo\ 5417 jf/wDdC4yw2RbKb9oJVSBa6Vf3Bc7nPKLLHZQneihO4kp7v5219zR4bv6X8lqeJjRr2AF3ylkkRxRJJemfybDVC5oHwOThBzF0kYMQEi0KkSF7gSgScrKVknuVn9wVnJxTClWn0TwaEv1eFRZmPyF51sw+T/ZtzM\ 5418 kiGjv8KITuG8mjKvdIuZsgfwkaixZm+8rgV0ISpTn2O7XW5Xti03xNBL9uWWPEGn209J/iWZjk05/8egO0aC54tGlVWpxchdC5dHtkvkF5tsHO0jsiFI8XtMwNw8Toy1ipmukEiJAu6rFBM2y4LMErMYqL3Y6jgm\ 5419 L1g2q75cb2F+bJ+fD4zgg5QEUXoYOGd4NYird5YTDSg72FR6LnJLKO3flmQ/XS0fyZ3UKXtu+5SvEDmSGtUfEg5yYMNVIZcTqu7/7HgaajAHFMnMiav27tAA/P7A5UG61OJeISP7c/DMeZONYQVqj6twSToW1C7l\ 5420 zGayHzQ2THI1VmvqAZUrLpeL/3GrTrVkyv9HUEoDuODbX+kN15zxwmLijux9WUE6jYhZXBsMt+V/dijoyUWzkrvgRqJbQuqd8MFiLldKVn1AHXXJlEt9pSUMMy2HCUwBtHm+bm1lEy/GJi1PGOBgJMgnJDGceXsU\ 5421 L416T9PI/4IxQ6sM4w6jKNKu2FlX+4TwOzlXvKdLZZd2PiIlL3eKWgjhl3lHcClm3HCjLeFJ6OVBrWvlXmC7MtvlnjQeIiSF2Y2OI/neLyv9TxXPeEHErYdKrSTqhPAEbPp/OuFz7xd49pUM3906Jchgq23ua3Oz\ 5422 2HPiaCUdKocellLRvoovtRuXBARsD10lZIzGqCKocpF4j6O3XpWodDr7S3/tu64yO3HrbUbBPRL6By+KjiGk3fJKkqvEFEHd/BN1HeJFovs8fHl/xm3dTFU69N3uBG5O0n6weZjJgjXHWNpOomHAEOriTTQc/lBd\ 5423 HlzccZG8lH9kFN6yzhKzSFNXWDsHE/wn4X9+XPsr+Fdhk+T5PHdJnjZvqov11Y08tGZp4GHp157/p1j1qnf4jV4oSXOXZfbz/wH6hAMl\ 5424 """))) 5425 ESP32S3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5426 eNqNW3t31MaS/yrjMTa2gb1qzYzUTbLBDsngJHv3AiGGsD5nabWkkBzWx5jJjs2F+9lX9eouPczmD8OM1I/qev6qquefdzfN9ebuw1l19/zaLM+vbXZ+nS3edP9k6osP986vQ/Wo+5rGFCc8ypxft1X313bfww/d\ 5427 wGxGb5zr/rfdg4ImwrM4w9KMONhbGowvS3rp4Xm26R7yyyaj/7NspxtRDJbAUfl5d5QGxnYLmbobkqWdszgf/rqTmaC+pFHwJn2hk+6kY/KOuF33rvW9xwdw2ln30XVndt0GTd49AR6s9B4n6QRw5mY1xZBlOntk\ 5428 RqMPUc1bPPR10/HMwnpLWAeINb0FHQ3rc+QFvnrzQp+zoxlm1mPmbkj2NrtzdkpvcaT/KyPHkrg/i2yfjQQAJxRykDNBThU1jXUr7soHdEodUZfcgDyXv6UP6Qly+OxmUp0+dU9zOMo8A5mCRCa1KjsmehshthvX\ 5429 icN5JeJacS0MyXKDA/WpGu7p+H+ySkv8Dv6RMF5Z21J/Pz6WT6ewF89xy7iacL2KRjpjvfOdHMyie9u9cd3RGk8qjVwu1NGKgfU6Po3VTK/yQ6VtC57ObO+NdKC2Nh0b/kDLTSfl4PgU/CxOCvkrntFR6SptV/kP\ 5430 Q/mrDVBOPlEjmwlX8XMJ4lrz4GU6udiwN+SC8LmnwyfK0DwqNRXX6/TBwJqgw1n2iRaAN8aDL1vDJK1wWvEvNF/d+SYu039+0Z+1IaJrRSiqfIA3ijnMyKEGNlbc0DGxJX6p0puCmIRzwk9ktnAgHTSSP7vf/VOS\ 5431 rzS3zrLDWZcvHid/gEO7+TWQvqJ1wO6AH9adwD+gvm7H0Tt0GwY2Zqdia4gK93lnD1OL3/3vz1ja7XD33sC+/qRBrxev50+/IzVtCgq1VclUj7zyl5jWLMYEYBAr+C/04tiINb7PGjqx6zGpG+LdM2ASsmuHmASj\ 5432 6yGzQmASLb/tk0ZHhsOnY4f6tmPD+wPlScHv5DOWej1x7P7sc1Yemc4Tm+HEOdiBEX64OfgK2oAYNaPz2sUDmjGQjV67/Itrh7+2tvpj660kSjTg2O0+G5hl74df3FIeg6/ix73Yyerowx8xHp4k6m0AZ8x4Y3Hb\ 5433 yVjDl6hlm/4TnLGamKFXXA7fr9XAVYrTKVACTagEwO4F/+GyM46tufLZzXh/gEOZ6ePOsW0mKbSZwNyKPx3sK27upKiNKwm+ItQZAWylMNG0m0smaQeoDKN6MZZgngCF90oB+bnND/hhIPVLkKlJwRVmGy8E/Hib\ 5434 OxVqTgekNMwkXOlHkqxTgc5KQPc5M8WAI5Ew05QRuHU2YiEqujlHYsszghHLYUk7kjpqACAJ8DcQo2AL3x4DN+bRpiiIOruXTjVG3UPHMqU3wD3BaqY4Ujpqp8BYdAk5URrENHIO7fn5FZPcvakrFg/re3we1sRT\ 5435 TatVk3gpPE+jiIkL1M2XKXwoaUieNoDJ8t1U8wheea92edthfTzsXkJ8eqi3Ykx+SZ+OyZiiu6ozti7PiAdUoMe4xQNDu6CdDWQp3gUVBnxYTSp3m/+KNtvwsY2C+fYLnmsi4jZsSnWNdF1oqYHaLfbZxhcc8FCx\ 5436 7+X+yw7L2XQEMitEmqg+SGXLFowDhumvzx/3U4lhdLFTkcbwtpJLtG36wwHLmJ0FcvyUQWyUa4geb77LGRLwJ4cxoKlVnRxQ5dnzWUYmYAeCB0DoYPPga2y+ywkdy8gwUvdMtc0OwepnM05NKoFmK0Kg8Lmyu+x9\ 5437 8CWEzopxXwmQ+D77DFD16h74fY6FqFf/Ib7ymN9wWtTDY9UteCxf0zkio3E5PdNOxwXTzVQKhRCdjxbwNT9rVK5hhqkrgn0EZRVp/F/OM0nIGEfYszpBK53MZiSLgHs8hi+7RweHu0nXOoh3oay6U48aiXu1fLpg\ 5438 aO6AudUrGPv656fn5yccqfHAewIV3nbY0Rc0I1u+vfMbu+B8j0TUT7CvYbXuU0DDz16CenT/VMX/sEAzBi9KANk4k3i6YR1D3/D0dzhpt3G1YJ7VZNIVOyl0VpywoN2DmYQC6gRVPrvsBpSz5x0HGBs5hNEPf9nj\ 5439 fLV9+upEwXPl30DngwNmi8twDMFRu1n06LkA8NUMdOCzpM8YL0G2xvwBhLwhRnRsQV8HhIJzAhUV+xznWhkpzwhUKTd8G09Q+ZYToDDW9MLDOceQ5eGLAxDgw/khJgFLWChkOS/d92OXVFBr/SNG1Kp0dBxh8wV/\ 5440 7WKK1seGbLttVWXIoFnPgK6FpBsjlPh3XIbtrwnkyCz7a1SZPIlO3BUll6S4vlQVkjzZrnwelM8Q8sDpBhl7JcCqThFqKin12R3CRMMggIJup9IZK2qqnKzLAQbiucKJ1M5yXfjDJ+bQIt+h4OnnR4vsa1Hs/PCV\ 5441 O43A5AG6JdQwkf1qOgtK5ddX+gtkeVSzPZ6xYN/GJ5v4qdjZIYmA8zHZbUJlbY5e+pkuZF2ghT4ODxmfcrJDMCNCSAwjoPHKLYVB3hHhuvmCObThaLjJ+Ahj0PDj/BjN+GyXoJtXULtSs8GkvUQeN0GHqkchEyuZ\ 5442 s0dxB4RHeGt0OhRlmJLjOy27t/rLpf5yrb9serV3lYTs+JRMGd/+qgoeq0pO+hSsbfacgRLiAlTr9xKInnDlBcHwI5XIctWlzqahI9np+2Rt6CTtR7KF1Op4gJHjA6cBBhLt9nLT06JSgdUSPclG9Ancuy9U8qKx\ 5443 ukBjAy7zC6rkwmuVYuYYKD/TN6fLjobmy2HgDJCX+Zi3ddZxJfs3FDC6wwADZ0yOaShrE+AVxAT4nOxboYdUsG+tRtj84/zjLyUJKnA9mULm1T5RZtjXElS//JMBSFmSMwEjbsLAeSFaXL9jsJQlV+8kuZR8Ffk5\ 5444 cqFn3SxP4Q8wnwgF3XzxjmiqqiY5fsDqUF2AEnlbPElGG6xWGDnFR4rOsRptqu1aXMQnslFXErwJGWhtZokdpSjwlljmGhKZQ6uGMA+chs3R3pkIN8KpDcUwiUNulZyGz7TTaBKeRntyqQNj4KVfTbcogn/0j9OT\ 5445 HwDQUQL4HZBSQqEKw3Z4w2EuL49rKGrBq4iE7RsgfofHIS8zGGBSY/KFPEtFzzf2mN1GL4cZJDyjZ0tc925sTUkBdtCTslxisb1AdTxeMG0+8RI7LtIPxS9mqfxdm/WJF+4l3uCcSjeAkCAqfl6r0mDFFcN6P8ZH\ 5446 IaoRiPQ0QqUsFZ8gBqB1HN8hrqBkDmTuMg7cMKeWx5hJSX7YqlTRxg28VRvYJQ95F4c6ySoPVDOhTpMKynSwEhdSH/BiDtAufJsMX6fHvZZxKBj7hVKFapwFZpj/NC/BgdmG8tAIirGkZRA9lsnZecul1BqcT70j\ 5447 H4zg+6M7Eb/h8lBKB8liHT2HJLeJqMhQGaHYWXPCBjZZc7mCS4wxk+Eo4NohPlf+tVKfDYP6jCvRqWbgEowMUzDSfAVbPFdpt3k0TgpwL9ZbIRaX9LdUhfF9OV6oN3kUKMAPV6Ne++LpPOHkwwRSjFWxy7qUMmLj\ 5448 MjfJc0kxCxKsSj1XpZWgsh2mr3JT9BlRm+/7RMIrwGjNNLWbxEgXUv80+FkiT5qtvp3YmPLg55y7mtHmVE2f2Nxy4jhK9AxFkJERuXwkgpwwIsQTULWwAExoMul/SEME0ivPlfyMQSZuM1FiAyu1LOyaiy64q3pe\ 5449 qc9NTNtjjdlQcBwdwOJSz/4FZz76DqDj6t6aKw3ZELb/V69+kssyhFOLD+TJvXSa6jhyfjpoQtVDy/qcYJgz06Bz/Pz9RwEItncPBSpnhms4FpPmrHf1gzNyW01vZEeC1kVyO5Ew6MsuzLucciyUbNmjbplyZ356\ 5450 A0/nJ3fmbAEo4YMbOMX8ZA9AwTOCUiaUW5gJChWebGkRI1DPtLLA4oo/0GUNMObL+az9FjhxsNvX1kNWFZXJmDCf445NYhI2Ryvx/neUFwFHTLsSrKeNd14L/wEacp0RGFzNoCQkVwdgsjG6hE7hxC773qiBbC8+\ 5451 XBFwxPULKf9dae51D/Hykyu5/IhloWyb/9r+JgkPd33xlsZ2t5WRnlUfcOtFiyGfh1tpGBX3ekXuJzwTk9+sfclfG/GTxb4ufaKwImkUdtGVdtSBgNpteo6N6IyvRXSL7goBs0GVPZ2zTftUfRZUK241EiYCOX+r\ 5452 CbHFIVHxNw6SIzJtk8hkL5T3V2iZnirO+mcqLpikX8OVXSuvNM2tMFDDWi6W1IOdfSRtKSOy/ogdPdv0392RLuxSD1ooWiBpNPJiqWdnxbXqZJbz1KYBpWnK3kWvHwbDQOFhIQzQqDXY9oLMubxh0wxbcUB+tZFb\ 5453 SPlsDz/vXad/FSqy0VQhtGzBqv6EIWfcEKrX3w8YmpkzQMkrtlmseEuHOeQUbCSNM1Md5vr84uyjFGxK2Z8n5ZgN7n1IUNO3us5/drqIo3iPqb4VwEazWL8U2vc5hVROFZgekOnXuwdYxCxn6c5StTzjaz4NeRqH\ 5454 8fbsI4e2giJxr00Oo2uoYIeTLeV/hsoWF1OtjHIc37CytmBe1xAass5b1VgTUJ1Oyh4meihFyr9tkfqxMYmScFJQ6ooVJbqsdfYrlcK0CcWLTngmGLbakvMyjRwrqD6B140mQ650eMLhtVYn9ZQGq6C8aSkPtjxD\ 5455 XVrATkczPrhTB6/VlQNkY3N+F0L/6gjsMTUfETG7sdSKW6WGdYl6eCUD5TIlqWyYXE0Q3vikUoHshZ1Gna23Ex4jRG3O6bhuoUc9YCRREDNR97EWAaWi4veeJfMrWcjWcs9wVxAwNxQx1rYplKYLwVNrYRC0ScIV\ 5456 52TUn1xeSdYpB8pVUVims5JU9mfwLbv9aI4iUXfBPLnwRcf6sv0SZaH4JB6C266O85e2+f/ICuS/DxaJqtkBrNRIH+A914xqfVvlN1SMe4eIENgCPWAqu1hfJh65Qk0kr7jRxBRb0nGS1Tuk5FcoC+2SN0fN5Iar\ 5457 4xue+naFgy1BU6Ta17TxyuNHvijESB1ijIFYYHGtDMP0XQjigp1WCsLhRc+tILftTL0q0m2BDlle61clu1pszVxy38ZO0W4ST+IZQvFKDAsykjoyia97oO59Psd0o2OSAf/N9zSVHdktXZshnXsuBVBMQg5eAZv/\ 5458 oWpWgXtTTIuIMgVl2J/b3639APEA4CVoSoDGfYW3ckBcmB7UQ2/wmbtlF5dHJI5QfD9IrnDL/VPZ7FvJ/3f7qaGdcJEZwZpUk/MSC5Vf2XLFI19/rYp3yxOC9G7RkfiJ+gmhSB0xj1eJJCQ7/ICFF89KYT6O6XEY\ 5459 ofMYqg8OqZAB8gjxVhRfmsGKOI4DW0Zx6fZ8nd/H7OsegjnMogH5+NmwaBNYr0FHwO8bqYhXfBvUYVRtT5VvzdXd+cSp9Z2eSLSDQT7vvGKPZ3++3pU7azsv4eENnyCo/IL9bjA/0zRRstpGhB4NXBTPMnyq2VhA\ 5460 ZSr0g3Yxk35qz3tY8Bfm77CFPHcIW6BHWn7zn+Q8sKiz1Drh/PcPYis3eY2jD9e//TS/wbIB0NLO/gUTjqjNUUFVEBAIXsCzdO+HpHCDl6WLjUIneCZuzxv//hug8YDbIcVc+0ErpQpcq3zvuNznlFliq4UuQwnd\ 5461 WUlX8ve+5n4MX8//SlLFR4x6AS/4RiWJ4ogkvTLlN1ugckn5HEgQcxdJGDEBItCpEhe4DoGSlZSsl9ys/+Ss5GKcUq2/SeDQ10p4lNmY8nkv2zDl/zJuZs6Q0V9hRKdw3uzwWenaMmUP4CNRY83hdF0L6EJUpj6n\ 5462 RrvcpowNN8TQK/blljxBr89PSf4lmY7NOf/HoDtFgucrRo1VqcXEPQtXpmPXeF5ssXG0T8iGIMUfKQFzizQx1SrmukIiJQq4q1LN2CwrMkvMYqD2YpvTlLxg2az5cr2Fz2OH53nPCD5ISRC5h4FzjteCuHpnOdGA\ 5463 soPNpeMiN4Ty4e1I9tPN6qHcQd1hz22f8PUhR1yj+kPGQQ5suKnkWkLT/6mOp6EGc0DhzIJOFe8NjcDvj1wepAst7iUe5GgBnrnssjGsQB1yFS7Lp4LapchsLvtBY8NkV1O1pgFQueJyufgft+5VS3b4hwO1tH8r\ 5464 vveV33DNGW8qZu7E7ssK0mdEzOJiMNyTn+pQ0JMrZjX3wI1EN/mFCAsWc7lasur71E+XTLnWl1nCONNymMBgN8vzDWsrm3gxNml4wgAHI4E/IUvhzNuTdFvUe5pG/heM2QeOO4yiSLtSX13tE8If5FzxXi6VXeJ8\ 5465 REpebhNFCOFXZY9xOWbccJst40no5UGtW+VeYLu6OOCONAoRksLiRseR8vCXtTIL+BEmLoi49ViplUSdEB6DTf97L3we/gLPvpLhB7tnBBlss8ddbW4Ve04craRD9djDUio6VPGVduOSgIDtoauEjNEYVQRVLhJv\ 5466 cQzWa9QvJWzxb8O1b7u67MStx4yCeyT0uy6KjiHk/fJKVqrEFEHd4hN1HdIdon0evtqfc1O3UJUOfZc7gzuTtB9sHuayYMsxlraTaBgwhLp0Bw2HP1AXB5e3XByv5feLcrait8Q80dRn1t37M/xt8H9/2Pgr+IWw\ 5467 ycpyUbqszLs3zcXm6kYeWrMy8LD2G88/JVad6rv8Ri+U5aUrCvv5/wDWE/uZ\ 5468 """))) 5469 ESP32C3ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5470 eNqVWmt7EzcW/itpEkjhaXcley4aaINNbZwLsNCHkg1rtsxoZlJomy3BLGG3+e+r91xGYwcb9kNiW9LoHJ3re47mv3uL5nKxd2er2hvPL63dnl/65Gb4N5hfmix8hr8qz8OP5BXmZ/PL2oXhKk9krMon4be7N5+P\ 5471 5peFnV+6ij+b8FQ1GG2H6WIwCbsbTIZ9nQnfXVjthvPLkvYOf2HQDt/PL9s0/Gh5ZREm6xQ0+Glr5Tv+zKsjEA7fsE0RnqBRMF8+Buc/4ixyjAYH8/I0kQust/VpfsTMX3rzEAwswmhyb34R2A8MtYNRNdkJu7td\ 5472 ZqAaHAaOBpP7h9vhQZOW8UROTrQ3P197mqswGog3gWFbhC9tmPGB86rNw3Ng6s/jsK5mIeLZts3zNRNKesxyUW3VdU7H5P3AfCYCG3SSg/zDdlDkEJ8/XAkv7oR0BuankY4Jn65Y0uAq0eLelZBmCqMeD0op/rb+\ 5473 B0ifdw/0rm9NdkiWVQTlFEPeN6gzKKjwx0pZLLUMD/v2ZW5ZnHbISubl1h0EInWwXGcnwQTqjG2TxJBMMAftMZUqYRUZ8wh003tgdtIXnOX9C9CnsbBlHo7RZLyALCwTKw9/RcG2pOO8icriCasAJPFZ5Tc7kd0n\ 5474 pYxn4k14LJ1hKY5TsnNYzzYOIq6RfdwGv7PD3lE8ZDkO/20wqiblUSeu60CswP72K3gALMmwLWBZW5zsY1HPpdOZZydU6briAUaCeGsZaesTVjykAXnbXLa0kbfKwP+8lc2GbGrgr22w6UydpmcY4RGcIxOPM2Jz\ 5475 Ni7RE7c2Uoc+aqJGjBGpAYec1l/fAoz9zoSrfEq2KX6AFS6dwtzYMJ/e84dyMoqgs54l41ReRTTsOajEshqr8aMXQe5jyZjXW+GcZGcwk2FmsAOFy9Yug4HsMJFSj9/fvLBnkR0in0QikGWQdathA3tSmFQdKf8l\ 5476 dlFrNCuHUZOnk6+XAf2WQGISkO7cScIXH0kssNYfOCyT+opngk2yEo3YAZS4Gl5ky7q/ZSMM12SZK7GtEDbhV4hX5Bzi3MQlQqVLNUGMgs5sCkml/MWYTZHTpzkPBEO7mPbZtSJRpnsm8h4iFHwRhyGI1A1ELsnT\ 5477 p/3da/GgT0bfuPVyvIBuyhgqaDJVh63tvTjkaShu3hRsDss+Tg5XRd/2No9biGOmnUquY5PbgeMQ8SqJ91Uq8RjbFAIq1kMQ6E2hB3lqgpjf1D1OUz35xz4IISzxyeRE4ANfZ4o7CF887glT8hIiEdKIUCgbOUpB\ 5478 sgvM3ZrIDDBGcZNjvB2ecvAj8fsD/1PYxujPif8ZTgCfhUXepZR8MLop7tWwma63lnYFtKjV8AlHFD/n9CCwXsmWNyeG5xNWA85Q5KMJpyXCTwKYBDwtYaYqvduwGSq6WwJrtrgrcM03937r29yEWd/kmnUjOQw+\ 5479 2JDtz/goFNXyrX1SysXdPiyZadAafYIa0oJrJekSuJ2uZ4EiVQWh5JI4FU9K2uwJYqzIViMkJXo28R1O7BqherFzwewhkPl+zqOMenbFXgAxegkimIF1uPoOGx5MC5+V5bTvsLja4Qn2+jE8oeA0yMJ4wml0NW68\ 5480 fwtLt1fvsOY1FJjeFNINK6NGZhX8VZdyvvxqwTGliHl2wcSdYhzxGphWW+6zZ7NXbLMo1Xk+rYstTijLm5v0TaRAhlvOxux+jLTOesiB/sBoITGBWVnDYiEsLj/eo9ZA6tufcUbrZYW3sLKU8gsCVuqjwKpydBux\ 5481 rZzMz4Nvtdlp+wICfnHY2Racv/DvsEgj3esjDghQhEsff4YRxLNrUaGpl/bYeBg2fzLGkqMYB4rd8et+pAVr2U+IBvbt1bvnYeshzmL/CWgfHMnXgtfabYS1m3CvI+Sxx/CBH+GeqGCqN/Ch884PVFgZm7ddUQph\ 5482 fSc5tK0/KJjZF99TT2id2Fz5F67BqoF82rsrGHMQPaarVZGxzP4XSAmQMD3zSI0fOHS5tEYNAzbVBJ0WsXmvEljyEizqKuKjTXS3oNSr/yiJ9QTojP4OhxCyYX99ceDigi2dVsmjzh7uS/WaroLOPFLm5WQbMwkt\ 5483 w28Re0GpzQLyNpUCeCS54U8QWimgs7PCoJHxUHJeSYlwowBGqNcpl5XyyeReM/qqhNmqQKHUDoR1mTH53bGkmoQy+geBgTSyxMTzz2ihKpb87BGj0f9LYDvfsCZgrKHWRaR/RJBxoePtZhMkMyyvBEQMpwl/a0qR\ 5484 bL3vR4KKBozemgErlioz8rZVM8r7ZiSDTP9HLlHOuUAhplNmVNFiB+hh3QroqQmFQdRCDVhM1NOTSHs5cFdZz4h7AfyLArc+/GWBu22vyPuntPCweAm92mI1GHCQaFYL1MH+57b3KzmgLZ+8eYaM+uzoFGI+vf0C\ 5485 tvxifv4PTB6/eYjJh0ePMPnoVh995SfjQ/DxNqoL2AjCCLlmXyBjxgZeSvouJX0jmFMrsJGUjvmk970kCM81SMOWAjSCkgnPwqhwuqbZVMSMmDBDI3cv/7BSQ6rgNK6Tq5aet28owN8oIKjpL7ANgpipKKMrapcL\ 5486 0q/O9skwtwQpaiGbu1HXLeQnYHIVrSu193Mk/JUQA60SUQmT59wd9LHqsIOuVeZfTQFAHYM6l06n0w1ygeCIsRSdR2MfaBtC65bNT7exq+uP8+jVWpE31CgwAnerzzCyLqKglmkbUSA3tlCkaENH/ZokD4teqZwR\ 5487 x6afiVdUO5/lxyJ5dJdsvly5clptx61KLPbyCFQVFCLDIReImb/mD1XNG+oLao3h5JXmRrbvrqsjgAf2gyOiuqLxJI6H+oTbEefSjIACkN+ogBuyXLgdOV9gKQ7VyDwSX3+9IQd8RE24VCuGp/RzqD9v4WfR9Fp0\ 5488 RCSJ/SGf70rdKd0VNAqo6rH0bBqfLTywp/9hH3TtPlQxPWBh+/Q73S7pE5K9CwsQZ/KXvHXnzOimwNyQCxqBFf0mw9tkucNw2vtNsL0ZI5CnCaB8rh1LZdcKySpd4zgN82zT6WaDrpt4BzGfz1Z6BQP1H7FDQyVy\ 5489 LkVoDTNVVbLqSi6Bl/WdauvU59/4cwASHA/RmiaoBM3gljTh5hc0LO0/6/xvMpPMFzSDKEvNZXsY0x61DrqqH3Tb7xP9CQjQ3kCtBWVSFs7iY8vcfk9mhoSWT/yvCJyZWgx4avJn4Jx+Urvt+fxCkngh3XgEEdoz\ 5490 /ZTBWAlsnJrEOFOBSLWUp0ibyN+1ZTkXlk5Ozb5dmO4DIZlygEXU9gATJp16DsQtK4xNu3YHO7sa3He2OacYI7mwTFWuYXckHC8SJ0REc8iA+dfwC6XcCuWs31QZeA6IRLxRRw7f6wnFJzqsnU6h3BQAvB5EX5Mr\ 5491 JW8HqkIo2AD9ldLiK4sep0hNq3IA4iJdoRenXNT+IRoKQA+4cSjMAeIbQKWbbMAnzqApVyCEc9eDenTfssfVrBFt4kJkaLeJoYbTRYXBqCWNq1QrlmrB0zBG3wlWGiFk0OlEesiUvumSA6KyZ+QUcAJv0KDCaDiM\ 5492 dK2cQLPCJggcnkAzdYsTqfQMOwWO5Tsv7FjppFrxYOM765ro1USOQB+FWNnvgM8VsWZbdInRHRB2QbeQWYwqcoN00ZlXD/FmZLsXy8qFAEtSLl1+6DVDOpXeaVn3OZCdyUzv6+Co2zOXPZG6hjPOmKJQqktK8pxx\ 5493 7F2XyaGEdNBq+mAtiVcyzs7iEUiFJLxanmg0lTzrR4aJ5vDDfirhuVqMc3BtZiF9+5XkRuWEY1E3SeTQCocAGdRdRi+QwEv7h8AKasooSIGNAAT0MwapsIyYpyYsimeblo9L0MCJ13KArjt6v8dWJ9ZSE28JAv0+\ 5494 THodRs1tQ1atEzGTxVfDMw6XqoSCUBkKkZYy/OAWossY7buivuISpa2vtGiqR1K1ODkyRZhcuofrOCzNGw7ObX3OBVYjTFbDDzc4nlNtl49fR0rIeY4a3Vcf4+hHROWN9LZ+kWBaHDGx7gJhE4+NaeP1H2i1hixg\ 5495 dyydjJy8SG6XDDtSk6ykU7bnp2F5Jb4PG0YI1kAQrzNbTQP+wIslSIbXuISnHOuryuWKrO2Q2gMeoC5JKnWqlV5xLXfa6qJsCr7tAVO5QsDSZrgjTlbFO0hx3O/12oM691PWIwyzkLzZK88l2BDnHWr/tLC9mg7c\ 5496 /oOEJcNskSUOX856iT+JdkyctPVL0F3wLnV9MNnkH0/oauTvsN1kdAV/l2tqvdApjIQ6LwUwngc0c60CVCfBJJ0vxqxrvT8jQWimdcMbR3LhR1dQ4vF0j9IuGcsrjit04WjlvRGK6FQbNoqyCSd8UAArL0yY4de6\ 5497 VuBrCKDiIy0gbaXWovdhTRGDbyNlA/VSM8CamgHOrnQ7paaqqhWbs+axgmjITCXyQNo1rXhINArJwDXF793IspU7rq75Qm4T6J8L0KJPIIjuLieVdlsUIPqWfszSW3JCpyE7lTZhf7LQjEOThxG1wka8oB+TTBWc\ 5498 SqWEDtD6SmnJRu31yomKi1r6yPQ6zeGqJzJLF/GNET/0E1xcWH/C77BoQfsHyYpV1RRRpYW2a3sNLD7tKXPEjwvEqMQeuo54zPGFvrklp0yu43Ek3OQl45/1fncc38ziQ8IrEv+OXqXaFsF3lzKV6QceQ8HbylYs\ 5499 qgUhiefxwlNFyGnCPyFTPY9pFS7nzXv2Mto71b1dFpslqrZ1JzFmRt2B5YtQUUFb9Hk5lDBWyssXkPvyCpUFN+j3xlT+39rEwGiKzl2Ao02vzkzf9oSrCjJrw+BWvvTeHJLo8eobSVJEkWBOpP9O2TB/z2Zcydtn\ 5500 atbSGd9jTggPYIhuSRVLrxWpi4dxZn/9So+9Efhg158qRFTZ9OkkdGlGNf/iXIzUz2/t/FtvWNV2Mm5Nb+gHyttV7b7cdumTVBLglb6w4Tn3Ovn6IyR9vahJIgWTHsidf8sI7OLgPjqfd1iD/XNU+fZscCBvGOn9\ 5501 wZ9SmQpM1laT1b+hhF5TrZPmK/wfnNDh9wRYwG68pgzkF8J4tZwXQaUanCA1EKjg2k474dKGML1XIHzMVBTx5AUCekuIokvJWanJ5CZCjw38VSfSsSnUIwUFSzZZcPOYsLqmIR+JSxrp++VLuXfwkxjc6LFSKf+N\ 5502 HPKab61xI4htPcy9/ZTJEdamyOYk6zgl91dpmnze9LduiAtJjVZRIfykRas/e/YArf7s9CZa/dkLWASa/dlxi3I9e/gAISN7NF903f69b7boTd+f3y3KC7zva02eJ9a6xISZ5nxx8bEbHCbOhcG6XJT0YjDEOiLX\ 5503 2JPh/i7GZklhkqv/AdnBtYo=\ 5504 """))) 5505 ESP32C6BETAROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5506 eNrNWlt7G7cR/SuyJEuJm68FyL3BiSXSpkTJtzqpY9Uu3XoX2FWdC79YpmK5Df97cebCXVIi5cc+UCKxu8Bg5syZC/a/+7P6arZ/f6van1wZO7my8VPl8Ts+5t3jyZUv4rf+5Kp0k6uCRvfiYPk8/sl+iH+SOJTF\ 5507 //V2/OPl6YSenlw14XVOcxzGP+ZpnL8/i6PJ4eRiclWb+LU3qEY7cfZilwWoeqeTq9AbPTzdjg+atIyr9uIn3lsUg/inP9mfTDE9JruMM6TxR8N3uXweR+PidRTYuviliVd8lLxq8sk+CfXHk3hfiPdX/GzT5Pma\ 5508 C7r0kPVC24yfEHLaJs8H4TNRWG+hufiJenPx4/v4/2gushRn2OMAwh+165j4v3BDVsHNi7rDuSzNKww6MuhK7W/rH0H7PHtc7/rUMFeSj6CeaBzX53mjOaOBnH+iK8PY8aEyPuybt7llddo+G5lvt8VJXCSM49x2\ 5509 FCEQ4pO1qiEZ4Rqsx6tUCZvImGdYNz2EsKOu4izP77A+jcUp87iNOuMbCGEZYwK/nWMs6ThPorp4wSbAkvhf5XsLlT0kowyj3M7yFCYd41ZspwScIBtjHIsUtcxTkE63485cb7SkWlGNbsVDl8P410ZQ1SmPFpbV\ 5510 U2Axh/ntHXgAkGQYC7itcWcHuKmdOkrn2QlVu4U7xkhUb5CRJpyx4aEN6NvmMqVtZasM/M9bmazPUIN8TY1Jx+o0HWDER7CPTDzOCOZse4vuuLHt6rBHoNVIMFoq7iZgKX99Cgj2Ky9c5UeETfED3FGkR4AbA/P7\ 5511 Q38qO8tw97iDZOzKq4r6HQcVLgu4Gz86DPIQtwz5fiuSk+4MrmS40tuBwWXqIgNAdniRUrffndzZ81YcWj5pF4Euo64bpQ3MSTSpNlL5S8yiaDQrm1HI087X64B+C5GYBEsv3Enoi7ckCAz6A5vlpe7wlYhJNqIR\ 5512 HMCIq/QiU4bulLUIHASZHGsoZNzIQRRj8HWs4YXCyHPeQ5d+ADiwhYCyBIDjpcoRZqPGvh7JFYQSt8eubPuvGeMUR/yJ/zFOY/TnyP8Le4VpEEceEPOeDPZEizW7pRPtgg5As+TThomk9ivhCRv2qW5yQJ4yoWcn\ 5513 A9ZXnG5CMk/AmH3ehssHIyYgipQSGiVMdqPjRVqz32gU7wTly28lJvv68BcNnXh8xIKv2whtphKisikzRNUb8y4IuvnWAZuktt92o89YsTm4YT14f9EIt8Lre0frhNiKX8BEMbQxOWrOINTYUcFQsxf1AiJzJosd\ 5514 Jm9FYcc/ZiwbwOq7vEaseT5nDkBeg+Dh5QqgUYT7jDrgCv8ry9Re4OZqhy8wLw4RSBxTHWviBVPljZG5DfrLsQSQMhJGejqRkvk6G2Is9Nu7Pd3drmuFVfi58w6N9VbjBZF30caJjUvadknmf2i3ytvUQxIL3uo9\ 5515 cVrx6yqVsA/pXCJhf13ENcikjNxFASE5Qlgjqrn8AOKw84+49B4rp3tizLiFUPNGg2QtoRSJ8jmAUXJmIMQ0Y2UUmhmIsBC8KQ9YUCaZbQanctHNinq3tzqzSX9qpycGKMdDpjJOTs47wZY+kNKJmlmONfI5kW/5\ 5516 8c5qsGe6vZnYSrMjd3h7hC9wMnwzqW+1VZWDe7BTOZpMI0k12evmDbT75nThqiBS5z/iJrXa+8dMrrBCkT6/TRBF2ArD1mFpmu3biRpBGJHGOSXd3eF7Ji/GL6TLfmRuVURrpnOj8xC27IcP84+vQARvwEb/BKQq\ 5517 oRiKjdugwT345mPsBYGtQTkVUCT0f2KuqAWipZhVVbwkR+/PXLNUUG2VMQGBIHzvlq0XUM+5x+gnnrRIg0Kq0DpuM+S3b7eUzef/WT8nIdLfZ1IlGPrrN8eFL/gRol7NhuzpgdRs6Wqqla94EiUSVtLBgECEdZok\ 5518 Zpum8h/Yy4hJIHKZ5hrngdV0CP9K8+cMrvJLwKVRD7M4lMvO+d/fM14q5VWH2qDpidyLK38ast0LkrH/SUpjGlmS4dXtAPfLfvKMc7AvVRjCmGc7NICLPUXki5OUuMfpleZ2OareXHIqKCVN+Edd8pO1O/B/bUGN\ 5519 UFP3HkgRvl5WGiQk/MDZ+JRzcZI1Ba9qMbLIXFHAaeZq7ZgHoVHkGU3yf0CzhJ7ix7Tl2stvPzxY9foNGRMVwcuU/OLeS8Dw5WT6GuKf/gTWKR8/foKLT+49xcWnk+kz8PXbZ52mS5WfDU+RvXxobYDMD9uN3H8g\ 5520 bpIJUUksLSWWgllDIbE2k1ibdL6XlB7Qs7in7nGyhdoEzwIe2B6I0Gebna30mvwVh+CzpUpIsyjNaDBWlbISZrHJXZjEHv2bU8SCtZ0yehbV2XJldef8gGC3JdmwVmRpMdA2Gr51hQDpGnusBa1adNEls+kRTSeB\ 5521 cU3qEH90unSTyfg6OiqaUnVAW8wpgZyw9GvR05Qb6XwqWuYeCgol2okYW3Q8XbTopK8A6eB1pDfA0tJgyl+4K3K0mUBK6pSc509kW2hy2Hw5SUZ+XKTNsFFDtC0lSlSiqDMEZfwtws/5UzXrOjWXEgdcpZGKAbro\ 5522 LOSy74L3h/BN40k7HssnLomnUhDDPOAl/MfNPteW2GSGW7GjWq6DZ7v3G/KgZ9QIqqSiyb6nn05/fo2fru60iWiRpE3ufb4rTiMVfnBSkll6Nm2fdR7JnH90wJ5rUUyU9ugEyn6gdK5zF925nW22MftbnnrhBWAn\ 5523 lH6NSviFxcyAGxLLlcwmEu2mSpRAbyha0CtwYKLUcr+yyrUfp4qwFAq4OvFODcWGuWTALVszpeYcErr8G4ChyRihlrt24LQ88xVfiEi5oGHxElv4X+RKMpnRFYiZf0V5Txt3qGvRaIxP+EcVDnTE8UiZHrx2EgGN\ 5524 PHkNfg9aWI38ryC1TCEBvq/zlxB+LFLW+avJhQRQJz2eRkGQ3oQIK5zHwUPQl0pGEiRXSe9+J50GImTaOTpHmMSlx7JeyjU7yMojipv0yDOZNExzDNxQnOzsKp2ieqnp2yWvXaaq15DvIRg0onFE10DXUBhC56XV\ 5525 lRtZOes2c3pe0JBzSWF66i4jpZ51mIaG6I46JHegAABNPYzOM+zBXOwMwxs7kqgOs3YawLvIbFXKnkgJs5EBQboqXfAI+MVTyHgC452A1UZQ9GhzLl94lOoOxM3tGKncrQTKYFuNwuE81+EC4wgLurzoDno2iOq8\ 5526 Cq21udfsF2qXKUkN6ajbPqUmO7Rlz/3PGEOBb9A2w2jckvTSCuEak37Hae1MG6CJVFCG3Rk786zYqiPKQrciX+0X2Bu12Ju2qrxMUE4U2gnfog76YneADB2BKeD6i+OLC6Hz0OlXGSaPi2X7Qnsl2ddUbRISnUHs\ 5527 UqZdCWRmJGDouvLgYDFnInMiZvXHHCeFwTR1iU41bOveMjkVLmd66ORYSVslF3bcboHsV0sUpSdqjSEvu4wx0sh92o0hfC0IPnvXrsykfFiJarXwDlRdJ62EViV00nNKRwBF3fwmqQQ1N9R7AQ9IvZqYxP8zZqxA\ 5528 6SM36XriqfDZi8X0v7Yd18ZIM3Fpus/9pNPpFK8iXQfhTSPorvrn7Pmqc0dpmb3LDXEf0IxswhBNLxfm3IFswlzrmDDgobqQPVZUKkkXc52ExqBVAJcOU4ZMLUJW/U93mdapvMqH79uV/FSqRTP/3I5+Bj/fsl5h\ 5529 ZiK5e8zrNdpIXP/Y4KNEOlmrMWTw3aF0GXJyGjnJsOw3dbISWBm+6L9V4uc+MOmq07dHZ40GBH/SDSN1y0Hzgo1V5VJuNot07JgHqDuRSmPAcn5Aew0dd2Qc+KaTfcohBmi27u+IQ1XtYZc46QM9eKGDgyM2IofY\ 5530 sYwsymMhFooGi7z8BjX/xq8F8GHfJ+EfwzI1lJO8HXcif9IimMRowlssesmzhHAyWrfSO1Qt1o/+DswmgzncWo5C9TTJ4ZzOSTrTyNNg86LRBLQQzkgnsyHbOOgpJ3RQyo+if/cxde2l/ywlDJ3gNEsgeScFQRCL\ 5531 4d0EKoWoeV1rFk2ZwidNI+VQ3vS/0nulIow8qXil+lGBos3w2rUcS8jV89UMiU3gFGdXzh2M7KdagZs1zzWV5aDKGjmWc5ZGPKPFgxE8EFR3W5GtHLAt+h/kLnH9qaRa9B+JwuJMIEh/q1XglhyDpNJ/6/pfoQfo\ 5532 N110pvOkPe3WP9Z7DYXJkaanUuVXxaZiaAmk9npxRBVM4DF+a+N01Q85Nb5oX0zwfT/aAkn4M35VQgvW30hdbK3atVZFy48ooNNG4t2+Zon4cUkmtAT0Ot5Gc3nLAPPQLpPrGTlCa/KWs9z1IemsfQGINwnHSPxH\ 5533 emNnWxTfpzJjxlGypR1DpG2Fy1lVM8oZXrUHrqpCjhD+BaF1Kjwq5Y0vLxlBNHeqcxdZ20lRs62hkL9R6b98BCv6b1xXkFMhsVIO+KH05TtUEdwD3R9Sef/1ptUR07+XtypiOVd3+jXph45+1UZmtCkEP1p6SQtR\ 5534 9Mnq6y9STJF6zqT3bQnPlwzmSl51UnBXXJ7vC69QQtCXs9q8bTjeDJFH7X4Kc7BJCYfMgJW/ufZQk4No0R9A/goE63jZsJIKq2+J/K6nvQqijF+X2NC2k7d5mgM5J9InqQRAhR8nnHJjkg8eIsHrqUjSrmDSE3n5\ 5535 oOEs7OLkIdqU91lEFvkvMMT2uHcir7MwnIr0DylShcO1p2T10xcORiRfp00PvRc99s99yS+oSNPwQXnagGM97RcJVNU7Q5ig81Gq3/TgJZXDa9N5F8O3UYuoT15joLdSiGZKjlB1JmcAain8Dz3poTh1Tck5KLIQ\ 5536 XVD/Vc8S8vb0x+SLkLLsoxSye1LeK9Hpk2Z9soJPdt3z1iN1LeC3ss8MAErDa0IIxaTiFgGMSb7ITwba4E+kFUUl8ovmJfD68jjGgSp7vfcGYe0N4PMPXH7SoJzPnh4/w+Vnk1mniU+UYPa/2aLXPf/1cVZe4KVP\ 5537 a/I8sbZITLxST2cXnxeD/X6viIOhnJX6dihAFZ1pX4a7sxibJc4k8/8BksMRng==\ 5538 """))) 5539 ESP32H2BETA1ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5540 eNrFWmtbG8cV/isYMCRunnZG2ts4MUi2QFyM66SOKa7cend2lzoXnoBFjNvov3fec9GsAMn+1g8CaXZ35sw573nPZfa/29PmZrr9eK3antwYO7mx4VPl4Ts+5t3R5MYX4Vt/clO6yU1Bo1thsHwR/mQ/hD9JGMrC\ 5541 /2Y9/PHydEJPT27a+iynOXbDH/M8zN+fhtFkd3I1uWlM+NobVKONMHuxyQJUvcPJTd0bPT1cDw+atAyr9sIn3FsUg/CnP9meXGB6THYdZkjDj5bvcvksjIbFmyCwdeFLG674IHnV5pNtEuqP43BfHe6v+Nm2zfMl\ 5542 F3TpIeuFthk+dZ3TNnk+CJ+JwnpzzYVP0JsLH9/H/2czkaU4xR4HEH4vrmPC/8INWQX3L+p2Z7I0rzDoyKArxd/WP4P2efaw3t2pYa4kH0E9wTiuz/MGcwYDOX+sK8PY4aEyPOzbt7llddo+G5lvt8VBWKQeh7nt\ 5543 KECgDk82qoZkhGuwHq9SJWwiY06wbroLYUddxVme32F9GgtT5mEbTcY3EMIyxgR+O8dY0nGeRHXxkk2AJfG/yrfmKntKRhkGuZ3lKUw6xq3YTgk4QTbGOBYpGpmnIJ2uh5253mhBtaIa3YqHLofhrw2galIeLSyr\ 5544 p8BiDvPbB/AAIMkwFnBb6053cFOcOkjn2QlVu4Xbx0hQby0jbX3Khoc2oG+by5Q2ylYZ+J+3MlmfoQb52gaTjtVpOsAIj2AfmXicEczZeIvuuLVxddijptVIMFoq7KbGUv7uFBDsV164yvcIm+IHuKNI9wA3Bub3\ 5545 u/5Qdpbh7nEHydiVVxX1Ow4qXFbjbvzoMMhT3DLk+61ITrozuJLhSm8DBpepiwwA2eBFSt1+d3Jnz6M4tHwSF4Eug65bpQ3MSTSpNlL5S8yiaDS3NqOQp50v1wH9FiIxCZaeu5PQF29JEFjrD2yWl3rAVwIm2YhG\ 5546 cAAj3qYXmbLuTtmIwLUgk2MNhYx7OYhiDL6ONbxQGHnBe+jSDwAHthBQlgBwuFQ5wmzQ2NcjuYJQ4rbYlW3/jDFOccQf+B/DNEZ/jvy/sFeYBnHkCTHvwWBLtNiwWzrRLugANEs+bZhIGn8rPGHDPtVNDshTJvTs\ 5547 ZMD6CtNNSOYJGLPP23D5YMQERJFSQqOEyW50vEob9huN4p2gfP2txGTf7P6ioVN8R2Lksr3QfirhKpsySVS9MW+E0Juv7bBVGvttNwCNFZ6D+5cEBxStMCx8v7e3TI618AV8FAIcU6RmDkKQHUUMNYdRXyBKZ8rY\ 5548 YApXLHa8ZMriAbK+y27EneczZgJkNwghXq4AIEX9mLEHdOF/ZZngC9xcbfAFZschwoljwmNlvGTCvDc+x9C/GFEALCPBpBe1ypS+zIwYq/vxbk93x3WtcAs/d94hs97tqEEUXsRosXJJG5fkKADtVnlMQCS94K0+\ 5549 EtcV765SCf6QziUS/JfFXYN8yshdjLM9BDcinOtL0IedfcCl91g53RJjhi3UDW+0ltylLkWifAZglJwfCD1NWRmF5gciLARvyx0WlKlmncGpjLQM2tu3pzbpT3F+IoJyPGRG4xzlvBNz6QMxneiZBVkioBMBFx/v\ 5550 rAaDpuur+O3dplz2dg9f4GL4ZlIfdVWVg0ewUjmaXASiarOz9g10++Zw7qggU+c/4Ca12fsjJljYoEhfrGbZco6vWyzb1AvTrH9+GkRDRBvnlHg3h++ZvRi9kC77kfl1TmOS7dzrOoQse3k5+/AaNPAGXPRPAKoS\ 5551 gqH4uA4S3IJnHmEvCG4tSqoa8vR/YqZoBKCl2FRVvCBH789ct1RQbZUx/YAefO8zWweHpeceox950iKtFU+F1nKrAb8aL6CL2X+WT0hY9I+ZTwmA/u7NYdUrfoRYV9Mhe7gjRVt6O9fKb/kQZRJW8sF6hImQOiQh\ 5552 3TSVv2T/IhKBXso010APoKZDeFaav2BklV+CLC8xD7M41MvO+d/fM1gqpVSH4qDtidzzK38astELkrH/UWpjGlmQ4fXnUxG/6CQnnIR9qcIQwbwE7QKh7RBBL0xS4h6nV9rPy1H1ZpJUQSlpwj+akp9s3I7/a0Q0\ 5553 okzTeyJV+HJZaZCQ8AOn4xecjJOsKRhVq5F56ooKTlNXa8c8CI0ixWiT/zvBDibTH9PIsdffXj657e2rFU018CIbv3z0CiB8Nbk4g/CHP4FwyqOjY1w8fvQcF59PLk5A1W9POj2XKj8dHoImLqMFkPVhs4H2d8RJ\ 5554 MuEoCaKlBFGQal1IkM0kyCad7yXlBfQs7ml6nGWhNMGzAAd2CA702epco/Sa9RW7oLKFQkjTJ01lMFaVshJmsclDGMTu/Ztzw4IVnjJ25sXZYmH14HyHQLcmmbAWZGkx0C4avnWFQOZk7L7Ws2rUeZPMpns0ncTE\ 5555 pelw62OTbjIZ3wVIRVOqDmiLOWWOE5Z+FYDacgWZm0r0zE0UVEq0FzG3aPli3qOTxgLkg9eR3ACmpcGUv3BbZG+1WCW1Ss7zY9kYuhw2X8yPkRoXaTts1RSxp0RZShB1ioiMv0X9c/5cDbtsu6XEAVdppGKIzlsL\ 5556 uey74P0hdtN4EsdD8cQ18YVUxDAQeAn/cbPPtSc2meJW7KiR6+DZ7v2GfOiEOkGVFDPZ9/TT6c+v8dM1nT4RLZLEvN7nm+I2UuLXTqoxS8+m8Vnnkcn5ZzvsuxZ1RGn3DqDsJ0rnOnfRndvZdh2zv+Wp534AfkLV\ 5557 16qEX1jHDLgjsVjErKpcu3kSMWOzOlw7cFFquWFZ5dqQU0VYCgVcmHinhmLDXDPgFq2ZUncO2Vz+DcDQZoxQy207sFqe+YovBKRc0bB4iS38L3IlmUzpCsTMv6K8J8Ydalu0GuMT/lHVOzrieKRMd86cREAjT96B\ 5558 35MIq5H/FbSWKSTA+E3+CsKPRcomfz25kgDqpMnTKgjS+xBhhfU4fAj6UslIaslV0offSZ+BKJl2jtYRJnHpvqyXcrkOrvKI4ibd80wmLRMdA7cuDjY2lVA31jmmGHPNa5ep6rXOtxAOWtE4QmxN11ATQuel1ZVb\ 5559 WTnrdnN6XtCQc2PP9NRdRko9yzANDdEdTZ08gAIANPUwOtCwOzOxMwxv7EjiOsza6QBvIrNVKXsiJcxGBgTpqnS1R8gvnkPGAxjvAKw2gqJHqwuGwqNKdyBu7sRI0W4lVNY2ahQO57kEFxgHWNDleXvQs0FU51Ud\ 5560 rc3NZj9Xu0xJakhH3f4pddmhLXvuf8YYanuDvhlGw5akmVYI15j0O05rp9oBTaR8MuzO2JlnxVYdUea6FfkaP8feKGLvIqryOkE5UWgrfI1a6PPdATIUbhVw/fn5xZXQed1pVRkmj6tF+0J7JdkXgVnTkOAMYpcy\ 5561 7UogMyMFQ9uVBwfzOROZEzGrP+Y4KQymyUtwqmEsesvkULic6aGTZSWxRC7sOG6B7NdIFKUnGo0hr7qMMdLIfdiNIXytFnz27lyZSvlwK6o1wjtQdZNECa1K6KTdlI4Aiqb9TVIJ6myo91rDPePFxGRKT4COKHvk\ 5562 5lxP3BQOezWf+9fYbG2NNBEX5vrUTzodTnEpUnQtpGkE2lX/nN1eFe4oJ7MPuR3uazQh23qIZperZ1zpt/VMi5h6wENNIRusqE6S7uUyCY1BkwD+XF8wXhoRsup/fMicTrVVPnwfV/IXUiqa2ac4+gnk/Jn1CjMV\ 5563 yd0Rr9dqA3H5Y4MPEuZkrdaQtTeH0mLIyWPkHMOy0zTJrajK2EXbrRIn9zUzrnp8PDhrNRr4g24MaSIBzQo2VpVLrdnOc7F9HqDWRCpdAcvJAe217vgi48C3ndRTjjDAsU1/Q7ypikdd4qFP9NiFjg322IgcX8cy\ 5564 Mq+NhVUoFMyT8nvU/Bu/FMBHfR+FfDouYvtvx52wn0QEkxht/RaLXvMsdX0wWrbSOxQt1o/+Dswmgxl8Wg5C9SzJ4ZTOSS7TytOg8qLV7LMQwkgn0yHbuNYzTuiglB9F/+ERdeul7yz1C53ftAsgeSfVQC0Ww5sJ\ 5565 VAdR07rRFJrShI+aQ8qRvOl/pfdKQRhIUvFK5aMCRZvgjYsES8jV09UMWU3N+c2mnDcY2U91C27WvNA8liMqa2Rfzlda8YyIByN4IKhuRpGtiw0uan6Qu4T1LyTPov/IEuZnAbU0t6IC1+T4I5XmW9f/Cj0+v++i\ 5566 M50n7WG3+LHeaxxM9jQ3lSK/KlZVQgsgtXcrIypfah7jdzYOb/sh58VX8bUE3/ejNZCEP+UXJbRa/Y3UxdZqXLQq+n1EAZ0eEu/2jCXixyWT0PrP63gM5fKOAeahXSZ303HE1eQtp7jLQ9JpfP2HNwnHSPwHel9n\ 5567 XRTfpxpjyhlUpB1DpG2Fy1lVU0oYXsfjVlUhRwj/ktB6ITwqtY0vrxlBNHeqcxdZbKSo2ZZQyN+o7l88gBX9t64ryKGQWCnH+1D64h2qCG6Abg+ptv961eqI6d/LOxWhlms67Zr0sqNftZEZrQrBzxZe0UIUPb79\ 5568 8otUUqSeU2l8W8LzNYO5khedFNwV1+bbwiuUEPTljDaPLcf7IfIs7qcwO6uUsMsMWPn7Cw81OYgWzQEkr0CwjpctK6mw+o7I73rKqyDK+GWJFV07eZen3ZETIn2S8n+U92HCC+5L8qlDIHg9D0niCiY9kFcPWs7C\ 5569 rg6eokv5mEVkkf8CQ6yPewfyMgvDqUj/kApVOFwbSlY/feFgRPJl2vTQe9Fj/9yW/IIqNA0flKcNONbTfpFAVb1ThAk6F6XiTU9dUjm0Np03MXyMWkR98hIDvZNCNFNyhGoyOQBQS+F/3ZMGilPXlJyDIgvRBbVf\ 5570 9SAhj0c/Jp+HlEUfpZDdk9peiU6fNMuTFXyyu563HKlLAb+WfWIAUBreEEIoJhWfEcCY5Iv8ZKAt/kT6UFQfv2xfAa+v9kMcqLKzrTcIa28An3/g8nGLWj57vn+CyyeTaaeHT5Rgtr9Zo5c9//VhWl7hlU9r8jyx\ 5571 tkhMuNJcTK8+zQf7/V4RButyWuq7oQBVcKZtGe7OYmyWOJPM/gf0hhEn\ 5572 """))) 5573 ESP32H2BETA2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5574 eNrNWmtb20YW/isECLTZ24yt26QbsFMbY0iyaTcpSx/TVhpJbLotz0JMQ3br/77znoskG+zk434w2KPRmTNn3vOei/Tf/Xl1N99/ulXsz+6Mnd3Z8CnS8B0f89PJ7M5n4Vt/dpe72V1Go3thMH8V/iTfhj9RGErC\ 5575 /2o7/PFyd0R3z+7q8jwlGYfhj3kR5PfnYTQ6nN3M7ioTvvYGxWgnSM92WYGiN53dlb3R8+l2uNHEeVi1Fz5hbpYNwp/+bH92BfEQdhskxOFHzbNcugijYfEqKGxd+FKHKz5oXtTpbJ+U+v00zCvD/ILvres0XXNB\ 5576 lx6yXWib4VOWKW2T5UH5RAzWaywXPsFuLnx8H/+/Xogu2Rn2OIDy43YdE/5nbsgmeHhRd7iQpXmFQUcHXan9bf3XsD5LD+vdF43jitIRzBMOx/VZbjjOcEDOn+rKOOxwUx5u9vVFatmcts+HzNNtdhwWKSdBth0F\ 5577 CJThzkrNEI1wDafHqxQRH5ExL7FufAhlR13DWZbvsD6NBZFp2EaV8ARCWMKYwG/nGEs6zkLUFq/5CLAk/hfpXmOy53Qow6C3syzCxBNMxXZywAm6McaxSFaJnIxsuh125nqjJdOKaXQrHrYchr82gKqKeTSzbJ4M\ 5578 iznIt4/gAUCSYSxgWu3ODjCpFR208+yEat3MHWEkmLeUkbo844OHNWBvm4pI2+pWGPiftyKsz1CDfnUFoRN1mg4wwi3YRyIeZwRztp2iO65tuzrOo6TVSDFaKuymxFL+vggo9isvXKRjwqb4AWZk8RhwY2B+c+in\ 5579 srMEsycdJGNXXk3U7ziocFmJ2fjRYZDnmDLk+VY0J9sZXElwpbeDAxfRWQKA7PAiuW6/K9zZy1YdWj5qF4Etg61rpQ3IJJrUM1L9c0hRNJqVzSjkaefrbUC/hUhMhKUbdxL64i0JAkv9gc3yUo/4SsAkH6IRHOAQ\ 5580 V+lFRJZdkZUoXAoyOdZQyHiQgyjG4OtEwwuFkVe8hy79AHBgCwFlDgCHS4UjzAaLfTmSKwglbo9d2fbPGeMUR/yxfxvEGP058j9irzgaxJFnxLzHgz2xYsVu6cS6oAPQLPk0mKXyK7EJu/Wx7nBAbjKjG2cDNlaQ\ 5581 NSOFZ6DLPu/BpYMRsw+FSYmLEiO7ofEmrthpNIR3IvLtVxKQfXX4i8ZNcRwJkA9vhMeqQojKxswQRW/CGyHoplsHfCSV/aobfSaKzcHDS4IAslroFY7fG6/XgwAEtIcAxxSpmYMQZMcWQ81h1BeI0pkydpjCFYsd\ 5582 L5mzhoCs77IbceflgpkA2Q1CiJcrAEhWPmXsAV34X1gm+AyTix2+wOw4RDhxTHhsj9dMmA/G5zb0L0cUYMtIMOm1hmVKX2dBeELZme1pdruuFW7h+y5FCCxyL2oQhWdttNi4ZOfkOQrAukXaJiCSXvBWn4jrincX\ 5583 sQR/aOciCf7r4q5BPmVkFkNtjOBGhHN7Dfqwi/e49A4rx3tymGELZcXWKSV3KXPRKF0AGDnnB0JPczZGpvmBKAvF6/yAFWWq2WZwKiOtoYudVckm/rkVT1SQT4ZMaJyiXHZCLn2gpRMzsx5r9HOi3/LtndVwnvH2\ 5584 JnqDA27JDG/H+AInwzcT+9ZaRT54gnPKR7OrwFZ1cl5/D+t+P21cFXTq/HtM0lN7d8IUi1PI4lebFNlScK7wbFUuyfjEZmBR5GYINogLTL27w3fMXwxeqJa8ZYZtiEySnQc9BzmEt9fXi/ffgQW+BxX9ADwVwi8U\ 5585 HrdBg8EIZf8Ee0Fsq1FRlagT+j8zUVSCz1zOVO27pEfvz1y2FLBrkTD7gB187xNbB2LiS4/RDyw0i0vFU6al3Ga8fxIvyJoX/1kvk+DonzKjEgb9/clh4Ru+hXhXEyI7PZCyLV7NttIVN6JcwkpGWI4gCMlDFBJO\ 5586 U/hrdjGiEaicx6mGegA1HpKp0lcMrvxzwEWBz7AUh4rZOf/bO8ZLoaTqUB7UPdG7ufKHIZ97Rjr2P0h1TCNLOnz3iWTELzvJS87BPtdaCGBewnaGyDZFzAtCcsxxeqX+NASK3kJyKjhHHPGPKuc7K3fg/9YiGkGm\ 5587 6j2TIny9rjRIMPiWs/ErzsVJ1xiMqsVIk7migNPM1doJD8KclQMY/g8Ilku9t3HLsrdfXT9bdfkNvGj698j49ZM3wOCb2dU51J/+DMrJT05OcfH0yQtcfDG7egmmvnjZaboU6dlwen7dHgDSPuw1UP6BOIiQbY4Q\ 5588 Gkny32NOLQX+XkKslxBL13LWHTCnFLLHyS4KExAXsOHNFU/YlGbkhSZ82SFobKkG0sxJsxiMFcB0JV0KGz3GYdjxPzktzNjOMeOmqcuWa6pHlwcEuC3Jg7UWi7OBNtDwrasEkiZjj7SU1bNs+mM2HpM4CYZrHbpu\ 5589 d/XTbDa5j4uCRKoNaIspEdGMtV+Lmzrd7MLEV1TdUv8ERRLtJefzFCtfNe056SlAP3gcWQ6QtDQY8xfuiIw3bDinFslleiq7QnfDpst5MWg2i+threfQ9pIoNwl6zhGK8Tcr/5W+0FPdEBxzqfVcoSGK8dt0FVLZ\ 5590 d8b7Q9ym8agdD6UTl8NXUgwTX1o+KEoTUm2HzeaYSmWWXM/7y/PxKZOX1AQqpI5JvqGfTn9+iZ+u6rSIaJGo7U/4dFcKYqnuSye1mKV74/Ze55HC+a8P2Gltid3Z8Rew9zOlcpWddWU7W29D+gWLbvwAPQfUfLVq\ 5591 +NklDEm9V8I8PHuwnCNR2lxtJlyHciW23Kss0p0W8bwdCgNck3inB8UHc8uYWz7NWBtzPn0CMNQJg9Ryx64iw/mCLwSk3LTz0WXzv8iVaDanK5kaFxFYYw51LGqN7xH/KMoDHXE8kscH506in5E778HvWQur5/5X\ 5592 kEGkkADdV+m3UJ5+EkLezG4keDrp79QKgvghRFhhPY4dgr5YspFS8pT48V+ly0CUfCSLUyuJsHkkS8Y8CLL0COImHnvuJhAdV4LdMjve2VVO3dnmmEJlpJTseazHEfJxBIVa7I74Wh6JSUo0jXOri9eyeNLt6PSw\ 5593 finrJ+qsGBspB21ANkxFk6oyegRLAHHqavRQwx4s5MCBAGNHEtpxvowpL0VFXqqiPVEU50cniRhUS1e79Ij62QsE7WOc4jHobQSsjTYEicyjTHegb27FSNVuKWC+VJMxsHypMB4eCU2t4Btn0egrLZia2p6CLiM5\ 5594 B51hF/xNuzlmp/V8lHI4JpoqPCXOo5LK+hOOAl5Mn3IbtqFzJy0HajekuzuMJVO0veM8IvM/5u1zj3uLeuNo+5QUaT1zeAOkfvNggublomLSXrJNb8UVfBVIzenQjO+gKh4/UR+a3oqfkAyRTSB4roODRqoXqdmK\ 5595 EZI2OQkeM2wLWtln4/6dLKpj/MxO2k0QOtH8xLFX4occI950GWGkwXnajRF8rRTY9e5dmUtpsBK1KH0XmFVRq6FVDR3Dw8SjsbSKBWLUw2sTjd/7kUJ6Kt1UKhTF3JRrUnP2khdUczjKiCwXKze9PyFBGaLH5MoF\ 5596 N/zqcqHFQzngoSoTf88p3EjT8GHN8PkIOJVXDMxKctOi/+ExkykVNOnwXbuMv5L6zCw+tqMfQYmfWMyYDxLd3AmvV2vTbtNt3txqUKQT2B0yp8OQRNE4MAqfzNcjCUgCbhIp7PMWuU/cubWdHbWznT/2Engktnp/\ 5597 LOxVo+7I9FlDc3fOd1dVc7fyUyWnqyKkA59JilLajCvhuT4ziaTjIs/zilSqyLrJtI7Eu6xk+6k0hytpSpZK5fGYn65zzJvICCks8YSqZOyrSZQfPribZ/rQ7oNQhtTSaG1BAXaQi0knJEctvkmdurzAPb+xTmV9\ 5598 PFq34uAHelbyD0A6GizgjvJ4Up/wOCNM5JXEa06asloTw0x8PRTrQ3bcUp88WunX0NPj/uMT6qFLN5hLC6T09VJY+Em6cla6GHhbgAiXWF1LPmbeD10DSZe+JMb6Qu+Rio1YrtATptpCfKrOBp5bB1VHmtEnnxly\ 5599 jpKzj115FmDE6YsVxFjzShNNzqnYLkcsnuL8MjjEppTd9ndbla08+mo6E8SEYf0ryYLoPwK4Zh94GlMUS4ZEG6HpiXXDLkv75oErzug9dtotSqynPJeC8nglKOPB5voKZQmg9n7F4oT7TaqvUUwfiOXB+27aNwV8\ 5600 34+2gBB/xu8uaCH5b7ISH1LlOtDw4rxJ29fhrZ6zRny75ABal3kdb0OwPPaHHNpldD9NRjyMLjiMrwlT5+3rOLzDISVa76k22Bar9ynxn3NHuI3mhgjdSlBhO80pyn/XPv5U+3H08K8JoVeSGkvB4f0tZ+4kO1bZ\ 5601 WdJ2N/TM1rFVZd6Q/y4/FhX7166ry1TCby5P3GH05RlqC25K7g+p5v5ykwJbf+c0pQgFVtXpocTXHfvqAZnRprg3WnplCl5/uvoyipQ3ZJ4zoTVLYL5lJBfy4pEiu+CCeV+4BLSPIXpsmrbtv4cUys243U9mDtYi\ 5602 aRigVPiH0389bOppmJxf29HBvOaGXWb1VY3f9GGrYidh197QQZNXauoDbgw2qKOgilI7CLxqHiHe0Gnpc4moXcHEx/IGQM052c3xc2j3VHqJpPJf4Pbbk96xvFPCEAoonXHioAmsdnesfvrCtygA1gRBPALrndHW\ 5603 9yXnAGT8QA4RIZyyvlJ2S/3h3hniAeUBVEDpg49YnhybzusQopSTQllfJqAXQzR2IShVibTh9Zzwv4ykleHUH0vONCiEEEdQVNR2fsrdI11cYseyV9JLHD0psZXa9E6zPkUZZPd9bQ02o03EQY2YXFyvInBQ/Mk3\ 5604 ro758Wd5BnYp7TYXfRQkJ6/30EJPUMkkaKIn0xpN9OTkCO6enO6hnE5e4DLa6L2LqtNGJxow+3/cohcuf3w/z2/w2qU1aRpZm0UmXKmu5jcfm8F+v5eFwTKf5/p+JjAVPGlfhrtSjE0iZ6LF/wCAbuTq\ 5605 """))) 5606 ESP32C2ROM.STUB_CODE = eval(zlib.decompress(base64.b64decode(b""" 5607 eNq1Wmt7E8cV/iuObXBC8/SZ0V4HgpFARrbBFFKCCxVNdmd2XUhwYyMH00b/vfOei3YlWyb90A+WpdmZM2fO5T2X2f/szJrL2c7djXpnemns9NLGv7qI3/FnfjqcXvoyfkuml5WbXpY0ejsOVs/iR/59/EjjUB7/\ 5608 N5vxw8vqlFZPL9vwugCN6kH8ME8j/WQWR9MH0/PpZWPi18GwHm9F6uU2M1APDqaXYTB+eLAZF5qsirsO4l+cW5bD+JFMd6anIA9iF5FCFn+0PMsV8zgaN28iw9bFL2184iPndVtMd4ip35/EeSHOr3lt2xbFmge6\ 5609 9YjlQseMfyEUdEymB+ZzEdhgIbn4F+Xm4p9P8P/RXHgpj3HGIZjf6/Yx8X/pRiyC6zd1D+ayNe8w7PGgO3W/rX8E6TP1uN9V0lBXWozjZx2V4xKmG9UZFeT8E90Zyo6LqrjYt28Ly+K0CSuZp9tyP24SJpG2HUcT\ 5610 CHFlo2JIx3gG7fEudcoqMuYI+2YPwOy4LzjL9B32p7FIsojHaHKeQBaWs03gt3NsSzrORFQWz1kF2BL/6+L2QmQPSSmjyLezTMJkE0zFcSqYE3hjG8cmZSN0SpLpZjyZG4yXRCui0aN4yHIUP200qibj0dKyeEps\ 5611 5kDffgUPgCUZtgVMa93xLiZ1pCN3np1QpVu6xxiJ4g0y0oZjVjykAXnbQkjajrfawP+8FWIJmxr4axsQnajT9AwjLsE5cvE4IzZnuyl64tZ2u0MfgXYjxmireJqArfxVEmDsA29cF3tkm+IHmFFmezA3NswXD/yB\ 5612 nCzH7EnPknEqryJKeg4qWBYwGz96CPIQU0Y83wrnJDuDJzmeDLagcCFd5jCQLd6k0uP3iTt70rFD26fdJpBllHWrsAGaBJOqI+W/AhW1RrNyGDV5Ovl6GdBvARKTYuuFOwl88ZHEAoP+wGF5q6/4SbRJVqIRO4AS\ 5613 V+FFSIY+yUYYDmKZHGsoZFyLQRRj8HWi4YVC0TM+Qx9+YHBACzHKCgYcH9WObDZK7JuxPEEocbfZlW3ymm2c4ojf9z9EMkZ/jv2POCtUgzhyn5B3f3hbpNiwWzqRLuAAMEs+bRhImrASnnBgn+khh+QpU1o7HbK8\ 5614 Irkp8TwFYiZ8DFcMxwxAFCklNEqY7EfH86xhv9Eo3gvKF/ckJvvmwS8aOrF8zIyvO4iRgxBQ2YwRoh5M+BRkusXGLquksff60Weitjm8Zj94f9kKtsLrB3vrmSDrqSCRQvBR0wZBx54URprAqCMQnjNebDF+qyH2\ 5615 XGTG7MFefR/aCDhP5gwDSG0QP7w8gXWU4S4bHkwL/2vL6F5icr3FDxgaR4gljtGOhfGc0fLa4NzF/eVwAtc1EkkGSkjxfJ0E4QahN9vT7G5fK8DC606ECCRyJWQQfpddqLhxS9NtySEA0q2LLvuQ3IKPekf8Vly7\ 5616 ziTygzuXSuRfF3QNkikjsygmpHsFG29dXJwBO+z8Ix69w87ZbVFmPEJoWDpBEpdQCUfFHIZRcXIg2DRjYZSaHAizYLytdplRxplNNk6FI1esMW2Em2XiJnvf7UA4UE1GDGicopz0Qi79zS96rGRim0RAWFqevsJ/\ 5617 JoviWAVxZrtszNeyDA5KI4fzdg9f4Gf4ZjLfCayuhnegqmo8PY1Q1eav2zcQ8JuDhbcCTp3/iEmquHeHDLFQRJk9uxlnq4Vdr+BsE5bIbCqZjVUaG79zmEFEYMTdHr1j5GLRgKn8BwZWFZamOde6DdTp7dnZ/OMr\ 5618 QMAbBJl/wJhqARcKjJvAwHj8kBzCyxDVWtRSARVC8p5RohHjJHuqOsku8TH4MxcsNSRa5ww9gAY/uClAUfZ54jH0iSmWWYC9L9KV9IuWvl6s2GE2//d6amSP/i5bat1wFrw6OW55zks6e4Z+D3alTstW06tixW8o\ 5619 ebCSAgYEH+zTpjHDNLU/Y58i6IApVVmhsR2WmY3IMYpnbErVF0xp+CsvdqiMnfO/vWPrqBU/HcqAdiDsLp78acRaLom15JNUwTSytPWrLycdftkZjjjd+qNyQrjyLP62RBw7QISLRCrMcfqk/bJT1oO5pE/whizl\ 5620 HwjiWNm4Xf+XzoQRUprBfam31/NKg2QA33PifcppN/GaATy17lgkqajVNEm1dsKDkGjjYAb/TywVDGVAXeMk3jQ/ZB2UXtw7u7/q3evkvHHrCtw+v/MS1vdyevoaDBy8B7RUh4dP8PDJnad4+HR6egQsfnvUa6vU\ 5621 xfHoAMnJWSd65HZw9Yjru+IUuaCRhMpKQiXgM5QSSnMJpWnve8WZANZiTjPgXArVB9bCKnA2oJ3Pb85FK6+5XfUAuLVU62iSpAkLxmqsaKT8sOktaMLu/ZMzwJJFnbHRLOqv5drpq5NdsrYNyXe15srKoTbK8M3Y\ 5622 x8ucWE0xM1Xnog9msz0iJ0Hv+gMjia66Ptx0Olk1jeJJTwB0voKSwymzvsZuOAklIVKmXpY/QhJ7GDskgmdw9orzfZtk/OVGRitELVSpaDvYYjlnRbpaZu2oVcF1TR5KGiL3M+yLzzL8XDxVNazdrRLJivGJzk+1\ 5623 KUggXGuMYWNb9AEKWVMyMCDe0njajcdihwvYUylf4YWAFvyn8F5oA2s6w1SctpHngMr+fEPecERtm1qKj/wF/XT68xv8dE2vqUObpF1HwRfb4gBSjwcnBZSltVm31nkkXf7RLnnh+T30D/b2WQ0VgML0iZd94s62\ 5624 myD/lmkvrBltAlRqrbL4PxUe7krhcZOH92dTsru2xhgy7vnMcXuxLrR9ppLAYYsP/NQ71RRr5oKtcVmdGfXSYMzFt7CGNme5WW6yAaCK3Nf8IJrKOQ1nQqL0v8iTdDqjJ+C5+JpSli6mUJOh1TiNfdt3uf5EyGxf\ 5625 5EdW4peRNVcs735nUWP/AdiUqzUAtpviJdieCH9N8Wp6LuHPCRq2qv7sOluwAl0cA8TwMsknglSxhKapdAXoBx0bXR7Qcdlj2TLj4hqI5xGGTbbneX3LmMVmG8r9rW0Fxq1Njg3GXPD2VaZCDcVtwHor4kaQDPQM\ 5626 FRwEXlnduZWd837jZeDFFAruwRkJWRYpIoPSDQYNOdGkEL6DzN105jovo/uHW/lcFA3NGzuWGN3CB7uG7TayUuV0IJxCnqRHRHblMHiE7/Ip+NyHDveBa2MIe3wD1JctimoHEOfGidTYVmJesJ1I4WueK2Yx4mga\ 5627 9HjRyvOsERV6HTp1c2PYL+QuJEkG2bjf66SOOERlT/zPGEMpbtDjwmg8jzS+CHoG2Xeclc60VZlKuWPYk3EszyKte3wspCrMNX5heePO8k47ITbfoQgotWe9Qb3uxdEGkkUszC1ZXDScC5SHXlvJMG6cL2sWoqtI\ 5628 s6bukonoCqKUKutzIJSRSKE/yoPDBc1UaCJeJROOnwJei+zDo0miRWqVHgiMMz70cqW0K2lLO+mOQMprJILSikbDx8s+ZIw1oh/0wwc/C2KZgytPZpL9r0S0RoAHom7SjkOrHDppDWVjGEXT/iopBjUg1HdbI026\ 5629 nu+WEUhmDFaBckAsC9ldcVC46vmC9oeuMXodrcZ8TtJeN1L8iQQdBDWN2HWdnLDDq8AdJV/2Fvet24CGYRtGaEy5MOcErQ1zLSfCkIegazpgTWWOdBrXn/YSgAZKp2wvjTBZJ59uMahTaVSM3nU7+VOp9Mz8czf6\ 5630 GdD8hf2M+U04d4e8X6vNvvXLhhcS52Sv1pC2t0fSGCjIY+TCwbLTNOlKQGXbRYOsFif3gbFWPb674Wo1Fvj9fgRpOvRBbWhYX3Uh1WK7SMUe8wD1FDKp6y2nBnTc0HNHNgXf9jJP17XUm2RLHKrurqXESe/rFQm1\ 5631 +PdYjxxgJzKyqG4FWIjz8mbzP+c7fL6Z+yQQZJitljKSt5Ne9E87OyZO2vAW+14wlRD2x+s223hLdyh/g+GmwzkcW64t9ebH4U7NSUbTymLgedlq9lkKasTKfsSKDnojCSlU8qNMbh1Se10axXKNSbct7ZKl/CTl\ 5632 QBCd4T0CKiKoy9xoDk2ZwifNIeUC3SRf61yp9SJSqtFSJaimol3rxnUoS+ard6E5EpvAKc62XBAYOU+9YnDWPNM8lmMqS+Qxk6d0YtkijFgEGet2x7KVy7BFY4N8Ju5/KqkW/UeeoIk43dMlSwLckPuKTPpmfScs\ 5633 9bL7uofO9Fbag371Y73XYJjuaYYqbw/U5U2l0JKN2qulEZUvgcf4DYuDVU/k7Pi8e4nAJ36MCtn6Y36tQUvZX0lcrK3GdVpFz45AoNcX4tO+Zo54uaQTWgB6He/iubwRADp0yvRqUo7gmr7lLHd9XDruXtbhQ8Ix\ 5634 Uv+R3q7ZFMEnVILMOI3qgMcQclvBCxbVjLKGV93lqIqQw4R/TtZ6KmBq2Ot8dcEWRLQzpV3mXU9E1Xb9SX76KzUFlq9LRf6t6zNyIBhWyWU8hL48QwXBTcydERX339y0OwL7C3kDItZyTa/zkp315Ks6MuP1lLx5\ 5635 tPRCFULpk9VXVaSeIvEcS8/akj1fsDHX8lqSGnfNtfmO4AplBYncqxZd3/AawY57jUqzu3bakLGv9tdXHKps8IJVyF1huzz+L1ZdafVFjt/0NlZtJ+c3Gm7ou8kLN+2uXOLoSsr98ZZXJHi6uGM8J1zX64u028Fk\ 5636 +17zLM7X9x+iz3iX1afnaChb2JwM9uWlE+3c/i7lqaC39pKs/iWCvojia3IcyGzAbrkjiQUk5zVqUI425CBP50XyVA+OER3o/pKqNr0nyeRy2fRel/BdsCLEkzcN6MURQpeKA1OTS+9ej43GXUikb+LUIyXZoIBC\ 5637 KIFWbaN3AEV3WWOKRSRZdk2K1AOp6hXfdOWNSbrBNeiqw62x0WRt/lF8Zu1T/t2QeVAcKr9YIqR/yD3ApzT4SFmBt2vz5+1L2OvLxxH+6/z17TeIZm9gO3/H4yctKvj86eMjPD6aznpdeEICs/PtBr2R+ePHWXWO\ 5638 9zKtKYrU2jI18UlzOjv/vBhMkkEZB0M1q/QFThhVdKYdGe5TMQOXuSKf/xdxa/YZ\ 5639 """))) 5640 5641 5642 def _main(): 5643 try: 5644 main() 5645 except FatalError as e: 5646 print('\nA fatal error occurred: %s' % e) 5647 sys.exit(2) 5648 5649 5650 if __name__ == '__main__': 5651 _main()