#!/usr/bin/python # NB: Before sending a PR to change the above line to '#!/usr/bin/env python2', please read https://github.com/espressif/esptool/issues/21 # # ESP8266 ROM Bootloader Utility # https://github.com/espressif/esptool # # Copyright (C) 2014-2016 Fredrik Ahlberg, Angus Gratton, Espressif Systems, other contributors as noted. # # 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. from __future__ import print_function, division import argparse import hashlib import inspect import json import os import serial import struct import subprocess import sys import tempfile import time __version__ = "1.3" PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3 class ESPROM(object): # These are the currently known commands supported by the ROM 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 # Maximum block sized for RAM and Flash writes, respectively. ESP_RAM_BLOCK = 0x1800 ESP_FLASH_BLOCK = 0x400 # 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 # OTP ROM addresses ESP_OTP_MAC0 = 0x3ff00050 ESP_OTP_MAC1 = 0x3ff00054 ESP_OTP_MAC3 = 0x3ff0005c # Flash sector size, minimum unit of erase. ESP_FLASH_SECTOR = 0x1000 def __init__(self, port=0, baud=ESP_ROM_BAUD): self._port = serial.serial_for_url(port) self._slip_reader = slip_reader(self._port) # 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 # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 self._port.baudrate = baud """ Read a SLIP packet from the serial port """ def read(self): return next(self._slip_reader) """ Write bytes to the serial port while performing SLIP escaping """ def write(self, packet): buf = b'\xc0' \ + (packet.replace(b'\xdb',b'\xdb\xdd').replace(b'\xc0',b'\xdb\xdc')) \ + b'\xc0' self._port.write(buf) """ Calculate checksum of a blob, as it is defined by the ROM """ @staticmethod def checksum(data, state=ESP_CHECKSUM_MAGIC): for b in data: if type(b) is int: # python 2/3 compat state ^= b else: state ^= ord(b) return state """ Send a request and read the response """ def command(self, op=None, data=None, chk=0): if op is not None: pkt = struct.pack(b'> 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) """ Read Chip ID from OTP ROM - see http://esp8266-re.foogod.com/wiki/System_get_chip_id_%28IoT_RTOS_SDK_0.9.9%29 """ def chip_id(self): id0 = self.read_reg(self.ESP_OTP_MAC0) id1 = self.read_reg(self.ESP_OTP_MAC1) return (id0 >> 24) | ((id1 & 0xffffff) << 8) """ Read SPI flash manufacturer and device id """ def flash_id(self): self.flash_begin(0, 0) self.write_reg(0x60000240, 0x0, 0xffffffff) self.write_reg(0x60000200, 0x10000000, 0xffffffff) flash_id = self.read_reg(0x60000240) return flash_id """ Abuse the loader protocol to force flash to be left in write mode """ def flash_unlock_dio(self): # Enable flash write mode self.flash_begin(0, 0) # Reset the chip rather than call flash_finish(), which would have # write protected the chip again (why oh why does it do that?!) self.mem_begin(0,0,0,0x40100000) self.mem_finish(0x40000080) """ Perform a chip erase of SPI flash """ def flash_erase(self): # Trick ROM to initialize SFlash self.flash_begin(0, 0) # This is hacky: we don't have a custom stub, instead we trick # the bootloader to jump to the SPIEraseChip() routine and then halt/crash # when it tries to boot an unconfigured system. self.mem_begin(0,0,0,0x40100000) self.mem_finish(0x40004984) # Yup - there's no good way to detect if we succeeded. # It it on the other hand unlikely to fail. def run_stub(self, stub, params, read_output=True): stub = dict(stub) stub['code'] = unhexify(stub['code']) if 'data' in stub: stub['data'] = unhexify(stub['data']) if stub['num_params'] != len(params): raise FatalError('Stub requires %d params, %d provided' % (stub['num_params'], len(params))) params = struct.pack(b'<' + (b'I' * stub['num_params']), *params) pc = params + stub['code'] # Upload self.mem_begin(len(pc), 1, len(pc), stub['params_start']) self.mem_block(pc, 0) if 'data' in stub: self.mem_begin(len(stub['data']), 1, len(stub['data']), stub['data_start']) self.mem_block(stub['data'], 0) self.mem_finish(stub['entry']) if read_output: print('Stub executed, reading response:') while True: p = self.read() print(hexify(p)) if p == '': return 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(filename): """ Load a firmware image, without knowing what kind of file (v1 or v2) it is. Returns a BaseFirmwareImage subclass, either ESPFirmwareImage (v1) or OTAFirmwareImage (v2). """ with open(filename, 'rb') as f: magic = ord(f.read(1)) f.seek(0) if magic == ESPROM.ESP_IMAGE_MAGIC: return ESPFirmwareImage(f) elif magic == ESPBOOTLOADER.IMAGE_V2_MAGIC: return OTAFirmwareImage(f) else: raise FatalError("Invalid image magic number: %d" % magic) class BaseFirmwareImage(object): """ Base class with common firmware image functions """ def __init__(self): self.segments = [] self.entrypoint = 0 def add_segment(self, addr, data, pad_to=4): """ Add a segment to the image, with specified address & data (padded to a boundary of pad_to size) """ # Data should be aligned on word boundary l = len(data) if l % pad_to: data += b"\x00" * (pad_to - l % pad_to) if l > 0: self.segments.append((addr, len(data), data)) def load_segment(self, f, is_irom_segment=False): """ Load the next segment from the image file """ (offset, size) = struct.unpack(' 0x40200000 or offset < 0x3ffe0000 or size > 65536: raise FatalError('Suspicious segment 0x%x, length %d' % (offset, size)) 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 = (offset, size, segment_data) self.segments.append(segment) return segment def save_segment(self, f, segment, checksum=None): """ Save the next segment to the image file, return next checksum value if provided """ (offset, size, data) = segment f.write(struct.pack(b' 16: raise FatalError('Invalid firmware image magic=%d segments=%d' % (magic, segments)) for i in range(segments): self.load_segment(load_file) self.checksum = self.read_checksum(load_file) def save(self, filename): with open(filename, 'wb') as f: self.write_v1_header(f, self.segments) checksum = ESPROM.ESP_CHECKSUM_MAGIC for segment in self.segments: checksum = self.save_segment(f, segment, checksum) self.append_checksum(f, checksum) class OTAFirmwareImage(BaseFirmwareImage): """ 'Version 2' firmware image, segments loaded by software bootloader stub (ie Espressif bootloader or rboot) """ def __init__(self, load_file=None): super(OTAFirmwareImage, self).__init__() self.version = 2 if load_file is not None: (magic, segments, first_flash_mode, first_flash_size_freq, first_entrypoint) = struct.unpack(' 16: raise FatalError('Invalid V2 second header magic=%d segments=%d' % (magic, segments)) # load all the usual segments for _ in range(segments): self.load_segment(load_file) self.checksum = self.read_checksum(load_file) def save(self, filename): with open(filename, 'wb') as f: # Save first header for irom0 segment f.write(struct.pack(b' 0: esp._port.baudrate = baud_rate # Read the greeting. p = esp.read() if p != b'OHAI': raise FatalError('Failed to connect to the flasher (got %s)' % hexify(p)) def flash_write(self, addr, data, show_progress=False): assert addr % self._esp.ESP_FLASH_SECTOR == 0, 'Address must be sector-aligned' assert len(data) % self._esp.ESP_FLASH_SECTOR == 0, 'Length must be sector-aligned' sys.stdout.write('Writing %d @ 0x%x... ' % (len(data), addr)) sys.stdout.flush() self._esp.write(struct.pack(b' length: raise FatalError('Read more than expected') p = self._esp.read() if len(p) != 16: raise FatalError('Expected digest, got: %s' % hexify(p)) expected_digest = hexify(p).upper() digest = hashlib.md5(data).hexdigest().upper() print if digest != expected_digest: raise FatalError('Digest mismatch: expected %s, got %s' % (expected_digest, digest)) p = self._esp.read() if len(p) != 1: raise FatalError('Expected status, got: %s' % hexify(p)) status_code = struct.unpack(', ) or a single # argument. def load_ram(esp, args): image = LoadFirmwareImage(args.filename) print('RAM boot...') for (offset, size, data) in image.segments: print('Downloading %d bytes at %08x...' % (size, offset), end=' ') sys.stdout.flush() esp.mem_begin(size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, offset) seq = 0 while len(data) > 0: esp.mem_block(data[0:esp.ESP_RAM_BLOCK], seq) data = data[esp.ESP_RAM_BLOCK:] seq += 1 print('done!') print('All segments done, executing at %08x' % image.entrypoint) esp.mem_finish(image.entrypoint) def read_mem(esp, args): print('0x%08x = 0x%08x' % (args.address, esp.read_reg(args.address))) def write_mem(esp, args): esp.write_reg(args.address, args.value, args.mask, 0) print('Wrote %08x, mask %08x to %08x' % (args.value, args.mask, args.address)) def dump_mem(esp, args): f = open(args.filename, 'wb') for i in range(args.size / 4): d = esp.read_reg(args.address + (i * 4)) f.write(struct.pack(b'> 16 args.flash_size = {18: '2m', 19: '4m', 20: '8m', 21: '16m', 22: '32m'}.get(size_id) if args.flash_size is None: print('Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x), defaulting to 4m' % (flash_id, size_id)) args.flash_size = '4m' else: print('Auto-detected Flash size:', args.flash_size) def _get_flash_params(esp, args): """ Return binary flash parameters (bitstring length 2) for args """ detect_flash_size(esp, args) flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] return struct.pack(b'BB', flash_mode, flash_size_freq) def _update_image_flash_params(address, flash_params, image): """ Modify the flash mode & size bytes if this looks like an executable image """ if address == 0 and (image[0] == b'\xe9' or image[0] == 0xE9): # python 2/3 compat 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): flash_params = _get_flash_params(esp, args) flasher = CesantaFlasher(esp, args.baud) for address, argfile in args.addr_filename: image = argfile.read() argfile.seek(0) # rewind in case we need it again if address + len(image) > int(args.flash_size.split('m')[0]) * (1 << 17): print('WARNING: Unlikely to work as data goes beyond end of flash. Hint: Use --flash_size') image = _update_image_flash_params(address, flash_params, image) # Pad to sector size, which is the minimum unit of writing (erasing really). if len(image) % esp.ESP_FLASH_SECTOR != 0: image += b'\xff' * (esp.ESP_FLASH_SECTOR - (len(image) % esp.ESP_FLASH_SECTOR)) t = time.time() flasher.flash_write(address, image, not args.no_progress) t = time.time() - t print('\rWrote %d bytes at 0x%x in %.1f seconds (%.1f kbit/s)...' % (len(image), address, t, len(image) / t * 8 / 1000)) print('Leaving...') if args.verify: print('Verifying just-written flash...') _verify_flash(esp, args, flasher) flasher.boot_fw() def image_info(args): image = LoadFirmwareImage(args.filename) print('Image version: %d' % image.version) print('Entry point: %08x' % image.entrypoint if image.entrypoint != 0 else 'Entry point not set') print('%d segments' % len(image.segments)) print checksum = ESPROM.ESP_CHECKSUM_MAGIC for (idx, (offset, size, data)) in enumerate(image.segments): if image.version == 2 and idx == 0: print('Segment 1: %d bytes IROM0 (no load address)' % size) else: print('Segment %d: %5d bytes at %08x' % (idx + 1, size, offset)) checksum = ESPROM.checksum(data, checksum) print print('Checksum: %02x (%s)' % (image.checksum, 'valid' if image.checksum == checksum else 'invalid!')) def make_image(args): image = ESPFirmwareImage() 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): data = open(seg, 'rb').read() image.add_segment(addr, data) image.entrypoint = args.entrypoint image.save(args.output) def elf2image(args): e = ELFFile(args.input) if args.version == '1': image = ESPFirmwareImage() else: image = OTAFirmwareImage() irom_data = e.load_section('.irom0.text') if len(irom_data) == 0: raise FatalError(".irom0.text section not found in ELF file - can't create V2 image.") image.add_segment(0, irom_data, 16) image.entrypoint = e.get_entry_point() for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): data = e.load_section(section) image.add_segment(e.get_symbol_addr(start), data) image.flash_mode = {'qio':0, 'qout':1, 'dio':2, 'dout': 3}[args.flash_mode] image.flash_size_freq = {'4m':0x00, '2m':0x10, '8m':0x20, '16m':0x30, '32m':0x40, '16m-c1': 0x50, '32m-c1':0x60, '32m-c2':0x70}[args.flash_size] image.flash_size_freq += {'40m':0, '26m':1, '20m':2, '80m': 0xf}[args.flash_freq] irom_offs = e.get_symbol_addr("_irom0_text_start") - 0x40200000 if args.version == '1': if args.output is None: args.output = args.input + '-' image.save(args.output + "0x00000.bin") data = e.load_section(".irom0.text") if irom_offs < 0: raise FatalError('Address of symbol _irom0_text_start in ELF is located before flash mapping address. Bad linker script?') if (irom_offs & 0xFFF) != 0: # irom0 isn't flash sector aligned print("WARNING: irom0 section offset is 0x%08x. ELF is probably linked for 'elf2image --version=2'" % irom_offs) with open(args.output + "0x%05x.bin" % irom_offs, "wb") as f: f.write(data) f.close() else: # V2 OTA image if args.output is None: args.output = "%s-0x%05x.bin" % (os.path.splitext(args.input)[0], irom_offs & ~(ESPROM.ESP_FLASH_SECTOR - 1)) image.save(args.output) def read_mac(esp, args): mac = esp.read_mac() print('MAC: %s' % ':'.join(map(lambda x: '%02x' % x, mac))) def chip_id(esp, args): chipid = esp.chip_id() print('Chip ID: 0x%08x' % chipid) def erase_flash(esp, args): flasher = CesantaFlasher(esp, args.baud) print('Erasing flash (this may take a while)...') t = time.time() flasher.flash_erase_chip() t = time.time() - t print('Erase took %.1f seconds' % t) def run(esp, args): esp.run() def flash_id(esp, args): flash_id = esp.flash_id() esp.flash_finish(False) print('Manufacturer: %02x' % (flash_id & 0xff)) print('Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff)) def read_flash(esp, args): flasher = CesantaFlasher(esp, args.baud) t = time.time() data = flasher.flash_read(args.address, args.size, not args.no_progress) t = time.time() - t print('\rRead %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) def _verify_flash(esp, args, flasher=None): differences = False flash_params = _get_flash_params(esp, args) if flasher is None: # get flash params before launching flasher flasher = CesantaFlasher(esp) for address, argfile in args.addr_filename: image = argfile.read() argfile.seek(0) # rewind in case we need it again image = _update_image_flash_params(address, flash_params, image) image_size = len(image) print('Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s...' % (image_size, image_size, address, argfile.name)) # Try digest first, only read if there are differences. digest, _ = flasher.flash_digest(address, image_size) digest = hexify(digest).upper() expected_digest = hashlib.md5(image).hexdigest().upper() if digest == expected_digest: print('-- verify OK (digest matched)') continue else: differences = True if getattr(args, 'diff', 'no') != 'yes': print('-- verify FAILED (digest mismatch)') continue flash = flasher.flash_read(address, image_size) assert flash != image 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])) for d in diff: 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)) if differences: raise FatalError("Verify failed.") def verify_flash(esp, args, flash_params=None): _verify_flash(esp, args) def version(args): print(__version__) # # End of operations functions # def main(): parser = argparse.ArgumentParser(description='esptool.py v%s - ESP8266 ROM Bootloader Utility' % __version__, prog='esptool') parser.add_argument( '--port', '-p', help='Serial port device', default=os.environ.get('ESPTOOL_PORT', '/dev/ttyUSB0')) parser.add_argument( '--baud', '-b', help='Serial port baud rate used when flashing/reading', type=arg_auto_int, default=os.environ.get('ESPTOOL_BAUD', ESPROM.ESP_ROM_BAUD)) subparsers = parser.add_subparsers( dest='operation', help='Run esptool {command} -h for additional help') 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) def add_spi_flash_subparsers(parent, auto_detect=False): """ Add common parser arguments for SPI flash properties """ parent.add_argument('--flash_freq', '-ff', help='SPI Flash frequency', choices=['40m', '26m', '20m', '80m'], default=os.environ.get('ESPTOOL_FF', '40m')) parent.add_argument('--flash_mode', '-fm', help='SPI Flash mode', choices=['qio', 'qout', 'dio', 'dout'], default=os.environ.get('ESPTOOL_FM', 'qio')) choices = ['4m', '2m', '8m', '16m', '32m', '16m-c1', '32m-c1', '32m-c2'] default = '4m' if auto_detect: default = 'detect' choices.insert(0, 'detect') parent.add_argument('--flash_size', '-fs', help='SPI Flash size in Mbit', type=lambda s: s.lower(), choices=choices, default=os.environ.get('ESPTOOL_FS', default)) parser_write_flash = subparsers.add_parser( 'write_flash', help='Write a binary blob to flash') parser_write_flash.add_argument('addr_filename', metavar='
', help='Address followed by binary filename, separated by space', action=AddrFilenamePairAction) add_spi_flash_subparsers(parser_write_flash, auto_detect=True) parser_write_flash.add_argument('--no-progress', '-p', help='Suppress progress output', action="store_true") parser_write_flash.add_argument('--verify', help='Verify just-written data on flash (recommended if concerned about flash integrity)', action='store_true') 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') add_spi_flash_subparsers(parser_elf2image) subparsers.add_parser( 'read_mac', help='Read MAC address from OTP ROM') subparsers.add_parser( 'chip_id', help='Read Chip ID from OTP ROM') subparsers.add_parser( 'flash_id', help='Read SPI flash manufacturer and device ID') parser_read_flash = subparsers.add_parser( 'read_flash', help='Read SPI flash content') 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') add_spi_flash_subparsers(parser_verify_flash, auto_detect=True) subparsers.add_parser( 'erase_flash', help='Perform Chip Erase on SPI flash') 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 args = parser.parse_args() print('esptool.py v%s' % __version__) # operation function can take 1 arg (args), 2 args (esp, arg) # or be a member function of the ESPROM class. if args.operation is None: parser.print_help() sys.exit(1) operation_func = globals()[args.operation] operation_args,_,_,_ = inspect.getargspec(operation_func) if operation_args[0] == 'esp': # operation function takes an ESPROM connection object initial_baud = min(ESPROM.ESP_ROM_BAUD, args.baud) # don't sync faster than the default baud rate esp = ESPROM(args.port, initial_baud) esp.connect() operation_func(esp, args) else: operation_func(args) 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) except ValueError as e: 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)) setattr(namespace, self.dest, pairs) # This is "wrapped" stub_flasher.c, to be loaded using run_stub. _CESANTA_FLASHER_STUB = """\ {"code_start": 1074790404, "code": "080000601C000060000000601000006031FCFF71FCFF\ 81FCFFC02000680332D218C020004807404074DCC48608005823C0200098081BA5A9239245005803\ 1B555903582337350129230B446604DFC6F3FF21EEFFC0200069020DF0000000010078480040004A\ 0040B449004012C1F0C921D911E901DD0209312020B4ED033C2C56C2073020B43C3C56420701F5FF\ C000003C4C569206CD0EEADD860300202C4101F1FFC0000056A204C2DCF0C02DC0CC6CCAE2D1EAFF\ 0606002030F456D3FD86FBFF00002020F501E8FFC00000EC82D0CCC0C02EC0C73DEB2ADC46030020\ 2C4101E1FFC00000DC42C2DCF0C02DC056BCFEC602003C5C8601003C6C4600003C7C08312D0CD811\ C821E80112C1100DF0000C180000140010400C0000607418000064180000801800008C1800008418\ 0000881800009018000018980040880F0040A80F0040349800404C4A0040740F0040800F0040980F\ 00400099004012C1E091F5FFC961CD0221EFFFE941F9310971D9519011C01A223902E2D1180C0222\ 6E1D21E4FF31E9FF2AF11A332D0F42630001EAFFC00000C030B43C2256A31621E1FF1A2228022030\ B43C3256B31501ADFFC00000DD023C4256ED1431D6FF4D010C52D90E192E126E0101DDFFC0000021\ D2FF32A101C020004802303420C0200039022C0201D7FFC00000463300000031CDFF1A333803D023\ C03199FF27B31ADC7F31CBFF1A3328030198FFC0000056C20E2193FF2ADD060E000031C6FF1A3328\ 030191FFC0000056820DD2DD10460800000021BEFF1A2228029CE231BCFFC020F51A33290331BBFF\ C02C411A332903C0F0F4222E1D22D204273D9332A3FFC02000280E27B3F721ABFF381E1A2242A400\ 01B5FFC00000381E2D0C42A40001B3FFC0000056120801B2FFC00000C02000280EC2DC0422D2FCC0\ 2000290E01ADFFC00000222E1D22D204226E1D281E22D204E7B204291E860000126E012198FF32A0\ 042A21C54C003198FF222E1D1A33380337B202C6D6FF2C02019FFFC000002191FF318CFF1A223A31\ 019CFFC00000218DFF1C031A22C549000C02060300003C528601003C624600003C72918BFF9A1108\ 71C861D851E841F83112C1200DF00010000068100000581000007010000074100000781000007C10\ 0000801000001C4B0040803C004091FDFF12C1E061F7FFC961E941F9310971D9519011C01A662906\ 21F3FFC2D1101A22390231F2FF0C0F1A33590331EAFFF26C1AED045C2247B3028636002D0C016DFF\ C0000021E5FF41EAFF2A611A4469040622000021E4FF1A222802F0D2C0D7BE01DD0E31E0FF4D0D1A\ 3328033D0101E2FFC00000561209D03D2010212001DFFFC000004D0D2D0C3D01015DFFC0000041D5\ FFDAFF1A444804D0648041D2FF1A4462640061D1FF106680622600673F1331D0FF10338028030C43\ 853A002642164613000041CAFF222C1A1A444804202FC047328006F6FF222C1A273F3861C2FF222C\ 1A1A6668066732B921BDFF3D0C1022800148FFC0000021BAFF1C031A2201BFFFC000000C02460300\ 5C3206020000005C424600005C5291B7FF9A110871C861D851E841F83112C1200DF0B0100000C010\ 0000D010000012C1E091FEFFC961D951E9410971F931CD039011C0ED02DD0431A1FF9C1422A06247\ B302062D0021F4FF1A22490286010021F1FF1A223902219CFF2AF12D0F011FFFC00000461C0022D1\ 10011CFFC0000021E9FFFD0C1A222802C7B20621E6FF1A22F8022D0E3D014D0F0195FFC000008C52\ 22A063C6180000218BFF3D01102280F04F200111FFC00000AC7D22D1103D014D0F010DFFC0000021\ D6FF32D110102280010EFFC0000021D3FF1C031A220185FFC00000FAEEF0CCC056ACF821CDFF317A\ FF1A223A310105FFC0000021C9FF1C031A22017CFFC000002D0C91C8FF9A110871C861D851E841F8\ 3112C1200DF0000200600000001040020060FFFFFF0012C1E00C02290131FAFF21FAFF026107C961\ C02000226300C02000C80320CC10564CFF21F5FFC02000380221F4FF20231029010C432D010163FF\ C0000008712D0CC86112C1200DF00080FE3F8449004012C1D0C9A109B17CFC22C1110C13C51C0026\ 1202463000220111C24110B68202462B0031F5FF3022A02802A002002D011C03851A0066820A2801\ 32210105A6FF0607003C12C60500000010212032A01085180066A20F2221003811482105B3FF2241\ 10861A004C1206FDFF2D011C03C5160066B20E280138114821583185CFFF06F7FF005C1286F5FF00\ 10212032A01085140066A20D2221003811482105E1FF06EFFF0022A06146EDFF45F0FFC6EBFF0000\ 01D2FFC0000006E9FF000C022241100C1322C110C50F00220111060600000022C1100C13C50E0022\ 011132C2FA303074B6230206C8FF08B1C8A112C1300DF0000000000010404F484149007519031027\ 000000110040A8100040BC0F0040583F0040CC2E00401CE20040D83900408000004021F4FF12C1E0\ C961C80221F2FF097129010C02D951C91101F4FFC0000001F3FFC00000AC2C22A3E801F2FFC00000\ 21EAFFC031412A233D0C01EFFFC000003D0222A00001EDFFC00000C1E4FF2D0C01E8FFC000002D01\ 32A004450400C5E7FFDD022D0C01E3FFC00000666D1F4B2131DCFF4600004B22C0200048023794F5\ 31D9FFC0200039023DF08601000001DCFFC000000871C861D85112C1200DF000000012C1F0026103\ 01EAFEC00000083112C1100DF000643B004012C1D0E98109B1C9A1D991F97129013911E2A0C001FA\ FFC00000CD02E792F40C0DE2A0C0F2A0DB860D00000001F4FFC00000204220E71240F7921C226102\ 01EFFFC0000052A0DC482157120952A0DD571205460500004D0C3801DA234242001BDD3811379DC5\ C6000000000C0DC2A0C001E3FFC00000C792F608B12D0DC8A1D891E881F87112C1300DF00000", "\ entry": 1074792180, "num_params": 1, "params_start": 1074790400, "data": "FE0510\ 401A0610403B0610405A0610407A061040820610408C0610408C061040", "data_start": 10736\ 43520} """ if __name__ == '__main__': try: main() except FatalError as e: print('\nA fatal error occurred: %s' % e) sys.exit(2)