Sat, 23 Dec 2023 15:48:12 +0100
Updated copyright for 2024.
9755 | 1 | # -*- coding: utf-8 -*- |
2 | ||
10439
21c28b0f9e41
Updated copyright for 2024.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10069
diff
changeset
|
3 | # Copyright (c) 2023 - 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
9755 | 4 | # |
5 | ||
6 | """ | |
7 | Module implementing the device interface class for Teensy boards with MicroPython. | |
8 | """ | |
9 | ||
9820 | 10 | from PyQt6.QtCore import QCoreApplication, QProcess, QUrl, pyqtSlot |
11 | from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest | |
9755 | 12 | from PyQt6.QtWidgets import QMenu |
13 | ||
14 | from eric7 import Globals, Preferences | |
15 | from eric7.EricWidgets import EricMessageBox | |
16 | from eric7.EricWidgets.EricApplication import ericApp | |
17 | ||
9765
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
18 | from ..MicroPythonWidget import HAS_QTCHART |
9756
9854647c8c5c
Reorganized the MicroPython package.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9755
diff
changeset
|
19 | from . import FirmwareGithubUrls |
9854647c8c5c
Reorganized the MicroPython package.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9755
diff
changeset
|
20 | from .DeviceBase import BaseDevice |
9755 | 21 | |
22 | ||
9756
9854647c8c5c
Reorganized the MicroPython package.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9755
diff
changeset
|
23 | class TeensyDevice(BaseDevice): |
9755 | 24 | """ |
25 | Class implementing the device for Teensy boards with MicroPython. | |
26 | """ | |
27 | ||
28 | def __init__(self, microPythonWidget, deviceType, parent=None): | |
29 | """ | |
30 | Constructor | |
31 | ||
32 | @param microPythonWidget reference to the main MicroPython widget | |
33 | @type MicroPythonWidget | |
34 | @param deviceType device type assigned to this device interface | |
35 | @type str | |
36 | @param parent reference to the parent object | |
37 | @type QObject | |
38 | """ | |
39 | super().__init__(microPythonWidget, deviceType, parent) | |
40 | ||
41 | self.__createTeensyMenu() | |
42 | ||
43 | def setButtons(self): | |
44 | """ | |
45 | Public method to enable the supported action buttons. | |
46 | """ | |
47 | super().setButtons() | |
9763
52f982c08301
Removed the 'Open' and 'Save' buttons from the MicroPython widget and made the repl and file manager start automatically upon connecting to the selected device.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9756
diff
changeset
|
48 | |
9755 | 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("Teensy") | |
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 getDocumentationUrl(self): | |
123 | """ | |
124 | Public method to get the device documentation URL. | |
125 | ||
126 | @return documentation URL of the device | |
127 | @rtype str | |
128 | """ | |
129 | return Preferences.getMicroPython("MicroPythonDocuUrl") | |
130 | ||
131 | def getFirmwareUrl(self): | |
132 | """ | |
133 | Public method to get the device firmware download URL. | |
134 | ||
135 | @return firmware download URL of the device | |
136 | @rtype str | |
137 | """ | |
138 | return Preferences.getMicroPython("MicroPythonFirmwareUrl") | |
139 | ||
140 | def __createTeensyMenu(self): | |
141 | """ | |
142 | Private method to create the microbit submenu. | |
143 | """ | |
144 | self.__teensyMenu = QMenu(self.tr("Teensy Functions")) | |
145 | ||
146 | self.__showMpyAct = self.__teensyMenu.addAction( | |
147 | self.tr("Show MicroPython Versions"), self.__showFirmwareVersions | |
148 | ) | |
149 | self.__teensyMenu.addSeparator() | |
150 | self.__teensyMenu.addAction( | |
9820 | 151 | self.tr("MicroPython Flash Instructions"), showTeensyFlashInstructions |
9755 | 152 | ) |
153 | self.__flashMpyAct = self.__teensyMenu.addAction( | |
9820 | 154 | self.tr("Flash MicroPython Firmware"), startTeensyLoader |
9755 | 155 | ) |
156 | self.__flashMpyAct.setToolTip( | |
157 | self.tr("Start the 'Teensy Loader' application to flash the Teensy device.") | |
158 | ) | |
9927 | 159 | self.__teensyMenu.addSeparator() |
160 | self.__resetAct = self.__teensyMenu.addAction( | |
161 | self.tr("Reset Device"), self.__resetDevice | |
162 | ) | |
9755 | 163 | |
164 | def addDeviceMenuEntries(self, menu): | |
165 | """ | |
166 | Public method to add device specific entries to the given menu. | |
167 | ||
168 | @param menu reference to the context menu | |
169 | @type QMenu | |
170 | """ | |
171 | connected = self.microPython.isConnected() | |
172 | linkConnected = self.microPython.isLinkConnected() | |
173 | ||
174 | self.__showMpyAct.setEnabled(connected) | |
175 | self.__flashMpyAct.setEnabled(not linkConnected) | |
9927 | 176 | self.__resetAct.setEnabled(connected) |
9755 | 177 | |
178 | menu.addMenu(self.__teensyMenu) | |
179 | ||
180 | @pyqtSlot() | |
181 | def __showFirmwareVersions(self): | |
182 | """ | |
183 | Private slot to show the firmware version of the connected device and the | |
184 | available firmware version. | |
185 | """ | |
186 | if self.microPython.isConnected(): | |
187 | if self._deviceData["mpy_name"] != "micropython": | |
188 | EricMessageBox.critical( | |
189 | None, | |
190 | self.tr("Show MicroPython Versions"), | |
191 | self.tr( | |
192 | """The firmware of the connected device cannot be""" | |
193 | """ determined or the board does not run MicroPython.""" | |
194 | """ Aborting...""" | |
195 | ), | |
196 | ) | |
197 | else: | |
198 | ui = ericApp().getObject("UserInterface") | |
199 | request = QNetworkRequest(QUrl(FirmwareGithubUrls["micropython"])) | |
200 | reply = ui.networkAccessManager().head(request) | |
201 | reply.finished.connect(lambda: self.__firmwareVersionResponse(reply)) | |
202 | ||
9820 | 203 | @pyqtSlot(QNetworkReply) |
9755 | 204 | def __firmwareVersionResponse(self, reply): |
205 | """ | |
9820 | 206 | Private slot handling the response of the latest version request. |
9755 | 207 | |
208 | @param reply reference to the reply object | |
209 | @type QNetworkReply | |
210 | """ | |
211 | latestUrl = reply.url().toString() | |
212 | tag = latestUrl.rsplit("/", 1)[-1] | |
213 | while tag and not tag[0].isdecimal(): | |
214 | # get rid of leading non-decimal characters | |
215 | tag = tag[1:] | |
216 | latestVersion = Globals.versionToTuple(tag) | |
217 | ||
218 | if self._deviceData["mpy_version"] == "unknown": | |
219 | currentVersionStr = self.tr("unknown") | |
220 | currentVersion = (0, 0, 0) | |
221 | else: | |
222 | currentVersionStr = self._deviceData["mpy_version"] | |
223 | currentVersion = Globals.versionToTuple(currentVersionStr) | |
224 | ||
225 | msg = self.tr( | |
226 | "<h4>MicroPython Version Information</h4>" | |
227 | "<table>" | |
228 | "<tr><td>Installed:</td><td>{0}</td></tr>" | |
229 | "<tr><td>Available:</td><td>{1}</td></tr>" | |
230 | "</table>" | |
231 | ).format(currentVersionStr, tag) | |
232 | if currentVersion < latestVersion: | |
233 | msg += self.tr("<p><b>Update available!</b></p>") | |
234 | ||
235 | EricMessageBox.information( | |
236 | None, | |
237 | self.tr("MicroPython Version"), | |
238 | msg, | |
239 | ) | |
240 | ||
9927 | 241 | @pyqtSlot() |
242 | def __resetDevice(self): | |
243 | """ | |
244 | Private slot to reset the connected device. | |
245 | """ | |
9989 | 246 | if self.microPython.isConnected(): |
247 | self.executeCommands( | |
248 | "import machine\nmachine.reset()\n", mode=self._submitMode | |
249 | ) | |
9927 | 250 | |
9765
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
251 | ################################################################## |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
252 | ## time related methods below |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
253 | ################################################################## |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
254 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
255 | def _getSetTimeCode(self): |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
256 | """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
257 | Protected method to get the device code to set the time. |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
258 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
259 | Note: This method must be implemented in the various device specific |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
260 | subclasses. |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
261 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
262 | @return code to be executed on the connected device to set the time |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
263 | @rtype str |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
264 | """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
265 | # rtc_time[0] - year 4 digit |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
266 | # rtc_time[1] - month 1..12 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
267 | # rtc_time[2] - day 1..31 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
268 | # rtc_time[3] - weekday 1..7 1=Monday |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
269 | # rtc_time[4] - hour 0..23 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
270 | # rtc_time[5] - minute 0..59 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
271 | # rtc_time[6] - second 0..59 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
272 | # rtc_time[7] - yearday 1..366 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
273 | # rtc_time[8] - isdst 0, 1, or -1 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
274 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
275 | # The machine.RTC.init() function takes the arguments in the |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
276 | # order: (year, month, day, weekday, hour, minute, second, |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
277 | # subseconds) |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
278 | # https://docs.micropython.org/en/latest/library/machine.RTC.html#machine-rtc |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
279 | return """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
280 | def set_time(rtc_time): |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
281 | import machine |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
282 | rtc = machine.RTC() |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
283 | rtc.init(rtc_time[:7] + (0,)) |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
284 | """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
285 | |
9755 | 286 | |
10069
435cc5875135
Corrected and checked some code style issues (unused function arguments).
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9989
diff
changeset
|
287 | def createDevice( |
435cc5875135
Corrected and checked some code style issues (unused function arguments).
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9989
diff
changeset
|
288 | microPythonWidget, deviceType, vid, pid, boardName, serialNumber # noqa: U100 |
435cc5875135
Corrected and checked some code style issues (unused function arguments).
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9989
diff
changeset
|
289 | ): |
9755 | 290 | """ |
291 | Function to instantiate a MicroPython device object. | |
292 | ||
293 | @param microPythonWidget reference to the main MicroPython widget | |
294 | @type MicroPythonWidget | |
295 | @param deviceType device type assigned to this device interface | |
296 | @type str | |
297 | @param vid vendor ID | |
298 | @type int | |
299 | @param pid product ID | |
300 | @type int | |
301 | @param boardName name of the board | |
302 | @type str | |
303 | @param serialNumber serial number of the board | |
304 | @type str | |
305 | @return reference to the instantiated device object | |
306 | @rtype PyBoardDevice | |
307 | """ | |
308 | return TeensyDevice(microPythonWidget, deviceType) | |
9820 | 309 | |
310 | ||
311 | @pyqtSlot() | |
312 | def showTeensyFlashInstructions(): | |
313 | """ | |
314 | Slot to show a message box with instruction to flash the Teensy. | |
315 | """ | |
316 | EricMessageBox.information( | |
317 | None, | |
318 | QCoreApplication.translate("TeensyDevice", "Flash MicroPython Firmware"), | |
319 | QCoreApplication.translate( | |
320 | "TeensyDevice", | |
321 | """<p>Teensy 4.0 and Teensy 4.1 are flashed using the 'Teensy Loader'""" | |
322 | """ application. Make sure you downloaded the MicroPython or""" | |
323 | """ CircuitPython .hex file.</p>""" | |
324 | """<p>See <a href="{0}">the PJRC Teensy web site</a>""" | |
325 | """ for details.</p>""", | |
326 | ).format("https://www.pjrc.com/teensy/loader.html"), | |
327 | ) | |
328 | ||
329 | ||
330 | @pyqtSlot() | |
331 | def startTeensyLoader(): | |
332 | """ | |
333 | Slot to start the 'Teensy Loader' application. | |
334 | ||
335 | Note: The application must be accessible via the application search path. | |
336 | """ | |
337 | ok, _ = QProcess.startDetached("teensy") | |
338 | if not ok: | |
339 | EricMessageBox.warning( | |
340 | None, | |
341 | QCoreApplication.translate("TeensyDevice", "Start 'Teensy Loader'"), | |
342 | QCoreApplication.translate( | |
343 | "TeensyDevice", | |
344 | """<p>The 'Teensy Loader' application <b>teensy</b> could not""" | |
345 | """ be started. Ensure it is in the application search path or""" | |
346 | """ start it manually.</p>""", | |
347 | ), | |
348 | ) |