|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing some utility functions and the MicroPythonDevice base |
|
8 class. |
|
9 """ |
|
10 |
|
11 from __future__ import unicode_literals |
|
12 |
|
13 import logging |
|
14 |
|
15 from PyQt5.QtCore import QObject |
|
16 |
|
17 import Globals |
|
18 import UI.PixmapCache |
|
19 |
|
20 |
|
21 SupportedBoards = { |
|
22 "esp": { |
|
23 "ids": [ |
|
24 (0x1A86, 0x7523), # HL-340 |
|
25 (0x10C4, 0xEA60), # CP210x |
|
26 (0x0403, 0x6015), # Sparkfun ESP32 VID, PID |
|
27 ], |
|
28 "description": "ESP8266, ESP32", |
|
29 "icon": "esp32Device", |
|
30 }, |
|
31 |
|
32 "adafruit": { |
|
33 "ids": [ |
|
34 (0x239A, 0x8015), # Adafruit Feather M0 CircuitPython |
|
35 (0x239A, 0x8023), # Adafruit Feather M0 Express CircuitPython |
|
36 (0x239A, 0x801B), # Adafruit Feather M0 Express CircuitPython |
|
37 (0x239A, 0x8014), # Adafruit Metro M0 CircuitPython |
|
38 (0x239A, 0x8019), # Adafruit CircuitPlayground |
|
39 # Express CircuitPython |
|
40 (0x239A, 0x801D), # Adafruit Gemma M0 |
|
41 (0x239A, 0x801F), # Adafruit Trinket M0 |
|
42 (0x239A, 0x8012), # Adafruit ItsyBitsy M0 |
|
43 (0x239A, 0x8021), # Adafruit Metro M4 |
|
44 (0x239A, 0x8025), # Adafruit Feather RadioFruit |
|
45 (0x239A, 0x8026), # Adafruit Feather M4 |
|
46 (0x239A, 0x8028), # Adafruit pIRKey M0 |
|
47 (0x239A, 0x802A), # Adafruit Feather 52840 |
|
48 (0x239A, 0x802C), # Adafruit Itsy M4 |
|
49 (0x239A, 0x802E), # Adafruit CRICKit M0 |
|
50 (0x239A, 0xD1ED), # Adafruit HalloWing M0 |
|
51 (0x239A, 0x8030), # Adafruit NeoTrellis M4 |
|
52 (0x239A, 0x8032), # Grand Central |
|
53 (0x2B04, 0xC00C), # Particle Argon |
|
54 (0x2B04, 0xC00D), # Particle Boron |
|
55 (0x2B04, 0xC00E), # Particle Xenon |
|
56 (0x239A, 0x8034), # future board |
|
57 (0x239A, 0x8036), # future board |
|
58 (0x239A, 0x8038), # future board |
|
59 (0x239A, 0x803A), # future board |
|
60 (0x239A, 0x803C), # future board |
|
61 (0x239A, 0x803E), # future board |
|
62 (0x239A, None), # Any Adafruit Boards |
|
63 ], |
|
64 "description": "Adafruit CircuitPython", |
|
65 "icon": "adafruitDevice", |
|
66 }, |
|
67 |
|
68 "bbc_microbit": { |
|
69 "ids": [ |
|
70 (0x0D28, 0x0204), # micro:bit USB VID, PID |
|
71 ], |
|
72 "description": "BBC micro:bit", |
|
73 "icon": "microbitDevice", |
|
74 }, |
|
75 } |
|
76 |
|
77 |
|
78 def getSupportedDevices(): |
|
79 """ |
|
80 Function to get a list of supported MicroPython devices. |
|
81 |
|
82 @return set of tuples with the board type and description |
|
83 @rtype set of tuples of (str, str) |
|
84 """ |
|
85 boards = [] |
|
86 for board in SupportedBoards: |
|
87 boards.append( |
|
88 (board, SupportedBoards[board]["description"])) |
|
89 return boards |
|
90 |
|
91 |
|
92 def getFoundDevices(): |
|
93 """ |
|
94 Function to check the serial ports for supported MicroPython devices. |
|
95 |
|
96 @return set of tuples with the board type, a description and the serial |
|
97 port it is connected at |
|
98 @rtype set of tuples of (str, str, str) |
|
99 """ |
|
100 from PyQt5.QtSerialPort import QSerialPortInfo |
|
101 |
|
102 foundDevices = [] |
|
103 |
|
104 availablePorts = QSerialPortInfo.availablePorts() |
|
105 for port in availablePorts: |
|
106 vid = port.vendorIdentifier() |
|
107 pid = port.productIdentifier() |
|
108 for board in SupportedBoards: |
|
109 if ((vid, pid) in SupportedBoards[board]["ids"] or |
|
110 (vid, None) in SupportedBoards[board]["ids"]): |
|
111 foundDevices.append( |
|
112 (board, SupportedBoards[board]["description"], |
|
113 port.portName())) |
|
114 break |
|
115 else: |
|
116 logging.debug("Unknown device: (0x%04x:0x%04x)", vid, pid) |
|
117 |
|
118 return foundDevices |
|
119 |
|
120 |
|
121 def getDeviceIcon(boardName, iconFormat=True): |
|
122 """ |
|
123 Function to get the icon for the given board. |
|
124 |
|
125 @param boardName name of the board |
|
126 @type str |
|
127 @param iconFormat flag indicating to get an icon or a pixmap |
|
128 @type bool |
|
129 @return icon for the board (iconFormat == True) or |
|
130 a pixmap (iconFormat == False) |
|
131 @rtype QIcon or QPixmap |
|
132 """ |
|
133 if boardName in SupportedBoards: |
|
134 iconName = SupportedBoards[boardName]["icon"] |
|
135 else: |
|
136 # return a generic MicroPython icon |
|
137 iconName = "micropython48" |
|
138 |
|
139 if iconFormat: |
|
140 return UI.PixmapCache.getIcon(iconName) |
|
141 else: |
|
142 return UI.PixmapCache.getPixmap(iconName) |
|
143 |
|
144 |
|
145 def getDevice(deviceType): |
|
146 """ |
|
147 Public method to instantiate a specific MicroPython device interface. |
|
148 |
|
149 @param deviceType type of the device interface |
|
150 @type str |
|
151 @return instantiated device interface |
|
152 @rtype MicroPythonDevice |
|
153 """ |
|
154 # TODO: not implemented yet |
|
155 return None |
|
156 |
|
157 |
|
158 class MicroPythonDevice(QObject): |
|
159 """ |
|
160 Base class for the more specific MicroPython devices. |
|
161 """ |
|
162 def __init__(self, parent=None): |
|
163 """ |
|
164 Constructor |
|
165 |
|
166 @param parent reference to the parent object |
|
167 @type QObject |
|
168 """ |
|
169 super(MicroPythonDevice, self).__init__(parent) |
|
170 |
|
171 def supportedActions(self): |
|
172 """ |
|
173 Public method to get the names of the supported actions. |
|
174 |
|
175 @return tuple of supported actions out of "repl", "run", "files", |
|
176 "chart" |
|
177 @rtype tuple of str |
|
178 """ |
|
179 return tuple() |
|
180 |
|
181 def findDevice(self, deviceType): |
|
182 """ |
|
183 Public method to find the first device of a specific type. |
|
184 |
|
185 @param deviceType device type |
|
186 @type str |
|
187 @return tuple containing the port the device is connected to and its |
|
188 serial number |
|
189 @rtype tuple of (str, str) |
|
190 """ |
|
191 from PyQt5.QtSerialPort import QSerialPortInfo |
|
192 |
|
193 availablePorts = QSerialPortInfo.availablePorts() |
|
194 for port in availablePorts: |
|
195 vid = port.vendorIdentifier() |
|
196 pid = port.productIdentifier() |
|
197 for board in SupportedBoards: |
|
198 if ((vid, pid) in SupportedBoards[board] or |
|
199 (vid, None) in SupportedBoards[board]): |
|
200 portName = port.portName() |
|
201 serialNumber = port.serialNumber() |
|
202 return (self.__portPath(portName), serialNumber) |
|
203 |
|
204 return (None, None) |
|
205 |
|
206 def __portPath(self, portName): |
|
207 """ |
|
208 Private method to get the full path of a given port. |
|
209 |
|
210 @param portName name of the port the path shall be determined for |
|
211 @type str |
|
212 @return full port path |
|
213 @rtype str |
|
214 """ |
|
215 if Globals.isWindowsPlatform(): |
|
216 # return name unchanged |
|
217 return portName |
|
218 else: |
|
219 # assume Posix system (Linux or macOS) |
|
220 return "/dev/{0}".format(portName) |