2016-10-06 13:21:30 +02:00
#!/usr/bin/env python
#
2021-01-11 13:20:02 +01:00
# ESP8266 & ESP32 family ROM Bootloader Utility
2021-05-31 15:32:51 +02:00
# Copyright (C) 2014-2021 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) CO LTD, other contributors as noted.
2017-01-05 12:54:40 +01:00
# https://github.com/espressif/esptool
2016-10-06 13:21:30 +02:00
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
2018-04-07 08:45:18 +02:00
from __future__ import division , print_function
2017-02-06 14:17:11 +01:00
2016-10-06 13:21:30 +02:00
import argparse
2018-04-07 08:45:18 +02:00
import base64
2018-09-21 08:39:36 +02:00
import binascii
2018-04-07 08:45:18 +02:00
import copy
2016-10-06 13:21:30 +02:00
import hashlib
import inspect
2018-04-07 08:45:18 +02:00
import io
2021-01-11 13:20:02 +01:00
import itertools
2016-10-06 13:21:30 +02:00
import os
2018-04-07 08:45:18 +02:00
import shlex
2021-01-11 13:20:02 +01:00
import string
2016-10-06 13:21:30 +02:00
import struct
import sys
import time
import zlib
2019-02-14 16:49:30 +01:00
try :
import serial
except ImportError :
print ( " Pyserial is not installed for %s . Check the README for installation instructions. " % ( sys . executable ) )
raise
2018-04-07 08:45:18 +02:00
2018-09-21 08:39:36 +02:00
# check 'serial' is 'pyserial' and not 'serial' https://github.com/espressif/esptool/issues/269
try :
if " serialization " in serial . __doc__ and " deserialization " in serial . __doc__ :
raise ImportError ( """
esptool . py depends on pyserial , but there is a conflict with a currently installed package named ' serial ' .
You may be able to work around this by ' pip uninstall serial; pip install pyserial ' \
but this may break other installed Python software that depends on ' serial ' .
There is no good fix for this right now , apart from configuring virtualenvs . \
See https : / / github . com / espressif / esptool / issues / 269 #issuecomment-385298196 for discussion of the underlying issue(s).""")
except TypeError :
pass # __doc__ returns None for pyserial
2019-02-14 16:49:30 +01:00
try :
import serial . tools . list_ports as list_ports
except ImportError :
print ( " The installed version ( %s ) of pyserial appears to be too old for esptool.py (Python interpreter %s ). "
" Check the README for installation instructions. " % ( sys . VERSION , sys . executable ) )
raise
2021-01-11 13:20:02 +01:00
except Exception :
if sys . platform == " darwin " :
# swallow the exception, this is a known issue in pyserial+macOS Big Sur preview ref https://github.com/espressif/esptool/issues/540
list_ports = None
else :
raise
2018-09-21 08:39:36 +02:00
2021-04-05 13:23:58 +02:00
__version__ = " 3.1-dev "
2016-10-06 13:21:30 +02:00
MAX_UINT32 = 0xffffffff
MAX_UINT24 = 0xffffff
2018-04-07 08:45:18 +02:00
DEFAULT_TIMEOUT = 3 # timeout for most flash operations
START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase)
CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase
MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run
SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader
MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum
ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region
2021-04-05 13:23:58 +02:00
ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data
2018-09-21 08:39:36 +02:00
MEM_END_ROM_TIMEOUT = 0.05 # special short timeout for ESP_MEM_END, as it may never respond
DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write
2021-01-11 13:20:02 +01:00
DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection
2018-04-07 08:45:18 +02:00
def timeout_per_mb ( seconds_per_mb , size_bytes ) :
""" Scales timeouts which are size-specific """
result = seconds_per_mb * ( size_bytes / 1e6 )
if result < DEFAULT_TIMEOUT :
return DEFAULT_TIMEOUT
return result
2017-08-01 07:51:04 +02:00
2021-04-05 13:23:58 +02:00
def _chip_to_rom_loader ( chip ) :
return {
' esp8266 ' : ESP8266ROM ,
' esp32 ' : ESP32ROM ,
' esp32s2 ' : ESP32S2ROM ,
' esp32s3beta2 ' : ESP32S3BETA2ROM ,
' esp32s3beta3 ' : ESP32S3BETA3ROM ,
' esp32c3 ' : ESP32C3ROM ,
2021-05-31 15:32:51 +02:00
' esp32c6beta ' : ESP32C6BETAROM ,
2021-04-05 13:23:58 +02:00
} [ chip ]
def get_default_connected_device ( serial_list , port , connect_attempts , initial_baud , chip = ' auto ' , trace = False ,
before = ' default_reset ' ) :
_esp = None
for each_port in reversed ( serial_list ) :
print ( " Serial port %s " % each_port )
try :
if chip == ' auto ' :
_esp = ESPLoader . detect_chip ( each_port , initial_baud , before , trace ,
connect_attempts )
else :
chip_class = _chip_to_rom_loader ( chip )
_esp = chip_class ( each_port , initial_baud , trace )
_esp . connect ( before , connect_attempts )
break
except ( FatalError , OSError ) as err :
if port is not None :
raise
print ( " %s failed to connect: %s " % ( each_port , err ) )
_esp = None
return _esp
2017-08-01 07:51:04 +02:00
DETECTED_FLASH_SIZES = { 0x12 : ' 256KB ' , 0x13 : ' 512KB ' , 0x14 : ' 1MB ' ,
2021-05-31 15:32:51 +02:00
0x15 : ' 2MB ' , 0x16 : ' 4MB ' , 0x17 : ' 8MB ' ,
0x18 : ' 16MB ' , 0x19 : ' 32MB ' , 0x1a : ' 64MB ' }
2017-08-01 07:51:04 +02:00
2016-10-06 13:21:30 +02:00
def check_supported_function ( func , check_func ) :
"""
Decorator implementation that wraps a check around an ESPLoader
bootloader function to check if it ' s supported.
This is used to capture the multidimensional differences in
2021-04-05 13:23:58 +02:00
functionality between the ESP8266 & ESP32 / 32 S2 / 32 S3 / 32 C3 ROM loaders , and the
2016-10-06 13:21:30 +02:00
software stub that runs on both . Not possible to do this cleanly
via inheritance alone .
"""
def inner ( * args , * * kwargs ) :
obj = args [ 0 ]
if check_func ( obj ) :
return func ( * args , * * kwargs )
else :
2016-12-30 00:28:30 +01:00
raise NotImplementedInROMError ( obj , func )
2016-10-06 13:21:30 +02:00
return inner
def stub_function_only ( func ) :
""" Attribute for a function only supported in the software stub loader """
return check_supported_function ( func , lambda o : o . IS_STUB )
def stub_and_esp32_function_only ( func ) :
2021-04-05 13:23:58 +02:00
""" Attribute for a function only supported by software stubs or ESP32/32S2/32S3/32C3 ROM """
2021-01-11 13:20:02 +01:00
return check_supported_function ( func , lambda o : o . IS_STUB or isinstance ( o , ESP32ROM ) )
2016-10-06 13:21:30 +02:00
2017-02-06 14:17:11 +01:00
PYTHON2 = sys . version_info [ 0 ] < 3 # True if on pre-Python 3
# Function to return nth byte of a bitstring
# Different behaviour on Python 2 vs 3
if PYTHON2 :
def byte ( bitstr , index ) :
return ord ( bitstr [ index ] )
else :
def byte ( bitstr , index ) :
return bitstr [ index ]
2018-04-07 08:45:18 +02:00
# Provide a 'basestring' class on Python 3
try :
basestring
except NameError :
basestring = str
2017-02-06 14:17:11 +01:00
2021-01-11 13:20:02 +01:00
def print_overwrite ( message , last_line = False ) :
""" Print a message, overwriting the currently printed line.
If last_line is False , don ' t append a newline at the end (expecting another subsequent call will overwrite this one.)
After a sequence of calls with last_line = False , call once with last_line = True .
If output is not a TTY ( for example redirected a pipe ) , no overwriting happens and this function is the same as print ( ) .
"""
if sys . stdout . isatty ( ) :
print ( " \r %s " % message , end = ' \n ' if last_line else ' ' )
else :
print ( message )
2019-10-01 10:21:44 +02:00
def _mask_to_shift ( mask ) :
""" Return the index of the least significant bit in the mask """
shift = 0
while mask & 0x1 == 0 :
shift + = 1
mask >> = 1
return shift
2016-10-06 13:21:30 +02:00
def esp8266_function_only ( func ) :
""" Attribute for a function only supported on ESP8266 """
return check_supported_function ( func , lambda o : o . CHIP_NAME == " ESP8266 " )
class ESPLoader ( object ) :
2018-09-21 08:39:36 +02:00
""" Base class providing access to ESP ROM & software stub bootloaders.
2016-10-06 13:21:30 +02:00
Subclasses provide ESP8266 & ESP32 specific functionality .
Don ' t instantiate this base class directly, either instantiate a subclass or
call ESPLoader . detect_chip ( ) which will interrogate the chip and return the
appropriate subclass instance .
"""
CHIP_NAME = " Espressif device "
IS_STUB = False
DEFAULT_PORT = " /dev/ttyUSB0 "
# Commands supported by ESP8266 ROM bootloader
ESP_FLASH_BEGIN = 0x02
ESP_FLASH_DATA = 0x03
ESP_FLASH_END = 0x04
ESP_MEM_BEGIN = 0x05
ESP_MEM_END = 0x06
ESP_MEM_DATA = 0x07
ESP_SYNC = 0x08
ESP_WRITE_REG = 0x09
ESP_READ_REG = 0x0a
# Some comands supported by ESP32 ROM bootloader (or -8266 w/ stub)
ESP_SPI_SET_PARAMS = 0x0B
ESP_SPI_ATTACH = 0x0D
2021-01-11 13:20:02 +01:00
ESP_READ_FLASH_SLOW = 0x0e # ROM only, much slower than the stub flash read
2016-10-06 13:21:30 +02:00
ESP_CHANGE_BAUDRATE = 0x0F
ESP_FLASH_DEFL_BEGIN = 0x10
ESP_FLASH_DEFL_DATA = 0x11
ESP_FLASH_DEFL_END = 0x12
ESP_SPI_FLASH_MD5 = 0x13
2021-05-31 15:32:51 +02:00
# Commands supported by ESP32-S2/S3/C3/C6 ROM bootloader only
2021-01-11 13:20:02 +01:00
ESP_GET_SECURITY_INFO = 0x14
2016-10-06 13:21:30 +02:00
# Some commands supported by stub only
ESP_ERASE_FLASH = 0xD0
ESP_ERASE_REGION = 0xD1
ESP_READ_FLASH = 0xD2
2017-01-05 12:54:40 +01:00
ESP_RUN_USER_CODE = 0xD3
2016-10-06 13:21:30 +02:00
2021-01-11 13:20:02 +01:00
# Flash encryption encrypted data command
2019-10-01 10:21:44 +02:00
ESP_FLASH_ENCRYPT_DATA = 0xD4
2021-01-11 13:20:02 +01:00
# Response code(s) sent by ROM
ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received
2016-10-06 13:21:30 +02:00
# Maximum block sized for RAM and Flash writes, respectively.
ESP_RAM_BLOCK = 0x1800
2016-12-30 00:28:30 +01:00
FLASH_WRITE_SIZE = 0x400
2016-10-06 13:21:30 +02:00
# Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want.
ESP_ROM_BAUD = 115200
# First byte of the application image
ESP_IMAGE_MAGIC = 0xe9
# Initial state for the checksum routine
ESP_CHECKSUM_MAGIC = 0xef
# Flash sector size, minimum unit of erase.
2016-12-30 00:28:30 +01:00
FLASH_SECTOR_SIZE = 0x1000
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
UART_DATE_REG_ADDR = 0x60000078
CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 # This ROM address has a different value on each chip model
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
UART_CLKDIV_MASK = 0xFFFFF
2016-10-06 13:21:30 +02:00
# Memory addresses
IROM_MAP_START = 0x40200000
IROM_MAP_END = 0x40300000
2016-10-28 00:53:02 +02:00
# The number of bytes in the UART response that signify command status
2016-10-06 13:21:30 +02:00
STATUS_BYTES_LENGTH = 2
2021-05-31 15:32:51 +02:00
# Response to ESP_SYNC might indicate that flasher stub is running instead of the ROM bootloader
sync_stub_detected = False
# Device PIDs
USB_JTAG_SERIAL_PID = 0x1001
2018-04-07 08:45:18 +02:00
def __init__ ( self , port = DEFAULT_PORT , baud = ESP_ROM_BAUD , trace_enabled = False ) :
2016-10-06 13:21:30 +02:00
""" Base constructor for ESPLoader bootloader interaction
Don ' t call this constructor, either instantiate ESP8266ROM
or ESP32ROM , or use ESPLoader . detect_chip ( ) .
This base class has all of the instance methods for bootloader
functionality supported across various chips & stub
loaders . Subclasses replace the functions they don ' t support
with ones which throw NotImplementedInROMError ( ) .
"""
2021-04-05 13:23:58 +02:00
self . secure_download_mode = False # flag is set to True if esptool detects the ROM is in Secure Download Mode
2018-04-07 08:45:18 +02:00
if isinstance ( port , basestring ) :
2017-01-05 12:54:40 +01:00
self . _port = serial . serial_for_url ( port )
2018-04-07 08:45:18 +02:00
else :
self . _port = port
self . _slip_reader = slip_reader ( self . _port , self . trace )
2016-10-06 13:21:30 +02:00
# setting baud rate in a separate step is a workaround for
# CH341 driver on some Linux versions (this opens at 9600 then
# sets), shouldn't matter for other platforms/drivers. See
2017-01-05 12:54:40 +01:00
# https://github.com/espressif/esptool/issues/44#issuecomment-107094446
2017-08-01 07:51:04 +02:00
self . _set_port_baudrate ( baud )
2018-04-07 08:45:18 +02:00
self . _trace_enabled = trace_enabled
2018-09-21 08:39:36 +02:00
# set write timeout, to prevent esptool blocked at write forever.
2019-02-14 16:49:30 +01:00
try :
self . _port . write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT
except NotImplementedError :
# no write timeout for RFC2217 ports
# need to set the property back to None or it will continue to fail
self . _port . write_timeout = None
2017-08-01 07:51:04 +02:00
2021-04-05 13:23:58 +02:00
@property
def serial_port ( self ) :
return self . _port . port
2017-08-01 07:51:04 +02:00
def _set_port_baudrate ( self , baud ) :
try :
self . _port . baudrate = baud
except IOError :
raise FatalError ( " Failed to set baud rate %d . The driver may not support this rate. " % baud )
2016-10-06 13:21:30 +02:00
@staticmethod
2021-01-11 13:20:02 +01:00
def detect_chip ( port = DEFAULT_PORT , baud = ESP_ROM_BAUD , connect_mode = ' default_reset ' , trace_enabled = False ,
connect_attempts = DEFAULT_CONNECT_ATTEMPTS ) :
2017-01-05 12:54:40 +01:00
""" Use serial access to detect the chip type.
2016-10-06 13:21:30 +02:00
We use the UART ' s datecode register for this, it ' s mapped at
the same address on ESP8266 & ESP32 so we can use one
memory read and compare to the datecode register for each chip
type .
2017-01-05 12:54:40 +01:00
This routine automatically performs ESPLoader . connect ( ) ( passing
connect_mode parameter ) as part of querying the chip .
2016-10-06 13:21:30 +02:00
"""
2018-04-07 08:45:18 +02:00
detect_port = ESPLoader ( port , baud , trace_enabled = trace_enabled )
2021-04-05 13:23:58 +02:00
detect_port . connect ( connect_mode , connect_attempts , detecting = True )
2018-11-26 23:22:11 +01:00
try :
print ( ' Detecting chip type... ' , end = ' ' )
sys . stdout . flush ( )
2021-04-05 13:23:58 +02:00
chip_magic_value = detect_port . read_reg ( ESPLoader . CHIP_DETECT_MAGIC_REG_ADDR )
2018-11-26 23:22:11 +01:00
2021-05-31 15:32:51 +02:00
for cls in [ ESP8266ROM , ESP32ROM , ESP32S2ROM , ESP32S3BETA2ROM , ESP32S3BETA3ROM , ESP32C3ROM , ESP32C6BETAROM ] :
2021-04-05 13:23:58 +02:00
if chip_magic_value in cls . CHIP_DETECT_MAGIC_VALUE :
2018-11-26 23:22:11 +01:00
# don't connect a second time
inst = cls ( detect_port . _port , baud , trace_enabled = trace_enabled )
2021-04-05 13:23:58 +02:00
inst . _post_connect ( )
2018-11-26 23:22:11 +01:00
print ( ' %s ' % inst . CHIP_NAME , end = ' ' )
2021-05-31 15:32:51 +02:00
if detect_port . sync_stub_detected :
inst = inst . STUB_CLASS ( inst )
inst . sync_stub_detected = True
2018-11-26 23:22:11 +01:00
return inst
2021-04-05 13:23:58 +02:00
except UnsupportedCommandError :
raise FatalError ( " Unsupported Command Error received. Probably this means Secure Download Mode is enabled, "
" autodetection will not work. Need to manually specify the chip. " )
2018-11-26 23:22:11 +01:00
finally :
print ( ' ' ) # end line
2021-04-05 13:23:58 +02:00
raise FatalError ( " Unexpected CHIP magic value 0x %08x . Failed to autodetect chip type. " % ( chip_magic_value ) )
2016-10-06 13:21:30 +02:00
""" Read a SLIP packet from the serial port """
def read ( self ) :
2017-02-06 14:17:11 +01:00
return next ( self . _slip_reader )
2016-10-06 13:21:30 +02:00
""" Write bytes to the serial port while performing SLIP escaping """
def write ( self , packet ) :
2017-02-06 14:17:11 +01:00
buf = b ' \xc0 ' \
2021-04-05 13:23:58 +02:00
+ ( packet . replace ( b ' \xdb ' , b ' \xdb \xdd ' ) . replace ( b ' \xc0 ' , b ' \xdb \xdc ' ) ) \
2017-02-06 14:17:11 +01:00
+ b ' \xc0 '
2018-09-21 08:39:36 +02:00
self . trace ( " Write %d bytes: %s " , len ( buf ) , HexFormatter ( buf ) )
2016-10-06 13:21:30 +02:00
self . _port . write ( buf )
2018-04-07 08:45:18 +02:00
def trace ( self , message , * format_args ) :
if self . _trace_enabled :
now = time . time ( )
try :
delta = now - self . _last_trace
except AttributeError :
delta = 0.0
self . _last_trace = now
prefix = " TRACE + %.3f " % delta
print ( prefix + ( message % format_args ) )
2016-10-06 13:21:30 +02:00
""" Calculate checksum of a blob, as it is defined by the ROM """
@staticmethod
def checksum ( data , state = ESP_CHECKSUM_MAGIC ) :
for b in data :
2017-02-06 14:17:11 +01:00
if type ( b ) is int : # python 2/3 compat
state ^ = b
else :
state ^ = ord ( b )
2016-10-06 13:21:30 +02:00
return state
""" Send a request and read the response """
2018-04-07 08:45:18 +02:00
def command ( self , op = None , data = b " " , chk = 0 , wait_response = True , timeout = DEFAULT_TIMEOUT ) :
saved_timeout = self . _port . timeout
new_timeout = min ( timeout , MAX_TIMEOUT )
if new_timeout != saved_timeout :
self . _port . timeout = new_timeout
2017-01-05 12:54:40 +01:00
2018-04-07 08:45:18 +02:00
try :
if op is not None :
2018-09-21 08:39:36 +02:00
self . trace ( " command op=0x %02x data len= %s wait_response= %d timeout= %.3f data= %s " ,
op , len ( data ) , 1 if wait_response else 0 , timeout , HexFormatter ( data ) )
2018-04-07 08:45:18 +02:00
pkt = struct . pack ( b ' <BBHI ' , 0x00 , op , len ( data ) , chk ) + data
self . write ( pkt )
if not wait_response :
return
# tries to get a response until that response has the
# same operation as the request or a retries limit has
# exceeded. This is needed for some esp8266s that
# reply with more sync responses than expected.
for retry in range ( 100 ) :
p = self . read ( )
if len ( p ) < 8 :
continue
( resp , op_ret , len_ret , val ) = struct . unpack ( ' <BBHI ' , p [ : 8 ] )
if resp != 1 :
continue
data = p [ 8 : ]
2021-01-11 13:20:02 +01:00
2018-04-07 08:45:18 +02:00
if op is None or op_ret == op :
return val , data
2021-01-11 13:20:02 +01:00
if byte ( data , 0 ) != 0 and byte ( data , 1 ) == self . ROM_INVALID_RECV_MSG :
2021-04-05 13:23:58 +02:00
self . flush_input ( ) # Unsupported read_reg can result in more than one error response for some reason
raise UnsupportedCommandError ( self , op )
2021-01-11 13:20:02 +01:00
2018-04-07 08:45:18 +02:00
finally :
if new_timeout != saved_timeout :
self . _port . timeout = saved_timeout
2016-10-06 13:21:30 +02:00
raise FatalError ( " Response doesn ' t match request " )
2018-04-07 08:45:18 +02:00
def check_command ( self , op_description , op = None , data = b ' ' , chk = 0 , timeout = DEFAULT_TIMEOUT ) :
2016-10-06 13:21:30 +02:00
"""
Execute a command with ' command ' , check the result code and throw an appropriate
FatalError if it fails .
Returns the " result " of a successful command .
"""
2018-04-07 08:45:18 +02:00
val , data = self . command ( op , data , chk , timeout = timeout )
2016-10-06 13:21:30 +02:00
# things are a bit weird here, bear with us
# the status bytes are the last 2/4 bytes in the data (depending on chip)
if len ( data ) < self . STATUS_BYTES_LENGTH :
raise FatalError ( " Failed to %s . Only got %d byte status response. " % ( op_description , len ( data ) ) )
status_bytes = data [ - self . STATUS_BYTES_LENGTH : ]
# we only care if the first one is non-zero. If it is, the second byte is a reason.
2017-02-06 14:17:11 +01:00
if byte ( status_bytes , 0 ) != 0 :
2016-10-06 13:21:30 +02:00
raise FatalError . WithResult ( ' Failed to %s ' % op_description , status_bytes )
# if we had more data than just the status bytes, return it as the result
# (this is used by the md5sum command, maybe other commands?)
if len ( data ) > self . STATUS_BYTES_LENGTH :
return data [ : - self . STATUS_BYTES_LENGTH ]
else : # otherwise, just return the 'val' field which comes from the reply header (this is used by read_reg)
return val
def flush_input ( self ) :
self . _port . flushInput ( )
2018-04-07 08:45:18 +02:00
self . _slip_reader = slip_reader ( self . _port , self . trace )
2016-10-06 13:21:30 +02:00
def sync ( self ) :
2021-05-31 15:32:51 +02:00
val , _ = self . command ( self . ESP_SYNC , b ' \x07 \x07 \x12 \x20 ' + 32 * b ' \x55 ' ,
timeout = SYNC_TIMEOUT )
# ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. If we receive 0 then it
# probably indicates that the chip wasn't or couldn't be reseted properly and esptool is talking to the
# flasher stub.
self . sync_stub_detected = val == 0
for _ in range ( 7 ) :
val , _ = self . command ( )
self . sync_stub_detected & = val == 0
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
def _setDTR ( self , state ) :
self . _port . setDTR ( state )
def _setRTS ( self , state ) :
self . _port . setRTS ( state )
# Work-around for adapters on Windows using the usbser.sys driver:
# generate a dummy change to DTR so that the set-control-line-state
# request is sent with the updated RTS state and the same DTR state
self . _port . setDTR ( self . _port . dtr )
2021-05-31 15:32:51 +02:00
def _get_pid ( self ) :
if list_ports is None :
print ( " \n Listing all serial ports is currently not available. Can ' t get device PID. " )
return
active_port = self . _port . port
2018-09-21 08:39:36 +02:00
2021-05-31 15:32:51 +02:00
# Pyserial only identifies regular ports, URL handlers are not supported
if not active_port . startswith ( ( " COM " , " /dev/ " ) ) :
print ( " \n Device PID identification is only supported on COM and /dev/ serial ports. " )
return
# Return the real path if the active port is a symlink
if active_port . startswith ( " /dev/ " ) and os . path . islink ( active_port ) :
active_port = os . path . realpath ( active_port )
ports = list_ports . comports ( )
for p in ports :
if p . device == active_port :
return p . pid
print ( " \n Failed to get PID of a device on {} , using standard reset sequence. " . format ( active_port ) )
def bootloader_reset ( self , esp32r0_delay = False , usb_jtag_serial = False ) :
""" Issue a reset-to-bootloader, with esp32r0 workaround options
and USB - JTAG - Serial custom reset sequence option
"""
# RTS = either CH_PD/EN or nRESET (both active low = chip in reset)
2017-03-10 14:40:14 +01:00
# DTR = GPIO0 (active low = boot to flasher)
#
# DTR & RTS are active low signals,
# ie True = pin @ 0V, False = pin @ VCC.
2021-05-31 15:32:51 +02:00
if usb_jtag_serial :
# Custom reset sequence, which is required when the device
# is connecting via its USB-JTAG-Serial peripheral
self . _setRTS ( False )
self . _setDTR ( False ) # Idle
time . sleep ( 0.1 )
self . _setDTR ( True ) # Set IO0
self . _setRTS ( False )
time . sleep ( 0.1 )
self . _setRTS ( True ) # Reset. Note dtr/rts calls inverted so we go through (1,1) instead of (0,0)
self . _setDTR ( False )
self . _setRTS ( True ) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting
time . sleep ( 0.1 )
self . _setDTR ( False )
self . _setRTS ( False )
else :
2018-09-21 08:39:36 +02:00
self . _setDTR ( False ) # IO0=HIGH
self . _setRTS ( True ) # EN=LOW, chip in reset
2017-03-10 14:40:14 +01:00
time . sleep ( 0.1 )
if esp32r0_delay :
# Some chips are more likely to trigger the esp32r0
# watchdog reset silicon bug if they're held with EN=LOW
# for a longer period
time . sleep ( 1.2 )
2018-09-21 08:39:36 +02:00
self . _setDTR ( True ) # IO0=LOW
self . _setRTS ( False ) # EN=HIGH, chip out of reset
2017-03-10 14:40:14 +01:00
if esp32r0_delay :
# Sleep longer after reset.
# This workaround only works on revision 0 ESP32 chips,
# it exploits a silicon bug spurious watchdog reset.
time . sleep ( 0.4 ) # allow watchdog reset to occur
time . sleep ( 0.05 )
2018-09-21 08:39:36 +02:00
self . _setDTR ( False ) # IO0=HIGH, done
2017-03-10 14:40:14 +01:00
2021-05-31 15:32:51 +02:00
def _connect_attempt ( self , mode = ' default_reset ' , esp32r0_delay = False , usb_jtag_serial = False ) :
""" A single connection attempt, with esp32r0 workaround options """
# esp32r0_delay is a workaround for bugs with the most common auto reset
# circuit and Windows, if the EN pin on the dev board does not have
# enough capacitance.
#
# Newer dev boards shouldn't have this problem (higher value capacitor
# on the EN pin), and ESP32 revision 1 can't use this workaround as it
# relies on a silicon bug.
#
# Details: https://github.com/espressif/esptool/issues/136
last_error = None
# If we're doing no_sync, we're likely communicating as a pass through
# with an intermediate device to the ESP32
if mode == " no_reset_no_sync " :
return last_error
if mode != ' no_reset ' :
self . bootloader_reset ( esp32r0_delay , usb_jtag_serial )
2017-03-10 14:40:14 +01:00
for _ in range ( 5 ) :
try :
self . flush_input ( )
self . _port . flushOutput ( )
self . sync ( )
return None
except FatalError as e :
if esp32r0_delay :
print ( ' _ ' , end = ' ' )
else :
print ( ' . ' , end = ' ' )
sys . stdout . flush ( )
time . sleep ( 0.05 )
last_error = e
return last_error
2021-04-05 13:23:58 +02:00
def get_memory_region ( self , name ) :
""" Returns a tuple of (start, end) for the memory map entry with the given name, or None if it doesn ' t exist
"""
try :
return [ ( start , end ) for ( start , end , n ) in self . MEMORY_MAP if n == name ] [ 0 ]
except IndexError :
return None
def connect ( self , mode = ' default_reset ' , attempts = DEFAULT_CONNECT_ATTEMPTS , detecting = False ) :
2016-10-06 13:21:30 +02:00
""" Try connecting repeatedly until successful, or giving up """
2021-05-31 15:32:51 +02:00
if mode in [ ' no_reset ' , ' no_reset_no_sync ' ] :
print ( ' WARNING: Pre-connection option " {} " was selected. ' . format ( mode ) ,
' Connection may fail if the chip is not in bootloader or flasher stub mode. ' )
2017-02-06 14:17:11 +01:00
print ( ' Connecting... ' , end = ' ' )
sys . stdout . flush ( )
last_error = None
2016-10-06 13:21:30 +02:00
2021-05-31 15:32:51 +02:00
usb_jtag_serial = ( mode == ' usb_reset ' ) or ( self . _get_pid ( ) == self . USB_JTAG_SERIAL_PID )
2017-02-06 14:17:11 +01:00
try :
2021-01-11 13:20:02 +01:00
for _ in range ( attempts ) if attempts > 0 else itertools . count ( ) :
2021-05-31 15:32:51 +02:00
last_error = self . _connect_attempt ( mode = mode , esp32r0_delay = False , usb_jtag_serial = usb_jtag_serial )
2017-03-10 14:40:14 +01:00
if last_error is None :
2021-04-05 13:23:58 +02:00
break
2021-05-31 15:32:51 +02:00
last_error = self . _connect_attempt ( mode = mode , esp32r0_delay = True , usb_jtag_serial = usb_jtag_serial )
2017-03-10 14:40:14 +01:00
if last_error is None :
2021-04-05 13:23:58 +02:00
break
2017-02-06 14:17:11 +01:00
finally :
print ( ' ' ) # end 'Connecting...' line
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
if last_error is not None :
raise FatalError ( ' Failed to connect to %s : %s ' % ( self . CHIP_NAME , last_error ) )
if not detecting :
try :
# check the date code registers match what we expect to see
chip_magic_value = self . read_reg ( ESPLoader . CHIP_DETECT_MAGIC_REG_ADDR )
if chip_magic_value not in self . CHIP_DETECT_MAGIC_VALUE :
actually = None
for cls in [ ESP8266ROM , ESP32ROM , ESP32S2ROM , ESP32S3BETA2ROM , ESP32S3BETA3ROM , ESP32C3ROM ] :
if chip_magic_value in cls . CHIP_DETECT_MAGIC_VALUE :
actually = cls
break
if actually is None :
print ( ( " WARNING: This chip doesn ' t appear to be a %s (chip magic value 0x %08x ). "
" Probably it is unsupported by this version of esptool. " ) % ( self . CHIP_NAME , chip_magic_value ) )
else :
raise FatalError ( " This chip is %s not %s . Wrong --chip argument? " % ( actually . CHIP_NAME , self . CHIP_NAME ) )
except UnsupportedCommandError :
self . secure_download_mode = True
self . _post_connect ( )
def _post_connect ( self ) :
"""
Additional initialization hook , may be overridden by the chip - specific class .
Gets called after connect , and after auto - detection .
"""
pass
def read_reg ( self , addr , timeout = DEFAULT_TIMEOUT ) :
2019-10-01 10:21:44 +02:00
""" Read memory address in target """
2016-10-06 13:21:30 +02:00
# we don't call check_command here because read_reg() function is called
# when detecting chip type, and the way we check for success (STATUS_BYTES_LENGTH) is different
# for different chip types (!)
2021-04-05 13:23:58 +02:00
val , data = self . command ( self . ESP_READ_REG , struct . pack ( ' <I ' , addr ) , timeout = timeout )
2017-02-06 14:17:11 +01:00
if byte ( data , 0 ) != 0 :
2016-10-06 13:21:30 +02:00
raise FatalError . WithResult ( " Failed to read register address %08x " % addr , data )
return val
2021-01-11 13:20:02 +01:00
""" Write to memory address in target """
def write_reg ( self , addr , value , mask = 0xFFFFFFFF , delay_us = 0 , delay_after_us = 0 ) :
command = struct . pack ( ' <IIII ' , addr , value , mask , delay_us )
if delay_after_us > 0 :
# add a dummy write to a date register as an excuse to have a delay
command + = struct . pack ( ' <IIII ' , self . UART_DATE_REG_ADDR , 0 , 0 , delay_after_us )
2019-10-01 10:21:44 +02:00
2021-01-11 13:20:02 +01:00
return self . check_command ( " write target memory " , self . ESP_WRITE_REG , command )
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
def update_reg ( self , addr , mask , new_val ) :
""" Update register at ' addr ' , replace the bits masked out by ' mask '
with new_val . new_val is shifted left to match the LSB of ' mask '
Returns just - written value of register .
"""
shift = _mask_to_shift ( mask )
val = self . read_reg ( addr )
val & = ~ mask
val | = ( new_val << shift ) & mask
self . write_reg ( addr , val )
return val
2016-10-06 13:21:30 +02:00
""" Start downloading an application image to RAM """
def mem_begin ( self , size , blocks , blocksize , offset ) :
2018-09-21 08:39:36 +02:00
if self . IS_STUB : # check we're not going to overwrite a running stub with this data
stub = self . STUB_CODE
load_start = offset
load_end = offset + size
for ( start , end ) in [ ( stub [ " data_start " ] , stub [ " data_start " ] + len ( stub [ " data " ] ) ) ,
( stub [ " text_start " ] , stub [ " text_start " ] + len ( stub [ " text " ] ) ) ] :
if load_start < end and load_end > start :
2021-04-05 13:23:58 +02:00
raise FatalError ( ( " Software loader is resident at 0x %08x -0x %08x . "
" Can ' t load binary at overlapping address range 0x %08x -0x %08x . "
" Either change binary loading address, or use the --no-stub "
2018-09-21 08:39:36 +02:00
" option to disable the software loader. " ) % ( start , end , load_start , load_end ) )
2016-10-06 13:21:30 +02:00
return self . check_command ( " enter RAM download mode " , self . ESP_MEM_BEGIN ,
struct . pack ( ' <IIII ' , size , blocks , blocksize , offset ) )
""" Send a block of an image to RAM """
def mem_block ( self , data , seq ) :
return self . check_command ( " write to target RAM " , self . ESP_MEM_DATA ,
struct . pack ( ' <IIII ' , len ( data ) , seq , 0 , 0 ) + data ,
self . checksum ( data ) )
""" Leave download mode and run the application """
def mem_finish ( self , entrypoint = 0 ) :
2018-09-21 08:39:36 +02:00
# Sending ESP_MEM_END usually sends a correct response back, however sometimes
# (with ROM loader) the executed code may reset the UART or change the baud rate
# before the transmit FIFO is empty. So in these cases we set a short timeout and
# ignore errors.
timeout = DEFAULT_TIMEOUT if self . IS_STUB else MEM_END_ROM_TIMEOUT
data = struct . pack ( ' <II ' , int ( entrypoint == 0 ) , entrypoint )
try :
return self . check_command ( " leave RAM download mode " , self . ESP_MEM_END ,
data = data , timeout = timeout )
except FatalError :
if self . IS_STUB :
raise
pass
2016-10-06 13:21:30 +02:00
2016-12-30 00:28:30 +01:00
""" Start downloading to Flash (performs an erase)
Returns number of blocks ( of size self . FLASH_WRITE_SIZE ) to write .
"""
2021-04-05 13:23:58 +02:00
def flash_begin ( self , size , offset , begin_rom_encrypted = False ) :
2017-02-06 14:17:11 +01:00
num_blocks = ( size + self . FLASH_WRITE_SIZE - 1 ) / / self . FLASH_WRITE_SIZE
2016-10-06 13:21:30 +02:00
erase_size = self . get_erase_size ( offset , size )
t = time . time ( )
2018-04-07 08:45:18 +02:00
if self . IS_STUB :
timeout = DEFAULT_TIMEOUT
else :
timeout = timeout_per_mb ( ERASE_REGION_TIMEOUT_PER_MB , size ) # ROM performs the erase up front
2021-01-11 13:20:02 +01:00
params = struct . pack ( ' <IIII ' , erase_size , num_blocks , self . FLASH_WRITE_SIZE , offset )
2021-05-31 15:32:51 +02:00
if isinstance ( self , ( ESP32S2ROM , ESP32S3BETA2ROM , ESP32S3BETA3ROM , ESP32C3ROM , ESP32C6BETAROM ) ) and not self . IS_STUB :
2021-04-05 13:23:58 +02:00
params + = struct . pack ( ' <I ' , 1 if begin_rom_encrypted else 0 )
2016-10-06 13:21:30 +02:00
self . check_command ( " enter Flash download mode " , self . ESP_FLASH_BEGIN ,
2021-01-11 13:20:02 +01:00
params , timeout = timeout )
2016-12-30 00:28:30 +01:00
if size != 0 and not self . IS_STUB :
2017-02-06 14:17:11 +01:00
print ( " Took %.2f s to erase flash block " % ( time . time ( ) - t ) )
2016-12-30 00:28:30 +01:00
return num_blocks
2016-10-06 13:21:30 +02:00
""" Write block to flash """
2018-04-07 08:45:18 +02:00
def flash_block ( self , data , seq , timeout = DEFAULT_TIMEOUT ) :
2016-10-06 13:21:30 +02:00
self . check_command ( " write to target Flash after seq %d " % seq ,
self . ESP_FLASH_DATA ,
struct . pack ( ' <IIII ' , len ( data ) , seq , 0 , 0 ) + data ,
2018-04-07 08:45:18 +02:00
self . checksum ( data ) ,
timeout = timeout )
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
""" Encrypt before writing to flash """
def flash_encrypt_block ( self , data , seq , timeout = DEFAULT_TIMEOUT ) :
2021-04-05 13:23:58 +02:00
if isinstance ( self , ( ESP32S2ROM , ESP32C3ROM ) ) and not self . IS_STUB :
# ROM support performs the encrypted writes via the normal write command,
# triggered by flash_begin(begin_rom_encrypted=True)
return self . flash_block ( data , seq , timeout )
2019-10-01 10:21:44 +02:00
self . check_command ( " Write encrypted to target Flash after seq %d " % seq ,
self . ESP_FLASH_ENCRYPT_DATA ,
struct . pack ( ' <IIII ' , len ( data ) , seq , 0 , 0 ) + data ,
self . checksum ( data ) ,
timeout = timeout )
2016-10-06 13:21:30 +02:00
""" Leave flash mode and run/reboot """
def flash_finish ( self , reboot = False ) :
pkt = struct . pack ( ' <I ' , int ( not reboot ) )
2017-01-05 12:54:40 +01:00
# stub sends a reply to this command
2016-10-06 13:21:30 +02:00
self . check_command ( " leave Flash mode " , self . ESP_FLASH_END , pkt )
""" Run application code in flash """
def run ( self , reboot = False ) :
# Fake flash begin immediately followed by flash end
self . flash_begin ( 0 , 0 )
self . flash_finish ( reboot )
""" Read SPI flash manufacturer and device id """
def flash_id ( self ) :
2016-10-28 00:53:02 +02:00
SPIFLASH_RDID = 0x9F
return self . run_spiflash_command ( SPIFLASH_RDID , b " " , 24 )
2016-10-06 13:21:30 +02:00
2021-01-11 13:20:02 +01:00
def get_security_info ( self ) :
# TODO: this only works on the ESP32S2 ROM code loader and needs to work in stub loader also
res = self . check_command ( ' get security info ' , self . ESP_GET_SECURITY_INFO , b ' ' )
res = struct . unpack ( " <IBBBBBBBB " , res )
flags , flash_crypt_cnt , key_purposes = res [ 0 ] , res [ 1 ] , res [ 2 : ]
# TODO: pack this as some kind of better data type
return ( flags , flash_crypt_cnt , key_purposes )
2021-04-05 13:23:58 +02:00
@classmethod
def parse_flash_size_arg ( cls , arg ) :
2016-10-06 13:21:30 +02:00
try :
2021-04-05 13:23:58 +02:00
return cls . FLASH_SIZES [ arg ]
2016-10-06 13:21:30 +02:00
except KeyError :
raise FatalError ( " Flash size ' %s ' is not supported by this chip type. Supported sizes: %s "
2021-04-05 13:23:58 +02:00
% ( arg , " , " . join ( cls . FLASH_SIZES . keys ( ) ) ) )
2016-10-06 13:21:30 +02:00
def run_stub ( self , stub = None ) :
if stub is None :
stub = self . STUB_CODE
2021-05-31 15:32:51 +02:00
if self . sync_stub_detected :
print ( " Stub is already running. No upload is necessary. " )
return self . STUB_CLASS ( self )
2016-10-06 13:21:30 +02:00
# Upload
2017-02-06 14:17:11 +01:00
print ( " Uploading stub... " )
2016-10-06 13:21:30 +02:00
for field in [ ' text ' , ' data ' ] :
if field in stub :
offs = stub [ field + " _start " ]
length = len ( stub [ field ] )
2017-02-06 14:17:11 +01:00
blocks = ( length + self . ESP_RAM_BLOCK - 1 ) / / self . ESP_RAM_BLOCK
2016-10-06 13:21:30 +02:00
self . mem_begin ( length , blocks , self . ESP_RAM_BLOCK , offs )
for seq in range ( blocks ) :
from_offs = seq * self . ESP_RAM_BLOCK
to_offs = from_offs + self . ESP_RAM_BLOCK
self . mem_block ( stub [ field ] [ from_offs : to_offs ] , seq )
2017-02-06 14:17:11 +01:00
print ( " Running stub... " )
2016-10-06 13:21:30 +02:00
self . mem_finish ( stub [ ' entry ' ] )
p = self . read ( )
2017-02-06 14:17:11 +01:00
if p != b ' OHAI ' :
2016-10-06 13:21:30 +02:00
raise FatalError ( " Failed to start stub. Unexpected response: %s " % p )
2017-02-06 14:17:11 +01:00
print ( " Stub running... " )
2016-10-06 13:21:30 +02:00
return self . STUB_CLASS ( self )
@stub_and_esp32_function_only
def flash_defl_begin ( self , size , compsize , offset ) :
2016-12-30 00:28:30 +01:00
""" Start downloading compressed data to Flash (performs an erase)
Returns number of blocks ( size self . FLASH_WRITE_SIZE ) to write .
"""
2017-02-06 14:17:11 +01:00
num_blocks = ( compsize + self . FLASH_WRITE_SIZE - 1 ) / / self . FLASH_WRITE_SIZE
erase_blocks = ( size + self . FLASH_WRITE_SIZE - 1 ) / / self . FLASH_WRITE_SIZE
2016-10-06 13:21:30 +02:00
t = time . time ( )
2017-02-06 14:17:11 +01:00
if self . IS_STUB :
write_size = size # stub expects number of bytes here, manages erasing internally
2018-04-07 08:45:18 +02:00
timeout = DEFAULT_TIMEOUT
2017-02-06 14:17:11 +01:00
else :
write_size = erase_blocks * self . FLASH_WRITE_SIZE # ROM expects rounded up to erase block size
2018-04-07 08:45:18 +02:00
timeout = timeout_per_mb ( ERASE_REGION_TIMEOUT_PER_MB , write_size ) # ROM performs the erase up front
2017-02-06 14:17:11 +01:00
print ( " Compressed %d bytes to %d ... " % ( size , compsize ) )
2021-01-11 13:20:02 +01:00
params = struct . pack ( ' <IIII ' , write_size , num_blocks , self . FLASH_WRITE_SIZE , offset )
2021-05-31 15:32:51 +02:00
if isinstance ( self , ( ESP32S2ROM , ESP32S3BETA2ROM , ESP32S3BETA3ROM , ESP32C3ROM , ESP32C6BETAROM ) ) and not self . IS_STUB :
2021-04-05 13:23:58 +02:00
params + = struct . pack ( ' <I ' , 0 ) # extra param is to enter encrypted flash mode via ROM (not supported currently)
2021-01-11 13:20:02 +01:00
self . check_command ( " enter compressed flash mode " , self . ESP_FLASH_DEFL_BEGIN , params , timeout = timeout )
2016-10-06 13:21:30 +02:00
if size != 0 and not self . IS_STUB :
# (stub erases as it writes, but ROM loaders erase on begin)
2017-02-06 14:17:11 +01:00
print ( " Took %.2f s to erase flash block " % ( time . time ( ) - t ) )
2016-12-30 00:28:30 +01:00
return num_blocks
2016-10-06 13:21:30 +02:00
""" Write block to flash, send compressed """
@stub_and_esp32_function_only
2018-04-07 08:45:18 +02:00
def flash_defl_block ( self , data , seq , timeout = DEFAULT_TIMEOUT ) :
2016-10-06 13:21:30 +02:00
self . check_command ( " write compressed data to flash after seq %d " % seq ,
2018-04-07 08:45:18 +02:00
self . ESP_FLASH_DEFL_DATA , struct . pack ( ' <IIII ' , len ( data ) , seq , 0 , 0 ) + data , self . checksum ( data ) , timeout = timeout )
2016-10-06 13:21:30 +02:00
""" Leave compressed flash mode and run/reboot """
@stub_and_esp32_function_only
def flash_defl_finish ( self , reboot = False ) :
2017-01-05 12:54:40 +01:00
if not reboot and not self . IS_STUB :
# skip sending flash_finish to ROM loader, as this
# exits the bootloader. Stub doesn't do this.
return
2016-10-06 13:21:30 +02:00
pkt = struct . pack ( ' <I ' , int ( not reboot ) )
self . check_command ( " leave compressed flash mode " , self . ESP_FLASH_DEFL_END , pkt )
self . in_bootloader = False
@stub_and_esp32_function_only
def flash_md5sum ( self , addr , size ) :
# the MD5 command returns additional bytes in the standard
# command reply slot
2018-04-07 08:45:18 +02:00
timeout = timeout_per_mb ( MD5_TIMEOUT_PER_MB , size )
res = self . check_command ( ' calculate md5sum ' , self . ESP_SPI_FLASH_MD5 , struct . pack ( ' <IIII ' , addr , size , 0 , 0 ) ,
timeout = timeout )
2016-10-06 13:21:30 +02:00
if len ( res ) == 32 :
2017-02-06 14:17:11 +01:00
return res . decode ( " utf-8 " ) # already hex formatted
2016-10-06 13:21:30 +02:00
elif len ( res ) == 16 :
return hexify ( res ) . lower ( )
else :
raise FatalError ( " MD5Sum command returned unexpected result: %r " % res )
@stub_and_esp32_function_only
def change_baud ( self , baud ) :
2017-02-06 14:17:11 +01:00
print ( " Changing baud rate to %d " % baud )
2018-04-07 08:45:18 +02:00
# stub takes the new baud rate and the old one
second_arg = self . _port . baudrate if self . IS_STUB else 0
self . command ( self . ESP_CHANGE_BAUDRATE , struct . pack ( ' <II ' , baud , second_arg ) )
2017-02-06 14:17:11 +01:00
print ( " Changed. " )
2017-08-01 07:51:04 +02:00
self . _set_port_baudrate ( baud )
2016-10-06 13:21:30 +02:00
time . sleep ( 0.05 ) # get rid of crap sent during baud rate change
self . flush_input ( )
@stub_function_only
def erase_flash ( self ) :
# depending on flash chip model the erase may take this long (maybe longer!)
2018-04-07 08:45:18 +02:00
self . check_command ( " erase flash " , self . ESP_ERASE_FLASH ,
timeout = CHIP_ERASE_TIMEOUT )
2016-10-06 13:21:30 +02:00
@stub_function_only
def erase_region ( self , offset , size ) :
2016-12-30 00:28:30 +01:00
if offset % self . FLASH_SECTOR_SIZE != 0 :
2016-10-06 13:21:30 +02:00
raise FatalError ( " Offset to erase from must be a multiple of 4096 " )
2016-12-30 00:28:30 +01:00
if size % self . FLASH_SECTOR_SIZE != 0 :
2016-10-06 13:21:30 +02:00
raise FatalError ( " Size of data to erase must be a multiple of 4096 " )
2018-04-07 08:45:18 +02:00
timeout = timeout_per_mb ( ERASE_REGION_TIMEOUT_PER_MB , size )
self . check_command ( " erase region " , self . ESP_ERASE_REGION , struct . pack ( ' <II ' , offset , size ) , timeout = timeout )
2016-10-06 13:21:30 +02:00
2021-01-11 13:20:02 +01:00
def read_flash_slow ( self , offset , length , progress_fn ) :
raise NotImplementedInROMError ( self , self . read_flash_slow )
2016-10-06 13:21:30 +02:00
def read_flash ( self , offset , length , progress_fn = None ) :
2021-01-11 13:20:02 +01:00
if not self . IS_STUB :
return self . read_flash_slow ( offset , length , progress_fn ) # ROM-only routine
2016-10-06 13:21:30 +02:00
# issue a standard bootloader command to trigger the read
self . check_command ( " read flash " , self . ESP_READ_FLASH ,
struct . pack ( ' <IIII ' ,
offset ,
length ,
2016-12-30 00:28:30 +01:00
self . FLASH_SECTOR_SIZE ,
2016-10-06 13:21:30 +02:00
64 ) )
2017-02-06 14:17:11 +01:00
# now we expect (length // block_size) SLIP frames with the data
data = b ' '
2016-10-06 13:21:30 +02:00
while len ( data ) < length :
p = self . read ( )
data + = p
2018-11-26 23:22:11 +01:00
if len ( data ) < length and len ( p ) < self . FLASH_SECTOR_SIZE :
raise FatalError ( ' Corrupt data, expected 0x %x bytes but received 0x %x bytes ' % ( self . FLASH_SECTOR_SIZE , len ( p ) ) )
2016-10-06 13:21:30 +02:00
self . write ( struct . pack ( ' <I ' , len ( data ) ) )
if progress_fn and ( len ( data ) % 1024 == 0 or len ( data ) == length ) :
progress_fn ( len ( data ) , length )
if progress_fn :
progress_fn ( len ( data ) , length )
if len ( data ) > length :
raise FatalError ( ' Read more than expected ' )
2019-10-01 10:21:44 +02:00
2016-10-06 13:21:30 +02:00
digest_frame = self . read ( )
if len ( digest_frame ) != 16 :
raise FatalError ( ' Expected digest, got: %s ' % hexify ( digest_frame ) )
expected_digest = hexify ( digest_frame ) . upper ( )
digest = hashlib . md5 ( data ) . hexdigest ( ) . upper ( )
if digest != expected_digest :
raise FatalError ( ' Digest mismatch: expected %s , got %s ' % ( expected_digest , digest ) )
return data
2017-05-06 19:29:12 +02:00
def flash_spi_attach ( self , hspi_arg ) :
2016-10-06 13:21:30 +02:00
""" Send SPI attach command to enable the SPI flash pins
ESP8266 ROM does this when you send flash_begin , ESP32 ROM
has it as a SPI command .
"""
# last 3 bytes in ESP_SPI_ATTACH argument are reserved values
2017-05-06 19:29:12 +02:00
arg = struct . pack ( ' <I ' , hspi_arg )
if not self . IS_STUB :
# ESP32 ROM loader takes additional 'is legacy' arg, which is not
# currently supported in the stub loader or esptool.py (as it's not usually needed.)
is_legacy = 0
arg + = struct . pack ( ' BBBB ' , is_legacy , 0 , 0 , 0 )
2016-10-06 13:21:30 +02:00
self . check_command ( " configure SPI flash pins " , ESP32ROM . ESP_SPI_ATTACH , arg )
def flash_set_parameters ( self , size ) :
""" Tell the ESP bootloader the parameters of the chip
Corresponds to the " flashchip " data structure that the ROM
has in RAM .
' size ' is in bytes .
All other flash parameters are currently hardcoded ( on ESP8266
these are mostly ignored by ROM code , on ESP32 I ' m not sure.)
"""
fl_id = 0
total_size = size
block_size = 64 * 1024
sector_size = 4 * 1024
page_size = 256
status_mask = 0xffff
self . check_command ( " set SPI params " , ESP32ROM . ESP_SPI_SET_PARAMS ,
struct . pack ( ' <IIIIII ' , fl_id , total_size , block_size , sector_size , page_size , status_mask ) )
2016-10-28 00:53:02 +02:00
def run_spiflash_command ( self , spiflash_command , data = b " " , read_bits = 0 ) :
""" Run an arbitrary SPI flash command.
This function uses the " USR_COMMAND " functionality in the ESP
SPI hardware , rather than the precanned commands supported by
hardware . So the value of spiflash_command is an actual command
byte , sent over the wire .
After writing command byte , writes ' data ' to MOSI and then
reads back ' read_bits ' of reply on MISO . Result is a number .
"""
# SPI_USR register flags
SPI_USR_COMMAND = ( 1 << 31 )
SPI_USR_MISO = ( 1 << 28 )
SPI_USR_MOSI = ( 1 << 27 )
2021-01-11 13:20:02 +01:00
# SPI registers, base address differs ESP32* vs 8266
2016-10-28 00:53:02 +02:00
base = self . SPI_REG_BASE
SPI_CMD_REG = base + 0x00
2021-01-11 13:20:02 +01:00
SPI_USR_REG = base + self . SPI_USR_OFFS
SPI_USR1_REG = base + self . SPI_USR1_OFFS
SPI_USR2_REG = base + self . SPI_USR2_OFFS
2016-10-28 00:53:02 +02:00
SPI_W0_REG = base + self . SPI_W0_OFFS
2021-04-05 13:23:58 +02:00
# following two registers are ESP32 & 32S2/32C3 only
2021-01-11 13:20:02 +01:00
if self . SPI_MOSI_DLEN_OFFS is not None :
2021-04-05 13:23:58 +02:00
# ESP32/32S2/32C3 has a more sophisticated way to set up "user" commands
2016-10-28 00:53:02 +02:00
def set_data_lengths ( mosi_bits , miso_bits ) :
2021-01-11 13:20:02 +01:00
SPI_MOSI_DLEN_REG = base + self . SPI_MOSI_DLEN_OFFS
SPI_MISO_DLEN_REG = base + self . SPI_MISO_DLEN_OFFS
2016-10-28 00:53:02 +02:00
if mosi_bits > 0 :
self . write_reg ( SPI_MOSI_DLEN_REG , mosi_bits - 1 )
if miso_bits > 0 :
self . write_reg ( SPI_MISO_DLEN_REG , miso_bits - 1 )
else :
def set_data_lengths ( mosi_bits , miso_bits ) :
SPI_DATA_LEN_REG = SPI_USR1_REG
SPI_MOSI_BITLEN_S = 17
SPI_MISO_BITLEN_S = 8
mosi_mask = 0 if ( mosi_bits == 0 ) else ( mosi_bits - 1 )
miso_mask = 0 if ( miso_bits == 0 ) else ( miso_bits - 1 )
self . write_reg ( SPI_DATA_LEN_REG ,
( miso_mask << SPI_MISO_BITLEN_S ) | (
mosi_mask << SPI_MOSI_BITLEN_S ) )
# SPI peripheral "command" bitmasks for SPI_CMD_REG
SPI_CMD_USR = ( 1 << 18 )
# shift values
2021-01-11 13:20:02 +01:00
SPI_USR2_COMMAND_LEN_SHIFT = 28
2016-10-28 00:53:02 +02:00
if read_bits > 32 :
raise FatalError ( " Reading more than 32 bits back from a SPI flash operation is unsupported " )
if len ( data ) > 64 :
raise FatalError ( " Writing more than 64 bytes of data with one SPI command is unsupported " )
data_bits = len ( data ) * 8
2017-01-05 12:54:40 +01:00
old_spi_usr = self . read_reg ( SPI_USR_REG )
old_spi_usr2 = self . read_reg ( SPI_USR2_REG )
2016-10-28 00:53:02 +02:00
flags = SPI_USR_COMMAND
if read_bits > 0 :
flags | = SPI_USR_MISO
if data_bits > 0 :
flags | = SPI_USR_MOSI
set_data_lengths ( data_bits , read_bits )
self . write_reg ( SPI_USR_REG , flags )
self . write_reg ( SPI_USR2_REG ,
2021-01-11 13:20:02 +01:00
( 7 << SPI_USR2_COMMAND_LEN_SHIFT ) | spiflash_command )
2016-10-28 00:53:02 +02:00
if data_bits == 0 :
self . write_reg ( SPI_W0_REG , 0 ) # clear data register before we read it
else :
2017-03-10 14:40:14 +01:00
data = pad_to ( data , 4 , b ' \00 ' ) # pad to 32-bit multiple
2017-02-06 14:17:11 +01:00
words = struct . unpack ( " I " * ( len ( data ) / / 4 ) , data )
2016-10-28 00:53:02 +02:00
next_reg = SPI_W0_REG
for word in words :
self . write_reg ( next_reg , word )
next_reg + = 4
self . write_reg ( SPI_CMD_REG , SPI_CMD_USR )
def wait_done ( ) :
2017-02-06 14:17:11 +01:00
for _ in range ( 10 ) :
2016-10-28 00:53:02 +02:00
if ( self . read_reg ( SPI_CMD_REG ) & SPI_CMD_USR ) == 0 :
return
raise FatalError ( " SPI command did not complete in time " )
wait_done ( )
status = self . read_reg ( SPI_W0_REG )
2017-01-05 12:54:40 +01:00
# restore some SPI controller registers
self . write_reg ( SPI_USR_REG , old_spi_usr )
self . write_reg ( SPI_USR2_REG , old_spi_usr2 )
2016-10-28 00:53:02 +02:00
return status
def read_status ( self , num_bytes = 2 ) :
""" Read up to 24 bits (num_bytes) of SPI flash status register contents
via RDSR , RDSR2 , RDSR3 commands
Not all SPI flash supports all three commands . The upper 1 or 2
bytes may be 0xFF .
"""
SPIFLASH_RDSR = 0x05
SPIFLASH_RDSR2 = 0x35
SPIFLASH_RDSR3 = 0x15
status = 0
shift = 0
for cmd in [ SPIFLASH_RDSR , SPIFLASH_RDSR2 , SPIFLASH_RDSR3 ] [ 0 : num_bytes ] :
status + = self . run_spiflash_command ( cmd , read_bits = 8 ) << shift
shift + = 8
return status
def write_status ( self , new_status , num_bytes = 2 , set_non_volatile = False ) :
""" Write up to 24 bits (num_bytes) of new status register
num_bytes can be 1 , 2 or 3.
Not all flash supports the additional commands to write the
second and third byte of the status register . When writing 2
bytes , esptool also sends a 16 - byte WRSR command ( as some
flash types use this instead of WRSR2 . )
If the set_non_volatile flag is set , non - volatile bits will
be set as well as volatile ones ( WREN used instead of WEVSR ) .
"""
SPIFLASH_WRSR = 0x01
SPIFLASH_WRSR2 = 0x31
SPIFLASH_WRSR3 = 0x11
SPIFLASH_WEVSR = 0x50
SPIFLASH_WREN = 0x06
SPIFLASH_WRDI = 0x04
enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR
# try using a 16-bit WRSR (not supported by all chips)
# this may be redundant, but shouldn't hurt
if num_bytes == 2 :
self . run_spiflash_command ( enable_cmd )
self . run_spiflash_command ( SPIFLASH_WRSR , struct . pack ( " <H " , new_status ) )
# also try using individual commands (also not supported by all chips for num_bytes 2 & 3)
for cmd in [ SPIFLASH_WRSR , SPIFLASH_WRSR2 , SPIFLASH_WRSR3 ] [ 0 : num_bytes ] :
self . run_spiflash_command ( enable_cmd )
self . run_spiflash_command ( cmd , struct . pack ( " B " , new_status & 0xFF ) )
new_status >> = 8
self . run_spiflash_command ( SPIFLASH_WRDI )
2019-10-01 10:21:44 +02:00
def get_crystal_freq ( self ) :
# Figure out the crystal frequency from the UART clock divider
# Returns a normalized value in integer MHz (40 or 26 are the only supported values)
#
# The logic here is:
# - We know that our baud rate and the ESP UART baud rate are roughly the same, or we couldn't communicate
# - We can read the UART clock divider register to know how the ESP derives this from the APB bus frequency
# - Multiplying these two together gives us the bus frequency which is either the crystal frequency (ESP32)
# or double the crystal frequency (ESP8266). See the self.XTAL_CLK_DIVIDER parameter for this factor.
uart_div = self . read_reg ( self . UART_CLKDIV_REG ) & self . UART_CLKDIV_MASK
est_xtal = ( self . _port . baudrate * uart_div ) / 1e6 / self . XTAL_CLK_DIVIDER
norm_xtal = 40 if est_xtal > 33 else 26
if abs ( norm_xtal - est_xtal ) > 1 :
print ( " WARNING: Detected crystal freq %.2f MHz is quite different to normalized freq %d MHz. Unsupported crystal in use? " % ( est_xtal , norm_xtal ) )
return norm_xtal
2017-01-05 12:54:40 +01:00
def hard_reset ( self ) :
2021-05-31 15:32:51 +02:00
print ( ' Hard resetting via RTS pin... ' )
2018-09-21 08:39:36 +02:00
self . _setRTS ( True ) # EN->LOW
2017-01-05 12:54:40 +01:00
time . sleep ( 0.1 )
2018-09-21 08:39:36 +02:00
self . _setRTS ( False )
2017-01-05 12:54:40 +01:00
def soft_reset ( self , stay_in_bootloader ) :
if not self . IS_STUB :
if stay_in_bootloader :
return # ROM bootloader is already in bootloader!
else :
# 'run user code' is as close to a soft reset as we can do
self . flash_begin ( 0 , 0 )
self . flash_finish ( False )
else :
if stay_in_bootloader :
# soft resetting from the stub loader
# will re-load the ROM bootloader
self . flash_begin ( 0 , 0 )
self . flash_finish ( True )
elif self . CHIP_NAME != " ESP8266 " :
raise FatalError ( " Soft resetting is currently only supported on ESP8266 " )
else :
# running user code from stub loader requires some hacks
# in the stub loader
self . command ( self . ESP_RUN_USER_CODE , wait_response = False )
2016-10-06 13:21:30 +02:00
class ESP8266ROM ( ESPLoader ) :
""" Access class for ESP8266 ROM bootloader
"""
CHIP_NAME = " ESP8266 "
IS_STUB = False
2021-04-05 13:23:58 +02:00
CHIP_DETECT_MAGIC_VALUE = [ 0xfff0c101 ]
2016-10-06 13:21:30 +02:00
# OTP ROM addresses
ESP_OTP_MAC0 = 0x3ff00050
ESP_OTP_MAC1 = 0x3ff00054
ESP_OTP_MAC3 = 0x3ff0005c
2016-10-28 00:53:02 +02:00
SPI_REG_BASE = 0x60000200
2021-01-11 13:20:02 +01:00
SPI_USR_OFFS = 0x1c
SPI_USR1_OFFS = 0x20
SPI_USR2_OFFS = 0x24
SPI_MOSI_DLEN_OFFS = None
SPI_MISO_DLEN_OFFS = None
2016-10-28 00:53:02 +02:00
SPI_W0_OFFS = 0x40
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
UART_CLKDIV_REG = 0x60000014
XTAL_CLK_DIVIDER = 2
2016-10-06 13:21:30 +02:00
FLASH_SIZES = {
2021-04-05 13:23:58 +02:00
' 512KB ' : 0x00 ,
' 256KB ' : 0x10 ,
' 1MB ' : 0x20 ,
' 2MB ' : 0x30 ,
' 4MB ' : 0x40 ,
2016-10-06 13:21:30 +02:00
' 2MB-c1 ' : 0x50 ,
2021-04-05 13:23:58 +02:00
' 4MB-c1 ' : 0x60 ,
' 8MB ' : 0x80 ,
' 16MB ' : 0x90 ,
2017-08-01 07:51:04 +02:00
}
2016-10-06 13:21:30 +02:00
2017-08-01 07:51:04 +02:00
BOOTLOADER_FLASH_OFFSET = 0
2019-10-01 10:21:44 +02:00
MEMORY_MAP = [ [ 0x3FF00000 , 0x3FF00010 , " DPORT " ] ,
[ 0x3FFE8000 , 0x40000000 , " DRAM " ] ,
[ 0x40100000 , 0x40108000 , " IRAM " ] ,
[ 0x40201010 , 0x402E1010 , " IROM " ] ]
2018-04-07 08:45:18 +02:00
def get_efuses ( self ) :
# Return the 128 bits of ESP8266 efuse as a single Python integer
2021-04-05 13:23:58 +02:00
result = self . read_reg ( 0x3ff0005c ) << 96
result | = self . read_reg ( 0x3ff00058 ) << 64
result | = self . read_reg ( 0x3ff00054 ) << 32
result | = self . read_reg ( 0x3ff00050 )
return result
def _get_flash_size ( self , efuses ) :
# rX_Y = EFUSE_DATA_OUTX[Y]
r0_4 = ( efuses & ( 1 << 4 ) ) != 0
r3_25 = ( efuses & ( 1 << 121 ) ) != 0
r3_26 = ( efuses & ( 1 << 122 ) ) != 0
r3_27 = ( efuses & ( 1 << 123 ) ) != 0
if r0_4 and not r3_25 :
if not r3_27 and not r3_26 :
return 1
elif not r3_27 and r3_26 :
return 2
if not r0_4 and r3_25 :
if not r3_27 and not r3_26 :
return 2
elif not r3_27 and r3_26 :
return 4
return - 1
2018-04-07 08:45:18 +02:00
2017-08-01 07:51:04 +02:00
def get_chip_description ( self ) :
2018-04-07 08:45:18 +02:00
efuses = self . get_efuses ( )
is_8285 = ( efuses & ( ( 1 << 4 ) | 1 << 80 ) ) != 0 # One or the other efuse bit is set for ESP8285
2021-04-05 13:23:58 +02:00
if is_8285 :
flash_size = self . _get_flash_size ( efuses )
max_temp = ( efuses & ( 1 << 5 ) ) != 0 # This efuse bit identifies the max flash temperature
chip_name = {
1 : " ESP8285H08 " if max_temp else " ESP8285N08 " ,
2 : " ESP8285H16 " if max_temp else " ESP8285N16 "
} . get ( flash_size , " ESP8285 " )
return chip_name
return " ESP8266EX "
2018-04-07 08:45:18 +02:00
def get_chip_features ( self ) :
2018-09-21 08:39:36 +02:00
features = [ " WiFi " ]
2021-04-05 13:23:58 +02:00
if " ESP8285 " in self . get_chip_description ( ) :
2018-04-07 08:45:18 +02:00
features + = [ " Embedded Flash " ]
return features
2017-01-05 12:54:40 +01:00
2017-05-06 19:29:12 +02:00
def flash_spi_attach ( self , hspi_arg ) :
2017-01-05 12:54:40 +01:00
if self . IS_STUB :
2017-05-06 19:29:12 +02:00
super ( ESP8266ROM , self ) . flash_spi_attach ( hspi_arg )
2017-01-05 12:54:40 +01:00
else :
# ESP8266 ROM has no flash_spi_attach command in serial protocol,
# but flash_begin will do it
self . flash_begin ( 0 , 0 )
2016-10-06 13:21:30 +02:00
def flash_set_parameters ( self , size ) :
2017-01-05 12:54:40 +01:00
# not implemented in ROM, but OK to silently skip for ROM
if self . IS_STUB :
super ( ESP8266ROM , self ) . flash_set_parameters ( size )
2016-10-06 13:21:30 +02:00
def chip_id ( self ) :
2018-09-21 08:39:36 +02:00
""" Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() function """
2016-10-06 13:21:30 +02:00
id0 = self . read_reg ( self . ESP_OTP_MAC0 )
id1 = self . read_reg ( self . ESP_OTP_MAC1 )
return ( id0 >> 24 ) | ( ( id1 & MAX_UINT24 ) << 8 )
def read_mac ( self ) :
""" Read MAC from OTP ROM """
mac0 = self . read_reg ( self . ESP_OTP_MAC0 )
mac1 = self . read_reg ( self . ESP_OTP_MAC1 )
mac3 = self . read_reg ( self . ESP_OTP_MAC3 )
if ( mac3 != 0 ) :
oui = ( ( mac3 >> 16 ) & 0xff , ( mac3 >> 8 ) & 0xff , mac3 & 0xff )
elif ( ( mac1 >> 16 ) & 0xff ) == 0 :
oui = ( 0x18 , 0xfe , 0x34 )
elif ( ( mac1 >> 16 ) & 0xff ) == 1 :
oui = ( 0xac , 0xd0 , 0x74 )
else :
raise FatalError ( " Unknown OUI " )
return oui + ( ( mac1 >> 8 ) & 0xff , mac1 & 0xff , ( mac0 >> 24 ) & 0xff )
def get_erase_size ( self , offset , size ) :
""" Calculate an erase size given a specific size in bytes.
Provides a workaround for the bootloader erase bug . """
sectors_per_block = 16
2016-12-30 00:28:30 +01:00
sector_size = self . FLASH_SECTOR_SIZE
2017-02-06 14:17:11 +01:00
num_sectors = ( size + sector_size - 1 ) / / sector_size
start_sector = offset / / sector_size
2016-10-06 13:21:30 +02:00
head_sectors = sectors_per_block - ( start_sector % sectors_per_block )
if num_sectors < head_sectors :
head_sectors = num_sectors
if num_sectors < 2 * head_sectors :
2017-02-06 14:17:11 +01:00
return ( num_sectors + 1 ) / / 2 * sector_size
2016-10-06 13:21:30 +02:00
else :
return ( num_sectors - head_sectors ) * sector_size
2018-09-21 08:39:36 +02:00
def override_vddsdio ( self , new_voltage ) :
raise NotImplementedInROMError ( " Overriding VDDSDIO setting only applies to ESP32 " )
2016-10-06 13:21:30 +02:00
class ESP8266StubLoader ( ESP8266ROM ) :
""" Access class for ESP8266 stub loader, runs on top of ROM.
"""
2016-10-28 00:53:02 +02:00
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
2016-10-06 13:21:30 +02:00
IS_STUB = True
def __init__ ( self , rom_loader ) :
2021-04-05 13:23:58 +02:00
self . secure_download_mode = rom_loader . secure_download_mode
2016-10-06 13:21:30 +02:00
self . _port = rom_loader . _port
2018-04-07 08:45:18 +02:00
self . _trace_enabled = rom_loader . _trace_enabled
2016-10-06 13:21:30 +02:00
self . flush_input ( ) # resets _slip_reader
2016-12-30 00:28:30 +01:00
def get_erase_size ( self , offset , size ) :
return size # stub doesn't have same size bug as ROM loader
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
ESP8266ROM . STUB_CLASS = ESP8266StubLoader
class ESP32ROM ( ESPLoader ) :
""" Access class for ESP32 ROM bootloader
"""
CHIP_NAME = " ESP32 "
2019-10-01 10:21:44 +02:00
IMAGE_CHIP_ID = 0
2016-10-06 13:21:30 +02:00
IS_STUB = False
2021-04-05 13:23:58 +02:00
CHIP_DETECT_MAGIC_VALUE = [ 0x00f01d83 ]
2016-10-06 13:21:30 +02:00
IROM_MAP_START = 0x400d0000
IROM_MAP_END = 0x40400000
2021-01-11 13:20:02 +01:00
2016-10-06 13:21:30 +02:00
DROM_MAP_START = 0x3F400000
2017-08-01 07:51:04 +02:00
DROM_MAP_END = 0x3F800000
2016-10-06 13:21:30 +02:00
# ESP32 uses a 4 byte status reply
STATUS_BYTES_LENGTH = 4
2021-01-11 13:20:02 +01:00
SPI_REG_BASE = 0x3ff42000
SPI_USR_OFFS = 0x1c
SPI_USR1_OFFS = 0x20
SPI_USR2_OFFS = 0x24
SPI_MOSI_DLEN_OFFS = 0x28
SPI_MISO_DLEN_OFFS = 0x2c
2021-04-05 13:23:58 +02:00
EFUSE_RD_REG_BASE = 0x3ff5a000
2021-01-11 13:20:02 +01:00
2021-04-05 13:23:58 +02:00
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18
2021-01-11 13:20:02 +01:00
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = ( 1 << 7 ) # EFUSE_RD_DISABLE_DL_ENCRYPT
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
DR_REG_SYSCON_BASE = 0x3ff66000
2016-10-28 00:53:02 +02:00
SPI_W0_OFFS = 0x80
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
UART_CLKDIV_REG = 0x3ff40014
XTAL_CLK_DIVIDER = 1
2016-10-06 13:21:30 +02:00
FLASH_SIZES = {
2021-04-05 13:23:58 +02:00
' 1MB ' : 0x00 ,
' 2MB ' : 0x10 ,
' 4MB ' : 0x20 ,
' 8MB ' : 0x30 ,
' 16MB ' : 0x40
2016-10-06 13:21:30 +02:00
}
2017-08-01 07:51:04 +02:00
BOOTLOADER_FLASH_OFFSET = 0x1000
2018-09-21 08:39:36 +02:00
OVERRIDE_VDDSDIO_CHOICES = [ " 1.8V " , " 1.9V " , " OFF " ]
2021-04-05 13:23:58 +02:00
MEMORY_MAP = [ [ 0x00000000 , 0x00010000 , " PADDING " ] ,
[ 0x3F400000 , 0x3F800000 , " DROM " ] ,
2019-10-01 10:21:44 +02:00
[ 0x3F800000 , 0x3FC00000 , " EXTRAM_DATA " ] ,
[ 0x3FF80000 , 0x3FF82000 , " RTC_DRAM " ] ,
[ 0x3FF90000 , 0x40000000 , " BYTE_ACCESSIBLE " ] ,
[ 0x3FFAE000 , 0x40000000 , " DRAM " ] ,
[ 0x3FFE0000 , 0x3FFFFFFC , " DIRAM_DRAM " ] ,
[ 0x40000000 , 0x40070000 , " IROM " ] ,
[ 0x40070000 , 0x40078000 , " CACHE_PRO " ] ,
[ 0x40078000 , 0x40080000 , " CACHE_APP " ] ,
[ 0x40080000 , 0x400A0000 , " IRAM " ] ,
[ 0x400A0000 , 0x400BFFFC , " DIRAM_IRAM " ] ,
[ 0x400C0000 , 0x400C2000 , " RTC_IRAM " ] ,
[ 0x400D0000 , 0x40400000 , " IROM " ] ,
[ 0x50000000 , 0x50002000 , " RTC_DATA " ] ]
2021-04-05 13:23:58 +02:00
FLASH_ENCRYPTED_WRITE_ALIGN = 32
2019-10-01 10:21:44 +02:00
""" Try to read the BLOCK1 (encryption key) and check if it is valid """
def is_flash_encryption_key_valid ( self ) :
""" Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1
this bit is at position 16 in EFUSE_BLK0_RDATA0_REG """
word0 = self . read_efuse ( 0 )
rd_disable = ( word0 >> 16 ) & 0x1
# reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed
if rd_disable :
return True
else :
2021-04-05 13:23:58 +02:00
# reading of BLOCK1 is ALLOWED so we will read and verify for non-zero.
# When ESP32 has not generated AES/encryption key in BLOCK1, the contents will be readable and 0.
# If the flash encryption is enabled it is expected to have a valid non-zero key. We break out on
# first occurance of non-zero value
2019-10-01 10:21:44 +02:00
key_word = [ 0 ] * 7
for i in range ( len ( key_word ) ) :
key_word [ i ] = self . read_efuse ( 14 + i )
# key is non-zero so break & return
if key_word [ i ] != 0 :
return True
return False
def get_flash_crypt_config ( self ) :
2021-04-05 13:23:58 +02:00
""" For flash encryption related commands we need to make sure
user has programmed all the relevant efuse correctly so before
writing encrypted write_flash_encrypt esptool will verify the values
of flash_crypt_config to be non zero if they are not read
protected . If the values are zero a warning will be printed
bit 3 in efuse_rd_disable [ 3 : 0 ] is mapped to flash_crypt_config
2019-10-01 10:21:44 +02:00
this bit is at position 19 in EFUSE_BLK0_RDATA0_REG """
word0 = self . read_efuse ( 0 )
rd_disable = ( word0 >> 19 ) & 0x1
if rd_disable == 0 :
""" we can read the flash_crypt_config efuse value
so go & read it ( EFUSE_BLK0_RDATA5_REG [ 31 : 28 ] ) """
word5 = self . read_efuse ( 5 )
word5 = ( word5 >> 28 ) & 0xF
return word5
else :
# if read of the efuse is disabled we assume it is set correctly
return 0xF
2021-01-11 13:20:02 +01:00
def get_encrypted_download_disabled ( self ) :
if self . read_reg ( self . EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG ) & self . EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT :
return True
else :
return False
def get_pkg_version ( self ) :
word3 = self . read_efuse ( 3 )
pkg_version = ( word3 >> 9 ) & 0x07
pkg_version + = ( ( word3 >> 2 ) & 0x1 ) << 3
return pkg_version
def get_chip_revision ( self ) :
2018-04-07 08:45:18 +02:00
word3 = self . read_efuse ( 3 )
2019-10-01 10:21:44 +02:00
word5 = self . read_efuse ( 5 )
apb_ctl_date = self . read_reg ( self . DR_REG_SYSCON_BASE + 0x7C )
2021-01-11 13:20:02 +01:00
2019-10-01 10:21:44 +02:00
rev_bit0 = ( word3 >> 15 ) & 0x1
rev_bit1 = ( word5 >> 20 ) & 0x1
rev_bit2 = ( apb_ctl_date >> 31 ) & 0x1
if rev_bit0 :
if rev_bit1 :
if rev_bit2 :
2021-01-11 13:20:02 +01:00
return 3
2019-10-01 10:21:44 +02:00
else :
2021-01-11 13:20:02 +01:00
return 2
2019-10-01 10:21:44 +02:00
else :
2021-01-11 13:20:02 +01:00
return 1
return 0
def get_chip_description ( self ) :
pkg_version = self . get_pkg_version ( )
chip_revision = self . get_chip_revision ( )
rev3 = ( chip_revision == 3 )
single_core = self . read_efuse ( 3 ) & ( 1 << 0 ) # CHIP_VER DIS_APP_CPU
chip_name = {
0 : " ESP32-S0WDQ6 " if single_core else " ESP32-D0WDQ6 " ,
1 : " ESP32-S0WD " if single_core else " ESP32-D0WD " ,
2 : " ESP32-D2WD " ,
4 : " ESP32-U4WDH " ,
5 : " ESP32-PICO-V3 " if rev3 else " ESP32-PICO-D4 " ,
6 : " ESP32-PICO-V3-02 " ,
} . get ( pkg_version , " unknown ESP32 " )
# ESP32-D0WD-V3, ESP32-D0WDQ6-V3
if chip_name . startswith ( " ESP32-D0WD " ) and rev3 :
chip_name + = " -V3 "
2019-10-01 10:21:44 +02:00
return " %s (revision %d ) " % ( chip_name , chip_revision )
2017-01-05 12:54:40 +01:00
2018-04-07 08:45:18 +02:00
def get_chip_features ( self ) :
features = [ " WiFi " ]
word3 = self . read_efuse ( 3 )
2018-09-21 08:39:36 +02:00
# names of variables in this section are lowercase
# versions of EFUSE names as documented in TRM and
# ESP-IDF efuse_reg.h
chip_ver_dis_bt = word3 & ( 1 << 1 )
if chip_ver_dis_bt == 0 :
2018-04-07 08:45:18 +02:00
features + = [ " BT " ]
2018-09-21 08:39:36 +02:00
chip_ver_dis_app_cpu = word3 & ( 1 << 0 )
if chip_ver_dis_app_cpu :
2018-04-07 08:45:18 +02:00
features + = [ " Single Core " ]
else :
features + = [ " Dual Core " ]
2018-09-21 08:39:36 +02:00
chip_cpu_freq_rated = word3 & ( 1 << 13 )
if chip_cpu_freq_rated :
chip_cpu_freq_low = word3 & ( 1 << 12 )
if chip_cpu_freq_low :
features + = [ " 160MHz " ]
else :
features + = [ " 240MHz " ]
2021-04-05 13:23:58 +02:00
pkg_version = self . get_pkg_version ( )
if pkg_version in [ 2 , 4 , 5 , 6 ] :
2018-04-07 08:45:18 +02:00
features + = [ " Embedded Flash " ]
2021-01-11 13:20:02 +01:00
if pkg_version == 6 :
features + = [ " Embedded PSRAM " ]
2018-04-07 08:45:18 +02:00
word4 = self . read_efuse ( 4 )
2018-09-21 08:39:36 +02:00
adc_vref = ( word4 >> 8 ) & 0x1F
if adc_vref :
2018-04-07 08:45:18 +02:00
features + = [ " VRef calibration in efuse " ]
2018-11-26 23:22:11 +01:00
blk3_part_res = word3 >> 14 & 0x1
if blk3_part_res :
features + = [ " BLK3 partially reserved " ]
word6 = self . read_efuse ( 6 )
coding_scheme = word6 & 0x3
features + = [ " Coding Scheme %s " % {
0 : " None " ,
1 : " 3/4 " ,
2 : " Repeat (UNSUPPORTED) " ,
3 : " Invalid " } [ coding_scheme ] ]
2018-04-07 08:45:18 +02:00
return features
2016-10-06 13:21:30 +02:00
def read_efuse ( self , n ) :
""" Read the nth word of the ESP3x EFUSE region. """
2021-04-05 13:23:58 +02:00
return self . read_reg ( self . EFUSE_RD_REG_BASE + ( 4 * n ) )
2016-10-06 13:21:30 +02:00
def chip_id ( self ) :
2018-09-21 08:39:36 +02:00
raise NotSupportedError ( self , " chip_id " )
2016-10-06 13:21:30 +02:00
def read_mac ( self ) :
""" Read MAC from EFUSE region """
2017-03-10 14:40:14 +01:00
words = [ self . read_efuse ( 2 ) , self . read_efuse ( 1 ) ]
2017-01-05 12:54:40 +01:00
bitstring = struct . pack ( " >II " , * words )
2017-03-10 14:40:14 +01:00
bitstring = bitstring [ 2 : 8 ] # trim the 2 byte CRC
2017-02-06 14:17:11 +01:00
try :
2017-03-10 14:40:14 +01:00
return tuple ( ord ( b ) for b in bitstring )
2017-02-06 14:17:11 +01:00
except TypeError : # Python 3, bitstring elements are already bytes
return tuple ( bitstring )
2016-10-06 13:21:30 +02:00
def get_erase_size ( self , offset , size ) :
return size
2018-09-21 08:39:36 +02:00
def override_vddsdio ( self , new_voltage ) :
new_voltage = new_voltage . upper ( )
if new_voltage not in self . OVERRIDE_VDDSDIO_CHOICES :
raise FatalError ( " The only accepted VDDSDIO overrides are ' 1.8V ' , ' 1.9V ' and ' OFF ' " )
RTC_CNTL_SDIO_CONF_REG = 0x3ff48074
RTC_CNTL_XPD_SDIO_REG = ( 1 << 31 )
RTC_CNTL_DREFH_SDIO_M = ( 3 << 29 )
RTC_CNTL_DREFM_SDIO_M = ( 3 << 27 )
RTC_CNTL_DREFL_SDIO_M = ( 3 << 25 )
# RTC_CNTL_SDIO_TIEH = (1 << 23) # not used here, setting TIEH=1 would set 3.3V output, not safe for esptool.py to do
RTC_CNTL_SDIO_FORCE = ( 1 << 22 )
RTC_CNTL_SDIO_PD_EN = ( 1 << 21 )
reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting
reg_val | = RTC_CNTL_SDIO_PD_EN
if new_voltage != " OFF " :
reg_val | = RTC_CNTL_XPD_SDIO_REG # enable internal LDO
if new_voltage == " 1.9V " :
reg_val | = ( RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M ) # boost voltage
self . write_reg ( RTC_CNTL_SDIO_CONF_REG , reg_val )
print ( " VDDSDIO regulator set to %s " % new_voltage )
2021-01-11 13:20:02 +01:00
def read_flash_slow ( self , offset , length , progress_fn ) :
BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow)
data = b ' '
while len ( data ) < length :
block_len = min ( BLOCK_LEN , length - len ( data ) )
r = self . check_command ( " read flash block " , self . ESP_READ_FLASH_SLOW ,
struct . pack ( ' <II ' , offset + len ( data ) , block_len ) )
if len ( r ) < block_len :
raise FatalError ( " Expected %d byte block, got %d bytes. Serial errors? " % ( block_len , len ( r ) ) )
data + = r [ : block_len ] # command always returns 64 byte buffer, regardless of how many bytes were actually read from flash
if progress_fn and ( len ( data ) % 1024 == 0 or len ( data ) == length ) :
progress_fn ( len ( data ) , length )
return data
class ESP32S2ROM ( ESP32ROM ) :
2021-04-05 13:23:58 +02:00
CHIP_NAME = " ESP32-S2 "
IMAGE_CHIP_ID = 2
2021-01-11 13:20:02 +01:00
IROM_MAP_START = 0x40080000
IROM_MAP_END = 0x40b80000
DROM_MAP_START = 0x3F000000
DROM_MAP_END = 0x3F3F0000
2021-04-05 13:23:58 +02:00
CHIP_DETECT_MAGIC_VALUE = [ 0x000007c6 ]
2021-01-11 13:20:02 +01:00
SPI_REG_BASE = 0x3f402000
SPI_USR_OFFS = 0x18
SPI_USR1_OFFS = 0x1c
SPI_USR2_OFFS = 0x20
SPI_MOSI_DLEN_OFFS = 0x24
SPI_MISO_DLEN_OFFS = 0x28
2021-04-05 13:23:58 +02:00
SPI_W0_OFFS = 0x58
2021-01-11 13:20:02 +01:00
2021-04-05 13:23:58 +02:00
MAC_EFUSE_REG = 0x3f41A044 # ESP32-S2 has special block for MAC efuses
2021-01-11 13:20:02 +01:00
UART_CLKDIV_REG = 0x3f400014
2021-04-05 13:23:58 +02:00
FLASH_ENCRYPTED_WRITE_ALIGN = 16
# todo: use espefuse APIs to get this info
EFUSE_BASE = 0x3f41A000
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY0_SHIFT = 24
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY1_SHIFT = 28
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY2_SHIFT = 0
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY3_SHIFT = 4
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY4_SHIFT = 8
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY5_SHIFT = 12
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
2021-01-11 13:20:02 +01:00
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19
2021-04-05 13:23:58 +02:00
PURPOSE_VAL_XTS_AES256_KEY_1 = 2
PURPOSE_VAL_XTS_AES256_KEY_2 = 3
PURPOSE_VAL_XTS_AES128_KEY = 4
UARTDEV_BUF_NO = 0x3ffffd14 # Variable in ROM .bss which indicates the port in use
UARTDEV_BUF_NO_USB = 2 # Value of the above variable indicating that USB is in use
USB_RAM_BLOCK = 0x800 # Max block size USB CDC is used
GPIO_STRAP_REG = 0x3f404038
GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode
RTC_CNTL_OPTION1_REG = 0x3f408128
RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB?
MEMORY_MAP = [ [ 0x00000000 , 0x00010000 , " PADDING " ] ,
[ 0x3F000000 , 0x3FF80000 , " DROM " ] ,
[ 0x3F500000 , 0x3FF80000 , " EXTRAM_DATA " ] ,
[ 0x3FF9E000 , 0x3FFA0000 , " RTC_DRAM " ] ,
[ 0x3FF9E000 , 0x40000000 , " BYTE_ACCESSIBLE " ] ,
[ 0x3FF9E000 , 0x40072000 , " MEM_INTERNAL " ] ,
[ 0x3FFB0000 , 0x40000000 , " DRAM " ] ,
[ 0x40000000 , 0x4001A100 , " IROM_MASK " ] ,
[ 0x40020000 , 0x40070000 , " IRAM " ] ,
[ 0x40070000 , 0x40072000 , " RTC_IRAM " ] ,
[ 0x40080000 , 0x40800000 , " IROM " ] ,
[ 0x50000000 , 0x50002000 , " RTC_DATA " ] ]
def get_pkg_version ( self ) :
num_word = 3
block1_addr = self . EFUSE_BASE + 0x044
word3 = self . read_reg ( block1_addr + ( 4 * num_word ) )
pkg_version = ( word3 >> 21 ) & 0x0F
return pkg_version
def get_chip_description ( self ) :
chip_name = {
0 : " ESP32-S2 " ,
1 : " ESP32-S2FH16 " ,
2 : " ESP32-S2FH32 " ,
} . get ( self . get_pkg_version ( ) , " unknown ESP32-S2 " )
return " %s " % ( chip_name )
def get_chip_features ( self ) :
features = [ " WiFi " ]
if self . secure_download_mode :
features + = [ " Secure Download Mode Enabled " ]
pkg_version = self . get_pkg_version ( )
if pkg_version in [ 1 , 2 ] :
if pkg_version == 1 :
features + = [ " Embedded 2MB Flash " ]
elif pkg_version == 2 :
features + = [ " Embedded 4MB Flash " ]
features + = [ " 105C temp rating " ]
num_word = 4
block2_addr = self . EFUSE_BASE + 0x05C
word4 = self . read_reg ( block2_addr + ( 4 * num_word ) )
block2_version = ( word4 >> 4 ) & 0x07
if block2_version == 1 :
features + = [ " ADC and temperature sensor calibration in BLK2 of efuse " ]
return features
def get_crystal_freq ( self ) :
# ESP32-S2 XTAL is fixed to 40MHz
return 40
def override_vddsdio ( self , new_voltage ) :
raise NotImplementedInROMError ( " VDD_SDIO overrides are not supported for ESP32-S2 " )
def read_mac ( self ) :
mac0 = self . read_reg ( self . MAC_EFUSE_REG )
mac1 = self . read_reg ( self . MAC_EFUSE_REG + 4 ) # only bottom 16 bits are MAC
bitstring = struct . pack ( " >II " , mac1 , mac0 ) [ 2 : ]
try :
return tuple ( ord ( b ) for b in bitstring )
except TypeError : # Python 3, bitstring elements are already bytes
return tuple ( bitstring )
def get_flash_crypt_config ( self ) :
return None # doesn't exist on ESP32-S2
def get_key_block_purpose ( self , key_block ) :
if key_block < 0 or key_block > 5 :
raise FatalError ( " Valid key block numbers must be in range 0-5 " )
reg , shift = [ ( self . EFUSE_PURPOSE_KEY0_REG , self . EFUSE_PURPOSE_KEY0_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY1_REG , self . EFUSE_PURPOSE_KEY1_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY2_REG , self . EFUSE_PURPOSE_KEY2_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY3_REG , self . EFUSE_PURPOSE_KEY3_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY4_REG , self . EFUSE_PURPOSE_KEY4_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY5_REG , self . EFUSE_PURPOSE_KEY5_SHIFT ) ] [ key_block ]
return ( self . read_reg ( reg ) >> shift ) & 0xF
def is_flash_encryption_key_valid ( self ) :
# Need to see either an AES-128 key or two AES-256 keys
purposes = [ self . get_key_block_purpose ( b ) for b in range ( 6 ) ]
if any ( p == self . PURPOSE_VAL_XTS_AES128_KEY for p in purposes ) :
return True
return any ( p == self . PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes ) \
and any ( p == self . PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes )
def uses_usb ( self , _cache = [ ] ) :
if self . secure_download_mode :
return False # can't detect native USB in secure download mode
if not _cache :
buf_no = self . read_reg ( self . UARTDEV_BUF_NO ) & 0xff
_cache . append ( buf_no == self . UARTDEV_BUF_NO_USB )
return _cache [ 0 ]
def _post_connect ( self ) :
if self . uses_usb ( ) :
self . ESP_RAM_BLOCK = self . USB_RAM_BLOCK
def _check_if_can_reset ( self ) :
"""
Check the strapping register to see if we can reset out of download mode .
"""
if os . getenv ( " ESPTOOL_TESTING " ) is not None :
print ( " ESPTOOL_TESTING is set, ignoring strapping mode check " )
# Esptool tests over USB CDC run with GPIO0 strapped low, don't complain in this case.
return
strap_reg = self . read_reg ( self . GPIO_STRAP_REG )
force_dl_reg = self . read_reg ( self . RTC_CNTL_OPTION1_REG )
if strap_reg & self . GPIO_STRAP_SPI_BOOT_MASK == 0 and force_dl_reg & self . RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 :
print ( " ERROR: {} chip was placed into download mode using GPIO0. \n "
" esptool.py can not exit the download mode over USB. "
" To run the app, reset the chip manually. \n "
" To suppress this error, set --after option to ' no_reset ' . " . format ( self . get_chip_description ( ) ) )
raise SystemExit ( 1 )
def hard_reset ( self ) :
if self . uses_usb ( ) :
self . _check_if_can_reset ( )
self . _setRTS ( True ) # EN->LOW
if self . uses_usb ( ) :
# Give the chip some time to come out of reset, to be able to handle further DTR/RTS transitions
time . sleep ( 0.2 )
self . _setRTS ( False )
time . sleep ( 0.2 )
else :
self . _setRTS ( False )
2021-05-31 15:32:51 +02:00
class ESP32S3ROM ( ESP32ROM ) :
CHIP_NAME = " ESP32-S3 "
2021-04-05 13:23:58 +02:00
IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x44000000
DROM_MAP_START = 0x3c000000
DROM_MAP_END = 0x3e000000
UART_DATE_REG_ADDR = 0x60000080
SPI_REG_BASE = 0x60002000
SPI_USR_OFFS = 0x18
SPI_USR1_OFFS = 0x1c
SPI_USR2_OFFS = 0x20
SPI_MOSI_DLEN_OFFS = 0x24
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
2021-05-31 15:32:51 +02:00
FLASH_ENCRYPTED_WRITE_ALIGN = 16
# todo: use espefuse APIs to get this info
EFUSE_BASE = 0x6001A000 # BLOCK0 read base address
MAC_EFUSE_REG = EFUSE_BASE + 0x044
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY0_SHIFT = 24
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY1_SHIFT = 28
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY2_SHIFT = 0
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY3_SHIFT = 4
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY4_SHIFT = 8
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY5_SHIFT = 12
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
2021-04-05 13:23:58 +02:00
2021-05-31 15:32:51 +02:00
PURPOSE_VAL_XTS_AES256_KEY_1 = 2
PURPOSE_VAL_XTS_AES256_KEY_2 = 3
PURPOSE_VAL_XTS_AES128_KEY = 4
2021-04-05 13:23:58 +02:00
UART_CLKDIV_REG = 0x60000014
GPIO_STRAP_REG = 0x60004038
MEMORY_MAP = [ [ 0x00000000 , 0x00010000 , " PADDING " ] ,
[ 0x3C000000 , 0x3D000000 , " DROM " ] ,
[ 0x3D000000 , 0x3E000000 , " EXTRAM_DATA " ] ,
[ 0x600FE000 , 0x60100000 , " RTC_DRAM " ] ,
[ 0x3FC88000 , 0x3FD00000 , " BYTE_ACCESSIBLE " ] ,
[ 0x3FC88000 , 0x403E2000 , " MEM_INTERNAL " ] ,
[ 0x3FC88000 , 0x3FD00000 , " DRAM " ] ,
[ 0x40000000 , 0x4001A100 , " IROM_MASK " ] ,
[ 0x40370000 , 0x403E0000 , " IRAM " ] ,
[ 0x600FE000 , 0x60100000 , " RTC_IRAM " ] ,
[ 0x42000000 , 0x42800000 , " IROM " ] ,
[ 0x50000000 , 0x50002000 , " RTC_DATA " ] ]
def get_chip_description ( self ) :
2021-05-31 15:32:51 +02:00
return " ESP32-S3 "
2021-04-05 13:23:58 +02:00
def get_chip_features ( self ) :
return [ " WiFi " , " BLE " ]
def get_crystal_freq ( self ) :
# ESP32S3 XTAL is fixed to 40MHz
return 40
2021-05-31 15:32:51 +02:00
def get_flash_crypt_config ( self ) :
return None # doesn't exist on ESP32-S3
def get_key_block_purpose ( self , key_block ) :
if key_block < 0 or key_block > 5 :
raise FatalError ( " Valid key block numbers must be in range 0-5 " )
reg , shift = [ ( self . EFUSE_PURPOSE_KEY0_REG , self . EFUSE_PURPOSE_KEY0_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY1_REG , self . EFUSE_PURPOSE_KEY1_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY2_REG , self . EFUSE_PURPOSE_KEY2_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY3_REG , self . EFUSE_PURPOSE_KEY3_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY4_REG , self . EFUSE_PURPOSE_KEY4_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY5_REG , self . EFUSE_PURPOSE_KEY5_SHIFT ) ] [ key_block ]
return ( self . read_reg ( reg ) >> shift ) & 0xF
def is_flash_encryption_key_valid ( self ) :
# Need to see either an AES-128 key or two AES-256 keys
purposes = [ self . get_key_block_purpose ( b ) for b in range ( 6 ) ]
if any ( p == self . PURPOSE_VAL_XTS_AES128_KEY for p in purposes ) :
return True
return any ( p == self . PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes ) \
and any ( p == self . PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes )
2021-04-05 13:23:58 +02:00
def override_vddsdio ( self , new_voltage ) :
raise NotImplementedInROMError ( " VDD_SDIO overrides are not supported for ESP32-S3 " )
def read_mac ( self ) :
mac0 = self . read_reg ( self . MAC_EFUSE_REG )
mac1 = self . read_reg ( self . MAC_EFUSE_REG + 4 ) # only bottom 16 bits are MAC
bitstring = struct . pack ( " >II " , mac1 , mac0 ) [ 2 : ]
try :
return tuple ( ord ( b ) for b in bitstring )
except TypeError : # Python 3, bitstring elements are already bytes
return tuple ( bitstring )
2021-05-31 15:32:51 +02:00
class ESP32S3BETA2ROM ( ESP32S3ROM ) :
CHIP_NAME = " ESP32-S3(beta2) "
IMAGE_CHIP_ID = 4
2021-04-05 13:23:58 +02:00
2021-05-31 15:32:51 +02:00
CHIP_DETECT_MAGIC_VALUE = [ 0xeb004136 ]
2021-04-05 13:23:58 +02:00
2021-05-31 15:32:51 +02:00
def get_chip_description ( self ) :
return " ESP32-S3(beta2) "
2021-04-05 13:23:58 +02:00
2021-05-31 15:32:51 +02:00
class ESP32S3BETA3ROM ( ESP32S3ROM ) :
CHIP_NAME = " ESP32-S3(beta3) "
IMAGE_CHIP_ID = 6
2021-04-05 13:23:58 +02:00
2021-05-31 15:32:51 +02:00
CHIP_DETECT_MAGIC_VALUE = [ 0x9 ]
2021-04-05 13:23:58 +02:00
def get_chip_description ( self ) :
return " ESP32-S3(beta3) "
class ESP32C3ROM ( ESP32ROM ) :
CHIP_NAME = " ESP32-C3 "
IMAGE_CHIP_ID = 5
IROM_MAP_START = 0x42000000
IROM_MAP_END = 0x42800000
DROM_MAP_START = 0x3c000000
DROM_MAP_END = 0x3c800000
SPI_REG_BASE = 0x60002000
SPI_USR_OFFS = 0x18
SPI_USR1_OFFS = 0x1C
SPI_USR2_OFFS = 0x20
SPI_MOSI_DLEN_OFFS = 0x24
SPI_MISO_DLEN_OFFS = 0x28
SPI_W0_OFFS = 0x58
BOOTLOADER_FLASH_OFFSET = 0x0
# Magic value for ESP32C3 eco 1+2 and ESP32C3 eco3 respectivly
CHIP_DETECT_MAGIC_VALUE = [ 0x6921506f , 0x1b31506f ]
UART_DATE_REG_ADDR = 0x60000000 + 0x7c
EFUSE_BASE = 0x60008800
MAC_EFUSE_REG = EFUSE_BASE + 0x044
EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address
EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY0_SHIFT = 24
EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34
EFUSE_PURPOSE_KEY1_SHIFT = 28
EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY2_SHIFT = 0
EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY3_SHIFT = 4
EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY4_SHIFT = 8
EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38
EFUSE_PURPOSE_KEY5_SHIFT = 12
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20
PURPOSE_VAL_XTS_AES128_KEY = 4
GPIO_STRAP_REG = 0x3f404038
FLASH_ENCRYPTED_WRITE_ALIGN = 16
MEMORY_MAP = [ [ 0x00000000 , 0x00010000 , " PADDING " ] ,
[ 0x3C000000 , 0x3C800000 , " DROM " ] ,
[ 0x3FC80000 , 0x3FCE0000 , " DRAM " ] ,
[ 0x3FC88000 , 0x3FD00000 , " BYTE_ACCESSIBLE " ] ,
[ 0x3FF00000 , 0x3FF20000 , " DROM_MASK " ] ,
[ 0x40000000 , 0x40060000 , " IROM_MASK " ] ,
[ 0x42000000 , 0x42800000 , " IROM " ] ,
[ 0x4037C000 , 0x403E0000 , " IRAM " ] ,
[ 0x50000000 , 0x50002000 , " RTC_IRAM " ] ,
[ 0x50000000 , 0x50002000 , " RTC_DRAM " ] ,
[ 0x600FE000 , 0x60100000 , " MEM_INTERNAL2 " ] ]
def get_pkg_version ( self ) :
num_word = 3
block1_addr = self . EFUSE_BASE + 0x044
word3 = self . read_reg ( block1_addr + ( 4 * num_word ) )
pkg_version = ( word3 >> 21 ) & 0x0F
return pkg_version
def get_chip_revision ( self ) :
# reads WAFER_VERSION field from EFUSE_RD_MAC_SPI_SYS_3_REG
block1_addr = self . EFUSE_BASE + 0x044
num_word = 3
pos = 18
return ( self . read_reg ( block1_addr + ( 4 * num_word ) ) & ( 0x7 << pos ) ) >> pos
2021-01-11 13:20:02 +01:00
def get_chip_description ( self ) :
2021-04-05 13:23:58 +02:00
chip_name = {
0 : " ESP32-C3 " ,
} . get ( self . get_pkg_version ( ) , " unknown ESP32-C3 " )
chip_revision = self . get_chip_revision ( )
return " %s (revision %d ) " % ( chip_name , chip_revision )
2021-01-11 13:20:02 +01:00
def get_chip_features ( self ) :
2021-04-05 13:23:58 +02:00
return [ " Wi-Fi " ]
def get_crystal_freq ( self ) :
# ESP32C3 XTAL is fixed to 40MHz
return 40
2021-01-11 13:20:02 +01:00
def override_vddsdio ( self , new_voltage ) :
2021-04-05 13:23:58 +02:00
raise NotImplementedInROMError ( " VDD_SDIO overrides are not supported for ESP32-C3 " )
2021-01-11 13:20:02 +01:00
def read_mac ( self ) :
mac0 = self . read_reg ( self . MAC_EFUSE_REG )
mac1 = self . read_reg ( self . MAC_EFUSE_REG + 4 ) # only bottom 16 bits are MAC
bitstring = struct . pack ( " >II " , mac1 , mac0 ) [ 2 : ]
try :
return tuple ( ord ( b ) for b in bitstring )
except TypeError : # Python 3, bitstring elements are already bytes
return tuple ( bitstring )
2021-04-05 13:23:58 +02:00
def get_flash_crypt_config ( self ) :
return None # doesn't exist on ESP32-C3
def get_key_block_purpose ( self , key_block ) :
if key_block < 0 or key_block > 5 :
raise FatalError ( " Valid key block numbers must be in range 0-5 " )
reg , shift = [ ( self . EFUSE_PURPOSE_KEY0_REG , self . EFUSE_PURPOSE_KEY0_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY1_REG , self . EFUSE_PURPOSE_KEY1_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY2_REG , self . EFUSE_PURPOSE_KEY2_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY3_REG , self . EFUSE_PURPOSE_KEY3_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY4_REG , self . EFUSE_PURPOSE_KEY4_SHIFT ) ,
( self . EFUSE_PURPOSE_KEY5_REG , self . EFUSE_PURPOSE_KEY5_SHIFT ) ] [ key_block ]
return ( self . read_reg ( reg ) >> shift ) & 0xF
def is_flash_encryption_key_valid ( self ) :
# Need to see an AES-128 key
purposes = [ self . get_key_block_purpose ( b ) for b in range ( 6 ) ]
return any ( p == self . PURPOSE_VAL_XTS_AES128_KEY for p in purposes )
2016-10-06 13:21:30 +02:00
2021-05-31 15:32:51 +02:00
class ESP32C6BETAROM ( ESP32C3ROM ) :
CHIP_NAME = " ESP32-C6 BETA "
IMAGE_CHIP_ID = 7
CHIP_DETECT_MAGIC_VALUE = [ 0x0da1806f ]
UART_DATE_REG_ADDR = 0x00000500
def get_chip_description ( self ) :
chip_name = {
0 : " ESP32-C6 " ,
} . get ( self . get_pkg_version ( ) , " unknown ESP32-C6 " )
chip_revision = self . get_chip_revision ( )
return " %s (revision %d ) " % ( chip_name , chip_revision )
2016-10-06 13:21:30 +02:00
class ESP32StubLoader ( ESP32ROM ) :
""" Access class for ESP32 stub loader, runs on top of ROM.
"""
2016-10-28 00:53:02 +02:00
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
2016-10-06 13:21:30 +02:00
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__ ( self , rom_loader ) :
2021-04-05 13:23:58 +02:00
self . secure_download_mode = rom_loader . secure_download_mode
2016-10-06 13:21:30 +02:00
self . _port = rom_loader . _port
2018-04-07 08:45:18 +02:00
self . _trace_enabled = rom_loader . _trace_enabled
2016-10-06 13:21:30 +02:00
self . flush_input ( ) # resets _slip_reader
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
ESP32ROM . STUB_CLASS = ESP32StubLoader
2021-01-11 13:20:02 +01:00
class ESP32S2StubLoader ( ESP32S2ROM ) :
2021-04-05 13:23:58 +02:00
""" Access class for ESP32-S2 stub loader, runs on top of ROM.
2021-01-11 13:20:02 +01:00
2021-04-05 13:23:58 +02:00
( Basically the same as ESP32StubLoader , but different base class .
2021-01-11 13:20:02 +01:00
Can possibly be made into a mixin . )
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__ ( self , rom_loader ) :
2021-04-05 13:23:58 +02:00
self . secure_download_mode = rom_loader . secure_download_mode
2021-01-11 13:20:02 +01:00
self . _port = rom_loader . _port
self . _trace_enabled = rom_loader . _trace_enabled
self . flush_input ( ) # resets _slip_reader
2021-04-05 13:23:58 +02:00
if rom_loader . uses_usb ( ) :
self . ESP_RAM_BLOCK = self . USB_RAM_BLOCK
self . FLASH_WRITE_SIZE = self . USB_RAM_BLOCK
2021-01-11 13:20:02 +01:00
ESP32S2ROM . STUB_CLASS = ESP32S2StubLoader
2021-04-05 13:23:58 +02:00
class ESP32S3BETA2StubLoader ( ESP32S3BETA2ROM ) :
""" Access class for ESP32S3 stub loader, runs on top of ROM.
( Basically the same as ESP32StubLoader , but different base class .
Can possibly be made into a mixin . )
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__ ( self , rom_loader ) :
self . secure_download_mode = rom_loader . secure_download_mode
self . _port = rom_loader . _port
self . _trace_enabled = rom_loader . _trace_enabled
self . flush_input ( ) # resets _slip_reader
ESP32S3BETA2ROM . STUB_CLASS = ESP32S3BETA2StubLoader
class ESP32S3BETA3StubLoader ( ESP32S3BETA3ROM ) :
""" Access class for ESP32S3 stub loader, runs on top of ROM.
( Basically the same as ESP32StubLoader , but different base class .
Can possibly be made into a mixin . )
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__ ( self , rom_loader ) :
self . secure_download_mode = rom_loader . secure_download_mode
self . _port = rom_loader . _port
self . _trace_enabled = rom_loader . _trace_enabled
self . flush_input ( ) # resets _slip_reader
ESP32S3BETA3ROM . STUB_CLASS = ESP32S3BETA3StubLoader
class ESP32C3StubLoader ( ESP32C3ROM ) :
""" Access class for ESP32C3 stub loader, runs on top of ROM.
( Basically the same as ESP32StubLoader , but different base class .
Can possibly be made into a mixin . )
"""
FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c
STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM
IS_STUB = True
def __init__ ( self , rom_loader ) :
self . secure_download_mode = rom_loader . secure_download_mode
self . _port = rom_loader . _port
self . _trace_enabled = rom_loader . _trace_enabled
self . flush_input ( ) # resets _slip_reader
ESP32C3ROM . STUB_CLASS = ESP32C3StubLoader
2016-10-06 13:21:30 +02:00
class ESPBOOTLOADER ( object ) :
2021-04-05 13:23:58 +02:00
""" These are constants related to software ESP8266 bootloader, working with ' v2 ' image files """
2016-10-06 13:21:30 +02:00
# First byte of the "v2" application image
IMAGE_V2_MAGIC = 0xea
# First 'segment' value in a "v2" application image, appears to be a constant version value?
IMAGE_V2_SEGMENT = 4
def LoadFirmwareImage ( chip , filename ) :
2021-04-05 13:23:58 +02:00
""" Load a firmware image. Can be for any supported SoC.
ESP8266 images will be examined to determine if they are original ROM firmware images ( ESP8266ROMFirmwareImage )
or " v2 " OTA bootloader images .
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
Returns a BaseFirmwareImage subclass , either ESP8266ROMFirmwareImage ( v1 ) or ESP8266V2FirmwareImage ( v2 ) .
2016-10-06 13:21:30 +02:00
"""
2021-04-05 13:23:58 +02:00
chip = chip . lower ( ) . replace ( " - " , " " )
2016-10-06 13:21:30 +02:00
with open ( filename , ' rb ' ) as f :
2021-01-11 13:20:02 +01:00
if chip == ' esp32 ' :
2016-10-06 13:21:30 +02:00
return ESP32FirmwareImage ( f )
2021-04-05 13:23:58 +02:00
elif chip == " esp32s2 " :
2021-01-11 13:20:02 +01:00
return ESP32S2FirmwareImage ( f )
2021-04-05 13:23:58 +02:00
elif chip == " esp32s3beta2 " :
return ESP32S3BETA2FirmwareImage ( f )
elif chip == " esp32s3beta3 " :
return ESP32S3BETA3FirmwareImage ( f )
elif chip == ' esp32c3 ' :
return ESP32C3FirmwareImage ( f )
2021-05-31 15:32:51 +02:00
elif chip == ' esp32c6beta ' :
return ESP32C6BETAFirmwareImage ( f )
2016-10-06 13:21:30 +02:00
else : # Otherwise, ESP8266 so look at magic to determine the image type
magic = ord ( f . read ( 1 ) )
f . seek ( 0 )
if magic == ESPLoader . ESP_IMAGE_MAGIC :
2018-09-21 08:39:36 +02:00
return ESP8266ROMFirmwareImage ( f )
2016-10-06 13:21:30 +02:00
elif magic == ESPBOOTLOADER . IMAGE_V2_MAGIC :
2018-09-21 08:39:36 +02:00
return ESP8266V2FirmwareImage ( f )
2016-10-06 13:21:30 +02:00
else :
raise FatalError ( " Invalid image magic number: %d " % magic )
class ImageSegment ( object ) :
""" Wrapper class for a segment in an ESP image
( very similar to a section in an ELFImage also ) """
def __init__ ( self , addr , data , file_offs = None ) :
self . addr = addr
2018-09-21 08:39:36 +02:00
self . data = data
2016-10-06 13:21:30 +02:00
self . file_offs = file_offs
2016-10-28 00:53:02 +02:00
self . include_in_checksum = True
2018-09-21 08:39:36 +02:00
if self . addr != 0 :
self . pad_to_alignment ( 4 ) # pad all "real" ImageSegments 4 byte aligned length
2016-10-06 13:21:30 +02:00
def copy_with_new_addr ( self , new_addr ) :
""" Return a new ImageSegment with same data, but mapped at
a new address . """
return ImageSegment ( new_addr , self . data , 0 )
2017-08-01 07:51:04 +02:00
def split_image ( self , split_len ) :
""" Return a new ImageSegment which splits " split_len " bytes
from the beginning of the data . Remaining bytes are kept in
this segment object ( and the start address is adjusted to match . ) """
result = copy . copy ( self )
result . data = self . data [ : split_len ]
self . data = self . data [ split_len : ]
self . addr + = split_len
self . file_offs = None
result . file_offs = None
return result
2016-10-06 13:21:30 +02:00
def __repr__ ( self ) :
r = " len 0x %05x load 0x %08x " % ( len ( self . data ) , self . addr )
if self . file_offs is not None :
r + = " file_offs 0x %08x " % ( self . file_offs )
return r
2021-04-05 13:23:58 +02:00
def get_memory_type ( self , image ) :
"""
Return a list describing the memory type ( s ) that is covered by this
segment ' s start address.
"""
return [ map_range [ 2 ] for map_range in image . ROM_LOADER . MEMORY_MAP if map_range [ 0 ] < = self . addr < map_range [ 1 ] ]
2018-09-21 08:39:36 +02:00
def pad_to_alignment ( self , alignment ) :
self . data = pad_to ( self . data , alignment , b ' \x00 ' )
2016-10-06 13:21:30 +02:00
class ELFSection ( ImageSegment ) :
""" Wrapper class for a section in an ELF image, has a section
name as well as the common properties of an ImageSegment . """
def __init__ ( self , name , addr , data ) :
super ( ELFSection , self ) . __init__ ( addr , data )
2017-02-06 14:17:11 +01:00
self . name = name . decode ( " utf-8 " )
2016-10-06 13:21:30 +02:00
def __repr__ ( self ) :
return " %s %s " % ( self . name , super ( ELFSection , self ) . __repr__ ( ) )
class BaseFirmwareImage ( object ) :
SEG_HEADER_LEN = 8
2019-02-14 16:49:30 +01:00
SHA256_DIGEST_LEN = 32
2016-10-06 13:21:30 +02:00
""" Base class with common firmware image functions """
def __init__ ( self ) :
self . segments = [ ]
self . entrypoint = 0
2019-02-14 16:49:30 +01:00
self . elf_sha256 = None
self . elf_sha256_offset = 0
2016-10-06 13:21:30 +02:00
def load_common_header ( self , load_file , expected_magic ) :
2019-10-01 10:21:44 +02:00
( magic , segments , self . flash_mode , self . flash_size_freq , self . entrypoint ) = struct . unpack ( ' <BBBBI ' , load_file . read ( 8 ) )
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
if magic != expected_magic :
raise FatalError ( ' Invalid firmware image magic=0x %x ' % ( magic ) )
return segments
2016-10-06 13:21:30 +02:00
2019-02-14 16:49:30 +01:00
def verify ( self ) :
if len ( self . segments ) > 16 :
raise FatalError ( ' Invalid segment count %d (max 16). Usually this indicates a linker script problem. ' % len ( self . segments ) )
2016-10-06 13:21:30 +02:00
def load_segment ( self , f , is_irom_segment = False ) :
""" Load the next segment from the image file """
file_offs = f . tell ( )
( offset , size ) = struct . unpack ( ' <II ' , f . read ( 8 ) )
2016-10-28 00:53:02 +02:00
self . warn_if_unusual_segment ( offset , size , is_irom_segment )
2016-10-06 13:21:30 +02:00
segment_data = f . read ( size )
if len ( segment_data ) < size :
raise FatalError ( ' End of file reading segment 0x %x , length %d (actual length %d ) ' % ( offset , size , len ( segment_data ) ) )
segment = ImageSegment ( offset , segment_data , file_offs )
self . segments . append ( segment )
return segment
2016-10-28 00:53:02 +02:00
def warn_if_unusual_segment ( self , offset , size , is_irom_segment ) :
if not is_irom_segment :
if offset > 0x40200000 or offset < 0x3ffe0000 or size > 65536 :
print ( ' WARNING: Suspicious segment 0x %x , length %d ' % ( offset , size ) )
2019-02-14 16:49:30 +01:00
def maybe_patch_segment_data ( self , f , segment_data ) :
""" If SHA256 digest of the ELF file needs to be inserted into this segment, do so. Returns segment data. """
segment_len = len ( segment_data )
2019-10-01 10:21:44 +02:00
file_pos = f . tell ( ) # file_pos is position in the .bin file
2019-02-14 16:49:30 +01:00
if self . elf_sha256_offset > = file_pos and self . elf_sha256_offset < file_pos + segment_len :
2019-10-01 10:21:44 +02:00
# SHA256 digest needs to be patched into this binary segment,
# calculate offset of the digest inside the binary segment.
2019-02-14 16:49:30 +01:00
patch_offset = self . elf_sha256_offset - file_pos
# Sanity checks
if patch_offset < self . SEG_HEADER_LEN or patch_offset + self . SHA256_DIGEST_LEN > segment_len :
2021-04-05 13:23:58 +02:00
raise FatalError ( ' Cannot place SHA256 digest on segment boundary '
2019-02-14 16:49:30 +01:00
' (elf_sha256_offset= %d , file_pos= %d , segment_size= %d ) ' %
( self . elf_sha256_offset , file_pos , segment_len ) )
2021-01-11 13:20:02 +01:00
# offset relative to the data part
patch_offset - = self . SEG_HEADER_LEN
2019-10-01 10:21:44 +02:00
if segment_data [ patch_offset : patch_offset + self . SHA256_DIGEST_LEN ] != b ' \x00 ' * self . SHA256_DIGEST_LEN :
raise FatalError ( ' Contents of segment at SHA256 digest offset 0x %x are not all zero. Refusing to overwrite. ' %
self . elf_sha256_offset )
2019-02-14 16:49:30 +01:00
assert ( len ( self . elf_sha256 ) == self . SHA256_DIGEST_LEN )
segment_data = segment_data [ 0 : patch_offset ] + self . elf_sha256 + \
segment_data [ patch_offset + self . SHA256_DIGEST_LEN : ]
return segment_data
2016-10-06 13:21:30 +02:00
def save_segment ( self , f , segment , checksum = None ) :
""" Save the next segment to the image file, return next checksum value if provided """
2019-02-14 16:49:30 +01:00
segment_data = self . maybe_patch_segment_data ( f , segment . data )
f . write ( struct . pack ( ' <II ' , segment . addr , len ( segment_data ) ) )
f . write ( segment_data )
2016-10-06 13:21:30 +02:00
if checksum is not None :
2019-02-14 16:49:30 +01:00
return ESPLoader . checksum ( segment_data , checksum )
2016-10-06 13:21:30 +02:00
def read_checksum ( self , f ) :
""" Return ESPLoader checksum from end of just-read image """
# Skip the padding. The checksum is stored in the last byte so that the
# file is a multiple of 16 bytes.
align_file_position ( f , 16 )
return ord ( f . read ( 1 ) )
2016-10-28 00:53:02 +02:00
def calculate_checksum ( self ) :
""" Calculate checksum of loaded image, based on segments in
segment array .
"""
checksum = ESPLoader . ESP_CHECKSUM_MAGIC
for seg in self . segments :
if seg . include_in_checksum :
checksum = ESPLoader . checksum ( seg . data , checksum )
return checksum
2016-10-06 13:21:30 +02:00
def append_checksum ( self , f , checksum ) :
""" Append ESPLoader checksum to the just-written image """
align_file_position ( f , 16 )
2017-02-06 14:17:11 +01:00
f . write ( struct . pack ( b ' B ' , checksum ) )
2016-10-06 13:21:30 +02:00
def write_common_header ( self , f , segments ) :
f . write ( struct . pack ( ' <BBBBI ' , ESPLoader . ESP_IMAGE_MAGIC , len ( segments ) ,
self . flash_mode , self . flash_size_freq , self . entrypoint ) )
def is_irom_addr ( self , addr ) :
""" Returns True if an address starts in the irom region.
Valid for ESP8266 only .
"""
2016-10-28 00:53:02 +02:00
return ESP8266ROM . IROM_MAP_START < = addr < ESP8266ROM . IROM_MAP_END
2016-10-06 13:21:30 +02:00
def get_irom_segment ( self ) :
2019-10-01 10:21:44 +02:00
irom_segments = [ s for s in self . segments if self . is_irom_addr ( s . addr ) ]
if len ( irom_segments ) > 0 :
if len ( irom_segments ) != 1 :
raise FatalError ( ' Found %d segments that could be irom0. Bad ELF file? ' % len ( irom_segments ) )
return irom_segments [ 0 ]
return None
2016-10-06 13:21:30 +02:00
def get_non_irom_segments ( self ) :
irom_segment = self . get_irom_segment ( )
return [ s for s in self . segments if s != irom_segment ]
2021-04-05 13:23:58 +02:00
def merge_adjacent_segments ( self ) :
if not self . segments :
return # nothing to merge
segments = [ ]
# The easiest way to merge the sections is the browse them backward.
for i in range ( len ( self . segments ) - 1 , 0 , - 1 ) :
# elem is the previous section, the one `next_elem` may need to be
# merged in
elem = self . segments [ i - 1 ]
next_elem = self . segments [ i ]
if all ( ( elem . get_memory_type ( self ) == next_elem . get_memory_type ( self ) ,
elem . include_in_checksum == next_elem . include_in_checksum ,
next_elem . addr == elem . addr + len ( elem . data ) ) ) :
# Merge any segment that ends where the next one starts, without spanning memory types
#
# (don't 'pad' any gaps here as they may be excluded from the image due to 'noinit'
# or other reasons.)
elem . data + = next_elem . data
else :
# The section next_elem cannot be merged into the previous one,
# which means it needs to be part of the final segments.
# As we are browsing the list backward, the elements need to be
# inserted at the beginning of the final list.
segments . insert ( 0 , next_elem )
# The first segment will always be here as it cannot be merged into any
# "previous" section.
segments . insert ( 0 , self . segments [ 0 ] )
# note: we could sort segments here as well, but the ordering of segments is sometimes
# important for other reasons (like embedded ELF SHA-256), so we assume that the linker
# script will have produced any adjacent sections in linear order in the ELF, anyhow.
self . segments = segments
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
class ESP8266ROMFirmwareImage ( BaseFirmwareImage ) :
2016-10-06 13:21:30 +02:00
""" ' Version 1 ' firmware image, segments loaded directly by the ROM bootloader. """
2016-10-28 00:53:02 +02:00
ROM_LOADER = ESP8266ROM
2016-10-06 13:21:30 +02:00
def __init__ ( self , load_file = None ) :
2018-09-21 08:39:36 +02:00
super ( ESP8266ROMFirmwareImage , self ) . __init__ ( )
2016-10-06 13:21:30 +02:00
self . flash_mode = 0
self . flash_size_freq = 0
self . version = 1
if load_file is not None :
segments = self . load_common_header ( load_file , ESPLoader . ESP_IMAGE_MAGIC )
2017-02-06 14:17:11 +01:00
for _ in range ( segments ) :
2016-10-06 13:21:30 +02:00
self . load_segment ( load_file )
self . checksum = self . read_checksum ( load_file )
2019-02-14 16:49:30 +01:00
self . verify ( )
2016-10-06 13:21:30 +02:00
def default_output_name ( self , input_file ) :
""" Derive a default output name from the ELF name. """
return input_file + ' - '
def save ( self , basename ) :
""" Save a set of V1 images for flashing. Parameter is a base filename. """
# IROM data goes in its own plain binary file
irom_segment = self . get_irom_segment ( )
if irom_segment is not None :
2016-10-28 00:53:02 +02:00
with open ( " %s 0x %05x .bin " % ( basename , irom_segment . addr - ESP8266ROM . IROM_MAP_START ) , " wb " ) as f :
2016-10-06 13:21:30 +02:00
f . write ( irom_segment . data )
# everything but IROM goes at 0x00000 in an image file
normal_segments = self . get_non_irom_segments ( )
with open ( " %s 0x00000.bin " % basename , ' wb ' ) as f :
self . write_common_header ( f , normal_segments )
checksum = ESPLoader . ESP_CHECKSUM_MAGIC
2016-10-28 00:53:02 +02:00
for segment in normal_segments :
2016-10-06 13:21:30 +02:00
checksum = self . save_segment ( f , segment , checksum )
self . append_checksum ( f , checksum )
2019-10-01 10:21:44 +02:00
ESP8266ROM . BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage
2018-09-21 08:39:36 +02:00
class ESP8266V2FirmwareImage ( BaseFirmwareImage ) :
2016-10-06 13:21:30 +02:00
""" ' Version 2 ' firmware image, segments loaded by software bootloader stub
( ie Espressif bootloader or rboot )
"""
2016-10-28 00:53:02 +02:00
ROM_LOADER = ESP8266ROM
2016-10-06 13:21:30 +02:00
def __init__ ( self , load_file = None ) :
2018-09-21 08:39:36 +02:00
super ( ESP8266V2FirmwareImage , self ) . __init__ ( )
2016-10-06 13:21:30 +02:00
self . version = 2
if load_file is not None :
segments = self . load_common_header ( load_file , ESPBOOTLOADER . IMAGE_V2_MAGIC )
if segments != ESPBOOTLOADER . IMAGE_V2_SEGMENT :
# segment count is not really segment count here, but we expect to see '4'
2017-02-06 14:17:11 +01:00
print ( ' Warning: V2 header has unexpected " segment " count %d (usually 4) ' % segments )
2016-10-06 13:21:30 +02:00
# irom segment comes before the second header
#
# the file is saved in the image with a zero load address
# in the header, so we need to calculate a load address
irom_segment = self . load_segment ( load_file , True )
2018-09-21 08:39:36 +02:00
irom_segment . addr = 0 # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8
2016-10-28 00:53:02 +02:00
irom_segment . include_in_checksum = False
2016-10-06 13:21:30 +02:00
first_flash_mode = self . flash_mode
first_flash_size_freq = self . flash_size_freq
first_entrypoint = self . entrypoint
# load the second header
2016-10-28 00:53:02 +02:00
segments = self . load_common_header ( load_file , ESPLoader . ESP_IMAGE_MAGIC )
2016-10-06 13:21:30 +02:00
if first_flash_mode != self . flash_mode :
print ( ' WARNING: Flash mode value in first header (0x %02x ) disagrees with second (0x %02x ). Using second value. '
% ( first_flash_mode , self . flash_mode ) )
if first_flash_size_freq != self . flash_size_freq :
print ( ' WARNING: Flash size/freq value in first header (0x %02x ) disagrees with second (0x %02x ). Using second value. '
% ( first_flash_size_freq , self . flash_size_freq ) )
if first_entrypoint != self . entrypoint :
print ( ' WARNING: Entrypoint address in first header (0x %08x ) disagrees with second header (0x %08x ). Using second value. '
% ( first_entrypoint , self . entrypoint ) )
# load all the usual segments
2017-02-06 14:17:11 +01:00
for _ in range ( segments ) :
2016-10-06 13:21:30 +02:00
self . load_segment ( load_file )
self . checksum = self . read_checksum ( load_file )
2019-02-14 16:49:30 +01:00
self . verify ( )
2016-10-06 13:21:30 +02:00
def default_output_name ( self , input_file ) :
""" Derive a default output name from the ELF name. """
irom_segment = self . get_irom_segment ( )
if irom_segment is not None :
2016-10-28 00:53:02 +02:00
irom_offs = irom_segment . addr - ESP8266ROM . IROM_MAP_START
2016-10-06 13:21:30 +02:00
else :
irom_offs = 0
return " %s -0x %05x .bin " % ( os . path . splitext ( input_file ) [ 0 ] ,
2016-12-30 00:28:30 +01:00
irom_offs & ~ ( ESPLoader . FLASH_SECTOR_SIZE - 1 ) )
2016-10-06 13:21:30 +02:00
def save ( self , filename ) :
with open ( filename , ' wb ' ) as f :
# Save first header for irom0 segment
2017-02-06 14:17:11 +01:00
f . write ( struct . pack ( b ' <BBBBI ' , ESPBOOTLOADER . IMAGE_V2_MAGIC , ESPBOOTLOADER . IMAGE_V2_SEGMENT ,
2016-10-06 13:21:30 +02:00
self . flash_mode , self . flash_size_freq , self . entrypoint ) )
irom_segment = self . get_irom_segment ( )
if irom_segment is not None :
# save irom0 segment, make sure it has load addr 0 in the file
irom_segment = irom_segment . copy_with_new_addr ( 0 )
2018-09-21 08:39:36 +02:00
irom_segment . pad_to_alignment ( 16 ) # irom_segment must end on a 16 byte boundary
2016-10-06 13:21:30 +02:00
self . save_segment ( f , irom_segment )
# second header, matches V1 header and contains loadable segments
normal_segments = self . get_non_irom_segments ( )
self . write_common_header ( f , normal_segments )
checksum = ESPLoader . ESP_CHECKSUM_MAGIC
for segment in normal_segments :
checksum = self . save_segment ( f , segment , checksum )
self . append_checksum ( f , checksum )
2018-09-21 08:39:36 +02:00
# calculate a crc32 of entire file and append
# (algorithm used by recent 8266 SDK bootloaders)
with open ( filename , ' rb ' ) as f :
crc = esp8266_crc32 ( f . read ( ) )
with open ( filename , ' ab ' ) as f :
f . write ( struct . pack ( b ' <I ' , crc ) )
# Backwards compatibility for previous API, remove in esptool.py V3
ESPFirmwareImage = ESP8266ROMFirmwareImage
OTAFirmwareImage = ESP8266V2FirmwareImage
def esp8266_crc32 ( data ) :
"""
CRC32 algorithm used by 8266 SDK bootloader ( and gen_appbin . py ) .
"""
crc = binascii . crc32 ( data , 0 ) & 0xFFFFFFFF
if crc & 0x80000000 :
return crc ^ 0xFFFFFFFF
else :
return crc + 1
2016-10-06 13:21:30 +02:00
class ESP32FirmwareImage ( BaseFirmwareImage ) :
""" ESP32 firmware image is very similar to V1 ESP8266 image,
except with an additional 16 byte reserved header at top of image ,
and because of new flash mapping capabilities the flash - mapped regions
can be placed in the normal image ( just @ 64 kB padded offsets ) .
"""
2016-10-28 00:53:02 +02:00
ROM_LOADER = ESP32ROM
2017-08-01 07:51:04 +02:00
# ROM bootloader will read the wp_pin field if SPI flash
# pins are remapped via flash. IDF actually enables QIO only
# from software bootloader, so this can be ignored. But needs
# to be set to this value so ROM bootloader will skip it.
WP_PIN_DISABLED = 0xEE
2019-10-01 10:21:44 +02:00
EXTENDED_HEADER_STRUCT_FMT = " <BBBBHB " + ( " B " * 8 ) + " B "
2017-08-01 07:51:04 +02:00
2019-02-14 16:49:30 +01:00
IROM_ALIGN = 65536
2016-10-06 13:21:30 +02:00
def __init__ ( self , load_file = None ) :
super ( ESP32FirmwareImage , self ) . __init__ ( )
2021-01-11 13:20:02 +01:00
self . secure_pad = None
2016-10-06 13:21:30 +02:00
self . flash_mode = 0
self . flash_size_freq = 0
self . version = 1
2017-08-01 07:51:04 +02:00
self . wp_pin = self . WP_PIN_DISABLED
# SPI pin drive levels
self . clk_drv = 0
self . q_drv = 0
self . d_drv = 0
self . cs_drv = 0
self . hd_drv = 0
self . wp_drv = 0
2019-10-01 10:21:44 +02:00
self . min_rev = 0
2017-08-01 07:51:04 +02:00
self . append_digest = True
2016-10-06 13:21:30 +02:00
if load_file is not None :
2017-08-01 07:51:04 +02:00
start = load_file . tell ( )
2016-12-30 00:28:30 +01:00
2017-08-01 07:51:04 +02:00
segments = self . load_common_header ( load_file , ESPLoader . ESP_IMAGE_MAGIC )
self . load_extended_header ( load_file )
2016-10-06 13:21:30 +02:00
2017-02-06 14:17:11 +01:00
for _ in range ( segments ) :
2016-10-06 13:21:30 +02:00
self . load_segment ( load_file )
self . checksum = self . read_checksum ( load_file )
2017-08-01 07:51:04 +02:00
if self . append_digest :
end = load_file . tell ( )
2017-09-12 08:40:52 +02:00
self . stored_digest = load_file . read ( 32 )
2017-08-01 07:51:04 +02:00
load_file . seek ( start )
calc_digest = hashlib . sha256 ( )
calc_digest . update ( load_file . read ( end - start ) )
self . calc_digest = calc_digest . digest ( ) # TODO: decide what to do here?
2019-02-14 16:49:30 +01:00
self . verify ( )
2016-10-06 13:21:30 +02:00
def is_flash_addr ( self , addr ) :
2021-01-11 13:20:02 +01:00
return ( self . ROM_LOADER . IROM_MAP_START < = addr < self . ROM_LOADER . IROM_MAP_END ) \
or ( self . ROM_LOADER . DROM_MAP_START < = addr < self . ROM_LOADER . DROM_MAP_END )
2016-10-06 13:21:30 +02:00
def default_output_name ( self , input_file ) :
""" Derive a default output name from the ELF name. """
return " %s .bin " % ( os . path . splitext ( input_file ) [ 0 ] )
2016-10-28 00:53:02 +02:00
def warn_if_unusual_segment ( self , offset , size , is_irom_segment ) :
pass # TODO: add warnings for ESP32 segment offset/size combinations that are wrong
2016-10-06 13:21:30 +02:00
def save ( self , filename ) :
2017-08-01 07:51:04 +02:00
total_segments = 0
with io . BytesIO ( ) as f : # write file to memory first
2016-10-06 13:21:30 +02:00
self . write_common_header ( f , self . segments )
2016-12-30 00:28:30 +01:00
2017-03-10 14:40:14 +01:00
# first 4 bytes of header are read by ROM bootloader for SPI
# config, but currently unused
2017-08-01 07:51:04 +02:00
self . save_extended_header ( f )
2016-10-06 13:21:30 +02:00
checksum = ESPLoader . ESP_CHECKSUM_MAGIC
2017-08-01 07:51:04 +02:00
# split segments into flash-mapped vs ram-loaded, and take copies so we can mutate them
flash_segments = [ copy . deepcopy ( s ) for s in sorted ( self . segments , key = lambda s : s . addr ) if self . is_flash_addr ( s . addr ) ]
ram_segments = [ copy . deepcopy ( s ) for s in sorted ( self . segments , key = lambda s : s . addr ) if not self . is_flash_addr ( s . addr ) ]
# check for multiple ELF sections that are mapped in the same flash mapping region.
# this is usually a sign of a broken linker script, but if you have a legitimate
2021-04-05 13:23:58 +02:00
# use case then let us know
2017-08-01 07:51:04 +02:00
if len ( flash_segments ) > 0 :
last_addr = flash_segments [ 0 ] . addr
for segment in flash_segments [ 1 : ] :
2019-02-14 16:49:30 +01:00
if segment . addr / / self . IROM_ALIGN == last_addr / / self . IROM_ALIGN :
2021-04-05 13:23:58 +02:00
raise FatalError ( ( " Segment loaded at 0x %08x lands in same 64KB flash mapping as segment loaded at 0x %08x . "
2017-08-01 07:51:04 +02:00
" Can ' t generate binary. Suggest changing linker script or ELF to merge sections. " ) %
( segment . addr , last_addr ) )
last_addr = segment . addr
def get_alignment_data_needed ( segment ) :
# Actual alignment (in data bytes) required for a segment header: positioned so that
# after we write the next 8 byte header, file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN
2016-10-06 13:21:30 +02:00
#
2017-08-01 07:51:04 +02:00
# (this is because the segment's vaddr may not be IROM_ALIGNed, more likely is aligned
# IROM_ALIGN+0x18 to account for the binary file header
2019-02-14 16:49:30 +01:00
align_past = ( segment . addr % self . IROM_ALIGN ) - self . SEG_HEADER_LEN
pad_len = ( self . IROM_ALIGN - ( f . tell ( ) % self . IROM_ALIGN ) ) + align_past
if pad_len == 0 or pad_len == self . IROM_ALIGN :
2017-08-01 07:51:04 +02:00
return 0 # already aligned
# subtract SEG_HEADER_LEN a second time, as the padding block has a header as well
pad_len - = self . SEG_HEADER_LEN
if pad_len < 0 :
2019-02-14 16:49:30 +01:00
pad_len + = self . IROM_ALIGN
2017-08-01 07:51:04 +02:00
return pad_len
# try to fit each flash segment on a 64kB aligned boundary
# by padding with parts of the non-flash segments...
while len ( flash_segments ) > 0 :
segment = flash_segments [ 0 ]
pad_len = get_alignment_data_needed ( segment )
if pad_len > 0 : # need to pad
if len ( ram_segments ) > 0 and pad_len > self . SEG_HEADER_LEN :
pad_segment = ram_segments [ 0 ] . split_image ( pad_len )
if len ( ram_segments [ 0 ] . data ) == 0 :
ram_segments . pop ( 0 )
else :
pad_segment = ImageSegment ( 0 , b ' \x00 ' * pad_len , f . tell ( ) )
checksum = self . save_segment ( f , pad_segment , checksum )
total_segments + = 1
else :
# write the flash segment
2019-02-14 16:49:30 +01:00
assert ( f . tell ( ) + 8 ) % self . IROM_ALIGN == segment . addr % self . IROM_ALIGN
checksum = self . save_flash_segment ( f , segment , checksum )
2017-08-01 07:51:04 +02:00
flash_segments . pop ( 0 )
total_segments + = 1
# flash segments all written, so write any remaining RAM segments
for segment in ram_segments :
2016-10-06 13:21:30 +02:00
checksum = self . save_segment ( f , segment , checksum )
2017-08-01 07:51:04 +02:00
total_segments + = 1
2018-09-21 08:39:36 +02:00
if self . secure_pad :
# pad the image so that after signing it will end on a a 64KB boundary.
# This ensures all mapped flash content will be verified.
if not self . append_digest :
raise FatalError ( " secure_pad only applies if a SHA-256 digest is also appended to the image " )
2019-02-14 16:49:30 +01:00
align_past = ( f . tell ( ) + self . SEG_HEADER_LEN ) % self . IROM_ALIGN
2018-09-21 08:39:36 +02:00
# 16 byte aligned checksum (force the alignment to simplify calculations)
checksum_space = 16
2021-01-11 13:20:02 +01:00
if self . secure_pad == ' 1 ' :
# after checksum: SHA-256 digest + (to be added by signing process) version, signature + 12 trailing bytes due to alignment
space_after_checksum = 32 + 4 + 64 + 12
elif self . secure_pad == ' 2 ' : # Secure Boot V2
# after checksum: SHA-256 digest + signature sector, but we place signature sector after the 64KB boundary
space_after_checksum = 32
2019-02-14 16:49:30 +01:00
pad_len = ( self . IROM_ALIGN - align_past - checksum_space - space_after_checksum ) % self . IROM_ALIGN
2018-09-21 08:39:36 +02:00
pad_segment = ImageSegment ( 0 , b ' \x00 ' * pad_len , f . tell ( ) )
checksum = self . save_segment ( f , pad_segment , checksum )
total_segments + = 1
2017-08-01 07:51:04 +02:00
# done writing segments
2016-10-06 13:21:30 +02:00
self . append_checksum ( f , checksum )
2018-09-21 08:39:36 +02:00
image_length = f . tell ( )
if self . secure_pad :
2019-02-14 16:49:30 +01:00
assert ( ( image_length + space_after_checksum ) % self . IROM_ALIGN ) == 0
2018-09-21 08:39:36 +02:00
2016-10-06 13:21:30 +02:00
# kinda hacky: go back to the initial header and write the new segment count
2017-08-01 07:51:04 +02:00
# that includes padding segments. This header is not checksummed
2016-10-06 13:21:30 +02:00
f . seek ( 1 )
2017-02-06 14:17:11 +01:00
try :
2017-08-01 07:51:04 +02:00
f . write ( chr ( total_segments ) )
2017-02-06 14:17:11 +01:00
except TypeError : # Python 3
2017-08-01 07:51:04 +02:00
f . write ( bytes ( [ total_segments ] ) )
if self . append_digest :
# calculate the SHA256 of the whole file and append it
f . seek ( 0 )
digest = hashlib . sha256 ( )
digest . update ( f . read ( image_length ) )
f . write ( digest . digest ( ) )
with open ( filename , ' wb ' ) as real_file :
real_file . write ( f . getvalue ( ) )
2019-02-14 16:49:30 +01:00
def save_flash_segment ( self , f , segment , checksum = None ) :
""" Save the next segment to the image file, return next checksum value if provided """
segment_end_pos = f . tell ( ) + len ( segment . data ) + self . SEG_HEADER_LEN
segment_len_remainder = segment_end_pos % self . IROM_ALIGN
if segment_len_remainder < 0x24 :
# Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the
# last MMU page, if an IROM/DROM segment was < 0x24 bytes over the page boundary.
segment . data + = b ' \x00 ' * ( 0x24 - segment_len_remainder )
return self . save_segment ( f , segment , checksum )
2017-08-01 07:51:04 +02:00
def load_extended_header ( self , load_file ) :
def split_byte ( n ) :
return ( n & 0x0F , ( n >> 4 ) & 0x0F )
fields = list ( struct . unpack ( self . EXTENDED_HEADER_STRUCT_FMT , load_file . read ( 16 ) ) )
self . wp_pin = fields [ 0 ]
# SPI pin drive stengths are two per byte
self . clk_drv , self . q_drv = split_byte ( fields [ 1 ] )
self . d_drv , self . cs_drv = split_byte ( fields [ 2 ] )
self . hd_drv , self . wp_drv = split_byte ( fields [ 3 ] )
2019-10-01 10:21:44 +02:00
chip_id = fields [ 4 ]
if chip_id != self . ROM_LOADER . IMAGE_CHIP_ID :
2021-04-05 13:23:58 +02:00
print ( ( " Unexpected chip id in image. Expected %d but value was %d . "
" Is this image for a different chip model? " ) % ( self . ROM_LOADER . IMAGE_CHIP_ID , chip_id ) )
2017-08-01 07:51:04 +02:00
2019-10-01 10:21:44 +02:00
# reserved fields in the middle should all be zero
if any ( f for f in fields [ 6 : - 1 ] if f != 0 ) :
2017-08-01 07:51:04 +02:00
print ( " Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py? " )
2019-10-01 10:21:44 +02:00
append_digest = fields [ - 1 ] # last byte is append_digest
if append_digest in [ 0 , 1 ] :
self . append_digest = ( append_digest == 1 )
else :
raise RuntimeError ( " Invalid value for append_digest field (0x %02x ). Should be 0 or 1. " , append_digest )
2017-08-01 07:51:04 +02:00
def save_extended_header ( self , save_file ) :
2021-04-05 13:23:58 +02:00
def join_byte ( ln , hn ) :
2017-08-01 07:51:04 +02:00
return ( ln & 0x0F ) + ( ( hn & 0x0F ) << 4 )
append_digest = 1 if self . append_digest else 0
fields = [ self . wp_pin ,
join_byte ( self . clk_drv , self . q_drv ) ,
join_byte ( self . d_drv , self . cs_drv ) ,
2019-10-01 10:21:44 +02:00
join_byte ( self . hd_drv , self . wp_drv ) ,
self . ROM_LOADER . IMAGE_CHIP_ID ,
self . min_rev ]
fields + = [ 0 ] * 8 # padding
2017-08-01 07:51:04 +02:00
fields + = [ append_digest ]
packed = struct . pack ( self . EXTENDED_HEADER_STRUCT_FMT , * fields )
save_file . write ( packed )
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
ESP32ROM . BOOTLOADER_IMAGE = ESP32FirmwareImage
2021-01-11 13:20:02 +01:00
class ESP32S2FirmwareImage ( ESP32FirmwareImage ) :
""" ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage """
ROM_LOADER = ESP32S2ROM
2021-04-05 13:23:58 +02:00
ESP32S2ROM . BOOTLOADER_IMAGE = ESP32S2FirmwareImage
class ESP32S3BETA2FirmwareImage ( ESP32FirmwareImage ) :
""" ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """
ROM_LOADER = ESP32S3BETA2ROM
ESP32S3BETA2ROM . BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage
class ESP32S3BETA3FirmwareImage ( ESP32FirmwareImage ) :
""" ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage """
ROM_LOADER = ESP32S3BETA3ROM
ESP32S3BETA3ROM . BOOTLOADER_IMAGE = ESP32S3BETA3FirmwareImage
class ESP32C3FirmwareImage ( ESP32FirmwareImage ) :
""" ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage """
ROM_LOADER = ESP32C3ROM
ESP32C3ROM . BOOTLOADER_IMAGE = ESP32C3FirmwareImage
2021-05-31 15:32:51 +02:00
class ESP32C6BETAFirmwareImage ( ESP32FirmwareImage ) :
""" ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage """
ROM_LOADER = ESP32C6BETAROM
ESP32C6BETAROM . BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage
2016-10-06 13:21:30 +02:00
class ELFFile ( object ) :
SEC_TYPE_PROGBITS = 0x01
SEC_TYPE_STRTAB = 0x03
2017-08-01 07:51:04 +02:00
LEN_SEC_HEADER = 0x28
2021-04-05 13:23:58 +02:00
SEG_TYPE_LOAD = 0x01
LEN_SEG_HEADER = 0x20
2016-10-06 13:21:30 +02:00
def __init__ ( self , name ) :
# Load sections from the ELF file
self . name = name
with open ( self . name , ' rb ' ) as f :
self . _read_elf_file ( f )
def get_section ( self , section_name ) :
for s in self . sections :
if s . name == section_name :
return s
raise ValueError ( " No section %s in ELF file " % section_name )
def _read_elf_file ( self , f ) :
# read the ELF file header
LEN_FILE_HEADER = 0x34
try :
2021-04-05 13:23:58 +02:00
( ident , _type , machine , _version ,
self . entrypoint , _phoff , shoff , _flags ,
_ehsize , _phentsize , _phnum , shentsize ,
shnum , shstrndx ) = struct . unpack ( " <16sHHLLLLLHHHHHH " , f . read ( LEN_FILE_HEADER ) )
2016-10-06 13:21:30 +02:00
except struct . error as e :
raise FatalError ( " Failed to read a valid ELF header from %s : %s " % ( self . name , e ) )
2017-02-06 14:17:11 +01:00
if byte ( ident , 0 ) != 0x7f or ident [ 1 : 4 ] != b ' ELF ' :
2016-10-06 13:21:30 +02:00
raise FatalError ( " %s has invalid ELF magic header " % self . name )
2021-04-05 13:23:58 +02:00
if machine not in [ 0x5e , 0xf3 ] :
raise FatalError ( " %s does not appear to be an Xtensa or an RISCV ELF file. e_machine= %04x " % ( self . name , machine ) )
2017-08-01 07:51:04 +02:00
if shentsize != self . LEN_SEC_HEADER :
2021-01-11 13:20:02 +01:00
raise FatalError ( " %s has unexpected section header entry size 0x %x (not 0x %x ) " % ( self . name , shentsize , self . LEN_SEC_HEADER ) )
2017-08-01 07:51:04 +02:00
if shnum == 0 :
raise FatalError ( " %s has 0 section headers " % ( self . name ) )
self . _read_sections ( f , shoff , shnum , shstrndx )
2021-04-05 13:23:58 +02:00
self . _read_segments ( f , _phoff , _phnum , shstrndx )
2016-10-06 13:21:30 +02:00
2017-08-01 07:51:04 +02:00
def _read_sections ( self , f , section_header_offs , section_header_count , shstrndx ) :
2016-10-06 13:21:30 +02:00
f . seek ( section_header_offs )
2017-08-01 07:51:04 +02:00
len_bytes = section_header_count * self . LEN_SEC_HEADER
section_header = f . read ( len_bytes )
2016-10-06 13:21:30 +02:00
if len ( section_header ) == 0 :
raise FatalError ( " No section header found at offset %04x in ELF file. " % section_header_offs )
2017-08-01 07:51:04 +02:00
if len ( section_header ) != ( len_bytes ) :
raise FatalError ( " Only read 0x %x bytes from section header (expected 0x %x .) Truncated ELF file? " % ( len ( section_header ) , len_bytes ) )
2016-10-06 13:21:30 +02:00
# walk through the section header and extract all sections
2017-08-01 07:51:04 +02:00
section_header_offsets = range ( 0 , len ( section_header ) , self . LEN_SEC_HEADER )
2016-10-06 13:21:30 +02:00
def read_section_header ( offs ) :
2021-04-05 13:23:58 +02:00
name_offs , sec_type , _flags , lma , sec_offs , size = struct . unpack_from ( " <LLLLLL " , section_header [ offs : ] )
2016-10-06 13:21:30 +02:00
return ( name_offs , sec_type , lma , size , sec_offs )
all_sections = [ read_section_header ( offs ) for offs in section_header_offsets ]
prog_sections = [ s for s in all_sections if s [ 1 ] == ELFFile . SEC_TYPE_PROGBITS ]
# search for the string table section
2017-08-01 07:51:04 +02:00
if not ( shstrndx * self . LEN_SEC_HEADER ) in section_header_offsets :
2016-10-06 13:21:30 +02:00
raise FatalError ( " ELF file has no STRTAB section at shstrndx %d " % shstrndx )
2021-04-05 13:23:58 +02:00
_ , sec_type , _ , sec_size , sec_offs = read_section_header ( shstrndx * self . LEN_SEC_HEADER )
2016-10-06 13:21:30 +02:00
if sec_type != ELFFile . SEC_TYPE_STRTAB :
2017-02-06 14:17:11 +01:00
print ( ' WARNING: ELF file has incorrect STRTAB section type 0x %02x ' % sec_type )
2016-10-06 13:21:30 +02:00
f . seek ( sec_offs )
string_table = f . read ( sec_size )
# build the real list of ELFSections by reading the actual section names from the
# string table section, and actual data for each section from the ELF file itself
def lookup_string ( offs ) :
raw = string_table [ offs : ]
2017-02-06 14:17:11 +01:00
return raw [ : raw . index ( b ' \x00 ' ) ]
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
def read_data ( offs , size ) :
2016-10-06 13:21:30 +02:00
f . seek ( offs )
return f . read ( size )
prog_sections = [ ELFSection ( lookup_string ( n_offs ) , lma , read_data ( offs , size ) ) for ( n_offs , _type , lma , size , offs ) in prog_sections
2019-02-14 16:49:30 +01:00
if lma != 0 and size > 0 ]
2016-10-06 13:21:30 +02:00
self . sections = prog_sections
2021-04-05 13:23:58 +02:00
def _read_segments ( self , f , segment_header_offs , segment_header_count , shstrndx ) :
f . seek ( segment_header_offs )
len_bytes = segment_header_count * self . LEN_SEG_HEADER
segment_header = f . read ( len_bytes )
if len ( segment_header ) == 0 :
raise FatalError ( " No segment header found at offset %04x in ELF file. " % segment_header_offs )
if len ( segment_header ) != ( len_bytes ) :
raise FatalError ( " Only read 0x %x bytes from segment header (expected 0x %x .) Truncated ELF file? " % ( len ( segment_header ) , len_bytes ) )
# walk through the segment header and extract all segments
segment_header_offsets = range ( 0 , len ( segment_header ) , self . LEN_SEG_HEADER )
def read_segment_header ( offs ) :
seg_type , seg_offs , _vaddr , lma , size , _memsize , _flags , _align = struct . unpack_from ( " <LLLLLLLL " , segment_header [ offs : ] )
return ( seg_type , lma , size , seg_offs )
all_segments = [ read_segment_header ( offs ) for offs in segment_header_offsets ]
prog_segments = [ s for s in all_segments if s [ 0 ] == ELFFile . SEG_TYPE_LOAD ]
def read_data ( offs , size ) :
f . seek ( offs )
return f . read ( size )
prog_segments = [ ELFSection ( b ' PHDR ' , lma , read_data ( offs , size ) ) for ( _type , lma , size , offs ) in prog_segments
if lma != 0 and size > 0 ]
self . segments = prog_segments
2019-02-14 16:49:30 +01:00
def sha256 ( self ) :
# return SHA256 hash of the input ELF file
sha256 = hashlib . sha256 ( )
with open ( self . name , ' rb ' ) as f :
sha256 . update ( f . read ( ) )
return sha256 . digest ( )
2016-10-06 13:21:30 +02:00
2018-04-07 08:45:18 +02:00
def slip_reader ( port , trace_function ) :
2016-10-06 13:21:30 +02:00
""" Generator to read SLIP packets from a serial port.
Yields one full SLIP packet at a time , raises exception on timeout or invalid data .
Designed to avoid too many calls to serial . read ( 1 ) , which can bog
down on slow systems .
"""
partial_packet = None
in_escape = False
while True :
waiting = port . inWaiting ( )
read_bytes = port . read ( 1 if waiting == 0 else waiting )
2017-02-06 14:17:11 +01:00
if read_bytes == b ' ' :
2018-04-07 08:45:18 +02:00
waiting_for = " header " if partial_packet is None else " content "
trace_function ( " Timed out waiting for packet %s " , waiting_for )
raise FatalError ( " Timed out waiting for packet %s " % waiting_for )
2018-09-21 08:39:36 +02:00
trace_function ( " Read %d bytes: %s " , len ( read_bytes ) , HexFormatter ( read_bytes ) )
2016-10-06 13:21:30 +02:00
for b in read_bytes :
2017-02-06 14:17:11 +01:00
if type ( b ) is int :
b = bytes ( [ b ] ) # python 2/3 compat
2016-10-06 13:21:30 +02:00
if partial_packet is None : # waiting for packet header
2017-02-06 14:17:11 +01:00
if b == b ' \xc0 ' :
partial_packet = b " "
2016-10-06 13:21:30 +02:00
else :
2018-09-21 08:39:36 +02:00
trace_function ( " Read invalid data: %s " , HexFormatter ( read_bytes ) )
trace_function ( " Remaining data in serial buffer: %s " , HexFormatter ( port . read ( port . inWaiting ( ) ) ) )
raise FatalError ( ' Invalid head of packet (0x %s ) ' % hexify ( b ) )
2016-10-06 13:21:30 +02:00
elif in_escape : # part-way through escape sequence
in_escape = False
2017-02-06 14:17:11 +01:00
if b == b ' \xdc ' :
partial_packet + = b ' \xc0 '
elif b == b ' \xdd ' :
partial_packet + = b ' \xdb '
2016-10-06 13:21:30 +02:00
else :
2018-09-21 08:39:36 +02:00
trace_function ( " Read invalid data: %s " , HexFormatter ( read_bytes ) )
trace_function ( " Remaining data in serial buffer: %s " , HexFormatter ( port . read ( port . inWaiting ( ) ) ) )
raise FatalError ( ' Invalid SLIP escape (0xdb, 0x %s ) ' % ( hexify ( b ) ) )
2017-02-06 14:17:11 +01:00
elif b == b ' \xdb ' : # start of escape sequence
2016-10-06 13:21:30 +02:00
in_escape = True
2017-02-06 14:17:11 +01:00
elif b == b ' \xc0 ' : # end of packet
2018-09-21 08:39:36 +02:00
trace_function ( " Received full packet: %s " , HexFormatter ( partial_packet ) )
2016-10-06 13:21:30 +02:00
yield partial_packet
partial_packet = None
else : # normal byte in packet
partial_packet + = b
def arg_auto_int ( x ) :
return int ( x , 0 )
def div_roundup ( a , b ) :
""" Return a/b rounded up to nearest integer,
equivalent result to int ( math . ceil ( float ( int ( a ) ) / float ( int ( b ) ) ) , only
without possible floating point accuracy errors .
"""
2017-02-06 14:17:11 +01:00
return ( int ( a ) + int ( b ) - 1 ) / / int ( b )
2016-10-06 13:21:30 +02:00
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
def align_file_position ( f , size ) :
""" Align the position in the file to the next block of specified size """
align = ( size - 1 ) - ( f . tell ( ) % size )
f . seek ( align , 1 )
def flash_size_bytes ( size ) :
""" Given a flash size of the type passed in args.flash_size
( ie 512 KB or 1 MB ) then return the size in bytes .
"""
if " MB " in size :
return int ( size [ : size . index ( " MB " ) ] ) * 1024 * 1024
elif " KB " in size :
return int ( size [ : size . index ( " KB " ) ] ) * 1024
else :
raise FatalError ( " Unknown size %s " % size )
2018-09-21 08:39:36 +02:00
def hexify ( s , uppercase = True ) :
format_str = ' %02X ' if uppercase else ' %02x '
2017-02-06 14:17:11 +01:00
if not PYTHON2 :
2018-09-21 08:39:36 +02:00
return ' ' . join ( format_str % c for c in s )
2017-02-06 14:17:11 +01:00
else :
2018-09-21 08:39:36 +02:00
return ' ' . join ( format_str % ord ( c ) for c in s )
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
class HexFormatter ( object ) :
"""
Wrapper class which takes binary data in its constructor
and returns a hex string as it ' s __str__ method.
2017-02-06 14:17:11 +01:00
2018-09-21 08:39:36 +02:00
This is intended for " lazy formatting " of trace ( ) output
in hex format . Avoids overhead ( significant on slow computers )
of generating long hex strings even if tracing is disabled .
2017-02-06 14:17:11 +01:00
2018-09-21 08:39:36 +02:00
Note that this doesn ' t save any overhead if passed as an
argument to " % " , only when passed to trace ( )
2017-02-06 14:17:11 +01:00
2018-09-21 08:39:36 +02:00
If auto_split is set ( default ) , any long line ( > 16 bytes ) will be
printed as separately indented lines , with ASCII decoding at the end
of each line .
"""
def __init__ ( self , binary_string , auto_split = True ) :
self . _s = binary_string
self . _auto_split = auto_split
def __str__ ( self ) :
if self . _auto_split and len ( self . _s ) > 16 :
result = " "
s = self . _s
while len ( s ) > 0 :
line = s [ : 16 ]
ascii_line = " " . join ( c if ( c == ' ' or ( c in string . printable and c not in string . whitespace ) )
else ' . ' for c in line . decode ( ' ascii ' , ' replace ' ) )
s = s [ 16 : ]
result + = " \n %-16s %-16s | %s " % ( hexify ( line [ : 8 ] , False ) , hexify ( line [ 8 : ] , False ) , ascii_line )
return result
else :
return hexify ( self . _s , False )
2016-10-06 13:21:30 +02:00
2017-03-10 14:40:14 +01:00
def pad_to ( data , alignment , pad_character = b ' \xFF ' ) :
""" Pad to the next alignment boundary """
pad_mod = len ( data ) % alignment
if pad_mod != 0 :
data + = pad_character * ( alignment - pad_mod )
return data
2016-10-06 13:21:30 +02:00
class FatalError ( RuntimeError ) :
"""
Wrapper class for runtime errors that aren ' t caused by internal bugs, but by
ESP8266 responses or input content .
"""
def __init__ ( self , message ) :
RuntimeError . __init__ ( self , message )
@staticmethod
def WithResult ( message , result ) :
"""
Return a fatal error object that appends the hex values of
' result ' as a string formatted argument .
"""
2017-02-06 14:17:11 +01:00
message + = " (result was %s ) " % hexify ( result )
2016-10-06 13:21:30 +02:00
return FatalError ( message )
class NotImplementedInROMError ( FatalError ) :
"""
Wrapper class for the error thrown when a particular ESP bootloader function
is not implemented in the ROM bootloader .
"""
2016-12-30 00:28:30 +01:00
def __init__ ( self , bootloader , func ) :
2017-02-06 14:17:11 +01:00
FatalError . __init__ ( self , " %s ROM does not support function %s . " % ( bootloader . CHIP_NAME , func . __name__ ) )
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
class NotSupportedError ( FatalError ) :
def __init__ ( self , esp , function_name ) :
FatalError . __init__ ( self , " Function %s is not supported for %s . " % ( function_name , esp . CHIP_NAME ) )
2016-10-06 13:21:30 +02:00
# "Operation" commands, executable at command line. One function each
#
# Each function takes either two args (<ESPLoader instance>, <args>) or a single <args>
# argument.
2021-04-05 13:23:58 +02:00
class UnsupportedCommandError ( RuntimeError ) :
2021-01-11 13:20:02 +01:00
"""
Wrapper class for when ROM loader returns an invalid command response .
2021-04-05 13:23:58 +02:00
Usually this indicates the loader is running in Secure Download Mode .
2021-01-11 13:20:02 +01:00
"""
2021-04-05 13:23:58 +02:00
def __init__ ( self , esp , op ) :
if esp . secure_download_mode :
msg = " This command (0x %x ) is not supported in Secure Download Mode " % op
else :
msg = " Invalid (unsupported) command 0x %x " % op
RuntimeError . __init__ ( self , msg )
2021-01-11 13:20:02 +01:00
2016-10-06 13:21:30 +02:00
def load_ram ( esp , args ) :
2018-09-21 08:39:36 +02:00
image = LoadFirmwareImage ( esp . CHIP_NAME , args . filename )
2016-10-06 13:21:30 +02:00
2017-02-06 14:17:11 +01:00
print ( ' RAM boot... ' )
2018-09-21 08:39:36 +02:00
for seg in image . segments :
size = len ( seg . data )
print ( ' Downloading %d bytes at %08x ... ' % ( size , seg . addr ) , end = ' ' )
2016-10-06 13:21:30 +02:00
sys . stdout . flush ( )
2018-09-21 08:39:36 +02:00
esp . mem_begin ( size , div_roundup ( size , esp . ESP_RAM_BLOCK ) , esp . ESP_RAM_BLOCK , seg . addr )
2016-10-06 13:21:30 +02:00
seq = 0
2018-09-21 08:39:36 +02:00
while len ( seg . data ) > 0 :
esp . mem_block ( seg . data [ 0 : esp . ESP_RAM_BLOCK ] , seq )
seg . data = seg . data [ esp . ESP_RAM_BLOCK : ]
2016-10-06 13:21:30 +02:00
seq + = 1
2017-02-06 14:17:11 +01:00
print ( ' done! ' )
2016-10-06 13:21:30 +02:00
2017-02-06 14:17:11 +01:00
print ( ' All segments done, executing at %08x ' % image . entrypoint )
2016-10-06 13:21:30 +02:00
esp . mem_finish ( image . entrypoint )
def read_mem ( esp , args ) :
2017-02-06 14:17:11 +01:00
print ( ' 0x %08x = 0x %08x ' % ( args . address , esp . read_reg ( args . address ) ) )
2016-10-06 13:21:30 +02:00
def write_mem ( esp , args ) :
esp . write_reg ( args . address , args . value , args . mask , 0 )
2017-02-06 14:17:11 +01:00
print ( ' Wrote %08x , mask %08x to %08x ' % ( args . value , args . mask , args . address ) )
2016-10-06 13:21:30 +02:00
def dump_mem ( esp , args ) :
2019-02-14 16:49:30 +01:00
with open ( args . filename , ' wb ' ) as f :
for i in range ( args . size / / 4 ) :
d = esp . read_reg ( args . address + ( i * 4 ) )
f . write ( struct . pack ( b ' <I ' , d ) )
if f . tell ( ) % 1024 == 0 :
2021-01-11 13:20:02 +01:00
print_overwrite ( ' %d bytes read... ( %d %% ) ' % ( f . tell ( ) ,
f . tell ( ) * 100 / / args . size ) )
2019-02-14 16:49:30 +01:00
sys . stdout . flush ( )
2021-04-05 13:23:58 +02:00
print_overwrite ( " Read %d bytes " % f . tell ( ) , last_line = True )
2017-02-06 14:17:11 +01:00
print ( ' Done! ' )
2016-10-06 13:21:30 +02:00
2017-01-05 12:54:40 +01:00
def detect_flash_size ( esp , args ) :
if args . flash_size == ' detect ' :
2021-04-05 13:23:58 +02:00
if esp . secure_download_mode :
raise FatalError ( " Detecting flash size is not supported in secure download mode. Need to manually specify flash size. " )
2017-01-05 12:54:40 +01:00
flash_id = esp . flash_id ( )
size_id = flash_id >> 16
2017-08-01 07:51:04 +02:00
args . flash_size = DETECTED_FLASH_SIZES . get ( size_id )
2017-01-05 12:54:40 +01:00
if args . flash_size is None :
2017-02-06 14:17:11 +01:00
print ( ' Warning: Could not auto-detect Flash size (FlashID=0x %x , SizeID=0x %x ), defaulting to 4MB ' % ( flash_id , size_id ) )
args . flash_size = ' 4MB '
2017-01-05 12:54:40 +01:00
else :
2017-02-06 14:17:11 +01:00
print ( ' Auto-detected Flash size: ' , args . flash_size )
2017-01-05 12:54:40 +01:00
2017-08-01 07:51:04 +02:00
def _update_image_flash_params ( esp , address , args , image ) :
""" Modify the flash mode & size bytes if this looks like an executable bootloader image """
if len ( image ) < 8 :
return image # not long enough to be a bootloader image
2017-01-05 12:54:40 +01:00
2017-08-01 07:51:04 +02:00
# unpack the (potential) image header
magic , _ , flash_mode , flash_size_freq = struct . unpack ( " BBBB " , image [ : 4 ] )
2019-10-01 10:21:44 +02:00
if address != esp . BOOTLOADER_FLASH_OFFSET :
return image # not flashing bootloader offset, so don't modify this
if ( args . flash_mode , args . flash_freq , args . flash_size ) == ( ' keep ' , ) * 3 :
return image # all settings are 'keep', not modifying anything
# easy check if this is an image: does it start with a magic byte?
if magic != esp . ESP_IMAGE_MAGIC :
print ( " Warning: Image file at 0x %x doesn ' t look like an image file, so not changing any flash settings. " % address )
return image
# make sure this really is an image, and not just data that
# starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted
# images that happen to start with a magic byte
try :
test_image = esp . BOOTLOADER_IMAGE ( io . BytesIO ( image ) )
test_image . verify ( )
except Exception :
2021-04-05 13:23:58 +02:00
print ( " Warning: Image file at 0x %x is not a valid %s image, so not changing any flash settings. " % ( address , esp . CHIP_NAME ) )
2019-10-01 10:21:44 +02:00
return image
2017-02-06 14:17:11 +01:00
2017-08-01 07:51:04 +02:00
if args . flash_mode != ' keep ' :
2021-04-05 13:23:58 +02:00
flash_mode = { ' qio ' : 0 , ' qout ' : 1 , ' dio ' : 2 , ' dout ' : 3 } [ args . flash_mode ]
2017-02-06 14:17:11 +01:00
2017-08-01 07:51:04 +02:00
flash_freq = flash_size_freq & 0x0F
if args . flash_freq != ' keep ' :
2021-04-05 13:23:58 +02:00
flash_freq = { ' 40m ' : 0 , ' 26m ' : 1 , ' 20m ' : 2 , ' 80m ' : 0xf } [ args . flash_freq ]
2017-08-01 07:51:04 +02:00
flash_size = flash_size_freq & 0xF0
if args . flash_size != ' keep ' :
flash_size = esp . parse_flash_size_arg ( args . flash_size )
flash_params = struct . pack ( b ' BB ' , flash_mode , flash_size + flash_freq )
if flash_params != image [ 2 : 4 ] :
2017-02-06 14:17:11 +01:00
print ( ' Flash params set to 0x %04x ' % struct . unpack ( " >H " , flash_params ) )
image = image [ 0 : 2 ] + flash_params + image [ 4 : ]
return image
def write_flash ( esp , args ) :
# set args.compress based on default behaviour:
# -> if either --compress or --no-compress is set, honour that
# -> otherwise, set --compress unless --no-stub is set
if args . compress is None and not args . no_compress :
args . compress = not args . no_stub
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
# In case we have encrypted files to write, we first do few sanity checks before actual flash
if args . encrypt or args . encrypt_files is not None :
2019-10-01 10:21:44 +02:00
do_write = True
2021-01-11 13:20:02 +01:00
2021-04-05 13:23:58 +02:00
if not esp . secure_download_mode :
if esp . get_encrypted_download_disabled ( ) :
raise FatalError ( " This chip has encrypt functionality in UART download mode disabled. "
" This is the Flash Encryption configuration for Production mode instead of Development mode. " )
2021-01-11 13:20:02 +01:00
2021-04-05 13:23:58 +02:00
crypt_cfg_efuse = esp . get_flash_crypt_config ( )
2019-10-01 10:21:44 +02:00
2021-04-05 13:23:58 +02:00
if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF :
print ( ' Unexpected FLASH_CRYPT_CONFIG value: 0x %x ' % ( crypt_cfg_efuse ) )
do_write = False
2019-10-01 10:21:44 +02:00
2021-04-05 13:23:58 +02:00
enc_key_valid = esp . is_flash_encryption_key_valid ( )
2019-10-01 10:21:44 +02:00
2021-04-05 13:23:58 +02:00
if not enc_key_valid :
print ( ' Flash encryption key is not programmed ' )
do_write = False
2019-10-01 10:21:44 +02:00
2021-04-05 13:23:58 +02:00
# Determine which files list contain the ones to encrypt
files_to_encrypt = args . addr_filename if args . encrypt else args . encrypt_files
for address , argfile in files_to_encrypt :
if address % esp . FLASH_ENCRYPTED_WRITE_ALIGN :
print ( " File %s address 0x %x is not %d byte aligned, can ' t flash encrypted " %
( argfile . name , address , esp . FLASH_ENCRYPTED_WRITE_ALIGN ) )
do_write = False
2019-10-01 10:21:44 +02:00
if not do_write and not args . ignore_flash_encryption_efuse_setting :
2021-04-05 13:23:58 +02:00
raise FatalError ( " Can ' t perform encrypted flash write, consult Flash Encryption documentation for more information " )
2019-10-01 10:21:44 +02:00
2016-10-06 13:21:30 +02:00
# verify file sizes fit in flash
2019-10-01 10:21:44 +02:00
if args . flash_size != ' keep ' : # TODO: check this even with 'keep'
flash_end = flash_size_bytes ( args . flash_size )
for address , argfile in args . addr_filename :
2021-05-31 15:32:51 +02:00
argfile . seek ( 0 , os . SEEK_END )
2019-10-01 10:21:44 +02:00
if address + argfile . tell ( ) > flash_end :
2021-04-05 13:23:58 +02:00
raise FatalError ( ( " File %s (length %d ) at offset %d will not fit in %d bytes of flash. "
2019-10-01 10:21:44 +02:00
" Use --flash-size argument, or change flashing address. " )
% ( argfile . name , argfile . tell ( ) , address , flash_end ) )
argfile . seek ( 0 )
2016-10-06 13:21:30 +02:00
2019-02-14 16:49:30 +01:00
if args . erase_all :
erase_flash ( esp , args )
2021-05-31 15:32:51 +02:00
else :
for address , argfile in args . addr_filename :
argfile . seek ( 0 , os . SEEK_END )
write_end = address + argfile . tell ( )
argfile . seek ( 0 )
bytes_over = address % esp . FLASH_SECTOR_SIZE
if bytes_over != 0 :
print ( " WARNING: Flash address {:#010x} is not aligned to a {:#x} byte flash sector. "
" {:#x} bytes before this address will be erased. "
. format ( address , esp . FLASH_SECTOR_SIZE , bytes_over ) )
# Print the address range of to-be-erased flash memory region
print ( " Flash will be erased from {:#010x} to {:#010x} ... "
. format ( address - bytes_over , div_roundup ( write_end , esp . FLASH_SECTOR_SIZE ) * esp . FLASH_SECTOR_SIZE - 1 ) )
2019-02-14 16:49:30 +01:00
2021-04-05 13:23:58 +02:00
""" Create a list describing all the files we have to flash. Each entry holds an " encrypt " flag
marking whether the file needs encryption or not . This list needs to be sorted .
First , append to each entry of our addr_filename list the flag args . encrypt
For example , if addr_filename is [ ( 0x1000 , " partition.bin " ) , ( 0x8000 , " bootloader " ) ] ,
all_files will be [ ( 0x1000 , " partition.bin " , args . encrypt ) , ( 0x8000 , " bootloader " , args . encrypt ) ] ,
where , of course , args . encrypt is either True or False
"""
all_files = [ ( offs , filename , args . encrypt ) for ( offs , filename ) in args . addr_filename ]
""" Now do the same with encrypt_files list, if defined.
In this case , the flag is True
"""
if args . encrypt_files is not None :
encrypted_files_flag = [ ( offs , filename , True ) for ( offs , filename ) in args . encrypt_files ]
# Concatenate both lists and sort them.
# As both list are already sorted, we could simply do a merge instead,
# but for the sake of simplicity and because the lists are very small,
# let's use sorted.
all_files = sorted ( all_files + encrypted_files_flag , key = lambda x : x [ 0 ] )
for address , argfile , encrypted in all_files :
compress = args . compress
# Check whether we can compress the current file before flashing
if compress and encrypted :
print ( ' \n WARNING: - compress and encrypt options are mutually exclusive ' )
print ( ' Will flash %s uncompressed ' % argfile . name )
compress = False
2019-10-01 10:21:44 +02:00
2016-12-30 00:28:30 +01:00
if args . no_stub :
2017-02-06 14:17:11 +01:00
print ( ' Erasing flash... ' )
2021-04-05 13:23:58 +02:00
image = pad_to ( argfile . read ( ) , esp . FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4 )
2018-04-07 08:45:18 +02:00
if len ( image ) == 0 :
print ( ' WARNING: File %s is empty ' % argfile . name )
continue
2017-08-01 07:51:04 +02:00
image = _update_image_flash_params ( esp , address , args , image )
2016-10-28 00:53:02 +02:00
calcmd5 = hashlib . md5 ( image ) . hexdigest ( )
uncsize = len ( image )
2021-04-05 13:23:58 +02:00
if compress :
2016-10-28 00:53:02 +02:00
uncimage = image
2016-10-06 13:21:30 +02:00
image = zlib . compress ( uncimage , 9 )
2021-04-05 13:23:58 +02:00
# Decompress the compressed binary a block at a time, to dynamically calculate the
# timeout based on the real write size
decompress = zlib . decompressobj ( )
2016-12-30 00:28:30 +01:00
blocks = esp . flash_defl_begin ( uncsize , len ( image ) , address )
2016-10-06 13:21:30 +02:00
else :
2021-04-05 13:23:58 +02:00
blocks = esp . flash_begin ( uncsize , address , begin_rom_encrypted = encrypted )
2016-10-06 13:21:30 +02:00
argfile . seek ( 0 ) # in case we need it again
seq = 0
2021-04-05 13:23:58 +02:00
bytes_sent = 0 # bytes sent on wire
bytes_written = 0 # bytes written to flash
2016-10-06 13:21:30 +02:00
t = time . time ( )
2021-04-05 13:23:58 +02:00
timeout = DEFAULT_TIMEOUT
2016-10-06 13:21:30 +02:00
while len ( image ) > 0 :
2021-04-05 13:23:58 +02:00
print_overwrite ( ' Writing at 0x %08x ... ( %d %% ) ' % ( address + bytes_written , 100 * ( seq + 1 ) / / blocks ) )
2016-10-06 13:21:30 +02:00
sys . stdout . flush ( )
block = image [ 0 : esp . FLASH_WRITE_SIZE ]
2021-04-05 13:23:58 +02:00
if compress :
# feeding each compressed block into the decompressor lets us see block-by-block how much will be written
block_uncompressed = len ( decompress . decompress ( block ) )
bytes_written + = block_uncompressed
block_timeout = max ( DEFAULT_TIMEOUT , timeout_per_mb ( ERASE_WRITE_TIMEOUT_PER_MB , block_uncompressed ) )
if not esp . IS_STUB :
timeout = block_timeout # ROM code writes block to flash before ACKing
esp . flash_defl_block ( block , seq , timeout = timeout )
if esp . IS_STUB :
timeout = block_timeout # Stub ACKs when block is received, then writes to flash while receiving the block after it
2016-10-06 13:21:30 +02:00
else :
# Pad the last block
2017-02-06 14:17:11 +01:00
block = block + b ' \xff ' * ( esp . FLASH_WRITE_SIZE - len ( block ) )
2021-04-05 13:23:58 +02:00
if encrypted :
2019-10-01 10:21:44 +02:00
esp . flash_encrypt_block ( block , seq )
else :
esp . flash_block ( block , seq )
2021-04-05 13:23:58 +02:00
bytes_written + = len ( block )
bytes_sent + = len ( block )
2016-10-06 13:21:30 +02:00
image = image [ esp . FLASH_WRITE_SIZE : ]
seq + = 1
2021-04-05 13:23:58 +02:00
if esp . IS_STUB :
# Stub only writes each block to flash after 'ack'ing the receive, so do a final dummy operation which will
# not be 'ack'ed until the last block has actually been written out to flash
esp . read_reg ( ESPLoader . CHIP_DETECT_MAGIC_REG_ADDR , timeout = timeout )
2016-10-06 13:21:30 +02:00
t = time . time ( ) - t
2016-10-28 00:53:02 +02:00
speed_msg = " "
2021-04-05 13:23:58 +02:00
if compress :
2016-10-28 00:53:02 +02:00
if t > 0.0 :
speed_msg = " (effective %.1f kbit/s) " % ( uncsize / t * 8 / 1000 )
2021-04-05 13:23:58 +02:00
print_overwrite ( ' Wrote %d bytes ( %d compressed) at 0x %08x in %.1f seconds %s ... ' % ( uncsize ,
bytes_sent ,
address , t , speed_msg ) , last_line = True )
2016-10-28 00:53:02 +02:00
else :
if t > 0.0 :
2021-04-05 13:23:58 +02:00
speed_msg = " ( %.1f kbit/s) " % ( bytes_written / t * 8 / 1000 )
print_overwrite ( ' Wrote %d bytes at 0x %08x in %.1f seconds %s ... ' % ( bytes_written , address , t , speed_msg ) , last_line = True )
2019-10-01 10:21:44 +02:00
2021-04-05 13:23:58 +02:00
if not encrypted and not esp . secure_download_mode :
2019-10-01 10:21:44 +02:00
try :
res = esp . flash_md5sum ( address , uncsize )
if res != calcmd5 :
print ( ' File md5: %s ' % calcmd5 )
print ( ' Flash md5: %s ' % res )
print ( ' MD5 of 0xFF is %s ' % ( hashlib . md5 ( b ' \xFF ' * uncsize ) . hexdigest ( ) ) )
raise FatalError ( " MD5 of file does not match data in flash! " )
else :
print ( ' Hash of data verified. ' )
except NotImplementedInROMError :
pass
2017-08-01 07:51:04 +02:00
2017-02-06 14:17:11 +01:00
print ( ' \n Leaving... ' )
2017-01-05 12:54:40 +01:00
if esp . IS_STUB :
# skip sending flash_finish to ROM loader here,
# as it causes the loader to exit and run user code
2016-10-06 13:21:30 +02:00
esp . flash_begin ( 0 , 0 )
2021-04-05 13:23:58 +02:00
# Get the "encrypted" flag for the last file flashed
# Note: all_files list contains triplets like:
# (address: Integer, filename: String, encrypted: Boolean)
last_file_encrypted = all_files [ - 1 ] [ 2 ]
# Check whether the last file flashed was compressed or not
if args . compress and not last_file_encrypted :
2016-10-06 13:21:30 +02:00
esp . flash_defl_finish ( False )
else :
esp . flash_finish ( False )
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
if args . verify :
2017-02-06 14:17:11 +01:00
print ( ' Verifying just-written flash... ' )
print ( ' (This option is deprecated, flash contents are now always read back after flashing.) ' )
2021-04-05 13:23:58 +02:00
# If some encrypted files have been flashed print a warning saying that we won't check them
if args . encrypt or args . encrypt_files is not None :
print ( ' WARNING: - cannot verify encrypted files, they will be ignored ' )
# Call verify_flash function only if there at least one non-encrypted file flashed
if not args . encrypt :
verify_flash ( esp , args )
2016-10-06 13:21:30 +02:00
def image_info ( args ) :
image = LoadFirmwareImage ( args . chip , args . filename )
print ( ' Image version: %d ' % image . version )
2017-02-06 14:17:11 +01:00
print ( ' Entry point: %08x ' % image . entrypoint if image . entrypoint != 0 else ' Entry point not set ' )
print ( ' %d segments ' % len ( image . segments ) )
2019-10-01 10:21:44 +02:00
print ( )
2016-10-06 13:21:30 +02:00
idx = 0
for seg in image . segments :
idx + = 1
2021-04-05 13:23:58 +02:00
segs = seg . get_memory_type ( image )
seg_name = " , " . join ( segs )
2019-10-01 10:21:44 +02:00
print ( ' Segment %d : %r [ %s ] ' % ( idx , seg , seg_name ) )
2016-10-28 00:53:02 +02:00
calc_checksum = image . calculate_checksum ( )
2017-02-06 14:17:11 +01:00
print ( ' Checksum: %02x ( %s ) ' % ( image . checksum ,
' valid ' if image . checksum == calc_checksum else ' invalid - calculated %02x ' % calc_checksum ) )
2017-09-12 08:40:52 +02:00
try :
digest_msg = ' Not appended '
if image . append_digest :
is_valid = image . stored_digest == image . calc_digest
digest_msg = " %s ( %s ) " % ( hexify ( image . calc_digest ) . lower ( ) ,
" valid " if is_valid else " invalid " )
print ( ' Validation Hash: %s ' % digest_msg )
except AttributeError :
pass # ESP8266 image has no append_digest field
2016-10-06 13:21:30 +02:00
def make_image ( args ) :
2018-09-21 08:39:36 +02:00
image = ESP8266ROMFirmwareImage ( )
2016-10-06 13:21:30 +02:00
if len ( args . segfile ) == 0 :
raise FatalError ( ' No segments specified ' )
if len ( args . segfile ) != len ( args . segaddr ) :
raise FatalError ( ' Number of specified files does not match number of specified addresses ' )
for ( seg , addr ) in zip ( args . segfile , args . segaddr ) :
2019-02-14 16:49:30 +01:00
with open ( seg , ' rb ' ) as f :
data = f . read ( )
image . segments . append ( ImageSegment ( addr , data ) )
2016-10-06 13:21:30 +02:00
image . entrypoint = args . entrypoint
image . save ( args . output )
def elf2image ( args ) :
e = ELFFile ( args . input )
if args . chip == ' auto ' : # Default to ESP8266 for backwards compatibility
2017-02-06 14:17:11 +01:00
print ( " Creating image for ESP8266... " )
2018-04-07 08:45:18 +02:00
args . chip = ' esp8266 '
2016-10-06 13:21:30 +02:00
if args . chip == ' esp32 ' :
image = ESP32FirmwareImage ( )
2021-01-11 13:20:02 +01:00
if args . secure_pad :
image . secure_pad = ' 1 '
elif args . secure_pad_v2 :
image . secure_pad = ' 2 '
2021-04-05 13:23:58 +02:00
elif args . chip == ' esp32s2 ' :
2021-01-11 13:20:02 +01:00
image = ESP32S2FirmwareImage ( )
if args . secure_pad_v2 :
image . secure_pad = ' 2 '
2021-04-05 13:23:58 +02:00
elif args . chip == ' esp32s3beta2 ' :
image = ESP32S3BETA2FirmwareImage ( )
if args . secure_pad_v2 :
image . secure_pad = ' 2 '
elif args . chip == ' esp32s3beta3 ' :
image = ESP32S3BETA3FirmwareImage ( )
if args . secure_pad_v2 :
image . secure_pad = ' 2 '
elif args . chip == ' esp32c3 ' :
image = ESP32C3FirmwareImage ( )
if args . secure_pad_v2 :
image . secure_pad = ' 2 '
2021-05-31 15:32:51 +02:00
elif args . chip == ' esp32c6beta ' :
image = ESP32C6BETAFirmwareImage ( )
if args . secure_pad_v2 :
image . secure_pad = ' 2 '
2016-10-06 13:21:30 +02:00
elif args . version == ' 1 ' : # ESP8266
2018-09-21 08:39:36 +02:00
image = ESP8266ROMFirmwareImage ( )
2016-10-06 13:21:30 +02:00
else :
2018-09-21 08:39:36 +02:00
image = ESP8266V2FirmwareImage ( )
2016-10-06 13:21:30 +02:00
image . entrypoint = e . entrypoint
2021-04-05 13:23:58 +02:00
image . flash_mode = { ' qio ' : 0 , ' qout ' : 1 , ' dio ' : 2 , ' dout ' : 3 } [ args . flash_mode ]
if args . chip != ' esp8266 ' :
image . min_rev = int ( args . min_rev )
# ELFSection is a subclass of ImageSegment, so can use interchangeably
image . segments = e . segments if args . use_segments else e . sections
2016-10-28 00:53:02 +02:00
image . flash_size_freq = image . ROM_LOADER . FLASH_SIZES [ args . flash_size ]
2021-04-05 13:23:58 +02:00
image . flash_size_freq + = { ' 40m ' : 0 , ' 26m ' : 1 , ' 20m ' : 2 , ' 80m ' : 0xf } [ args . flash_freq ]
2016-10-06 13:21:30 +02:00
2019-02-14 16:49:30 +01:00
if args . elf_sha256_offset :
image . elf_sha256 = e . sha256 ( )
image . elf_sha256_offset = args . elf_sha256_offset
2021-04-05 13:23:58 +02:00
before = len ( image . segments )
image . merge_adjacent_segments ( )
if len ( image . segments ) != before :
delta = before - len ( image . segments )
print ( " Merged %d ELF section %s " % ( delta , " s " if delta > 1 else " " ) )
2019-02-14 16:49:30 +01:00
image . verify ( )
2016-10-06 13:21:30 +02:00
if args . output is None :
args . output = image . default_output_name ( args . input )
image . save ( args . output )
def read_mac ( esp , args ) :
mac = esp . read_mac ( )
2017-01-05 12:54:40 +01:00
2016-12-30 00:28:30 +01:00
def print_mac ( label , mac ) :
2017-02-06 14:17:11 +01:00
print ( ' %s : %s ' % ( label , ' : ' . join ( map ( lambda x : ' %02x ' % x , mac ) ) ) )
2017-01-05 12:54:40 +01:00
print_mac ( " MAC " , mac )
2016-10-06 13:21:30 +02:00
def chip_id ( esp , args ) :
2018-09-21 08:39:36 +02:00
try :
chipid = esp . chip_id ( )
print ( ' Chip ID: 0x %08x ' % chipid )
except NotSupportedError :
print ( ' Warning: %s has no Chip ID. Reading MAC instead. ' % esp . CHIP_NAME )
read_mac ( esp , args )
2016-10-06 13:21:30 +02:00
def erase_flash ( esp , args ) :
2017-02-06 14:17:11 +01:00
print ( ' Erasing flash (this may take a while)... ' )
2016-10-06 13:21:30 +02:00
t = time . time ( )
esp . erase_flash ( )
2017-02-06 14:17:11 +01:00
print ( ' Chip erase completed successfully in %.1f s ' % ( time . time ( ) - t ) )
2016-10-06 13:21:30 +02:00
def erase_region ( esp , args ) :
2017-02-06 14:17:11 +01:00
print ( ' Erasing region (may be slow depending on size)... ' )
2016-10-28 00:53:02 +02:00
t = time . time ( )
2016-10-06 13:21:30 +02:00
esp . erase_region ( args . address , args . size )
2017-02-06 14:17:11 +01:00
print ( ' Erase completed successfully in %.1f seconds. ' % ( time . time ( ) - t ) )
2016-10-06 13:21:30 +02:00
def run ( esp , args ) :
esp . run ( )
def flash_id ( esp , args ) :
flash_id = esp . flash_id ( )
2017-02-06 14:17:11 +01:00
print ( ' Manufacturer: %02x ' % ( flash_id & 0xff ) )
2017-08-01 07:51:04 +02:00
flid_lowbyte = ( flash_id >> 16 ) & 0xFF
print ( ' Device: %02x %02x ' % ( ( flash_id >> 8 ) & 0xff , flid_lowbyte ) )
print ( ' Detected flash size: %s ' % ( DETECTED_FLASH_SIZES . get ( flid_lowbyte , " Unknown " ) ) )
2016-10-06 13:21:30 +02:00
def read_flash ( esp , args ) :
if args . no_progress :
flash_progress = None
else :
def flash_progress ( progress , length ) :
msg = ' %d ( %d %% ) ' % ( progress , progress * 100.0 / length )
padding = ' \b ' * len ( msg )
if progress == length :
padding = ' \n '
sys . stdout . write ( msg + padding )
sys . stdout . flush ( )
t = time . time ( )
data = esp . read_flash ( args . address , args . size , flash_progress )
t = time . time ( ) - t
2021-01-11 13:20:02 +01:00
print_overwrite ( ' Read %d bytes at 0x %x in %.1f seconds ( %.1f kbit/s)... '
% ( len ( data ) , args . address , t , len ( data ) / t * 8 / 1000 ) , last_line = True )
2019-02-14 16:49:30 +01:00
with open ( args . filename , ' wb ' ) as f :
f . write ( data )
2016-10-06 13:21:30 +02:00
2017-08-01 07:51:04 +02:00
def verify_flash ( esp , args ) :
2016-10-06 13:21:30 +02:00
differences = False
2017-02-06 14:17:11 +01:00
2016-10-06 13:21:30 +02:00
for address , argfile in args . addr_filename :
2017-03-10 14:40:14 +01:00
image = pad_to ( argfile . read ( ) , 4 )
2016-10-06 13:21:30 +02:00
argfile . seek ( 0 ) # rewind in case we need it again
2017-02-06 14:17:11 +01:00
2017-08-01 07:51:04 +02:00
image = _update_image_flash_params ( esp , address , args , image )
2017-02-06 14:17:11 +01:00
2016-10-06 13:21:30 +02:00
image_size = len ( image )
2017-02-06 14:17:11 +01:00
print ( ' Verifying 0x %x ( %d ) bytes @ 0x %08x in flash against %s ... ' % ( image_size , image_size , address , argfile . name ) )
2016-10-06 13:21:30 +02:00
# Try digest first, only read if there are differences.
digest = esp . flash_md5sum ( address , image_size )
expected_digest = hashlib . md5 ( image ) . hexdigest ( )
if digest == expected_digest :
2017-02-06 14:17:11 +01:00
print ( ' -- verify OK (digest matched) ' )
2016-10-06 13:21:30 +02:00
continue
else :
differences = True
if getattr ( args , ' diff ' , ' no ' ) != ' yes ' :
2017-02-06 14:17:11 +01:00
print ( ' -- verify FAILED (digest mismatch) ' )
2016-10-06 13:21:30 +02:00
continue
flash = esp . read_flash ( address , image_size )
assert flash != image
2017-02-06 14:17:11 +01:00
diff = [ i for i in range ( image_size ) if flash [ i ] != image [ i ] ]
print ( ' -- verify FAILED: %d differences, first @ 0x %08x ' % ( len ( diff ) , address + diff [ 0 ] ) )
2016-10-06 13:21:30 +02:00
for d in diff :
2017-02-06 14:17:11 +01:00
flash_byte = flash [ d ]
image_byte = image [ d ]
if PYTHON2 :
flash_byte = ord ( flash_byte )
image_byte = ord ( image_byte )
print ( ' %08x %02x %02x ' % ( address + d , flash_byte , image_byte ) )
2016-10-06 13:21:30 +02:00
if differences :
raise FatalError ( " Verify failed. " )
2016-10-28 00:53:02 +02:00
def read_flash_status ( esp , args ) :
2017-02-06 14:17:11 +01:00
print ( ' Status value: 0x %04x ' % esp . read_status ( args . bytes ) )
2016-10-28 00:53:02 +02:00
def write_flash_status ( esp , args ) :
fmt = " 0x %% 0 %d x " % ( args . bytes * 2 )
args . value = args . value & ( ( 1 << ( args . bytes * 8 ) ) - 1 )
2017-02-06 14:17:11 +01:00
print ( ( ' Initial flash status: ' + fmt ) % esp . read_status ( args . bytes ) )
print ( ( ' Setting flash status: ' + fmt ) % args . value )
2016-10-28 00:53:02 +02:00
esp . write_status ( args . value , args . bytes , args . non_volatile )
2017-02-06 14:17:11 +01:00
print ( ( ' After flash status: ' + fmt ) % esp . read_status ( args . bytes ) )
2016-10-28 00:53:02 +02:00
2021-01-11 13:20:02 +01:00
def get_security_info ( esp , args ) :
( flags , flash_crypt_cnt , key_purposes ) = esp . get_security_info ( )
# TODO: better display
print ( ' Flags: 0x %08x ( %s ) ' % ( flags , bin ( flags ) ) )
print ( ' Flash_Crypt_Cnt: 0x %x ' % flash_crypt_cnt )
print ( ' Key_Purposes: %s ' % ( key_purposes , ) )
2021-04-05 13:23:58 +02:00
def merge_bin ( args ) :
chip_class = _chip_to_rom_loader ( args . chip )
# sort the files by offset. The AddrFilenamePairAction has already checked for overlap
input_files = sorted ( args . addr_filename , key = lambda x : x [ 0 ] )
if not input_files :
raise FatalError ( " No input files specified " )
first_addr = input_files [ 0 ] [ 0 ]
if first_addr < args . target_offset :
raise FatalError ( " Output file target offset is 0x %x . Input file offset 0x %x is before this. " % ( args . target_offset , first_addr ) )
if args . format != ' raw ' :
raise FatalError ( " This version of esptool only supports the ' raw ' output format " )
with open ( args . output , ' wb ' ) as of :
def pad_to ( flash_offs ) :
# account for output file offset if there is any
of . write ( b ' \xFF ' * ( flash_offs - args . target_offset - of . tell ( ) ) )
for addr , argfile in input_files :
pad_to ( addr )
image = argfile . read ( )
image = _update_image_flash_params ( chip_class , addr , args , image )
of . write ( image )
if args . fill_flash_size :
pad_to ( flash_size_bytes ( args . fill_flash_size ) )
print ( " Wrote 0x %x bytes to file %s , ready to flash to offset 0x %x " % ( of . tell ( ) , args . output , args . target_offset ) )
2016-10-06 13:21:30 +02:00
def version ( args ) :
2017-02-06 14:17:11 +01:00
print ( __version__ )
2016-10-06 13:21:30 +02:00
#
# End of operations functions
#
2021-04-05 13:23:58 +02:00
def main ( argv = None , esp = None ) :
2019-02-14 16:49:30 +01:00
"""
Main function for esptool
2021-04-05 13:23:58 +02:00
argv - Optional override for default arguments parsing ( that uses sys . argv ) , can be a list of custom arguments
2019-10-01 10:21:44 +02:00
as strings . Arguments and their values need to be added as individual items to the list e . g . " -b 115200 " thus
becomes [ ' -b ' , ' 115200 ' ] .
2021-04-05 13:23:58 +02:00
esp - Optional override of the connected device previously returned by get_default_connected_device ( )
2019-02-14 16:49:30 +01:00
"""
2021-04-05 13:23:58 +02:00
external_esp = esp is not None
2016-10-06 13:21:30 +02:00
parser = argparse . ArgumentParser ( description = ' esptool.py v %s - ESP8266 ROM Bootloader Utility ' % __version__ , prog = ' esptool ' )
parser . add_argument ( ' --chip ' , ' -c ' ,
help = ' Target chip type ' ,
2021-04-05 13:23:58 +02:00
type = lambda c : c . lower ( ) . replace ( ' - ' , ' ' ) , # support ESP32-S2, etc.
2021-05-31 15:32:51 +02:00
choices = [ ' auto ' , ' esp8266 ' , ' esp32 ' , ' esp32s2 ' , ' esp32s3beta2 ' , ' esp32s3beta3 ' , ' esp32c3 ' , ' esp32c6beta ' ] ,
2016-10-06 13:21:30 +02:00
default = os . environ . get ( ' ESPTOOL_CHIP ' , ' auto ' ) )
parser . add_argument (
' --port ' , ' -p ' ,
help = ' Serial port device ' ,
2018-09-21 08:39:36 +02:00
default = os . environ . get ( ' ESPTOOL_PORT ' , None ) )
2016-10-06 13:21:30 +02:00
parser . add_argument (
' --baud ' , ' -b ' ,
help = ' Serial port baud rate used when flashing/reading ' ,
type = arg_auto_int ,
default = os . environ . get ( ' ESPTOOL_BAUD ' , ESPLoader . ESP_ROM_BAUD ) )
2017-01-05 12:54:40 +01:00
parser . add_argument (
' --before ' ,
help = ' What to do before connecting to the chip ' ,
2021-05-31 15:32:51 +02:00
choices = [ ' default_reset ' , ' usb_reset ' , ' no_reset ' , ' no_reset_no_sync ' ] ,
2017-01-05 12:54:40 +01:00
default = os . environ . get ( ' ESPTOOL_BEFORE ' , ' default_reset ' ) )
parser . add_argument (
' --after ' , ' -a ' ,
help = ' What to do after esptool.py is finished ' ,
2021-05-31 15:32:51 +02:00
choices = [ ' hard_reset ' , ' soft_reset ' , ' no_reset ' , ' no_reset_stub ' ] ,
2017-01-05 12:54:40 +01:00
default = os . environ . get ( ' ESPTOOL_AFTER ' , ' hard_reset ' ) )
2016-10-06 13:21:30 +02:00
parser . add_argument (
' --no-stub ' ,
help = " Disable launching the flasher stub, only talk to ROM bootloader. Some features will not be available. " ,
action = ' store_true ' )
2018-04-07 08:45:18 +02:00
parser . add_argument (
' --trace ' , ' -t ' ,
help = " Enable trace-level output of esptool.py interactions. " ,
action = ' store_true ' )
2018-09-21 08:39:36 +02:00
parser . add_argument (
' --override-vddsdio ' ,
help = " Override ESP32 VDDSDIO internal voltage regulator (use with care) " ,
choices = ESP32ROM . OVERRIDE_VDDSDIO_CHOICES ,
nargs = ' ? ' )
2021-01-11 13:20:02 +01:00
parser . add_argument (
' --connect-attempts ' ,
help = ( ' Number of attempts to connect, negative or 0 for infinite. '
' Default: %d . ' % DEFAULT_CONNECT_ATTEMPTS ) ,
type = int ,
default = os . environ . get ( ' ESPTOOL_CONNECT_ATTEMPTS ' , DEFAULT_CONNECT_ATTEMPTS ) )
2016-10-06 13:21:30 +02:00
subparsers = parser . add_subparsers (
dest = ' operation ' ,
help = ' Run esptool {command} -h for additional help ' )
2017-05-06 19:29:12 +02:00
def add_spi_connection_arg ( parent ) :
2021-04-05 13:23:58 +02:00
parent . add_argument ( ' --spi-connection ' , ' -sc ' , help = ' ESP32-only argument. Override default SPI Flash connection. '
2017-05-06 19:29:12 +02:00
' 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). ' ,
action = SpiConnectionAction )
2016-10-06 13:21:30 +02:00
parser_load_ram = subparsers . add_parser (
' load_ram ' ,
help = ' Download an image to RAM and execute ' )
parser_load_ram . add_argument ( ' filename ' , help = ' Firmware image ' )
parser_dump_mem = subparsers . add_parser (
' dump_mem ' ,
help = ' Dump arbitrary memory to disk ' )
parser_dump_mem . add_argument ( ' address ' , help = ' Base address ' , type = arg_auto_int )
parser_dump_mem . add_argument ( ' size ' , help = ' Size of region to dump ' , type = arg_auto_int )
parser_dump_mem . add_argument ( ' filename ' , help = ' Name of binary dump ' )
parser_read_mem = subparsers . add_parser (
' read_mem ' ,
help = ' Read arbitrary memory location ' )
parser_read_mem . add_argument ( ' address ' , help = ' Address to read ' , type = arg_auto_int )
parser_write_mem = subparsers . add_parser (
' write_mem ' ,
help = ' Read-modify-write to arbitrary memory location ' )
parser_write_mem . add_argument ( ' address ' , help = ' Address to write ' , type = arg_auto_int )
parser_write_mem . add_argument ( ' value ' , help = ' Value ' , type = arg_auto_int )
2021-04-05 13:23:58 +02:00
parser_write_mem . add_argument ( ' mask ' , help = ' Mask of bits to write ' , type = arg_auto_int , nargs = ' ? ' , default = ' 0xFFFFFFFF ' )
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
def add_spi_flash_subparsers ( parent , allow_keep , auto_detect ) :
2016-10-06 13:21:30 +02:00
""" Add common parser arguments for SPI flash properties """
2021-04-05 13:23:58 +02:00
extra_keep_args = [ ' keep ' ] if allow_keep else [ ]
2017-08-01 07:51:04 +02:00
2021-04-05 13:23:58 +02:00
if auto_detect and allow_keep :
2019-10-01 10:21:44 +02:00
extra_fs_message = " , detect, or keep "
2021-04-05 13:23:58 +02:00
elif auto_detect :
extra_fs_message = " , or detect "
elif allow_keep :
extra_fs_message = " , or keep "
2019-10-01 10:21:44 +02:00
else :
extra_fs_message = " "
2016-10-06 13:21:30 +02:00
parent . add_argument ( ' --flash_freq ' , ' -ff ' , help = ' SPI Flash frequency ' ,
2017-08-01 07:51:04 +02:00
choices = extra_keep_args + [ ' 40m ' , ' 26m ' , ' 20m ' , ' 80m ' ] ,
2021-04-05 13:23:58 +02:00
default = os . environ . get ( ' ESPTOOL_FF ' , ' keep ' if allow_keep else ' 40m ' ) )
2016-10-06 13:21:30 +02:00
parent . add_argument ( ' --flash_mode ' , ' -fm ' , help = ' SPI Flash mode ' ,
2017-08-01 07:51:04 +02:00
choices = extra_keep_args + [ ' qio ' , ' qout ' , ' dio ' , ' dout ' ] ,
2021-04-05 13:23:58 +02:00
default = os . environ . get ( ' ESPTOOL_FM ' , ' keep ' if allow_keep else ' qio ' ) )
2016-10-06 13:21:30 +02:00
parent . add_argument ( ' --flash_size ' , ' -fs ' , help = ' SPI Flash size in MegaBytes (1MB, 2MB, 4MB, 8MB, 16M) '
2019-10-01 10:21:44 +02:00
' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1) ' + extra_fs_message ,
2017-01-05 12:54:40 +01:00
action = FlashSizeAction , auto_detect = auto_detect ,
2021-04-05 13:23:58 +02:00
default = os . environ . get ( ' ESPTOOL_FS ' , ' keep ' if allow_keep else ' 1MB ' ) )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parent )
2016-10-06 13:21:30 +02:00
2019-10-01 10:21:44 +02:00
parser_write_flash = subparsers . add_parser (
' write_flash ' ,
help = ' Write a binary blob to flash ' )
2016-10-06 13:21:30 +02:00
parser_write_flash . add_argument ( ' addr_filename ' , metavar = ' <address> <filename> ' , help = ' Address followed by binary filename, separated by space ' ,
action = AddrFilenamePairAction )
2019-02-14 16:49:30 +01:00
parser_write_flash . add_argument ( ' --erase-all ' , ' -e ' ,
help = ' Erase all regions of flash (not just write areas) before programming ' ,
action = " store_true " )
2021-04-05 13:23:58 +02:00
add_spi_flash_subparsers ( parser_write_flash , allow_keep = True , auto_detect = True )
2016-10-06 13:21:30 +02:00
parser_write_flash . add_argument ( ' --no-progress ' , ' -p ' , help = ' Suppress progress output ' , action = " store_true " )
2021-04-05 13:23:58 +02:00
parser_write_flash . add_argument ( ' --verify ' , help = ' Verify just-written data on flash '
2017-02-06 14:17:11 +01:00
' (mostly superfluous, data is read back during flashing) ' , action = ' store_true ' )
2021-01-11 13:20:02 +01:00
parser_write_flash . add_argument ( ' --encrypt ' , help = ' Apply flash encryption when writing data (required correct efuse settings) ' ,
2019-10-01 10:21:44 +02:00
action = ' store_true ' )
2021-04-05 13:23:58 +02:00
# In order to not break backward compatibility, our list of encrypted files to flash is a new parameter
parser_write_flash . add_argument ( ' --encrypt-files ' , metavar = ' <address> <filename> ' ,
help = ' Files to be encrypted on the flash. Address followed by binary filename, separated by space. ' ,
action = AddrFilenamePairAction )
2019-10-01 10:21:44 +02:00
parser_write_flash . add_argument ( ' --ignore-flash-encryption-efuse-setting ' , help = ' Ignore flash encryption efuse settings ' ,
action = ' store_true ' )
2019-02-14 16:49:30 +01:00
2017-02-06 14:17:11 +01:00
compress_args = parser_write_flash . add_mutually_exclusive_group ( required = False )
2021-04-05 13:23:58 +02:00
compress_args . add_argument ( ' --compress ' , ' -z ' , help = ' Compress data in transfer (default unless --no-stub is specified) ' ,
action = " store_true " , default = None )
compress_args . add_argument ( ' --no-compress ' , ' -u ' , help = ' Disable data compression during transfer (default if --no-stub is specified) ' ,
action = " store_true " )
2016-10-06 13:21:30 +02:00
subparsers . add_parser (
' run ' ,
help = ' Run application code in flash ' )
parser_image_info = subparsers . add_parser (
' image_info ' ,
help = ' Dump headers from an application image ' )
parser_image_info . add_argument ( ' filename ' , help = ' Image file to parse ' )
parser_make_image = subparsers . add_parser (
' make_image ' ,
help = ' Create an application image from binary files ' )
parser_make_image . add_argument ( ' output ' , help = ' Output image file ' )
parser_make_image . add_argument ( ' --segfile ' , ' -f ' , action = ' append ' , help = ' Segment input file ' )
parser_make_image . add_argument ( ' --segaddr ' , ' -a ' , action = ' append ' , help = ' Segment base address ' , type = arg_auto_int )
parser_make_image . add_argument ( ' --entrypoint ' , ' -e ' , help = ' Address of entry point ' , type = arg_auto_int , default = 0 )
parser_elf2image = subparsers . add_parser (
' elf2image ' ,
help = ' Create an application image from ELF file ' )
parser_elf2image . add_argument ( ' input ' , help = ' Input ELF file ' )
parser_elf2image . add_argument ( ' --output ' , ' -o ' , help = ' Output filename prefix (for version 1 image), or filename (for version 2 single image) ' , type = str )
2021-04-05 13:23:58 +02:00
parser_elf2image . add_argument ( ' --version ' , ' -e ' , help = ' Output image version ' , choices = [ ' 1 ' , ' 2 ' ] , default = ' 1 ' )
parser_elf2image . add_argument ( ' --min-rev ' , ' -r ' , help = ' Minimum chip revision ' , choices = [ ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' ] , default = ' 0 ' )
2021-01-11 13:20:02 +01:00
parser_elf2image . add_argument ( ' --secure-pad ' , action = ' store_true ' ,
help = ' Pad image so once signed it will end on a 64KB boundary. For Secure Boot v1 images only. ' )
parser_elf2image . add_argument ( ' --secure-pad-v2 ' , action = ' store_true ' ,
help = ' Pad image to 64KB, so once signed its signature sector will start at the next 64K block. '
' For Secure Boot v2 images only. ' )
2019-02-14 16:49:30 +01:00
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. ' ,
type = arg_auto_int , default = None )
2021-04-05 13:23:58 +02:00
parser_elf2image . add_argument ( ' --use_segments ' , help = ' If set, ELF segments will be used instead of ELF sections to genereate the image. ' ,
action = ' store_true ' )
2016-12-30 00:28:30 +01:00
2021-04-05 13:23:58 +02:00
add_spi_flash_subparsers ( parser_elf2image , allow_keep = False , auto_detect = False )
2016-10-06 13:21:30 +02:00
subparsers . add_parser (
' read_mac ' ,
help = ' Read MAC address from OTP ROM ' )
subparsers . add_parser (
' chip_id ' ,
help = ' Read Chip ID from OTP ROM ' )
2017-05-06 19:29:12 +02:00
parser_flash_id = subparsers . add_parser (
2016-10-06 13:21:30 +02:00
' flash_id ' ,
help = ' Read SPI flash manufacturer and device ID ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_flash_id )
2016-10-06 13:21:30 +02:00
2016-10-28 00:53:02 +02:00
parser_read_status = subparsers . add_parser (
' read_flash_status ' ,
help = ' Read SPI flash status register ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_read_status )
2021-04-05 13:23:58 +02:00
parser_read_status . add_argument ( ' --bytes ' , help = ' Number of bytes to read (1-3) ' , type = int , choices = [ 1 , 2 , 3 ] , default = 2 )
2016-10-28 00:53:02 +02:00
parser_write_status = subparsers . add_parser (
' write_flash_status ' ,
help = ' Write SPI flash status register ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_write_status )
2016-10-28 00:53:02 +02:00
parser_write_status . add_argument ( ' --non-volatile ' , help = ' Write non-volatile bits (use with caution) ' , action = ' store_true ' )
2021-04-05 13:23:58 +02:00
parser_write_status . add_argument ( ' --bytes ' , help = ' Number of status bytes to write (1-3) ' , type = int , choices = [ 1 , 2 , 3 ] , default = 2 )
2016-10-28 00:53:02 +02:00
parser_write_status . add_argument ( ' value ' , help = ' New value ' , type = arg_auto_int )
2016-10-06 13:21:30 +02:00
parser_read_flash = subparsers . add_parser (
' read_flash ' ,
help = ' Read SPI flash content ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_read_flash )
2016-10-06 13:21:30 +02:00
parser_read_flash . add_argument ( ' address ' , help = ' Start address ' , type = arg_auto_int )
parser_read_flash . add_argument ( ' size ' , help = ' Size of region to dump ' , type = arg_auto_int )
parser_read_flash . add_argument ( ' filename ' , help = ' Name of binary dump ' )
parser_read_flash . add_argument ( ' --no-progress ' , ' -p ' , help = ' Suppress progress output ' , action = " store_true " )
parser_verify_flash = subparsers . add_parser (
' verify_flash ' ,
help = ' Verify a binary blob against flash ' )
parser_verify_flash . add_argument ( ' addr_filename ' , help = ' Address and binary file to verify there, separated by space ' ,
action = AddrFilenamePairAction )
parser_verify_flash . add_argument ( ' --diff ' , ' -d ' , help = ' Show differences ' ,
choices = [ ' no ' , ' yes ' ] , default = ' no ' )
2021-04-05 13:23:58 +02:00
add_spi_flash_subparsers ( parser_verify_flash , allow_keep = True , auto_detect = True )
2016-10-06 13:21:30 +02:00
2017-05-06 19:29:12 +02:00
parser_erase_flash = subparsers . add_parser (
2016-10-06 13:21:30 +02:00
' erase_flash ' ,
help = ' Perform Chip Erase on SPI flash ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_erase_flash )
2016-10-06 13:21:30 +02:00
parser_erase_region = subparsers . add_parser (
' erase_region ' ,
help = ' Erase a region of the flash ' )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parser_erase_region )
2016-10-06 13:21:30 +02:00
parser_erase_region . add_argument ( ' address ' , help = ' Start address (must be multiple of 4096) ' , type = arg_auto_int )
parser_erase_region . add_argument ( ' size ' , help = ' Size of region to erase (must be multiple of 4096) ' , type = arg_auto_int )
2021-04-05 13:23:58 +02:00
parser_merge_bin = subparsers . add_parser (
' merge_bin ' ,
help = ' Merge multiple raw binary files into a single file for later flashing ' )
parser_merge_bin . add_argument ( ' --output ' , ' -o ' , help = ' Output filename ' , type = str , required = True )
parser_merge_bin . add_argument ( ' --format ' , ' -f ' , help = ' Format of the output file ' , choices = ' raw ' , default = ' raw ' ) # for future expansion
add_spi_flash_subparsers ( parser_merge_bin , allow_keep = True , auto_detect = False )
parser_merge_bin . add_argument ( ' --target-offset ' , ' -t ' , help = ' Target offset where the output file will be flashed ' ,
type = arg_auto_int , default = 0 )
parser_merge_bin . add_argument ( ' --fill-flash-size ' , help = ' If set, the final binary file will be padded with FF '
' bytes up to this flash size. ' , action = FlashSizeAction )
parser_merge_bin . add_argument ( ' addr_filename ' , metavar = ' <address> <filename> ' ,
help = ' Address followed by binary filename, separated by space ' ,
action = AddrFilenamePairAction )
2016-10-06 13:21:30 +02:00
subparsers . add_parser (
' version ' , help = ' Print esptool version ' )
2021-01-11 13:20:02 +01:00
subparsers . add_parser ( ' get_security_info ' , help = ' Get some security-related data ' )
2016-10-06 13:21:30 +02:00
# internal sanity check - every operation matches a module function of the same name
for operation in subparsers . choices . keys ( ) :
assert operation in globals ( ) , " %s should be a module function " % operation
2021-04-05 13:23:58 +02:00
argv = expand_file_arguments ( argv or sys . argv [ 1 : ] )
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
args = parser . parse_args ( argv )
2017-02-06 14:17:11 +01:00
print ( ' esptool.py v %s ' % __version__ )
2016-10-06 13:21:30 +02:00
# operation function can take 1 arg (args), 2 args (esp, arg)
# or be a member function of the ESPLoader class.
2017-02-06 14:17:11 +01:00
if args . operation is None :
parser . print_help ( )
sys . exit ( 1 )
2021-04-05 13:23:58 +02:00
# Forbid the usage of both --encrypt, which means encrypt all the given files,
# and --encrypt-files, which represents the list of files to encrypt.
# The reason is that allowing both at the same time increases the chances of
# having contradictory lists (e.g. one file not available in one of list).
if args . operation == " write_flash " and args . encrypt and args . encrypt_files is not None :
raise FatalError ( " Options --encrypt and --encrypt-files must not be specified at the same time. " )
2016-10-06 13:21:30 +02:00
operation_func = globals ( ) [ args . operation ]
2018-04-07 08:45:18 +02:00
if PYTHON2 :
# This function is depreciated in Python3
operation_args = inspect . getargspec ( operation_func ) . args
else :
operation_args = inspect . getfullargspec ( operation_func ) . args
2016-10-06 13:21:30 +02:00
if operation_args [ 0 ] == ' esp ' : # operation function takes an ESPLoader connection object
2018-09-21 08:39:36 +02:00
if args . before != " no_reset_no_sync " :
initial_baud = min ( ESPLoader . ESP_ROM_BAUD , args . baud ) # don't sync faster than the default baud rate
2017-01-05 12:54:40 +01:00
else :
2018-09-21 08:39:36 +02:00
initial_baud = args . baud
if args . port is None :
2021-04-05 13:23:58 +02:00
ser_list = get_port_list ( )
2018-09-21 08:39:36 +02:00
print ( " Found %d serial ports " % len ( ser_list ) )
else :
ser_list = [ args . port ]
2021-04-05 13:23:58 +02:00
esp = esp or get_default_connected_device ( ser_list , port = args . port , connect_attempts = args . connect_attempts ,
initial_baud = initial_baud , chip = args . chip , trace = args . trace ,
before = args . before )
2018-09-21 08:39:36 +02:00
if esp is None :
2019-10-01 10:21:44 +02:00
raise FatalError ( " Could not connect to an Espressif device on any of the %d available serial ports. " % len ( ser_list ) )
2016-10-06 13:21:30 +02:00
2021-04-05 13:23:58 +02:00
if esp . secure_download_mode :
print ( " Chip is %s in Secure Download Mode " % esp . CHIP_NAME )
else :
print ( " Chip is %s " % ( esp . get_chip_description ( ) ) )
print ( " Features: %s " % " , " . join ( esp . get_chip_features ( ) ) )
print ( " Crystal is %d MHz " % esp . get_crystal_freq ( ) )
read_mac ( esp , args )
2018-09-21 08:39:36 +02:00
2016-10-06 13:21:30 +02:00
if not args . no_stub :
2021-04-05 13:23:58 +02:00
if esp . secure_download_mode :
print ( " WARNING: Stub loader is not supported in Secure Download Mode, setting --no-stub " )
args . no_stub = True
else :
esp = esp . run_stub ( )
2016-10-06 13:21:30 +02:00
2018-09-21 08:39:36 +02:00
if args . override_vddsdio :
esp . override_vddsdio ( args . override_vddsdio )
2016-10-06 13:21:30 +02:00
if args . baud > initial_baud :
2016-12-30 00:28:30 +01:00
try :
esp . change_baud ( args . baud )
except NotImplementedInROMError :
2017-02-06 14:17:11 +01:00
print ( " WARNING: ROM doesn ' t support changing baud rate. Keeping initial baud rate %d " % initial_baud )
2016-10-06 13:21:30 +02:00
2017-05-06 19:29:12 +02:00
# override common SPI flash parameter stuff if configured to do so
if hasattr ( args , " spi_connection " ) and args . spi_connection is not None :
if esp . CHIP_NAME != " ESP32 " :
raise FatalError ( " Chip %s does not support --spi-connection option. " % esp . CHIP_NAME )
print ( " Configuring SPI flash mode... " )
esp . flash_spi_attach ( args . spi_connection )
elif args . no_stub :
print ( " Enabling default SPI flash mode... " )
# ROM loader doesn't enable flash unless we explicitly do it
esp . flash_spi_attach ( 0 )
2016-10-06 13:21:30 +02:00
if hasattr ( args , " flash_size " ) :
2017-02-06 14:17:11 +01:00
print ( " Configuring flash size... " )
2017-01-05 12:54:40 +01:00
detect_flash_size ( esp , args )
2019-10-01 10:21:44 +02:00
if args . flash_size != ' keep ' : # TODO: should set this even with 'keep'
esp . flash_set_parameters ( flash_size_bytes ( args . flash_size ) )
2016-10-06 13:21:30 +02:00
2019-02-14 16:49:30 +01:00
try :
operation_func ( esp , args )
finally :
try : # Clean up AddrFilenamePairAction files
for address , argfile in args . addr_filename :
argfile . close ( )
except AttributeError :
pass
2017-01-05 12:54:40 +01:00
2018-09-21 08:39:36 +02:00
# Handle post-operation behaviour (reset or other)
if operation_func == load_ram :
# the ESP is now running the loaded image, so let it run
print ( ' Exiting immediately. ' )
elif args . after == ' hard_reset ' :
2017-01-05 12:54:40 +01:00
esp . hard_reset ( )
elif args . after == ' soft_reset ' :
2017-02-06 14:17:11 +01:00
print ( ' Soft resetting... ' )
2017-01-05 12:54:40 +01:00
# flash_finish will trigger a soft reset
esp . soft_reset ( False )
2021-05-31 15:32:51 +02:00
elif args . after == ' no_reset_stub ' :
print ( ' Staying in flasher stub. ' )
else : # args.after == 'no_reset'
2017-02-06 14:17:11 +01:00
print ( ' Staying in bootloader. ' )
2017-01-05 12:54:40 +01:00
if esp . IS_STUB :
esp . soft_reset ( True ) # exit stub back to ROM loader
2021-04-05 13:23:58 +02:00
if not external_esp :
esp . _port . close ( )
2018-09-21 08:39:36 +02:00
2016-10-06 13:21:30 +02:00
else :
operation_func ( args )
2021-04-05 13:23:58 +02:00
def get_port_list ( ) :
if list_ports is None :
raise FatalError ( " Listing all serial ports is currently not available. Please try to specify the port when "
" running esptool.py or update the pyserial package to the latest version " )
return sorted ( ports . device for ports in list_ports . comports ( ) )
def expand_file_arguments ( argv ) :
2017-01-05 12:54:40 +01:00
""" Any argument starting with " @ " gets replaced with all values read from a text file.
Text file arguments can be split by newline or by space .
Values are added " as-is " , as if they were specified in this order on the command line .
"""
new_args = [ ]
expanded = False
2021-04-05 13:23:58 +02:00
for arg in argv :
2017-01-05 12:54:40 +01:00
if arg . startswith ( " @ " ) :
expanded = True
2021-04-05 13:23:58 +02:00
with open ( arg [ 1 : ] , " r " ) as f :
2017-01-05 12:54:40 +01:00
for line in f . readlines ( ) :
new_args + = shlex . split ( line )
else :
new_args . append ( arg )
if expanded :
2017-02-06 14:17:11 +01:00
print ( " esptool.py %s " % ( " " . join ( new_args [ 1 : ] ) ) )
2021-04-05 13:23:58 +02:00
return new_args
return argv
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
class FlashSizeAction ( argparse . Action ) :
""" Custom flash size parser class to support backwards compatibility with megabit size arguments.
( At next major relase , remove deprecated sizes and this can become a ' normal ' choices = argument again . )
"""
2017-01-05 12:54:40 +01:00
def __init__ ( self , option_strings , dest , nargs = 1 , auto_detect = False , * * kwargs ) :
2016-10-06 13:21:30 +02:00
super ( FlashSizeAction , self ) . __init__ ( option_strings , dest , nargs , * * kwargs )
2017-01-05 12:54:40 +01:00
self . _auto_detect = auto_detect
2016-10-06 13:21:30 +02:00
def __call__ ( self , parser , namespace , values , option_string = None ) :
try :
value = {
' 2m ' : ' 256KB ' ,
' 4m ' : ' 512KB ' ,
' 8m ' : ' 1MB ' ,
' 16m ' : ' 2MB ' ,
' 32m ' : ' 4MB ' ,
' 16m-c1 ' : ' 2MB-c1 ' ,
' 32m-c1 ' : ' 4MB-c1 ' ,
} [ values [ 0 ] ]
print ( " WARNING: Flash size arguments in megabits like ' %s ' are deprecated. " % ( values [ 0 ] ) )
print ( " Please use the equivalent size ' %s ' . " % ( value ) )
print ( " Megabit arguments may be removed in a future release. " )
except KeyError :
value = values [ 0 ]
known_sizes = dict ( ESP8266ROM . FLASH_SIZES )
known_sizes . update ( ESP32ROM . FLASH_SIZES )
2017-01-05 12:54:40 +01:00
if self . _auto_detect :
known_sizes [ ' detect ' ] = ' detect '
2019-10-01 10:21:44 +02:00
known_sizes [ ' keep ' ] = ' keep '
2016-10-06 13:21:30 +02:00
if value not in known_sizes :
raise argparse . ArgumentError ( self , ' %s is not a known flash size. Known sizes: %s ' % ( value , " , " . join ( known_sizes . keys ( ) ) ) )
setattr ( namespace , self . dest , value )
2017-05-06 19:29:12 +02:00
class SpiConnectionAction ( argparse . Action ) :
""" Custom action to parse ' spi connection ' override. Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas.
"""
def __call__ ( self , parser , namespace , value , option_string = None ) :
if value . upper ( ) == " SPI " :
value = 0
elif value . upper ( ) == " HSPI " :
value = 1
elif " , " in value :
values = value . split ( " , " )
if len ( values ) != 5 :
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 )
try :
2021-04-05 13:23:58 +02:00
values = tuple ( int ( v , 0 ) for v in values )
2017-05-06 19:29:12 +02:00
except ValueError :
raise argparse . ArgumentError ( self , ' %s is not a valid argument. All pins must be numeric values ' % values )
if any ( [ v for v in values if v > 33 or v < 0 ] ) :
raise argparse . ArgumentError ( self , ' Pin numbers must be in the range 0-33. ' )
# encode the pin numbers as a 32-bit integer with packed 6-bit values, the same way ESP32 ROM takes them
# TODO: make this less ESP32 ROM specific somehow...
2021-04-05 13:23:58 +02:00
clk , q , d , hd , cs = values
2017-05-06 19:29:12 +02:00
value = ( hd << 24 ) | ( cs << 18 ) | ( d << 12 ) | ( q << 6 ) | clk
else :
2021-04-05 13:23:58 +02:00
raise argparse . ArgumentError ( self , ' %s is not a valid spi-connection value. '
2018-04-07 08:45:18 +02:00
' Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS). ' % value )
2017-05-06 19:29:12 +02:00
setattr ( namespace , self . dest , value )
2016-10-06 13:21:30 +02:00
class AddrFilenamePairAction ( argparse . Action ) :
""" Custom parser class for the address/filename pairs passed as arguments """
def __init__ ( self , option_strings , dest , nargs = ' + ' , * * kwargs ) :
super ( AddrFilenamePairAction , self ) . __init__ ( option_strings , dest , nargs , * * kwargs )
def __call__ ( self , parser , namespace , values , option_string = None ) :
# validate pair arguments
pairs = [ ]
2021-04-05 13:23:58 +02:00
for i in range ( 0 , len ( values ) , 2 ) :
2016-10-06 13:21:30 +02:00
try :
2021-04-05 13:23:58 +02:00
address = int ( values [ i ] , 0 )
2018-09-21 08:39:36 +02:00
except ValueError :
2021-04-05 13:23:58 +02:00
raise argparse . ArgumentError ( self , ' Address " %s " must be a number ' % values [ i ] )
2016-10-06 13:21:30 +02:00
try :
argfile = open ( values [ i + 1 ] , ' rb ' )
except IOError as e :
raise argparse . ArgumentError ( self , e )
except IndexError :
2021-04-05 13:23:58 +02:00
raise argparse . ArgumentError ( self , ' Must be pairs of an address and the binary filename to write there ' )
2016-10-06 13:21:30 +02:00
pairs . append ( ( address , argfile ) )
2017-03-10 14:40:14 +01:00
# Sort the addresses and check for overlapping
end = 0
2021-01-11 13:20:02 +01:00
for address , argfile in sorted ( pairs , key = lambda x : x [ 0 ] ) :
argfile . seek ( 0 , 2 ) # seek to end
2017-03-10 14:40:14 +01:00
size = argfile . tell ( )
argfile . seek ( 0 )
sector_start = address & ~ ( ESPLoader . FLASH_SECTOR_SIZE - 1 )
sector_end = ( ( address + size + ESPLoader . FLASH_SECTOR_SIZE - 1 ) & ~ ( ESPLoader . FLASH_SECTOR_SIZE - 1 ) ) - 1
if sector_start < end :
message = ' Detected overlap at address: 0x %x for file: %s ' % ( address , argfile . name )
raise argparse . ArgumentError ( self , message )
end = sector_end
2016-10-06 13:21:30 +02:00
setattr ( namespace , self . dest , pairs )
2017-01-05 12:54:40 +01:00
2016-10-06 13:21:30 +02:00
# Binary stub code (see flasher_stub dir for source & details)
ESP8266ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNq9PGtD3Da2f8U2BBgCjWTP2HIezTDAJGmTbUI3NO2lLfIr3dymJRO2YbvJ / e3X5yXLngGSbbcfBvyQpaPzPkdH + vfmeX1xvnk7KDZPLqw5udDq5EKpaftHn1w0Dfzm5 / Co + + Xtz + Db + + 0 DI03bG6PoJy3N2L + f \
TuXq4R5 / kI9dV / A3pyF1fHJRwr0KAhg7tu2fpH02ObmoGxivbWABtrrtIl3A22ft3Rg + h65TuNDypO1ETQCQL563fagAIPgJvpm1Q00QMkVtdbEPQMIlt5s9h7 + 3 U / cg2se / 8 mU7SF3QIPBd1sIT3w / bhwICXbRA \
1 Ti523EfhGfS4mQTpkJzN2kf4fLjD0ftnw7Cb6GbOWCp1 + jbXiP4JGmhqRDW2y34qmScUoP4iIcBagj + W15YABXolcU7xwTGZwLhiPr + Vw / 3 HhEn2ZLf2rG72WpBUNDx9HE7cYtXDAv2eyo815 + 4 XoEMQP2qZ6bs \
8 yCNMGjYDTJ4ybMg5LmbdvxNr8fxKnAZ5N7MlfE6KXpvepJiRgPpavwOEr4BLLgb6Mn1lpcsD8196KEScTTuMRFMFwNUmG4mTsiftn9q70bHcnPXA7NQ3vjF2Lup4MbizR3vg6anNUofMuiq9lSIKvqkdy + xpcxu \
JQOoPoQkpAyA7jNW7tMDb6xMpxR6TgmJ7sZ2Ny + QitODOf7beYz / Lh46nvqCr4rxI74qy8 / 4 qjI5XrWtK + ka5lbjlKaPN2Rs / jYkmAD6HFQfySZ + ojXIcGTXIpRQml1sWy1VxrZVcFVsQfnEFvRabBlvNWva0uEL \
hohZZxUJY6okOVCpj20AKf4yyqC1IaVcAJo1QwACqMr97bZHULuWDQS2g850 + J4H17 / zgBbV0vaOU0ftBWAoDun7Dhh8qv2nR9R9tTQjaIWqNSKMIRDUPVApCdaDvn5E + APuLhl2x8 / jj33 + RgSxVfF5zUaALrRc \
FAyZ4unFDGW1gjo5MoAmTK05yA19ptO38g0jsOI3A0Wfd091HO0Bj0Wo1wGOch2fTGbP9mMbbSC3tXpBl0kGzQM2TcYXNPo6ifEfGC80jlpFQQP2VW / turG3oEVkoz5H6TKKnu0TgxCzCCONCU7QedpGhy04Bb1e \
PWIctGjKUKtkxM064a4QkxOysK7 / uoa5tg8nDMyEOEbpVqar0tcWHoqAix + QHCIi2w9ylOaTzfE + UBSeluAwsBZr0kXQcGuTvpGHM0Ak8Ca1P4bn7bgl + DoKUCe9mwyfHHU9W + 4 ZiFGir9CSysT / Is / HuLG6lgB2 \
7 oMNLw1Or + nPxSb8Rdrxny7X3afz9zxXtIiApH63n8tr0uIl2IPhuDwEgF5kguKGSNaO8KWMCqa2G / gFY6jAz2dEw0o / AM78CUcEXahL7xPjt1TY8itGoXM55HXcg / IWOkX8Kslcj + 0 FQDxpLyyCvjNjEavRg0sE \
k6 + w1U12FkUzA7sZn8eVOpQnJAE5Xxt37aSBwQHx0yhEBdgU + swA2 + hj5tvKwYS6RNVzlh1xJAG1CczzR54MygCyUdLxWe544ZSkQ + l / LKu / xlKHeRnRZ6bnvQU7e / BhM / zwGKixBkaJjGCDPsIONSnLWwDdP3hY \
FaCDfkxQg9df65vUISokhBM9cATyhxVjVaC9atW9cWM12efki3pdEczfLPXTCiHOZBnmY4lI5t8B0IxJw2ze91UaZmJgp6ryzBvQK + 40 lkkvVn92wHhJPYdcPlR6Q9pBizPkwF2wnSAdGY1Wxx1PuhFj4ODC0f8X \
NC / fg / 42 k99QLd4VelwynEmR9DEOF9KELh8ucxKP7ubWPTZLJVAKkBfT6OXkLYwD7ltG1Cdf4BF0VSU3wZ1ZA1s + ZvZNv / PsZpN439QzL1ASQFr61BnFojTzF9zAHG3twgPoAcW4FG2BBg3Gy8HCgIxp8wQ + L9i0 \
ND7dj9iNAqVWHDxtw9q / QxfZDXBKyYxNjvAf22IQIuBErSXY1E / 3 g2kc0OusYxe0fQqsbbABfcqcdEBw5yjl + z4bhewsOZzEV + DEcQPqdTAtromhJtqx2l3qo45nDn / w + Hanzk3cQU6kBpbresic7YFoZNLsd0YD \
ve7JHGeggvFqgUF4nA6ntqA4wKlpKVNXrA / j98QXSCK0BFufx + IjqiVX7uFnreei6wLd92AdnO84BL5HqtbOvzkiv31fPT3AuDPqO5gpBWCgKwHnZU1efJPdJWyi7sQghSY + J / 2 B2gFMFsLV + MJNKROM86HzXtrA \
snZzZH7IbhfgfeLYGkS1nt4AyVE / wN + nEYG1xArxl63vUjDn1BVj2RA / utZ5Zj7HqHHcJw1AT3JCEUI16cBR6rtt9A2 / fI65jS8PxEF9SkawlZUJjanTFoOVfeQ5XjB6 + pJxY5KOZeoJU4g4reOYdDl4XKWh2J1G \
VvyR4ya8LSc / wO3hr2hzH2MA5juIDbnL + mQTZHgizhz6HUCOsbiJACVSc7wRd20akU6gae6FwuVa1vko2BtgX / yCKsZRD9lzSpwszEUJzqW / kLQdEtLcg093veiDuaYffcTysIs64DEmN4I1YpkalBHIeKXuBYck \
c0qDWsf + S + pfWKj0Qgh0O2t2fYp02IC5ECfx4vuHT8y9iAEuJ68Y7ymb95o8twVFdC5EQG0aJIFjfKe3Dx / xvFNPMbqhj6TZkV2 / PNSJiGvaXu02Mxz6JudoCN9Bp28o + oDeizhCCWWcGuG9Ft5N6jmPw214YdEk \
8 / xbfGzmHktbkCpqEUPcYiY5IzAO0zevofOjcL0It8 + + ZK / KHrx6Tn6jGR / ZGzjCDkcYzvqBHRu3IBfIpPPH9A7EEATQAim1OoK / k63XAKrdQkA2juzo7tegyj6APO2SDoAUQeuPbnouD8RdkI08AQjJO96iwREL \
aIr0zFddog4ocQFGCf6bZAT2f8Tc00LArm + Nd1ue84uaRi1CCVp0cgYO8 / g9WPHTfejzNaYXiu8BP4twveMoxV6wieFpS5kbIbsUEApAVq9CmTRi4S9yoimaLdbJNuax19QrwN97ctsRlvEpyEoPgDml0VTSQnLU \
DrgFA35D4VJVHLEPoAi9Jv6WBNSoWxIEgch / F82sPoLsK / EHGJ8qAz75V6erdDIL9VE4Jq45Zp1BQUcQPAdYjykaoSReMBZeN8GE85G + jRVfVsf2s06WdDNlYbL6m7TLlqoG8 + 2 KNAkSG2QEOTRRTQBRxbwTKLVC \
c1ctWULWu / XOPiOQ7QPGPPygAAdF10yXWhgCKMHcIJCfrgk90F9J6BPKJXJnMOrci4Eq9tpa7iB5KirKtiPS4W2xG3V + FqnzqSg / Tbgr0fOtA4a1HgMIzRsnRGB + 1 kfABUG41 / 4 vM + LQOtkjmpQY / B2TOdNkvtZP \
Nu9uzcWXRAbmSUMC8ap5fw3rGNhjXnwAnYcx4biTCPpdjRGLGLGCEf2fYoRnQjlSvOFU55RxgDwUcRRmAg5yFQVrYtrBRcqZy0zqrzx8PDvw5zWHUjBvRkGf + rX62LnKHA17h2AAwUeqIYYqOaB39AVeAHVC9rCC \
XKBzuRETZm / V5JjyIvWO + KQCxzJFIG35lq3heGmKQE5IFTcElKNr1Y5ZgehWsawSNTb6IhLABBGaVUSTftW5QJRrC8nANNV7XsGLSRlpyCvgRRU06hTWXMatu5c34vvBm04XIKA7OANeZyF0Bqw + mo + j9N2E2he9 \
POAnSX / + n0u / 0 P81AYzMrc7F2Doq93gCgXALgXf3Gbds1x1qwv91BpmXU1uagv0tfoXLQ6HXc5IWZKTsmBFhWBWWXlox8dKd8VUICm1vZOtbyOsJslOyAsr7yziXyl8FFhpSYjB6BcutpXoKWlO95MSDxo5fEjvT \
XfOEIAIP9umNoLWehZ2IAUXfSsTRCVYRTpz9fAXkfvX89FdMCQGf53Na8sSoEtGwwc4ChiHjlRPB5bEk / NXTvRgMZAPdi + tUG87PDzqNEr0Ejb / DeRiDg8 / gWSzacEJzW4Dv1E0PV0x601uECc2N5gnefMYpyVzS \
IcrM9iFpUoCkFgmkyWjZRZ0Jk0B6FR18avOO2hAr5eMFAP0C6PVc0pcXc19gfm + / yi2GF6hRdgkTusC0LShCHb6CT / WrLv9pemtXM6eKIg6jmmyOadPwJX3TaqxOSsHW6Gru + SrKc + XYgNorDSj7vOnfB2o0RNeX \
BizCGy9XeQv34j6tWV / AmBhq1YGXtiH1kaF1DGG0au / vbgSvnXjR2GHSKaDpV0xHIA665FrSxI4Rkp6hfYgJxV00QOsK1qNVVLLz6VT8fU / FYyYwfIB8yDaPVqY8B1Xpn4bcR7yPfO6EbN5f12Xf3NHEXEOTWjQ7 \
asaFE7octQf69JlY73f4avOTbDd6bhgigFHU8QreantX69sHoRqY9haXPUsOfh2hMyQYW / MaYWx / n5YodZlAyhHMObJNxgZDiVFEzn / sZQkoj6kPfM2GOkDp / ZWijxkpmECJ8z3lKh + II + I9cqhbuLdwhkJTDhEw \
1 xNbVLV3hp0j0cCNgZQEMQVWC13elQKqDzuBFEjZUGcQOWwjK0bEisYSK7bR6W5kP3vCbNRqMqfVWA8njBnVvOg4WFNwGkfC2IRsHYefTW9hViqm8h1avB3BuxHr0cpbZ4XoVO1TJznW1hx8QWkeXOFpvtjbHnFK \
gscZUfCcQ4hvOeVXT6ScqIQCHsOy3KTbN0N2vsoZafBKTR9wDArDmReQTDCstYtyex3pchPBO8dc6MzmEWX79j / G19lJOmPs6cWbOOAvQl7wDtGHMBRwaYjzrbrgJeuCE / q5Zx1NtxLO + IismYXmLMDsuL3FctBM \
o + Dszd7xj12aAEYzWXbn7IIxrd6hQXwHt2dnehaqBX6PuZM3nGdiT0YbLgSCIhCrAV / jM4I85 + IVWOfSekHZBrf05NTCLLwFX0ezF92yWPv5JqkTXABOA5KqPCAhwuo3S8JkWT4L1QqVzQkyi31MBY4FMwyo5fJl \
DAOVHygjRUmtOhjtkUOJMJYc / IxdDglJw6OrcSfKZQt + / p7oQc8Ugzbmyh1DL6xaCcw7Aka9dfk1zGuFGTtmpAiyWchLF4VpI1gaGpBQmgDpG7wDUN6jAgjzs9 / V74tI4N9 + B44MaJsSmuUY0HwD3c2A74CbcvS4 \
D2b25iL8jFQ5ukycLS14qa2Fa3djy1vmzHll2XhmEjP0boUKGnZR1A2QsBHntdNIrNwNzpUK7mtsPt77ALfba1NwYRTHMhSbxZg + DDSnBnF4NQIVkMa41AZOSOvYrXfr / Tru + 38 wGMKZHFASBYjSlO / JWfcs9ym8 \
huipRfOW2t5Af8rBlIuuKt6SLkFcpDdL9Pdekt8LJmYpqfURnpCWEpT0t3561XlEnhdURxJA6uLe9fZWaIXmD3Hh5ccr2y2fUH6O8 / RscF3OjnwhLK1yw1exrBatgGItcImJWFIN4T97dLdfEd1PEcexT / ekR3dL \
dM819GnG85PzYU3iz6yogN5jFlNdSOwwRvpCCZnGEttxcLKpUMfb6LRP4CeEpByY1hGi79k5vy + CUJOWM042IcM / CW8DHAvMRra0 / 4 GgkpKkxUxW78Y9G7 / VUJYU8tUQ6Wfk9bto1k / qeTEQLWKAUiVuTjGZl0J5 \
b62 / JtR2aXchU2Q32HPnjutPZVUorUonH8Gnq6P7RbhxXYD / jrNYLW429JSQmFMMXnareGyRGvo + RxetaNzyBZALIj8MF / 2 Y0PSoud2EbEuMI + z2LXGuGlqNUaUsXMHP7qOfVd4euxAEg + JhLPyI2g8D4VckcuRb \
LVUyl5zRxJLx1l2B5d8WjfAvflVJKB5z7QOGERmXbWV9i9tjla / nVDEoiYE2GoT + Q2BzVGHNCn93MpxA6xSKn03FEFoThPFtrJtUww / 8 wFhTSqMptohEbSw1YmJpCn2b4oJ81SrlhQMql9IFWBSnyKTyyqIrVu + x \
o1I8HcvzNTQKT2RlTBSeZ61M + hnzPTdG27THqbYCM8mgo3o2qVhhk2g1gG3SkWeTYOD1rlRMJyuKcPvmiU1S7nkk / 03 z9DHJXN3LdFYflVT6k8wTBsXF0Dwt1Rua4u6lJuomd / sxtmmPE + NXkh79baiiUkx9quQK \
RlpKncghLH14kchok4qOsLpY61GXLAs7ndhVxi5yk6DCmHJ1M + frMbmLxIUbqMZz6rif8A + cterW7Bt2p4lJp53v1I / hpViIIuke1cN / IKq8HGlDiVEOr3XxfE4bP / ruIrBUTgnwzo + Qtee14DJ2YUJ21JLQtaMW \
hhZq2YMkkimNNKpSr5imJGroYv0yN0Hl6jYs22XMGMYm04R9FSz8QXchZuc04TRA5dmbS2jxDlD7nkuhsOIiA8jsbAkzBwj + enC5IPlemMfOZ8vYIYae + djB7qeBnj4S7MQednBJVELbdEkLjVothDsPilWe1Gte \
Fme / vakkc + 7 hWhAz9RCjOPuLCXZEZjzvc2aLAOhK34SleinrbgA0M25oaaRXO4L4wvTaNqSJMF + DqAFG1a8irrrekOLoonVRXhUcH3DZEFYATMAx0ZRmX3xaXk2XUmr2P50i9ZSol1urqHRy9cpI66ZtXL8 + YinT \
WWBbP6WZ + t / 4 yczTQTIz7TtJ3bJ1vWRI2TPAmA80Qi / oKyno20tWq1ZgXtVcEfIp37zOeubViHkth1Hf6sgPFjqKvyTs + xSmQCDTm5da1wFj / FnWVf211tVy3ULHAqd9Frgi + jP96K9vXQ2WORd / deiH3nMh0qO3 \
E4NZJruhuf6ZVoI3ttdYuWyhGvmFl / wzLLSYf0qVhebFEqx / y7q83PUOWXWZLsk / SpfkGUdjrHp8dWK7zwpaIu00yjZFGr / 0 EMjljojDwm694Y1WluLgs3 + S9mgB25FUrsXAjNUK2XZ1O1rY0T1SBrK3Cl2hPd5S \
BK6BRczhRhdcJXZKIiSt0NidryFiLtPXuBwMxRxQsNzYDycLDKVLmG7683WpXin + s84dImSNWpQswp3TbWQYWuRs7BkhZ82jA1DH4vYd0vB2xNZPFb8eMz9VfjEoSyJ9XzBPsH2V51jnqJw5j + 9 E + CC + A1Y15WJB \
LUrLtVPUC6yZqonAqMnevnruV61INvY9JaiNi1oW3p4tSSrWkvUDQUDTkZFicjuEMgkdsYol8JYVP06b7rDXxVtXOvG4uiJhoF7z / 271 zXuYjau8aflsPijXgvU0v2ahZaN1qDS0nPHnugeM3jFrDpDQxRPc62fH \
3 nq127slK5cQjLcxfBuMB1RlqF3KAZhh57K6n08gQp70y0j + 7 MVDKfzAoOm8jy1X9qFSWWP5uLTWDlYTgJzZvJgPgeecFlYWxl1lYQv / 3 imveqAl3ZIVOil6HdEDKuUxc1nk9fsOv0NNtJYXXXWfcfk + aYXpvQ2S \
Vz35EjYylAt / P84l7GOYfcyQfcBwQvbVis8LfEQXkCe6EGaS + gdapOTE1kS23MmiIuXm2D4An8GCQY2pjHDEZIl5p3L8E1zg6iIwt1uzLsIJF3YDIbEVRA94MeH67YysAC7VTLgOW + QY9wfWmAi21EllsKb6kCYm \
O6yxBHr8RFbQ + Df5wOgvLxAQXEW / MawO891w0auU1MKq27grEYdngARYnwQ5Q5hqT + Ul9E5 + GNpb2vXq2oyveDe54l16xbus / w5gq / neFNFtmMWDHFA7XYO8GrBywSjP1WkvEIt9Cwafdp1t56RPVfwA1mUb / TdA \
Ae6kmLWuwgqmog1UCsomAFe0e2JXdrX8RuuH2i1KT9 / xBrCW / / Yg422Jf2TTBeQusl0ua8VaIimRT5cPT8CVbNCoyvDolnIaQNUq7pisKo9FLTMz1bwcV3MNGi4PJkvxqpfb5XBBydIjGnesA7dH4fbWWgE11BXt \
pYKL53wBDSve1t7Y0ZpBJ7y4dWS33 / A + MhBd + 3 VwKyjs + rcniwCy2JOziWwYBLBnlMsxWGiMm9NoZwsXfsomJ97rSyQDqCfbbtP5ArG + TdBg9WvOWsXABjXop4hF3m5p2UkAc8CgkAmB66YFx1SNtwu0zEbfe7ui \
lDoG7VlD32g24 / kzrn + QTVZdDl0e5tAFFuq3DsduVwqg0ses9FAoeTcAam + 7 vGVd18vPTfqAHxoBCPMBhwKTt00D8 + eD70vl2VYIaBsr + 2 uopOP9nLee2Ms76e3XSZYDuBI0XwMANmongLAJ4qGSkVBy3Xdj51CA \
XqCYJbJfxEwSb2dvNxpvlzMYgowwV / / hp9e012Rrt2NuxdmVJu / DXPv1HjFJSxlLUuQGB3Ee9pvJcidukLQLs8ihB8nNH35vdh / xxhvaJ5R7G8EsHzGATn4uZRSGt4e5TXsMF + 6 dUJD9QzOT3 + tG1yW + cg27BD / A \
01 tflaRgKQBUJCMRSX / JNYtI7Djc759eUcYhnk8R4vkUIZ5PEd4nxa21f / LM8KiSrv4UbjoddOqfFHMa8npW70Ah0nreURDMz / HJOTzLeXMk6svGDI5UKNnRQIWD5bgl6YhKs92Bak + / phNPoWjGvX4gkGwD6gWe \
bVR6G0xoP7ckorg8lj4OOQvd8Ak9HfDMAqUJ6cgHrT0JXXmOj7MLckCEw1vsYzReQi + M7SHV3wqm1PrxPgNKpzjtCUv2T56QTbzd40e94ynwZI7j8yWEuVIqKhRR0dKUzNIRMNPudB5nE8f + nD2uQMqX + gzBP9db \
j4UPSOWjg2EOuzlpydwYaMqr5PB / AWEyri + WWzsq6mKQFnS2TgkFyxVwkMYjXGCznHAb7nBERFZyWEhdLyOyj / / jt9y0ypZ5LQjWj2fowWPaIaPyKZwcb8vSaVekIRqsUmugvszjAKmTPzY722ujHeTKc1lNxIoa \
3 G8PvgS6dFCzWPUBjirRY3nveVz0jqzyZsK8rzzKIdv7XL6Kux / LqTvyEvUwiGapD5g1cc + igVg23zs5fwOEftYpcMyoJKyjOa2oWR / D / HD0kkpLII + NlCz3Vu1NOuSlgQZYxEqCZry1gyxSccEPb2wVpspjjrU0 \
Zuyae5xF67GBO1sBVIgpY3EMxqOTE / z04V022A2sgZRQrKTLR5DsNG8AjQC8ecbhZNOd + RWsVBSNXwT9bER49KTnXPaSjWeYXlGP9x7dcAoA2k5GE1wtH38Rba8FO3ujQzFQUS1HS / zPCoNotBiuSbBG1vQqpabu \
DMiPwLldyFz2reVIjarwDHoz3JGKhF3S5 / 65 IIVsx5Uz8NLL + uFS3M4S9OZ4naZeUlSsms61rI3dBkPa88ujzhGTkFmOGBEdBroiF80FTgXqYbyIrxYz + L3wje1Z / 4 w2qNbF4kCkRYkHukzH3jMMOHM5 + cojwZLc \
gpCizDpJ7U69EqmNutihlOVJcbGGh5XoLgw3Lo0iIQsupzR8olNF20EWIIF6txPDRq0Uw6acSWMPHiVRxACOSs4f4mvKGR / z1s0qpN3rDR40wl1hNbKRPrMVR0TVuttToRI5JWYimNjtvMu + okIuLYkQch4Je2Gf \
pDA6Pu2OXTrnUxUK + x + w / s8 + W / 3 k35z7Nxf + zfs + K5rB8YH58N4 / 3 s2Ud1bYD + RPpiFZj8r3whvmVGDO8zfMsT6TtmRwvg9q9x1e87AJOj97HU9VPr3tgXfMijqqRJpfUy3dal60kqSQXXpYedfAYZPpS7 + c9RFt \
o1l4e + 6 XDoRLxAFB / tne62Cefz44a4JO89N8Lk7TDYrfsE + KnsRGd1YHQQNZ0Diwwnv32Hm1DZxhidvRawy2 + Wg2a1ac9FYZPukTh3tBY0FcXMiy23j2mn25yjsyYymPsUcEwFNrxNezB3yaJsqSFiDr3e6oJtyL \
2 RwKRRBonkildhny3K6glvhENR + j1M77hay7JAIALeKjuwZzAs7Ewsfm6Y7wGK3RdWfqCDrnMhHZq6VmT34O + RiGZqBWilXHNNGBKFY + iU4WKJfgpKHlg + yA24 / OCx94Wk / 6 Nylg0w6TAM042u + Whmo + kYPw6g52 \
zWSrxMbfRuG / WTrUvaAH8P + t0CkTLrZrwt + WtOGhlAQfigbd6ZKalhOr / XyInHgEGwaaBo89sH2JggyU + eaut / AsXm7qRW3e + uCnq1fIGMXu6CF06h / w2gdgr / QSDyrtiOG2pSgo0Kpj6kz2C2YbER / TsrQgImei \
FcDhExwKN4tEIxasXkbGOUt4AtoEowOEBlvjWVB91S7d407fWGZS9D7rXDT3KbYeGokiXcOqLDgqpknxNIEENr8Uk9HTA + 8 Iu3G3fU4Qk + EGTMUMpIvI7TlRGhz64uCY0yl1lkouLfIBMFT2gk + y1EuhxKvcOPod \
PDrujnvkVu0ktgD + 2 IcfcieXTYGSMJR5bIGdMZzNMVfYNX4DGaUsv74OkCG4OCYkldvuzwPeDSZrhEvfxD52u9ebOwGeMv3j23O7gLOmtcrGZpykY9O + qX85X / zLeziJ24eVPbd8KHXvDF2UxYnnk0uJsnB8wj + O \
LEFjgZuLhwDXjXcDBeLo4uIZrdbd / MSnSGGbml1tNT13V5zszZda48E3cqQxqivrf1AMPuiuXlBZbu9ZxlVS4izXdJLxHXe1qiOa3lIDUus1uhXTWharppzNTfCGFx01uulg7i8ZYtVVQSzSe / aNN3rOHjZhqfHw \
blM3GJ1V2eGvXIljcKV5LP2R0H36VeTg + NGDqKg9WJfOHh76EcNy6GEMOdi / 6 viXfv0NIIuBTA7G9o + IQretl3zseTY9j2147HXvpHG94rBtPWivB + / jwX0yuB8P7tPBvRncl4P1iuH6Ra994N / 0 WvonfOvTq8 + M \
/ lN / + pr7 + BN56Dqeuo7HhvfpNffZNffmyvvzK + 5 + ueKud / r3yvvyyvvFVbJz7e9T5Tb9JBydf8K8h5A312iBAeR6AMnw6Hfd62 / Nv7np3 / S6vePf9A6V7HkWPYK8HWiaAZx2cF8O7utkhZTov1CK / 9 ta4I9qiT + q \
Rf6olvmjWui6 + 0 / 8 adWlLZ0EZih5lO6TUGPsFpj5ND / J7zlJW2XjLp3pJnutvpObZLEaG / Ph / wFCW8dM \
2016-10-06 13:21:30 +02:00
""" )))
ESP32ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNqVWm1z2zYS / iu0EtmRL + kAFEUCvutEdh3ZTtKp3Tayk1PvSoJgk7uMR3Z0Y9lN / vthX0CAlJrefZBNguBid7H77Av4 + 97 Krld7B0m1t1gL5X5isW6y54u1NNENXLQ3ZbZY28rd1DAtPMkP4XLHXZfu1yzWRiQw \
AlRT96zRneEn7k + WJKvFWrulbOpuc / ebhNWEgLcm9JaS7n / eoeBYAdqOHaWI + xLGhCNpRRBHVIMGWHCjhZsKNDKgA5zKDkFN02TtRkUktUpY9EbFojrO4f26x5RjxnEAM5V4PD + lpziz / F9m9leHnxRJuxNJb0 / w \
pzxHFtRlvHgVkRSGtBEWZkmRqypSsO5xqNP3dBFGUNXz + 01 RHMXPbjQFaQYiSWhrtokjxJT4tZ5ZN8 / tiy4DK7aOFGf6bOmeQF2utq9Jmu6PCUlvG8EWDQT8DydkSc + 8 kRt1BDw / IqstQesqCGJKsmQFs0D7oFjc \
hbEbdFZY8r1SA7c86w99yQ1KDazCn7EQKxsMB5cZs47wTUkKb5opk5BA370qWXVejQbojnmM1VnCddPfSL24ph2o5f + pdQPWjAIotjE9 / pZecYpgIQ0 + OkIBDpQehN3SItoFoaZ8pXobp7L4fjr1V6c0jO / orCXl \
HaOSfosSxgjQZlWwJG7TrN + 0 CGl0dN2Ci2bRVewXVTqKMIE3ye9sZ6YGiGFs0vwDRJLOEY0mxg2PtS + Z9IrfcFzqKsbA9KzvotEC6Epl4MYv5rWK1wXs7YwnZ0FyO4kMhc0b14 + NwCCIVdGrSA / teEYwI8RnIgBP \
pCNg5QxtJdrWngEGverFqiXTHb / uvrUipuuIUUQlNMtIOazILkicPwNPZlhyfyrQTXOVnY8NuTRsqRi7d2X27qfzxeKQQgm9bTkiol8eO4XlvAMYmx6zz0 / IWUGrdryJehDMJOx5TThR1SS2ap28G9dae1TmYEC3 \
Jhv9 / ASoHAxG8O9JBgSM0DEQq24EQQdaUgRuyuenj1ERMHdAKilDZKk9ctQEnCoC58Dat7AziAMpgY31WyHJOMs0WL8HPgxJkrRkZeR9abBB71a9AFriVRKPpx + 8 V + 6 wsiKsQ67FNoU + AszdkgBAKPDhQHhEATLK \
e0tKbJCEpzAB5DOHPoSmcQqAI3KkkMMMLGCwPxZ / O + QYkY6u9GkcVJ4hJINCS7 / Rkz6XT4mJNrSCEniu4A1L2TVhmqVdgOd1xSqptqjEzzFs7uMubXzX01RMp / gKnZrnZJtzNqM2SXLgU8M0PEML4ntZDRiqUADm \
psn + KJXy11fxjQOoGvF / CgjyDfsAoFg7DBkxyOtu8p0d4gFipGRb8FE6lsnp7Dpe / yIktOje8vw7wx6fRbskw7StHt + Y / fBWxc64wc8GZrwcTMdgD / Mxpa3oFLzrVfQ2QHRZdvOwDh9RPMA6oQrv4EYUZLVkLjmx \
F5SANmz + eMODuRgWxVR / Zi4f4818H98s45tVfLOOb0CpvzEe1qJ1JljvPbvVThky5DhblmVzRnJKBLoqaBL9OXu6uH4LhI4anhJlFUGki1CPoMw + MS7fQMSaXLo9Umz1ud + ImtbF + dvMr93AG3fRsgtMPlwgS7Pd \
aCZu6fQT6V0yaPvaisxsufb2WnTt1civRijgz / FQN1FFMvkRI + DDDQpxG3n3xNv / ElK0JAQcjJ1oDMPgIp4diWle8ivE34S4qja0 / DB4OC4Ido3lRBfpnK + 2 y60KkHRM + GfjwsCAb5fp7COTEbF + 4 cmToNt6I97M \
OTGzlDyVPsvE / f1IZKrKUkRC + yw4RwXDyF8sVu8ofy3TVx4I33B9Ow4 + XTa8RkGZhlHNC2Dhx11YAjQBFXd6SSqBHAS8W6OOfwUFJqRYxAMWRm9ggg1Aga / n28DBhrwHozwa + FNfblTm6z6OKbF5 / sPp4Rnx2fYg \
QNmQMohqShkUkoAbEZoYWE9kz3u1Xa8QxN0y3XpDimmncu2nrcgVIXF74xS2F1HIou6JTzw8Cx1JhIqIVKEe + vyel7aMUVc + + 5 x + 3 MdYpVIOWdKhDV0ZQ1ev6R + kohMmA9iiSZw1xTpBtu1i21WLfa8pzgP2kX9j \
+ Vvb1oWvByXCGuc + HkZsfyPhLQQfSeEUX2 / DwhHEVPFqUKRs4xN6lxZ5RS5hZOI8vi7Y9em5rznsjr9w8aPGsLf / qN / VqdqSdoe9xLKB4O / dWcAyj1JBgkNCmab5kQwcOI / 7 WM6KzwchAx1x6i + / bMdkX6N0VGTy \
jf5YSltVleRKJptCvQwB1KSUHGIchQ4FdOUQKdjl4go + Xh6UVrF8teSek4kGEYwUqQdqglLGcBdHpg735L6HDN52fwxRMPuLT2lbsU4pK5D5qiPtFScLhmugoMfx0SHIfMS9QYmT9nBQ3l4sVqOLXSr4MQSY4o4o \
SMs2hgz6t8e3fIFdqmOgsTxOGrw4HXaYzM4uZt3MRZrHRxcL7hzUaYhkEPHJSAkrwYINbvHX170hx3FhbgnocEIoLdNuAoTUxiE6wDj4WBmN4xzcwFkUZMVtR2nohLqgH3KSU3wQ4k7Pm998ckGZABpLfpc0PNlw \
nkHjJ6DZBlEunf3GOsox4DUnOOiWQbhpLgNdg0g4e9xaFPaUYpZglbIglkCZzV0YN0SPKg7i3 / cgcooVAZkbT3IWmjywXtVZb + ZhkNM37Te6x5XIz4ml / YbJIUu / + OmL1Z0OYAJ5oeXUqKvu44jd1qho3e5ytrW4 \
k3j497aqQB / 3 zR6srNK + IqdBi9E0QVNk8fdoUPrBD8ScyLpRrp047kt0GVX1MCHrT5hz9wD1cesNfkam7crWs94z4xnMgtlg6ilGFz7QYEaYJtzCy3xTL4UQQbeWQ2e44GyIISv2VlEdW58pzrk0rX1e3P5gm4o5 \
BFclt5VEmOaO2YDrfnqxS60C1CtS4r4xbuLREOWHv + MlYFkSenVVNiejbeyQPMOhxTFlKNhjK4mS7z63aTe0HpS82sg6D0C6Ww6lUByVnLg4VN8L1USbird9wjm1tBo7C / ZR8q / BtunFxmL7VJQr + Veua8Qd77A8 \
4 PSelgczhQhuoh5DT5TTraLoTVHmrCfY + nzJMEim5beBsU + Z2DjZgQuGupL3DQ0K4DU3ES0h + VFLK / cdpyGJUOJFaskcOOVpm57byMS61 / DAxY8hH0DYNPFSpCdkEfTKF15WvYGwPQz2BSGhFL51feutcx3Zswav \
yJ6evqAoL31c7 / gjLlf65VCA9SQsuFxRlghAbiHoasuJAB6M1dQttnJUH2FCcP + Y6AJiqPEX1Ej + 76 h1rugtvyfOU1fxHubYNL0Gy1 + / BeLDI4orDSS2mvvncfzU4gFOInwG46EK0l3VbyuVVA8BVGDdoqgtj + N4 \
AiJO9qmzgnUaBKSJj / HcVYe5Uk7hIvGP8sjGKPqzaWftlIITA + zkiOOkFYZBD9BK4 + kDlqI / cVaKiXCkLkzds02FrQhRtcr8uJpT0xVPhVKMTOufgdfXccZDc2IHkUUXoE1 + yl6kYOfVfyC9fQMLvAArmbBZKr + 3 \
tgucjvR1a6Czoxhtd4 / 9 Cs99rTTstcXFJkEM5E1UrpWgj16MmbOD2yl4wCGlcHry / eL6M3W00TJsZBl4nKWp0ATvg / 2 ANKHmNjjWcD1WNBem0Lcxcj3iHr / GAoAb1yU3Lky6s7jdJUMQNupl2 / QZtrE + wzXgp0So \
K + N + d6mhfSKrYzIAw0G / KjKqFER7zNAcB8jSapu / i9mjziYEfDKUbkEtxPiq1Zv1kA8GQYtYwth7uPsSMM1 / SgCVBdYtNlgSpMmial2YegsinXIage3KW7hY0jKyn0uYfP42nNthFWL9I1K0246Tb19OaUxmsQ1g \
9 HSs + vOKpj1peqD0VasPr + 5 w5WtmDWIWMGBgSmlOCrqV + YgSbzzZrajbhuV8 / hAKBVk0FIFkcchxwa4ZOvBx65yjKFkoD4lsY5eEO2o8avkmZSz9dw8pNogs1fq4uXgCYsgwac7En1sMGXIh7QG7q3RbZfn41GA5 \
NT1Bhyfjmd1jgl2HxLxAd9uNoiEeFFm69s7ozGDvjks1nPYJxoEyQi3KHz0X + QEWC46YL9Y9ptUpcvWDv83w9tOMqzLbS14Rg8BNsRV2NQyOrHyry / aKfeX5h0k1JxNRbeh9e7qCzMBDOlV2YnqCCdwxH5Ogos65 \
5 cjApdNNtMCYNeaD0pbdTXDDTmskY42JBvCBF1mcg6jhkJAOU + 7 KhDcrH7 / GR3FpCj1Hjl9VldA07L4zg3jwpOy / QhMHE2f7J4Uu5NV17OV4aCSO + XAVUj / IhUGpkhuVYAfaH6LF5z8abRwL4J / 96 dtOe6S1x0cp \
ufduBN09AlrwIgDqcvJ6y / krvQyim8yLPqEkQIFD9DdD5i97J23gx / qSDzWLl9xI1iSUUsX0FKzljFNM7Y0s3ao8 / 0 XHEk83jyJmUm4veJDGFCOJLF5eAuWbt6D1K / zGSN + cY + p9X653Pazc8YkCdgcOiFEsBdk7 \
Sgxz9vtwPG3kzTnxXPrjWvkD95f5NArxiE + 6 scJSdOqHebUfM7426Wj0F8CzSu3CWjckfS1DFC3ZOA2e / u / 6 c18eoIBawuG74qMomCwqqhIx1E4S + p6Ab7HfuQxEsStaEojgwabXr6W6ueRmvPQfi / gTmGKL7ibv \
2 DaZe5Lm3n + lAElAU681QU2pnFp1I89DBlvnIz4lwGCbEwyWEz4pkhl7FNpdcfZmFn0hA4ai21rmI + kZuIYBzc1BaJEECursavYhoIPmjlGYoM / ezho / 4 XQ4z4jVcASUbUE0mXgY / WbzqZKyPzj7R7sC2HrO3YSw \
yNM / g83dLRmmiBoa + SVro3N62H7P5xdqy4cn4ZstzHLwU6o0fN6gWKs + YRL4GdD4M / VOQxa5S9NFsTugrVC9SnfjVF2MHvl10WMHnnADbStac9Cezu + Fz7OINZz9jI + wtp3c1 / 5 jPS9a3nl1EFjp6mrvaYIfjf7z \
06 q8hU9HpSiySeqUmLkn9np1e98OyonM3WBdrsroG1M + 4 djjJzGhcS4mkyz78l9OLzRi \
2021-01-11 13:20:02 +01:00
""" )))
ESP32S2ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNqVW3l31EYS / yrjAV8YXro1GqnFJusxxzwTXhIgYAjx21hqSTY81ms7DtgE8tm361KXNILs / jFYavVRXeevqps / Ny + bq8vNu5Nq8 / DKuPAz8Ds6vLJevdADv5R + 5 / CqbXZDn9ic7cGftfChDL / 28 MqbCbTAlEn4 \
1 ha95q3wTzoJj0UafmGpJgktWfjN9WowcE4DnQ1 / s94kgRSYPszgHFFfQpu5DNMZtZ1q2gIVoTUPXWGOFOYBYm1vwoK62Tq0djQ8nyx2n5vFLu0wUAtj6gEhgYCwqoMnc / Ngn75iz / J / 6 dlfEX63w6rwl / + onxNC \
GuCMl51UNJPxtPG4Hm8KiakUL4sBYUVyQg + xBbl6cL26gzDjp9CawCamBuQIUljdBfwWRG8jxIZ + QQRFGUlpasUvPySrGGyoT9X4msTgYZuxPNqw / sIE8sMO6UTkvCakuHtA8A3SzhJY7uIufEka66AXsB64iiKY \
hcagbSW / OzcNazPz0GxCoy2ATvhnZsxlE5UFl5kxg3CkJW637YKnsDB / GGqZb8JDD / POuI15WcJzO5RicXhK7K / t / 8 lyDxqMG3CsYMXsOxoSGMGb9PjpHm7griumUVSFUSLAQfBS74pVKCGm + n2xkKd9mh7HgAPh \
2 cQwKitSmog7mLNLgOWD3BqRm3IqhXru / EjBu3faLqpkW7kClpMIt9ezAG / CGlfwD5yPDYboC9qF57ZukE9e8YhAZVFpd5c8GpqoWgBNqYzUyGLCVXzOQbxL7pzGnTdzpSus4bi + 1 gOPvqtSQ3E + VOUluRljPtEE \
8 MWGCRq7RHVRYh3oYORrcXjZTdNvP + 2 PuiSia0UoeiXUTMUcZuTQSaByoHGA0qWwnl2wJCyqWlg0VS + FoT6kYcqtF37KUWjWd6s6AJUJ / URI4kF6QSojHzraPxuZU685X / 2 ut9waCdhV9 + S8VTtai45Xa5F3JI2S \
w2uldu5UPOlthAOQGwRTm0rY3F3sqKV5JnZfLpGZ2i2MkzYGaQnyzpPOl / NIdVj19MsCIBq2 + suWNRC4JhY8FcVrpxwtAS3wupdgslvbondNpoItBMGSnGZnn7ANYB4yufP02 + DpJ / B0SY4afXvZLm6Ff0vyOg7D \
AW0S1BvtkglBHUlGYh2Q1HxdB + jXsTyh1XzHBzbl5PCC9S98qSsdk6TRL0nzehR0I7p5gn9tojS60TVvyY2FbyLvbrK6VTQHfrfVtAMTvFabjk8VF8adzqMt6d6lE6soU3kyRrxCw5aCew7qU7KTA / n2eDe7EzqW \
bBJ + gGXFXlEbwq + qCY + gyuarsoN220bmGavAl / uKL5itzgXqCq4YmB / oOtWyA7WabUTQ4pku43eS8uvqhaiAt0AhA4MLahBS2dKyximTUbHsfh9tiES8P1d2uvcQxr1Kn8w8QS7YoJm9Aofy + ucnh4d7BOk7Pgee \
10 YM90FoyDhCYppwk2EZaEKyTkF4CEmBePBWyIoZiyph9iYjrljAgvN3p6xt6fbzLZjl7nQb / mylMIE3RaenZ5TytCViIIXyFwJ9Kta / kp9I8YBmpEG5OouYZwJkoHtRXjrS + QPKHZiYEDKsJGhasiGJOBqlohO3 \
DalzB5KSCBXkeZDeoCdzk5WgXfF6ZR01VpBpn6U3yQ2uRG7Q13bFnVmSMVLIBk / b2ocOsCm / JxlOohMzbLHbDmEGpKLl9NbMfLsnvn37VbHfiLu6gzEGWFiKnOfjGUhMjF / pl3fPWMLOX7B0nZc243 + mdGltjYQB \
uNyaL8kT1b5nyk8jEiDv8uS + Zy1Olav6AmDotLj1t + IocVMr9KzYwffTBbgPczCjaI5Akp1hpUaDR4BgpxO / Hh0KgGINoopjSobxIArypln0jPRDwfgxqbzTkjjRL2f65VK / XOkXBlLomtbKQTZdiSlCg7mHKrr3 \
ONonpQZV5AMglgvBfL9w + SJBl9kebtLWIFL6aiz43ya4szPk31Oxp3OSIS5fvgAcPH8ZBJHTJD77ToGgjPbgq / EwRFI8jz7couf4 + JSNJEXql0i00ZnF4neKu5Y9jcQu9ldnV6KeOeRpCbsub7 / qYgMHy3NCGh0E \
mz9DF / 7 xfAnBRxsihgMk + wx2P + EdW / 6 CZK5LF6GmoxCKXRmTVa2A8I / Tjw9ychswLqbSTy7H9 + 1 yCD1mGX0eA + ZT2P7yHe / fay7Dl63I2FUZHTBcbwiel5nCDNk7mqaqaL8FB0oI1GHQBcChh4enr0kxyuSxAKwX \
XCebRTLBhsuWF8opWvq8fQh0PNuAdYAXgHiSl8QUaABrLirllLK + ByhWPAAEmyYGmCIfuALDgzES + lgvIW23tATB + RnBeRIpOoWyIWYMDYpQ4kRhmebvoDRzst79aX / vESDQLrNyNI1JFvvfors4o1gWGlSa5I4I \
ZWB9A7 / EgqtbLHYHlalBGYuDzKa0bsGaw1IhJv6 + X1KxZtErzo1uKaai + GJTVf1tTZ + yuHHZGo6pVJHnkOIFrlbrHLvUOXbNefgrAd5g9E0qVKZn3GzMHxI7HeVIFFLNIwHsbt5B939I14532ExzvTuXKb08uXQb \
n67eS1 / zgZ + K9H1HAfHupJsz41QPEmkpp4FdTxH + 3 mNEIs5xxZOsiSrb6LVjaLsH + M88nuYQc / OGDIlWeEyq6 + 1 EKTZYQ0XTfQQBrOG / e4RW2vbWjX69e421u3nEb5j1NWTxnBLvVGu0B2j3ErkbBvoJW9dcAbpm \
tSpQ + G / JhmOyXo / 4 evBilRSnk8g6zuS4KOCbv6 + zoO9oRmZPI9AXSOTLL9RY + DtCxubL3 / 1 KDrfH / nJw6PFkGmHxNhtaD8hVKotEn4C / 1 xyF6rFlEC49I4cJOqPXC0hhZEknqWezGu8rN6KfpV3ZSkJaC3kEeGef \
LqDICwgMMxTHWpFOyNAdZ7 + gAbjMiOzBACQNQ6Ak2bRqr + wNpjvhHRutUqUfoZ7G7k1g4K3ZM3A2O0vK + saKKRVHAN5sJTMdHQE6zN4zolbqxj2n + zLJ8Yi6AM8VZizacby12n5Ovq5tubbKq51QZMN5pVZlGG9y \
lzfUBactvrDcirg1GitGahNKB2bMxIRSlP4XlLaE3qz7eg1fqz2gs2LvgQWHLWyDzSRPDy + vn24wYAE85vMPNIetubaSi2Rg / OyCHxJhgT1rJy2I2JxMewzL3jzt5w3Wb1RPMWmdkzzF8EBW5F0ZdszZB0qdEleu \
9 MpyOHOE6W8JStAeHcXSO4BIm + iq1DrVz3WNDGotgPwFN + FzG6NC3UXfHvvCB2BSweIA / FZh9DzOD7B7siS / jYaWvZ + 0 qqeTeJ8tsXKzIXnCsZSnEYS2S56HjtHaAyYgo0qKMcubyt8PySk9kYNp0ntZgU / RMHHq \
OsuBA0u8V6 + iKZcU5GSxKi62jIdCCDcLkbOmx2RPiJidlsRCxPxL + oKGHefRA7fy3PY29UDRaeJYq5fqmBi + bPZI + KtL5Cku4gwXXClNegsthHWmlQ4GTeKlvFp8PeHp0uF0s950cKZv5VMaWfeLKuLLQi1IHW3O \
3 F4OvnlzzOU1UQME3f + mKk1bYI74IeaZoMtO9B3bGtUw50Mr8JyF + ZIlXki0DbbjEAYcAMfJ4bpsI27B5pAduTtgr6Bj8N2OVPDAaBuIHZDwuxmoVlfg3uD8LZUpaSMVeo8KTw0uMNk4Y72V47YqPYiRBG0Yg9BZ \
S5 / x1KzkdMVFPI2pLlSrnN1YyfXuArq + AEvnGmKZ8SGil5pBrxjanfwdcIGsWapjJv618365Wa13iyobjkM1MhjUzpsIH9qmo + ODI8SONMxW9sMF3P4S34OarG7pgPJHWMBgwNXK7DupsGcpEq23n0n7QGULkSEK \
AtLE7ETrB38RT + TYo1R4oOGmlcDPRrlp857ADA5wiixj1XROyaKrdTWoMCnlQ2VvS8nxYGwipDxDUmIYRV1KFUDMmagCifocT4aJKHBwzsjKSEvCovsKFcsrLIAlkYKz90QVAImaj4wRFrflEdX85OwPogP0cAVo \
T53tXG / Q / HiUPvuMp7RZr3hZvlZTcBkXo3RHWkZOuCiER2 + RwAOI73Le1hqO / 15 xwJuja2AOC7NRgabLeUvqHn6XZCmCPAuDc + bfELcKiftzhQE4iwfKEL7g80R1yOK + jIkXOOhyge6YRxBhrJTiJ2pP3cGdPboi \
gbvsAPm3x65BeOU6BjJjHYMwPt1wLhv6ckcctp1B / CSne0ti9fKHGOKc3hTa0rEe + r2ctF + ABwYtAC3ykAuLcamMZoCUA72ngulb81HM7f4ArZMF78uSC8nKpoNEIx3JRzPyd1ImKcW / q8jG3ChF3 + 5 K93qPQhIo \
gwukfiIvidxu9JFfQScojY1GA5mt5 + QLs7xm9VANTQdD0tU2H0cVcvkC3alUKPGEeQP15kKdtWBNxJ / 92 YEEdKulPo6Re0mg5rAUbAUQnDXHMCklpVcFFgzafeVoZ8rvRFZp2DdwqmuoncQIjB / u2dWUT5YyLnG2 \
zbWyEo6wgvuQWyxCUbjaRWQGETnaPyMPB7gEiIV6Uw52PDtrGTz0vEn2C8Tyv2B6 + VRQkoXqk / / zRzZovMIw0I2ieMg0jPiUoqxO2sfQ6c3hKZNVmL / YN5WEf / AOF4QGjIwZndGScK4ZFEL065UBcc98H8KW5X + A \
9 ivmWUYlQWXRXF4mdr / lqk0R2W0zjrlF8MunwuIzOQxC3OTXsYYHCAAKRIiHpSJfzn8V9PYbYrEPAMZVZiIuy / B1IZvdP0Yf1C5FrzBkdTkKFgrMhmB5rM03yxujGNpnHzpgXI1i70U0 + JoLWH2MbbPfVoEwDEBT \
WXLNvRlWCRCKdZUBSZhACbxAUdy5fQBvFKQ6DPpyaPhr + 9 RS2Ee90gPXWcgbTMCXAYaBGgsFEKy4THYwRHAcRFidH1H9zmN56sVIMQJ6zviqln282gEsBM881M7rZEqhzyeMJqqEC + 3 B667 / EXPUKhnUEId5MhQr \
MU + uqgmTWrFWMdl0xefXOCdWdpq / T / thQw + GGzoP4 / HWSMXGj6EV423ro / mVfFyDTqVkJIZldLlnYOO1WkwZ5rfi7aSGjtI3uT4kyWeKbn2TiimwQoO3YKYjVw9oMPDFZ5MoQotSmo + cS1CgjdcOXsK1g / wtXjvI \
t / IjvD74BktA34yHwUJbBbFVIZKEET8Bmw + TSgiSLEzdZTXk7NWZPKYIa3Qa0OItbywjXJTk5hssFhqskmdb691Mm3xSiXWNkSDacBDteEhRc402hpvky1dYH + Q2OX9CBMln2J5vDtR8Ou6lzfMOipEQjeikcusg \
zHOdPZJae7yyCF8lVarkenqJVWc + / 4 K + oMrgjTGsA2asCnWTHRR / PomzQrkVz90SvmolzK / jVS7HJTnELbYr5YnvKcLDxfrjgc4i + 6 A07LZSyoIAYyD8bG28EgJr1FQJOmAbz8hBYzg3jLB9 / uZ5PxFFhfCPaGr0 \
CVCBENeCsBTAdT6NcSsOl8ZiG2cFj9BsTQ8YsWfrs3haiLGiVsfW9Wq9s81W2032YujjtxJFWNpYVaPr3Gb4O1udDJ / lGCL7cTV4jN9UK + SCRpe2bMV75KizeL07ibd4XFcH7a6rwIhPlOC5Do5uUHeTb0xpTzo / \
H73CZ7ZvyLp4N2wqE7eYCuRKLDVWHuQSF5GGve / wobdaq1ujlv89IFvLekOnkZQ + rzZvT / A / rPz2 + 2 V5Af9txZp8Vph5lqXhS3N6eXHdNebz1ITGurwsB / + / pa13N / lLb6IsSYxJP / 8 XjDX + 9 Q == \
2021-04-05 13:23:58 +02:00
""" )))
ESP32S3BETA2ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNqVW + t23LYRfpXVyrrZyjkAuUsCalyv4nQt59JItizLjnJqEiRjt64ry5tqrSh99mJuAMhdNemPlUkQBGYGM9 / c6F93Fu1ysXMwqnculsr4n4Lfm4uldskNXfBNNXlwsXT1Iz8nDheH8M / GxbKr / K / zE9QIRmDJ \
zD / rbG941 / + ZjPylnfif36rN / Ejhf9N0N3hxSi8a7f8teot4UmB5v4IxRH0FY2rhl1MJO / W4Ayr8aOmnwhoTWAeI1b0FLU3TjR8NNJziozenKZ + eZnizGZDjyfB7G7hS986O6CnOrP7IzP6 + 8 NsfhQMYrRwFcCjk \
tCAlJ1zVtJ5yJIS4KzOIJNWJXO2APJu9pYs4ghI + + 7 zKh1 / x1o9mwMpYwZnCiazyAr8Z0dsKsX6ePw5bRVLaJpGaG5JlBwz1qRruaflf1FV4D + Ttqkci + DjTTNL72UyujmAvfgc0lVcTqddApIHNR6J3 / hyajrf3 \
rLUVqXSqvTa5DgprmRuTCr3O9hJty / l1FntvpgW1NZFt + IGWa3 / KzjIXPBZectk5v + GptHVqV9nT4fknG + A5VZEa2UykitclHNecJ08i5y3bcwXXhscrYj5ShuZRJ6 / iel4fNKwJOqzULS0AT7RfoNVzeClVuFTx \
P6RytReLsEx / / EP / rQUR3SSEoso7eJIIhwXZ18B9Ftl0tghXBE16ijcFiQdnu2 / JYIEVk9hnRDK / nCoJJfWdb5nhW5enjyMS4FT / fgNET2kdsDiQhLGH8EfDuW1YeoaAoWFjhhPTgGfY550reLV4V7074XPuhrv3 \
JvY1J056nb8eH39NCtoCd / 7 M65KpXsHj / yW0Nl8lAB1ZwT / X82Uroqn6oiGObU9IfkplT0BIKK4NEhLMbobCco5JNPy0TxqxDMxHtl1zF9sJWK0ZU5rBUOCu6 + IPJ0zEgVhm1Ovf23BFiBY81XiTURzIykDXqymR \
LphdVwyphmUIPkQoB5Os4N4Dvck22ekwbmlGk4rJNsqDnMlHo0VLYQCiA + AoW0BtNhk6kB9vQqZm9SzBZh8QT7BmV / ubGpHTr5EDRd + Jqsz4CeN2T23qO9QmmxMTQcy4XPqmWafxILQ5QIlgd4OUn42TkTaBQT30 \
qohDRnf / p / 9 DVMLtzYj00uZfhHMakfwdbvAYbjbv7 + 5 tRkVrJQ6bzIqKw68GaTufHOcMHRakWp + Dur5 + fnxxcUghFUloS2Krt163q4LeUJO3936mI4UoAV3kZOj9l7Cgv3Il3KmXoBf + T138kw9TkUamwrcr1n55 \
vGDlQoQ4fgfs + r3rnAUH5uWvazZnxCXGVLRaMBBXQBBTZ6NLP6EcPfPkss + yaOkHL4gJ0tXj88MERJIIFvTdWRB6VsXjA7VFW + IzR28CXropyBzgWty7wcgAbEb / HWh5Q7LwkjHANdCaA4db0TYHkPjrqnqaQZR9 \
l0wQtCar74eQxbiDMd26yd7pLhzgwXgP / tmdwEJOZbx0H8kuWXQVRmX9uBYjL69cDanhMlyRQm7B6QBJSdCq0aBHQFU + Yi + 44 mK + R2tEwyQ9r8WTa1aYLJ6aoBR5vxbivyRyy6LhyvUgrK / warQSSdS8X8Viuctl \
VuoeIOKqB8AD7oIjPIzhmBENTYDVZkcwAZhyhxLTZ2lCgiN6z2DsA4lYNb6fqy9FobO9c3vUSlDxBWISapYc + 3 RI4oghaTILxDFBgDpVE90DxpOa5A7PAezXhz3JHDdnEBh6xHQRXhxBL4m01y7Y8Jw1ak6MERsH \
Gemd0vF9CR51PeagFinn3bvJ3f47Zs3n6Q2Q9G9QjBlFDhDLeEGC6Gh4do3 / FBsbRAFAs1Z3aTwbenBeJ2kC8hqR67E7INkhBEwYAjTkMtlIvCuBATDoxB0lkggnoGMyvRYsOnd / uM8qF207XOGb8QwR7myT0kF0 \
lyXHBcnbgHaV + GS7ho4klUDPXsd38PAKUnG0S7XCICq8W3ekoudVoudAhCvvUil58316 + G / Tm8v0ZpneLNKboyNWEEgtMDSYzE6P2Po2KkHIfbBbDbBh1CtgrhaxPWOsUM + 6 JINYIfyjAOATDsuxVPOI + U4ie7dG \
8 BHjPhLwdrKXuSEoCkFN8QX67E8MFnobJl8uekpaRuVB9awuFqKuqKIMyHiCWWJZgjya3r9TTa17Hb25yTBE + Y3ubBKpoW9sIjPAg6lIzSrEem98V2lUhPh8CQIcMTngWCQXwtiI1VP4ZL8GBbeC / VqthqTfjG9e \
gBymhMZIF8rgapso0 + znqEJ2 + QtraFkSlgNGyKkE34EB + vw9seya6GaRTVT2vXi6bkWeZ54IYKylSNsybiBqFu + Jpromrg3HRqB2IA0NMV3xJGKCm6Y6I4zckKMJdQpdX88FgW4JAmxJsaVToLjKkERK0eFrkppt \
6 dQsa07N + pwCip0OOWyj1OBl065DljbqC0Z8dbBGPEvzewCBdacfjg6fQp1TSq5Y7dTljNJFo + AqZrVvzIzDqV7uN0gUV8YmrAw9JzVbnRgXXfMQi2TinfHGH + FOsuIkqSF3qk9iwqBJVqjTCl5nvkdwM4Rx7w8Y \
/ rD + qfHmkjJpHAa45 + FlyGuIQr45oqdv6YTxKURBHUUwFxfkVnEcqgI8vn + Kbvg4hDqgCz8S7HpKNdfBmyoUYT + MMVn + KpoXoqIbKsCGZBqa0oGAZLjmVzX8 / XZcZmwt05hFUpEFQFKPtiRs35Lnhz + CMW7gX7Ao \
HLx / L8SnTwkc62DMnhBAJzBg1C38vbYRQ5sVmw / w / 4 yTL92r9ede74 / HMSTekwj4l / UuoynWCMdlKz2EjGIliObB + FwOrlrDH5wsdR1IDKAcYHLmhqNNtyb6BunXHIU2 + kveMhmsk + s25Jp00rxstYb62vLRYsLb \
3 v8a / HD + IFZNko4Jll4 + 9 bhdJvlv3pPjBPorGt1dycridnEMoCk7uVjsnWxz4g5 + xUnRNKdjRhVDKmWJnDyuYmdvMIfDUk0 + 6 h7DtQHLTgjO7cm8Dy7abcLWXMpRsSMBxRtSVQL5diog / 0e2 Z0Rm / YsV55IQVWf9 \
UAwXV9H / wDhEmVUyjnNwwblUamJaEUVKJTz0KoYJK4hynFvas + 6 tBEdsO7BBsRx1PB8CZ1vIOKCY6XZlqVsWXQEjqjvi6lOF / rE7jytjmVHN78Vikx3QBfugHyO6MPpbxkcNLSnNG9hcyu4F + aAI652sOudyJm9Z \
97 ack0CJfqjRiwoMCFNFG6i633FCjFRdhaLbgmkW4OnsT5y29iT / KqFYxYqdXtnxJqhjbzh2hzBNqmOXAxPDbCjRFz1xJjMVzdTmx2RQy + A7TpcGSWuYmA / 5 + ldSklCE5P0J / 4 AVWCQtzyLNRc3aPxo8o3CPrD2o \
UI7YcSIBXykxNJwiNrU6GtmSWwjl2t5FUpYzfYNWdd5Sya3rzrhv1cz / NIgY4LCmZ88l0FuX9qvngNjzPw9jjW0GcoFDxjByvecXO6bEGv4odqfqyRlX4NstaGAjaTdkpS45f3I / e / DA + / fKHb5kWKG848NKBXiX \
1 lhJu3OGU5B0p7iEqNVcHsG7P6wJAIsYOmNo3vSjOCfoDxkXjh9L + + 3 sFWXILmlzQPyurXAD06YvyXZBZsSQS4qrVVqW18T2kL2hOludSW8MlBn6H3T3gudmwu + j9fzahN / GhfxqB6spkBdOD6bzxEQ1UJ2ej + WO \
xLrzATjpcxCOIBwK05kPKxp6Pb1tFfXGten5lBRIOnx4yYFpMEPRWnYcpkkMGQ + M1zQ16zhaEKxXOKaXzqbjenaNJrhDpFd0lbFvo97yM9oSm1dTztMnif21vIvggsliBKbQEZonQzjJyojbpvgPN07NS / wYY4e8 \
qTXR3YOmVSwIb8w3a2DAIEf75glprEL61 + 5 rhdZfpP9R9ja3CBkZlW5bvUX + lgLqhj7YAKfX6j2Hxvng1SaXa7NEVIbz1BCMAjuNuKEsqkMgDjsdT7kPbc5xl52MyvOdfCfhEuB3HLhgoppLQ1nqmFAwMYMSBjYe \
SzzoD1TyAcwE10 / lZlXuUUHaStQylQtuzVP7AS9G8qgQBZQYCxEzD89LDr / QJ / nwK7AifGjmIyc + THEq2YBKhIaKla8T2hEnsih8eWLO8BMAhDqTPeb3zHMg + Ps0xIQXExMCXUt9nSueyrs / QzZwDWf5EtaGUhZ2 \
KVFtjJR22jWdvPYnEHhUYVj2qzVa3G4 / ll0fha + TdmJfJWjScJOCXZjiar1RkxUffsbRWqtODwkibPkQ1rgl1E / LTUnZC1uAlo4JgrGGgR3d0oAO / ABDk + BxntmjD6zAgzShsYEjAE / + vaukxdHiLQbkGHinfRR4 \
rUr7IZWFyRqwB9TVYVD1MqfNVWgKdo8jXhKmrAgmjYN7eOaKDdZErJq1sMJLMkyuOeZQrOvY + VhSUOnzqyQShgioqmM6DLaHdNbBwikjURmL0eZs4RlbhVjfMHBzxRkTMz1h4G6TpyR4 / xBfKx9 + x8LUk358h1R1 \
VMRpDPuzQfEHCm0USb77lo7GYpcZCedUCrh3PLdy4HxhRHNI33Dh01oiEzv8NWEYFXA / p7DfcQ + rlApaS + 1 yTO / YWKPFc0GAPLfmJhVraIV2txdO6INIMXzthx1W6JlMb7GzgePtbczvjJSmM9AKd0Y1uzofZHhd \
SI0PgXs9iRr4K6ZDbugst / OYWLkiRga6E4za5Z5UmIi + q5TSBCtUOkUVD2Mo6VRMJnu9rkxI / SEZnMjgJx / n1K7 / KjkiQD19DU / OEfssYR92EVdgQXP8ZLFQ0 / ZzfnGmaoHZdPAgcKEwqTCSy1MB / pjM0yEa3qxC \
UMVfbeBnXUTjKlYGU2SOGop8qPq / ww2mGBehvWOlOeNEqUrS8zrUHHoFByrwoLOs6xFNczVZOqY6JZ1aO30Vy3QYYLd / tIjxbsjdCeZJKaRAU9dLkz + 8 QG4YCOH7MfxoLmeP7eQzDB0 / NXC6 / Y6DcYb2KtviQJ8r \
3 HhttjiysGTv7XR / 9 dtMehWD5VwEwkIw2E5sh5HyN3BxJR8IfH4BjazpwfVn + Gc5Bbk4ZbFncbomRoc4VQ7JJDK1g3NCH6Olbw8fawSyMoLpTgASusdklvohLP6RWgHYVhR9th + B4Gug7XO13BZAuo7Ne4X9KDaa \
CsW89ReWEYr5Y5HAo46YV2kGwboJ6ko5s6PADa8NRar0AcZsTfqFWU5ttmG7j2KBJOW6Jrio7bZEhTwgJEBSVhlubcB0wO / KsWd3Iw6xcx4AYU0v48KQfoJCgJZirz0J54BVKIZA28MwxEP5PHxOwiJzHquvQMme \
cnkzhBvw / MrJd2ggRbfkhSoJnzuOLC1 / 09 oUe9xxwq5wQWWQyknHG78iFidT2hfzBAndl / ThGKpiwUqIlsaJXKjShxWMPZ + fiGKecKs5PLV + / b / KU487ZxwUp91H7dbAno41Q5wzWVNVCKl8z1DmYTvQ + iInx9nb \
cPJ7OMuubzAHryUnhogWpdP / ElO + hW9ilE8hyS6bU8nRDZyzy + IXOIalLJGbwoZifpv8P4COs2CNBr89ZqNel6mnX38o + KCN9gUi3FgW7uQbjnKlsY91h53YiSYq8UX5ei7ZNmzXyGfvwmXRe3UcqSKx7eyP8H9b \
/ O3TorqC / 3 OhVVnmpVVl5p + 0 HxZXn2XQKDtRfrCpFtXgP2e46tEOP0kX8uGhndrst / 8 CeNOGkA == \
2021-04-05 13:23:58 +02:00
""" )))
ESP32S3BETA3ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNqVW3l33LYR / yqrlXXZznsAuUsCalJLcbqWczSSD1l21FeDIBm7dV1Z3jzLitLPXswFgNxVk / 6 xMgnimBnM / OYA / OvOsrta7uxPmp3zK2XCT8Hv9fmV9tkLPfCLm907v / LNg9AnNVeH8M / G + VXvwq8PHdQEWmDK \
Inzr7aB5N / yZTcKjnYVfWKorQksVfvN8NRg4p4FGh3 + rwSSBFJg + zGAMUe + gTS3DdCpjp5n2QEVorUNXmGMG8wCxejChpW66Da2Rhmf46fWznM9AM4xsR + QEMsLaBp7UndMj + oo93R / pOVwXfvcncQMmK1sBHAo5 \
HUjJC1cNzac8CSGtygwiSU0mVzsizxZv6CG1oIRPP6 / yEWa8Ca0FsDJVsKewI6u8wO + A6O2E2NAvbId1iZSuzaTmx2TZEUNDqsZrWv4XdRXGgby9eyCCTz3NLH8 / OJCnI1iLx4Cm8mwi9QaINLD4RPQu7EPb8 / KB \
tc6RSufaa7PnqLCWuTG50JtiL9O2koez2Ac9LaitSWzDD7Rch132lrngtjjIF2c8IlBpm9yuisfj / c8WwH1yiRpZTKSKzzVs14I7zxLnHduzg2fD7Y6YT5SheTTZUJwv6IOGOUGHlbqhCeCLDhN0egGDcoXLFf99 \
Lld7vozTDNvfD0ctieg2IxRV3sOXTDgsyLEGdkZg6OAdERdUq0mNFckHu / vvyGKBF5MZaIKy + + FPTTCpbx1lxqMunj1MUIBdw / gWqJ7TPGByIApjD + GPho3bsPQNEUPDwownpgXXcJ9XdjC0euvenvBG9 + PVBx2H \
qpM6vSpfTY + / IQ3tgLuw6U3NVK8A8v8SWleuEoCerOKfHzizFdG4oWiIYzsQUuji7AkICcW1QUKC3u1YWN4ziYa / DkkjloH5xLZvb2M7Q6s1bUozGgre9X36YYeZeBBtoiqSHkdcjp5quskoDlQV0MfNiXLB7MYx \
pBoWIfgQIRxM0sF7AHpTbLLTYdzSjCaOqTYqgJwpJ5NlR2EAogPgKBtAYzYZOpCdQLZpWDtrsNl7hAwwZ9 + ElwaRM8xRAkXfi6Yc8BfG7YHWNLdoTbEgJqKUcbp8pFmn8CC0BUCJYHeLlJ9Os5Yug0E99qqIQ0b3 \
/ 6 f / o92E5c2E1NKWX8R9mpD8PS7wEF427 + 7 ubSY9a0UxKsfBV4uUnc2OS8YNCzJtzkBXXz09Pj8 / pICK5LMlkdWboNiuohFq9ubOz7ShECOgg5yNff8VTBiefA1v6gVoRfjTVP / irVSkj7no7YqpXxwvWbUQHo7f \
ArNh7aZksYFtheeGbRlBiQEVTRasw1cQwjTF5CJ0qCdPArnssSya + f5zYoI09fjsMEOQLH4FbfcWRF64tHmgtGhJvOPoS8BHtxUZAzyLczcYF4DF6H8ALa9JFkEyBrgGWkvgcCtZ5ggPf11VTjOKsW + TCSLWbHV8 \
DFiM35 / Sq5 / tPduFDdyf7sE / uzOYyKuCpx7C2AWLzmFMNoxqMe56Q4ZzsOR / SRm3YGeAnCxc1WjKE6ConLD7W / EtP + A0aJKh2RNykdxZWYq0Y4JP5PY6iPyymK1IJivPo4De4dNkJYZoeD3HIrnNVzp1B7BwFfpx \
c / voAQ9TIGZEOzNItcURdACm / KFE80WeimCL3jMY9UAK5qZ3S / WlKHOxd2aPOokmvkA0Qq2SLZ + PSTxi9RdXAorPfRXzzY4BI0lNcofvAPPr452sj18wAIxdYT4JT45wl8XYaydsuc8aFSfGiI39gvRO6TRewkbd \
TDmcRcp59X52u + NO + fJZ / gImRCaLJhBCXvHPEIvE9tbHl2pjg2gBgNbqNt1nc48O7CRPQl4hfj30 + yRFBIIZA4GGfKaYiIclSABWvbikTCZxL3RKqNdCRu / vjtdZ5aLrxjN8Oz1AnDvdpJQQXWbNsUE2GmTjxC / b \
NXRk6QR69yaNwW2sSNnRQtUKg6j6ft3misa7TOOBCF / fplwy8l2uBm / yl4v85Sp / WQ5Vp8OkCLXidbRBslxurg43nIDmfTBlDUhi1Evo3oj8njB8qCd9lk2scPBBMPERh + hYt3nAAsiifL9mBxLsfSDCe1nLXBM6 \
xQin + gJd + EfGD70NnS + WA22tkxahnrrzpegt6ipjNMqkSBKLYKRp / K36av2r5NxNgRHLb / Rms7ANXWWbmAEejCN9cwj / wQov8yAJIfsCBDhhcsDXSF6EoRLrqfDJrg6qbxW7ukaNSb + eXj8HOcwJoJEulMHlNlGm \
2 fVRueziF1bVuiZ4B7CQXYnuBKP1xTti2bfJ8yKbqPV7aXf9ijxPAxHAWEdht2UAQSCt3hFNTUNcGw6VQO1AGhpCvOpRAgc / z3VGGLmmMCkWLXTzaSFQdENYYGsKNb0CxVWGJFKLDn8iqdmOds2y5jSszzmy2PmY \
wy5JDQabbh3EdElfMABsojXiXprfQwosQv14dPgYip5Sf8XSp64PKHU0qj7IM9zX5oCjq0EeOEoaV9pmrAwDv3Ww2jFNuuYjVszEYeNL2MKdbMZZVlDu1ZDEjEGTzdDk5TwkrxaPqSLYsZts + cXNOIrEVWIFJnlY \
79 Nj6oCkCZQ6k73Y1N + kWZxkTruxtMPlRxnXpknCX6 + 5 Wt66WKp9P8WU + utkdwiXfqwZG5KRaEobIsThnF838Pe7aV2wGc1TrkmVGEBPPdmS8H5Lvh / + BFa6gX / B1LDx7p0Yyz4m1GyilQdCALbAslHp8PfKJnBt \
V8Ag + oUnnKTpwYlAGQzieJrC5z2Jln9Z70vaao1wfLFy0lBQKQYif7BKX4Iz1 / AHO0vxB5IIKBqYkrnhyNSvidRB + g1HrK3 + kpfMGpvsuYs5Ke00T + vWUN9Y3lpMjLu734CDLu + l2kp2roIFmo8Dbq + yPLkcyHEG \
pzAa / WDNyuJ3sQ0wqzg5X + 6 dbHOCDw7HS2m1pG1GFUMqZYqSXLHiKMBgrocFnXLSP4RnAyafEVzak8UQdbTfhKW54KPSuQWUeEhVCf27uaD / H1meoZr1L9Wla4JaXQyDNZxcJccE7RCHuqwd + + CEC6nnpBQkiZTq \
fOhuDBNWEeXYt7an / RuJmth2YIHqatJzfwitbSXtkFeZflemuhFsghbVH3GNyqHj7M / SzFiLVIs7qSRlR3TBOujgiC4MC6 / Sp5amlCMeWFyK85UArPx6mXXBNU9eshksuSCBCrYSYIMKjAhTVReputtz8oxUXcbS \
3 JJpFuDp7d84xR1I / mVGsUp1Pb2y4nVUx0FzOkPCRKpJZyGYRBZjiT4fiDPrqainNj9ljVoa33JCNUpwY8dyzNe / s / KFIiQfdvgnzMAi6bgXaS5q1v2j0TeKA8naowqViB0nEgnWElzDLuLRV08tW / IKMV43eMjK \
d2Zo0KopOyrN9f0pn261iz + NQgnYrPnpU4kA15UI1FNA7MWfx0HINgO5wCFjGLnes / MdU2Ohf5LOsJrZKZfpuy0MHYC0a7JSn + 0 / uZ89 + HAc + vnDFwwrlJC8X6kT79IcK4l5yXAKku4Vlxq1WsgnGPvjmsiwSjE1 \
xuztMLzzgv6QimH7sRzSnb6kHNpnZyEQ2Gsr3EC3 + QuyXZAZMeSzIqzLi / ea2B6zN1Znqws5QQNlhkMSenvOfQvh98F6fm3Gb + tj4rWDlRdIGOf780VmohqozvfH8rnFuv0BOBlyELcgbgrTWY5rHno9vZ1LeuO7 \
fH9qyiE8frzgJD6aoWgtOw7TZoaMG8ZzmoZ1HC0I5qs800t703Pdu0ET3CHSHT0V7NvoBPoJLYknXHNO4GeZ / XW8iuCCKVIEptARmkdjOCnqhNum + g8fr5oXGCrvkDe1Jrl70DTHggjGfL0GBgxydN88Io1VSP / a \
da3Q + oucktSDxS1CRkFl3k5vkb + lgLqlax3g9Dq959E4773c5NJukYnKcAIbg1FgpxU3VCR1iMThichjPq02Z7jKTkFl / F5uU / gM + D0HLpjBlnJcJzVPqKSYUW0DTydr3Oj3dGAPmAmun0rTqt6j4rWVqGUuD3yA \
T8cU + DCRT5UooMRYiJhl / F5z + IU + KYRfkRXhQzMfJfFhqmeSDahMaKhY5TqhHXGGi8KXL + YULwog1JnioZy4PwWCf8hDTBiYmRDoWu7rfPVYxv4M2cAn2MsXMDfUuPAsE9XGSM2nW3Pe1 / 0 NBJ5UGKb9eo0Wd9sP \
ZdUH8Q7TTjp / iZo0XqRiF6a4sm / UbMWHn3K01qlnhwQRtv4K5rgh1M / rUFk9DA8KLW0TBGMtAzu6pREdeE1Dk + Cxn9mjDBk8SBsPQbAF4CmMu8yOQzp8xYAcA + / 8 zAWGufzsxFnorAF7QF09BlUvSlpcxcPD / mHC \
S8KUFcHkcfAAz3y1wZqI5bQOZnhBhsnFyBKqeD07H0sKKpcBVBYJQwTkmpQOg + 0 hnU20cMpIVMFitCVbeMFWIdY3Dtx8dcrEzE8YuLvsq + XqwhyH1V99z8IEi8njO6Sqp + pOa9ifjapCUIGjSPLtd7Q1Fs + ikXBO \
pYB7z32dB + cLLZpD + pYrotYSmXgPoCEMo8ru5xz2ez7vqqW01vGtmZKPnHJODRcEyHNrPtBiDXVod3txh96LFOOdQDyJhbuG8xs8 + 8 D27ibld0Zq1gVohT + lYl5TjjK8PqbGh8C9niUN / BXTIT92lttlSqx8lSID \
3 QtG7fL5VeyIvquW0gQrVN5FVV + lUNKrlEwOzsUKIfXHrHEmjR9DnNP44VByRIB6 + hN8OUPss4R9eOK4Agua4yeLhZpumPOLM1VLzKajB4EHhUmFkVyeKvPHZJ4e0fB6FYIc3 + 3 Ay19E4ypWRlNkjlqKfOhYYIeP \
oFJchPaOJeiCEyWXpedNrDkMCg5U4EFn2TQT6uYbsnRMdWratW7 + MpXpMMDu / mgR4 + 2 YuxPMk3JIgQPgIE2 + oIHcMBDqOV + tK9lje7muodOVBK + 77 zkYZ2h3xRYH + lz6xmezxZGFJXvv5vdXb3DSUAyWSxEIC8Hg \
gWM3jpS / hYdLuUjw + Tkcdc33P32Gf67mIBevLB5mPFsTo0OcKptkMpna0T6hj9Fyxg + XOiJZBcF0LwAJJ81klvormPwDnRHgwaPos / 0 ABH8C2j67q20BpE / poF / hQRUbjUMxb / 2 FZYRi / lBl8KgT5jnNINi0UV0p \
Z / YUuOGzoUiVLmocrEm / MMtpzDYs90EskKTcNAQXjd2WqJAbhARIypzhMw / oDvjtPHt2P + EQu + QGENb8Ik0M6ScoBGgpnstn4RywCsUQOA8xDPFwbh2vnbDIfMDqS1Cyx1zejOEGfL / 0 clsNpOiveCIn4XPPkaXl \
m69ttcdHUXhuXFEZxHk5E8e7xuJkavt8kSGh / 5 Kul6EqVqyEaGmcyMUqfZzB2LPFiSjmCR9Gx682zP9X + Rpw55SD4vxYUvs1sKdTzRD7zNZUFWIqPzCURVwOtL4qyXEOFpz9Hs6y6xv1wWfJiSGiRekMr2vKjfk2 \
RfkUkuyyOdUc3cA + + yLd1jGVnPHEaykw4ib73wI9Z8EaDX57yka9LlPPb4oouPZG6wIRfioT93Lfo145 + se6w046oiYqcaDcscuWjcu1cjleuKwGQ6eJKhLbzv0J / p + Mv39cukv4nxla1XVZW1UX4Uv3fnn5WRqN \
sjMVGlu3dKP / wuHdgx3 + kk8UwkM7t8Vv / wXDV5Qe \
2021-04-05 13:23:58 +02:00
""" )))
ESP32C3ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2021-05-31 15:32:51 +02:00
eNqVWn1 / E8cR / iqObezAL7S70r2tSYxEJGQZTGkKIVDRcLd750ISNRgRTFt99 + 4 zM3t7ki2Z / mFLutvbnZ155pmXvf8cLurLxeHRTnU4nF1qvTu7tMmB / 9 ebXarMf / q / Ks / 9 j + QN7k9ml67wl6s8kWtVPvK / i / uz \
2 WB2afTssqj4s / ZPVb3Brr9teiM / u8JNP2 + h / PfCjy76s8uS5vZ / / qLuf5xdNqn / 0 fBI42 + 6 FGvw01rLd / ypN6dY2H / DNMY / QVchfPkEkv + Avcg2amzMytO0nBe9cS / zUxb + 0 qrHEGDhryYkwIXfgZep6Q2q0Z5f \
oNhnGare1AvVGz2Y7vpnVVrGTRWyqcPZfOOGlv6qX6D2MmvjvzT + jvXCV03un4Nc / 33 kxznWI55tmjzfcCMsPWTVBIM5l9NOeT4In4nOeq3yYAI / HWzZx + f3S5GleEFmg / DjuI7yn4VZMeL6oub + UpbmFQYdGcJK \
8 be238MAPLtf7 + rUBEUCl / H2MX2e11vU28jYR2FlAWvpH7bN61yzOnWf7czDdXHiF3EevIUeeRS4jOFJakhGuAfr8SpVwiZS6gzrpvch7KirOM3zG6xP1 / yUud9GnfEAAlkmQPd / xjCWwnWeJOjiKZsAS + Kzyg9a \
lT0gowwn4lB4LJ1gKLZTsn9oyzDHIkUt8xRbXE / 3 O1ux0OXQ / 9 ceVHXKVwvx3gKLGcyvv4IHAEmKsYBhjXlxjEEdr04nlv0waLcwD3HFq9fJlca9YMNDG9C3zmVKHWWrFPzPapmsz1CDfE2NSSfBaTrA8I9gH5l4 \
nBLM6Tgk7LjRcXXYw9FqJBgt1WPWaezVKSDYb7xwlY8Jm + IHGFGkY8CNgfnX + 3 YqOyMSnXSQjF3ZoKJ + x0GFzhxG40eHQR5gyJDHa5GcdKdwJ8Od3h4MLlMXGQCyx4uUYfvdyY0 + j + LQ8klcBLr0um4CbWBOYspg \
oyB / iVkCGtXaZgLkaeebdUC / hUhUgqVbdxL64i0JAl34gc3yUl / xHY9JNqISHMCI6 / QiU7rulLUI7AiZa9xmREz4FfiKnEOcm6QEVRZpCBADbzOdQlMpf1FqG3PaNOcLHmgX4664WjTK656Lvvuggi + S0JOIq6Fy \
iZ827c7uxIOuZd849SpfwDZlpAq6mQaHdfp + vGTpUpy8NgyHVR8nh6uib1udxynEMdPWJFfTkzteYs94lfB9lQofYxojecXmLAR2C9kHeWoCzq9dR9I07PxzNw + hdOLa4ET5B75OQupBKcaTjjIlLoGJEEZkhbKW \
rRjSnRfu9kjuIMcwB8zxuv + SyY / Ub0 / scz + NCj9H9mc4AXwWiLxHIflkcCDuVTNMN6OlWUtaAmp4hwPizxk9iHSvZOTNSODZiM2APZh8MOKwRPmTJEySPK3kTFV6r2YYhgRvJV / T5p5kbLa + / 2 sXcyMWfZtrulpi \
GHywJuxPeCvEavnOMRnl4l43LZkE0hpcsxrCQtFI0KX8drxZBGKqCkrJJXCGfFLCZkcRw5DcBoakQM8Q3 + PAHhiqw50LFg9EZrsxjyLq + ZK9AGq0QiK4A3QU7oiBB2jhs9Ic9gsMrvb4Bnv9EJ5gOAyyMp5yGF3n \
jY / vgXS9 / IAxb2HA9ECWrtkYDpFV8i9Xyv7y5YI5xcQ4u + DFi5DjiNcAWk15zJ7NXrHLqgzOc70tdjigrE6u0ndxBQJuORmy + 3 Gmdd7JHOgPghrhBBZlg4hGRFx9vLNaDa3v3uCM2soIq4GylOILCCu1UWFVObgD \
bitHs7n3rSZ72byCgl9NW2zB + Y39gEGB6d6eMiHAEEX65AZBwGdXWKF2K3Ns3QzDn8BYMosxUewP33aZFqJlz8EG + v3yw49 + 6 j72ov + B1N47knWSrzW7oLUDuNcp4tgT + MAPcE9UMNU7 + NC89YOgrIzhrdeMQrl + \
ITG0cZ9CMnMsvhc8oSkEc + WfuAarevKp763lmL3oMW25ioiljr9AS0gJ03OL0PiJqatIHWoYiBkgWIQ6Nu9UAitegkFtUXy6bd0dGHX577DE5gVoj / aIKYQwbK8O9lJcMNJplDxa6OmxVK / petKZx5V5OGFjItTS \
vwvuxUpN5jNvVYUEHkGu / xxKKyXpbFHoLTLsS8wrKRBuVcAA9TrFslI + ebm3nH1VImxlUCg1PRFd7qj83lBCTUIR / ZOkgXRlRYgfb7BCZVb87Iyz0f9LYXvfsCUAVl / rgunPKGVchOvNdggSDMulJBH9ccLf6lI0 \
647 tQLKiHmdvdY8NS5UZeds6jPIujOQir / 8 DlyhzLlBI6JQFDdlim9AD3SGhpz4ULqIWqhPp2dDaSVx7lbirrAPiDoF / EXGHh7 + MuJtmSd4 / poFT8xp21WadDJgk6vUCtXd80 / R2LQY05dN3zxBRn52 + hJpf3nkF \
LL + azf + Om4 / ePcbNx6dnuHl2u5t95S + G05fvo62QGEETPtAcS74oFA8Iu0Qqpx4zuVM8xkpstxLb6V7JolMBkjFMkBCiXkL6DkRZNecBm / ySVuWkqLiff1qrHoPKAqOTk5Zgzoqn0L1bBioa / xOooOQyFTO05exq \
KfrV + TFBckdyxFDC5sWgbRXyEwBbRePK0PU5FflK6IBGlawLEXLOfUHLBAEQatU2yeybMVLPgtO5Ih2Px1v0UtciWIqeo9IPQwMiVCzbn25iS9c + yqM / h1q8phaBkkS3ukGQTVyCKqapxYDc0kJ5Elo5waNJ88Dy \
Ws0MBhvfwFRUNZ / nj0Tz6CvpfLVm5YDaDJugsdjFo3TKEDn6TS7Alr / kj4OZt1QW1BTDzqsQFRn8bT9HUh3gB1tEXUXXk3jdVybciJhLG4L4WUvp1me9cCNytsBQqmLkftlfHY8 / l92m9ltobmZn9LNtbtw9hsLG \
X0MlJpPOQp5EqW2 + L9vTyLBU / pqrndbf8BAQgS3VcqHbAfgtWS3 / n3d + U05df / 72 doZCugm9xNAA0vzdaa7hr8V2zTLr9Iaiq1bxjGA2m6wV8r0AcYGKovpVipyqCapIYsOs1Usp7SZ0TqgMDHEIVVctEcxY5OP2 \
+ 2 NY5KWwjQkGDAZDxzkuFgydUrcUrJHfsXPkIFAaRKMbFQkGf6Qbxewijkfr0 / 4 qd5LZgu6kQXg9jZGOugVtoS + nHd + G3wj7TUqtlG8l8hbxuVVxvyOA0d4f2HegzISUYjnC1MjJtYkofDa7WFMY6IPmTK / DoRZK \
44 gk + k8lLXJSkqL6Q8yGa0CrRtPWtTT4kKKmD2XVlC + Csi1sp9KxZcMT29diPVec7O0HZt / b5WBFMHVcA5RpsIfLdxFwrCiecqGHohWHVn6pw + KNLJ512yk9ieNN8K8eh1DtRsRP73v08Zx60keogY6iF8thktO9 \
YEnYWSHvKwXMJSPHkqQITeuqIM3BRDBzIwcKzj6GjZA34KzBqBPwG9LJYrTd9wqFjpwBi3PLgxp0d + EGZxHDkBvVHIN1SDfMFRQjOLbySsO2IdYXFCnJWMhQXYhLp38h3sfWEv2rZBpAaLognHAQELBSkl132NxI \
6 KamYr6 / x3BRVWzalwkp / xbvnA8XduhQIoBRYwLgtMVKvz0RuhBPdgKEcEu3bVJT812AsSST0WFGAE46vhM8ZXomrkBzyNzgrfxBuDhoZxW11MWaErJYaXinGMZutOzT8mp1NwmLqvd7nsRNEOXASWD0WlyNqfVZ \
1 + 9 HITZPu / GH7zkBXe / KnYV04tfYmQoExcquO71XLRKWQhgqHY2lFSsAozZZB9i1 + r2fBEBPpV0JTTjROCU71P88Z2YKGjGU + ujvMN / do7vIUIZojRm3FN5xy1CQuIFUBIVkJiX1oaUzt0k2peYAlJszNGsJnlX / \
0 y0mTSqa8uHbuAwiS0F4Xn6OVz + D925YrFC / SBgzp7xe25zf / NjgXQh9ZID9oXRGGiFhOodIW0YeSdQRbNN8LfWUGvSXdp6ODyTxAWNPrERkdiE / xYlMoXFoVLiQY4SnhYMpn + WnXYy / ZNwwhfS4CynkmpQOlIht \
+ Lgqkb6QHKVWuZxuNW1O8VD8S0vKnksHtpbDBxeoHKlOKfoxErFEYCcCl7KvNlm + 3 nDNd + G89JOQhoosCAFqStZeTzqhN4nwJnEa9xrP / MEyueZktA2Xf6MDiZ + A6mSwhE8KDYVjFKOEjmzg8YYTpKIJmWchDp / O \
FkP23nBqxdoQFy76t07lmI0OfrjCQFrfrESGN / D829LSwYsaRLnE63UWc1qvo66CpAHoiLO + Ds9Iako8VwULU5IpPtUUA8uNjLozmwqHzgUSC8cpxr60GZU4fbWGGK2exBzZBBaktCbE + VVwiEIp6e7vR5G1HC61 \
XQ / iQr / + XFId + kT0bg9R4GzVihbRMLRnDNmVsMuzvZXmXPeOoTugTjOdSKSjDNxSNktheRzCcnh5qNyWi68AVF / NzalkcJIj0Bss02uiufe + i / iShu3bEc4KtP2JXxsJleTvpCU2Um060Oj2h / rRPApFHiTixyUL \
kK6EapvQMQgLW + HdE9plcjUdRkRMXnMutzlQncX3oXiTQ8q1PlARsMu8X6MVseAGdgzoihhdC1 + wohYU6H + MJ4xBgRw + 7 FOC6BwdBYz5M1T4kfNzmjgNExdZbFAEi12 / iTdT8tvVM0dRfWO6Ukwl8JbyngP0vToi \
6 IB74YdDqrdvb1sdQX4itYRn9LpTO6bvO6oN5lGjbZxrVl5Xg88 / Wn8LSIoYUs8LYTRNUP7IOK7kja + Aa + lGHwqTgPRxiU4mJSJtUOxR3EyhjjcN28neMw6vy / zZzH / x / yopYW24yFxL0urwgswf4TAzoCZjr97S \
gJMXmZpj7ii2eKN4irfn / IRz7jzySYPPKMOZSBJXUOmJHK83nI1dnDxAH / JImpBhH1W5O + mdyJs8oU + PhoBWMXsNnR0d / vpCtarabH0L6xe9F7T7Q8k4gAr0zyshp4ZyPicbps5z7wWiAWUBXEqFrrMczqrO6wY2 \
VqWFs8 / jKTO9kxOCO3VrMun7tzt3mbRgTHDHlNMMih81lEohUbrsxGx1XFsCx6pf1uHEx0pWhf4YPdmBSdFwjbHuWxtQe3sb231T84KUnxGtcbQpw3L / 6 rwOuB33b8il4D3JZ0Fp9vTgGSpPlCjo5DTZtMF5Y3b6 \
EH6cPTpAlZw9pl4bToZe121v / fCbHXq19ucPi / ICL9hqleeJ1kWi / J16vrj43F7sJ0XhL7pyUdKbuNDsgLzjUC53Z1E6S4xKlv8Diz + ELA == \
""" )))
ESP32C6BETAROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
eNrFWmt700YW / itpEpKWvc3Iug0tiU3tOA6XZftAKTymRRpJWWjrXYKB0F3 / 9533 XCQ5iQ3f9oMTezSaOXPOe95zkf5zuKwvl4d3dsrD + aWx80sbPmUWvuNjXp3NL30evg3ml4WbX + Y0ehAGi0fhT / pD + BOHoTT8 \
r3fDHy93x3T3 / LKpnme0xnH4Yx6E9QfLMIrLzfxiflmb8CsaluO9sEG + zzKU0Wx + WUXje7PdcK9JirBxFD5hbp4Pw5 / B / HC + wA5Y731YIaH1aJbLVmE0bFAHma0LX5pwxQfhyyabH5Jc / 70 f5lVhfsn3Nk2Wbbig \
W49YNXTS8KmqjE7K60H4VHQWtcoLn6A6Fz5 + gP / fr0SW / BnOOITwk24fE / 7 nbsQquHlTd7ySrXmHYU8G3an7bf33MACvHva7vjQsFmdjqCfYxw143WDRYCPn7 + vOsHe4qQg3 + + ZlZlmddsB25uk2Pw2bVNOwth0H \
FFThzlrVEI9xDdbjXcqYTWTMQ + ybHEPYcV9xltd32J / GwpJZOEad8gQCWcqYwG / nGEs6zouoLh6zCbAl / pfZQauye2SUUZDbWV7CJFNMxXEKwAmyMcyxSV7LOjnpdDeczEXjNdWKavQoHrochb82gKpOeDS3rJ4c \
mzmsb7 + CBwBJhrGAaY17doRJ3dJBOs9 + qNrN3QlGgnorGWmqZ2x4aAP6tpksaTvZSgP / 81 YWGzDUIF9TY9GpOk0PGOEWnCMVjzOCOdtN0RM3ttsd9qhoNxKMtgqnqbCVv74EBPudNy6zCWFT / AAz8mQCuDEw / 3 Hs \
Z3KyFLOnPSTjVF5VNOg5qNBZhdn40WOQe5gy4vlWJCfdGVxJcSXag8Fl6TwFQPZ4k0KP31 / c2fNOHNo + 7 jaBLoOuG6UNrElMqTZS + Qusomg0Vw6jkKeTb9YB / RYiMTG2bt1J6IuPJAis9AcOy1t9xVcCJtmIRnAA \
I16lF1my6i9Zi8CVIJPDDUWNGzmIwgy + TjXCUCR5xGfo0w8AB7YQUBYAcLhUOsJs0Ng3Y7mCUOIO2JXt4DljnOKIP / VPwzJGf479LzgrTIM4cpeY93R4IFqs2S2daBd0AJolnzZMJLW / Ep5wYJ / oIYfkKXO6dz5k \
fYXl5iTzHIw54GO4bDhmAqJIKaFRwmQ / Ol4kNfuNBvJeXH7 / rYRlXx / / pqETt49Z8E0HocOUQlQ2YYYooymfgqCb7RyxSWr7bT / 6 TBWbwxv2g / fnjXArvD6abBJiJ3wBE4XQxuSoOYNQY08FI01g1AuIzJks9pi8 \
FYU9 / 1 iybACr7 / Maseb5ijkAqQ2Ch5crgEZe3WHUAVf4X1qm9hyTyz2 + wLw4QiBxTHWsicdMlTdG5i7or8cSQMpIGIl0ISXzTTbEWDXoZnua3e1rhVX4vvMejUVX4wWRd97Fia1b2m5L5n9ot8y61EMSCz7qbXFa \
8 esykbAP6VwsYX9TxDXIpIzMooAQTxDWiGrevwVx2NU7XHqNnZMDMWY4QlXzQSvJWqpCJMpWAEbBmYEQ05KVkWtmIMJC8KY4YkGZZHYZnMpFNyvq1cHVlU3yplueGKCYjpjKODk57wVb + kBKJ2pmOTbI50S + 9 dt7 \
u8Geye52YivMnszwdoIvcDJ8M4nvtFUWw9uwUzGeLwJJNenz5gW0 + 2 LWuiqI1Pl3mKRWe33G5Aor5MmjzwmiCLvCsHW1tszu54kaQRiRxjkl3f3RayYvxi + kS58ytyqiNdO50XkIW / bt29W7H0EEL8BGPwNSpVAM \
xcZd0OABfPMMZ0Fga1BRVSgSBm + YK2qBaCFmVRWvyRH9lWuWEqotUyYgEISPPnP0HOo59xj9yIvmSaWQyrWU2w753c9bymarPzavSYj0d5hUCYb + + uSw8QXfQtSr2ZCdHUnNllxNtbIrnkSJhJV0sEIgwj5NHLJN \
U / q37GXEJBC5SDKN88BqMoJ / JdkjBlfxJeDSqIdVHCpm5 / yH14yXUnnVoTZoIpG7vfKnEds9JxkHH6U0ppE1GX78PMD9up885BzsSxWGMObZDg3gYmeIfGGRAnOcXmk + L0cZrSSnglKSmH / UBd9ZuyP / 9 w7UCDV1 \
dFeK8M2y0iAh4QfOxheci5OsCXhVi5E2c0UBp5mrtVMehEaRZ1Bz4v9Ns4Se / GnSce37b9 / ever1WzImKoLXKfnx7SeA4ZP54jnEn70B6xRnZ / dx8f7tB7j4YL54CL5 + + bDXdCmzZ6PZ87edAZD24ayB + I / ER4Rv \
CwTSWJL / iGm1MjzHS6D1EmjpWsGyA + mUQkac6aIwAXcBG94seMI2Nyu8pn35MZhsrQbS / ElzGYyVhecV8LHRLRjDTv7JyWHOek4YN21dtl5TfXV + RIDbkTxYa7EkH2oPDd / 6 QoBujT3RUlZt2bbIbDKh5SQkbkga \
wo9ei24 + n17HRUlLqg7oiBmljnOWfiNummIrkS9Ey9w9QYlEJynYmqLjRduck44CpIO / kd4ASEuDCX / hfshkO3UU1CM5z + 7 LsdDesNl6eozMOE + aUaOG6JpJlKIEUZcIx / ibV79mD9Ssm9RcSARwpcYoRm / bU8jk \
3 DmfD4GbxuNuPBROXAwvpBQmtrRsJkyGUrgZNl9iKhVZcr0YrM / Hp0q / oRaQwNfED + mn9ttQRUIphZ18zSoprTJuFney + 2 xfQGabXejwJRdkLVxBIKjOmlI3 + rJ6Y4fpb73Y2Khi16tLKMGttycSLpovBwjVmbbK \
tL9h + TuRfaJnjLtipj1wIR2NykkJqhEArQ7sRT7gkb / 674 + g8Nec + nmn9mF7zHh43YjJiRjHZ7c9XDVlK9i2TRekAjhwIQDkopuP1pr / Va7E8 + WJV7qhxvisCzTUpmg0qFM3fZbqT8fXyuTotRNxzVUp7xJmyLL3 \
/ O8gsJgU4SUUZz9A4g5YT + YXqiThk3bN5CZQWeE3jhKi80TyjkoykuTud9KnJPKl48bSNEICmJzIltJJgl08jGWSiefWARFvLeaq8tO9fWVPlCm1cFIpJXrBxqkp + Yb / NKJsRNLqRFRSoT1cWN1cmmFN3m / cRNi / \
kv1TdUyMjZVstoAYqqJJPvnOWbCkeis / u7hIV2JjGN3YsdQmDZ7CKF6ofiiqk84sJCaCJ9kRsaaR7nXlEd3zBwjOpzjTKYhsjI78eHtczSvU5Q5czb0XKdMtVdYPVWeMLF8peEd0wbWo / k0uwBityGLrhjqcAi8j \
6 QUZsQ / 5 trNc8bMEz7ZsBC / xTPEpIR11Uz6YMt97OVDGxV7L3U56DNRfyPb3GEym7NrERUwWuCXsQ23PHWqDo89TUVD1TNgtkgbtMwiaV4iIaXfJts0U5 / kqoFqQ3YzvwSqZ3FYnmq3EUWgNWZu6 / vd0cNiuKk8D \
6 vyKEtIuDwkuM + rKVzmnuGDdT5h6ys / ttDsEARRNTpi9Fkdkpn3Sp4SxhuFZP87wtUqQF127spQq4ApZU6Zes7LruJPQ6qMWy / AwyXgiXWGBGDXtWnQPV4NYIS08TmqoRN24wVAT9pzVrOpwlP5YqktuRX8BIEfo \
KblqxQ2 + plppmVANeajOxd8L8hppEt4s2Ks / AKVqwaCsJQUtBx9vMZNS3ZKNXnd7 + IWUYWb1qRv9BD7cuhMC7kcJEO6M92u0Q7fttsJ80DBI2t8fMaFTjARCYKwyacl6LFWXAJuWFPL5HUlO0ru1mx13s50 / lWxH \
o6n3p0JeVZKzgTjl07sLvruu27uVm2qxrC4hXXbKeZAAuJwL3qU + GomltyKP7cpMisWmTS5OxLOsJPWZdIJr6UBWyuTJhJ + jc8CbyggJLMGEimGcq82IbzbcxV19NvdR6EJKZgQKCMDO8XLai8dxh20Sp6le4p4P \
LFPVnI637fgzPRX5CZCOhyt4o6Qn + izHGSEirxzecKaUN5pb5uLqoSwfsd9W + ozRSnOGnhMPbp1Rz1y6v1xGIHdv1qLCK2nBad8O7wUQ3xKp1 / 2 UePCxryPpyldEWF / rPVKbEcmVamQqIsStmnzouUlQ91Yz + owz \
R85RcfaxL71 / I05fXgGNNY + 6 nNUpBVLGo5F + HR + iU8qRB / udyFYecrU9CCLCsP9CsiD6j / jd9uXhb + WaItEw8J + kAdaPurza8xuuOKP32NlUwhzVmd6Xgsh4Mu0wWWf8CHNzXr6GUXs9T6fKpJIUgV6YmN0QyoMD \
XnTvBPiBH + 8 gs / U / 8 VsKWjH + m7TERqpdDxqN + G / adXD4qAVLxLdLCqClkdfxLgJLFMU6dMr4epqMcBi / 5 GxuQ5R60b17wyccUZ71jgqCXeZ9wmCE4 + G9ii6YG + J0Kw0l1tOSgvyP3YNO1R8HEP + YELqQ1Nj8ix4d \
ctpOCye6cJ52TQw12Cbu8OYpOe / 6 o09RfuP6gswk9BbyYB0aX5 + hiuDe4 + GIiutvtgtgzBMJpaGqqnvdkuRtT79qILORBl9N1l6Ogsvfv / rOidQ2pJ5nwmmWkPyeYVzK + 0 UK65IL7kMhEtA + hugBadZ1 + W4Ax0l3 \
ktwcbTv + PVTM / ubsny19DN77xBDiEa4isVZu9XWMD / pYVVGTslNv6ZLJazPNETf / WrxRRMW7WmHBBTcJucMfskl9 / BB3O5jkVJ7yN5yMXZzeQ8vwjvQLReScHp3sTqNTeXWEIRRQOufEQZNXbeNY / QyEbJH836zI \
HcSG6Bmd / lByDmzph2JEhHBK + So5MLWBo2cIBpQHUPGkjzgSKftN760HEYoeZ1R4H0Gfd9MbIEqliEl1Kv12PvnfABjpiDh1x4QTDQofNZRKEXHAyidiq7u9JW6sOyVJHUl5rWWm3mm2ZSg + FFDXXG1zchFvdLlc \
3 kOKJG0jtmskBhVbpRimX + ggufbWXPyQtyrTxwfolqeoZFL0y9NZg355enYCl0 / vH6CiTh / gMjrm0cu61zEnKjCHf96h1yt / ebcsLvCSpTVZFlubxyZcqRfLi0 / t4GAQ5WGwKpaFvo0JXAWHOpTh / irGprEz8ep / \
TB3fMw == \
2016-10-06 13:21:30 +02:00
""" )))
2017-02-06 14:17:11 +01:00
2017-05-06 19:29:12 +02:00
def _main ( ) :
2016-10-06 13:21:30 +02:00
try :
main ( )
except FatalError as e :
2017-02-06 14:17:11 +01:00
print ( ' \n A fatal error occurred: %s ' % e )
2016-10-06 13:21:30 +02:00
sys . exit ( 2 )
2017-05-06 19:29:12 +02:00
if __name__ == ' __main__ ' :
_main ( )