From f12d6267b477a6a7eb3db3a749b51946248ad9f9 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 5 Nov 2023 17:55:06 +0100 Subject: [PATCH] reorganize flash tool --- tools/flashtool/flasher.py | 124 ++++++++++++++++++++++++++++ tools/flashtool/flashtool.py | 156 ++++++++--------------------------- tools/flashtool/version.py | 1 + 3 files changed, 158 insertions(+), 123 deletions(-) create mode 100644 tools/flashtool/flasher.py create mode 100644 tools/flashtool/version.py diff --git a/tools/flashtool/flasher.py b/tools/flashtool/flasher.py new file mode 100644 index 0000000..db2708f --- /dev/null +++ b/tools/flashtool/flasher.py @@ -0,0 +1,124 @@ + +import flashtool.esptool as esptool +import os +from flashtool.version import VERSION + +class Flasher(): + def getVersion(self): + return ("Version %s, esptool %s"%(VERSION,str(esptool.__version__))) + + UPDATE_ADDR = 0x10000 + HDROFFSET = 288 + VERSIONOFFSET = 16 + NAMEOFFSET = 48 + IDOFFSET=12 #2 byte chipid + MINSIZE = HDROFFSET + NAMEOFFSET + 32 + CHECKBYTES = { + 288: 0x32, # app header magic + 289: 0x54, + 290: 0xcd, + 291: 0xab + } + #flash addresses for full images based on chip id + FLASH_ADDR={ + 0: 0x1000, + 9: 0 + } + def getString(self,buffer, offset, len): + return buffer[offset:offset + len].rstrip(b'\0').decode('utf-8') + def getFirmwareInfo(self,filename,isFull): + with open(filename,"rb") as ih: + buffer = ih.read(self.MINSIZE) + if len(buffer) != self.MINSIZE: + return self.setErr("invalid image file %s, to short"%filename) + if buffer[0] != 0xe9: + return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) + chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] + flashoffset=self.FLASH_ADDR.get(chipid) + if flashoffset is None: + return self.setErr("unknown chip id in image %d",chipid); + if isFull: + offset=self.UPDATE_ADDR-flashoffset; + offset-=self.MINSIZE + ih.seek(offset,os.SEEK_CUR) + buffer=ih.read(self.MINSIZE) + if len(buffer) != self.MINSIZE: + return self.setErr("invalid image file %s, to short"%filename) + if buffer[0] != 0xe9: + return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) + for k, v in self.CHECKBYTES.items(): + if buffer[k] != v: + return self.setErr("invalid magic at %d, expected %d got %d" + % (k+offset, v, buffer[k])) + name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32) + version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32) + chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] + flashoffset=flashoffset if isFull else self.UPDATE_ADDR + return { + 'error':False, + 'info':"%s:%s"%(name,version), + 'chipid':chipid, + 'flashbase':flashoffset + } + def setErr(self,err): + return {'error':True,'info':err} + def checkImageFile(self,filename,isFull): + if not os.path.exists(filename): + return self.setErr("file %s not found"%filename) + return self.getFirmwareInfo(filename,isFull) + + def checkSettings(self,port,fileName,isFull): + if port is None: + print("ERROR: no com port selected") + return + if fileName is None or fileName == '': + print("ERROR: no filename selected") + return + info = self.checkImageFile(fileName, isFull) + if info['error']: + print("ERROR: %s" % info['info']) + return + return {'fileName': fileName,'port':port,'isFull':isFull,'info':info} + def runEspTool(self,command): + print("run esptool: %s" % " ".join(command)) + try: + esptool.main(command) + print("esptool done") + return True + except Exception as e: + print("Exception in esptool %s" % e) + def verifyChip(self,param): + if not param: + print("check failed") + return + imageChipId=param['info']['chipid'] + chip=esptool.ESPLoader.detect_chip(param['port']) + print("Detected chip %s, id=%d"%(chip.CHIP_NAME,chip.IMAGE_CHIP_ID)) + if (chip.IMAGE_CHIP_ID != imageChipId): + print("##Error: chip id in image %d does not match detected chip"%imageChipId) + return + print("Checks OK") + param['chipname']=chip.CHIP_NAME + return param + def runCheck(self,port,fileName,isFull): + param = self.checkSettings(port,fileName,isFull) + if not param: + return + print("Settings OK") + param=self.verifyChip(param) + if not param: + print("Check Failed") + return + print("flashbase=0x%x"%param['info']['flashbase']) + return param + def runFlash(self,param): + if not param: + return + if param['isFull']: + command=['--chip',param['chipname'],'--port',param['port'],'write_flash',str(param['info']['flashbase']),param['fileName']] + self.runEspTool(command) + else: + command=['--chip',param['chipname'],'--port',param['port'],'erase_region','0xe000','0x2000'] + self.runEspTool(command) + command = ['--chip', param['chipname'], '--port', param['port'], 'write_flash', str(param['info']['flashbase']), param['fileName']] + self.runEspTool(command) \ No newline at end of file diff --git a/tools/flashtool/flashtool.py b/tools/flashtool/flashtool.py index 9f0c048..e995478 100755 --- a/tools/flashtool/flashtool.py +++ b/tools/flashtool/flashtool.py @@ -1,6 +1,38 @@ #! /usr/bin/env python3 +import builtins import subprocess import sys +import os +import importlib.abc +import importlib.util +import types + + +''' +Inject a base package for our current directory +''' +class MyLoader(importlib.abc.InspectLoader): + def is_package(self, fullname: str) -> bool: + return True + def get_source(self, fullname: str): + return None + def get_code(self, fullname: str): + return "" +class MyFinder(importlib.abc.MetaPathFinder): + def __init__(self,baspkg,basedir=os.path.dirname(__file__),debug=False): + self.pkg=baspkg + self.dir=basedir + self.debug=debug + def find_spec(self,fullname, path, target=None): + if fullname == self.pkg: + if self.debug: + print("F:matching %"%fullname) + spec=importlib.util.spec_from_file_location(fullname, self.dir,loader=MyLoader(), submodule_search_locations=[self.dir]) + if self.debug: + print("F:injecting:",spec) + return spec +sys.meta_path.insert(0,MyFinder('flashtool')) + try: import serial @@ -16,130 +48,8 @@ import tkinter.font as tkFont import os import serial.tools.list_ports from tkinter import filedialog as FileDialog +from flashtool.flasher import Flasher -import builtins -import esptool - -VERSION="Version 2.0" -class Flasher(): - def getVersion(self): - return ("Version %s, esptool %s"%(VERSION,str(esptool.__version__))) - - UPDATE_ADDR = 0x10000 - HDROFFSET = 288 - VERSIONOFFSET = 16 - NAMEOFFSET = 48 - IDOFFSET=12 #2 byte chipid - MINSIZE = HDROFFSET + NAMEOFFSET + 32 - CHECKBYTES = { - 288: 0x32, # app header magic - 289: 0x54, - 290: 0xcd, - 291: 0xab - } - #flash addresses for full images based on chip id - FLASH_ADDR={ - 0: 0x1000, - 9: 0 - } - def getString(self,buffer, offset, len): - return buffer[offset:offset + len].rstrip(b'\0').decode('utf-8') - def getFirmwareInfo(self,filename,isFull): - with open(filename,"rb") as ih: - buffer = ih.read(self.MINSIZE) - if len(buffer) != self.MINSIZE: - return self.setErr("invalid image file %s, to short"%filename) - if buffer[0] != 0xe9: - return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) - chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] - flashoffset=self.FLASH_ADDR.get(chipid) - if flashoffset is None: - return self.setErr("unknown chip id in image %d",chipid); - if isFull: - offset=self.UPDATE_ADDR-flashoffset; - offset-=self.MINSIZE - ih.seek(offset,os.SEEK_CUR) - buffer=ih.read(self.MINSIZE) - if len(buffer) != self.MINSIZE: - return self.setErr("invalid image file %s, to short"%filename) - if buffer[0] != 0xe9: - return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) - for k, v in self.CHECKBYTES.items(): - if buffer[k] != v: - return self.setErr("invalid magic at %d, expected %d got %d" - % (k+offset, v, buffer[k])) - name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32) - version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32) - chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] - flashoffset=flashoffset if isFull else self.UPDATE_ADDR - return { - 'error':False, - 'info':"%s:%s"%(name,version), - 'chipid':chipid, - 'flashbase':flashoffset - } - def setErr(self,err): - return {'error':True,'info':err} - def checkImageFile(self,filename,isFull): - if not os.path.exists(filename): - return self.setErr("file %s not found"%filename) - return self.getFirmwareInfo(filename,isFull) - - def checkSettings(self,port,fileName,isFull): - if port is None: - print("ERROR: no com port selected") - return - if fileName is None or fileName == '': - print("ERROR: no filename selected") - return - info = self.checkImageFile(fileName, isFull) - if info['error']: - print("ERROR: %s" % info['info']) - return - return {'fileName': fileName,'port':port,'isFull':isFull,'info':info} - def runEspTool(self,command): - print("run esptool: %s" % " ".join(command)) - try: - esptool.main(command) - print("esptool done") - return True - except Exception as e: - print("Exception in esptool %s" % e) - def verifyChip(self,param): - if not param: - print("check failed") - return - imageChipId=param['info']['chipid'] - chip=esptool.ESPLoader.detect_chip(param['port']) - print("Detected chip %s, id=%d"%(chip.CHIP_NAME,chip.IMAGE_CHIP_ID)) - if (chip.IMAGE_CHIP_ID != imageChipId): - print("##Error: chip id in image %d does not match detected chip"%imageChipId) - return - print("Checks OK") - param['chipname']=chip.CHIP_NAME - return param - def runCheck(self,port,fileName,isFull): - param = self.checkSettings(port,fileName,isFull) - if not param: - return - print("Settings OK") - param=self.verifyChip(param) - if not param: - print("Check Failed") - return - print("flashbase=0x%x"%param['info']['flashbase']) - return param - def runFlash(self,param): - if not param: - return - if param['isFull']: - command=['--chip',param['chipname'],'--port',param['port'],'write_flash',str(param['info']['flashbase']),param['fileName']] - self.runEspTool(command) - else: - command=['--chip',param['chipname'],'--port',param['port'],'erase_region','0xe000','0x2000'] - self.runEspTool(command) - command = ['--chip', param['chipname'], '--port', param['port'], 'write_flash', str(param['info']['flashbase']), param['fileName']] - self.runEspTool(command) def main(): diff --git a/tools/flashtool/version.py b/tools/flashtool/version.py new file mode 100644 index 0000000..ceda338 --- /dev/null +++ b/tools/flashtool/version.py @@ -0,0 +1 @@ +VERSION="2.1" \ No newline at end of file