diff --git a/Main.py b/Main.py index ae356bf..3ebfc26 100644 --- a/Main.py +++ b/Main.py @@ -11,7 +11,7 @@ from serial.tools import list_ports from esptool import ESPROM from argparse import Namespace -__version__ = "0.1.0" +__version__ = "0.2.0" # --------------------------------------------------------------------------- diff --git a/README.md b/README.md index 622892b..4d8d105 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Check the [releases section](https://github.com/marcelstoer/nodemcu-pyflasher/re Scan the [list of open issues](https://github.com/marcelstoer/nodemcu-pyflasher/issues) for bugs and pending features. - Due to [pyinstaller/pyinstaller#2355](https://github.com/pyinstaller/pyinstaller/issues/2355) I can't provide an app bundle for macOS yet. The PyInstaller `.spec` file and the build script are ready, though. -- Due to [esptool/issues/#155](https://github.com/espressif/esptool/issues/155) you'll see "debug" output from esptool.py when it attempts to reconnect to your device. **Note** diff --git a/esptool.py b/esptool.py index 131c634..be178d1 100755 --- a/esptool.py +++ b/esptool.py @@ -33,7 +33,7 @@ import tempfile import time -__version__ = "1.3-dev" +__version__ = "1.3" PYTHON2 = sys.version_info[0] < 3 # True if on pre-Python 3 @@ -133,34 +133,41 @@ class ESPROM(object): """ Try connecting repeatedly until successful, or giving up """ def connect(self): - print('Connecting...') + print('Connecting...', end='') + sys.stdout.flush() + last_error = None - for _ in range(4): - # issue reset-to-bootloader: - # RTS = either CH_PD or nRESET (both active low = chip in reset) - # DTR = GPIO0 (active low = boot to flasher) - self._port.setDTR(False) - self._port.setRTS(True) - time.sleep(0.05) - self._port.setDTR(True) - self._port.setRTS(False) - time.sleep(0.05) - self._port.setDTR(False) - - # worst-case latency timer should be 255ms (probably <20ms) - self._port.timeout = 0.3 + try: for _ in range(4): - try: - self._port.flushInput() - self._slip_reader = slip_reader(self._port) - self._port.flushOutput() - self.sync() - self._port.timeout = 5 - return - except Exception as e: - print("Couldn't connect. [%s; %s]. Retrying..." % (type(e), str(e))) # not sure about this - time.sleep(0.05) - raise FatalError('Failed to connect to ESP8266') + # issue reset-to-bootloader: + # RTS = either CH_PD or nRESET (both active low = chip in reset) + # DTR = GPIO0 (active low = boot to flasher) + self._port.setDTR(False) + self._port.setRTS(True) + time.sleep(0.05) + self._port.setDTR(True) + self._port.setRTS(False) + time.sleep(0.05) + self._port.setDTR(False) + + # worst-case latency timer should be 255ms (probably <20ms) + self._port.timeout = 0.3 + for _ in range(4): + try: + self._port.flushInput() + self._slip_reader = slip_reader(self._port) + self._port.flushOutput() + self.sync() + self._port.timeout = 5 + return + except Exception as e: + print('.', end='') + sys.stdout.flush() + time.sleep(0.05) + last_error = e + finally: + print('') # end 'Connecting...' line + raise FatalError('Failed to connect to ESP8266: %s' % last_error) """ Read memory address in target """ def read_reg(self, addr): @@ -515,7 +522,7 @@ class ELFFile(object): print("Error calling %s, do you have Xtensa toolchain in PATH?" % tool_nm) sys.exit(1) for l in proc.stdout: - fields = l.strip().split() + fields = l.strip().decode("utf-8", "strict").split() try: if fields[0] == "U": print("Warning: ELF binary has undefined symbol %s" % fields[1]) @@ -540,7 +547,7 @@ class ELFFile(object): print("Error calling %s, do you have Xtensa toolchain in PATH?" % tool_readelf) sys.exit(1) for l in proc.stdout: - fields = l.strip().split() + fields = l.strip().decode("utf-8", "strict").split() if fields[0] == "Entry": return int(fields[3], 0) @@ -872,13 +879,25 @@ def detect_flash_size(esp, args): print('Auto-detected Flash size:', args.flash_size) -def write_flash(esp, args): +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] - flash_params = struct.pack(b'BB', flash_mode, flash_size_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: @@ -886,10 +905,7 @@ def write_flash(esp, args): 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') - # Fix sflash config data. - if address == 0 and (image[0] == b'\xe9' or image[0] == 0xE9): # python 2/3 compat - print('Flash params set to 0x%02x%02x' % (flash_mode, flash_size_freq)) - image = image[0:2] + flash_params + image[4:] + 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)) @@ -901,7 +917,7 @@ def write_flash(esp, args): print('Leaving...') if args.verify: print('Verifying just-written flash...') - _verify_flash(flasher, args, flash_params) + _verify_flash(esp, args, flasher) flasher.boot_fw() @@ -1014,13 +1030,17 @@ def read_flash(esp, args): open(args.filename, 'wb').write(data) -def _verify_flash(flasher, args, flash_params=None): +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 - if address == 0 and (image[0] == b'\xe9' or image[0] == 0xE9) and flash_params is not None: - image = image[0:2] + flash_params + image[4:] + + 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)) @@ -1054,8 +1074,7 @@ def _verify_flash(flasher, args, flash_params=None): def verify_flash(esp, args, flash_params=None): - flasher = CesantaFlasher(esp) - _verify_flash(flasher, args, flash_params) + _verify_flash(esp, args) def version(args): @@ -1132,7 +1151,7 @@ def main(): 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 (only necessary if very cautious, data is already CRCed', 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', @@ -1186,6 +1205,7 @@ def main(): 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', diff --git a/images/gui.png b/images/gui.png index 7cdc8fb..806223d 100644 Binary files a/images/gui.png and b/images/gui.png differ