Sat, 25 Feb 2023 19:18:07 +0100
MicroPython
- added support for 'paste' mode to circumvent the reset of CircuitPython when executing some commands through the device interface
9755 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> | |
4 | # | |
5 | ||
6 | """ | |
7 | Module implementing the device interface class for Teensy boards with MicroPython. | |
8 | """ | |
9 | ||
10 | from PyQt6.QtCore import QProcess, QUrl, pyqtSlot | |
11 | from PyQt6.QtNetwork import QNetworkRequest | |
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( | |
151 | self.tr("MicroPython Flash Instructions"), self.__showFlashInstructions | |
152 | ) | |
153 | self.__flashMpyAct = self.__teensyMenu.addAction( | |
154 | self.tr("Flash MicroPython Firmware"), self.__startTeensyLoader | |
155 | ) | |
156 | self.__flashMpyAct.setToolTip( | |
157 | self.tr("Start the 'Teensy Loader' application to flash the Teensy device.") | |
158 | ) | |
159 | ||
160 | def addDeviceMenuEntries(self, menu): | |
161 | """ | |
162 | Public method to add device specific entries to the given menu. | |
163 | ||
164 | @param menu reference to the context menu | |
165 | @type QMenu | |
166 | """ | |
167 | connected = self.microPython.isConnected() | |
168 | linkConnected = self.microPython.isLinkConnected() | |
169 | ||
170 | self.__showMpyAct.setEnabled(connected) | |
171 | self.__flashMpyAct.setEnabled(not linkConnected) | |
172 | ||
173 | menu.addMenu(self.__teensyMenu) | |
174 | ||
175 | @pyqtSlot() | |
176 | def __showFirmwareVersions(self): | |
177 | """ | |
178 | Private slot to show the firmware version of the connected device and the | |
179 | available firmware version. | |
180 | """ | |
181 | if self.microPython.isConnected(): | |
182 | if self._deviceData["mpy_name"] != "micropython": | |
183 | EricMessageBox.critical( | |
184 | None, | |
185 | self.tr("Show MicroPython Versions"), | |
186 | self.tr( | |
187 | """The firmware of the connected device cannot be""" | |
188 | """ determined or the board does not run MicroPython.""" | |
189 | """ Aborting...""" | |
190 | ), | |
191 | ) | |
192 | else: | |
193 | ui = ericApp().getObject("UserInterface") | |
194 | request = QNetworkRequest(QUrl(FirmwareGithubUrls["micropython"])) | |
195 | reply = ui.networkAccessManager().head(request) | |
196 | reply.finished.connect(lambda: self.__firmwareVersionResponse(reply)) | |
197 | ||
198 | def __firmwareVersionResponse(self, reply): | |
199 | """ | |
200 | Private method handling the response of the latest version request. | |
201 | ||
202 | @param reply reference to the reply object | |
203 | @type QNetworkReply | |
204 | """ | |
205 | latestUrl = reply.url().toString() | |
206 | tag = latestUrl.rsplit("/", 1)[-1] | |
207 | while tag and not tag[0].isdecimal(): | |
208 | # get rid of leading non-decimal characters | |
209 | tag = tag[1:] | |
210 | latestVersion = Globals.versionToTuple(tag) | |
211 | ||
212 | if self._deviceData["mpy_version"] == "unknown": | |
213 | currentVersionStr = self.tr("unknown") | |
214 | currentVersion = (0, 0, 0) | |
215 | else: | |
216 | currentVersionStr = self._deviceData["mpy_version"] | |
217 | currentVersion = Globals.versionToTuple(currentVersionStr) | |
218 | ||
219 | msg = self.tr( | |
220 | "<h4>MicroPython Version Information</h4>" | |
221 | "<table>" | |
222 | "<tr><td>Installed:</td><td>{0}</td></tr>" | |
223 | "<tr><td>Available:</td><td>{1}</td></tr>" | |
224 | "</table>" | |
225 | ).format(currentVersionStr, tag) | |
226 | if currentVersion < latestVersion: | |
227 | msg += self.tr("<p><b>Update available!</b></p>") | |
228 | ||
229 | EricMessageBox.information( | |
230 | None, | |
231 | self.tr("MicroPython Version"), | |
232 | msg, | |
233 | ) | |
234 | ||
235 | def __showFlashInstructions(self): | |
236 | """ | |
237 | Private method to show a message box with instruction to flash the Teensy. | |
238 | """ | |
239 | EricMessageBox.information( | |
240 | self.microPython, | |
241 | self.tr("Flash MicroPython Firmware"), | |
242 | self.tr( | |
243 | """<p>Teensy 4.0 and Teensy 4.1 are flashed using the 'Teensy Loader'""" | |
244 | """ application. Make sure you downloaded the MicroPython or""" | |
245 | """ CircuitPython .hex file.</p>""" | |
246 | """<p>See <a href="{0}">the PJRC Teensy web site</a>""" | |
247 | """ for details.</p>""" | |
248 | ).format("https://www.pjrc.com/teensy/loader.html"), | |
249 | ) | |
250 | ||
251 | def __startTeensyLoader(self): | |
252 | """ | |
253 | Private method to start the 'Teensy Loader' application. | |
254 | ||
255 | Note: The application must be accessible via the application search path. | |
256 | """ | |
257 | ok, _ = QProcess.startDetached("teensy") | |
258 | if not ok: | |
259 | EricMessageBox.warning( | |
260 | self.microPython, | |
261 | self.tr("Start 'Teensy Loader'"), | |
262 | self.tr( | |
263 | """<p>The 'Teensy Loader' application <b>teensy</b> could not""" | |
264 | """ be started. Ensure it is in the application search path or""" | |
265 | """ start it manually.</p>""" | |
266 | ), | |
267 | ) | |
268 | ||
9765
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
269 | ################################################################## |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
270 | ## time related methods below |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
271 | ################################################################## |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
272 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
273 | def _getSetTimeCode(self): |
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 | 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
|
276 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
277 | 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
|
278 | subclasses. |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
279 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
280 | @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
|
281 | @rtype str |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
282 | """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
283 | # rtc_time[0] - year 4 digit |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
284 | # rtc_time[1] - month 1..12 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
285 | # rtc_time[2] - day 1..31 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
286 | # 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
|
287 | # rtc_time[4] - hour 0..23 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
288 | # rtc_time[5] - minute 0..59 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
289 | # rtc_time[6] - second 0..59 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
290 | # rtc_time[7] - yearday 1..366 |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
291 | # 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
|
292 | |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
293 | # 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
|
294 | # 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
|
295 | # subseconds) |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
296 | # 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
|
297 | return """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
298 | def set_time(rtc_time): |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
299 | import machine |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
300 | rtc = machine.RTC() |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
301 | rtc.init(rtc_time[:7] + (0,)) |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
302 | """ |
6378da868bb0
Reorganized the MicroPython code even more.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9763
diff
changeset
|
303 | |
9755 | 304 | |
305 | def createDevice(microPythonWidget, deviceType, vid, pid, boardName, serialNumber): | |
306 | """ | |
307 | Function to instantiate a MicroPython device object. | |
308 | ||
309 | @param microPythonWidget reference to the main MicroPython widget | |
310 | @type MicroPythonWidget | |
311 | @param deviceType device type assigned to this device interface | |
312 | @type str | |
313 | @param vid vendor ID | |
314 | @type int | |
315 | @param pid product ID | |
316 | @type int | |
317 | @param boardName name of the board | |
318 | @type str | |
319 | @param serialNumber serial number of the board | |
320 | @type str | |
321 | @return reference to the instantiated device object | |
322 | @rtype PyBoardDevice | |
323 | """ | |
324 | return TeensyDevice(microPythonWidget, deviceType) |