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