micropython-stubber 1.20.6__py3-none-any.whl → 1.23.1__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.20.6.dist-info → micropython_stubber-1.23.1.dist-info}/METADATA +4 -3
- micropython_stubber-1.23.1.dist-info/RECORD +159 -0
- mpflash/README.md +14 -4
- mpflash/mpflash/add_firmware.py +1 -1
- mpflash/mpflash/ask_input.py +1 -1
- {stubber → mpflash/mpflash}/basicgit.py +3 -13
- mpflash/mpflash/bootloader/__init__.py +2 -37
- mpflash/mpflash/bootloader/activate.py +60 -0
- mpflash/mpflash/bootloader/detect.py +82 -0
- mpflash/mpflash/bootloader/manual.py +10 -11
- mpflash/mpflash/bootloader/micropython.py +2 -0
- mpflash/mpflash/bootloader/touch1200.py +13 -22
- mpflash/mpflash/cli_download.py +1 -1
- mpflash/mpflash/cli_flash.py +4 -4
- mpflash/mpflash/cli_group.py +18 -5
- mpflash/mpflash/cli_main.py +3 -5
- mpflash/mpflash/common.py +1 -0
- mpflash/mpflash/config.py +6 -9
- mpflash/mpflash/connected.py +9 -5
- mpflash/mpflash/download.py +9 -5
- mpflash/mpflash/downloaded.py +4 -3
- mpflash/mpflash/{flash.py → flash/__init__.py} +20 -17
- mpflash/mpflash/{flash_esp.py → flash/esp.py} +1 -1
- mpflash/mpflash/flash/stm32.py +19 -0
- mpflash/mpflash/{flash_stm32_dfu.py → flash/stm32_dfu.py} +4 -1
- mpflash/mpflash/{flash_uf2.py → flash/uf2/__init__.py} +44 -23
- mpflash/mpflash/{flash_uf2_linux.py → flash/uf2/linux.py} +15 -8
- mpflash/mpflash/{flash_uf2_macos.py → flash/uf2/macos.py} +13 -5
- mpflash/mpflash/{flash_uf2_windows.py → flash/uf2/windows.py} +16 -7
- mpflash/mpflash/{worklist.py → flash/worklist.py} +9 -9
- mpflash/mpflash/list.py +13 -3
- mpflash/mpflash/mpboard_id/__init__.py +1 -1
- mpflash/mpflash/mpboard_id/add_boards.py +3 -7
- mpflash/mpflash/mpboard_id/board_id.py +1 -1
- mpflash/mpflash/mpremoteboard/__init__.py +5 -4
- {stubber/utils → mpflash/mpflash}/versions.py +31 -24
- mpflash/poetry.lock +16 -5
- mpflash/pyproject.toml +8 -3
- stubber/__init__.py +1 -1
- stubber/board/createstubs.py +4 -4
- stubber/board/createstubs_db.py +5 -5
- stubber/board/createstubs_db_min.py +1 -1
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +5 -5
- stubber/board/createstubs_mem_min.py +1 -1
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +1 -1
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/codemod/utils.py +2 -3
- stubber/commands/build_cmd.py +1 -1
- stubber/commands/cli.py +8 -4
- stubber/commands/clone_cmd.py +16 -4
- stubber/commands/enrich_folder_cmd.py +2 -1
- stubber/commands/get_core_cmd.py +5 -3
- stubber/commands/get_docstubs_cmd.py +6 -4
- stubber/commands/get_frozen_cmd.py +5 -3
- stubber/commands/get_mcu_cmd.py +9 -4
- stubber/commands/merge_cmd.py +3 -3
- stubber/commands/publish_cmd.py +2 -3
- stubber/commands/stub_cmd.py +2 -1
- stubber/commands/switch_cmd.py +14 -6
- stubber/commands/variants_cmd.py +3 -3
- stubber/freeze/get_frozen.py +1 -1
- stubber/minify.py +4 -3
- stubber/publish/candidates.py +4 -10
- stubber/publish/defaults.py +4 -9
- stubber/publish/missing_class_methods.py +1 -1
- stubber/publish/package.py +1 -1
- stubber/publish/pathnames.py +1 -1
- stubber/publish/publish.py +1 -1
- stubber/publish/stubpackage.py +3 -5
- stubber/rst/reader.py +5 -13
- stubber/tools/manifestfile.py +71 -27
- stubber/tools/readme.md +3 -2
- stubber/update_fallback.py +1 -1
- stubber/utils/__init__.py +2 -1
- stubber/utils/config.py +3 -5
- stubber/utils/manifest.py +2 -4
- stubber/utils/repos.py +5 -12
- micropython_stubber-1.20.6.dist-info/RECORD +0 -159
- mpflash/mpflash/flash_stm32.py +0 -24
- mpflash/mpflash/flash_stm32_cube.py +0 -111
- mpflash/mpflash/vendor/versions.py +0 -119
- {micropython_stubber-1.20.6.dist-info → micropython_stubber-1.23.1.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.20.6.dist-info → micropython_stubber-1.23.1.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.20.6.dist-info → micropython_stubber-1.23.1.dist-info}/entry_points.txt +0 -0
- /mpflash/{mpflash/vendor/basicgit.py → basicgit.py} +0 -0
- /mpflash/mpflash/{flash_uf2_boardid.py → flash/uf2/boardid.py} +0 -0
- /mpflash/mpflash/{uf2disk.py → flash/uf2/uf2disk.py} +0 -0
mpflash/mpflash/cli_group.py
CHANGED
@@ -27,7 +27,7 @@ def cb_verbose(ctx, param, value):
|
|
27
27
|
return value
|
28
28
|
|
29
29
|
|
30
|
-
def cb_interactive(ctx, param, value:bool):
|
30
|
+
def cb_interactive(ctx, param, value: bool):
|
31
31
|
log.trace(f"Setting interactive mode to {value}")
|
32
32
|
config.interactive = value
|
33
33
|
return value
|
@@ -39,6 +39,10 @@ def cb_test(ctx, param, value):
|
|
39
39
|
config.tests = value
|
40
40
|
return value
|
41
41
|
|
42
|
+
def cb_usb(ctx, param, value: bool):
|
43
|
+
config.usb = bool(value)
|
44
|
+
return value
|
45
|
+
|
42
46
|
|
43
47
|
def cb_quiet(ctx, param, value):
|
44
48
|
log.trace(f"Setting quiet mode to {value}")
|
@@ -78,19 +82,28 @@ def cb_quiet(ctx, param, value):
|
|
78
82
|
help="Enables verbose mode.",
|
79
83
|
callback=cb_verbose,
|
80
84
|
)
|
85
|
+
@click.option(
|
86
|
+
"--usb",
|
87
|
+
"-u",
|
88
|
+
is_eager=True,
|
89
|
+
is_flag=True,
|
90
|
+
default=False,
|
91
|
+
help="Shows USB location of the connected boards.",
|
92
|
+
callback=cb_usb,
|
93
|
+
show_default=True,
|
94
|
+
)
|
81
95
|
@click.option(
|
82
96
|
"--test",
|
83
97
|
is_eager=True,
|
84
|
-
help="
|
98
|
+
help="Test a specific feature.",
|
85
99
|
callback=cb_test,
|
86
100
|
multiple=True,
|
87
101
|
default=[],
|
88
102
|
envvar="MPFLASH_TEST",
|
89
|
-
|
90
|
-
metavar="TEST",
|
103
|
+
metavar="FLAG",
|
91
104
|
)
|
92
105
|
def cli(**kwargs):
|
93
|
-
"""mpflash - MicroPython
|
106
|
+
"""mpflash - MicroPython flashing tool.
|
94
107
|
|
95
108
|
A CLI to download and flash MicroPython firmware to different ports and boards.
|
96
109
|
"""
|
mpflash/mpflash/cli_main.py
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
"""mpflash is a CLI to download and flash MicroPython firmware to various boards."""
|
2
2
|
|
3
|
-
# import rich_click as click
|
4
|
-
|
5
3
|
import os
|
6
4
|
|
7
|
-
import click
|
5
|
+
import click.exceptions as click_exceptions
|
8
6
|
from loguru import logger as log
|
9
7
|
|
10
8
|
from .cli_download import cli_download
|
@@ -29,10 +27,10 @@ def mpflash():
|
|
29
27
|
except AttributeError as e:
|
30
28
|
log.error(f"Error: {e}")
|
31
29
|
exit(-1)
|
32
|
-
except
|
30
|
+
except click_exceptions.ClickException as e:
|
33
31
|
log.error(f"Error: {e}")
|
34
32
|
exit(-2)
|
35
|
-
except
|
33
|
+
except click_exceptions.Abort as e:
|
36
34
|
# Aborted - Ctrl-C
|
37
35
|
exit(-3)
|
38
36
|
|
mpflash/mpflash/common.py
CHANGED
mpflash/mpflash/config.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
"""centralized configuration for mpflash"""
|
2
2
|
|
3
3
|
import os
|
4
|
+
from importlib.metadata import version
|
4
5
|
from pathlib import Path
|
5
6
|
from typing import List
|
6
7
|
|
7
|
-
import pkg_resources
|
8
8
|
import platformdirs
|
9
9
|
|
10
10
|
from mpflash.logger import log
|
@@ -12,10 +12,7 @@ from mpflash.logger import log
|
|
12
12
|
|
13
13
|
def get_version():
|
14
14
|
name = __package__ or "mpflash"
|
15
|
-
|
16
|
-
return pkg_resources.get_distribution(name).version
|
17
|
-
except pkg_resources.DistributionNotFound:
|
18
|
-
return "Package not found"
|
15
|
+
return version(name)
|
19
16
|
|
20
17
|
|
21
18
|
class MPtoolConfig:
|
@@ -23,6 +20,7 @@ class MPtoolConfig:
|
|
23
20
|
|
24
21
|
quiet: bool = False
|
25
22
|
verbose: bool = False
|
23
|
+
usb: bool = False
|
26
24
|
ignore_ports: List[str] = []
|
27
25
|
firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
|
28
26
|
# test options specified on the commandline
|
@@ -32,16 +30,15 @@ class MPtoolConfig:
|
|
32
30
|
@property
|
33
31
|
def interactive(self):
|
34
32
|
# No interactions in CI
|
35
|
-
if os.getenv(
|
33
|
+
if os.getenv("GITHUB_ACTIONS") == "true":
|
36
34
|
log.warning("Disabling interactive mode in CI")
|
37
35
|
return False
|
38
36
|
return self._interactive
|
39
|
-
|
37
|
+
|
40
38
|
@interactive.setter
|
41
|
-
def interactive(self, value:bool):
|
39
|
+
def interactive(self, value: bool):
|
42
40
|
self._interactive = value
|
43
41
|
|
44
42
|
|
45
|
-
|
46
43
|
config = MPtoolConfig()
|
47
44
|
__version__ = get_version()
|
mpflash/mpflash/connected.py
CHANGED
@@ -9,9 +9,7 @@ from mpflash.mpremoteboard import MPRemoteBoard
|
|
9
9
|
from .common import filtered_comports
|
10
10
|
|
11
11
|
|
12
|
-
def connected_ports_boards(
|
13
|
-
*, include: List[str], ignore: List[str]
|
14
|
-
) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
|
12
|
+
def connected_ports_boards(*, include: List[str], ignore: List[str]) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
|
15
13
|
"""
|
16
14
|
Returns a tuple containing lists of unique ports and boards from the connected MCUs.
|
17
15
|
Boards that are physically connected, but give no tangible response are ignored.
|
@@ -50,11 +48,17 @@ def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False)
|
|
50
48
|
include=include,
|
51
49
|
bluetooth=bluetooth,
|
52
50
|
)
|
53
|
-
conn_mcus = [MPRemoteBoard(c.device) for c in comports]
|
51
|
+
conn_mcus = [MPRemoteBoard(c.device, location=c.location or "?") for c in comports]
|
54
52
|
|
55
53
|
# a lot of boilerplate to show a progress bar with the comport currently scanned
|
56
54
|
# low update rate to facilitate screen readers/narration
|
57
|
-
with Progress(
|
55
|
+
with Progress(
|
56
|
+
rp_spinner,
|
57
|
+
rp_text,
|
58
|
+
rp_bar,
|
59
|
+
TimeElapsedColumn(),
|
60
|
+
refresh_per_second=1,
|
61
|
+
) as progress:
|
58
62
|
tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
|
59
63
|
progress.tasks[tsk_scan].fields["device"] = "..."
|
60
64
|
progress.tasks[tsk_scan].visible = True
|
mpflash/mpflash/download.py
CHANGED
@@ -21,7 +21,7 @@ from rich.progress import track
|
|
21
21
|
from mpflash.common import PORT_FWTYPES, FWInfo
|
22
22
|
from mpflash.errors import MPFlashError
|
23
23
|
from mpflash.mpboard_id import get_known_ports
|
24
|
-
from mpflash.
|
24
|
+
from mpflash.versions import clean_version
|
25
25
|
|
26
26
|
# avoid conflict with the ujson used by MicroPython
|
27
27
|
jsonlines.ujson = None # type: ignore
|
@@ -134,7 +134,13 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]
|
|
134
134
|
for board in urls:
|
135
135
|
board["port"] = port
|
136
136
|
|
137
|
-
for board in track(
|
137
|
+
for board in track(
|
138
|
+
urls,
|
139
|
+
description=f"Checking {port} download pages",
|
140
|
+
transient=True,
|
141
|
+
refresh_per_second=1,
|
142
|
+
show_speed=False,
|
143
|
+
):
|
138
144
|
# add a board to the list for each firmware found
|
139
145
|
firmware_urls: List[str] = []
|
140
146
|
for ext in PORT_FWTYPES[port]:
|
@@ -273,9 +279,7 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
|
|
273
279
|
log.debug(f"Total {len(board_urls)} firmwares")
|
274
280
|
|
275
281
|
relevant = [
|
276
|
-
board
|
277
|
-
for board in board_urls
|
278
|
-
if board.version in versions and board.build == "0" and board.board in boards and not board.preview
|
282
|
+
board for board in board_urls if board.version in versions and board.build == "0" and board.board in boards and not board.preview
|
279
283
|
]
|
280
284
|
|
281
285
|
if preview:
|
mpflash/mpflash/downloaded.py
CHANGED
@@ -4,8 +4,8 @@ from typing import Dict, List, Optional
|
|
4
4
|
import jsonlines
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
from mpflash.common import FWInfo
|
8
|
-
from mpflash.
|
7
|
+
from mpflash.common import PORT_FWTYPES, FWInfo
|
8
|
+
from mpflash.versions import clean_version
|
9
9
|
|
10
10
|
from .config import config
|
11
11
|
|
@@ -109,7 +109,7 @@ def filter_downloaded_fwlist(
|
|
109
109
|
log.trace(f"Filtering firmware for {version} : {len(fw_list)} found.")
|
110
110
|
# filter by port
|
111
111
|
if port:
|
112
|
-
fw_list = [fw for fw in fw_list if fw.port == port]
|
112
|
+
fw_list = [fw for fw in fw_list if fw.port == port and Path(fw.firmware).suffix in PORT_FWTYPES[port]]
|
113
113
|
log.trace(f"Filtering firmware for {port} : {len(fw_list)} found.")
|
114
114
|
|
115
115
|
if board_id:
|
@@ -120,6 +120,7 @@ def filter_downloaded_fwlist(
|
|
120
120
|
# the firmware variant should match exactly the board_id
|
121
121
|
fw_list = [fw for fw in fw_list if fw.variant == board_id]
|
122
122
|
log.trace(f"Filtering firmware for {board_id} : {len(fw_list)} found.")
|
123
|
+
|
123
124
|
if selector and port in selector:
|
124
125
|
fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
|
125
126
|
return fw_list
|
@@ -5,9 +5,9 @@ from loguru import logger as log
|
|
5
5
|
from mpflash.bootloader import enter_bootloader
|
6
6
|
from mpflash.common import PORT_FWTYPES, BootloaderMethod
|
7
7
|
|
8
|
-
from .
|
9
|
-
from .
|
10
|
-
from .
|
8
|
+
from .esp import flash_esp
|
9
|
+
from .stm32 import flash_stm32
|
10
|
+
from .uf2 import flash_uf2
|
11
11
|
from .worklist import WorkList
|
12
12
|
|
13
13
|
# #########################################################################################################
|
@@ -30,20 +30,23 @@ def flash_list(
|
|
30
30
|
continue
|
31
31
|
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
|
32
32
|
updated = None
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
try:
|
34
|
+
if mcu.port in UF2_PORTS and fw_file.suffix == ".uf2":
|
35
|
+
if not enter_bootloader(mcu, bootloader):
|
36
|
+
continue
|
37
|
+
updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
|
38
|
+
elif mcu.port in ["stm32"]:
|
39
|
+
if not enter_bootloader(mcu, bootloader):
|
40
|
+
continue
|
41
|
+
updated = flash_stm32(mcu, fw_file, erase=erase)
|
42
|
+
elif mcu.port in ["esp32", "esp8266"]:
|
43
|
+
# bootloader is handled by esptool for esp32/esp8266
|
44
|
+
updated = flash_esp(mcu, fw_file=fw_file, erase=erase)
|
45
|
+
else:
|
46
|
+
log.error(f"Don't (yet) know how to flash {mcu.port}-{mcu.board} on {mcu.serialport}")
|
47
|
+
except Exception as e:
|
48
|
+
log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
|
49
|
+
log.exception(e)
|
47
50
|
|
48
51
|
if updated:
|
49
52
|
flashed.append(updated)
|
@@ -53,7 +53,7 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
|
|
53
53
|
log.error(f"Failed to flash {mcu.board} on {mcu.serialport} : {e}")
|
54
54
|
return None
|
55
55
|
|
56
|
-
log.info("Done flashing, resetting the board
|
56
|
+
log.info("Done flashing, resetting the board...")
|
57
57
|
mcu.wait_for_restart()
|
58
58
|
log.success(f"Flashed {mcu.serialport} to {mcu.board} {mcu.version}")
|
59
59
|
return mcu
|
@@ -0,0 +1,19 @@
|
|
1
|
+
"""Flash STM32 boards using pydfu"""
|
2
|
+
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from loguru import logger as log
|
6
|
+
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
9
|
+
# from .flash_stm32_cube import flash_stm32_cubecli
|
10
|
+
from .stm32_dfu import dfu_init, flash_stm32_dfu
|
11
|
+
|
12
|
+
|
13
|
+
def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool):
|
14
|
+
# sourcery skip: lift-return-into-if
|
15
|
+
dfu_init()
|
16
|
+
if updated := flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase):
|
17
|
+
mcu.wait_for_restart()
|
18
|
+
log.success(f"Flashed {mcu.version} to {mcu.board}")
|
19
|
+
return updated
|
@@ -26,7 +26,7 @@ def init_libusb_windows() -> bool:
|
|
26
26
|
|
27
27
|
|
28
28
|
try:
|
29
|
-
from
|
29
|
+
from ..vendor import pydfu as pydfu
|
30
30
|
except ImportError:
|
31
31
|
pydfu = None
|
32
32
|
|
@@ -39,6 +39,7 @@ def dfu_init():
|
|
39
39
|
log.error("pydfu not found")
|
40
40
|
return None
|
41
41
|
if platform.system() == "Windows":
|
42
|
+
log.debug("Initializing libusb backend on Windows...")
|
42
43
|
init_libusb_windows()
|
43
44
|
|
44
45
|
|
@@ -59,6 +60,8 @@ def flash_stm32_dfu(
|
|
59
60
|
Returns:
|
60
61
|
Optional[MPRemoteBoard]: The flashed remote board if successful, None otherwise.
|
61
62
|
"""
|
63
|
+
log.info("Using pydfu to flash STM32 boards")
|
64
|
+
|
62
65
|
if not pydfu:
|
63
66
|
log.error("pydfu not found, please install it with 'pip install pydfu' if supported")
|
64
67
|
return None
|
@@ -4,20 +4,21 @@ Flash SAMD and RP2 via UF2
|
|
4
4
|
|
5
5
|
import shutil
|
6
6
|
import sys
|
7
|
-
import time
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import Optional
|
10
9
|
|
10
|
+
import tenacity
|
11
11
|
from loguru import logger as log
|
12
|
-
|
12
|
+
|
13
|
+
from tenacity import stop_after_attempt, wait_fixed
|
13
14
|
|
14
15
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
16
|
|
16
|
-
from .common import PORT_FWTYPES
|
17
|
-
from .
|
18
|
-
from .
|
19
|
-
from .
|
20
|
-
from .
|
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
|
21
22
|
|
22
23
|
|
23
24
|
def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemoteBoard]:
|
@@ -37,18 +38,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
37
38
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
38
39
|
return None
|
39
40
|
if erase:
|
40
|
-
log.
|
41
|
+
log.warning("Erase not (yet) supported on .UF2, skipping erase.")
|
41
42
|
|
42
|
-
|
43
|
-
destination = wait_for_UF2_linux()
|
44
|
-
elif sys.platform == "win32":
|
45
|
-
destination = wait_for_UF2_windows()
|
46
|
-
elif sys.platform == "darwin":
|
47
|
-
log.warning(f"OS {sys.platform} not tested/supported")
|
48
|
-
destination = wait_for_UF2_macos()
|
49
|
-
else:
|
50
|
-
log.warning(f"OS {sys.platform} not tested/supported")
|
51
|
-
return None
|
43
|
+
destination = waitfor_uf2(board_id=mcu.port.upper())
|
52
44
|
|
53
45
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
54
46
|
log.error("Board is not in bootloader mode")
|
@@ -57,11 +49,40 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
57
49
|
log.info("Board is in bootloader mode")
|
58
50
|
board_id = get_board_id(destination) # type: ignore
|
59
51
|
log.info(f"Board ID: {board_id}")
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
+
|
63
59
|
if sys.platform in ["linux"]:
|
64
60
|
dismount_uf2_linux()
|
65
|
-
|
66
|
-
|
61
|
+
|
62
|
+
mcu.wait_for_restart()
|
67
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)
|
@@ -12,7 +12,7 @@ from typing import List
|
|
12
12
|
from loguru import logger as log
|
13
13
|
from rich.progress import track
|
14
14
|
|
15
|
-
from .
|
15
|
+
from .boardid import get_board_id
|
16
16
|
from .uf2disk import UF2Disk
|
17
17
|
|
18
18
|
glb_dismount_me: List[UF2Disk] = []
|
@@ -70,7 +70,7 @@ def pmount(disk: UF2Disk):
|
|
70
70
|
log.debug(f"Mounted {disk.label} at {disk.mountpoint}")
|
71
71
|
glb_dismount_me.append(disk)
|
72
72
|
else:
|
73
|
-
log.
|
73
|
+
log.trace(f"\n{disk.label} already mounted at {disk.mountpoint}")
|
74
74
|
|
75
75
|
|
76
76
|
def pumount(disk: UF2Disk):
|
@@ -95,24 +95,31 @@ def dismount_uf2_linux():
|
|
95
95
|
glb_dismount_me = []
|
96
96
|
|
97
97
|
|
98
|
-
def wait_for_UF2_linux(s_max: int = 10):
|
98
|
+
def wait_for_UF2_linux(board_id: str, s_max: int = 10):
|
99
99
|
destination = ""
|
100
100
|
wait = 10
|
101
101
|
uf2_drives = []
|
102
102
|
# while not destination and wait > 0:
|
103
103
|
for _ in track(
|
104
|
-
range(s_max),
|
104
|
+
range(s_max),
|
105
|
+
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
106
|
+
transient=True,
|
107
|
+
show_speed=False,
|
108
|
+
refresh_per_second=1,
|
109
|
+
total=s_max,
|
105
110
|
):
|
106
|
-
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
107
111
|
uf2_drives += list(get_uf2_drives())
|
108
112
|
for drive in get_uf2_drives():
|
109
113
|
pmount(drive)
|
110
114
|
time.sleep(1)
|
111
115
|
try:
|
112
116
|
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
this_board_id = get_board_id(Path(drive.mountpoint))
|
118
|
+
if not board_id or board_id.upper() in this_board_id.upper():
|
119
|
+
# is it the correct board?
|
120
|
+
destination = Path(drive.mountpoint)
|
121
|
+
break
|
122
|
+
continue
|
116
123
|
except PermissionError:
|
117
124
|
log.debug(f"Permission error on {drive.mountpoint}")
|
118
125
|
continue
|
@@ -9,23 +9,31 @@ from typing import Optional
|
|
9
9
|
|
10
10
|
from rich.progress import track
|
11
11
|
|
12
|
+
from .boardid import get_board_id
|
12
13
|
|
13
14
|
|
14
|
-
def wait_for_UF2_macos(s_max: int = 10) -> Optional[Path]:
|
15
|
+
def wait_for_UF2_macos(board_id: str, s_max: int = 10) -> Optional[Path]:
|
15
16
|
"""Wait for the MCU to mount as a drive"""
|
16
17
|
if s_max < 1:
|
17
18
|
s_max = 10
|
18
19
|
destination = None
|
19
20
|
for _ in track(
|
20
|
-
range(s_max),
|
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,
|
21
27
|
):
|
22
|
-
# log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
|
23
28
|
vol_mounts = Path("/Volumes").iterdir()
|
24
29
|
for vol in vol_mounts:
|
25
30
|
try:
|
26
31
|
if Path(vol, "INFO_UF2.TXT").exists():
|
27
|
-
|
28
|
-
|
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
|
29
37
|
except OSError:
|
30
38
|
pass
|
31
39
|
if destination:
|
@@ -7,25 +7,34 @@ import time
|
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Optional
|
9
9
|
|
10
|
+
from .boardid import get_board_id
|
10
11
|
import psutil
|
11
12
|
from rich.progress import track
|
12
13
|
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
def wait_for_UF2_windows(s_max: int = 10) -> Optional[Path]:
|
15
|
+
def wait_for_UF2_windows(board_id: str, s_max: int = 10)-> Optional[Path]:
|
17
16
|
"""Wait for the MCU to mount as a drive"""
|
18
17
|
if s_max < 1:
|
19
18
|
s_max = 10
|
20
19
|
destination = None
|
21
|
-
for _ in track(
|
22
|
-
|
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
|
+
):
|
23
28
|
drives = [drive.device for drive in psutil.disk_partitions()]
|
24
29
|
for drive in drives:
|
25
30
|
try:
|
26
31
|
if Path(drive, "INFO_UF2.TXT").exists():
|
27
|
-
|
28
|
-
|
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
|
29
38
|
except OSError:
|
30
39
|
pass
|
31
40
|
if destination:
|
@@ -1,15 +1,16 @@
|
|
1
|
+
"""Worklist for updating boards"""
|
2
|
+
|
1
3
|
from pathlib import Path
|
2
4
|
from typing import Dict, List, Optional, Tuple
|
3
5
|
|
4
6
|
from loguru import logger as log
|
5
7
|
|
6
8
|
from mpflash.common import FWInfo, filtered_comports
|
9
|
+
from mpflash.downloaded import find_downloaded_firmware
|
7
10
|
from mpflash.errors import MPFlashError
|
8
|
-
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
from .mpboard_id import find_known_board
|
12
|
-
from .mpremoteboard import MPRemoteBoard
|
11
|
+
from mpflash.list import show_mcus
|
12
|
+
from mpflash.mpboard_id import find_known_board
|
13
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
13
14
|
|
14
15
|
# #########################################################################################################
|
15
16
|
WorkList = List[Tuple[MPRemoteBoard, FWInfo]]
|
@@ -39,9 +40,7 @@ def auto_update(
|
|
39
40
|
wl: WorkList = []
|
40
41
|
for mcu in conn_boards:
|
41
42
|
if mcu.family not in ("micropython", "unknown"):
|
42
|
-
log.warning(
|
43
|
-
f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware"
|
44
|
-
)
|
43
|
+
log.warning(f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware")
|
45
44
|
continue
|
46
45
|
board_firmwares = find_downloaded_firmware(
|
47
46
|
fw_folder=fw_folder,
|
@@ -54,8 +53,9 @@ def auto_update(
|
|
54
53
|
if not board_firmwares:
|
55
54
|
log.error(f"No {target_version} firmware found for {mcu.board} on {mcu.serialport}.")
|
56
55
|
continue
|
56
|
+
|
57
57
|
if len(board_firmwares) > 1:
|
58
|
-
log.
|
58
|
+
log.warning(f"Multiple {target_version} firmwares found for {mcu.board} on {mcu.serialport}.")
|
59
59
|
|
60
60
|
# just use the last firmware
|
61
61
|
fw_info = board_firmwares[-1]
|
mpflash/mpflash/list.py
CHANGED
@@ -3,8 +3,9 @@ from typing import List
|
|
3
3
|
from rich.progress import track
|
4
4
|
from rich.table import Table
|
5
5
|
|
6
|
+
from mpflash.config import config
|
6
7
|
from mpflash.mpremoteboard import MPRemoteBoard
|
7
|
-
from mpflash.
|
8
|
+
from mpflash.versions import clean_version
|
8
9
|
|
9
10
|
from .logger import console
|
10
11
|
|
@@ -64,8 +65,15 @@ def mcu_table(
|
|
64
65
|
table.add_column("Version", overflow="fold", min_width=5, max_width=16)
|
65
66
|
if needs_build:
|
66
67
|
table.add_column("Build" if is_wide else "Bld", justify="right")
|
67
|
-
|
68
|
-
|
68
|
+
if config.usb:
|
69
|
+
table.add_column("Location", overflow="fold", max_width=40)
|
70
|
+
for mcu in track(
|
71
|
+
conn_mcus,
|
72
|
+
description="Updating board info",
|
73
|
+
transient=True,
|
74
|
+
show_speed=False,
|
75
|
+
refresh_per_second=1,
|
76
|
+
):
|
69
77
|
if refresh:
|
70
78
|
try:
|
71
79
|
mcu.get_mcu_info()
|
@@ -84,6 +92,8 @@ def mcu_table(
|
|
84
92
|
row.append(clean_version(mcu.version))
|
85
93
|
if needs_build:
|
86
94
|
row.append(mcu.build)
|
95
|
+
if config.usb:
|
96
|
+
row.append(mcu.location)
|
87
97
|
|
88
98
|
table.add_row(*row)
|
89
99
|
return table
|
@@ -10,7 +10,7 @@ from typing import List, Optional, Tuple
|
|
10
10
|
from mpflash.errors import MPFlashError
|
11
11
|
from mpflash.mpboard_id.board import Board
|
12
12
|
from mpflash.mpboard_id.store import read_known_boardinfo
|
13
|
-
from mpflash.
|
13
|
+
from mpflash.versions import clean_version
|
14
14
|
|
15
15
|
# KNOWN ports and boards are sourced from the micropython repo,
|
16
16
|
# this info is stored in the board_info.json file
|