2016-10-06 13:21:30 +02:00
#!/usr/bin/env python
#
# ESP8266 & ESP32 ROM Bootloader Utility
# Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems (Shanghai) PTE 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
2016-10-06 13:21:30 +02:00
import os
2018-04-07 08:45:18 +02:00
import shlex
2016-10-06 13:21:30 +02:00
import struct
import sys
import time
import zlib
2018-09-21 08:39:36 +02:00
import string
import serial . tools . list_ports as list_ports
2016-10-06 13:21:30 +02:00
2018-04-07 08:45:18 +02:00
import serial
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
__version__ = " 2.5.0 "
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
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
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
DETECTED_FLASH_SIZES = { 0x12 : ' 256KB ' , 0x13 : ' 512KB ' , 0x14 : ' 1MB ' ,
0x15 : ' 2MB ' , 0x16 : ' 4MB ' , 0x17 : ' 8MB ' , 0x18 : ' 16MB ' }
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
functionality between the ESP8266 & ESP32 ROM loaders , and the
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 ) :
""" Attribute for a function only supported by software stubs or ESP32 ROM """
return check_supported_function ( func , lambda o : o . IS_STUB or o . CHIP_NAME == " ESP32 " )
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
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
ESP_CHANGE_BAUDRATE = 0x0F
ESP_FLASH_DEFL_BEGIN = 0x10
ESP_FLASH_DEFL_DATA = 0x11
ESP_FLASH_DEFL_END = 0x12
ESP_SPI_FLASH_MD5 = 0x13
# 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
# 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
UART_DATA_REG_ADDR = 0x60000078
# 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
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 ( ) .
"""
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.
self . _port . write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT
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
2018-04-07 08:45:18 +02:00
def detect_chip ( port = DEFAULT_PORT , baud = ESP_ROM_BAUD , connect_mode = ' default_reset ' , trace_enabled = False ) :
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 )
2017-01-05 12:54:40 +01:00
detect_port . connect ( connect_mode )
2017-02-06 14:17:11 +01:00
print ( ' Detecting chip type... ' , end = ' ' )
sys . stdout . flush ( )
2016-10-06 13:21:30 +02:00
date_reg = detect_port . read_reg ( ESPLoader . UART_DATA_REG_ADDR )
for cls in [ ESP8266ROM , ESP32ROM ] :
if date_reg == cls . DATE_REG_VALUE :
# don't connect a second time
2018-04-07 08:45:18 +02:00
inst = cls ( detect_port . _port , baud , trace_enabled = trace_enabled )
2017-02-06 14:17:11 +01:00
print ( ' %s ' % inst . CHIP_NAME )
2016-10-06 13:21:30 +02:00
return inst
2017-02-06 14:17:11 +01:00
print ( ' ' )
2016-10-06 13:21:30 +02:00
raise FatalError ( " Unexpected UART datecode value 0x %08x . Failed to autodetect chip type. " % date_reg )
""" 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 ' \
+ ( packet . replace ( b ' \xdb ' , b ' \xdb \xdd ' ) . replace ( b ' \xc0 ' , b ' \xdb \xdc ' ) ) \
+ 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 : ]
if op is None or op_ret == op :
return val , data
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 ) :
2018-04-07 08:45:18 +02:00
self . command ( self . ESP_SYNC , b ' \x07 \x07 \x12 \x20 ' + 32 * b ' \x55 ' ,
timeout = SYNC_TIMEOUT )
2017-02-06 14:17:11 +01:00
for i in range ( 7 ) :
2016-10-06 13:21:30 +02:00
self . command ( )
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 )
2017-03-10 14:40:14 +01:00
def _connect_attempt ( self , mode = ' default_reset ' , esp32r0_delay = 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
2018-09-21 08:39:36 +02:00
# 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
2017-03-10 14:40:14 +01:00
# issue reset-to-bootloader:
# RTS = either CH_PD/EN or nRESET (both active low = chip in reset
# DTR = GPIO0 (active low = boot to flasher)
#
# DTR & RTS are active low signals,
# ie True = pin @ 0V, False = pin @ VCC.
if mode != ' no_reset ' :
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
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
2017-01-05 12:54:40 +01:00
def connect ( self , mode = ' default_reset ' ) :
2016-10-06 13:21:30 +02:00
""" Try connecting repeatedly until successful, or giving up """
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
2017-02-06 14:17:11 +01:00
try :
2018-09-21 08:39:36 +02:00
for _ in range ( 7 ) :
2017-03-10 14:40:14 +01:00
last_error = self . _connect_attempt ( mode = mode , esp32r0_delay = False )
if last_error is None :
return
last_error = self . _connect_attempt ( mode = mode , esp32r0_delay = True )
if last_error is None :
return
2017-02-06 14:17:11 +01:00
finally :
print ( ' ' ) # end 'Connecting...' line
raise FatalError ( ' Failed to connect to %s : %s ' % ( self . CHIP_NAME , last_error ) )
2016-10-06 13:21:30 +02:00
""" Read memory address in target """
def read_reg ( self , addr ) :
# 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 (!)
val , data = self . command ( self . ESP_READ_REG , struct . pack ( ' <I ' , addr ) )
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
""" Write to memory address in target """
def write_reg ( self , addr , value , mask = 0xFFFFFFFF , delay_us = 0 ) :
return self . check_command ( " write target memory " , self . ESP_WRITE_REG ,
struct . pack ( ' <IIII ' , addr , value , mask , delay_us ) )
""" 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 :
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 " +
" 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 .
"""
2016-10-06 13:21:30 +02:00
def flash_begin ( self , size , offset ) :
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
2016-10-06 13:21:30 +02:00
self . check_command ( " enter Flash download mode " , self . ESP_FLASH_BEGIN ,
2018-04-07 08:45:18 +02:00
struct . pack ( ' <IIII ' , erase_size , num_blocks , self . FLASH_WRITE_SIZE , offset ) ,
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
""" 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
def parse_flash_size_arg ( self , arg ) :
try :
return self . FLASH_SIZES [ arg ]
except KeyError :
raise FatalError ( " Flash size ' %s ' is not supported by this chip type. Supported sizes: %s "
% ( arg , " , " . join ( self . FLASH_SIZES . keys ( ) ) ) )
def run_stub ( self , stub = None ) :
if stub is None :
if self . IS_STUB :
raise FatalError ( " Not possible for a stub to load another stub (memory likely to overlap.) " )
stub = self . STUB_CODE
# 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 ) )
2016-10-06 13:21:30 +02:00
self . check_command ( " enter compressed flash mode " , self . ESP_FLASH_DEFL_BEGIN ,
2018-04-07 08:45:18 +02:00
struct . pack ( ' <IIII ' , write_size , num_blocks , self . FLASH_WRITE_SIZE , offset ) ,
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
@stub_function_only
def read_flash ( self , offset , length , progress_fn = None ) :
# 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
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 ' )
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 )
# SPI registers, base address differs ESP32 vs 8266
base = self . SPI_REG_BASE
SPI_CMD_REG = base + 0x00
SPI_USR_REG = base + 0x1C
SPI_USR1_REG = base + 0x20
SPI_USR2_REG = base + 0x24
SPI_W0_REG = base + self . SPI_W0_OFFS
# following two registers are ESP32 only
if self . SPI_HAS_MOSI_DLEN_REG :
# ESP32 has a more sophisticated wayto set up "user" commands
def set_data_lengths ( mosi_bits , miso_bits ) :
SPI_MOSI_DLEN_REG = base + 0x28
SPI_MISO_DLEN_REG = base + 0x2C
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
SPI_USR2_DLEN_SHIFT = 28
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 ,
( 7 << SPI_USR2_DLEN_SHIFT ) | spiflash_command )
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 )
2017-01-05 12:54:40 +01:00
def hard_reset ( self ) :
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
DATE_REG_VALUE = 0x00062000
# 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
SPI_W0_OFFS = 0x40
SPI_HAS_MOSI_DLEN_REG = False
2016-10-06 13:21:30 +02:00
FLASH_SIZES = {
' 512KB ' : 0x00 ,
' 256KB ' : 0x10 ,
' 1MB ' : 0x20 ,
' 2MB ' : 0x30 ,
' 4MB ' : 0x40 ,
' 2MB-c1 ' : 0x50 ,
' 4MB-c1 ' : 0x60 ,
2017-08-01 07:51:04 +02:00
' 8MB ' : 0x80 ,
' 16MB ' : 0x90 ,
}
2016-10-06 13:21:30 +02:00
2017-08-01 07:51:04 +02:00
BOOTLOADER_FLASH_OFFSET = 0
2018-04-07 08:45:18 +02:00
def get_efuses ( self ) :
# Return the 128 bits of ESP8266 efuse as a single Python integer
return ( self . read_reg ( 0x3ff0005c ) << 96 |
self . read_reg ( 0x3ff00058 ) << 64 |
self . read_reg ( 0x3ff00054 ) << 32 |
self . read_reg ( 0x3ff00050 ) )
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
return " ESP8285 " if is_8285 else " ESP8266EX "
def get_chip_features ( self ) :
2018-09-21 08:39:36 +02:00
features = [ " WiFi " ]
2018-04-07 08:45:18 +02:00
if self . get_chip_description ( ) == " ESP8285 " :
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 ) :
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 "
IS_STUB = False
DATE_REG_VALUE = 0x15122500
IROM_MAP_START = 0x400d0000
IROM_MAP_END = 0x40400000
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
2016-10-28 00:53:02 +02:00
SPI_REG_BASE = 0x60002000
EFUSE_REG_BASE = 0x6001a000
2016-10-06 13:21:30 +02:00
2016-10-28 00:53:02 +02:00
SPI_W0_OFFS = 0x80
SPI_HAS_MOSI_DLEN_REG = True
2016-10-06 13:21:30 +02:00
FLASH_SIZES = {
' 1MB ' : 0x00 ,
' 2MB ' : 0x10 ,
' 4MB ' : 0x20 ,
' 8MB ' : 0x30 ,
' 16MB ' : 0x40
}
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 " ]
2017-08-01 07:51:04 +02:00
def get_chip_description ( self ) :
2018-04-07 08:45:18 +02:00
word3 = self . read_efuse ( 3 )
2018-09-21 08:39:36 +02:00
chip_ver_rev1 = ( word3 >> 15 ) & 0x1
2018-04-07 08:45:18 +02:00
pkg_version = ( word3 >> 9 ) & 0x07
2017-08-01 07:51:04 +02:00
chip_name = {
0 : " ESP32D0WDQ6 " ,
1 : " ESP32D0WDQ5 " ,
2 : " ESP32D2WDQ5 " ,
2018-04-07 08:45:18 +02:00
5 : " ESP32-PICO-D4 " ,
2017-08-01 07:51:04 +02:00
} . get ( pkg_version , " unknown ESP32 " )
2018-09-21 08:39:36 +02:00
return " %s (revision %d ) " % ( chip_name , chip_ver_rev1 )
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 " ]
2018-04-07 08:45:18 +02:00
pkg_version = ( word3 >> 9 ) & 0x07
2018-09-21 08:39:36 +02:00
if pkg_version in [ 2 , 4 , 5 ] :
2018-04-07 08:45:18 +02:00
features + = [ " Embedded Flash " ]
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 " ]
return features
2016-10-06 13:21:30 +02:00
def read_efuse ( self , n ) :
""" Read the nth word of the ESP3x EFUSE region. """
2016-10-28 00:53:02 +02:00
return self . read_reg ( self . EFUSE_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 )
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 ) :
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
class ESPBOOTLOADER ( object ) :
""" These are constants related to software ESP bootloader, working with ' v2 ' image files """
# 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 ) :
""" Load a firmware image. Can be for ESP8266 or ESP32. ESP8266 images will be examined to determine if they are
2018-09-21 08:39:36 +02:00
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
"""
with open ( filename , ' rb ' ) as f :
2018-09-21 08:39:36 +02:00
if chip . lower ( ) == ' esp32 ' :
2016-10-06 13:21:30 +02:00
return ESP32FirmwareImage ( f )
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
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
""" Base class with common firmware image functions """
def __init__ ( self ) :
self . segments = [ ]
self . entrypoint = 0
def load_common_header ( self , load_file , expected_magic ) :
( magic , segments , self . flash_mode , self . flash_size_freq , self . entrypoint ) = struct . unpack ( ' <BBBBI ' , load_file . read ( 8 ) )
if magic != expected_magic or segments > 16 :
raise FatalError ( ' Invalid firmware image magic= %d segments= %d ' % ( magic , segments ) )
return segments
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 ) )
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 """
f . write ( struct . pack ( ' <II ' , segment . addr , len ( segment . data ) ) )
f . write ( segment . data )
if checksum is not None :
return ESPLoader . checksum ( segment . data , checksum )
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 ) :
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
def get_non_irom_segments ( self ) :
irom_segment = self . get_irom_segment ( )
return [ s for s in self . segments if s != irom_segment ]
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 )
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 )
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 )
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
EXTENDED_HEADER_STRUCT_FMT = " B " * 16
2016-10-06 13:21:30 +02:00
def __init__ ( self , load_file = None ) :
super ( ESP32FirmwareImage , self ) . __init__ ( )
2018-09-21 08:39:36 +02:00
self . secure_pad = False
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
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?
2016-10-06 13:21:30 +02:00
def is_flash_addr ( self , addr ) :
return ( ESP32ROM . IROM_MAP_START < = addr < ESP32ROM . IROM_MAP_END ) \
or ( ESP32ROM . DROM_MAP_START < = addr < ESP32ROM . DROM_MAP_END )
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 ) ]
IROM_ALIGN = 65536
# 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
# use case then let us know (we can merge segments here, but as a rule you probably
# want to merge them in your linker script.)
if len ( flash_segments ) > 0 :
last_addr = flash_segments [ 0 ] . addr
for segment in flash_segments [ 1 : ] :
if segment . addr / / IROM_ALIGN == last_addr / / IROM_ALIGN :
raise FatalError ( ( " Segment loaded at 0x %08x lands in same 64KB flash mapping as segment loaded at 0x %08x . " +
" 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
align_past = ( segment . addr % IROM_ALIGN ) - self . SEG_HEADER_LEN
pad_len = ( IROM_ALIGN - ( f . tell ( ) % IROM_ALIGN ) ) + align_past
if pad_len == 0 or pad_len == IROM_ALIGN :
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 :
pad_len + = IROM_ALIGN
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
2016-10-06 13:21:30 +02:00
assert ( f . tell ( ) + 8 ) % IROM_ALIGN == segment . addr % IROM_ALIGN
2017-08-01 07:51:04 +02:00
checksum = self . save_segment ( f , segment , checksum )
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 " )
align_past = ( f . tell ( ) + self . SEG_HEADER_LEN ) % IROM_ALIGN
# 16 byte aligned checksum (force the alignment to simplify calculations)
checksum_space = 16
# 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
pad_len = ( IROM_ALIGN - align_past - checksum_space - space_after_checksum ) % IROM_ALIGN
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 :
assert ( ( image_length + space_after_checksum ) % IROM_ALIGN ) == 0
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 ( ) )
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 ] )
if fields [ 15 ] in [ 0 , 1 ] :
self . append_digest = ( fields [ 15 ] == 1 )
else :
raise RuntimeError ( " Invalid value for append_digest field (0x %02x ). Should be 0 or 1. " , fields [ 15 ] )
# remaining fields in the middle should all be zero
if any ( f for f in fields [ 4 : 15 ] if f != 0 ) :
print ( " Warning: some reserved header fields have non-zero values. This image may be from a newer esptool.py? " )
def save_extended_header ( self , save_file ) :
def join_byte ( ln , hn ) :
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 ) ,
join_byte ( self . hd_drv , self . wp_drv ) ]
fields + = [ 0 ] * 11
fields + = [ append_digest ]
packed = struct . pack ( self . EXTENDED_HEADER_STRUCT_FMT , * fields )
save_file . write ( packed )
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
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 :
( ident , _type , machine , _version ,
self . entrypoint , _phoff , shoff , _flags ,
2017-08-01 07:51:04 +02:00
_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 )
if machine != 0x5e :
raise FatalError ( " %s does not appear to be an Xtensa ELF file. e_machine= %04x " % ( self . name , machine ) )
2017-08-01 07:51:04 +02:00
if shentsize != self . LEN_SEC_HEADER :
raise FatalError ( " %s has unexpected section header entry size 0x %x (not 0x28) " % ( self . name , shentsize , self . LEN_SEC_HEADER ) )
if shnum == 0 :
raise FatalError ( " %s has 0 section headers " % ( self . name ) )
self . _read_sections ( f , shoff , shnum , 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 ) :
name_offs , sec_type , _flags , lma , sec_offs , size = struct . unpack_from ( " <LLLLLL " , section_header [ offs : ] )
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 )
2017-08-01 07:51:04 +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
def read_data ( offs , size ) :
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
if lma != 0 ]
self . sections = prog_sections
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.
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 ) :
2017-02-06 14:17:11 +01:00
f = open ( args . filename , ' wb ' )
for i in range ( args . size / / 4 ) :
2016-10-06 13:21:30 +02:00
d = esp . read_reg ( args . address + ( i * 4 ) )
2017-02-06 14:17:11 +01:00
f . write ( struct . pack ( b ' <I ' , d ) )
2016-10-06 13:21:30 +02:00
if f . tell ( ) % 1024 == 0 :
2017-02-06 14:17:11 +01:00
print ( ' \r %d bytes read... ( %d %% ) ' % ( f . tell ( ) ,
f . tell ( ) * 100 / / args . size ) ,
end = ' ' )
2016-10-06 13:21:30 +02:00
sys . stdout . flush ( )
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 ' :
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 ] )
if address != esp . BOOTLOADER_FLASH_OFFSET or magic != esp . ESP_IMAGE_MAGIC :
return image # not flashing a bootloader, so don't modify this
2017-02-06 14:17:11 +01:00
2017-08-01 07:51:04 +02:00
if args . flash_mode != ' keep ' :
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 ' :
flash_freq = { ' 40m ' : 0 , ' 26m ' : 1 , ' 20m ' : 2 , ' 80m ' : 0xf } [ args . flash_freq ]
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
# verify file sizes fit in flash
flash_end = flash_size_bytes ( args . flash_size )
for address , argfile in args . addr_filename :
argfile . seek ( 0 , 2 ) # seek to end
if address + argfile . tell ( ) > flash_end :
2016-10-28 00:53:02 +02:00
raise FatalError ( ( " File %s (length %d ) at offset %d will not fit in %d bytes of flash. " +
" Use --flash-size argument, or change flashing address. " )
2016-10-06 13:21:30 +02:00
% ( argfile . name , argfile . tell ( ) , address , flash_end ) )
argfile . seek ( 0 )
for address , argfile in args . addr_filename :
2016-12-30 00:28:30 +01:00
if args . no_stub :
2017-02-06 14:17:11 +01:00
print ( ' Erasing flash... ' )
2017-03-10 14:40:14 +01:00
image = pad_to ( argfile . read ( ) , 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 )
2016-10-06 13:21:30 +02:00
if args . compress :
2016-10-28 00:53:02 +02:00
uncimage = image
2016-10-06 13:21:30 +02:00
image = zlib . compress ( uncimage , 9 )
2017-08-01 07:51:04 +02:00
ratio = uncsize / len ( image )
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 :
2017-08-01 07:51:04 +02:00
ratio = 1.0
2016-12-30 00:28:30 +01:00
blocks = esp . flash_begin ( uncsize , address )
2016-10-06 13:21:30 +02:00
argfile . seek ( 0 ) # in case we need it again
seq = 0
written = 0
t = time . time ( )
while len ( image ) > 0 :
2017-02-06 14:17:11 +01:00
print ( ' \r Writing at 0x %08x ... ( %d %% ) ' % ( address + seq * esp . FLASH_WRITE_SIZE , 100 * ( seq + 1 ) / / blocks ) , end = ' ' )
2016-10-06 13:21:30 +02:00
sys . stdout . flush ( )
block = image [ 0 : esp . FLASH_WRITE_SIZE ]
if args . compress :
2018-04-07 08:45:18 +02:00
esp . flash_defl_block ( block , seq , timeout = DEFAULT_TIMEOUT * ratio )
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 ) )
2016-10-06 13:21:30 +02:00
esp . flash_block ( block , seq )
image = image [ esp . FLASH_WRITE_SIZE : ]
seq + = 1
written + = len ( block )
t = time . time ( ) - t
2016-10-28 00:53:02 +02:00
speed_msg = " "
if args . compress :
if t > 0.0 :
speed_msg = " (effective %.1f kbit/s) " % ( uncsize / t * 8 / 1000 )
2017-02-06 14:17:11 +01:00
print ( ' \r Wrote %d bytes ( %d compressed) at 0x %08x in %.1f seconds %s ... ' % ( uncsize , written , address , t , speed_msg ) )
2016-10-28 00:53:02 +02:00
else :
if t > 0.0 :
speed_msg = " ( %.1f kbit/s) " % ( written / t * 8 / 1000 )
2017-02-06 14:17:11 +01:00
print ( ' \r Wrote %d bytes at 0x %08x in %.1f seconds %s ... ' % ( written , address , t , speed_msg ) )
2016-12-30 00:28:30 +01:00
try :
res = esp . flash_md5sum ( address , uncsize )
if res != calcmd5 :
2017-02-06 14:17:11 +01:00
print ( ' File md5: %s ' % calcmd5 )
print ( ' Flash md5: %s ' % res )
print ( ' MD5 of 0xFF is %s ' % ( hashlib . md5 ( b ' \xFF ' * uncsize ) . hexdigest ( ) ) )
2016-12-30 00:28:30 +01:00
raise FatalError ( " MD5 of file does not match data in flash! " )
else :
2017-02-06 14:17:11 +01:00
print ( ' Hash of data verified. ' )
2016-12-30 00:28:30 +01:00
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 )
if args . compress :
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.) ' )
2017-08-01 07:51:04 +02:00
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 ) )
2016-10-06 13:21:30 +02:00
print
idx = 0
for seg in image . segments :
idx + = 1
2017-02-06 14:17:11 +01:00
print ( ' Segment %d : %r ' % ( idx , seg ) )
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 ) :
2017-02-06 14:17:11 +01:00
data = open ( seg , ' rb ' ) . read ( )
2016-10-06 13:21:30 +02:00
image . segments . append ( ImageSegment ( addr , data ) )
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 ( )
2018-09-21 08:39:36 +02:00
image . secure_pad = args . secure_pad
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
image . segments = e . sections # ELFSection is a subclass of ImageSegment
image . flash_mode = { ' qio ' : 0 , ' qout ' : 1 , ' dio ' : 2 , ' dout ' : 3 } [ args . flash_mode ]
2016-10-28 00:53:02 +02:00
image . flash_size_freq = image . ROM_LOADER . FLASH_SIZES [ args . flash_size ]
2016-10-06 13:21:30 +02:00
image . flash_size_freq + = { ' 40m ' : 0 , ' 26m ' : 1 , ' 20m ' : 2 , ' 80m ' : 0xf } [ args . flash_freq ]
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
2017-02-06 14:17:11 +01:00
print ( ' \r Read %d bytes at 0x %x in %.1f seconds ( %.1f kbit/s)... '
% ( len ( data ) , args . address , t , len ( data ) / t * 8 / 1000 ) )
open ( args . filename , ' wb ' ) . 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
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
#
def main ( ) :
parser = argparse . ArgumentParser ( description = ' esptool.py v %s - ESP8266 ROM Bootloader Utility ' % __version__ , prog = ' esptool ' )
parser . add_argument ( ' --chip ' , ' -c ' ,
help = ' Target chip type ' ,
2017-02-06 14:17:11 +01:00
choices = [ ' auto ' , ' esp8266 ' , ' esp32 ' ] ,
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 ' ,
2018-09-21 08:39:36 +02:00
choices = [ ' default_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 ' ,
choices = [ ' hard_reset ' , ' soft_reset ' , ' no_reset ' ] ,
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 = ' ? ' )
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 ) :
parent . add_argument ( ' --spi-connection ' , ' -sc ' , help = ' ESP32-only argument. Override default SPI Flash connection. ' +
' 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 )
parser_write_mem . add_argument ( ' mask ' , help = ' Mask of bits to write ' , type = arg_auto_int )
2017-08-01 07:51:04 +02:00
def add_spi_flash_subparsers ( parent , is_elf2image ) :
2016-10-06 13:21:30 +02:00
""" Add common parser arguments for SPI flash properties """
2017-08-01 07:51:04 +02:00
extra_keep_args = [ ] if is_elf2image else [ ' keep ' ]
auto_detect = not is_elf2image
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 ' ] ,
default = os . environ . get ( ' ESPTOOL_FF ' , ' 40m ' if is_elf2image else ' keep ' ) )
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 ' ] ,
default = os . environ . get ( ' ESPTOOL_FM ' , ' qio ' if is_elf2image else ' keep ' ) )
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) '
2017-08-01 07:51:04 +02:00
' plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1) ' ,
2017-01-05 12:54:40 +01:00
action = FlashSizeAction , auto_detect = auto_detect ,
default = os . environ . get ( ' ESPTOOL_FS ' , ' detect ' if auto_detect else ' 1MB ' ) )
2017-05-06 19:29:12 +02:00
add_spi_connection_arg ( parent )
2016-10-06 13:21:30 +02:00
parser_write_flash = subparsers . add_parser (
' write_flash ' ,
help = ' Write a binary blob to flash ' )
parser_write_flash . add_argument ( ' addr_filename ' , metavar = ' <address> <filename> ' , help = ' Address followed by binary filename, separated by space ' ,
action = AddrFilenamePairAction )
2017-08-01 07:51:04 +02:00
add_spi_flash_subparsers ( parser_write_flash , is_elf2image = False )
2016-10-06 13:21:30 +02:00
parser_write_flash . add_argument ( ' --no-progress ' , ' -p ' , help = ' Suppress progress output ' , action = " store_true " )
2017-02-06 14:17:11 +01:00
parser_write_flash . add_argument ( ' --verify ' , help = ' Verify just-written data on flash ' +
' (mostly superfluous, data is read back during flashing) ' , action = ' store_true ' )
compress_args = parser_write_flash . add_mutually_exclusive_group ( required = False )
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 )
parser_elf2image . add_argument ( ' --version ' , ' -e ' , help = ' Output image version ' , choices = [ ' 1 ' , ' 2 ' ] , default = ' 1 ' )
2018-09-21 08:39:36 +02:00
parser_elf2image . add_argument ( ' --secure-pad ' , action = ' store_true ' , help = ' Pad image so once signed it will end on a 64KB boundary. For ESP32 images only. ' )
2016-12-30 00:28:30 +01:00
2017-08-01 07:51:04 +02:00
add_spi_flash_subparsers ( parser_elf2image , is_elf2image = True )
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 )
2016-10-28 00:53:02 +02:00
parser_read_status . add_argument ( ' --bytes ' , help = ' Number of bytes to read (1-3) ' , type = int , choices = [ 1 , 2 , 3 ] , default = 2 )
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 ' )
parser_write_status . add_argument ( ' --bytes ' , help = ' Number of status bytes to write (1-3) ' , type = int , choices = [ 1 , 2 , 3 ] , default = 2 )
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 ' )
2017-08-01 07:51:04 +02:00
add_spi_flash_subparsers ( parser_verify_flash , is_elf2image = False )
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 )
subparsers . add_parser (
' version ' , help = ' Print esptool version ' )
# 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
2017-01-05 12:54:40 +01:00
expand_file_arguments ( )
2016-10-06 13:21:30 +02:00
args = parser . parse_args ( )
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 )
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 :
ser_list = sorted ( ports . device for ports in list_ports . comports ( ) )
print ( " Found %d serial ports " % len ( ser_list ) )
else :
ser_list = [ args . port ]
esp = None
for each_port in reversed ( ser_list ) :
print ( " Serial port %s " % each_port )
try :
if args . chip == ' auto ' :
esp = ESPLoader . detect_chip ( each_port , initial_baud , args . before , args . trace )
else :
chip_class = {
' esp8266 ' : ESP8266ROM ,
' esp32 ' : ESP32ROM ,
} [ args . chip ]
esp = chip_class ( each_port , initial_baud , args . trace )
esp . connect ( args . before )
break
except FatalError as err :
if args . port is not None :
raise
print ( " %s failed to connect: %s " % ( each_port , err ) )
esp = None
if esp is None :
raise FatalError ( " All of the %d available serial ports could not connect to a Espressif device. " % len ( ser_list ) )
2016-10-06 13:21:30 +02:00
2017-08-01 07:51:04 +02:00
print ( " Chip is %s " % ( esp . get_chip_description ( ) ) )
2018-04-07 08:45:18 +02:00
print ( " Features: %s " % " , " . join ( esp . get_chip_features ( ) ) )
2018-09-21 08:39:36 +02:00
read_mac ( esp , args )
2016-10-06 13:21:30 +02:00
if not args . no_stub :
esp = esp . run_stub ( )
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 )
2016-10-06 13:21:30 +02:00
esp . flash_set_parameters ( flash_size_bytes ( args . flash_size ) )
operation_func ( esp , args )
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 ' :
2018-04-07 08:45:18 +02:00
print ( ' Hard resetting via RTS pin... ' )
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 )
else :
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
2018-09-21 08:39:36 +02:00
esp . _port . close ( )
2016-10-06 13:21:30 +02:00
else :
operation_func ( args )
2017-01-05 12:54:40 +01:00
def expand_file_arguments ( ) :
""" 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
for arg in sys . argv :
if arg . startswith ( " @ " ) :
expanded = True
with open ( arg [ 1 : ] , " r " ) as f :
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 : ] ) ) )
2017-01-05 12:54:40 +01:00
sys . argv = new_args
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 '
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 :
values = tuple ( int ( v , 0 ) for v in values )
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...
clk , q , d , hd , cs = values
value = ( hd << 24 ) | ( cs << 18 ) | ( d << 12 ) | ( q << 6 ) | clk
else :
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 = [ ]
for i in range ( 0 , len ( values ) , 2 ) :
try :
address = int ( values [ i ] , 0 )
2018-09-21 08:39:36 +02:00
except ValueError :
2016-10-06 13:21:30 +02:00
raise argparse . ArgumentError ( self , ' Address " %s " must be a number ' % values [ i ] )
try :
argfile = open ( values [ i + 1 ] , ' rb ' )
except IOError as e :
raise argparse . ArgumentError ( self , e )
except IndexError :
raise argparse . ArgumentError ( self , ' Must be pairs of an address and the binary filename to write there ' )
pairs . append ( ( address , argfile ) )
2017-03-10 14:40:14 +01:00
# Sort the addresses and check for overlapping
end = 0
for address , argfile in sorted ( pairs ) :
argfile . seek ( 0 , 2 ) # seek to end
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 """
2018-09-21 08:39:36 +02:00
eNrNPHt / 1 Da2X2XshJCE0Fq2x5bTsMxMwvAo3PLYpHQ3bWPLNpTbdpMhv03K0vvZr89Lkj0TAn3d + 0 dgZFvS0XmfoyP95 + Z5c3l + c3dU3Ty + LPXxpYqOL6No0v2jji / bFv7m9 + BR / 093 f21z96sH04ddv7T7q + DT \
u91bw43mLn2mvW5NN0NbwCwT + pJenAwmUKt / K + 8 bAs0DyPRnohkGULtOk5XLOb4szC1eRxnJr27am97AqQe1HZAh6WFi0JDhqh62eggabXmwdqjSNYL10gMQaGR7F9BovEZhEZ96b6CzqtzQZXS8GCBHWxCOz + Xn \
0 + 6 fxmuo2BvCeGBUkddQrV3EXve4YIAiH1QgVll70EUedFHvpaG57Dxq7KFI9XkgijzWw4bMXhmhUUd4rb1G6Rovsddk5zH + F32B / 10 + sOzyiH9V6UP + Zcxn / Et1EzQxN2pd4K / X9lk3SC0zFiAHyNWTxxsCEg8Z \
EKiwqKLrWSpieuiiut8mCsu1EAlIC47L / e5pXM668eNyCvOV3XBtXN4l0WlyGs1YFMEUMTzs3lYJIxDQA3yd + QIHIMVfhjl8rXlWbbYD + H7 / HvWOVCB07SZRQjklD7d3YO4RDWkALfFM4JeFjGnSulwBKlKwZVRE \
yg4GVImjdGQf4NA78A + Plg5Hu + o5wIoImcqPZ90PxMyZ / GAUxRUDwoOZdhXIEWMABLLDwJqFURN4Knsr3QQx / AYnSfproqcqDqfAJiF / BECYdXw2nj3bj8twg1gGJlEmyUlcVTeLznz5lgESmS4GCQyBv6Jw1M6g \
36 Yj2iZ8EZbhXBiDidbB82yffxvGRpUNsSFMkwIbhzBRzDTX90g / opWxKw99mGgwpUbwY9QhVsescHNiYZUT8xSgpBTP1TQ0aGmuAEagLctlBjEZCC9znMMzCBeYgCIh2OAzjXooj5 + 1 AnDGkADg2du1Fj + efytP \
9 o9 / btfk08c8J / SpfQXW7mOvM / z9wpsucRAWuq8six5wUfaOIAMst2 + 9 IcTWZfwxqD3qNb90o1XeaPOf4HlHe81aVpkBIgqaa7NldYCrm8jH8KAD5GcauR3A + ZlDgs5dF + UDMJPnoffwG4Eq9uhaK / hCJTvdhOfy \
LKJn3Z8GaH8d8EKfrDq74b9L + u + 63 yBIYx9XqgYbIGJ2wrossxa0ZczF81Lenfnq6HyZBZvuy6YGR4QkWGc / WdO8M4VOZ8NORyT5qsQFXdaggXHxn9FX2jwZAQumRy / AHLKmjcEKaVZuGet / 54 mA1KFi / mFpuueE \
ziaiuQzO9Tn / yP9Ga4A50PgtD03LKK9aRrZyGUdsWhqg49eOBOCh1LGI9oZF + Eth9pYxLwSpWqFoLTT5YunVPr0y2X / 5 xN6QT + ZPUJ0 / DtY94 + ADEgvfeeI + v8vzwVgpT6fibpTuv + bjBpqLFvgbjQVIitL51Mcv \
DFf4w8UscJE3bkaugRNk9H30Dui / 58 QdaNyAb8giWfXJwxRgQZD5xTdo2rmbqSn3Z47y4H02zSSE4fLPYPznDN24Evdjxj9QVR2C5hclnJ8cojE6gIdPD0bwAfoek2QEcBkxK0bJmkcowB2Q81seXiQGcGjwcKOy \
5 PgcVIp9q49Yq + mY9RAix2Qjp600qRBii3VHYfSckWvmI8t9X + FXm8J3JLM91ouyLWG9QRzEvYELNvvTOPYnPaXyXjfLtBvevDjylEQVZMoR / JKoZkzeZ5fW / EtxAOfPWbwml6XNsx3oPqIIyYepsIxC / nzB + jRC \
OLe7RsFOuf4ypFY03v56RF8Z5BnQVu03BBp + kMw5HhxbJcur156Y1Q5LywglBSW + tECM / BGNQIePz9mJxqYZv / X8DHEj2oqVktNjnZUxpXXUZIoWXbLOSCUo / jOhN80cpTCQmOoaleYGMuc8xBhceAW4bi1nXy + e \
E2ajNBaDhrM8ZEsqbqDMpMQpytxwKBgmoVXgauvhigKSLHroPNmAidOMEvTvwPtORmsgE74njiFIb7w78PI2xqDwfjx8H5LbDbyGH6jhB2xWjB / + 4 fQ0cTKDf9OXx8fgp / 0 bRpkTu9Hnq / 3 NBJ3NZBQzOlCzINve \
+ yezTubSIlH0cR51iEqcFKlTmOUtZtCGXQtg2DQgn7htpg7hVRwyfyoORpqKOSoJQd / Ei + BWVd6g6Jjt8c2cVa9IXYwfxCVMM37C7nocjM8kZQAMFdw6 / ZKteHlAMTl47nX531W5hYPsTF5yQNoxT9mSOKvxHBy8 \
9 KIb0vLwzwQAuKqNWexQLw3UUePNH2G2cmNRruOo23svQLO + B3mED9LXoG1ArVaJx7swdtrBrRFgskeb9B7RETOr5i3zsq + FgZ0Kkhr4X8cFCEtBEV + Z393F0Hn9J + rS / dxgBwcsSYca8AzNWoSpq + DfjDHDBhQN \
+ sPqW8KZqSmjoDMXWJc5aeEGBJXSDcEpjQLrMm3B3mzGfxE / oMHmvg8Vu3RTr + 8 S / OgcNDD53g6D48c6OPKCVnbjaY4rM25lUfKNXZG46nYZmVvG94NlFLKMZJh / E / mIgn96fQz2Cb7hRwBBAgmv + DVbiugGiNKb \
cFZGz8u0Kj8HooEkMD8rio6WNASSfRZEz4O0CrBTkBDXkxg2pKRGLTGnMf9 + OkpFXPUIg66nf4cIuzqUIPzsKfMWZqIwzsGUwKvcESUaTyaQ5hFfJJuAICVRC5xk5jyUuHKOFMEdGHtnvyC0zylX0UcjE6TN7txF \
um6xXfNfWv5jrg1Hmyy0hhYXmSSANTZFJcxLw5iS5rNkjoWuoSyP53CJotDlcYmc / LueiCaHAL4BMQLBRZ4E5xMzJ2ebhM7OwK53S6nyUTCFJeUkfE0yJesIS2rro3ANTfM6qoZ6DzFw4wMYKCwGtthZMC + OwJyW \
3 Ckd5A4te / 8 J657Lolmdwzo4iTfh5QKN1Hug3IgwpSLnE6vsCTk2LXBmXeDq165c / UKWjgEEjmOOyAa1qadP7Jr1n0Jr0Dpo + 9 qAHU2wYxF6kW / C3RCYbEZGyhKaY8tGT2XlQzLtTT + e78GOgnrgmJoyO4stQyNU \
HHg5BR0wneopMUpbW133JnwUCpYIap7aebNTCpvb + k3yKAn34RuL7IA4i0ZWMrKRkQUPgloF8LTZvO8oYmpY0ZDgCpwXYRe1gJqkZFnbeRznX8TH57u + FyKJzzoKMSVSsJ3R7BQOkLjzdi6pa0lfAc9dJ3EDnttL \
jsi + wqr / cnmjzm3kRK7BvZZz9Dk2PB5zjOfFr1G0tz + nXGMUL + uVTqEUAD3QOloEEFpV7eKeAHjoXEGyQ0 + oUUf74DOcrKHncJMcLEh + ISFqL6eX0LPGrJhf4 / zazj8X7vNU3g44UOcSFr1zfv8Kuu2QBYX5Kn2F \
VCGtIBPaLIKtpwC6wSjoHfxzQQnZSEGCLb6gVDC1stugtcDjN4CDnEz4At2ugRWvIxJTZ8UXQSzWm + w22HCY0bw7PG05LYQypTmnAdDGnYwv5sNob7Ae3GVKOKGKDLLhYiWdBY6rwlcgRDvsoBeoEmbwLBbPc / yB \
RYFLvLQoSZjo7QtF4gyfuCV2K + 7 WDZjLZbdEz / aFuZIL4g + Mik18SvuEEXuiRTqTpGkVcqDY5qFsXMiuEtDZnA / D4tkD25XkTGsZYhzTnk6jdzlah + xHLuO12w8WB64zO + + YQsRgQHkpuyx4jyoyDK28faRFX / Jp \
MGMtesaYvp653rbppaTAb / VnJIEXBW + ImiT33hqnr / kFbp / gcOBrqcmcSQwIazAMjUYutbrCRXiAvAhpuvpRMtqA7rAzBUkdNBpVBupaBd85SLqYGEF / jpvSPR79dcigQqWOBftecyTpFWsV7vwmJww8uyMyMCb + \
f2Aagjla5XTIlB26o / Xt9ek + Ych5os5YWze1Q3xwG216iIkX3i9QJkHSgHBi3k6dfeFT5XQBTI1CPjm9BPu0AP / 3 JcQh6kDinc5gKZ9sixIrEtTzZd1yfHOK6oSWvX1800txRerJsAPBjFTCIRGo7a0ZZSJwywjY \
bcggnRyqjj02IUmK6hkLPXgqHYNwqrC8jZB / 5 myhz0woemOf6gD8Nu5bJb4WQimJZmvC4AczQz87ID4jM4hhfSrzAJ + pLV6uM8HsAGCaZ4Ho7Vol2fFCg62ElgaFDNm7lpwnVLhbROyiPOB9LshZyXLRw8zW2XJY \
dCtyHnB70HTuWChwjEh7o4UHccacwnvyTBVsqOFO / xWWOgnYdzRFxdt6hcyMFCxKzakEQWL2kiRHYQkG7pwnkliflShfB7DVnyxKfZqPOOGvVFzmcZDNghzhSBdBFpebk2Q0C / TpGXL8waLMZqW + T3memvMdILo6 \
z9XpA1pKER247P1komblpgs7EULNG4ZYtIKpOoR91i1o8hYGmAWbQJ1k9jW0FrIrDzoCQkVU5DbLDt2iUzCEkwvszCQGX0HjlCEOM5rZ5FwTFVNBicstdsayW / wpLP70APh / QUmvtp3cR2aFpzh + 51 ufd8uCmVLJ \
JoAEdXCHf0d97s3VIRKQd9Ehr / sZqWmHaBB9Y81Zzs5luQhyYAx9ihTAtHwU6Yils0AgJshHpwlViRTxC6cJi8IXElW4BLMrMglub2y55HmZuz1i7bmjoKpr3nanb2U / GvL3AHmZhfJqS8pMKmL / Bh3T9Iyzm2Z7 \
7 TRkxNktHuYOkJpC9qfV5KESobkP / oHbj7ccLNKdT2i3NudnGDHdcIuxBUaGXAVazBY5X1iqYt6TG + nZW8zigNSYCDI6UfoIsFwmhOzYZf + A9ZtE8pSzW53 / Ax4venApB8GZBJp768NCmw8H7hifJRy7F39h7O4I \
720 h1LkXSuRLqb2Zq5vQ1e3eHImrnWH3Zx1nXRvRxLcIKrGyClgINLLPQguff2Kff1BlRSPmH6oMiAoFY2uqbPqZIyTFlSSGuSFlwqtKfJDUUR / 8 TEv9EE0DTR5yphOpj8z1hGt3Mlb8bmcCos1ODjdsGQS9SHpO \
nbebjl616lT9MYAJxQWtoWQJugpKkgOGdq7dX2EncWZvRnVXbYSxGehTnZzEIitSJwRd9E9iSQDgKx06ZcSf + SfmdRrCJc3d4cfueFhHKiw5ZXdX + P9jk1bKrB0RS + hk2VG0 / J + v5v / 5 CuY3HCdE5ECsL4uBY8FI \
W5HI78BUwTNaKxGzoO6Gdlx1yRs1qsCxLmCsZVwAXBdA8wtY4fZFAO6gmX6OjL3xiMY473EGaXZiDh5QGdGDLcbs52ofmeRg9yAmHKlrwu1DWolz6jo3jsLtd63vqM2HUcEZ4bo1MySSwlhPxdtE4kzKhtr2lbcX \
kMnyl3jpBUfbbaugLsAEVE3TmuVcP66iswpDyDuwA6JBy / USasZAJe / AYVIpvcJ9PUQdFkiYvk87DL8DgrmF4jxMm1dJQb80q7HWbFLtHmqACp3aF1KmyHaxyWZcfmfIRqDlSUSEsp7wbj0txJquicr9yhV + tYVT \
K569BXcqgqqglLajGwgfi7RnZKs / xcjOr7ewrFyxmjWlTcrfamHbj7awNz5Fw7zlbUx2jdC1rFdZ2ewvtLL6j7eyrFLiZUML2fQ + 70 zExjnewWhodCIGNqN6dXVywknMzBaOY2btPVlB7dFdVWs9y4fK7eQVOrVo \
dnchw1O2E3BMAa9FJsSf27KBFQa2nxwZeaqS6jleid86f + gVD8ReDQ / bFgwho2uSrZWf5EW + 8 / 1 k2FbOIYYrbAWpxxNSZLA2uoovlmjD8WQVWdpMqc60rXbDHpkij0yR6khDUlMmToKBEqpaf0 / CMvR1osyXw4Uj \
HJogEIvqHcrlsnC + EAdI6DMW + vzkbWFeQSgu / 7 SlaOvDuAMMXjFbiU / UaKP1j8fnbAmfZ1xsXD0Kr4xL4LHCqoUu4HrImG29 + uwytzkUVd1 / 73 l5OS76VAw5lqudYlD5DmbDGtDKIvuddS6dhxlitqdGQ4JeHMQA \
qOzbsJsx9o / npD0UTwYotqUUhz85WpEkxDKSugWbF9krISAJeMssAxVHVSlIphL / NvhVlEdLW / VcNb4e / AdevKmgnVXgLalsg0ev6gMGQrYWIhRzfZXOXkNnlfexS7bkRQLldW39IacwHqhrl / KOh + raeju3eX9s \
6 CGyeiXlHfW8w7LnESY9Z1C0spfi9b26FU6f5It8l4EolkrkvTvwFUy2jZ7AHaEGxkzWlEI9ZNRKMtJwbaBSJxPW6VSPf / KD + APpK / YH3JGNvmOg8hOsIb3vpFcjfdlf6bkEdh3bsDxIRFZ + xE0m4QS59gEsDPgu \
St8cYNrNZlqKVFTOAUcvlbmVAP5pkynr + wKkf3o85hvVa9lMM5uVUG7YcD7DcljxB3DYkLeiVe4B4s47fFCPP + geVL578ORK9yCdcB19iYW0q / XlGVecXMlQM5 + h + g4mHVro9KUk95yTgOq / ZUDLzIbggp7MsQO5 \
h8wOV7qH + 0 LsdIWHcIVWnNJxMlobWubdA9xbmZXrU1oWvAMlsJtqLPGDyFhlr92phaqefTyLLZWS9bmsYgcXHY0 / ltGq6 / XYK1BiZrGkwfJeB8oweHpMdgOdrRM8n3rWBxEHFhDL + ssK4 + eICh9NF8HBmQA8llCe \
YAh0imNjNBCX29H + 6 GtRgU / vjcgp2z64D6eIsJZVav8MDrHzgpLjeKx070VAFUVQzBGZ9aKaf7 + aMN6eflMsgm0vzt9BHwE3vZUbjVyvaHvNi + 5 VdRGiQV6U22hpRc6r6ogAheDCHr + ciXtUcaagnt3znytsKmnG \
XyT4IP7iEMQmkxBI1B4zfclVM3joisYnoFqsAZeHCh + qN4fspysXzZWlK3SjU20QkUDmHl3pnMtD0KH4kbjVVWbdwSAM95M + Os + 5 QzBRJDZ / w3mWqxI + 4 z8lFDPeRu5vqM + S4xhSLvO3j0HD0p72Dm8NESIO / 08 Q \
obN + sRoVwK8o0Jo7I / 9 p28A75BG4wHsGC / 2 jom5QPSCgqmof8ERsmnB6DPEzrppBe1rKWntQ41Z + ubNWVK6SV19NSjBiKaZDS6wIIuexW3inCOpiASID3lf6C / z6gUIEjY8w + wM / xu + 5 Sg5cQ85PQahixpxNytkF \
QpwhAV8CYRQ42uVTokuLKsE8hVrVMVXN9oplM66AtLmvX9l3k11p8wuVzbp9 / 00 mireviAfVSu + Z + sd3pDdAhOxT2JktD1e8SK56kV71YnzVi + yqF / ngBTY0uqBlcoH + 8 + naFFAcEp7xJDteR + ACI1 + 3 h7t2oO3d \
EeBndAELadQ / CKlN9NmoQzmmKmnXvrNfnxP6n5ODbYbo7zCNO / ml1C / dlkPR3w8 / XQS008vl + 6 dke + mQTfD52SP4vqPdt0xQ8 / oOMWgVbbnzQY0caaZTVniIDYl5 + A0JecNqDDgQNkVgf6mM35POR8kXduRdf1BY \
2 EnPvXDfOOU4PHhjq57lDBvsvtWNukPOwa1NrPRt5XBhKMcSWDcrLhQvt9b08eJMWmT / TXK653ZT1biSwxZYJFOjMji5wd5p9d3T70ZPv + czV8Xx4mkIetAsGL78Njty49SeYceSjeAWHzfTPLN5QFkB3ch5E8TD \
5 h6YhplnlxsRX0PupWF5bFs5WpRvwc6P8YrosbKpgbGM1ZgvoZt00ayZbPUifOvZEPth0Y09f8HeUAQnvfkr2NooA04yQ2ZDrQ25Dx5uLj80cL4RH2oBEMn + pczrneVR9Yr + wizR / D4DBrcK9D8b0Q0KmuW9MLIE \
GBuPLe4BP94ewY4fMK22B2h5241RjQV85eYOwjoGRNtwNR4yqXemCNhjPDgq5o4Z2OOG7sBra6 + 0 oP4hH4mq + YgSVQ5 + zqUimPAtOtEVzwLOPRUGo6xbYD7WRv / lVWnmsoYHj18eH7 / + 8 fI9QsInqlq + ZaV3rrj2 \
LxPgwy2xA7nNBPVcL10slYQtvLop5h5E4tJlBXgokQ + f2GJ / 4 C0wY23x4PhY7zyUE2Q4V + GSUxS6ywmoQkT3pXfKzWgoMYBEKKQn2uIp7w / SkT98F7ldQ1R5AnMc7Pev1jBxgJdnBHh5RoCXZwR3iU06IixovXIh \
C7HICXt + sX93TbzqIhutyH / zbnvYxFNyo / WjGV8TQbs6Um3uPoR1YLlP3nv8kNWHdw / G0XnvC + 1 laGKKf0drkV2K9u6vUauv4IG4295Xo1P / ipqJvTnnMWdEmrv + zTzQVVNXD3bUWIr9GilxktsvakLsOTwrgoD5 \
r3c / hhyCo5rjc1Cp77GDXAHDn4ZrLE / kvY7AbdXjr6TwlU + GFRmwcqL + B5A / xX + 7 Lyf7tE4e6iWzGJ54bR8lkudADLSYSK8hKdGmtkuFafBH9xw4Bzwz9Qz4LGOL7JTSZtpe8C8AQOG / U2bVdteRGA / YPiNr3C1V \
qvc5 / dO2S0zjAPqGD8qKZvMep30 + U3K6YDdcuoYEz2rzJT14iATv1uBtXUyEw + GOWsopdY + 2 UnSkAyI0VtQxN6jIly / / Lqa + Gp64ZbqUji + MHneZuOQZ0XvC6fFYE + rflOI0dBDze8zALVtojAbGfCa6pS + x9LmV \
+ Be5Le0hvOiJI3LyEV7vsvmY7U5yfHONnQiUyOkjhsva982d0SiVwznN1IOAVN9OyWarnRz2ZCfGZGsUrR8lDB0epm3RsbNna9s + PygKIbzHM9NjD6wQJJ2iH4 + QD4rHemc33NoR8gI1r6LiY7knST5o0O8Aa67Y \
MNXFswdgrjt + Pz9D7ncGCg0HbDFjOQ7E3jVc86Oyb9muaufutXxHRGum / QOOcDsLaREgQ0Mpop / ped0OyTv0ndgzukkao + GSwLYS9oM43YBn3Tyxh5jN8lUe2kihv0k7rwt7bd + G8NeAd1azjULPttgD1J4BauHy \
DP2Mz6a02srLSNDsXziGJ44QHUVPDliZig / 8 HBVRdHw8 / Wb9mVga6JFupVj7kP4jtJJv8IYOU / x9C3qpvRXOgFa4GSvLG0d8hOSDjOEDKJesVAhppJY8DvkA89jRShA + bqZuAnsXgZybH99j3pG7T8ZiIRCm3F2l \
FOGtMqtBw1KJG58M2gA8IhMbRSQbCsIuM8eH0jh67LgS9lbQKU59Gb1e4y4Lae1TtkPfWVy6G6 + WCYVIuk3AGQvQ4JYEYNdlNAm7D / ibPnjpexWv / cap3zj3G5d + 433 / wkA9uECwGLb9W + Gw2lvbu92 + xF + pfVZ / \
4 d0hWBRysZtyJToOrXzXDioHwC8oPdR4oPk6Rejf7ga3MuD1eVARJZlIDALYW1VliAZHrmloOahQmNLywhIK4Z8zw7Txj3zhw0pVFfA5SE3KzhX5Ppb9A95NaooVXeuG / VfKK2w / g6kOxMfI9uTTkpP / La7ytQsm \
6 kzuDiI4IiXhgyLBwBvZ2P2BpW6S4VTl4S + y76esJq6Xlle6qtVu4ktOlxt38cyPZAxwJyFhnUre0G3m + vIAZkrvHMMRP / ItES2DqePh1M8oNEcBFZteHr7kxEfZPGOZoec / wPOS9xc8aCVMwke0uY6Jdqj9gzgG \
NUl7 + rmwUKLFhMautLtu3J5hI0iV46 / d5LPGu9cuXoHDyi4AIyG + A41uknlGJwflziFF1HlA66xwr9aelsOzDuPRYYRpl5QvcYswl + j441EW2gNvB35GGtJQCWvoqtg4gIChRCf0Dk + OhZiQIVOrbudLPNcKwYRN \
EPKTvEtL0N3TS7fD4e4lXmdR2tMfF3LBjl2gVOi0LtzuOGiG + wn3c0 + 9 RhFcl4JaKK88UqV85UtTuwO / BSt6Dkw / 2 lso5IRhu0LZf1gZNWIKRP0YOWfojeljqMXKUO1Q5fawDefzrXCjADV9AdIrvKiWbj8UIO7I \
WdEnI + 5 TLPepOd9bS + osOoIUVR2847v9PH3Ko4yXeOViiVFIWEmXsFZ0TieS1EhFz28glPMPnovxRE / 8 a / ZGKsdNBZzctIedooRSszjOBp + rGW + s8ykpW4gxE / 8 GDuzE27gbsyURon + noP3S4JfH5zIzfk3Z6R7Q \
8 n296nvn6tk + VB + FzZs7I7yC + Pu35 + UCLiJWUZ7qNM / TtHvT / Hy + + MV / qLuHdXleyo3FNkuBOmTs7ZJ5ewBUYMJ / iIovOSjAu1cjr4Ha0b7JuYwIL4qtKejFRt3aN3yrMXXIvIbX4ZL38DGS1V6j4nBzeQav8YLi \
1 OFjvuySLjquSD12jX376 + oRKW + y + jO68o0bsB / eKnlT8yE1RFRLEZaSS3QxJfLhSa9uoEyNV70pjG38h3yX7tczjy5YLS5di9ZDLm7UCDWKsX1zy454 / umQ / u4G1KkzIE / sr7G3hqW9kmFaenixzuDobP9QpV8g \
RGVmvdbgHmg1mBvPjEWDG / Tstbmu0buAsBzkcwZjGrXitm81 + H54A3g8aCeDdjpoZ4O2HrRNv60G8Kje9yO / 0 fvSv0ZcnSzvcf1pf + qadvyJPHQdT13HY8N2dk07v6atP9g + / 0 Dr5w + 0 + veMr2qbD7YXH5Kda / 8 + \
VW6zT8LR + Sesewh5e40WGECuBpCoARZVb7w1v3HLb / SG7R1l2 / cbL / xGjyBvB5pmAGc5aJtBu0lWSIn6C6X4z9YCv1dL / F4t8nu1zO / VQte1P / FPRS6bZiUwR8mj46VjlrTU7qEsGGuNy4OtsnlLlOmt9CY7vb6P \
nORxF3HqX / 8 XohBRvg == \
2016-10-06 13:21:30 +02:00
""" )))
ESP32ROM . STUB_CODE = eval ( zlib . decompress ( base64 . b64decode ( b """
2018-09-21 08:39:36 +02:00
eNqNWnt31LgV / yqOYfJaskeyPbZEz0IS6BBg2xIoIdDp2bFlOyFbcoDOQjgL / ezVfVnyzNDtHxNkPa / u43cf4vedZXez3LmbNDvzm94k / k9xH1oKW + XR / Eb5ptX + s / W / fn7jVEKdxsyX / i + 01 O2zExrFmfX / M1PD \
foomyE8roUBFrehnhKJu6ocd7WTgzIbayvepbDh7D9bEVG2tkZe + pR35c3e + PPu4Tj9uA5vrTG7iv4tkV22 + iVKHRGoX6NSlp6QONHdtxDO3cqa1dGboQCLOvnyfe8NPh7ZRYbUDofb3aQP5KbpHJO4toSZLYQjo \
fuAbU7iJCTfpahqtp8L982NiUS + syo9gWxh65edBb3OeAk0vQdSeIDeFGRlvCqLLgdvp4bn / 1 BPfn0ciVtyGa01hh9PQGWQFfJvSijYbDR5fjiR9gvxc8qbm + CRlUTt1t4CNjm26wmhD / 6 JeIrlqRUnxw6qI18oc \
cstE4kBKi / j78FBaJ9SNa / Ro32LYN0gK2QvcqaBxJI3hludgD08egNrp0cCu / 1 MmyZKlmYGJuvIWDPnJXUeTGx3aA4 / rbJsb + FtOWLqgZ34vx5oBet3F + t5EezaRMC3928CP28NZjm3PelnqZnQscZqgigZBy6HP \
cl8wnuwfzHRPj3Gju5zwSHQAUq4jCnk / W8EZ9 + Llhu6K91PBnlu07YoMXtsZoZJSX / 00 v8T4EQ1s9iPLsfXy1mvmDx3FsM / 3 kcIGglo9gOKSr56PmHLOxzbpNm8M0OlF2FoWObQLQQqvb26sC69pyRiNnh2AajH + \
+ T81bnBeqKljZcvB0sHGizcvns3nfo4pZXVHHCKjfOhX + xEtXDa3iXcIOhm5AWF7DK8gFl0Al / LEE9pkCWMII0IXWbNxd1PSKVfs / R2puvvyNfwDJMNVwfDGODB2Tmi679GIvHe5f3Ib7w / zU + JEHZyWcLZuCahN \
BP6Bqp / m18HddI7sCD2AJk2ts4DvYD9aoE8Tc9ou8i1ZBKL5qi0H31wnMY5m7BebbItngor363gc87JWt0jJVv0TKpC4GyVevNY3EZnsDvF6Sq76BGZmV / CJsjpKoZ0jZWCRNTgUu79PxxqaE + IAY5 / svWF9Qa06 \
mF / z / m7K9NoRvXcIawYHHggC7EWxZaSEOK0jWcB424xjkRFjZI5jXc / He + Na2dPwPtX / 2 KflOcX6nPXYgG5yF / YuA4LIfvKtm5QDq0bAK2Ns3RirSfs8 / vAA14K6T73 + t + ZHtgTrom7wnHBf / 1 FubRENAFUo4Ch + \
HBlpGXAS3H7JrgSt6NkDQPSW4xGRkA7T4p3AmuAEmd + wIa5Rka + ufZIeInyeOVJWNAg23SZaDcBe1 + wTug0yhH4bBT2NrJkELUUNIb4jOrlVKQw2FGknnOjaP9KIy1he7 + OPZfxxE38ASF0w1gGes4nAEZdsLFsg \
Nhvhg1yx7ul + xjwGdfgYOIVWWt6ZX4MfMc0Fz / uO5PB2CECPPMOhM5ux20ChV / GUeOlf4JRTQS + QfSOkvbyiRRI2qvIgitAGQR06YjBdI2QEpDyLS9G / MgGBsLMBgFl3NozlUYSBXJg + h9uX74gMW5 / O5h + Fkonw \
Y + FPxCNYZ9H9sQYixPGJRAvxuNGJl3FdMVHNmnFdpVcPK0JT5zhwwNOetZsvbSo / XOd0RBfnEcyzttgsPIlHNAfaGPRlu + j1f2Mtq5A / kIGVs / k1XHhKE + vsqcBUT0cTPn6lYLLhWKuttmfzHTIyZEn / HgaTsVnW \
a9DahavgUrXJPpnj08Bxsu874K5R5Pn3jO85EKXhXLhQq18Rx01HQdaQBG1AWoz5u / t / Ozl6TMZHwfP9AmP65SFrEKUJmG4U91cSvA3ZIWiiGeH54Sh93UgDQebw4QW5E + 1 QRMnKcDyTENEt4Spv0oR06eslH90x \
2 JxLZHj4r310JSZjj + LcHWz9TP8UlBPiYtBiS5dQJFXvac4HzPqZvC5O1QUth6xFc4gr8aRn6nUKPsscc0AikLAGMFvk0DARa9ke0dPinseYVTxNK5Bx1XGciCc8Jfx2OplEoeFUsOGIqemErI6ypyEkB7m7fYg3 \
3 ZsheHscHGC3Fu / CarxgSooOHVGqmHtVTibJqICSM0R + z6qbDexwdhRnGk4tHAEQWJXLklugQSk046JBQuAC4Zxi6 / seqAC / HV + x1c / 5 yKizERRgrtdSGnGDxfYbOIQZS + f3a / Mfjng3QO0GZXt1e7jXJec / 5 afR \
dW84qHKkBtz7NvTqLD3iy4PbQj1xu9iHUe / pfPn2dDuEg9qVF5QM6H4W7q + GDfKGG1iegrqFXqRJfwyNXTDSKOvP9k5n4yqRdmnqj + zGmmUa0U4gdSJucziS7Wh8KsfgWcKBHUYDFQVIWsfhiiBfHzq7bhI6MVtC \
M5hJPt3E7ML6zg6VHBSHthYvdZH9Fe / AyWzNENSXn5K + lP5OOpGf / bb0o5fxfTVO6CXjLn / HzzO + bEnQzaEpugbWK5DZClUEYRcoy / 5 TIMw00Q6IXn1YiBPysG0TbTv7IkRqEeDoUFdaOnEXbGDofCCTocOTwzlm \
v0Lwj3xoybUFXqJH00z5dNCdiDDHhNmCtROTFY3OrzzwZy6lT1EfJfd42T5SapyycuAkHsujQ2FAz6jCIexCW8Hd70lOcIA2X4qn2OaIRsTJvEb1M2PvB6pYI1AsZskiJR3X5T4nqeV / ooQKfpCBGv1xFVnOKAnS \
3 TEbGkbTGbk / 526 zpuhgnLVagD89o7t4DHrDFhWlkDX / eqx6Xm04FGtQbvEQDhpOfMQ13LWtiHi3YR8IgnSzTvyZIOaMAhk1hPZSWAnMnl0Fne + zmM29BPU8KB4XDXR1tG1EWotIK5TeDtNU + YvUuTBKwaLYQ4aR \
OlY0xwm9tmIp / + Tah6HC9U5HJj6UxPINcRUoXYFk3oFFj + C4P0V + ulw9rUZ + vJQ3hio6LyF2ac5MHMf6FLZ6ZNUV + TQsV + u3kLi32Q8wGQ8BW8gP + M7l41DU1BmtE89UZ5RWC2ngZWwtbD4hF7ez + 5 qgHmqxPRad \
etJZO4D5Yg / qKJ6xriKLtpyCNALtzLpBszNJ6LFwa2lW04DC15bT2WqffPzgZ6ZcsYB2Id6gZJ / DDKvxuSNhpYEeLBOXicyvIsI130ZRI1kQKuPpao9qQ8SsWT5OcjC9bQMrxVWvMHNQqR / lIcbIuLkIKgdZsB9v \
kOW7EIkhujD0GkJCMiDOvVQ4mvButj1Y0olMrATKkxUbQuFbBmuSCa2Kh9asmLPKbrTZ6Byu1TJrMQmI1c1IzMoPaZFSicIY1ivgfA24YSEitJbDpYizzym + Q0F070CdWnxjklnwlFD99GfaspMEZXwj2Ki7O + jt \
AVX03FAl2ycn41m / fJoiGddP2d3DHWxCz3ikrRl9oNuxt5mLjmoIGMZGUcOAebEPstnYgQ0BZDmO15RahbC + T / hNBAIJK9UC39PJ50QCsjPG7WolnOrlYaLUF2BL0xD8nCGxQ0yFjynbJUtfVyHjkReUtcgAhp + t \
OXcNUUQILnQU + 2 jFod / w6iMveh0nBKxcwUXVHDB2UbmhG8XqAEhwRcMeSEvhDLFj0U4ZDug5iovRDQEy1nXwjapkEIczpqtEcDSb80NPRHSYM4uK4sws4ge + NwzuikI + cM7gEyZURhX3j0XaTmr0EpOncUAOeAr3 \
ArqbhKZhDK9EA0DnJ9 + Y0f007Dho2eZQv5dQv09S2afh2En01zKLW34GajBE + EA8BOTs8FXn7QfgzGeQw5f6BkI5Zz + zHtjoKQAQoOWCnNNS9QJMbPUHCvF6fjsC6MEijuOUmUvClh9IeklqDCUWcC8nfVwCa / J1 \
wXqIugZ1wPfOD + KxSIMaC73iGBt + sKrheaI2XDyCuWi57O1qgI + GH + ywA2sV78OuWMqoiTp8IhDmtuE5Ap9bohxEaqHEsMkyZtQXjgkgnyhvQKzuM / nHqkdH + Zkde0mPnVBig2IglkcH + K32XoAt3RM1OYMIo99l \
OgC4mmxwcnElZCXAd3bvxQye3YLegaKfUZGgYUdrN5mY1CPY1K1an4P92Xo / KCkm8t1lxe + RwL0uLt2a9o / NGjBjdU7HTlAi3CYyKQnkpNy8Rq9oLj8LoQMEWZoZmWDf + 0 z + slpgeh0eppuCgZPXO85w + FlCEIRa \
2 QRELfWfOHQtB3c74wAHppnPcOELcK2vQJqPGEBBTpEfBwsc3 + cdZ5fXC36aU + W34W12Jy7t / LrOCnm9U1JXgcLFDohrvZRCFXu4f / kbRsevJTR + dQkHoepmW6 + g922IcOSBweqXUYxiLocE1ZnFLlD + Dpef8qYW \
X0ttXMmhabDrUHElU0gheOW0FBZvfr + x8l41xNK7jLhswxiQuCxwG6OBMuYUVum + UomLTtnmAGK6nXKrDGXi0aOj2rslZ8E2LuWSnypX4tnWoTe38f + FwrkHXKOKrjec0H4luB0u04yWpoGQsJyXjNm1cyfB / 6 v2 \
y7 + X9Uf4H2taVUVuc2WMH + mulx + / DJ1FUUJnWy9r / q9tUcF5h0fijfIqL0udffsvV73qkg == \
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 ( )