src/eric7/MicroPython/Devices/RP2040Devices.py

branch
eric7
changeset 9756
9854647c8c5c
parent 9755
1a09700229e7
child 9763
52f982c08301
equal deleted inserted replaced
9755:1a09700229e7 9756:9854647c8c5c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2021 - 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the device interface class for RP2040 based boards
8 (e.g. Raspberry Pi Pico).
9 """
10
11 from PyQt6.QtCore import QUrl, pyqtSlot
12 from PyQt6.QtNetwork import QNetworkRequest
13 from PyQt6.QtWidgets import QMenu
14
15 from eric7 import Globals, Preferences
16 from eric7.EricWidgets import EricMessageBox
17 from eric7.EricWidgets.EricApplication import ericApp
18
19 from . import FirmwareGithubUrls
20 from .DeviceBase import BaseDevice
21 from ..MicroPythonWidget import HAS_QTCHART
22
23
24 class RP2040Device(BaseDevice):
25 """
26 Class implementing the device for RP2040 based boards.
27 """
28
29 def __init__(self, microPythonWidget, deviceType, parent=None):
30 """
31 Constructor
32
33 @param microPythonWidget reference to the main MicroPython widget
34 @type MicroPythonWidget
35 @param deviceType device type assigned to this device interface
36 @type str
37 @param parent reference to the parent object
38 @type QObject
39 """
40 super().__init__(microPythonWidget, deviceType, parent)
41
42 self.__createRP2040Menu()
43
44 def setButtons(self):
45 """
46 Public method to enable the supported action buttons.
47 """
48 super().setButtons()
49 self.microPython.setActionButtons(
50 run=True, repl=True, files=True, chart=HAS_QTCHART
51 )
52
53 def forceInterrupt(self):
54 """
55 Public method to determine the need for an interrupt when opening the
56 serial connection.
57
58 @return flag indicating an interrupt is needed
59 @rtype bool
60 """
61 return False
62
63 def deviceName(self):
64 """
65 Public method to get the name of the device.
66
67 @return name of the device
68 @rtype str
69 """
70 return self.tr("RP2040")
71
72 def canStartRepl(self):
73 """
74 Public method to determine, if a REPL can be started.
75
76 @return tuple containing a flag indicating it is safe to start a REPL
77 and a reason why it cannot.
78 @rtype tuple of (bool, str)
79 """
80 return True, ""
81
82 def canStartPlotter(self):
83 """
84 Public method to determine, if a Plotter can be started.
85
86 @return tuple containing a flag indicating it is safe to start a
87 Plotter and a reason why it cannot.
88 @rtype tuple of (bool, str)
89 """
90 return True, ""
91
92 def canRunScript(self):
93 """
94 Public method to determine, if a script can be executed.
95
96 @return tuple containing a flag indicating it is safe to start a
97 Plotter and a reason why it cannot.
98 @rtype tuple of (bool, str)
99 """
100 return True, ""
101
102 def runScript(self, script):
103 """
104 Public method to run the given Python script.
105
106 @param script script to be executed
107 @type str
108 """
109 pythonScript = script.split("\n")
110 self.sendCommands(pythonScript)
111
112 def canStartFileManager(self):
113 """
114 Public method to determine, if a File Manager can be started.
115
116 @return tuple containing a flag indicating it is safe to start a
117 File Manager and a reason why it cannot.
118 @rtype tuple of (bool, str)
119 """
120 return True, ""
121
122 def __createRP2040Menu(self):
123 """
124 Private method to create the RO2040 submenu.
125 """
126 self.__rp2040Menu = QMenu(self.tr("RP2040 Functions"))
127
128 self.__showMpyAct = self.__rp2040Menu.addAction(
129 self.tr("Show MicroPython Versions"), self.__showFirmwareVersions
130 )
131 self.__rp2040Menu.addSeparator()
132 self.__bootloaderAct = self.__rp2040Menu.addAction(
133 self.tr("Activate Bootloader"), self.__activateBootloader
134 )
135 self.__flashMpyAct = self.__rp2040Menu.addAction(
136 self.tr("Flash MicroPython Firmware"), self.__flashPython
137 )
138
139 def addDeviceMenuEntries(self, menu):
140 """
141 Public method to add device specific entries to the given menu.
142
143 @param menu reference to the context menu
144 @type QMenu
145 """
146 connected = self.microPython.isConnected()
147 linkConnected = self.microPython.isLinkConnected()
148
149 self.__showMpyAct.setEnabled(connected)
150 self.__bootloaderAct.setEnabled(connected)
151 self.__flashMpyAct.setEnabled(not linkConnected)
152
153 menu.addMenu(self.__rp2040Menu)
154
155 def hasFlashMenuEntry(self):
156 """
157 Public method to check, if the device has its own flash menu entry.
158
159 @return flag indicating a specific flash menu entry
160 @rtype bool
161 """
162 return True
163
164 @pyqtSlot()
165 def __flashPython(self):
166 """
167 Private slot to flash a MicroPython firmware to the device.
168 """
169 from ..UF2FlashDialog import UF2FlashDialog
170
171 dlg = UF2FlashDialog(boardType="rp2040")
172 dlg.exec()
173
174 def __activateBootloader(self):
175 """
176 Private method to switch the board into 'bootloader' mode.
177 """
178 if self.microPython.isConnected():
179 self.microPython.commandsInterface().execute(
180 [
181 "import machine",
182 "machine.bootloader()",
183 ]
184 )
185 # simulate pressing the disconnect button
186 self.microPython.on_connectButton_clicked()
187
188 @pyqtSlot()
189 def __showFirmwareVersions(self):
190 """
191 Private slot to show the firmware version of the connected device and the
192 available firmware version.
193 """
194 if self.microPython.isConnected():
195 if self._deviceData["mpy_name"] != "micropython":
196 EricMessageBox.critical(
197 None,
198 self.tr("Show MicroPython Versions"),
199 self.tr(
200 """The firmware of the connected device cannot be"""
201 """ determined or the board does not run MicroPython."""
202 """ Aborting..."""
203 ),
204 )
205 else:
206 if self._deviceData["mpy_variant"] == "Pimoroni":
207 # MicroPython with Pimoroni add-on libraries
208 url = QUrl(FirmwareGithubUrls["pimoroni_pico"])
209 else:
210 url = QUrl(FirmwareGithubUrls["micropython"])
211 ui = ericApp().getObject("UserInterface")
212 request = QNetworkRequest(url)
213 reply = ui.networkAccessManager().head(request)
214 reply.finished.connect(lambda: self.__firmwareVersionResponse(reply))
215
216 def __firmwareVersionResponse(self, reply):
217 """
218 Private method handling the response of the latest version request.
219
220 @param reply reference to the reply object
221 @type QNetworkReply
222 """
223 latestUrl = reply.url().toString()
224 tag = latestUrl.rsplit("/", 1)[-1]
225 while tag and not tag[0].isdecimal():
226 # get rid of leading non-decimal characters
227 tag = tag[1:]
228 latestVersion = Globals.versionToTuple(tag)
229
230 if self._deviceData["mpy_version"] == "unknown":
231 currentVersionStr = self.tr("unknown")
232 currentVersion = (0, 0, 0)
233 else:
234 currentVersionStr = self._deviceData["mpy_version"]
235 currentVersion = Globals.versionToTuple(currentVersionStr)
236
237 msg = self.tr(
238 "<h4>MicroPython Version Information</h4>"
239 "<table>"
240 "<tr><td>Installed:</td><td>{0}</td><td></td></tr>"
241 "<tr><td>Available:</td><td>{1}</td><td>{2}</td></tr>"
242 "</table>"
243 ).format(
244 currentVersionStr,
245 tag,
246 self.tr("({0})").format(self._deviceData["mpy_variant"])
247 if self._deviceData["mpy_variant"]
248 else "",
249 )
250 if (
251 self._deviceData["mpy_variant"] not in ["Pimoroni"]
252 and currentVersion < latestVersion
253 ):
254 # cannot derive that info for 'Pimoroni' variant
255 msg += self.tr("<p><b>Update available!</b></p>")
256
257 EricMessageBox.information(
258 None,
259 self.tr("MicroPython Version"),
260 msg,
261 )
262
263 def getDocumentationUrl(self):
264 """
265 Public method to get the device documentation URL.
266
267 @return documentation URL of the device
268 @rtype str
269 """
270 return Preferences.getMicroPython("MicroPythonDocuUrl")
271
272 def getDownloadMenuEntries(self):
273 """
274 Public method to retrieve the entries for the downloads menu.
275
276 @return list of tuples with menu text and URL to be opened for each
277 entry
278 @rtype list of tuple of (str, str)
279 """
280 return [
281 (
282 self.tr("MicroPython Firmware"),
283 Preferences.getMicroPython("MicroPythonFirmwareUrl"),
284 ),
285 ("<separator>", ""),
286 (self.tr("Pimoroni Pico Firmware"), FirmwareGithubUrls["pimoroni_pico"]),
287 ("<separator>", ""),
288 (
289 self.tr("CircuitPython Firmware"),
290 Preferences.getMicroPython("CircuitPythonFirmwareUrl"),
291 ),
292 (
293 self.tr("CircuitPython Libraries"),
294 Preferences.getMicroPython("CircuitPythonLibrariesUrl"),
295 ),
296 ]
297
298
299 def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber):
300 """
301 Function to instantiate a MicroPython device object.
302
303 @param microPythonWidget reference to the main MicroPython widget
304 @type MicroPythonWidget
305 @param deviceType device type assigned to this device interface
306 @type str
307 @param vid vendor ID
308 @type int
309 @param pid product ID
310 @type int
311 @param boardName name of the board
312 @type str
313 @param serialNumber serial number of the board
314 @type str
315 @return reference to the instantiated device object
316 @rtype RP2040Device
317 """
318 return RP2040Device(microPythonWidget, deviceType)

eric ide

mercurial