mpflash 0.8.2__py3-none-any.whl → 0.8.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.
- mpflash/bootloader/__init__.py +36 -0
- mpflash/bootloader/manual.py +102 -0
- mpflash/bootloader/micropython.py +10 -0
- mpflash/bootloader/touch1200.py +45 -0
- mpflash/cli_download.py +1 -0
- mpflash/cli_flash.py +12 -7
- mpflash/cli_group.py +4 -1
- mpflash/cli_list.py +6 -2
- mpflash/cli_main.py +5 -2
- mpflash/common.py +14 -1
- mpflash/download.py +2 -3
- mpflash/downloaded.py +8 -4
- mpflash/errors.py +5 -1
- mpflash/flash.py +10 -27
- mpflash/flash_uf2_boardid.py +2 -1
- mpflash/flash_uf2_macos.py +0 -3
- mpflash/vendor/click_aliases.py +91 -0
- {mpflash-0.8.2.dist-info → mpflash-0.8.4.dist-info}/METADATA +1 -1
- {mpflash-0.8.2.dist-info → mpflash-0.8.4.dist-info}/RECORD +22 -17
- {mpflash-0.8.2.dist-info → mpflash-0.8.4.dist-info}/LICENSE +0 -0
- {mpflash-0.8.2.dist-info → mpflash-0.8.4.dist-info}/WHEEL +0 -0
- {mpflash-0.8.2.dist-info → mpflash-0.8.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
from mpflash.common import BootloaderMethod
|
4
|
+
from mpflash.errors import MPFlashError
|
5
|
+
from mpflash.logger import log
|
6
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
7
|
+
|
8
|
+
from .manual import enter_bootloader_manual
|
9
|
+
from .micropython import enter_bootloader_mpy
|
10
|
+
from .touch1200 import enter_bootloader_cdc_1200bps
|
11
|
+
|
12
|
+
|
13
|
+
def enter_bootloader(
|
14
|
+
mcu: MPRemoteBoard,
|
15
|
+
method: BootloaderMethod = BootloaderMethod.MPY,
|
16
|
+
timeout: int = 10,
|
17
|
+
wait_after: int = 2,
|
18
|
+
):
|
19
|
+
"""Enter the bootloader mode for the board"""
|
20
|
+
if method == BootloaderMethod.NONE:
|
21
|
+
# NO bootloader requested, so must be OK to flash
|
22
|
+
return True
|
23
|
+
|
24
|
+
log.info(f"Entering bootloader on {mcu.board} on {mcu.serialport} using method: {method.value}")
|
25
|
+
if method == BootloaderMethod.MPY:
|
26
|
+
result = enter_bootloader_mpy(mcu, timeout=timeout)
|
27
|
+
elif method == BootloaderMethod.MANUAL:
|
28
|
+
result = enter_bootloader_manual(mcu, timeout=timeout)
|
29
|
+
elif method == BootloaderMethod.TOUCH_1200:
|
30
|
+
result = enter_bootloader_cdc_1200bps(mcu, timeout=timeout)
|
31
|
+
else:
|
32
|
+
raise MPFlashError(f"Unknown bootloader method {method}")
|
33
|
+
if result:
|
34
|
+
time.sleep(wait_after)
|
35
|
+
log.error(f"Failed to enter bootloader on {mcu.serialport}")
|
36
|
+
return result
|
@@ -0,0 +1,102 @@
|
|
1
|
+
"""Manual bootloader mode entry for various MCUs."""
|
2
|
+
|
3
|
+
from click.exceptions import Abort
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.highlighter import RegexHighlighter
|
6
|
+
from rich.panel import Panel
|
7
|
+
from rich.prompt import Confirm
|
8
|
+
from rich.theme import Theme
|
9
|
+
|
10
|
+
# from mpflash.logger import console, log
|
11
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
12
|
+
|
13
|
+
|
14
|
+
class MCUHighlighter(RegexHighlighter):
|
15
|
+
"""Apply style to anything that looks like an email."""
|
16
|
+
|
17
|
+
base_style = "mcu."
|
18
|
+
highlights = [
|
19
|
+
r"(?P<bold>Method[\s\d\:]*)",
|
20
|
+
r"(?P<bold> \d.)", # numbered items
|
21
|
+
r"(?P<bold> - )", # bullets
|
22
|
+
r"(?P<pad>GPIO[\d]*)",
|
23
|
+
r"(?P<pad>GPI[\d]*)",
|
24
|
+
r"(?P<pad>IO[\d]*)",
|
25
|
+
r"(?P<pad>RUN)",
|
26
|
+
r"(?P<pad>GND)",
|
27
|
+
r"(?P<pad>VCC)",
|
28
|
+
r"(?P<pad>3.3V)",
|
29
|
+
r"(?P<pad>5V)",
|
30
|
+
# buttons
|
31
|
+
r"(?P<button>BOOTSEL)",
|
32
|
+
r"(?P<button>RESET)",
|
33
|
+
r"(?P<button>reset)",
|
34
|
+
# other
|
35
|
+
r"(?P<cable>USB)",
|
36
|
+
# r"(?P<mcu>SAMD[\d]*)",
|
37
|
+
# r"(?P<mcu>ESP[\d]*)",
|
38
|
+
# r"(?P<mcu>rp2)",
|
39
|
+
# r"(?P<mcu>rp2040)",
|
40
|
+
]
|
41
|
+
|
42
|
+
|
43
|
+
# https://rich.readthedocs.io/en/stable/appendix/colors.html?highlight=colors#standard-colors
|
44
|
+
mcu_theme = Theme(
|
45
|
+
{
|
46
|
+
"mcu.bold": "orange3",
|
47
|
+
"mcu.mcu": "orange3",
|
48
|
+
"mcu.button": "bold green",
|
49
|
+
"mcu.pad": "dodger_blue2",
|
50
|
+
"mcu.cable": "dodger_blue2",
|
51
|
+
}
|
52
|
+
)
|
53
|
+
|
54
|
+
|
55
|
+
def enter_bootloader_manual(mcu: MPRemoteBoard, timeout: int = 10):
|
56
|
+
|
57
|
+
message: str
|
58
|
+
if mcu.port == "rp2":
|
59
|
+
message = f"""\
|
60
|
+
Please put your {" ".join([mcu.port,mcu.board])} device into bootloader mode by either:
|
61
|
+
Method 1:
|
62
|
+
1. Unplug the USB cable,
|
63
|
+
2. Press and hold the BOOTSEL button on the device,
|
64
|
+
3. Plug the USB cable back in.
|
65
|
+
4. Release the BOOTSEL button.
|
66
|
+
|
67
|
+
Method 2:
|
68
|
+
1. Press and hold the BOOTSEL button on the device,
|
69
|
+
2. Reset the device by either:
|
70
|
+
- pressing the RESET button on the device
|
71
|
+
- by power-cycling the device,
|
72
|
+
- by briefly connecting the RUN pin to GND
|
73
|
+
3. Release the BOOTSEL button.
|
74
|
+
"""
|
75
|
+
elif mcu.port == "samd":
|
76
|
+
message = f"""\
|
77
|
+
Please put your {mcu.port.upper()} device into bootloader mode by:
|
78
|
+
- Pressing or sliding the RESET button twice in fast succession
|
79
|
+
"""
|
80
|
+
else:
|
81
|
+
message = f"""\
|
82
|
+
Please put your {mcu.port.upper()} device into bootloader mode by:
|
83
|
+
- Pressing the RESET button on the device
|
84
|
+
"""
|
85
|
+
|
86
|
+
# todo: would be nice to re-use the console instance from logger
|
87
|
+
console = Console(highlighter=MCUHighlighter(), theme=mcu_theme)
|
88
|
+
message += "\nIf you are unsure how to enter bootloader mode, please refer to the device documentation."
|
89
|
+
console.print(
|
90
|
+
Panel(
|
91
|
+
message,
|
92
|
+
highlight=True,
|
93
|
+
title="Manual Bootloader",
|
94
|
+
title_align="left",
|
95
|
+
expand=False,
|
96
|
+
)
|
97
|
+
)
|
98
|
+
try:
|
99
|
+
answer = Confirm.ask("Press Enter to continue", default="y")
|
100
|
+
except Abort:
|
101
|
+
return False
|
102
|
+
return answer in ["y", "Y", True]
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"""Module for handling the bootloader mode for micropython boards"""
|
2
|
+
|
3
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
4
|
+
|
5
|
+
|
6
|
+
def enter_bootloader_mpy(mcu: MPRemoteBoard, timeout: int = 10):
|
7
|
+
"""Enter the bootloader mode for the board using mpremote and micropython on the board"""
|
8
|
+
mcu.run_command("bootloader", timeout=timeout)
|
9
|
+
# todo: check if mpremote command was successful
|
10
|
+
return True
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import sys
|
2
|
+
import time
|
3
|
+
|
4
|
+
import serial
|
5
|
+
|
6
|
+
from mpflash.errors import MPFlashError
|
7
|
+
from mpflash.logger import log
|
8
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
9
|
+
|
10
|
+
from .manual import enter_bootloader_manual
|
11
|
+
|
12
|
+
|
13
|
+
def enter_bootloader_cdc_1200bps(mcu: MPRemoteBoard, timeout: int = 10):
|
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
|
20
|
+
if not mcu.serialport:
|
21
|
+
raise MPFlashError("No serial port specified")
|
22
|
+
# try to initiate serial port connection on PORT with 1200 baudrate
|
23
|
+
try:
|
24
|
+
with serial.Serial(
|
25
|
+
port=mcu.serialport,
|
26
|
+
baudrate=1200,
|
27
|
+
parity=serial.PARITY_NONE,
|
28
|
+
stopbits=serial.STOPBITS_ONE,
|
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)
|
36
|
+
|
37
|
+
except serial.SerialException as e:
|
38
|
+
log.exception(e)
|
39
|
+
raise MPFlashError("pySerial error: " + str(e) + "\n") from e
|
40
|
+
except Exception as e:
|
41
|
+
log.exception(e)
|
42
|
+
raise MPFlashError("Error: " + str(e) + "\n") from e
|
43
|
+
|
44
|
+
# be optimistic
|
45
|
+
return True
|
mpflash/cli_download.py
CHANGED
mpflash/cli_flash.py
CHANGED
@@ -4,7 +4,7 @@ from typing import List
|
|
4
4
|
import rich_click as click
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
|
7
|
+
from mpflash.common import BootloaderMethod
|
8
8
|
from mpflash.errors import MPFlashError
|
9
9
|
from mpflash.mpboard_id import find_known_board
|
10
10
|
from mpflash.mpremoteboard import MPRemoteBoard
|
@@ -101,11 +101,13 @@ from .worklist import WorkList, full_auto_worklist, manual_worklist, single_auto
|
|
101
101
|
help="""Erase flash before writing new firmware. (Not supported on UF2 boards)""",
|
102
102
|
)
|
103
103
|
@click.option(
|
104
|
-
"--bootloader
|
105
|
-
|
106
|
-
|
104
|
+
"--bootloader",
|
105
|
+
"-b",
|
106
|
+
"bootloader",
|
107
|
+
type=click.Choice([e.value for e in BootloaderMethod]),
|
108
|
+
default="mpy",
|
107
109
|
show_default=True,
|
108
|
-
help="""
|
110
|
+
help="""How to enter the (MicroPython) bootloader before flashing.""",
|
109
111
|
)
|
110
112
|
def cli_flash_board(**kwargs) -> int:
|
111
113
|
# version to versions, board to boards
|
@@ -121,6 +123,7 @@ def cli_flash_board(**kwargs) -> int:
|
|
121
123
|
params.boards = list(params.boards)
|
122
124
|
params.serial = list(params.serial)
|
123
125
|
params.ignore = list(params.ignore)
|
126
|
+
params.bootloader = BootloaderMethod(params.bootloader)
|
124
127
|
|
125
128
|
# make it simple for the user to flash one board by asking for the serial port if not specified
|
126
129
|
if params.boards == ["?"] and params.serial == "*":
|
@@ -129,14 +132,16 @@ def cli_flash_board(**kwargs) -> int:
|
|
129
132
|
# Detect connected boards if not specified,
|
130
133
|
# and ask for input if boards cannot be detected
|
131
134
|
all_boards: List[MPRemoteBoard] = []
|
132
|
-
if not params.boards
|
135
|
+
if not params.boards:
|
133
136
|
# nothing specified - detect connected boards
|
134
137
|
params.ports, params.boards, all_boards = connected_ports_boards(include=params.ports, ignore=params.ignore)
|
135
138
|
if params.boards == []:
|
136
|
-
# No MicroPython boards detected, but it could be unflashed or
|
139
|
+
# No MicroPython boards detected, but it could be unflashed or in bootloader mode
|
137
140
|
# Ask for serial port and board_id to flash
|
138
141
|
params.serial = ["?"]
|
139
142
|
params.boards = ["?"]
|
143
|
+
# assume manual mode if no board is detected
|
144
|
+
params.bootloader = BootloaderMethod("manual")
|
140
145
|
else:
|
141
146
|
resolve_board_ids(params)
|
142
147
|
|
mpflash/cli_group.py
CHANGED
@@ -5,6 +5,8 @@ Additional comands are added in the submodules.
|
|
5
5
|
|
6
6
|
import rich_click as click
|
7
7
|
|
8
|
+
from mpflash.vendor.click_aliases import ClickAliasedGroup
|
9
|
+
|
8
10
|
from .config import __version__, config
|
9
11
|
from .logger import log, make_quiet, set_loglevel
|
10
12
|
|
@@ -42,7 +44,8 @@ def cb_quiet(ctx, param, value):
|
|
42
44
|
return value
|
43
45
|
|
44
46
|
|
45
|
-
@click.group()
|
47
|
+
@click.group(cls=ClickAliasedGroup)
|
48
|
+
# @click.group()
|
46
49
|
@click.version_option(package_name="mpflash")
|
47
50
|
@click.option(
|
48
51
|
"--quiet",
|
mpflash/cli_list.py
CHANGED
@@ -5,12 +5,16 @@ import rich_click as click
|
|
5
5
|
from rich import print
|
6
6
|
|
7
7
|
from .cli_group import cli
|
8
|
+
from .connected import list_mcus
|
8
9
|
from .list import show_mcus
|
9
10
|
from .logger import make_quiet
|
10
|
-
from .connected import list_mcus
|
11
11
|
|
12
12
|
|
13
|
-
@cli.command(
|
13
|
+
@cli.command(
|
14
|
+
"list",
|
15
|
+
help="List the connected MCU boards. alias: devs",
|
16
|
+
aliases=["devs"],
|
17
|
+
)
|
14
18
|
@click.option(
|
15
19
|
"--json",
|
16
20
|
"-j",
|
mpflash/cli_main.py
CHANGED
@@ -19,12 +19,12 @@ def mpflash():
|
|
19
19
|
cli.add_command(cli_flash_board)
|
20
20
|
|
21
21
|
# cli(auto_envvar_prefix="MPFLASH")
|
22
|
-
if False and os.environ.get("COMPUTERNAME")
|
22
|
+
if False and os.environ.get("COMPUTERNAME").startswith("JOSVERL"):
|
23
23
|
# intentional less error suppression on dev machine
|
24
24
|
result = cli(standalone_mode=False)
|
25
25
|
else:
|
26
26
|
try:
|
27
|
-
result = cli(standalone_mode=
|
27
|
+
result = cli(standalone_mode=True)
|
28
28
|
exit(result)
|
29
29
|
except AttributeError as e:
|
30
30
|
log.error(f"Error: {e}")
|
@@ -32,6 +32,9 @@ def mpflash():
|
|
32
32
|
except click.exceptions.ClickException as e:
|
33
33
|
log.error(f"Error: {e}")
|
34
34
|
exit(-2)
|
35
|
+
except click.exceptions.Abort as e:
|
36
|
+
# Aborted - Ctrl-C
|
37
|
+
exit(-3)
|
35
38
|
|
36
39
|
|
37
40
|
if __name__ == "__main__":
|
mpflash/common.py
CHANGED
@@ -2,6 +2,7 @@ import fnmatch
|
|
2
2
|
import os
|
3
3
|
import sys
|
4
4
|
from dataclasses import dataclass, field
|
5
|
+
from enum import Enum
|
5
6
|
from pathlib import Path
|
6
7
|
from typing import List, Optional, Union
|
7
8
|
|
@@ -90,14 +91,26 @@ class DownloadParams(Params):
|
|
90
91
|
force: bool = False
|
91
92
|
|
92
93
|
|
94
|
+
class BootloaderMethod(Enum):
|
95
|
+
MANUAL = "manual"
|
96
|
+
MPY = "mpy"
|
97
|
+
TOUCH_1200 = "touch1200"
|
98
|
+
NONE = "none"
|
99
|
+
|
100
|
+
|
101
|
+
|
93
102
|
@dataclass
|
94
103
|
class FlashParams(Params):
|
95
104
|
"""Parameters for flashing a board"""
|
96
105
|
|
97
106
|
erase: bool = True
|
98
|
-
bootloader:
|
107
|
+
bootloader: BootloaderMethod = BootloaderMethod.NONE
|
99
108
|
cpu: str = ""
|
100
109
|
|
110
|
+
def __post_init__(self):
|
111
|
+
if isinstance(self.bootloader, str):
|
112
|
+
self.bootloader = BootloaderMethod(self.bootloader)
|
113
|
+
|
101
114
|
|
102
115
|
ParamType = Union[DownloadParams, FlashParams]
|
103
116
|
|
mpflash/download.py
CHANGED
@@ -19,7 +19,6 @@ from loguru import logger as log
|
|
19
19
|
from rich.progress import track
|
20
20
|
|
21
21
|
from mpflash.common import PORT_FWTYPES, FWInfo
|
22
|
-
from mpflash.downloaded import clean_downloaded_firmwares
|
23
22
|
from mpflash.errors import MPFlashError
|
24
23
|
from mpflash.mpboard_id import get_known_ports
|
25
24
|
from mpflash.vendor.versions import clean_version
|
@@ -246,8 +245,8 @@ def download_firmwares(
|
|
246
245
|
continue
|
247
246
|
writer.write(board.to_dict())
|
248
247
|
downloaded += 1
|
249
|
-
if downloaded > 0:
|
250
|
-
|
248
|
+
# if downloaded > 0:
|
249
|
+
# clean_downloaded_firmwares(firmware_folder)
|
251
250
|
log.success(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
|
252
251
|
return downloaded + skipped
|
253
252
|
|
mpflash/downloaded.py
CHANGED
@@ -39,6 +39,7 @@ def clean_downloaded_firmwares(fw_folder: Path) -> None:
|
|
39
39
|
writer.write(fw.to_dict())
|
40
40
|
log.info(f"Removed duplicate entries from firmware.jsonl in {fw_folder}")
|
41
41
|
|
42
|
+
|
42
43
|
def find_downloaded_firmware(
|
43
44
|
*,
|
44
45
|
board_id: str,
|
@@ -53,6 +54,7 @@ def find_downloaded_firmware(
|
|
53
54
|
selector = {}
|
54
55
|
fw_folder = fw_folder or config.firmware_folder
|
55
56
|
# Use the information in firmwares.jsonl to find the firmware file
|
57
|
+
log.debug(f"{trie}] Looking for firmware for {board_id} {version} ")
|
56
58
|
fw_list = downloaded_firmwares(fw_folder)
|
57
59
|
if not fw_list:
|
58
60
|
log.error("No firmware files found. Please download the firmware first.")
|
@@ -101,13 +103,14 @@ def filter_downloaded_fwlist(
|
|
101
103
|
# never get a preview for an older version
|
102
104
|
fw_list = [fw for fw in fw_list if fw.preview]
|
103
105
|
else:
|
104
|
-
# FWInfo version has no v1.2.3 prefix
|
105
|
-
|
106
|
-
fw_list = [fw for fw in fw_list if fw.version
|
107
|
-
|
106
|
+
# older FWInfo version has no v1.2.3 prefix
|
107
|
+
either = [clean_version(version, drop_v=False), clean_version(version, drop_v=True)]
|
108
|
+
fw_list = [fw for fw in fw_list if fw.version in either]
|
109
|
+
log.trace(f"Filtering firmware for {version} : {len(fw_list)} found.")
|
108
110
|
# filter by port
|
109
111
|
if port:
|
110
112
|
fw_list = [fw for fw in fw_list if fw.port == port]
|
113
|
+
log.trace(f"Filtering firmware for {port} : {len(fw_list)} found.")
|
111
114
|
|
112
115
|
if board_id:
|
113
116
|
if variants:
|
@@ -116,6 +119,7 @@ def filter_downloaded_fwlist(
|
|
116
119
|
else:
|
117
120
|
# the firmware variant should match exactly the board_id
|
118
121
|
fw_list = [fw for fw in fw_list if fw.variant == board_id]
|
122
|
+
log.trace(f"Filtering firmware for {board_id} : {len(fw_list)} found.")
|
119
123
|
if selector and port in selector:
|
120
124
|
fw_list = [fw for fw in fw_list if fw.filename.endswith(selector[port])]
|
121
125
|
return fw_list
|
mpflash/errors.py
CHANGED
mpflash/flash.py
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
import time
|
2
1
|
from pathlib import Path
|
3
2
|
|
4
3
|
from loguru import logger as log
|
5
4
|
|
6
|
-
from mpflash.
|
7
|
-
from mpflash.
|
5
|
+
from mpflash.bootloader import enter_bootloader
|
6
|
+
from mpflash.common import PORT_FWTYPES, BootloaderMethod
|
8
7
|
|
9
8
|
from .flash_esp import flash_esp
|
10
9
|
from .flash_stm32 import flash_stm32
|
@@ -14,13 +13,15 @@ from .worklist import WorkList
|
|
14
13
|
# #########################################################################################################
|
15
14
|
|
16
15
|
|
16
|
+
|
17
17
|
def flash_list(
|
18
18
|
todo: WorkList,
|
19
19
|
fw_folder: Path,
|
20
20
|
erase: bool,
|
21
|
-
bootloader:
|
21
|
+
bootloader: BootloaderMethod,
|
22
22
|
):
|
23
23
|
"""Flash a list of boards with the specified firmware."""
|
24
|
+
UF2_PORTS = [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts]
|
24
25
|
flashed = []
|
25
26
|
for mcu, fw_info in todo:
|
26
27
|
fw_file = fw_folder / fw_info.filename
|
@@ -30,14 +31,13 @@ def flash_list(
|
|
30
31
|
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info.version}")
|
31
32
|
updated = None
|
32
33
|
# try:
|
33
|
-
if mcu.port in
|
34
|
-
|
35
|
-
|
36
|
-
enter_bootloader(mcu)
|
34
|
+
if mcu.port in UF2_PORTS and fw_file.suffix == ".uf2":
|
35
|
+
if not enter_bootloader(mcu, bootloader):
|
36
|
+
continue
|
37
37
|
updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
|
38
38
|
elif mcu.port in ["stm32"]:
|
39
|
-
if bootloader:
|
40
|
-
|
39
|
+
if not enter_bootloader(mcu, bootloader):
|
40
|
+
continue
|
41
41
|
updated = flash_stm32(mcu, fw_file, erase=erase)
|
42
42
|
elif mcu.port in ["esp32", "esp8266"]:
|
43
43
|
# bootloader is handled by esptool for esp32/esp8266
|
@@ -50,20 +50,3 @@ def flash_list(
|
|
50
50
|
else:
|
51
51
|
log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
|
52
52
|
return flashed
|
53
|
-
|
54
|
-
|
55
|
-
def enter_bootloader(mcu: MPRemoteBoard, timeout: int = 10, wait_after: int = 2):
|
56
|
-
"""Enter the bootloader mode for the board"""
|
57
|
-
log.info(f"Entering bootloader on {mcu.board} on {mcu.serialport}")
|
58
|
-
mcu.run_command("bootloader", timeout=timeout)
|
59
|
-
time.sleep(wait_after)
|
60
|
-
|
61
|
-
|
62
|
-
# TODO:
|
63
|
-
# flash from some sort of queue to allow different images to be flashed to the same board
|
64
|
-
# - flash variant 1
|
65
|
-
# - stub variant 1
|
66
|
-
# - flash variant 2
|
67
|
-
# - stub variant 2
|
68
|
-
#
|
69
|
-
# JIT download / download any missing firmwares based on the detected boards
|
mpflash/flash_uf2_boardid.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from pathlib import Path
|
2
|
+
|
2
3
|
from loguru import logger as log
|
3
4
|
|
4
5
|
|
@@ -10,5 +11,5 @@ def get_board_id(path: Path):
|
|
10
11
|
for line in data:
|
11
12
|
if line.startswith("Board-ID"):
|
12
13
|
board_id = line[9:].strip()
|
13
|
-
log.
|
14
|
+
log.debug(f"INFO_UF2.TXT Board-ID={board_id}")
|
14
15
|
return board_id
|
mpflash/flash_uf2_macos.py
CHANGED
@@ -3,15 +3,12 @@
|
|
3
3
|
# sourcery skip: snake-case-functions
|
4
4
|
from __future__ import annotations
|
5
5
|
|
6
|
-
import sys
|
7
6
|
import time
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import Optional
|
10
9
|
|
11
|
-
from loguru import logger as log
|
12
10
|
from rich.progress import track
|
13
11
|
|
14
|
-
from .flash_uf2_boardid import get_board_id
|
15
12
|
|
16
13
|
|
17
14
|
def wait_for_UF2_macos(s_max: int = 10) -> Optional[Path]:
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Copyright (c) 2016 Robbin Bonthond
|
2
|
+
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
# ------------------------------------------------------------------------------------
|
13
|
+
# modified to avoid conflcts with rich_click
|
14
|
+
|
15
|
+
# sourcery skip: assign-if-exp, use-named-expression
|
16
|
+
|
17
|
+
import rich_click as click
|
18
|
+
|
19
|
+
_click7 = click.__version__[0] >= "7"
|
20
|
+
|
21
|
+
|
22
|
+
class ClickAliasedGroup(click.RichGroup):
|
23
|
+
def __init__(self, *args, **kwargs):
|
24
|
+
super().__init__(*args, **kwargs)
|
25
|
+
self._commands = {}
|
26
|
+
self._aliases = {}
|
27
|
+
|
28
|
+
def add_command(self, *args, **kwargs):
|
29
|
+
aliases = kwargs.pop("aliases", [])
|
30
|
+
super().add_command(*args, **kwargs)
|
31
|
+
if aliases:
|
32
|
+
cmd = args[0]
|
33
|
+
name = args[1] if len(args) > 1 else None
|
34
|
+
name = name or cmd.name
|
35
|
+
if name is None:
|
36
|
+
raise TypeError("Command has no name.")
|
37
|
+
|
38
|
+
self._commands[name] = aliases
|
39
|
+
for alias in aliases:
|
40
|
+
self._aliases[alias] = cmd.name
|
41
|
+
|
42
|
+
def command(self, *args, **kwargs):
|
43
|
+
aliases = kwargs.pop("aliases", [])
|
44
|
+
decorator = super().command(*args, **kwargs)
|
45
|
+
if not aliases:
|
46
|
+
return decorator
|
47
|
+
|
48
|
+
def _decorator(f):
|
49
|
+
cmd = decorator(f)
|
50
|
+
if aliases:
|
51
|
+
self._commands[cmd.name] = aliases
|
52
|
+
for alias in aliases:
|
53
|
+
self._aliases[alias] = cmd.name
|
54
|
+
return cmd
|
55
|
+
|
56
|
+
return _decorator
|
57
|
+
|
58
|
+
def group(self, *args, **kwargs):
|
59
|
+
aliases = kwargs.pop("aliases", [])
|
60
|
+
decorator = super().group(*args, **kwargs)
|
61
|
+
if not aliases:
|
62
|
+
return decorator
|
63
|
+
|
64
|
+
def _decorator(f):
|
65
|
+
cmd = decorator(f)
|
66
|
+
if aliases:
|
67
|
+
self._commands[cmd.name] = aliases
|
68
|
+
for alias in aliases:
|
69
|
+
self._aliases[alias] = cmd.name
|
70
|
+
return cmd
|
71
|
+
|
72
|
+
return _decorator
|
73
|
+
|
74
|
+
def resolve_alias(self, cmd_name):
|
75
|
+
if cmd_name in self._aliases:
|
76
|
+
return self._aliases[cmd_name]
|
77
|
+
return cmd_name
|
78
|
+
|
79
|
+
def get_command(self, ctx, cmd_name):
|
80
|
+
cmd_name = self.resolve_alias(cmd_name)
|
81
|
+
command = super().get_command(ctx, cmd_name)
|
82
|
+
if command:
|
83
|
+
return command
|
84
|
+
return None
|
85
|
+
|
86
|
+
# def format_commands(self, ctx, formatter):
|
87
|
+
# TODO: output alias with commands - but that is a significant re-write
|
88
|
+
# for now add alias to help text
|
89
|
+
|
90
|
+
|
91
|
+
# ------------------------------------------------------------------------------------
|
@@ -1,26 +1,30 @@
|
|
1
1
|
mpflash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
mpflash/add_firmware.py,sha256=u_g9mID557fptLEJ1Nld9n27V1R1og8uEkadm0O3YTw,3435
|
3
3
|
mpflash/ask_input.py,sha256=e54WfgktG7Ff8dBUknXioROq3lPNZ-eacUjfmUvFfS0,8496
|
4
|
-
mpflash/
|
5
|
-
mpflash/
|
6
|
-
mpflash/
|
7
|
-
mpflash/
|
8
|
-
mpflash/
|
9
|
-
mpflash/
|
4
|
+
mpflash/bootloader/__init__.py,sha256=NRi_zWLNhstoUeisXlknrNyrCcEaHrzEkTxnrmUg1m0,1304
|
5
|
+
mpflash/bootloader/manual.py,sha256=4FNQg0JpyRmfISrDDfZ5j9iQBK-Jyrnz8nLqKf1ZEuc,3104
|
6
|
+
mpflash/bootloader/micropython.py,sha256=QQJlXUnyVA4cRS2O4GbrQhpKhFFvxtQC5G6WQcSCbp8,409
|
7
|
+
mpflash/bootloader/touch1200.py,sha256=AJ4GPLtvCR5LDDe5Uu_5Rg8OI2JHxEB-blzCcO5x9Tc,1511
|
8
|
+
mpflash/cli_download.py,sha256=zufBC0xNPZCq28mG_fepupziizbm86wTpfNEszkei9Q,3656
|
9
|
+
mpflash/cli_flash.py,sha256=ITd9eJzvdtr6SeeB3PO5Ja2jG4TFxsW7JfL3pCNJBn0,7288
|
10
|
+
mpflash/cli_group.py,sha256=gvUaQM-4wk6fV50YxkMJeYEt3D0ZsQJM7t86T8xOpWE,2139
|
11
|
+
mpflash/cli_list.py,sha256=_3XzR8RyjlojbOqGKm_TLwcQojCb4OgCYi7mcjjoflI,2046
|
12
|
+
mpflash/cli_main.py,sha256=MHhU1cAYEhwOpHG2p3OEfwpSrn9-J2foGVZqby_HO8k,1134
|
13
|
+
mpflash/common.py,sha256=gkj71hGibMEcmThJ5LSrAV5vp6SpJAJNLHWO4GjB040,5929
|
10
14
|
mpflash/config.py,sha256=R7PomStIFHSTnLhpWhw9P2DL_b-8a8Wrja3aY-dPm6A,762
|
11
15
|
mpflash/connected.py,sha256=CBG_DJ33OPWAPbX-ICQpL1LcFOhNYpLUSB0Q5v7gi9s,3029
|
12
|
-
mpflash/download.py,sha256=
|
13
|
-
mpflash/downloaded.py,sha256=
|
14
|
-
mpflash/errors.py,sha256=
|
15
|
-
mpflash/flash.py,sha256=
|
16
|
+
mpflash/download.py,sha256=VFV0XP8mTnBXOeo_JbyHBjIQJ0PUUiXu8hoO0lAxid8,14235
|
17
|
+
mpflash/downloaded.py,sha256=cHq_fWUuZ0sXOCs5d3NIf3etUDsAXtnwnO7tkRqJE9A,4870
|
18
|
+
mpflash/errors.py,sha256=IAidY3qkZsXy6Pm1rdmVFmGyg81ywHhse3itaPctA2w,247
|
19
|
+
mpflash/flash.py,sha256=U17kp3ftG-noqhgA1HSYxK945sLhIR-Rh3xZgckT4H8,1927
|
16
20
|
mpflash/flash_esp.py,sha256=dEc_B7-f1BMUGBMrwIm83ulcCqaS5MlrPAp3FCNgNfk,2311
|
17
21
|
mpflash/flash_stm32.py,sha256=d4BoQl3a9Tchnvn2ZTuq2MpYBB4MTaRukwtEncI95k0,823
|
18
22
|
mpflash/flash_stm32_cube.py,sha256=w7aGWjReeWUKl0Q3ZjXH8BRqNO1Tk9AO7gtRNUg1c9Y,3970
|
19
23
|
mpflash/flash_stm32_dfu.py,sha256=G70EZodWb-aRi507Jxbys-VEwbBGU1oZacow3_nq-d4,2972
|
20
24
|
mpflash/flash_uf2.py,sha256=2IKgEOnlsB9bILJWHILMKbGo9b39NKazBWp1-T_eKKs,2463
|
21
|
-
mpflash/flash_uf2_boardid.py,sha256=
|
25
|
+
mpflash/flash_uf2_boardid.py,sha256=U5wGM8VA3wEpUxQCMtuXpMZZomdVH8J_Zd5_GekUMuU,423
|
22
26
|
mpflash/flash_uf2_linux.py,sha256=Oy9V4g7JSuks2hHFeO_OHdBKSGktbqZOtsivuxfl-xg,4055
|
23
|
-
mpflash/flash_uf2_macos.py,sha256=
|
27
|
+
mpflash/flash_uf2_macos.py,sha256=fGz-LGoYKX772sqJb4pAvcMw-2Orlc83ecOIk0-1GYc,987
|
24
28
|
mpflash/flash_uf2_windows.py,sha256=a-iIGPkwIoUXA7atCz0_uZp-kipSL24P-IE5ka1B1Mk,1025
|
25
29
|
mpflash/list.py,sha256=0TawTkwhjKPPj7nTHoDn8nQ54WOkGRurP1BJVeg956g,2963
|
26
30
|
mpflash/logger.py,sha256=dI_H_a7EOdQJyvoeRHQuYeZuTKYVUS3DUPTLhE9rkdM,1098
|
@@ -35,13 +39,14 @@ mpflash/mpremoteboard/mpy_fw_info.py,sha256=6AQbN3jtQgllqWQYl4e-63KeEtV08EXk8_Jn
|
|
35
39
|
mpflash/mpremoteboard/runner.py,sha256=-PgzAeBGbyXaAUlwyiw4mcINsP2U1XRRjP1_QdBrxpg,4786
|
36
40
|
mpflash/uf2disk.py,sha256=4_P2l-kedM7VSliA2u706LQLxvu3xWSod1-lj-xjZis,298
|
37
41
|
mpflash/vendor/basicgit.py,sha256=sflgCv7apLbV2w8F6gmhc-3kuqDnnS4tdGol6JT2uTM,9545
|
42
|
+
mpflash/vendor/click_aliases.py,sha256=c853EHSlkE2DvFqeFvFpwXKuJj3_jsXDP7iotVOKaAw,3156
|
38
43
|
mpflash/vendor/dfu.py,sha256=ZXMcE6aH4-43Wh4tbQT4U-q-BU3RUiL3JAxmP_QAK2s,5755
|
39
44
|
mpflash/vendor/pydfu.py,sha256=_MdBRo1EeNeKDqFPSTB5tNL1jGSBJgsVeVjE5e7Pb8s,20542
|
40
45
|
mpflash/vendor/readme.md,sha256=iIIZxuLUIGHQ0KODzYVtMezsztvyxCXcNJp_AzwTIPk,86
|
41
46
|
mpflash/vendor/versions.py,sha256=thk1a5wEEhXIQoL0zZ7oooeFyQeSoI00CIUbZF0b3rI,3783
|
42
47
|
mpflash/worklist.py,sha256=MKHDynttVP3lsHSfb7DEqeQ2mRV0da96lD4lIcS_zQY,5962
|
43
|
-
mpflash-0.8.
|
44
|
-
mpflash-0.8.
|
45
|
-
mpflash-0.8.
|
46
|
-
mpflash-0.8.
|
47
|
-
mpflash-0.8.
|
48
|
+
mpflash-0.8.4.dist-info/entry_points.txt,sha256=Jk_visOhYOsZIcSP2Ms9hKqfKy1iorR-6dYltSoWCpY,52
|
49
|
+
mpflash-0.8.4.dist-info/LICENSE,sha256=mWpNhsIxWzetYNnTpr4eb3HtgsxGIC8KcYWxXEcxQvE,1077
|
50
|
+
mpflash-0.8.4.dist-info/METADATA,sha256=wjudSFDz_BU3eYbqjjnxd68dCHfzaZpnR37MD2UCh9s,15275
|
51
|
+
mpflash-0.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
52
|
+
mpflash-0.8.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|