--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/MicroPython/Devices/__init__.py Mon Feb 13 17:49:52 2023 +0100 @@ -0,0 +1,490 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package containing the device interface modules and device specific dialogs. +""" + +import contextlib +import importlib +import logging + +from PyQt6.QtCore import QCoreApplication +from PyQt6.QtSerialPort import QSerialPortInfo + +from eric7 import Preferences +from eric7.EricGui import EricPixmapCache + +from .DeviceBase import BaseDevice + +SupportedBoards = { + "bbc_microbit": { + "ids": [ + (0x0D28, 0x0204), # micro:bit + ], + "description": "BBC micro:bit", + "icon": "microbitDevice", + "port_description": "BBC micro:bit CMSIS-DAP", + "module": ".MicrobitDevices", + }, + "calliope": { + "ids": [ + (0x0D28, 0x0204), # Calliope mini + ], + "description": "Calliope mini", + "icon": "calliope_mini", + "port_description": "DAPLink CMSIS-DAP", + "module": ".MicrobitDevices", + }, + "circuitpython": { + "ids": [ + (0x0483, 0x572A), # STMicroelectronics NUCLEO-F446RE - CPy + (0x04D8, 0xE799), # Cytron Maker Zero SAMD21 + (0x04D8, 0xEA2A), # BHDynamics DynaLoRa_USB + (0x04D8, 0xEAD1), # BH Dynamics DynOSSAT-EDU-EPS-v1.0 + (0x04D8, 0xEAD2), # BH Dynamics DynOSSAT-EDU-OBC-v1.0 + (0x04D8, 0xEC44), # maholli PyCubed + (0x04D8, 0xEC63), # Kevin Neubauer CircuitBrains Basic + (0x04D8, 0xEC64), # Kevin Neubauer CircuitBrains Deluxe + (0x04D8, 0xEC72), # XinaBox CC03 + (0x04D8, 0xEC75), # XinaBox CS11 + (0x04D8, 0xED5F), # Itaca Innovation uChip CircuitPython + (0x04D8, 0xED94), # maholli kicksat-sprite + (0x04D8, 0xEDB3), # Capable Robot Programmable USB Hub + (0x04D8, 0xEDBE), # maholli SAM32 + (0x04D8, 0xEE8C), # J&J Studios LLC datum-Distance + (0x04D8, 0xEE8D), # J&J Studios LLC datum-IMU + (0x04D8, 0xEE8E), # J&J Studios LLC datum-Light + (0x04D8, 0xEE8F), # J&J Studios LLC datum-Weather + (0x04D8, 0xEF67), # senseBox MCU + (0x054C, 0x0BC2), # Sony Spresense + (0x1209, 0x2017), # Benjamin Shockley Mini SAM M4 + (0x1209, 0x3141), # CrumpSpace CrumpS2 + (0x1209, 0x3252), # Targett Module Clip w/Wroom + (0x1209, 0x3253), # Targett Module Clip w/Wrover + (0x1209, 0x4203), # 42. Keebs Frood + (0x1209, 0x4D43), # Robotics Masters Robo HAT MM1 M4 + (0x1209, 0x4DDD), # ODT CP Sapling + (0x1209, 0x4DDE), # ODT CP Sapling M0 w/ SPI Flash + (0x1209, 0x4DDF), # ODT CP Sapling Rev B + (0x1209, 0x4DF0), # Oak Dev Tech Pixelwing ESP32S2 + (0x1209, 0x4DF1), # Oak Dev Tech BREAD2040 + (0x1209, 0x4DF2), # Oak Dev Tech CAST AWAY RP2040 + (0x1209, 0x5A52), # ZRichard RP2.65-F + (0x1209, 0x5BF0), # Foosn Fomu + (0x1209, 0x7150), # Electronic Cats Hunter Cat NFC + (0x1209, 0x7382), # Invector Labs AB iLabs Challenger 840 + (0x1209, 0x805A), # Electronic Cats BastBLE + (0x1209, 0x8CAE), # takayoshiotake Octave RP2040 + (0x1209, 0xA182), # Solder Party RP2040 Stamp + (0x1209, 0xB182), # Solder Party BBQ20 Keyboard + (0x1209, 0xBAB0), # Electronic Cats Bast WiFi + (0x1209, 0xBAB1), # Electronic Cats Meow Meow + (0x1209, 0xBAB2), # Electronic Cats CatWAN USBStick + (0x1209, 0xBAB3), # Electronic Cats Bast Pro Mini M0 + (0x1209, 0xBAB6), # Electronic Cats Escornabot Makech + (0x1209, 0xBAB8), # Electronic Cats NFC Copy Cat + (0x1209, 0xC051), # Betrusted Simmel + (0x1209, 0xCB74), # 0xCB Helios + (0x1209, 0xD10D), # Diodes Delight Piunora + (0x1209, 0xD1B5), # Radomir Dopieralski PewPew LCD + (0x1209, 0xE3E3), # StackRduino M0 PRO + (0x1209, 0xEF00), # 2231puppy E-Fidget + (0x1209, 0xF123), # Electrolama minik + (0x1209, 0xF500), # Silicognition LLC M4-Shim + (0x1209, 0xF502), # Silicognition LLC RP2040-Shim + (0x16D0, 0x08C6), # Pimoroni Keybow 2040 + (0x16D0, 0x08C7), # Pimoroni Tiny 2040 (8MB) + (0x16D0, 0x08C8), # Pimoroni PicoSystem + (0x16D0, 0x10ED), # Mechwild PillBug + (0x1915, 0xB001), # Makerdiary Pitaya Go + (0x192F, 0xB1B2), # WarmBit BluePixel nRF52840 + (0x1B4F, 0x0015), # SparkFun RedBoard Turbo Board + (0x1B4F, 0x0016), # SparkFun SAMD51 Thing+ + (0x1B4F, 0x0017), # SparkFun LUMIDrive Board + (0x1B4F, 0x0020), # SparkFun MicroMod SAMD51 Processor + (0x1B4F, 0x0021), # SparkFun MicroMod nRF52840 Processor + (0x1B4F, 0x0024), # SparkFun MicroMod RP2040 Processor + (0x1B4F, 0x0025), # SparkFun Thing Plus RP2040 + (0x1B4F, 0x0026), # SparkFun Pro Micro RP2040 + (0x1B4F, 0x0027), # SparkFun STM32 MicroMod Processor + (0x1B4F, 0x0028), # SparkFun Thing Plus - STM32 + (0x1B4F, 0x002E), # PJRC/Sparkfun Teensy MicroMod + (0x1B4F, 0x5289), # SparkFun SFE_nRF52840_Mini + (0x1B4F, 0x8D22), # SparkFun SAMD21 Mini Breakout + (0x1B4F, 0x8D23), # SparkFun SAMD21 Dev Breakout + (0x1B4F, 0x8D24), # SparkFun Qwiic Micro + (0x1D50, 0x60E8), # Radomir Dopieralski PewPew M4 + (0x1D50, 0x6152), # nrf52.jpconstantineau.com BlueMicro833 + (0x1D50, 0x6153), # JPConstantineau PyKey18 + (0x1D50, 0x6153), # JPConstantineau PyKey44 + (0x1D50, 0x6153), # JPConstantineau PyKey60 + (0x1D50, 0x6153), # JPConstantineau PyKey87 + (0x1D50, 0x6154), # JPConstantineau EncoderPad RP2040 + (0x1D50, 0x6161), # nrf52.jpconstantineau.com BlueMicro840 + (0x2019, 0x7103), # Benjamin Shockley Fig Pi + (0x2341, 0x8053), # Arduino MKR1300 + (0x2341, 0x8057), # Arduino Nano 33 IoT + (0x2341, 0x805A), # Arduino Arduino_Nano_33_BLE + (0x2341, 0x824D), # Arduino Zero + (0x2786, 0x9207), # Switch Sc. BLE-SS dev board Multi Sensor + (0x2786, 0x920D), # Switch Sc. SSCI ISP1807 Dev Board + (0x2786, 0x920F), # Switch Sc. SSCI ISP1807 Micro Board + (0x2886, 0x002F), # Seeed Seeeduino XIAO + (0x2886, 0x0042), # Seeed Seeeduino XIAO RP2040 + (0x2886, 0x0045), # Seeed XIAO nRF52840 Sense + (0x2886, 0x802D), # Seeed Seeeduino Wio Terminal + (0x2886, 0x802F), # Seeed Seeeduino XIAO KB + (0x2886, 0xF001), # Makerdiary nRF52840 M.2 Developer Kit + (0x2886, 0xF002), # Makerdiary M60 Keyboard + (0x2B04, 0xC00C), # Particle Argon + (0x2B04, 0xC00D), # Particle Boron + (0x2B04, 0xC00E), # Particle Xenon + (0x2E8A, 0x1000), # Cytron Maker Pi RP2040 + (0x2E8A, 0x1002), # Pimoroni Pico LiPo (4MB) + (0x2E8A, 0x1003), # Pimoroni Pico LiPo (16MB) + (0x2E8A, 0x1005), # Melopero Shake RP2040 + (0x2E8A, 0x1006), # Invector Labs Challenger RP2040 WiFi + (0x2E8A, 0x1008), # Pimoroni PGA2040 + (0x2E8A, 0x1009), # Pimoroni Interstate 75 + (0x2E8A, 0x100A), # Pimoroni Plasma 2040 + (0x2E8A, 0x100B), # Invector Labs Challenger RP2040 LTE + (0x2E8A, 0x100D), # Invector Labs Challenger NB RP2040 WiFi + (0x2E8A, 0x100E), # Raspberry Pi Zero + (0x2E8A, 0x100F), # Cytron Maker Nano RP2040 + (0x2E8A, 0x1012), # Raspberry Pi Compute Module 4 IO Board + (0x2E8A, 0x1013), # Raspberry Pi 4B + (0x2E8A, 0x1014), # Raspberry Pi Compute Module 4 + (0x2E8A, 0x1015), # Raspberry Pi Zero 2W + (0x2E8A, 0x1016), # Pimoroni Tiny 2040 (2MB) + (0x2E8A, 0x1019), # Pimoroni Motor 2040 + (0x2E8A, 0x101A), # Pimoroni Servo 2040 + (0x2E8A, 0x101B), # Pimoroni Badger 2040 + (0x2E8A, 0x101E), # Raspberry Pi Zero W + (0x2E8A, 0x101F), # Waveshare Electronics RP2040-Zero + (0x2E8A, 0x1023), # Invector Labs Challenger RP2040 LoRa + (0x2E8A, 0x1026), # ELECFREAKS Pico:ed + (0x2E8A, 0x1027), # WIZnet W5100S-EVB-Pico + (0x2E8A, 0x1029), # WIZnet W5500-EVB-Pico + (0x2E8A, 0x102C), # Invector Labs Challenger RP2040 WiFi/BLE + (0x2E8A, 0x102D), # Invector Labs Challenger RP2040 SD/RTC + (0x2E8A, 0x102E), # VCC-GND Studio YD-RP2040 + (0x2E8A, 0x1032), # Invector Labs Challenger RP2040 SubGHz + (0x2E8A, 0x1039), # Waveshare Electronics Waveshare RP2040-LCD-1.28 + (0x2E8A, 0x1048), # nullbits Bit-C PRO + (0x303A, 0x7001), # Espressif ESP32-S2-HMI-DevKit-1 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N32R8 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8R2 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-N8R8 + (0x303A, 0x7003), # Espressif ESP32-S3-DevKitC-1-nopsram + (0x303A, 0x7005), # Espressif ESP32-S3-Box-2.5 + (0x303A, 0x7007), # Espressif ESP32-S3-DevKitM-1-N8 + (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N4 + (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N4R2 + (0x303A, 0x7009), # Espressif ESP32-S2-DevKitC-1-N8R2 + (0x303A, 0x700B), # Espressif ESP32-S3-USB-OTG-N8 + (0x303A, 0x700D), # Espressif ESP32-S3-Box-Lite + (0x303A, 0x700F), # Espressif ESP32-S3-EYE + (0x303A, 0x8002), # UnexpectedMaker TinyS2 + (0x303A, 0x8007), # LILYGO TTGO T8 ESP32-S2 + (0x303A, 0x800D), # Gravitech Cucumber RS + (0x303A, 0x80A1), # Gravitech Cucumber R + (0x303A, 0x80A4), # Gravitech Cucumber M + (0x303A, 0x80A7), # Gravitech Cucumber MS + (0x303A, 0x80AA), # Espressif Franzininho WIFI w/Wroom + (0x303A, 0x80AD), # Espressif Franzininho WIFI w/Wrover + (0x303A, 0x80AF), # Artisense Reference Design RD00 + (0x303A, 0x80B2), # Muselab nanoESP32-S2 w/Wrover + (0x303A, 0x80B5), # UnexpectedMaker FeatherS2 Neo + (0x303A, 0x80B7), # MORPHEANS MORPHESP-240 + (0x303A, 0x80C3), # Lolin S2 Mini + (0x303A, 0x80C6), # Lolin S2 Pico + (0x303A, 0x80D1), # UnexpectedMaker TinyS3 + (0x303A, 0x80D4), # UnexpectedMaker ProS3 + (0x303A, 0x80D7), # UnexpectedMaker FeatherS3 + (0x303A, 0x80D9), # FutureKeys HexKy_S2 + (0x303A, 0x80E0), # BananaPi BPI-Leaf-S3 + (0x303A, 0x80E6), # BananaPi BPI-Bit-S2 + (0x303A, 0x80E8), # HiiBot IoTs2 + (0x303A, 0x80EA), # LILYGO TTGO T8 ESP32-S2-WROOM + (0x303A, 0x80ED), # LILYGO TTGO T8 ESP32-S2 + (0x303A, 0x80F9), # Cytron Maker Feather AIoT S3 + (0x303A, 0x80FC), # Espressif MixGo CE + (0x303A, 0x80FD), # Espressif MixGo CE + (0x303A, 0x810A), # Waveshare Electronics ESP32-S2-Pico + (0x303A, 0x810C), # Waveshare Electronics ESP32-S2-Pico-LCD + (0x303A, 0x8111), # Smart Bee Designs Bee-S3 + (0x303A, 0x8114), # Smart Bee Designs Bee-Motion-S3 + (0x303A, 0x8117), # WEMOS LOLIN S3 16MB Flash 8MB PSRAM + (0x303A, 0x812C), # BananaPi BPI-PicoW-S3 + (0x30A4, 0x0002), # Blues Inc. Swan R5 + (0x3171, 0x0101), # 8086.net Commander + (0x31E2, 0x2001), # BDMICRO LLC VINA-D21 + (0x31E2, 0x2011), # BDMICRO LLC VINA-D51 + (0x31E2, 0x2021), # BDMICRO LLC VINA-D51 + (0x32BD, 0x3001), # Alorium Tech. AloriumTech Evo M51 + (0x4097, 0x0001), # TG-Boards Datalore IP M4 + (0x612B, 0x80A7), # Ai-Thinker ESP 12k NodeMCU + (0x239A, None), # Any Adafruit Boards + ], + "description": "CircuitPython", + "icon": "circuitPythonDevice", + "port_description": "", + "module": ".CircuitPythonDevices", + }, + "esp": { + "ids": [ + (0x0403, 0x6001), # M5Stack ESP32 device"), + (0x0403, 0x6001), # FT232/FT245 (XinaBox CW01, CW02) + (0x0403, 0x6010), # FT2232C/D/L/HL/Q (ESP-WROVER-KIT) + (0x0403, 0x6011), # FT4232 + (0x0403, 0x6014), # FT232H + (0x0403, 0x6015), # Sparkfun ESP32 + (0x0403, 0x601C), # FT4222H + (0x10C4, 0xEA60), # CP210x + (0x1A86, 0x55D4), # CH343 + (0x1A86, 0x7523), # HL-340, CH340 + ], + "description": "ESP32, ESP8266", + "icon": "esp32Device", + "port_description": "", + "module": ".EspDevices", + }, + "generic": { + # only manually configured devices use this + "ids": [], + "description": QCoreApplication.translate("MicroPythonDevice", "Generic Board"), + "icon": "micropython48", + "port_description": "", + "module": ".GenericMicroPythonDevices", + }, + "pyboard": { + "ids": [ + (0xF055, 0x9800), # Pyboard in CDC mode + (0xF055, 0x9801), # Pyboard in CDC+HID mode + (0xF055, 0x9802), # Pyboard in CDC+MSC mode + ], + "description": "PyBoard", + "icon": "micropython48", + "port_description": "Pyboard", + "module": ".PyBoardDevices", + }, + "rp2040": { + "ids": [ + (0x2E8A, 0x0005), # Raspberry Pi Pico + ], + "description": QCoreApplication.translate("MicroPythonDevice", "RP2040 based"), + "icon": "rp2040Device", + "port_description": "", + "module": ".RP2040Devices", + }, + "teensy": { + "ids": [ + (0xF055, 0x9802), # Pyboard in CDC+MSC mode + ], + "description": "Teensy", + "icon": "micropython48", + "port_description": "Teensy", + "module": ".TeensyDevices", + }, +} + +IgnoredBoards = ( + (0x8086, 0x9C3D), + (0x8086, None), +) + +FirmwareGithubUrls = { + "micropython": "https://github.com/micropython/micropython/releases/latest", + "circuitpython": "https://github.com/adafruit/circuitpython/releases/latest", + "pimoroni_pico": "https://github.com/pimoroni/pimoroni-pico/releases/latest", + "microbit_v1": "https://github.com/bbcmicrobit/micropython/releases/latest", + "microbit_v2": ( + "https://github.com/microbit-foundation/micropython-microbit-v2/releases/latest" + ), +} + + +def getSupportedDevices(): + """ + Function to get a list of supported MicroPython devices. + + @return set of tuples with the board type and description + @rtype set of tuples of (str, str) + """ + boards = [] + for board in SupportedBoards: + boards.append((board, SupportedBoards[board]["description"])) + return boards + + +def getFoundDevices(): + """ + Function to check the serial ports for supported MicroPython devices. + + @return tuple containing a list of tuples with the board type, the port + description, a description, the serial port it is connected at, the + VID and PID for known device types, a list of tuples with VID, PID + and description for unknown devices and a list of tuples with VID, + PID, description and port name for ports with missing VID or PID + @rtype tuple of (list of tuples of (str, str, str, str, int, int), + list of tuples of (int, int, str), + list of tuples of (int, int, str, str) + """ + foundDevices = [] + unknownDevices = [] + unknownPorts = [] + + manualDevices = {} + for deviceDict in Preferences.getMicroPython("ManualDevices"): + manualDevices[(deviceDict["vid"], deviceDict["pid"])] = deviceDict + + availablePorts = QSerialPortInfo.availablePorts() + for port in availablePorts: + if port.hasVendorIdentifier() and port.hasProductIdentifier(): + supported = False + vid = port.vendorIdentifier() + pid = port.productIdentifier() + + for board in SupportedBoards: + if (vid, pid) in SupportedBoards[board]["ids"] or ( + vid, + None, + ) in SupportedBoards[board]["ids"]: + if board in ("bbc_microbit", "calliope") and ( + port.description().strip() + != SupportedBoards[board]["port_description"] + ): + # both boards have the same VID and PID + # try to differentiate based on port description + continue + elif board in ("pyboard", "teensy") and ( + not port.description().startswith( + SupportedBoards[board]["port_description"] + ) + ): + # both boards have the same VID and PID + # try to differentiate based on port description + continue + foundDevices.append( + ( + board, + port.description(), + SupportedBoards[board]["description"], + port.portName(), + vid, + pid, + port.serialNumber(), + ) + ) + supported = True + if not supported and (vid, pid) in manualDevices: + # check the locally added ones next + board = manualDevices[(vid, pid)]["type"] + foundDevices.append( + ( + board, + port.description(), + SupportedBoards[board]["description"], + port.portName(), + vid, + pid, + port.serialNumber(), + ) + ) + supported = True + if not supported: + if vid and pid: + if (vid, pid) not in IgnoredBoards and ( + vid, + None, + ) not in IgnoredBoards: + unknownDevices.append((vid, pid, port.description())) + logging.debug( + "Unknown device: (0x%04x:0x%04x %s)", + vid, + pid, + port.description(), + ) + else: + # either VID or PID or both not detected + desc = port.description() + if not desc: + desc = QCoreApplication.translate( + "MicroPythonDevice", "Unknown Device" + ) + unknownPorts.append((vid, pid, desc, port.portName())) + + elif bool(port.portName()) and Preferences.getMicroPython( + "EnableManualDeviceSelection" + ): + # no VID and/or PID available (e.g. in Linux container of ChromeOS) + desc = port.description() + if not desc: + desc = QCoreApplication.translate("MicroPythonDevice", "Unknown Device") + unknownPorts.append((0, 0, desc, port.portName())) + + return foundDevices, unknownDevices, unknownPorts + + +def getDeviceIcon(boardName, iconFormat=True): + """ + Function to get the icon for the given board. + + @param boardName name of the board + @type str + @param iconFormat flag indicating to get an icon or a pixmap + @type bool + @return icon for the board (iconFormat == True) or + a pixmap (iconFormat == False) + @rtype QIcon or QPixmap + """ + iconName = ( + SupportedBoards[boardName]["icon"] + if boardName in SupportedBoards + else + # return a generic MicroPython icon + "micropython48" + ) + + if iconFormat: + return EricPixmapCache.getIcon(iconName) + else: + return EricPixmapCache.getPixmap(iconName) + + +def getDevice(deviceType, microPythonWidget, vid, pid, boardName="", serialNumber=""): + """ + Public method to instantiate a specific MicroPython device interface. + + @param deviceType type of the device interface + @type str + @param microPythonWidget reference to the main MicroPython widget + @type MicroPythonWidget + @param vid vendor ID (only used for deviceType 'generic') + @type int + @param pid product ID (only used for deviceType 'generic') + @type int + @param boardName name of the board (defaults to "") + @type str (optional) + @param serialNumber serial number of the board (defaults to "") + @type str (optional) + @return instantiated device interface + @rtype BaseDevice + """ + + with contextlib.suppress(KeyError): + mod = importlib.import_module( + SupportedBoards[deviceType]["module"], __package__ + ) + if mod: + return mod.createDevice( + microPythonWidget, deviceType, vid, pid, boardName, serialNumber + ) + + # nothing specific requested or specific one failed or is not supported yet + return BaseDevice(microPythonWidget, deviceType)