src/eric7/MicroPython/MicroPythonDevices.py

branch
eric7
changeset 9756
9854647c8c5c
parent 9755
1a09700229e7
child 9757
ab6e87f6f1c4
equal deleted inserted replaced
9755:1a09700229e7 9756:9854647c8c5c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2019 - 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing some utility functions and the MicroPythonDevice base
8 class.
9 """
10
11 import contextlib
12 import copy
13 import importlib
14 import logging
15 import os
16
17 from PyQt6.QtCore import QCoreApplication, QObject, pyqtSlot
18 from PyQt6.QtSerialPort import QSerialPortInfo
19 from PyQt6.QtWidgets import QInputDialog
20
21 from eric7 import Preferences
22 from eric7.EricGui import EricPixmapCache
23 from eric7.EricWidgets import EricMessageBox
24 from eric7.EricWidgets.EricApplication import ericApp
25
26 SupportedBoards = {
27 "esp": {
28 "ids": [
29 (0x0403, 0x6001), # M5Stack ESP32 device"),
30 (0x0403, 0x6001), # FT232/FT245 (XinaBox CW01, CW02)
31 (0x0403, 0x6010), # FT2232C/D/L/HL/Q (ESP-WROVER-KIT)
32 (0x0403, 0x6011), # FT4232
33 (0x0403, 0x6014), # FT232H
34 (0x0403, 0x6015), # Sparkfun ESP32
35 (0x0403, 0x601C), # FT4222H
36 (0x10C4, 0xEA60), # CP210x
37 (0x1A86, 0x55D4), # CH343
38 (0x1A86, 0x7523), # HL-340, CH340
39 ],
40 "description": "ESP32, ESP8266",
41 "icon": "esp32Device",
42 "port_description": "",
43 },
44 "circuitpython": {
45 "ids": [
46 (0x0483, 0x572A), # STMicroelectronics NUCLEO-F446RE - CPy
47 (0x04D8, 0xE799), # Cytron Maker Zero SAMD21
48 (0x04D8, 0xEA2A), # BHDynamics DynaLoRa_USB
49 (0x04D8, 0xEAD1), # BH Dynamics DynOSSAT-EDU-EPS-v1.0
50 (0x04D8, 0xEAD2), # BH Dynamics DynOSSAT-EDU-OBC-v1.0
51 (0x04D8, 0xEC44), # maholli PyCubed
52 (0x04D8, 0xEC63), # Kevin Neubauer CircuitBrains Basic
53 (0x04D8, 0xEC64), # Kevin Neubauer CircuitBrains Deluxe
54 (0x04D8, 0xEC72), # XinaBox CC03
55 (0x04D8, 0xEC75), # XinaBox CS11
56 (0x04D8, 0xED5F), # Itaca Innovation uChip CircuitPython
57 (0x04D8, 0xED94), # maholli kicksat-sprite
58 (0x04D8, 0xEDB3), # Capable Robot Programmable USB Hub
59 (0x04D8, 0xEDBE), # maholli SAM32
60 (0x04D8, 0xEE8C), # J&J Studios LLC datum-Distance
61 (0x04D8, 0xEE8D), # J&J Studios LLC datum-IMU
62 (0x04D8, 0xEE8E), # J&J Studios LLC datum-Light
63 (0x04D8, 0xEE8F), # J&J Studios LLC datum-Weather
64 (0x04D8, 0xEF67), # senseBox MCU
65 (0x054C, 0x0BC2), # Sony Spresense
66 (0x1209, 0x2017), # Benjamin Shockley Mini SAM M4
67 (0x1209, 0x3141), # CrumpSpace CrumpS2
68 (0x1209, 0x3252), # Targett Module Clip w/Wroom
69 (0x1209, 0x3253), # Targett Module Clip w/Wrover
70 (0x1209, 0x4203), # 42. Keebs Frood
71 (0x1209, 0x4D43), # Robotics Masters Robo HAT MM1 M4
72 (0x1209, 0x4DDD), # ODT CP Sapling
73 (0x1209, 0x4DDE), # ODT CP Sapling M0 w/ SPI Flash
74 (0x1209, 0x4DDF), # ODT CP Sapling Rev B
75 (0x1209, 0x4DF0), # Oak Dev Tech Pixelwing ESP32S2
76 (0x1209, 0x4DF1), # Oak Dev Tech BREAD2040
77 (0x1209, 0x4DF2), # Oak Dev Tech CAST AWAY RP2040
78 (0x1209, 0x5A52), # ZRichard RP2.65-F
79 (0x1209, 0x5BF0), # Foosn Fomu
80 (0x1209, 0x7150), # Electronic Cats Hunter Cat NFC
81 (0x1209, 0x7382), # Invector Labs AB iLabs Challenger 840
82 (0x1209, 0x805A), # Electronic Cats BastBLE
83 (0x1209, 0x8CAE), # takayoshiotake Octave RP2040
84 (0x1209, 0xA182), # Solder Party RP2040 Stamp
85 (0x1209, 0xB182), # Solder Party BBQ20 Keyboard
86 (0x1209, 0xBAB0), # Electronic Cats Bast WiFi
87 (0x1209, 0xBAB1), # Electronic Cats Meow Meow
88 (0x1209, 0xBAB2), # Electronic Cats CatWAN USBStick
89 (0x1209, 0xBAB3), # Electronic Cats Bast Pro Mini M0
90 (0x1209, 0xBAB6), # Electronic Cats Escornabot Makech
91 (0x1209, 0xBAB8), # Electronic Cats NFC Copy Cat
92 (0x1209, 0xC051), # Betrusted Simmel
93 (0x1209, 0xCB74), # 0xCB Helios
94 (0x1209, 0xD10D), # Diodes Delight Piunora
95 (0x1209, 0xD1B5), # Radomir Dopieralski PewPew LCD
96 (0x1209, 0xE3E3), # StackRduino M0 PRO
97 (0x1209, 0xEF00), # 2231puppy E-Fidget
98 (0x1209, 0xF123), # Electrolama minik
99 (0x1209, 0xF500), # Silicognition LLC M4-Shim
100 (0x1209, 0xF502), # Silicognition LLC RP2040-Shim
101 (0x16D0, 0x08C6), # Pimoroni Keybow 2040
102 (0x16D0, 0x08C7), # Pimoroni Tiny 2040 (8MB)
103 (0x16D0, 0x08C8), # Pimoroni PicoSystem
104 (0x16D0, 0x10ED), # Mechwild PillBug
105 (0x1915, 0xB001), # Makerdiary Pitaya Go
106 (0x192F, 0xB1B2), # WarmBit BluePixel nRF52840
107 (0x1B4F, 0x0015), # SparkFun RedBoard Turbo Board
108 (0x1B4F, 0x0016), # SparkFun SAMD51 Thing+
109 (0x1B4F, 0x0017), # SparkFun LUMIDrive Board
110 (0x1B4F, 0x0020), # SparkFun MicroMod SAMD51 Processor
111 (0x1B4F, 0x0021), # SparkFun MicroMod nRF52840 Processor
112 (0x1B4F, 0x0024), # SparkFun MicroMod RP2040 Processor
113 (0x1B4F, 0x0025), # SparkFun Thing Plus RP2040
114 (0x1B4F, 0x0026), # SparkFun Pro Micro RP2040
115 (0x1B4F, 0x0027), # SparkFun STM32 MicroMod Processor
116 (0x1B4F, 0x0028), # SparkFun Thing Plus - STM32
117 (0x1B4F, 0x002E), # PJRC/Sparkfun Teensy MicroMod
118 (0x1B4F, 0x5289), # SparkFun SFE_nRF52840_Mini
119 (0x1B4F, 0x8D22), # SparkFun SAMD21 Mini Breakout
120 (0x1B4F, 0x8D23), # SparkFun SAMD21 Dev Breakout
121 (0x1B4F, 0x8D24), # SparkFun Qwiic Micro
122 (0x1D50, 0x60E8), # Radomir Dopieralski PewPew M4
123 (0x1D50, 0x6152), # nrf52.jpconstantineau.com BlueMicro833
124 (0x1D50, 0x6153), # JPConstantineau PyKey18
125 (0x1D50, 0x6153), # JPConstantineau PyKey44
126 (0x1D50, 0x6153), # JPConstantineau PyKey60
127 (0x1D50, 0x6153), # JPConstantineau PyKey87
128 (0x1D50, 0x6154), # JPConstantineau EncoderPad RP2040
129 (0x1D50, 0x6161), # nrf52.jpconstantineau.com BlueMicro840
130 (0x2019, 0x7103), # Benjamin Shockley Fig Pi
131 (0x2341, 0x8053), # Arduino MKR1300
132 (0x2341, 0x8057), # Arduino Nano 33 IoT
133 (0x2341, 0x805A), # Arduino Arduino_Nano_33_BLE
134 (0x2341, 0x824D), # Arduino Zero
135 (0x2786, 0x9207), # Switch Sc. BLE-SS dev board Multi Sensor
136 (0x2786, 0x920D), # Switch Sc. SSCI ISP1807 Dev Board
137 (0x2786, 0x920F), # Switch Sc. SSCI ISP1807 Micro Board
138 (0x2886, 0x002F), # Seeed Seeeduino XIAO
139 (0x2886, 0x0042), # Seeed Seeeduino XIAO RP2040
140 (0x2886, 0x0045), # Seeed XIAO nRF52840 Sense
141 (0x2886, 0x802D), # Seeed Seeeduino Wio Terminal
142 (0x2886, 0x802F), # Seeed Seeeduino XIAO KB
143 (0x2886, 0xF001), # Makerdiary nRF52840 M.2 Developer Kit
144 (0x2886, 0xF002), # Makerdiary M60 Keyboard
145 (0x2B04, 0xC00C), # Particle Argon
146 (0x2B04, 0xC00D), # Particle Boron
147 (0x2B04, 0xC00E), # Particle Xenon
148 (0x2E8A, 0x1000), # Cytron Maker Pi RP2040
149 (0x2E8A, 0x1002), # Pimoroni Pico LiPo (4MB)
150 (0x2E8A, 0x1003), # Pimoroni Pico LiPo (16MB)
151 (0x2E8A, 0x1005), # Melopero Shake RP2040
152 (0x2E8A, 0x1006), # Invector Labs Challenger RP2040 WiFi
153 (0x2E8A, 0x1008), # Pimoroni PGA2040
154 (0x2E8A, 0x1009), # Pimoroni Interstate 75
155 (0x2E8A, 0x100A), # Pimoroni Plasma 2040
156 (0x2E8A, 0x100B), # Invector Labs Challenger RP2040 LTE
157 (0x2E8A, 0x100D), # Invector Labs Challenger NB RP2040 WiFi
158 (0x2E8A, 0x100E), # Raspberry Pi Zero
159 (0x2E8A, 0x100F), # Cytron Maker Nano RP2040
160 (0x2E8A, 0x1012), # Raspberry Pi Compute Module 4 IO Board
161 (0x2E8A, 0x1013), # Raspberry Pi 4B
162 (0x2E8A, 0x1014), # Raspberry Pi Compute Module 4
163 (0x2E8A, 0x1015), # Raspberry Pi Zero 2W
164 (0x2E8A, 0x1016), # Pimoroni Tiny 2040 (2MB)
165 (0x2E8A, 0x1019), # Pimoroni Motor 2040
166 (0x2E8A, 0x101A), # Pimoroni Servo 2040
167 (0x2E8A, 0x101B), # Pimoroni Badger 2040
168 (0x2E8A, 0x101E), # Raspberry Pi Zero W
169 (0x2E8A, 0x101F), # Waveshare Electronics RP2040-Zero
170 (0x2E8A, 0x1023), # Invector Labs Challenger RP2040 LoRa
171 (0x2E8A, 0x1026), # ELECFREAKS Pico:ed
172 (0x2E8A, 0x1027), # WIZnet W5100S-EVB-Pico
173 (0x2E8A, 0x1029), # WIZnet W5500-EVB-Pico
174 (0x2E8A, 0x102C), # Invector Labs Challenger RP2040 WiFi/BLE
175 (0x2E8A, 0x102D), # Invector Labs Challenger RP2040 SD/RTC
176 (0x2E8A, 0x102E), # VCC-GND Studio YD-RP2040
177 (0x2E8A, 0x1032), # Invector Labs Challenger RP2040 SubGHz
178 (0x2E8A, 0x1039), # Waveshare Electronics Waveshare RP2040-LCD-1.28
179 (0x2E8A, 0x1048), # nullbits Bit-C PRO
180 (0x303A, 0x7001), # Espressif ESP32-S2-HMI-DevKit-1
181 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1
182 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N32R8
183 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8
184 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8R2
185 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8R8
186 (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-nopsram
187 (0x303A, 0x7005), # Espressif ESP32-S3-Box-2.5
188 (0x303A, 0x7007), # Espressif ESP32-S3-DevKitM-1-N8
189 (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N4
190 (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N4R2
191 (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N8R2
192 (0x303A, 0x700B), # Espressif ESP32-S3-USB-OTG-N8
193 (0x303A, 0x700D), # Espressif ESP32-S3-Box-Lite
194 (0x303A, 0x700F), # Espressif ESP32-S3-EYE
195 (0x303A, 0x8002), # UnexpectedMaker TinyS2
196 (0x303A, 0x8007), # LILYGO TTGO T8 ESP32-S2
197 (0x303A, 0x800D), # Gravitech Cucumber RS
198 (0x303A, 0x80A1), # Gravitech Cucumber R
199 (0x303A, 0x80A4), # Gravitech Cucumber M
200 (0x303A, 0x80A7), # Gravitech Cucumber MS
201 (0x303A, 0x80AA), # Espressif Franzininho WIFI w/Wroom
202 (0x303A, 0x80AD), # Espressif Franzininho WIFI w/Wrover
203 (0x303A, 0x80AF), # Artisense Reference Design RD00
204 (0x303A, 0x80B2), # Muselab nanoESP32-S2 w/Wrover
205 (0x303A, 0x80B5), # UnexpectedMaker FeatherS2 Neo
206 (0x303A, 0x80B7), # MORPHEANS MORPHESP-240
207 (0x303A, 0x80C3), # Lolin S2 Mini
208 (0x303A, 0x80C6), # Lolin S2 Pico
209 (0x303A, 0x80D1), # UnexpectedMaker TinyS3
210 (0x303A, 0x80D4), # UnexpectedMaker ProS3
211 (0x303A, 0x80D7), # UnexpectedMaker FeatherS3
212 (0x303A, 0x80D9), # FutureKeys HexKy_S2
213 (0x303A, 0x80E0), # BananaPi BPI-Leaf-S3
214 (0x303A, 0x80E6), # BananaPi BPI-Bit-S2
215 (0x303A, 0x80E8), # HiiBot IoTs2
216 (0x303A, 0x80EA), # LILYGO TTGO T8 ESP32-S2-WROOM
217 (0x303A, 0x80ED), # LILYGO TTGO T8 ESP32-S2
218 (0x303A, 0x80F9), # Cytron Maker Feather AIoT S3
219 (0x303A, 0x80FC), # Espressif MixGo CE
220 (0x303A, 0x80FD), # Espressif MixGo CE
221 (0x303A, 0x810A), # Waveshare Electronics ESP32-S2-Pico
222 (0x303A, 0x810C), # Waveshare Electronics ESP32-S2-Pico-LCD
223 (0x303A, 0x8111), # Smart Bee Designs Bee-S3
224 (0x303A, 0x8114), # Smart Bee Designs Bee-Motion-S3
225 (0x303A, 0x8117), # WEMOS LOLIN S3 16MB Flash 8MB PSRAM
226 (0x303A, 0x812C), # BananaPi BPI-PicoW-S3
227 (0x30A4, 0x0002), # Blues Inc. Swan R5
228 (0x3171, 0x0101), # 8086.net Commander
229 (0x31E2, 0x2001), # BDMICRO LLC VINA-D21
230 (0x31E2, 0x2011), # BDMICRO LLC VINA-D51
231 (0x31E2, 0x2021), # BDMICRO LLC VINA-D51
232 (0x32BD, 0x3001), # Alorium Tech. AloriumTech Evo M51
233 (0x4097, 0x0001), # TG-Boards Datalore IP M4
234 (0x612B, 0x80A7), # Ai-Thinker ESP 12k NodeMCU
235 (0x239A, None), # Any Adafruit Boards
236 ],
237 "description": "CircuitPython",
238 "icon": "circuitPythonDevice",
239 "port_description": "",
240 },
241 "bbc_microbit": {
242 "ids": [
243 (0x0D28, 0x0204), # micro:bit
244 ],
245 "description": "BBC micro:bit",
246 "icon": "microbitDevice",
247 "port_description": "BBC micro:bit CMSIS-DAP",
248 },
249 "calliope": {
250 "ids": [
251 (0x0D28, 0x0204), # Calliope mini
252 ],
253 "description": "Calliope mini",
254 "icon": "calliope_mini",
255 "port_description": "DAPLink CMSIS-DAP",
256 },
257 "pyboard": {
258 "ids": [
259 (0xF055, 0x9800), # Pyboard in CDC mode
260 (0xF055, 0x9801), # Pyboard in CDC+HID mode
261 (0xF055, 0x9802), # Pyboard in CDC+MSC mode
262 ],
263 "description": "PyBoard",
264 "icon": "micropython48",
265 "port_description": "Pyboard",
266 },
267 "rp2040": {
268 "ids": [
269 (0x2E8A, 0x0005), # Raspberry Pi Pico
270 ],
271 "description": QCoreApplication.translate("MicroPythonDevice", "RP2040 based"),
272 "icon": "rp2040Device",
273 "port_description": "",
274 },
275 "teensy": {
276 "ids": [
277 (0xF055, 0x9802), # Pyboard in CDC+MSC mode
278 ],
279 "description": "Teensy",
280 "icon": "micropython48",
281 "port_description": "Teensy",
282 },
283 "generic": {
284 # only manually configured devices use this
285 "ids": [],
286 "description": QCoreApplication.translate("MicroPythonDevice", "Generic Board"),
287 "icon": "micropython48",
288 "port_description": "",
289 },
290 }
291
292 IgnoredBoards = (
293 (0x8086, 0x9C3D),
294 (0x8086, None),
295 )
296
297 FirmwareGithubUrls = {
298 "micropython": "https://github.com/micropython/micropython/releases/latest",
299 "circuitpython": "https://github.com/adafruit/circuitpython/releases/latest",
300 "pimoroni_pico": "https://github.com/pimoroni/pimoroni-pico/releases/latest",
301 "microbit_v1": "https://github.com/bbcmicrobit/micropython/releases/latest",
302 "microbit_v2": (
303 "https://github.com/microbit-foundation/micropython-microbit-v2/releases/latest"
304 ),
305 }
306
307
308 def getSupportedDevices():
309 """
310 Function to get a list of supported MicroPython devices.
311
312 @return set of tuples with the board type and description
313 @rtype set of tuples of (str, str)
314 """
315 boards = []
316 for board in SupportedBoards:
317 boards.append((board, SupportedBoards[board]["description"]))
318 return boards
319
320
321 def getFoundDevices():
322 """
323 Function to check the serial ports for supported MicroPython devices.
324
325 @return tuple containing a list of tuples with the board type, the port
326 description, a description, the serial port it is connected at, the
327 VID and PID for known device types, a list of tuples with VID, PID
328 and description for unknown devices and a list of tuples with VID,
329 PID, description and port name for ports with missing VID or PID
330 @rtype tuple of (list of tuples of (str, str, str, str, int, int),
331 list of tuples of (int, int, str),
332 list of tuples of (int, int, str, str)
333 """
334 foundDevices = []
335 unknownDevices = []
336 unknownPorts = []
337
338 manualDevices = {}
339 for deviceDict in Preferences.getMicroPython("ManualDevices"):
340 manualDevices[(deviceDict["vid"], deviceDict["pid"])] = deviceDict
341
342 availablePorts = QSerialPortInfo.availablePorts()
343 for port in availablePorts:
344 if port.hasVendorIdentifier() and port.hasProductIdentifier():
345 supported = False
346 vid = port.vendorIdentifier()
347 pid = port.productIdentifier()
348
349 for board in SupportedBoards:
350 if (vid, pid) in SupportedBoards[board]["ids"] or (
351 vid,
352 None,
353 ) in SupportedBoards[board]["ids"]:
354 if board in ("bbc_microbit", "calliope") and (
355 port.description().strip()
356 != SupportedBoards[board]["port_description"]
357 ):
358 # both boards have the same VID and PID
359 # try to differentiate based on port description
360 continue
361 elif board in ("pyboard", "teensy") and (
362 not port.description().startswith(
363 SupportedBoards[board]["port_description"]
364 )
365 ):
366 # both boards have the same VID and PID
367 # try to differentiate based on port description
368 continue
369 foundDevices.append(
370 (
371 board,
372 port.description(),
373 SupportedBoards[board]["description"],
374 port.portName(),
375 vid,
376 pid,
377 port.serialNumber(),
378 )
379 )
380 supported = True
381 if not supported and (vid, pid) in manualDevices:
382 # check the locally added ones next
383 board = manualDevices[(vid, pid)]["type"]
384 foundDevices.append(
385 (
386 board,
387 port.description(),
388 SupportedBoards[board]["description"],
389 port.portName(),
390 vid,
391 pid,
392 port.serialNumber(),
393 )
394 )
395 supported = True
396 if not supported:
397 if vid and pid:
398 if (vid, pid) not in IgnoredBoards and (
399 vid,
400 None,
401 ) not in IgnoredBoards:
402 unknownDevices.append((vid, pid, port.description()))
403 logging.debug(
404 "Unknown device: (0x%04x:0x%04x %s)",
405 vid,
406 pid,
407 port.description(),
408 )
409 else:
410 # either VID or PID or both not detected
411 desc = port.description()
412 if not desc:
413 desc = QCoreApplication.translate(
414 "MicroPythonDevice", "Unknown Device"
415 )
416 unknownPorts.append((vid, pid, desc, port.portName()))
417
418 elif bool(port.portName()) and Preferences.getMicroPython(
419 "EnableManualDeviceSelection"
420 ):
421 # no VID and/or PID available (e.g. in Linux container of ChromeOS)
422 desc = port.description()
423 if not desc:
424 desc = QCoreApplication.translate("MicroPythonDevice", "Unknown Device")
425 unknownPorts.append((0, 0, desc, port.portName()))
426
427 return foundDevices, unknownDevices, unknownPorts
428
429
430 def getDeviceIcon(boardName, iconFormat=True):
431 """
432 Function to get the icon for the given board.
433
434 @param boardName name of the board
435 @type str
436 @param iconFormat flag indicating to get an icon or a pixmap
437 @type bool
438 @return icon for the board (iconFormat == True) or
439 a pixmap (iconFormat == False)
440 @rtype QIcon or QPixmap
441 """
442 iconName = (
443 SupportedBoards[boardName]["icon"]
444 if boardName in SupportedBoards
445 else
446 # return a generic MicroPython icon
447 "micropython48"
448 )
449
450 if iconFormat:
451 return EricPixmapCache.getIcon(iconName)
452 else:
453 return EricPixmapCache.getPixmap(iconName)
454
455
456 def getDevice(deviceType, microPythonWidget, vid, pid, boardName="", serialNumber=""):
457 """
458 Public method to instantiate a specific MicroPython device interface.
459
460 @param deviceType type of the device interface
461 @type str
462 @param microPythonWidget reference to the main MicroPython widget
463 @type MicroPythonWidget
464 @param vid vendor ID (only used for deviceType 'generic')
465 @type int
466 @param pid product ID (only used for deviceType 'generic')
467 @type int
468 @param boardName name of the board (defaults to "")
469 @type str (optional)
470 @param serialNumber serial number of the board (defaults to "")
471 @type str (optional)
472 @return instantiated device interface
473 @rtype MicroPythonDevice
474 """
475 deviceMapping = {
476 "bbc_microbit": ".MicrobitDevices",
477 "calliope": ".MicrobitDevices",
478 "circuitpython": ".CircuitPythonDevices",
479 "esp": ".EspDevices",
480 "generic": ".GenericMicroPythonDevices",
481 "pyboard": ".PyBoardDevices",
482 "rp2040": ".RP2040Devices",
483 "teensy": ".TeensyDevices",
484 }
485
486 with contextlib.suppress(KeyError):
487 mod = importlib.import_module(deviceMapping[deviceType], __package__)
488 if mod:
489 return mod.createDevice(
490 microPythonWidget, deviceType, vid, pid, boardName, serialNumber
491 )
492
493 # nothing specific requested or specific one failed or is not supported yet
494 return MicroPythonDevice(microPythonWidget, deviceType)
495
496
497 class MicroPythonDevice(QObject):
498 """
499 Base class for the more specific MicroPython devices.
500 """
501
502 def __init__(self, microPythonWidget, deviceType, parent=None):
503 """
504 Constructor
505
506 @param microPythonWidget reference to the main MicroPython widget
507 @type MicroPythonWidget
508 @param deviceType device type assigned to this device interface
509 @type str
510 @param parent reference to the parent object
511 @type QObject
512 """
513 super().__init__(parent)
514
515 self._deviceType = deviceType
516 self.microPython = microPythonWidget
517 self._deviceData = {} # dictionary with essential device data
518
519 def setConnected(self, connected):
520 """
521 Public method to set the connection state.
522
523 Note: This method can be overwritten to perform actions upon connect
524 or disconnect of the device.
525
526 @param connected connection state
527 @type bool
528 """
529 self._deviceData = {}
530
531 if connected:
532 with contextlib.suppress(OSError):
533 self._deviceData = self.microPython.commandsInterface().getDeviceData()
534
535 def getDeviceType(self):
536 """
537 Public method to get the device type.
538
539 @return type of the device
540 @rtype str
541 """
542 return self._deviceType
543
544 def getDeviceData(self):
545 """
546 Public method to get a copy of the determined device data.
547
548 @return dictionary containing the essential device data
549 @rtype dict
550 """
551 return copy.deepcopy(self._deviceData)
552
553 def checkDeviceData(self):
554 """
555 Public method to check the validity of the device data determined during
556 connecting the device.
557
558 @return flag indicating valid device data
559 @rtype bool
560 """
561 if bool(self._deviceData):
562 return True
563 else:
564 EricMessageBox.critical(
565 None,
566 self.tr("Show MicroPython Versions"),
567 self.tr(
568 """<p>The device data is not available. Try to connect to the"""
569 """ device again. Aborting...</p>"""
570 ).format(self.getDeviceType()),
571 )
572 return False
573
574 def setButtons(self):
575 """
576 Public method to enable the supported action buttons.
577 """
578 self.microPython.setActionButtons(
579 open=False, save=False, run=False, repl=False, files=False, chart=False
580 )
581
582 def forceInterrupt(self):
583 """
584 Public method to determine the need for an interrupt when opening the
585 serial connection.
586
587 @return flag indicating an interrupt is needed
588 @rtype bool
589 """
590 return True
591
592 def deviceName(self):
593 """
594 Public method to get the name of the device.
595
596 @return name of the device
597 @rtype str
598 """
599 return self.tr("Unsupported Device")
600
601 def canStartRepl(self):
602 """
603 Public method to determine, if a REPL can be started.
604
605 @return tuple containing a flag indicating it is safe to start a REPL
606 and a reason why it cannot.
607 @rtype tuple of (bool, str)
608 """
609 return False, self.tr("REPL is not supported by this device.")
610
611 def setRepl(self, on):
612 """
613 Public method to set the REPL status and dependent status.
614
615 @param on flag indicating the active status
616 @type bool
617 """
618 pass
619
620 def canStartPlotter(self):
621 """
622 Public method to determine, if a Plotter can be started.
623
624 @return tuple containing a flag indicating it is safe to start a
625 Plotter and a reason why it cannot.
626 @rtype tuple of (bool, str)
627 """
628 return False, self.tr("Plotter is not supported by this device.")
629
630 def setPlotter(self, on):
631 """
632 Public method to set the Plotter status and dependent status.
633
634 @param on flag indicating the active status
635 @type bool
636 """
637 pass
638
639 def canRunScript(self):
640 """
641 Public method to determine, if a script can be executed.
642
643 @return tuple containing a flag indicating it is safe to start a
644 Plotter and a reason why it cannot.
645 @rtype tuple of (bool, str)
646 """
647 return False, self.tr("Running scripts is not supported by this device.")
648
649 def runScript(self, script):
650 """
651 Public method to run the given Python script.
652
653 @param script script to be executed
654 @type str
655 """
656 pass
657
658 def canStartFileManager(self):
659 """
660 Public method to determine, if a File Manager can be started.
661
662 @return tuple containing a flag indicating it is safe to start a
663 File Manager and a reason why it cannot.
664 @rtype tuple of (bool, str)
665 """
666 return False, self.tr("File Manager is not supported by this device.")
667
668 def setFileManager(self, on):
669 """
670 Public method to set the File Manager status and dependent status.
671
672 @param on flag indicating the active status
673 @type bool
674 """
675 pass
676
677 def supportsLocalFileAccess(self):
678 """
679 Public method to indicate file access via a local directory.
680
681 @return flag indicating file access via local directory
682 @rtype bool
683 """
684 return False # default
685
686 def getWorkspace(self):
687 """
688 Public method to get the workspace directory.
689
690 @return workspace directory used for saving files
691 @rtype str
692 """
693 return (
694 Preferences.getMicroPython("MpyWorkspace")
695 or Preferences.getMultiProject("Workspace")
696 or os.path.expanduser("~")
697 )
698
699 def selectDeviceDirectory(self, deviceDirectories):
700 """
701 Public method to select the device directory from a list of detected
702 ones.
703
704 @param deviceDirectories list of directories to select from
705 @type list of str
706 @return selected directory or an empty string
707 @rtype str
708 """
709 deviceDirectory, ok = QInputDialog.getItem(
710 None,
711 self.tr("Select Device Directory"),
712 self.tr("Select the directory for the connected device:"),
713 [""] + deviceDirectories,
714 0,
715 False,
716 )
717 if ok:
718 return deviceDirectory
719 else:
720 # user cancelled
721 return ""
722
723 def sendCommands(self, commandsList):
724 """
725 Public method to send a list of commands to the device.
726
727 @param commandsList list of commands to be sent to the device
728 @type list of str
729 """
730 rawOn = [ # sequence of commands to enter raw mode
731 b"\x02", # Ctrl-B: exit raw repl (just in case)
732 b"\r\x03\x03\x03", # Ctrl-C three times: interrupt any running
733 # program
734 b"\r\x01", # Ctrl-A: enter raw REPL
735 ]
736 newLine = [
737 b'print("\\n")\r',
738 ]
739 commands = [c.encode("utf-8)") + b"\r" for c in commandsList]
740 commands.append(b"\r")
741 commands.append(b"\x04")
742 rawOff = [b"\x02", b"\x02"]
743 commandSequence = rawOn + newLine + commands + rawOff
744 self.microPython.commandsInterface().executeAsync(commandSequence)
745
746 @pyqtSlot()
747 def handleDataFlood(self):
748 """
749 Public slot handling a data floof from the device.
750 """
751 pass
752
753 def addDeviceMenuEntries(self, menu):
754 """
755 Public method to add device specific entries to the given menu.
756
757 @param menu reference to the context menu
758 @type QMenu
759 """
760 pass
761
762 def hasFlashMenuEntry(self):
763 """
764 Public method to check, if the device has its own flash menu entry.
765
766 @return flag indicating a specific flash menu entry
767 @rtype bool
768 """
769 return False
770
771 def hasTimeCommands(self):
772 """
773 Public method to check, if the device supports time commands.
774
775 The default returns True.
776
777 @return flag indicating support for time commands
778 @rtype bool
779 """
780 return True
781
782 def hasDocumentationUrl(self):
783 """
784 Public method to check, if the device has a configured documentation
785 URL.
786
787 @return flag indicating a configured documentation URL
788 @rtype bool
789 """
790 return bool(self.getDocumentationUrl())
791
792 def getDocumentationUrl(self):
793 """
794 Public method to get the device documentation URL.
795
796 @return documentation URL of the device
797 @rtype str
798 """
799 return ""
800
801 def hasFirmwareUrl(self):
802 """
803 Public method to check, if the device has a configured firmware
804 download URL.
805
806 @return flag indicating a configured firmware download URL
807 @rtype bool
808 """
809 return bool(self.getFirmwareUrl())
810
811 def getFirmwareUrl(self):
812 """
813 Public method to get the device firmware download URL.
814
815 @return firmware download URL of the device
816 @rtype str
817 """
818 return ""
819
820 def downloadFirmware(self):
821 """
822 Public method to download the device firmware.
823 """
824 url = self.getFirmwareUrl()
825 if url:
826 ericApp().getObject("UserInterface").launchHelpViewer(url)
827
828 def getDownloadMenuEntries(self):
829 """
830 Public method to retrieve the entries for the downloads menu.
831
832 @return list of tuples with menu text and URL to be opened for each
833 entry
834 @rtype list of tuple of (str, str)
835 """
836 return []

eric ide

mercurial