eric7/MicroPython/EspDevices.py

branch
eric7
changeset 8312
800c432b34c8
parent 8218
7c09585bd960
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2019 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the device interface class for ESP32 and ESP8266 based
8 boards.
9 """
10
11 import sys
12
13 from PyQt5.QtCore import pyqtSlot, QProcess
14 from PyQt5.QtWidgets import QDialog
15
16 from E5Gui import E5MessageBox
17 from E5Gui.E5ProcessDialog import E5ProcessDialog
18 from E5Gui.E5Application import e5App
19
20 from .MicroPythonDevices import MicroPythonDevice
21 from .MicroPythonWidget import HAS_QTCHART
22
23 import Preferences
24
25
26 class EspDevice(MicroPythonDevice):
27 """
28 Class implementing the device for ESP32 and ESP8266 based boards.
29 """
30 def __init__(self, microPythonWidget, deviceType, parent=None):
31 """
32 Constructor
33
34 @param microPythonWidget reference to the main MicroPython widget
35 @type MicroPythonWidget
36 @param deviceType device type assigned to this device interface
37 @type str
38 @param parent reference to the parent object
39 @type QObject
40 """
41 super().__init__(microPythonWidget, deviceType, parent)
42
43 def setButtons(self):
44 """
45 Public method to enable the supported action buttons.
46 """
47 super().setButtons()
48 self.microPython.setActionButtons(
49 run=True, repl=True, files=True, chart=HAS_QTCHART)
50
51 def forceInterrupt(self):
52 """
53 Public method to determine the need for an interrupt when opening the
54 serial connection.
55
56 @return flag indicating an interrupt is needed
57 @rtype bool
58 """
59 return True
60
61 def deviceName(self):
62 """
63 Public method to get the name of the device.
64
65 @return name of the device
66 @rtype str
67 """
68 return self.tr("ESP8266, ESP32")
69
70 def canStartRepl(self):
71 """
72 Public method to determine, if a REPL can be started.
73
74 @return tuple containing a flag indicating it is safe to start a REPL
75 and a reason why it cannot.
76 @rtype tuple of (bool, str)
77 """
78 return True, ""
79
80 def canStartPlotter(self):
81 """
82 Public method to determine, if a Plotter can be started.
83
84 @return tuple containing a flag indicating it is safe to start a
85 Plotter and a reason why it cannot.
86 @rtype tuple of (bool, str)
87 """
88 return True, ""
89
90 def canRunScript(self):
91 """
92 Public method to determine, if a script can be executed.
93
94 @return tuple containing a flag indicating it is safe to start a
95 Plotter and a reason why it cannot.
96 @rtype tuple of (bool, str)
97 """
98 return True, ""
99
100 def runScript(self, script):
101 """
102 Public method to run the given Python script.
103
104 @param script script to be executed
105 @type str
106 """
107 pythonScript = script.split("\n")
108 self.sendCommands(pythonScript)
109
110 def canStartFileManager(self):
111 """
112 Public method to determine, if a File Manager can be started.
113
114 @return tuple containing a flag indicating it is safe to start a
115 File Manager and a reason why it cannot.
116 @rtype tuple of (bool, str)
117 """
118 return True, ""
119
120 def addDeviceMenuEntries(self, menu):
121 """
122 Public method to add device specific entries to the given menu.
123
124 @param menu reference to the context menu
125 @type QMenu
126 """
127 connected = self.microPython.isConnected()
128
129 act = menu.addAction(self.tr("Erase Flash"),
130 self.__eraseFlash)
131 act.setEnabled(not connected)
132 act = menu.addAction(self.tr("Flash MicroPython Firmware"),
133 self.__flashMicroPython)
134 act.setEnabled(not connected)
135 menu.addSeparator()
136 act = menu.addAction(self.tr("Flash Additional Firmware"),
137 self.__flashAddons)
138 act.setEnabled(not connected)
139 menu.addSeparator()
140 act = menu.addAction(self.tr("Backup Firmware"),
141 self.__backupFlash)
142 act.setEnabled(not connected)
143 act = menu.addAction(self.tr("Restore Firmware"),
144 self.__restoreFlash)
145 act.setEnabled(not connected)
146 menu.addSeparator()
147 act = menu.addAction(self.tr("Show Chip ID"),
148 self.__showChipID)
149 act.setEnabled(not connected)
150 act = menu.addAction(self.tr("Show Flash ID"),
151 self.__showFlashID)
152 act.setEnabled(not connected)
153 act = menu.addAction(self.tr("Show MAC Address"),
154 self.__showMACAddress)
155 act.setEnabled(not connected)
156 menu.addSeparator()
157 act = menu.addAction(self.tr("Reset Device"), self.__resetDevice)
158 menu.addSeparator()
159 menu.addAction(self.tr("Install 'esptool.py'"), self.__installEspTool)
160
161 def hasFlashMenuEntry(self):
162 """
163 Public method to check, if the device has its own flash menu entry.
164
165 @return flag indicating a specific flash menu entry
166 @rtype bool
167 """
168 return True
169
170 @pyqtSlot()
171 def __eraseFlash(self):
172 """
173 Private slot to erase the device flash memory.
174 """
175 ok = E5MessageBox.yesNo(
176 self.microPython,
177 self.tr("Erase Flash"),
178 self.tr("""Shall the flash of the selected device really be"""
179 """ erased?"""))
180 if ok:
181 flashArgs = [
182 "-u",
183 "-m", "esptool",
184 "--port", self.microPython.getCurrentPort(),
185 "erase_flash",
186 ]
187 dlg = E5ProcessDialog(self.tr("'esptool erase_flash' Output"),
188 self.tr("Erase Flash"),
189 showProgress=True)
190 res = dlg.startProcess(sys.executable, flashArgs)
191 if res:
192 dlg.exec()
193
194 @pyqtSlot()
195 def __flashMicroPython(self):
196 """
197 Private slot to flash a MicroPython firmware to the device.
198
199 @exception ValueError raised to indicate an unsupported chip type
200 """
201 from .EspFirmwareSelectionDialog import EspFirmwareSelectionDialog
202 dlg = EspFirmwareSelectionDialog()
203 if dlg.exec() == QDialog.DialogCode.Accepted:
204 chip, firmware, baudRate, flashMode, _ = dlg.getData()
205 if chip not in ("esp8266", "esp32"):
206 raise ValueError(self.tr("Unsupported chip type '{0}'.")
207 .format(chip))
208
209 if chip == "esp8266":
210 flashAddress = "0x0000"
211 elif chip == "esp32":
212 flashAddress = "0x1000"
213
214 flashArgs = [
215 "-u",
216 "-m", "esptool",
217 "--chip", chip,
218 "--port", self.microPython.getCurrentPort(),
219 ]
220 if baudRate != "115200":
221 flashArgs += [
222 "--baud", baudRate
223 ]
224 flashArgs.append("write_flash")
225 if flashMode:
226 flashArgs += [
227 "--flash_mode", flashMode
228 ]
229 flashArgs += [
230 flashAddress,
231 firmware,
232 ]
233 dlg = E5ProcessDialog(self.tr("'esptool write_flash' Output"),
234 self.tr("Flash MicroPython Firmware"),
235 showProgress=True)
236 res = dlg.startProcess(sys.executable, flashArgs)
237 if res:
238 dlg.exec()
239
240 @pyqtSlot()
241 def __flashAddons(self):
242 """
243 Private slot to flash some additional firmware images.
244 """
245 from .EspFirmwareSelectionDialog import EspFirmwareSelectionDialog
246 dlg = EspFirmwareSelectionDialog(addon=True)
247 if dlg.exec() == QDialog.DialogCode.Accepted:
248 chip, firmware, baudRate, flashMode, flashAddress = dlg.getData()
249 flashArgs = [
250 "-u",
251 "-m", "esptool",
252 "--chip", chip,
253 "--port", self.microPython.getCurrentPort(),
254 ]
255 if baudRate != "115200":
256 flashArgs += [
257 "--baud", baudRate
258 ]
259 flashArgs.append("write_flash")
260 if flashMode:
261 flashArgs += [
262 "--flash_mode", flashMode
263 ]
264 flashArgs += [
265 flashAddress.lower(),
266 firmware,
267 ]
268 dlg = E5ProcessDialog(self.tr("'esptool write_flash' Output"),
269 self.tr("Flash Additional Firmware"),
270 showProgress=True)
271 res = dlg.startProcess(sys.executable, flashArgs)
272 if res:
273 dlg.exec()
274
275 @pyqtSlot()
276 def __backupFlash(self):
277 """
278 Private slot to backup the currently flashed firmware.
279 """
280 from .EspBackupRestoreFirmwareDialog import (
281 EspBackupRestoreFirmwareDialog
282 )
283 dlg = EspBackupRestoreFirmwareDialog(backupMode=True)
284 if dlg.exec() == QDialog.DialogCode.Accepted:
285 chip, flashSize, flashMode, firmware = dlg.getData()
286 flashArgs = [
287 "-u",
288 "-m", "esptool",
289 "--chip", chip,
290 "--port", self.microPython.getCurrentPort(),
291 "read_flash",
292 "0x0", flashSize,
293 firmware,
294 ]
295 dlg = E5ProcessDialog(self.tr("'esptool read_flash' Output"),
296 self.tr("Backup Firmware"),
297 showProgress=True)
298 res = dlg.startProcess(sys.executable, flashArgs)
299 if res:
300 dlg.exec()
301
302 @pyqtSlot()
303 def __restoreFlash(self):
304 """
305 Private slot to restore a previously saved firmware.
306 """
307 from .EspBackupRestoreFirmwareDialog import (
308 EspBackupRestoreFirmwareDialog
309 )
310 dlg = EspBackupRestoreFirmwareDialog(backupMode=False)
311 if dlg.exec() == QDialog.DialogCode.Accepted:
312 chip, flashSize, flashMode, firmware = dlg.getData()
313 flashArgs = [
314 "-u",
315 "-m", "esptool",
316 "--chip", chip,
317 "--port", self.microPython.getCurrentPort(),
318 "write_flash",
319 "--flash_mode", flashMode,
320 ]
321 if bool(flashSize):
322 flashArgs.extend([
323 "--flash_size", flashSize,
324 ])
325 flashArgs.extend([
326 "0x0",
327 firmware,
328 ])
329 dlg = E5ProcessDialog(self.tr("'esptool write_flash' Output"),
330 self.tr("Restore Firmware"),
331 showProgress=True)
332 res = dlg.startProcess(sys.executable, flashArgs)
333 if res:
334 dlg.exec()
335
336 @pyqtSlot()
337 def __showChipID(self):
338 """
339 Private slot to show the ID of the ESP chip.
340 """
341 args = [
342 "-u",
343 "-m", "esptool",
344 "--port", self.microPython.getCurrentPort(),
345 "chip_id"
346 ]
347 dlg = E5ProcessDialog(self.tr("'esptool chip_id' Output"),
348 self.tr("Show Chip ID"))
349 res = dlg.startProcess(sys.executable, args)
350 if res:
351 dlg.exec()
352
353 @pyqtSlot()
354 def __showFlashID(self):
355 """
356 Private slot to show the ID of the ESP flash chip.
357 """
358 args = [
359 "-u",
360 "-m", "esptool",
361 "--port", self.microPython.getCurrentPort(),
362 "flash_id"
363 ]
364 dlg = E5ProcessDialog(self.tr("'esptool flash_id' Output"),
365 self.tr("Show Flash ID"))
366 res = dlg.startProcess(sys.executable, args)
367 if res:
368 dlg.exec()
369
370 @pyqtSlot()
371 def __showMACAddress(self):
372 """
373 Private slot to show the MAC address of the ESP chip.
374 """
375 args = [
376 "-u",
377 "-m", "esptool",
378 "--port", self.microPython.getCurrentPort(),
379 "read_mac"
380 ]
381 dlg = E5ProcessDialog(self.tr("'esptool read_mac' Output"),
382 self.tr("Show MAC Address"))
383 res = dlg.startProcess(sys.executable, args)
384 if res:
385 dlg.exec()
386
387 @pyqtSlot()
388 def __resetDevice(self):
389 """
390 Private slot to reset the connected device.
391 """
392 if self.microPython.isConnected():
393 self.microPython.commandsInterface().execute([
394 "import machine",
395 "machine.reset()",
396 ])
397 else:
398 # perform a reset via esptool using flash_id command ignoring
399 # the output
400 args = [
401 "-u",
402 "-m", "esptool",
403 "--port", self.microPython.getCurrentPort(),
404 "flash_id"
405 ]
406 proc = QProcess()
407 proc.start(sys.executable, args)
408 procStarted = proc.waitForStarted(10000)
409 if procStarted:
410 proc.waitForFinished(10000)
411
412 @pyqtSlot()
413 def __installEspTool(self):
414 """
415 Private slot to install the esptool package via pip.
416 """
417 pip = e5App().getObject("Pip")
418 pip.installPackages(["esptool"], interpreter=sys.executable)
419
420 def getDocumentationUrl(self):
421 """
422 Public method to get the device documentation URL.
423
424 @return documentation URL of the device
425 @rtype str
426 """
427 return Preferences.getMicroPython("MicroPythonDocuUrl")
428
429 def getFirmwareUrl(self):
430 """
431 Public method to get the device firmware download URL.
432
433 @return firmware download URL of the device
434 @rtype str
435 """
436 return Preferences.getMicroPython("MicroPythonFirmwareUrl")

eric ide

mercurial