1313
1414Pyinstaller:
1515Windows:
16- pyinstaller --onefile --clean --noconsole --distpath=./Windows_exe --icon=RTK.ico --add-binary="RTK_Surveyor.ino.partitions .bin;." --add-binary="RTK_Surveyor.ino.bootloader.bin;." --add-binary="boot_app0.bin;." --add-binary="RTK.png;." RTK_Firmware_Uploader_GUI.py
16+ pyinstaller --onefile --clean --noconsole --distpath=./Windows_exe --icon=RTK.ico --add-binary="RTK_Surveyor_Partitions_4MB.bin;." --add-binary="RTK_Surveyor_Partitions_16MB .bin;." --add-binary="RTK_Surveyor.ino.bootloader.bin;." --add-binary="boot_app0.bin;." --add-binary="RTK.png;." RTK_Firmware_Uploader_GUI.py
1717Linux:
18- pyinstaller --onefile --clean --noconsole --distpath=./Linux_exe --icon=RTK.ico --add-binary="RTK_Surveyor.ino.partitions .bin:." --add-binary="RTK_Surveyor.ino.bootloader.bin:." --add-binary="boot_app0.bin:." --add-binary="RTK.png:." RTK_Firmware_Uploader_GUI.py
18+ pyinstaller --onefile --clean --noconsole --distpath=./Linux_exe --icon=RTK.ico --add-binary="RTK_Surveyor_Partitions_4MB.bin:." --add-binary="RTK_Surveyor_Partitions_16MB .bin:." --add-binary="RTK_Surveyor.ino.bootloader.bin:." --add-binary="boot_app0.bin:." --add-binary="RTK.png:." RTK_Firmware_Uploader_GUI.py
1919
2020Pyinstaller needs:
2121RTK_Firmware_Uploader_GUI.py (this file!)
2222RTK.ico (icon file for the .exe)
2323RTK.png (icon for the GUI widget)
2424esptool.py (v3.3, copied from https://github.com/espressif/esptool/releases/tag/v3.3)
25- RTK_Surveyor.ino.partitions.bin
25+ RTK_Surveyor_Partitions_4MB.bin
26+ RTK_Surveyor_Partitions_16MB.bin
2627RTK_Surveyor.ino.bootloader.bin
2728boot_app0.bin
2829
3536
3637from typing import Iterator , Tuple
3738
38- from PyQt5 .QtCore import QSettings , QProcess , QTimer , Qt , QIODevice , pyqtSlot
39+ from PyQt5 .QtCore import QSettings , QProcess , QTimer , Qt , QIODevice , pyqtSlot , QObject
3940from PyQt5 .QtWidgets import QWidget , QLabel , QComboBox , QGridLayout , \
4041 QPushButton , QApplication , QLineEdit , QFileDialog , QPlainTextEdit , \
4142 QAction , QActionGroup , QMenu , QMenuBar , QMainWindow , QMessageBox
4243from PyQt5 .QtGui import QCloseEvent , QTextCursor , QIcon , QFont
43- from PyQt5 .QtSerialPort import QSerialPortInfo , QSerialPortInfo
44+ from PyQt5 .QtSerialPort import QSerialPortInfo
4445
4546import sys
4647import os
5556# Setting constants
5657SETTING_PORT_NAME = 'port_name'
5758SETTING_FILE_LOCATION = 'file_location'
59+ #SETTING_PARTITION_LOCATION = 'partition_location'
5860SETTING_BAUD_RATE = 'baud'
5961
60- guiVersion = 'v1.2 '
62+ guiVersion = 'v1.3 '
6163
6264def gen_serial_ports () -> Iterator [Tuple [str , str , str ]]:
6365 """Return all available serial ports."""
@@ -70,12 +72,13 @@ def resource_path(relative_path):
7072 base_path = getattr (sys , '_MEIPASS' , os .path .dirname (os .path .abspath (__file__ )))
7173 return os .path .join (base_path , relative_path )
7274
73- class messageRedirect :
75+ class messageRedirect ( QObject ) :
7476 """Wrap a class around a QPlainTextEdit so we can redirect stdout and stderr to it"""
7577
76- def __init__ (self , edit , out = None ) -> None :
78+ def __init__ (self , edit , out = None , flashSize = None ) -> None :
7779 self .edit = edit
7880 self .out = out
81+ self .flashSize = flashSize
7982
8083 def write (self , msg ) -> None :
8184 if msg .startswith ("\r " ):
@@ -86,9 +89,21 @@ def write(self, msg) -> None:
8689 self .edit .insertPlainText (msg )
8790 self .edit .ensureCursorVisible ()
8891 self .edit .repaint ()
92+ QApplication .processEvents () # This prevents the circle of doom...
93+
8994 if self .out : # Echo to out (stdout) too if desired
9095 self .out .write (msg )
9196
97+ if self .flashSize :
98+ if msg .find ("Detected flash size: 4MB" ) >= 0 :
99+ self .flashSize [0 ] = 4
100+ elif msg .find ("Detected flash size: 8MB" ) >= 0 :
101+ self .flashSize [0 ] = 8
102+ elif msg .find ("Detected flash size: 16MB" ) >= 0 :
103+ self .flashSize [0 ] = 16
104+ elif msg .find ("Detected flash size: " ) >= 0 :
105+ self .flashSize [0 ] = 0
106+
92107 def flush (self ) -> None :
93108 None
94109
@@ -103,13 +118,12 @@ class MainWidget(QWidget):
103118 def __init__ (self , parent : QWidget = None ) -> None :
104119 super ().__init__ (parent )
105120
106- self .timer = QTimer ()
107- self .timer .timeout .connect (self .repaintMessageBox )
121+ self .flashSize = [0 ] # flashSize needs to be mutable. Use a single element list
108122
109123 # File location line edit
110- self .msg_label = QLabel (self .tr ('Firmware File:' ))
124+ self .file_label = QLabel (self .tr ('Firmware File:' ))
111125 self .fileLocation_lineedit = QLineEdit ()
112- self .msg_label .setBuddy (self .fileLocation_lineedit )
126+ self .file_label .setBuddy (self .fileLocation_lineedit )
113127 self .fileLocation_lineedit .setEnabled (False )
114128 self .fileLocation_lineedit .returnPressed .connect (self .on_browse_btn_pressed )
115129
@@ -118,6 +132,18 @@ def __init__(self, parent: QWidget = None) -> None:
118132 self .browse_btn .setEnabled (True )
119133 self .browse_btn .pressed .connect (self .on_browse_btn_pressed )
120134
135+ # # Partition file location line edit
136+ # self.partition_label = QLabel(self.tr('Partition File:'))
137+ # self.partitionFileLocation_lineedit = QLineEdit()
138+ # self.partition_label.setBuddy(self.partitionFileLocation_lineedit)
139+ # self.partitionFileLocation_lineedit.setEnabled(False)
140+ # self.partitionFileLocation_lineedit.returnPressed.connect(self.on_partition_browse_btn_pressed)
141+
142+ # # Browse for new file button
143+ # self.partition_browse_btn = QPushButton(self.tr('Browse'))
144+ # self.partition_browse_btn.setEnabled(True)
145+ # self.partition_browse_btn.pressed.connect(self.on_partition_browse_btn_pressed)
146+
121147 # Port Combobox
122148 self .port_label = QLabel (self .tr ('COM Port:' ))
123149 self .port_combobox = QComboBox ()
@@ -152,10 +178,14 @@ def __init__(self, parent: QWidget = None) -> None:
152178 # Arrange Layout
153179 layout = QGridLayout ()
154180
155- layout .addWidget (self .msg_label , 1 , 0 )
181+ layout .addWidget (self .file_label , 1 , 0 )
156182 layout .addWidget (self .fileLocation_lineedit , 1 , 1 )
157183 layout .addWidget (self .browse_btn , 1 , 2 )
158184
185+ # layout.addWidget(self.partition_label, 2, 0)
186+ # layout.addWidget(self.partitionFileLocation_lineedit, 2, 1)
187+ # layout.addWidget(self.partition_browse_btn, 2, 2)
188+
159189 layout .addWidget (self .port_label , 2 , 0 )
160190 layout .addWidget (self .port_combobox , 2 , 1 )
161191 layout .addWidget (self .refresh_btn , 2 , 2 )
@@ -175,20 +205,11 @@ def __init__(self, parent: QWidget = None) -> None:
175205
176206 def writeMessage (self , msg ) -> None :
177207 self .messageBox .moveCursor (QTextCursor .End )
178- self .messageBox .ensureCursorVisible ()
208+ # self.messageBox.ensureCursorVisible()
179209 self .messageBox .appendPlainText (msg )
180210 self .messageBox .ensureCursorVisible ()
181211 self .messageBox .repaint ()
182-
183- def startTimer (self ) -> None :
184- self .timer .start (1000 )
185-
186- def endTimer (self ) -> None :
187- self .timer .stop ()
188-
189- def repaintMessageBox (self ) -> None :
190- self .messageBox .ensureCursorVisible ()
191- self .messageBox .repaint ()
212+ QApplication .processEvents ()
192213
193214 def _load_settings (self ) -> None :
194215 """Load settings on startup."""
@@ -202,6 +223,12 @@ def _load_settings(self) -> None:
202223 if lastFile is not None :
203224 self .fileLocation_lineedit .setText (lastFile )
204225
226+ # lastFile = self.settings.value(SETTING_PARTITION_LOCATION)
227+ # if lastFile is not None:
228+ # self.partitionFileLocation_lineedit.setText(lastFile)
229+ # else:
230+ # self.partitionFileLocation_lineedit.setText(resource_path("RTK_Surveyor.ino.partitions.bin"))
231+
205232 baud = self .settings .value (SETTING_BAUD_RATE )
206233 if baud is not None :
207234 index = self .baud_combobox .findData (baud )
@@ -212,6 +239,7 @@ def _save_settings(self) -> None:
212239 """Save settings on shutdown."""
213240 self .settings .setValue (SETTING_PORT_NAME , self .port )
214241 self .settings .setValue (SETTING_FILE_LOCATION , self .theFileName )
242+ # self.settings.setValue(SETTING_PARTITION_LOCATION, self.thePartitionFileName)
215243 self .settings .setValue (SETTING_BAUD_RATE , self .baudRate )
216244
217245 def _clean_settings (self ) -> None :
@@ -264,15 +292,18 @@ def theFileName(self) -> str:
264292 """Return the file name."""
265293 return self .fileLocation_lineedit .text ()
266294
295+ # @property
296+ # def thePartitionFileName(self) -> str:
297+ # """Return the partition file name."""
298+ # return self.partitionFileLocation_lineedit.text()
299+
267300 def closeEvent (self , event : QCloseEvent ) -> None :
268301 """Handle Close event of the Widget."""
269302 try :
270303 self ._save_settings ()
271304 except :
272305 pass
273306
274- self .endTimer ()
275-
276307 event .accept ()
277308
278309 def on_refresh_btn_pressed (self ) -> None :
@@ -291,6 +322,18 @@ def on_browse_btn_pressed(self) -> None:
291322 if fileName :
292323 self .fileLocation_lineedit .setText (fileName )
293324
325+ # def on_partition_browse_btn_pressed(self) -> None:
326+ # """Open dialog to select partition bin file."""
327+ # options = QFileDialog.Options()
328+ # fileName, _ = QFileDialog.getOpenFileName(
329+ # None,
330+ # "Select Partition File",
331+ # "",
332+ # "Parition Files (*.bin);;All Files (*)",
333+ # options=options)
334+ # if fileName:
335+ # self.partitionFileLocation_lineedit.setText(fileName)
336+
294337 def on_upload_btn_pressed (self ) -> None :
295338 """Upload the firmware"""
296339 portAvailable = False
@@ -303,7 +346,7 @@ def on_upload_btn_pressed(self) -> None:
303346
304347 fileExists = False
305348 try :
306- f = open (self .fileLocation_lineedit . text () )
349+ f = open (self .theFileName )
307350 fileExists = True
308351 except IOError :
309352 fileExists = False
@@ -313,11 +356,67 @@ def on_upload_btn_pressed(self) -> None:
313356 return
314357 f .close ()
315358
359+ # fileExists = False
360+ # try:
361+ # f = open(self.thePartitionFileName)
362+ # fileExists = True
363+ # except IOError:
364+ # fileExists = False
365+ # finally:
366+ # if (fileExists == False):
367+ # self.writeMessage("File Not Found")
368+ # return
369+ # f.close()
370+
316371 try :
317372 self ._save_settings () # Save the settings in case the upload crashes
318373 except :
319374 pass
320375
376+ self .flashSize [0 ] = 0
377+
378+ self .writeMessage ("Detecting flash size\n \n " )
379+
380+ command = []
381+ command .extend (["--chip" ,"esp32" ])
382+ command .extend (["--port" ,self .port ])
383+ command .extend (["--baud" ,self .baudRate ])
384+ command .extend (["flash_id" ])
385+
386+ try :
387+ esptool .main (command )
388+ except (ValueError , IOError , FatalError , ImportError , NotImplementedInROMError , UnsupportedCommandError , NotSupportedError , RuntimeError ) as err :
389+ self .writeMessage (str (err ))
390+ self .messageBox .ensureCursorVisible ()
391+ self .messageBox .repaint ()
392+ return
393+ except :
394+ self .messageBox .ensureCursorVisible ()
395+ self .messageBox .repaint ()
396+ return
397+
398+ if self .flashSize [0 ] == 0 :
399+ self .writeMessage ("Flash size not detected! Defaulting to 16MB\n " )
400+ self .flashSize [0 ] = 16
401+ else :
402+ self .writeMessage ("Flash size is " + str (self .flashSize [0 ]) + "MB\n " )
403+
404+ thePartitionFileName = ''
405+ firmwareSizeCorrect = True
406+ if self .flashSize [0 ] == 16 :
407+ thePartitionFileName = resource_path ("RTK_Surveyor_Partitions_16MB.bin" )
408+ # if self.theFileName.find("16MB") < 0:
409+ # firmwareSizeCorrect = False
410+ else :
411+ thePartitionFileName = resource_path ("RTK_Surveyor_Partitions_4MB.bin" )
412+ # if self.theFileName.find("4MB") < 0:
413+ # firmwareSizeCorrect = False
414+
415+ if firmwareSizeCorrect == False :
416+ reply = QMessageBox .warning (self , "Firmware size mismatch" , "Do you want to continue?" , QMessageBox .Yes | QMessageBox .No , QMessageBox .No )
417+ if reply == QMessageBox .No :
418+ return
419+
321420 self .writeMessage ("Uploading firmware\n " )
322421
323422 command = []
@@ -327,22 +426,23 @@ def on_upload_btn_pressed(self) -> None:
327426 command .extend (["--baud" ,self .baudRate ])
328427 command .extend (["--before" ,"default_reset" ,"--after" ,"hard_reset" ,"write_flash" ,"-z" ,"--flash_mode" ,"dio" ,"--flash_freq" ,"80m" ,"--flash_size" ,"detect" ])
329428 command .extend (["0x1000" ,resource_path ("RTK_Surveyor.ino.bootloader.bin" )])
330- command .extend (["0x8000" ,resource_path ( "RTK_Surveyor.ino.partitions.bin" ) ])
429+ command .extend (["0x8000" ,thePartitionFileName ])
331430 command .extend (["0xe000" ,resource_path ("boot_app0.bin" )])
332431 command .extend (["0x10000" ,self .theFileName ])
333432
334433 self .writeMessage ("Command: esptool.main(%s)\n \n " % " " .join (command ))
335434
336435 #print("python esptool.py %s\n\n" % " ".join(command)) # Useful for debugging - cut and paste into a command prompt
337436
338- self .startTimer ()
339-
340437 try :
341438 esptool .main (command )
342439 except (ValueError , IOError , FatalError , ImportError , NotImplementedInROMError , UnsupportedCommandError , NotSupportedError , RuntimeError ) as err :
343440 self .writeMessage (str (err ))
441+ except :
442+ pass
344443
345- self .endTimer ()
444+ self .messageBox .ensureCursorVisible ()
445+ self .messageBox .repaint ()
346446
347447if __name__ == '__main__' :
348448 from sys import exit as sysExit
@@ -352,10 +452,10 @@ def on_upload_btn_pressed(self) -> None:
352452 app .setWindowIcon (QIcon (resource_path ("RTK.png" )))
353453 w = MainWidget ()
354454 if 1 : # Change to 0 to have the messages echoed on stdout
355- sys .stdout = messageRedirect (w .messageBox ) # Divert stdout to messageBox
455+ sys .stdout = messageRedirect (w .messageBox , flashSize = w . flashSize ) # Divert stdout to messageBox. Report flash size via flashSize
356456 sys .stderr = messageRedirect (w .messageBox ) # Divert stderr to messageBox
357457 else :
358- sys .stdout = messageRedirect (w .messageBox , sys .stdout ) # Echo to stdout too
359- sys .stderr = messageRedirect (w .messageBox , sys .stderr ) # Echo to stderr too
458+ sys .stdout = messageRedirect (w .messageBox , flashSize = w . flashSize , out = sys .stdout ) # Echo to stdout too
459+ sys .stderr = messageRedirect (w .messageBox , out = sys .stderr ) # Echo to stderr too
360460 w .show ()
361461 sys .exit (app .exec_ ())
0 commit comments