src/eric7/MicroPython/EspDevices.py

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

eric ide

mercurial