src/eric7/MicroPython/UF2FlashDialog.py

branch
eric7
changeset 11178
52699bca6df9
parent 11176
1b0e4bf80f49
child 11208
f776db7cc222
equal deleted inserted replaced
11177:f511038a0061 11178:52699bca6df9
10 import contextlib 10 import contextlib
11 import os 11 import os
12 import shutil 12 import shutil
13 13
14 from PyQt6.QtCore import QCoreApplication, QEventLoop, Qt, QThread, pyqtSlot 14 from PyQt6.QtCore import QCoreApplication, QEventLoop, Qt, QThread, pyqtSlot
15 from PyQt6.QtSerialPort import QSerialPortInfo
16 from PyQt6.QtWidgets import QDialog, QInputDialog 15 from PyQt6.QtWidgets import QDialog, QInputDialog
17 16
18 from eric7.EricGui import EricPixmapCache 17 from eric7.EricGui import EricPixmapCache
19 from eric7.EricWidgets import EricMessageBox 18 from eric7.EricWidgets import EricMessageBox
20 from eric7.EricWidgets.EricApplication import ericApp 19 from eric7.EricWidgets.EricApplication import ericApp
26 25
27 with contextlib.suppress(ImportError): 26 with contextlib.suppress(ImportError):
28 import usb.core 27 import usb.core
29 28
30 SupportedUF2Boards = { 29 SupportedUF2Boards = {
31 "circuitpython": { 30 "MPy or CPy": {
32 "volumes": { 31 "volumes": {
33 (0x03EB, 0x2402): [ 32 (0x03EB, 0x2402): [
34 ("SAMD21", "SAMD21 Board"), 33 ("SAMD21", "SAMD21 Board"),
35 ("SAME54", "SAME54 Board"), 34 ("SAME54", "SAME54 Board"),
36 ], 35 ],
776 "<li>Select the firmware file to be flashed and click the" 775 "<li>Select the firmware file to be flashed and click the"
777 " flash button.</li>" 776 " flash button.</li>"
778 "</ol>", 777 "</ol>",
779 ), 778 ),
780 "show_all": True, 779 "show_all": True,
781 "firmware": "CircuitPython",
782 },
783 "nrf52": {
784 "volumes": {
785 (0x239A, 0x0029): [
786 ("FTHR840BOOT", "Feather nRF52840 Express"),
787 ("MDK840DONGL", "MDK nRF52840 USB Dongle"),
788 ],
789 (0x239A, 0x0063): [
790 ("NANO33BOOT", "Nano 33 BLE"),
791 ],
792 (0x239A, 0x00DA): [
793 ("XENONBOOT ", "Particle Xenon"),
794 ],
795 (0x2886, 0x0045): [
796 ("XIAO-SENSE", "XIAO nRF52840 Sense"),
797 ],
798 },
799 "instructions": QCoreApplication.translate(
800 "UF2FlashDialog",
801 "<h3>NRF52 Board</h3>"
802 "<p>In order to prepare the board for flashing follow these"
803 " steps:</p><ol>"
804 "<li>Switch your device to 'bootloader' mode by double-pressing"
805 " the reset button.</li>"
806 "<li>Wait until the device has entered 'bootloader' mode.</li>"
807 "<li>(If this does not happen, then try shorter or longer"
808 " pauses between presses.)</li>"
809 "<li>Ensure the boot volume is available (this may require"
810 " mounting it).</li>"
811 "<li>Select the firmware file to be flashed and click the"
812 " flash button.</li>"
813 "</ol>",
814 ),
815 "show_all": True,
816 "firmware": "MicroPython / CircuitPython", 780 "firmware": "MicroPython / CircuitPython",
817 }, 781 },
818 "rp2": { 782 "RP2": {
819 "volumes": { 783 "volumes": {
820 (0x0000, 0x0000): [ 784 (0x2E8A, 0x0003): [
821 ("RPI-RP2", "Raspberry Pi Pico"), # we don't have valid VID/PID 785 ("RPI-RP2", "Raspberry Pi Pico"),
822 ("RP2350", "Raspberry Pi Pico 2"), # we don't have valid VID/PID 786 ],
787 (0x2E8A, 0x000F): [
788 ("RP2350", "Raspberry Pi Pico 2"),
823 ], 789 ],
824 }, 790 },
825 "instructions": QCoreApplication.translate( 791 "instructions": QCoreApplication.translate(
826 "UF2FlashDialog", 792 "UF2FlashDialog",
827 "<h3>Pi Pico (RP2040/RP2350) Board</h3>" 793 "<h3>Pi Pico (RP2040/RP2350) Board</h3>"
891 for board, description, vidpid in foundDevices.copy(): 857 for board, description, vidpid in foundDevices.copy():
892 if not usb.core.find(idVendor=vidpid[0], idProduct=vidpid[1]): 858 if not usb.core.find(idVendor=vidpid[0], idProduct=vidpid[1]):
893 foundDevices.discard((board, description, vidpid)) 859 foundDevices.discard((board, description, vidpid))
894 860
895 # step 2: determine all devices that have their UF2 volume not mounted 861 # step 2: determine all devices that have their UF2 volume not mounted
896 availablePorts = QSerialPortInfo.availablePorts() 862 for device in usb.core.find(find_all=True):
897 for port in availablePorts: 863 vid = device.idVendor
898 vid = port.vendorIdentifier() 864 pid = device.idProduct
899 pid = port.productIdentifier()
900
901 if vid == 0 and pid == 0:
902 # no device detected at port
903 continue
904 865
905 for board in SupportedUF2Boards: 866 for board in SupportedUF2Boards:
906 if (not boardType or (board.startswith(boardType))) and ( 867 if (not boardType or (board.startswith(boardType))) and (
907 vid, 868 vid,
908 pid, 869 pid,
909 ) in SupportedUF2Boards[board]["volumes"]: 870 ) in SupportedUF2Boards[board]["volumes"]:
910 for device in foundDevices: 871 for device in foundDevices:
911 if (vid, pid) == device[2]: 872 if (vid, pid) == device[2]:
912 break 873 break
913 else: 874 else:
875 description = ", ".join(
876 v[1] for v in SupportedUF2Boards[board]["volumes"][(vid, pid)]
877 )
914 foundDevices.add( 878 foundDevices.add(
915 ( 879 (
916 board, 880 board,
917 port.description(), 881 description,
918 (vid, pid), 882 (vid, pid),
919 ) 883 )
920 ) 884 )
921 885
922 return [*foundDevices] 886 return [*foundDevices]
1207 "<h4>Multiple Boot Volumes were found</h4>" 1171 "<h4>Multiple Boot Volumes were found</h4>"
1208 "<p>These volume paths were found.</p><ul><li>{0}</li></ul>" 1172 "<p>These volume paths were found.</p><ul><li>{0}</li></ul>"
1209 "<p>Please ensure that only one device of a type is ready for" 1173 "<p>Please ensure that only one device of a type is ready for"
1210 " flashing. Press <b>Refresh</b> when ready.</p>" 1174 " flashing. Press <b>Refresh</b> when ready.</p>"
1211 ).format("</li><li>".join(sorted(volumePaths))) 1175 ).format("</li><li>".join(sorted(volumePaths)))
1176 self.infoEdit.setHtml(htmlText)
1177
1178 def __showFlashInstruction(self):
1179 """
1180 Private method to show information for the actual flashing process.
1181 """
1182 self.infoLabel.setText(self.tr("Flash Instructions:"))
1183
1184 htmlText = self.tr(
1185 "<h4>Flash selected device.</h4>"
1186 "<p>Follow the instructions below to flash the selected device.</p><ol>"
1187 "<li>Select the firmware file to be flashed.</li>"
1188 "<li>Click the flash button.</li>"
1189 "</ol>"
1190 )
1212 self.infoEdit.setHtml(htmlText) 1191 self.infoEdit.setHtml(htmlText)
1213 1192
1214 @pyqtSlot() 1193 @pyqtSlot()
1215 def on_flashButton_clicked(self): 1194 def on_flashButton_clicked(self):
1216 """ 1195 """
1251 """ 1230 """
1252 Private slot to refresh the dialog. 1231 Private slot to refresh the dialog.
1253 """ 1232 """
1254 self.__populate() 1233 self.__populate()
1255 self.__updateFlashButton() 1234 self.__updateFlashButton()
1256 self.on_devicesComboBox_currentIndexChanged(0)
1257 1235
1258 @pyqtSlot(int) 1236 @pyqtSlot(int)
1259 def on_devicesComboBox_currentIndexChanged(self, index): 1237 def on_devicesComboBox_currentIndexChanged(self, index):
1260 """ 1238 """
1261 Private slot to handle the selection of a board. 1239 Private slot to handle the selection of a board.
1276 self.bootPicker.clear() 1254 self.bootPicker.clear()
1277 else: 1255 else:
1278 volumes = SupportedUF2Boards[boardType]["volumes"][vidpid] 1256 volumes = SupportedUF2Boards[boardType]["volumes"][vidpid]
1279 foundVolumes = [] 1257 foundVolumes = []
1280 for volume, _ in volumes: 1258 for volume, _ in volumes:
1281 foundVolumes += FileSystemUtilities.findVolume( 1259 foundVolumes += FileSystemUtilities.findVolume(volume, findAll=True)
1282 volume, findAll=True, markerFile="INFO_UF2.TXT"
1283 )
1284 foundVolumes = list(set(foundVolumes)) # make entries unique 1260 foundVolumes = list(set(foundVolumes)) # make entries unique
1285 1261
1286 if len(foundVolumes) == 0: 1262 if len(foundVolumes) == 0:
1287 self.__showNoVolumeInformation([v[0] for v in volumes], boardType) 1263 self.__showNoVolumeInformation([v[0] for v in volumes], boardType)
1288 self.bootPicker.clear() 1264 self.bootPicker.clear()
1289 elif len(foundVolumes) == 1: 1265 elif len(foundVolumes) == 1:
1266 self.__showFlashInstruction()
1290 self.bootPicker.setText(foundVolumes[0]) 1267 self.bootPicker.setText(foundVolumes[0])
1291 else: 1268 else:
1292 self.__showMultipleVolumesInformation(foundVolumes) 1269 self.__showMultipleVolumesInformation(foundVolumes)
1293 self.bootPicker.clear() 1270 self.bootPicker.clear()
1294 1271

eric ide

mercurial