micropython-stubber 1.23.0__py3-none-any.whl → 1.23.1.post1__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.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/METADATA +32 -14
- micropython_stubber-1.23.1.post1.dist-info/RECORD +159 -0
- micropython_stubber-1.23.1.post1.dist-info/entry_points.txt +5 -0
- mpflash/README.md +40 -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 +2 -2
- mpflash/mpflash/cli_flash.py +13 -16
- mpflash/mpflash/cli_group.py +18 -5
- mpflash/mpflash/cli_list.py +8 -2
- mpflash/mpflash/cli_main.py +3 -5
- mpflash/mpflash/common.py +3 -1
- mpflash/mpflash/config.py +2 -1
- mpflash/mpflash/connected.py +11 -8
- mpflash/mpflash/download.py +9 -5
- mpflash/mpflash/downloaded.py +1 -1
- mpflash/mpflash/{flash.py → flash/__init__.py} +3 -3
- mpflash/mpflash/{flash_esp.py → flash/esp.py} +1 -1
- mpflash/mpflash/{flash_stm32.py → flash/stm32.py} +4 -3
- mpflash/mpflash/{flash_stm32_dfu.py → flash/stm32_dfu.py} +1 -1
- mpflash/mpflash/{flash_uf2.py → flash/uf2/__init__.py} +19 -20
- mpflash/mpflash/{flash_uf2_linux.py → flash/uf2/linux.py} +12 -11
- mpflash/mpflash/{flash_uf2_macos.py → flash/uf2/macos.py} +11 -6
- mpflash/mpflash/{flash_uf2_windows.py → flash/uf2/windows.py} +11 -6
- mpflash/mpflash/{worklist.py → flash/worklist.py} +8 -9
- mpflash/mpflash/list.py +26 -9
- 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 +57 -17
- {stubber/utils → mpflash/mpflash}/versions.py +31 -24
- mpflash/poetry.lock +16 -5
- mpflash/pyproject.toml +4 -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/bulk/mcu_stubber.py +28 -45
- stubber/codemod/enrich.py +1 -1
- stubber/codemod/merge_docstub.py +1 -1
- stubber/codemod/utils.py +2 -3
- stubber/commands/build_cmd.py +1 -1
- stubber/commands/cli.py +6 -12
- stubber/commands/clone_cmd.py +3 -2
- stubber/commands/config_cmd.py +1 -1
- stubber/commands/enrich_folder_cmd.py +1 -1
- stubber/commands/get_core_cmd.py +1 -1
- stubber/commands/get_docstubs_cmd.py +6 -3
- stubber/commands/get_frozen_cmd.py +6 -3
- stubber/commands/get_mcu_cmd.py +53 -12
- stubber/commands/merge_cmd.py +2 -3
- stubber/commands/publish_cmd.py +2 -3
- stubber/commands/stub_cmd.py +1 -1
- stubber/commands/switch_cmd.py +2 -2
- stubber/commands/variants_cmd.py +2 -3
- stubber/downloader.py +2 -1
- stubber/freeze/common.py +7 -3
- stubber/freeze/freeze_folder.py +2 -2
- stubber/freeze/freeze_manifest_2.py +19 -6
- stubber/freeze/get_frozen.py +8 -4
- stubber/get_cpython.py +15 -4
- stubber/minify.py +11 -6
- stubber/publish/candidates.py +20 -7
- stubber/publish/defaults.py +4 -9
- stubber/publish/merge_docstubs.py +4 -2
- stubber/publish/missing_class_methods.py +5 -3
- stubber/publish/package.py +8 -4
- stubber/publish/pathnames.py +2 -2
- stubber/publish/publish.py +2 -2
- stubber/publish/pypi.py +6 -2
- stubber/publish/stubpackage.py +39 -17
- stubber/rst/classsort.py +2 -1
- stubber/rst/lookup.py +1 -0
- stubber/rst/reader.py +12 -20
- stubber/rst/report_return.py +12 -4
- stubber/rst/rst_utils.py +2 -1
- stubber/stubs_from_docs.py +1 -1
- stubber/tools/manifestfile.py +1 -2
- stubber/update_fallback.py +2 -2
- stubber/update_module_list.py +1 -1
- stubber/utils/__init__.py +2 -1
- stubber/utils/config.py +18 -8
- stubber/utils/manifest.py +2 -4
- stubber/utils/post.py +2 -1
- stubber/utils/repos.py +4 -5
- stubber/utils/stubmaker.py +1 -1
- stubber/utils/typed_config_toml.py +5 -2
- stubber/variants.py +1 -1
- micropython_stubber-1.23.0.dist-info/RECORD +0 -159
- micropython_stubber-1.23.0.dist-info/entry_points.txt +0 -3
- mpflash/mpflash/flash_stm32_cube.py +0 -111
- mpflash/mpflash/vendor/versions.py +0 -119
- {micropython_stubber-1.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.23.0.dist-info → micropython_stubber-1.23.1.post1.dist-info}/WHEEL +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
@@ -0,0 +1,82 @@
|
|
1
|
+
""" Detect if a board is in bootloader mode
|
2
|
+
"""
|
3
|
+
|
4
|
+
import os
|
5
|
+
|
6
|
+
from mpflash.common import PORT_FWTYPES
|
7
|
+
from mpflash.flash.uf2 import waitfor_uf2
|
8
|
+
from mpflash.logger import log
|
9
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
10
|
+
|
11
|
+
|
12
|
+
def in_bootloader(mcu: MPRemoteBoard) -> bool:
|
13
|
+
"""Check if the board is in bootloader mode"""
|
14
|
+
if ".uf2" in PORT_FWTYPES[mcu.port]:
|
15
|
+
return in_uf2_bootloader(mcu.port.upper())
|
16
|
+
elif mcu.port in ["stm32"]:
|
17
|
+
return in_stm32_bootloader()
|
18
|
+
elif mcu.port in ["esp32", "esp8266"]:
|
19
|
+
log.debug("esp32/esp8266 does not have a bootloader mode, Assume OK to flash")
|
20
|
+
return True
|
21
|
+
|
22
|
+
log.error(f"Bootloader mode not supported on {mcu.board} on {mcu.serialport}")
|
23
|
+
return False
|
24
|
+
|
25
|
+
|
26
|
+
def in_uf2_bootloader(board_id: str) -> bool:
|
27
|
+
"""
|
28
|
+
Check if the board is in UF2 bootloader mode.
|
29
|
+
|
30
|
+
:param board_id: The board ID to check for (SAMD or RP2)
|
31
|
+
"""
|
32
|
+
return bool(waitfor_uf2(board_id=board_id))
|
33
|
+
|
34
|
+
|
35
|
+
def in_stm32_bootloader() -> bool:
|
36
|
+
"""Check if the board is in STM32 bootloader mode"""
|
37
|
+
if os.name == "nt":
|
38
|
+
driver_installed, status = check_for_stm32_bootloader_device()
|
39
|
+
if not driver_installed:
|
40
|
+
log.warning("STM32 BOOTLOADER device not found.")
|
41
|
+
return False
|
42
|
+
print()
|
43
|
+
if status != "OK":
|
44
|
+
log.warning(f"STM32 BOOTLOADER device found, Device status: {status}")
|
45
|
+
log.error("Please use Zadig to install a WinUSB (libusb) driver.\nhttps://github.com/pbatard/libwdi/wiki/Zadig")
|
46
|
+
return False
|
47
|
+
return check_dfu_devices()
|
48
|
+
|
49
|
+
|
50
|
+
def check_dfu_devices():
|
51
|
+
"""Check if there are any DFU devices connected"""
|
52
|
+
# JIT import
|
53
|
+
from mpflash.flash.stm32_dfu import dfu_init
|
54
|
+
from mpflash.vendor.pydfu import get_dfu_devices
|
55
|
+
|
56
|
+
# need to init on windows to get the right usb backend
|
57
|
+
dfu_init()
|
58
|
+
devices = get_dfu_devices()
|
59
|
+
return len(devices) > 0
|
60
|
+
|
61
|
+
|
62
|
+
def check_for_stm32_bootloader_device():
|
63
|
+
import win32com.client
|
64
|
+
|
65
|
+
# Windows only
|
66
|
+
# Create a WMI interface object
|
67
|
+
wmi = win32com.client.GetObject("winmgmts:")
|
68
|
+
|
69
|
+
# Query for USB devices
|
70
|
+
for usb_device in wmi.InstancesOf("Win32_PnPEntity"):
|
71
|
+
try:
|
72
|
+
# Check if device name or description contains "STM32 BOOTLOADER"
|
73
|
+
if str(usb_device.Name).strip() in {
|
74
|
+
"STM32 BOOTLOADER",
|
75
|
+
"STM BOOTLOADER",
|
76
|
+
}:
|
77
|
+
# Just the first match is enough
|
78
|
+
return True, usb_device.Status
|
79
|
+
except Exception:
|
80
|
+
pass
|
81
|
+
# If no matching device was found
|
82
|
+
return False, "Not found."
|
@@ -12,13 +12,14 @@ from mpflash.mpremoteboard import MPRemoteBoard
|
|
12
12
|
|
13
13
|
|
14
14
|
class MCUHighlighter(RegexHighlighter):
|
15
|
-
"""Apply style to
|
15
|
+
"""Apply style to things that should stand out."""
|
16
16
|
|
17
17
|
base_style = "mcu."
|
18
18
|
highlights = [
|
19
19
|
r"(?P<bold>Method[\s\d\:]*)",
|
20
20
|
r"(?P<bold> \d.)", # numbered items
|
21
21
|
r"(?P<bold> - )", # bullets
|
22
|
+
# mcu things
|
22
23
|
r"(?P<pad>GPIO[\d]*)",
|
23
24
|
r"(?P<pad>GPI[\d]*)",
|
24
25
|
r"(?P<pad>IO[\d]*)",
|
@@ -33,21 +34,19 @@ class MCUHighlighter(RegexHighlighter):
|
|
33
34
|
r"(?P<button>reset)",
|
34
35
|
# other
|
35
36
|
r"(?P<cable>USB)",
|
36
|
-
|
37
|
-
|
38
|
-
# r"(?P<mcu>rp2)",
|
39
|
-
# r"(?P<mcu>rp2040)",
|
37
|
+
r"(?P<cable>USB-C)",
|
38
|
+
r"(?P<cable>Serial)",
|
40
39
|
]
|
41
40
|
|
42
41
|
|
43
42
|
# https://rich.readthedocs.io/en/stable/appendix/colors.html?highlight=colors#standard-colors
|
43
|
+
# use 3 colors to keep things simple but clear
|
44
44
|
mcu_theme = Theme(
|
45
45
|
{
|
46
|
-
"mcu.bold": "orange3",
|
47
|
-
"mcu.
|
48
|
-
"mcu.
|
49
|
-
"mcu.
|
50
|
-
"mcu.cable": "dodger_blue2",
|
46
|
+
"mcu.bold": "orange3", # readers guidance
|
47
|
+
"mcu.button": "bold green", # things to press
|
48
|
+
"mcu.pad": "dodger_blue2", # things to connect
|
49
|
+
"mcu.cable": "dodger_blue2", # things to connect
|
51
50
|
}
|
52
51
|
)
|
53
52
|
|
@@ -84,7 +83,7 @@ Please put your {mcu.port.upper()} device into bootloader mode by:
|
|
84
83
|
"""
|
85
84
|
|
86
85
|
# todo: would be nice to re-use the console instance from logger
|
87
|
-
console = Console(highlighter=MCUHighlighter(), theme=mcu_theme)
|
86
|
+
console = Console(highlighter=MCUHighlighter(), theme=mcu_theme) # type: ignore
|
88
87
|
message += "\nIf you are unsure how to enter bootloader mode, please refer to the device documentation."
|
89
88
|
console.print(
|
90
89
|
Panel(
|
@@ -1,10 +1,12 @@
|
|
1
1
|
"""Module for handling the bootloader mode for micropython boards"""
|
2
2
|
|
3
|
+
from mpflash.logger import log
|
3
4
|
from mpflash.mpremoteboard import MPRemoteBoard
|
4
5
|
|
5
6
|
|
6
7
|
def enter_bootloader_mpy(mcu: MPRemoteBoard, timeout: int = 10):
|
7
8
|
"""Enter the bootloader mode for the board using mpremote and micropython on the board"""
|
9
|
+
log.info(f"Attempting bootloader on {mcu.serialport} using 'mpremote bootloader'")
|
8
10
|
mcu.run_command("bootloader", timeout=timeout)
|
9
11
|
# todo: check if mpremote command was successful
|
10
12
|
return True
|
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
Enter bootloader using Touch 1200Bd for boards with bootloaders that support this.
|
3
|
+
|
4
|
+
"""
|
5
|
+
|
2
6
|
import time
|
3
7
|
|
4
8
|
import serial
|
@@ -7,32 +11,19 @@ from mpflash.errors import MPFlashError
|
|
7
11
|
from mpflash.logger import log
|
8
12
|
from mpflash.mpremoteboard import MPRemoteBoard
|
9
13
|
|
10
|
-
from .manual import enter_bootloader_manual
|
11
|
-
|
12
14
|
|
13
|
-
def
|
14
|
-
if sys.platform == "win32":
|
15
|
-
log.warning("Touch 1200bps method is currently not supported on Windows, switching to manual")
|
16
|
-
return enter_bootloader_manual(mcu, timeout=timeout)
|
17
|
-
|
18
|
-
log.info(f"Entering bootloader on {mcu.board} on {mcu.serialport} using CDC 1200bps")
|
19
|
-
# if port argument is present perform soft reset
|
15
|
+
def enter_bootloader_touch_1200bps(mcu: MPRemoteBoard, timeout: int = 10):
|
20
16
|
if not mcu.serialport:
|
21
17
|
raise MPFlashError("No serial port specified")
|
18
|
+
log.info(f"Attempting bootloader on {mcu.serialport} using 'Touch 1200Bd'")
|
19
|
+
# if port argument is present perform soft reset
|
22
20
|
# try to initiate serial port connection on PORT with 1200 baudrate
|
23
21
|
try:
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
bytesize=serial.EIGHTBITS,
|
30
|
-
rtscts=True,
|
31
|
-
) as connection:
|
32
|
-
print("Connection established")
|
33
|
-
connection.rts = True
|
34
|
-
connection.dtr = False
|
35
|
-
time.sleep(0.4)
|
22
|
+
com = serial.Serial(mcu.serialport, 1200, dsrdtr=True)
|
23
|
+
com.rts = False # required
|
24
|
+
com.dtr = False # might as well
|
25
|
+
time.sleep(0.2)
|
26
|
+
com.close()
|
36
27
|
|
37
28
|
except serial.SerialException as e:
|
38
29
|
log.exception(e)
|
mpflash/mpflash/cli_download.py
CHANGED
@@ -8,7 +8,7 @@ from loguru import logger as log
|
|
8
8
|
from mpflash.connected import connected_ports_boards
|
9
9
|
from mpflash.errors import MPFlashError
|
10
10
|
from mpflash.mpboard_id import find_known_board
|
11
|
-
from mpflash.
|
11
|
+
from mpflash.versions import clean_version
|
12
12
|
|
13
13
|
from .ask_input import ask_missing_params
|
14
14
|
from .cli_group import cli
|
@@ -58,7 +58,7 @@ from .download import download
|
|
58
58
|
default=["*"],
|
59
59
|
show_default=True,
|
60
60
|
multiple=True,
|
61
|
-
help="Which serial port(s) to flash",
|
61
|
+
help="Which serial port(s) (or globs) to flash",
|
62
62
|
metavar="SERIALPORT",
|
63
63
|
)
|
64
64
|
@click.option(
|
mpflash/mpflash/cli_flash.py
CHANGED
@@ -4,21 +4,18 @@ from typing import List
|
|
4
4
|
import rich_click as click
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
from mpflash.
|
7
|
+
from mpflash.ask_input import ask_missing_params
|
8
|
+
from mpflash.cli_download import connected_ports_boards
|
9
|
+
from mpflash.cli_group import cli
|
10
|
+
from mpflash.cli_list import show_mcus
|
11
|
+
from mpflash.common import BootloaderMethod, FlashParams, Params
|
12
|
+
from mpflash.config import config
|
8
13
|
from mpflash.errors import MPFlashError
|
14
|
+
from mpflash.flash import flash_list
|
15
|
+
from mpflash.flash.worklist import WorkList, full_auto_worklist, manual_worklist, single_auto_worklist
|
9
16
|
from mpflash.mpboard_id import find_known_board
|
10
17
|
from mpflash.mpremoteboard import MPRemoteBoard
|
11
|
-
from mpflash.
|
12
|
-
|
13
|
-
from .ask_input import ask_missing_params
|
14
|
-
from .cli_download import connected_ports_boards
|
15
|
-
from .cli_group import cli
|
16
|
-
from .cli_list import show_mcus
|
17
|
-
from .common import FlashParams
|
18
|
-
from .config import config
|
19
|
-
from .flash import flash_list
|
20
|
-
from .worklist import (WorkList, full_auto_worklist, manual_worklist,
|
21
|
-
single_auto_worklist)
|
18
|
+
from mpflash.versions import clean_version
|
22
19
|
|
23
20
|
# #########################################################################################################
|
24
21
|
# CLI
|
@@ -56,7 +53,7 @@ from .worklist import (WorkList, full_auto_worklist, manual_worklist,
|
|
56
53
|
default=["*"],
|
57
54
|
multiple=True,
|
58
55
|
show_default=True,
|
59
|
-
help="Which serial port(s) to flash",
|
56
|
+
help="Which serial port(s) (or globs) to flash",
|
60
57
|
metavar="SERIALPORT",
|
61
58
|
)
|
62
59
|
@click.option(
|
@@ -106,7 +103,7 @@ from .worklist import (WorkList, full_auto_worklist, manual_worklist,
|
|
106
103
|
"-bl",
|
107
104
|
"bootloader",
|
108
105
|
type=click.Choice([e.value for e in BootloaderMethod]),
|
109
|
-
default="
|
106
|
+
default="auto",
|
110
107
|
show_default=True,
|
111
108
|
help="""How to enter the (MicroPython) bootloader before flashing.""",
|
112
109
|
)
|
@@ -122,7 +119,7 @@ def cli_flash_board(**kwargs) -> int:
|
|
122
119
|
params = FlashParams(**kwargs)
|
123
120
|
params.versions = list(params.versions)
|
124
121
|
params.ports = list(params.ports)
|
125
|
-
params.boards = list(params.boards)
|
122
|
+
params.boards = list(params.boards)
|
126
123
|
params.serial = list(params.serial)
|
127
124
|
params.ignore = list(params.ignore)
|
128
125
|
params.bootloader = BootloaderMethod(params.bootloader)
|
@@ -203,7 +200,7 @@ def cli_flash_board(**kwargs) -> int:
|
|
203
200
|
return 1
|
204
201
|
|
205
202
|
|
206
|
-
def resolve_board_ids(params):
|
203
|
+
def resolve_board_ids(params: Params):
|
207
204
|
"""Resolve board descriptions to board_id, and remove empty strings from list of boards"""
|
208
205
|
for board_id in params.boards:
|
209
206
|
if board_id == "":
|
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_list.py
CHANGED
@@ -32,14 +32,14 @@ from .logger import make_quiet
|
|
32
32
|
default=["*"],
|
33
33
|
multiple=True,
|
34
34
|
show_default=True,
|
35
|
-
help="
|
35
|
+
help="Serial port(s) (or globs) to list. ",
|
36
36
|
metavar="SERIALPORT",
|
37
37
|
)
|
38
38
|
@click.option(
|
39
39
|
"--ignore",
|
40
40
|
"-i",
|
41
41
|
is_eager=True,
|
42
|
-
help="Serial port(s) to ignore. Defaults to MPFLASH_IGNORE.",
|
42
|
+
help="Serial port(s) (or globs) to ignore. Defaults to MPFLASH_IGNORE.",
|
43
43
|
multiple=True,
|
44
44
|
default=[],
|
45
45
|
envvar="MPFLASH_IGNORE",
|
@@ -73,7 +73,13 @@ def cli_list_mcus(serial: List[str], ignore: List[str], bluetooth: bool, as_json
|
|
73
73
|
# TODO? Ask user to select a serialport if [?] is given ?
|
74
74
|
|
75
75
|
conn_mcus = list_mcus(ignore=ignore, include=serial, bluetooth=bluetooth)
|
76
|
+
# ignore boards that have the [micropython-stubber] ignore flag set
|
77
|
+
conn_mcus = [item for item in conn_mcus if not (item.toml.get("mpflash", {}).get("ignore", False))]
|
76
78
|
if as_json:
|
79
|
+
# remove the path and firmware attibutes from the json output as they are always empty
|
80
|
+
for mcu in conn_mcus:
|
81
|
+
del mcu.path
|
82
|
+
del mcu.firmware
|
77
83
|
print(json.dumps([mcu.__dict__ for mcu in conn_mcus], indent=4))
|
78
84
|
progress = False
|
79
85
|
if progress:
|
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
|
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
@@ -92,6 +92,7 @@ class DownloadParams(Params):
|
|
92
92
|
|
93
93
|
|
94
94
|
class BootloaderMethod(Enum):
|
95
|
+
AUTO = "auto"
|
95
96
|
MANUAL = "manual"
|
96
97
|
MPY = "mpy"
|
97
98
|
TOUCH_1200 = "touch1200"
|
@@ -121,7 +122,8 @@ def filtered_comports(
|
|
121
122
|
bluetooth: bool = False,
|
122
123
|
) -> List[ListPortInfo]: # sourcery skip: assign-if-exp
|
123
124
|
"""
|
124
|
-
Get a list of filtered comports.
|
125
|
+
Get a list of filtered comports using the include and ignore lists.
|
126
|
+
both can be globs (e.g. COM*) or exact port names (e.g. COM1)
|
125
127
|
"""
|
126
128
|
if not ignore:
|
127
129
|
ignore = []
|
mpflash/mpflash/config.py
CHANGED
@@ -1,11 +1,11 @@
|
|
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
8
|
import platformdirs
|
8
|
-
from importlib.metadata import version
|
9
9
|
|
10
10
|
from mpflash.logger import log
|
11
11
|
|
@@ -20,6 +20,7 @@ class MPtoolConfig:
|
|
20
20
|
|
21
21
|
quiet: bool = False
|
22
22
|
verbose: bool = False
|
23
|
+
usb: bool = False
|
23
24
|
ignore_ports: List[str] = []
|
24
25
|
firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
|
25
26
|
# test options specified on the commandline
|
mpflash/mpflash/connected.py
CHANGED
@@ -4,14 +4,11 @@ from rich import print
|
|
4
4
|
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
5
5
|
from rich.table import Column
|
6
6
|
|
7
|
+
from mpflash.common import filtered_comports
|
7
8
|
from mpflash.mpremoteboard import MPRemoteBoard
|
8
9
|
|
9
|
-
from .common import filtered_comports
|
10
10
|
|
11
|
-
|
12
|
-
def connected_ports_boards(
|
13
|
-
*, include: List[str], ignore: List[str]
|
14
|
-
) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
|
11
|
+
def connected_ports_boards(*, include: List[str], ignore: List[str]) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
|
15
12
|
"""
|
16
13
|
Returns a tuple containing lists of unique ports and boards from the connected MCUs.
|
17
14
|
Boards that are physically connected, but give no tangible response are ignored.
|
@@ -34,7 +31,7 @@ rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Co
|
|
34
31
|
rp_bar = BarColumn(bar_width=None, table_column=Column())
|
35
32
|
|
36
33
|
|
37
|
-
def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False):
|
34
|
+
def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False) -> List[MPRemoteBoard]:
|
38
35
|
"""
|
39
36
|
Retrieves information about connected microcontroller boards.
|
40
37
|
|
@@ -50,11 +47,17 @@ def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False)
|
|
50
47
|
include=include,
|
51
48
|
bluetooth=bluetooth,
|
52
49
|
)
|
53
|
-
conn_mcus = [MPRemoteBoard(c.device) for c in comports]
|
50
|
+
conn_mcus = [MPRemoteBoard(c.device, location=c.location or "?") for c in comports]
|
54
51
|
|
55
52
|
# a lot of boilerplate to show a progress bar with the comport currently scanned
|
56
53
|
# low update rate to facilitate screen readers/narration
|
57
|
-
with Progress(
|
54
|
+
with Progress(
|
55
|
+
rp_spinner,
|
56
|
+
rp_text,
|
57
|
+
rp_bar,
|
58
|
+
TimeElapsedColumn(),
|
59
|
+
refresh_per_second=1,
|
60
|
+
) as progress:
|
58
61
|
tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
|
59
62
|
progress.tasks[tsk_scan].fields["device"] = "..."
|
60
63
|
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
@@ -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
|
# #########################################################################################################
|
@@ -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
|
@@ -4,12 +4,13 @@ from pathlib import Path
|
|
4
4
|
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
# from .flash_stm32_cube import flash_stm32_cubecli
|
8
|
-
from .flash_stm32_dfu import dfu_init, flash_stm32_dfu
|
9
7
|
from mpflash.mpremoteboard import MPRemoteBoard
|
10
8
|
|
9
|
+
# from .flash_stm32_cube import flash_stm32_cubecli
|
10
|
+
from .stm32_dfu import dfu_init, flash_stm32_dfu
|
11
|
+
|
11
12
|
|
12
|
-
def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool
|
13
|
+
def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool):
|
13
14
|
# sourcery skip: lift-return-into-if
|
14
15
|
dfu_init()
|
15
16
|
if updated := flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase):
|
@@ -4,22 +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
|
-
from rich.progress import track
|
13
12
|
|
14
|
-
from mpflash.mpremoteboard import MPRemoteBoard
|
15
|
-
import tenacity
|
16
13
|
from tenacity import stop_after_attempt, wait_fixed
|
17
14
|
|
18
|
-
from .
|
19
|
-
|
20
|
-
from .
|
21
|
-
from .
|
22
|
-
from .
|
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
|
23
22
|
|
24
23
|
|
25
24
|
def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemoteBoard]:
|
@@ -39,7 +38,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
39
38
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
40
39
|
return None
|
41
40
|
if erase:
|
42
|
-
|
41
|
+
log.warning("Erase not (yet) supported on .UF2, skipping erase.")
|
42
|
+
|
43
|
+
destination = waitfor_uf2(board_id=mcu.port.upper())
|
43
44
|
|
44
45
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
45
46
|
log.error("Board is not in bootloader mode")
|
@@ -49,8 +50,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
49
50
|
board_id = get_board_id(destination) # type: ignore
|
50
51
|
log.info(f"Board ID: {board_id}")
|
51
52
|
try:
|
52
|
-
|
53
|
-
log.success("Done copying, resetting the board
|
53
|
+
copy_firmware_to_uf2(fw_file, destination)
|
54
|
+
log.success("Done copying, resetting the board.")
|
54
55
|
except tenacity.RetryError:
|
55
56
|
log.error("Failed to copy the firmware file to the board.")
|
56
57
|
return None
|
@@ -59,31 +60,29 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
59
60
|
dismount_uf2_linux()
|
60
61
|
|
61
62
|
mcu.wait_for_restart()
|
62
|
-
# time.sleep(1) # 5 secs to short on linux
|
63
63
|
return mcu
|
64
64
|
|
65
65
|
|
66
|
-
def waitfor_uf2():
|
66
|
+
def waitfor_uf2(board_id: str):
|
67
67
|
"""
|
68
68
|
Wait for the UF2 drive to mount
|
69
69
|
"""
|
70
70
|
if sys.platform == "linux":
|
71
|
-
return wait_for_UF2_linux()
|
71
|
+
return wait_for_UF2_linux(board_id=board_id)
|
72
72
|
elif sys.platform == "win32":
|
73
|
-
return wait_for_UF2_windows()
|
73
|
+
return wait_for_UF2_windows(board_id=board_id)
|
74
74
|
elif sys.platform == "darwin":
|
75
|
-
|
76
|
-
return wait_for_UF2_macos()
|
75
|
+
return wait_for_UF2_macos(board_id=board_id)
|
77
76
|
else:
|
78
77
|
log.warning(f"OS {sys.platform} not tested/supported")
|
79
78
|
return None
|
80
79
|
|
81
80
|
|
82
81
|
@tenacity.retry(stop=stop_after_attempt(3), wait=wait_fixed(1), reraise=False)
|
83
|
-
def
|
82
|
+
def copy_firmware_to_uf2(fw_file: Path, destination: Path):
|
84
83
|
"""
|
85
84
|
Copy the firmware file to the destination,
|
86
85
|
Retry 3 times with 1s delay
|
87
86
|
"""
|
88
87
|
log.info(f"Copying {fw_file} to {destination}.")
|
89
|
-
shutil.copy(fw_file, destination)
|
88
|
+
return shutil.copy(fw_file, destination)
|