micropython-stubber 1.24.1__py3-none-any.whl → 1.24.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {micropython_stubber-1.24.1.dist-info → micropython_stubber-1.24.4.dist-info}/METADATA +9 -29
- micropython_stubber-1.24.4.dist-info/RECORD +107 -0
- {micropython_stubber-1.24.1.dist-info → micropython_stubber-1.24.4.dist-info}/WHEEL +1 -1
- stubber/__init__.py +1 -1
- stubber/board/createstubs.py +44 -38
- stubber/board/createstubs_db.py +17 -12
- stubber/board/createstubs_db_min.py +63 -63
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +17 -12
- stubber/board/createstubs_mem_min.py +99 -99
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +111 -112
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/modulelist.txt +27 -27
- stubber/codemod/board.py +1 -1
- stubber/codemod/enrich.py +13 -13
- stubber/codemod/merge_docstub.py +83 -53
- stubber/codemod/visitors/type_helpers.py +143 -41
- stubber/commands/enrich_folder_cmd.py +17 -17
- stubber/commands/get_docstubs_cmd.py +27 -9
- stubber/commands/get_frozen_cmd.py +1 -0
- stubber/commands/merge_cmd.py +2 -4
- stubber/merge_config.py +5 -36
- stubber/minify.py +3 -3
- stubber/modcat.py +118 -0
- stubber/publish/merge_docstubs.py +22 -5
- stubber/publish/stubpackage.py +33 -28
- stubber/rst/lookup.py +6 -23
- stubber/rst/reader.py +8 -13
- stubber/stubs_from_docs.py +2 -1
- stubber/tools/manifestfile.py +2 -1
- stubber/{cst_transformer.py → typing_collector.py} +36 -4
- micropython_stubber-1.24.1.dist-info/RECORD +0 -161
- mpflash/README.md +0 -220
- mpflash/libusb_flash.ipynb +0 -203
- mpflash/mpflash/__init__.py +0 -0
- mpflash/mpflash/add_firmware.py +0 -98
- mpflash/mpflash/ask_input.py +0 -236
- mpflash/mpflash/basicgit.py +0 -324
- mpflash/mpflash/bootloader/__init__.py +0 -2
- mpflash/mpflash/bootloader/activate.py +0 -60
- mpflash/mpflash/bootloader/detect.py +0 -82
- mpflash/mpflash/bootloader/manual.py +0 -101
- mpflash/mpflash/bootloader/micropython.py +0 -12
- mpflash/mpflash/bootloader/touch1200.py +0 -36
- mpflash/mpflash/cli_download.py +0 -129
- mpflash/mpflash/cli_flash.py +0 -224
- mpflash/mpflash/cli_group.py +0 -111
- mpflash/mpflash/cli_list.py +0 -87
- mpflash/mpflash/cli_main.py +0 -39
- mpflash/mpflash/common.py +0 -217
- mpflash/mpflash/config.py +0 -44
- mpflash/mpflash/connected.py +0 -96
- mpflash/mpflash/download.py +0 -364
- mpflash/mpflash/downloaded.py +0 -138
- mpflash/mpflash/errors.py +0 -9
- mpflash/mpflash/flash/__init__.py +0 -55
- mpflash/mpflash/flash/esp.py +0 -59
- mpflash/mpflash/flash/stm32.py +0 -19
- mpflash/mpflash/flash/stm32_dfu.py +0 -104
- mpflash/mpflash/flash/uf2/__init__.py +0 -88
- mpflash/mpflash/flash/uf2/boardid.py +0 -15
- mpflash/mpflash/flash/uf2/linux.py +0 -136
- mpflash/mpflash/flash/uf2/macos.py +0 -42
- mpflash/mpflash/flash/uf2/uf2disk.py +0 -12
- mpflash/mpflash/flash/uf2/windows.py +0 -43
- mpflash/mpflash/flash/worklist.py +0 -170
- mpflash/mpflash/list.py +0 -106
- mpflash/mpflash/logger.py +0 -41
- mpflash/mpflash/mpboard_id/__init__.py +0 -98
- mpflash/mpflash/mpboard_id/add_boards.py +0 -262
- mpflash/mpflash/mpboard_id/board.py +0 -37
- mpflash/mpflash/mpboard_id/board_id.py +0 -90
- mpflash/mpflash/mpboard_id/board_info.zip +0 -0
- mpflash/mpflash/mpboard_id/store.py +0 -48
- mpflash/mpflash/mpremoteboard/__init__.py +0 -271
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +0 -152
- mpflash/mpflash/mpremoteboard/runner.py +0 -140
- mpflash/mpflash/vendor/board_database.py +0 -185
- mpflash/mpflash/vendor/click_aliases.py +0 -91
- mpflash/mpflash/vendor/dfu.py +0 -165
- mpflash/mpflash/vendor/pydfu.py +0 -605
- mpflash/mpflash/vendor/readme.md +0 -12
- mpflash/mpflash/versions.py +0 -123
- mpflash/poetry.lock +0 -2603
- mpflash/pyproject.toml +0 -66
- mpflash/stm32_udev_rules.md +0 -63
- stubber/codemod/test_enrich.py +0 -87
- {micropython_stubber-1.24.1.dist-info → micropython_stubber-1.24.4.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.24.1.dist-info → micropython_stubber-1.24.4.dist-info}/entry_points.txt +0 -0
@@ -1,104 +0,0 @@
|
|
1
|
-
import platform
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
from loguru import logger as log
|
6
|
-
|
7
|
-
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
-
|
9
|
-
|
10
|
-
def init_libusb_windows() -> bool:
|
11
|
-
"""
|
12
|
-
Initializes the libusb backend on Windows.
|
13
|
-
|
14
|
-
Returns:
|
15
|
-
bool: True if the initialization is successful, False otherwise.
|
16
|
-
"""
|
17
|
-
import libusb # type: ignore
|
18
|
-
import usb.backend.libusb1 as libusb1
|
19
|
-
|
20
|
-
arch = "x64" if platform.architecture()[0] == "64bit" else "x86"
|
21
|
-
libusb1_dll = Path(libusb.__file__).parent / f"_platform\\_windows\\{arch}\\libusb-1.0.dll"
|
22
|
-
if not libusb1_dll.exists():
|
23
|
-
raise FileNotFoundError(f"libusb1.dll not found at {libusb1_dll}")
|
24
|
-
backend = libusb1.get_backend(find_library=lambda x: libusb1_dll.as_posix())
|
25
|
-
return backend is not None
|
26
|
-
|
27
|
-
|
28
|
-
try:
|
29
|
-
from ..vendor import pydfu as pydfu
|
30
|
-
except ImportError:
|
31
|
-
pydfu = None
|
32
|
-
|
33
|
-
|
34
|
-
def dfu_init():
|
35
|
-
"""
|
36
|
-
Initializes the DFU (Device Firmware Upgrade) process.
|
37
|
-
"""
|
38
|
-
if not pydfu:
|
39
|
-
log.error("pydfu not found")
|
40
|
-
return None
|
41
|
-
if platform.system() == "Windows":
|
42
|
-
log.debug("Initializing libusb backend on Windows...")
|
43
|
-
init_libusb_windows()
|
44
|
-
|
45
|
-
|
46
|
-
def flash_stm32_dfu(
|
47
|
-
mcu: MPRemoteBoard,
|
48
|
-
fw_file: Path,
|
49
|
-
*,
|
50
|
-
erase: bool = True,
|
51
|
-
) -> Optional[MPRemoteBoard]:
|
52
|
-
"""
|
53
|
-
Flashes the STM32 microcontroller using DFU (Device Firmware Upgrade).
|
54
|
-
|
55
|
-
Args:
|
56
|
-
mcu (MPRemoteBoard): The remote board to flash.
|
57
|
-
fw_file (Path): The path to the firmware file (.dfu).
|
58
|
-
erase (bool, optional): Whether to erase the memory before flashing. Defaults to True.
|
59
|
-
|
60
|
-
Returns:
|
61
|
-
Optional[MPRemoteBoard]: The flashed remote board if successful, None otherwise.
|
62
|
-
"""
|
63
|
-
log.info("Using pydfu to flash STM32 boards")
|
64
|
-
|
65
|
-
if not pydfu:
|
66
|
-
log.error("pydfu not found, please install it with 'pip install pydfu' if supported")
|
67
|
-
return None
|
68
|
-
|
69
|
-
if not fw_file.exists():
|
70
|
-
log.error(f"File {fw_file} not found")
|
71
|
-
return None
|
72
|
-
|
73
|
-
if fw_file.suffix != ".dfu":
|
74
|
-
log.error(f"File {fw_file} is not a .dfu file")
|
75
|
-
return None
|
76
|
-
|
77
|
-
kwargs = {"idVendor": 0x0483, "idProduct": 0xDF11}
|
78
|
-
log.debug("List SPECIFIED DFU devices...")
|
79
|
-
try:
|
80
|
-
pydfu.list_dfu_devices(**kwargs)
|
81
|
-
except ValueError as e:
|
82
|
-
log.error(f"Insuffient permissions to access usb DFU devices: {e}")
|
83
|
-
return None
|
84
|
-
|
85
|
-
# Needs to be a list of serial ports
|
86
|
-
log.debug("Inititialize pydfu...")
|
87
|
-
pydfu.init(**kwargs)
|
88
|
-
|
89
|
-
if erase:
|
90
|
-
log.info("Mass erase...")
|
91
|
-
pydfu.mass_erase()
|
92
|
-
|
93
|
-
log.debug("Read DFU file...")
|
94
|
-
elements = pydfu.read_dfu_file(fw_file)
|
95
|
-
if not elements:
|
96
|
-
print("No data in dfu file")
|
97
|
-
return
|
98
|
-
log.info("Writing memory...")
|
99
|
-
pydfu.write_elements(elements, False, progress=pydfu.cli_progress)
|
100
|
-
|
101
|
-
log.debug("Exiting DFU...")
|
102
|
-
pydfu.exit_dfu()
|
103
|
-
log.success("Done flashing, resetting the board and wait for it to restart")
|
104
|
-
return mcu
|
@@ -1,88 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Flash SAMD and RP2 via UF2
|
3
|
-
"""
|
4
|
-
|
5
|
-
import shutil
|
6
|
-
import sys
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Optional
|
9
|
-
|
10
|
-
import tenacity
|
11
|
-
from loguru import logger as log
|
12
|
-
|
13
|
-
from tenacity import stop_after_attempt, wait_fixed
|
14
|
-
|
15
|
-
from mpflash.mpremoteboard import MPRemoteBoard
|
16
|
-
|
17
|
-
from mpflash.common import PORT_FWTYPES
|
18
|
-
from .boardid import get_board_id
|
19
|
-
from .linux import dismount_uf2_linux, wait_for_UF2_linux
|
20
|
-
from .macos import wait_for_UF2_macos
|
21
|
-
from .windows import wait_for_UF2_windows
|
22
|
-
|
23
|
-
|
24
|
-
def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemoteBoard]:
|
25
|
-
"""
|
26
|
-
Flash .UF2 devices via bootloader and filecopy
|
27
|
-
- mpremote bootloader
|
28
|
-
- Wait for the device to mount as a drive (up to 5s)
|
29
|
-
- detect new drive with INFO_UF2.TXT
|
30
|
-
- copy the firmware file to the drive
|
31
|
-
- wait for the device to restart (5s)
|
32
|
-
|
33
|
-
for Linux - to support headless operation ( GH Actions ) :
|
34
|
-
pmount and pumount are used to mount and unmount the drive
|
35
|
-
as this is not done automatically by the OS in headless mode.
|
36
|
-
"""
|
37
|
-
if ".uf2" not in PORT_FWTYPES[mcu.port]:
|
38
|
-
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
39
|
-
return None
|
40
|
-
if erase:
|
41
|
-
log.warning("Erase not (yet) supported on .UF2, skipping erase.")
|
42
|
-
|
43
|
-
destination = waitfor_uf2(board_id=mcu.port.upper())
|
44
|
-
|
45
|
-
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
46
|
-
log.error("Board is not in bootloader mode")
|
47
|
-
return None
|
48
|
-
|
49
|
-
log.info("Board is in bootloader mode")
|
50
|
-
board_id = get_board_id(destination) # type: ignore
|
51
|
-
log.info(f"Board ID: {board_id}")
|
52
|
-
try:
|
53
|
-
copy_firmware_to_uf2(fw_file, destination)
|
54
|
-
log.success("Done copying, resetting the board.")
|
55
|
-
except tenacity.RetryError:
|
56
|
-
log.error("Failed to copy the firmware file to the board.")
|
57
|
-
return None
|
58
|
-
|
59
|
-
if sys.platform in ["linux"]:
|
60
|
-
dismount_uf2_linux()
|
61
|
-
|
62
|
-
mcu.wait_for_restart()
|
63
|
-
return mcu
|
64
|
-
|
65
|
-
|
66
|
-
def waitfor_uf2(board_id: str):
|
67
|
-
"""
|
68
|
-
Wait for the UF2 drive to mount
|
69
|
-
"""
|
70
|
-
if sys.platform == "linux":
|
71
|
-
return wait_for_UF2_linux(board_id=board_id)
|
72
|
-
elif sys.platform == "win32":
|
73
|
-
return wait_for_UF2_windows(board_id=board_id)
|
74
|
-
elif sys.platform == "darwin":
|
75
|
-
return wait_for_UF2_macos(board_id=board_id)
|
76
|
-
else:
|
77
|
-
log.warning(f"OS {sys.platform} not tested/supported")
|
78
|
-
return None
|
79
|
-
|
80
|
-
|
81
|
-
@tenacity.retry(stop=stop_after_attempt(3), wait=wait_fixed(1), reraise=False)
|
82
|
-
def copy_firmware_to_uf2(fw_file: Path, destination: Path):
|
83
|
-
"""
|
84
|
-
Copy the firmware file to the destination,
|
85
|
-
Retry 3 times with 1s delay
|
86
|
-
"""
|
87
|
-
log.info(f"Copying {fw_file} to {destination}.")
|
88
|
-
return shutil.copy(fw_file, destination)
|
@@ -1,15 +0,0 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
|
3
|
-
from loguru import logger as log
|
4
|
-
|
5
|
-
|
6
|
-
def get_board_id(path: Path):
|
7
|
-
# Option : read Board-ID from INFO_UF2.TXT
|
8
|
-
board_id = "Unknown"
|
9
|
-
with open(path / "INFO_UF2.TXT") as f:
|
10
|
-
data = f.readlines()
|
11
|
-
for line in data:
|
12
|
-
if line.startswith("Board-ID"):
|
13
|
-
board_id = line[9:].strip()
|
14
|
-
log.debug(f"INFO_UF2.TXT Board-ID={board_id}")
|
15
|
-
return board_id
|
@@ -1,136 +0,0 @@
|
|
1
|
-
""" Flashing UF2 based MCU on Linux"""
|
2
|
-
|
3
|
-
# sourcery skip: snake-case-functions
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
import subprocess
|
7
|
-
import sys
|
8
|
-
import time
|
9
|
-
from pathlib import Path
|
10
|
-
from typing import List
|
11
|
-
|
12
|
-
from loguru import logger as log
|
13
|
-
from rich.progress import track
|
14
|
-
|
15
|
-
from .boardid import get_board_id
|
16
|
-
from .uf2disk import UF2Disk
|
17
|
-
|
18
|
-
glb_dismount_me: List[UF2Disk] = []
|
19
|
-
|
20
|
-
|
21
|
-
def get_uf2_drives():
|
22
|
-
"""
|
23
|
-
Get a list of all the (un)mounted UF2 drives
|
24
|
-
"""
|
25
|
-
# import blkinfo only on linux
|
26
|
-
from blkinfo import BlkDiskInfo
|
27
|
-
|
28
|
-
myblkd = BlkDiskInfo()
|
29
|
-
filters = {
|
30
|
-
"tran": "usb",
|
31
|
-
}
|
32
|
-
usb_disks = myblkd.get_disks(filters)
|
33
|
-
for disk in usb_disks:
|
34
|
-
if disk["fstype"] == "vfat":
|
35
|
-
uf2_part = disk
|
36
|
-
# unpartioned usb disk or partition (e.g. /dev/sdb )
|
37
|
-
# SEEED WIO Terminal is unpartioned
|
38
|
-
# print( json.dumps(uf2_part, indent=4))
|
39
|
-
uf2 = UF2Disk()
|
40
|
-
uf2.device_path = "/dev/" + uf2_part["name"]
|
41
|
-
uf2.label = uf2_part["label"]
|
42
|
-
uf2.mountpoint = uf2_part["mountpoint"]
|
43
|
-
yield uf2
|
44
|
-
elif disk["type"] == "disk" and disk.get("children") and len(disk.get("children")) > 0:
|
45
|
-
if (
|
46
|
-
disk.get("children")[0]["type"] == "part"
|
47
|
-
and disk.get("children")[0]["fstype"] == "vfat"
|
48
|
-
):
|
49
|
-
uf2_part = disk.get("children")[0]
|
50
|
-
# print( json.dumps(uf2_part, indent=4))
|
51
|
-
uf2 = UF2Disk()
|
52
|
-
uf2.device_path = "/dev/" + uf2_part["name"]
|
53
|
-
uf2.label = uf2_part["label"]
|
54
|
-
uf2.mountpoint = uf2_part["mountpoint"]
|
55
|
-
yield uf2
|
56
|
-
|
57
|
-
|
58
|
-
def pmount(disk: UF2Disk):
|
59
|
-
"""
|
60
|
-
Mount a UF2 drive if there is no mountpoint yet.
|
61
|
-
"""
|
62
|
-
global glb_dismount_me
|
63
|
-
if not disk.mountpoint:
|
64
|
-
if not disk.label:
|
65
|
-
disk.label = "UF2BOOT"
|
66
|
-
disk.mountpoint = f"/media/{disk.label}"
|
67
|
-
# capture error if pmount is not installed
|
68
|
-
try:
|
69
|
-
# drive is always vfat - so specify to have quicker results
|
70
|
-
subprocess.run(["pmount", "-t", "vfat", disk.device_path, disk.mountpoint])
|
71
|
-
except FileNotFoundError:
|
72
|
-
log.error(
|
73
|
-
"pmount not found, please install it using 'sudo apt install pmount', and add this user to the plugdev group."
|
74
|
-
)
|
75
|
-
return
|
76
|
-
log.debug(f"Mounted {disk.label} at {disk.mountpoint}")
|
77
|
-
glb_dismount_me.append(disk)
|
78
|
-
else:
|
79
|
-
log.trace(f"\n{disk.label} already mounted at {disk.mountpoint}")
|
80
|
-
|
81
|
-
|
82
|
-
def pumount(disk: UF2Disk):
|
83
|
-
"""
|
84
|
-
Unmount a UF2 drive
|
85
|
-
"""
|
86
|
-
if sys.platform != "linux":
|
87
|
-
log.error("pumount only works on Linux")
|
88
|
-
return
|
89
|
-
if disk.mountpoint:
|
90
|
-
subprocess.run(["pumount", disk.mountpoint]) # ), f"/media/{disk.label}"])
|
91
|
-
log.info(f"Unmounted {disk.label} from {disk.mountpoint}")
|
92
|
-
disk.mountpoint = f""
|
93
|
-
else:
|
94
|
-
log.warning(f"{disk.label} already dismounted")
|
95
|
-
|
96
|
-
|
97
|
-
def dismount_uf2_linux():
|
98
|
-
global glb_dismount_me
|
99
|
-
for disk in glb_dismount_me:
|
100
|
-
pumount(disk)
|
101
|
-
glb_dismount_me = []
|
102
|
-
|
103
|
-
|
104
|
-
def wait_for_UF2_linux(board_id: str, s_max: int = 10):
|
105
|
-
destination = ""
|
106
|
-
wait = 10
|
107
|
-
uf2_drives = []
|
108
|
-
# while not destination and wait > 0:
|
109
|
-
for _ in track(
|
110
|
-
range(s_max),
|
111
|
-
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
112
|
-
transient=True,
|
113
|
-
show_speed=False,
|
114
|
-
refresh_per_second=1,
|
115
|
-
total=s_max,
|
116
|
-
):
|
117
|
-
uf2_drives += list(get_uf2_drives())
|
118
|
-
for drive in get_uf2_drives():
|
119
|
-
pmount(drive)
|
120
|
-
time.sleep(1)
|
121
|
-
try:
|
122
|
-
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
123
|
-
this_board_id = get_board_id(Path(drive.mountpoint))
|
124
|
-
if not board_id or board_id.upper() in this_board_id.upper():
|
125
|
-
# is it the correct board?
|
126
|
-
destination = Path(drive.mountpoint)
|
127
|
-
break
|
128
|
-
continue
|
129
|
-
except PermissionError:
|
130
|
-
log.debug(f"Permission error on {drive.mountpoint}")
|
131
|
-
continue
|
132
|
-
if destination:
|
133
|
-
break
|
134
|
-
time.sleep(1)
|
135
|
-
wait -= 1
|
136
|
-
return destination
|
@@ -1,42 +0,0 @@
|
|
1
|
-
""" Flashing UF2 based MCU on macos"""
|
2
|
-
|
3
|
-
# sourcery skip: snake-case-functions
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
import time
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Optional
|
9
|
-
|
10
|
-
from rich.progress import track
|
11
|
-
|
12
|
-
from .boardid import get_board_id
|
13
|
-
|
14
|
-
|
15
|
-
def wait_for_UF2_macos(board_id: str, s_max: int = 10) -> Optional[Path]:
|
16
|
-
"""Wait for the MCU to mount as a drive"""
|
17
|
-
if s_max < 1:
|
18
|
-
s_max = 10
|
19
|
-
destination = None
|
20
|
-
for _ in track(
|
21
|
-
range(s_max),
|
22
|
-
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
23
|
-
transient=True,
|
24
|
-
show_speed=False,
|
25
|
-
refresh_per_second=1,
|
26
|
-
total=s_max,
|
27
|
-
):
|
28
|
-
vol_mounts = Path("/Volumes").iterdir()
|
29
|
-
for vol in vol_mounts:
|
30
|
-
try:
|
31
|
-
if Path(vol, "INFO_UF2.TXT").exists():
|
32
|
-
this_board_id = get_board_id(Path(vol))
|
33
|
-
if not board_id or board_id.upper() in this_board_id.upper():
|
34
|
-
destination = Path(vol)
|
35
|
-
break
|
36
|
-
continue
|
37
|
-
except OSError:
|
38
|
-
pass
|
39
|
-
if destination:
|
40
|
-
break
|
41
|
-
time.sleep(1)
|
42
|
-
return destination
|
@@ -1,12 +0,0 @@
|
|
1
|
-
"""Info to support mounting and unmounting of UF2 drives on linux and macos"""
|
2
|
-
|
3
|
-
|
4
|
-
class UF2Disk:
|
5
|
-
"""Info to support mounting and unmounting of UF2 drives on linux"""
|
6
|
-
|
7
|
-
device_path: str
|
8
|
-
label: str
|
9
|
-
mountpoint: str
|
10
|
-
|
11
|
-
def __repr__(self):
|
12
|
-
return repr(self.__dict__)
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# sourcery skip: snake-case-functions
|
2
|
-
"""Flash a MCU with a UF2 bootloader on Windows"""
|
3
|
-
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
import time
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Optional
|
9
|
-
|
10
|
-
from .boardid import get_board_id
|
11
|
-
import psutil
|
12
|
-
from rich.progress import track
|
13
|
-
|
14
|
-
|
15
|
-
def wait_for_UF2_windows(board_id: str, s_max: int = 10)-> Optional[Path]:
|
16
|
-
"""Wait for the MCU to mount as a drive"""
|
17
|
-
if s_max < 1:
|
18
|
-
s_max = 10
|
19
|
-
destination = None
|
20
|
-
for _ in track(
|
21
|
-
range(s_max),
|
22
|
-
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
23
|
-
transient=True,
|
24
|
-
show_speed=False,
|
25
|
-
refresh_per_second=1,
|
26
|
-
total=s_max,
|
27
|
-
):
|
28
|
-
drives = [drive.device for drive in psutil.disk_partitions()]
|
29
|
-
for drive in drives:
|
30
|
-
try:
|
31
|
-
if Path(drive, "INFO_UF2.TXT").exists():
|
32
|
-
this_board_id = get_board_id(Path(drive))
|
33
|
-
if not board_id or board_id.upper() in this_board_id.upper():
|
34
|
-
# is it the correct board?
|
35
|
-
destination = Path(drive)
|
36
|
-
break
|
37
|
-
continue
|
38
|
-
except OSError:
|
39
|
-
pass
|
40
|
-
if destination:
|
41
|
-
break
|
42
|
-
time.sleep(1)
|
43
|
-
return destination
|
@@ -1,170 +0,0 @@
|
|
1
|
-
"""Worklist for updating boards"""
|
2
|
-
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import Dict, List, Optional, Tuple
|
5
|
-
|
6
|
-
from loguru import logger as log
|
7
|
-
|
8
|
-
from mpflash.common import FWInfo, filtered_comports
|
9
|
-
from mpflash.downloaded import find_downloaded_firmware
|
10
|
-
from mpflash.errors import MPFlashError
|
11
|
-
from mpflash.list import show_mcus
|
12
|
-
from mpflash.mpboard_id import find_known_board
|
13
|
-
from mpflash.mpremoteboard import MPRemoteBoard
|
14
|
-
|
15
|
-
# #########################################################################################################
|
16
|
-
WorkList = List[Tuple[MPRemoteBoard, FWInfo]]
|
17
|
-
# #########################################################################################################
|
18
|
-
|
19
|
-
|
20
|
-
def auto_update(
|
21
|
-
conn_boards: List[MPRemoteBoard],
|
22
|
-
target_version: str,
|
23
|
-
fw_folder: Path,
|
24
|
-
*,
|
25
|
-
selector: Optional[Dict[str, str]] = None,
|
26
|
-
) -> WorkList:
|
27
|
-
"""Builds a list of boards to update based on the connected boards and the firmwares available locally in the firmware folder.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
conn_boards (List[MPRemoteBoard]): List of connected boards
|
31
|
-
target_version (str): Target firmware version
|
32
|
-
fw_folder (Path): Path to the firmware folder
|
33
|
-
selector (Optional[Dict[str, str]], optional): Selector for filtering firmware. Defaults to None.
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
WorkList: List of boards and firmware information to update
|
37
|
-
"""
|
38
|
-
if selector is None:
|
39
|
-
selector = {}
|
40
|
-
wl: WorkList = []
|
41
|
-
for mcu in conn_boards:
|
42
|
-
if mcu.family not in ("micropython", "unknown"):
|
43
|
-
log.warning(f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware")
|
44
|
-
continue
|
45
|
-
board_firmwares = find_downloaded_firmware(
|
46
|
-
fw_folder=fw_folder,
|
47
|
-
board_id=mcu.board,
|
48
|
-
version=target_version,
|
49
|
-
port=mcu.port,
|
50
|
-
selector=selector,
|
51
|
-
)
|
52
|
-
|
53
|
-
if not board_firmwares:
|
54
|
-
log.error(f"No {target_version} firmware found for {mcu.board} on {mcu.serialport}.")
|
55
|
-
continue
|
56
|
-
|
57
|
-
if len(board_firmwares) > 1:
|
58
|
-
log.warning(f"Multiple {target_version} firmwares found for {mcu.board} on {mcu.serialport}.")
|
59
|
-
|
60
|
-
# just use the last firmware
|
61
|
-
fw_info = board_firmwares[-1]
|
62
|
-
log.info(f"Found {target_version} firmware {fw_info.filename} for {mcu.board} on {mcu.serialport}.")
|
63
|
-
wl.append((mcu, fw_info))
|
64
|
-
return wl
|
65
|
-
|
66
|
-
|
67
|
-
def manual_worklist(
|
68
|
-
serial: str,
|
69
|
-
*,
|
70
|
-
board_id: str,
|
71
|
-
version: str,
|
72
|
-
fw_folder: Path,
|
73
|
-
) -> WorkList:
|
74
|
-
"""Create a worklist for a single board specified manually.
|
75
|
-
|
76
|
-
Args:
|
77
|
-
serial (str): Serial port of the board
|
78
|
-
board (str): Board_ID
|
79
|
-
version (str): Firmware version
|
80
|
-
fw_folder (Path): Path to the firmware folder
|
81
|
-
|
82
|
-
Returns:
|
83
|
-
WorkList: List of boards and firmware information to update
|
84
|
-
"""
|
85
|
-
log.trace(f"Manual updating {serial} to {board_id} {version}")
|
86
|
-
mcu = MPRemoteBoard(serial)
|
87
|
-
# Lookup the matching port and cpu in board_info based in the board name
|
88
|
-
try:
|
89
|
-
info = find_known_board(board_id)
|
90
|
-
mcu.port = info.port
|
91
|
-
# need the CPU type for the esptool
|
92
|
-
mcu.cpu = info.cpu
|
93
|
-
except (LookupError, MPFlashError) as e:
|
94
|
-
log.error(f"Board {board_id} not found in board_info.zip")
|
95
|
-
log.exception(e)
|
96
|
-
return []
|
97
|
-
mcu.board = board_id
|
98
|
-
firmwares = find_downloaded_firmware(fw_folder=fw_folder, board_id=board_id, version=version, port=mcu.port)
|
99
|
-
if not firmwares:
|
100
|
-
log.error(f"No firmware found for {mcu.port} {board_id} version {version}")
|
101
|
-
return []
|
102
|
-
# use the most recent matching firmware
|
103
|
-
return [(mcu, firmwares[-1])] # type: ignore
|
104
|
-
|
105
|
-
|
106
|
-
def single_auto_worklist(
|
107
|
-
serial: str,
|
108
|
-
*,
|
109
|
-
version: str,
|
110
|
-
fw_folder: Path,
|
111
|
-
) -> WorkList:
|
112
|
-
"""Create a worklist for a single serial-port.
|
113
|
-
|
114
|
-
Args:
|
115
|
-
serial_port (str): Serial port of the board
|
116
|
-
version (str): Firmware version
|
117
|
-
fw_folder (Path): Path to the firmware folder
|
118
|
-
|
119
|
-
Returns:
|
120
|
-
WorkList: List of boards and firmware information to update
|
121
|
-
"""
|
122
|
-
log.trace(f"Auto updating {serial} to {version}")
|
123
|
-
conn_boards = [MPRemoteBoard(serial)]
|
124
|
-
todo = auto_update(conn_boards, version, fw_folder) # type: ignore # List / list
|
125
|
-
show_mcus(conn_boards) # type: ignore
|
126
|
-
return todo
|
127
|
-
|
128
|
-
|
129
|
-
def full_auto_worklist(
|
130
|
-
all_boards: List[MPRemoteBoard], *, include: List[str], ignore: List[str], version: str, fw_folder: Path
|
131
|
-
) -> WorkList:
|
132
|
-
"""
|
133
|
-
Create a worklist for all connected micropython boards based on the information retrieved from the board.
|
134
|
-
This allows the firmware version of one or moae boards to be changed without needing to specify the port or board_id manually.
|
135
|
-
|
136
|
-
Args:
|
137
|
-
version (str): Firmware version
|
138
|
-
fw_folder (Path): Path to the firmware folder
|
139
|
-
|
140
|
-
Returns:
|
141
|
-
WorkList: List of boards and firmware information to update
|
142
|
-
"""
|
143
|
-
log.trace(f"Auto updating all boards to {version}")
|
144
|
-
if selected_boards := filter_boards(all_boards, include=include, ignore=ignore):
|
145
|
-
return auto_update(selected_boards, version, fw_folder)
|
146
|
-
else:
|
147
|
-
return []
|
148
|
-
|
149
|
-
|
150
|
-
def filter_boards(
|
151
|
-
all_boards: List[MPRemoteBoard],
|
152
|
-
*,
|
153
|
-
include: List[str],
|
154
|
-
ignore: List[str],
|
155
|
-
):
|
156
|
-
try:
|
157
|
-
comports = [
|
158
|
-
p.device
|
159
|
-
for p in filtered_comports(
|
160
|
-
ignore=ignore,
|
161
|
-
include=include,
|
162
|
-
bluetooth=False,
|
163
|
-
)
|
164
|
-
]
|
165
|
-
selected_boards = [b for b in all_boards if b.serialport in comports]
|
166
|
-
# [MPRemoteBoard(port.device, update=True) for port in comports]
|
167
|
-
except ConnectionError as e:
|
168
|
-
log.error(f"Error connecting to boards: {e}")
|
169
|
-
return []
|
170
|
-
return selected_boards # type: ignore
|