mpflash 0.5.0__py3-none-any.whl → 0.7.0__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/ask_input.py +94 -23
- mpflash/cli_download.py +31 -15
- mpflash/cli_flash.py +58 -113
- mpflash/cli_group.py +1 -1
- mpflash/cli_list.py +2 -60
- mpflash/cli_main.py +7 -3
- mpflash/common.py +2 -126
- mpflash/download.py +15 -3
- mpflash/downloaded.py +108 -0
- mpflash/errors.py +5 -0
- mpflash/flash.py +38 -133
- mpflash/flash_esp.py +10 -15
- mpflash/flash_stm32.py +1 -3
- mpflash/flash_stm32_cube.py +0 -1
- mpflash/flash_stm32_dfu.py +19 -5
- mpflash/flash_uf2.py +1 -1
- mpflash/list.py +88 -0
- mpflash/mpboard_id/{api.py → __init__.py} +23 -16
- mpflash/mpboard_id/board_id.py +40 -22
- mpflash/mpremoteboard/__init__.py +98 -27
- mpflash/mpremoteboard/runner.py +5 -1
- mpflash/vendor/versions.py +113 -0
- mpflash/worklist.py +147 -0
- {mpflash-0.5.0.dist-info → mpflash-0.7.0.dist-info}/METADATA +4 -4
- mpflash-0.7.0.dist-info/RECORD +40 -0
- mpflash-0.5.0.dist-info/RECORD +0 -35
- /mpflash/{vendored → vendor}/dfu.py +0 -0
- /mpflash/{vendored → vendor}/pydfu.py +0 -0
- /mpflash/{vendored → vendor}/readme.md +0 -0
- {mpflash-0.5.0.dist-info → mpflash-0.7.0.dist-info}/LICENSE +0 -0
- {mpflash-0.5.0.dist-info → mpflash-0.7.0.dist-info}/WHEEL +0 -0
- {mpflash-0.5.0.dist-info → mpflash-0.7.0.dist-info}/entry_points.txt +0 -0
mpflash/common.py
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
import os
|
2
2
|
import time
|
3
|
-
from functools import lru_cache
|
4
3
|
from typing import TypedDict
|
5
4
|
|
6
5
|
from github import Auth, Github
|
7
|
-
from loguru import logger as log
|
8
|
-
from packaging.version import parse
|
9
6
|
from rich.progress import track
|
10
7
|
|
11
|
-
from mpflash.
|
8
|
+
from mpflash.errors import MPFlashError
|
9
|
+
# from mpflash.mpremoteboard import MPRemoteBoard
|
12
10
|
|
13
11
|
PORT_FWTYPES = {
|
14
12
|
"stm32": [".dfu"], # need .dfu for pydfu.py - .hex for cube cli/GUI
|
@@ -38,125 +36,3 @@ class FWInfo(TypedDict):
|
|
38
36
|
preview: bool
|
39
37
|
version: str
|
40
38
|
build: str
|
41
|
-
|
42
|
-
|
43
|
-
#############################################################
|
44
|
-
# Version handling copied from stubber/utils/versions.py
|
45
|
-
#############################################################
|
46
|
-
V_PREVIEW = "preview"
|
47
|
-
"Latest preview version"
|
48
|
-
|
49
|
-
SET_PREVIEW = {"preview", "latest", "master"}
|
50
|
-
|
51
|
-
|
52
|
-
def clean_version(
|
53
|
-
version: str,
|
54
|
-
*,
|
55
|
-
build: bool = False,
|
56
|
-
patch: bool = False,
|
57
|
-
commit: bool = False,
|
58
|
-
drop_v: bool = False,
|
59
|
-
flat: bool = False,
|
60
|
-
):
|
61
|
-
"Clean up and transform the many flavours of versions"
|
62
|
-
# 'v1.13.0-103-gb137d064e' --> 'v1.13-103'
|
63
|
-
if version in {"", "-"}:
|
64
|
-
return version
|
65
|
-
if version.lower() == "stable":
|
66
|
-
_v = get_stable_mp_version()
|
67
|
-
if not _v:
|
68
|
-
log.warning("Could not determine the latest stable version")
|
69
|
-
return "stable"
|
70
|
-
version = _v
|
71
|
-
log.info(f"Using latest stable version: {version}")
|
72
|
-
is_preview = "-preview" in version
|
73
|
-
nibbles = version.split("-")
|
74
|
-
ver_ = nibbles[0].lower().lstrip("v")
|
75
|
-
if not patch and ver_ >= "1.10.0" and ver_ < "1.20.0" and ver_.endswith(".0"):
|
76
|
-
# remove the last ".0" - but only for versions between 1.10 and 1.20 (because)
|
77
|
-
nibbles[0] = nibbles[0][:-2]
|
78
|
-
if len(nibbles) == 1:
|
79
|
-
version = nibbles[0]
|
80
|
-
elif build and not is_preview:
|
81
|
-
version = "-".join(nibbles) if commit else "-".join(nibbles[:-1])
|
82
|
-
else:
|
83
|
-
# version = "-".join((nibbles[0], LATEST))
|
84
|
-
# HACK: this is not always right, but good enough most of the time
|
85
|
-
if is_preview:
|
86
|
-
version = "-".join((nibbles[0], V_PREVIEW))
|
87
|
-
else:
|
88
|
-
version = V_PREVIEW
|
89
|
-
if flat:
|
90
|
-
version = version.strip().replace(".", "_").replace("-", "_")
|
91
|
-
else:
|
92
|
-
version = version.strip().replace("_preview", "-preview").replace("_", ".")
|
93
|
-
|
94
|
-
if drop_v:
|
95
|
-
version = version.lstrip("v")
|
96
|
-
elif not version.startswith("v") and version.lower() not in SET_PREVIEW:
|
97
|
-
version = "v" + version
|
98
|
-
if version in SET_PREVIEW:
|
99
|
-
version = V_PREVIEW
|
100
|
-
return version
|
101
|
-
|
102
|
-
|
103
|
-
@lru_cache(maxsize=10)
|
104
|
-
def micropython_versions(minver: str = "v1.20"):
|
105
|
-
"""Get the list of micropython versions from github tags"""
|
106
|
-
try:
|
107
|
-
gh_client = GH_CLIENT
|
108
|
-
repo = gh_client.get_repo("micropython/micropython")
|
109
|
-
versions = [tag.name for tag in repo.get_tags() if parse(tag.name) >= parse(minver)]
|
110
|
-
except Exception:
|
111
|
-
versions = [
|
112
|
-
"v9.99.9-preview",
|
113
|
-
"v1.22.2",
|
114
|
-
"v1.22.1",
|
115
|
-
"v1.22.0",
|
116
|
-
"v1.21.1",
|
117
|
-
"v1.21.0",
|
118
|
-
"v1.20.0",
|
119
|
-
"v1.19.1",
|
120
|
-
"v1.19",
|
121
|
-
"v1.18",
|
122
|
-
"v1.17",
|
123
|
-
"v1.16",
|
124
|
-
"v1.15",
|
125
|
-
"v1.14",
|
126
|
-
"v1.13",
|
127
|
-
"v1.12",
|
128
|
-
"v1.11",
|
129
|
-
"v1.10",
|
130
|
-
]
|
131
|
-
versions = [v for v in versions if parse(v) >= parse(minver)]
|
132
|
-
return sorted(versions)
|
133
|
-
|
134
|
-
|
135
|
-
def get_stable_mp_version() -> str:
|
136
|
-
# read the versions from the git tags
|
137
|
-
all_versions = micropython_versions(minver="v1.17")
|
138
|
-
return [v for v in all_versions if not v.endswith(V_PREVIEW)][-1]
|
139
|
-
|
140
|
-
|
141
|
-
def get_preview_mp_version() -> str:
|
142
|
-
# read the versions from the git tags
|
143
|
-
all_versions = micropython_versions(minver="v1.17")
|
144
|
-
return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
|
145
|
-
|
146
|
-
|
147
|
-
#############################################################
|
148
|
-
def wait_for_restart(mcu: MPRemoteBoard, timeout: int = 10):
|
149
|
-
"""wait for the board to restart"""
|
150
|
-
for _ in track(
|
151
|
-
range(timeout),
|
152
|
-
description="Waiting for the board to restart",
|
153
|
-
transient=True,
|
154
|
-
get_time=lambda: time.time(),
|
155
|
-
show_speed=False,
|
156
|
-
):
|
157
|
-
time.sleep(1)
|
158
|
-
try:
|
159
|
-
mcu.get_mcu_info()
|
160
|
-
break
|
161
|
-
except ConnectionError:
|
162
|
-
pass
|
mpflash/download.py
CHANGED
@@ -46,6 +46,9 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
46
46
|
"""
|
47
47
|
Get the urls to all the board pages listed on this page.
|
48
48
|
Assumes that all links to firmware have "class": "board-card"
|
49
|
+
|
50
|
+
Args:
|
51
|
+
page_url (str): The url of the page to get the board urls from.
|
49
52
|
"""
|
50
53
|
downloads_html = get_page(page_url)
|
51
54
|
soup = BeautifulSoup(downloads_html, "html.parser")
|
@@ -57,8 +60,17 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
57
60
|
return [{"board": board, "url": page_url + board} for board in boards]
|
58
61
|
|
59
62
|
|
60
|
-
def
|
61
|
-
"""
|
63
|
+
def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
|
64
|
+
"""
|
65
|
+
Get the urls to all the firmware files for a board.
|
66
|
+
Args:
|
67
|
+
page_url (str): The url of the page to get the board urls from.
|
68
|
+
??? base_url (str): The base url to join the relative urls to.
|
69
|
+
ext (str): The extension of the firmware files to get. (with or withouth leading .)
|
70
|
+
|
71
|
+
the urls are relative urls to the site root
|
72
|
+
|
73
|
+
"""
|
62
74
|
html = get_page(board_url)
|
63
75
|
soup = BeautifulSoup(html, "html.parser")
|
64
76
|
# get all the a tags:
|
@@ -109,7 +121,7 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[Firmwar
|
|
109
121
|
# add a board to the list for each firmware found
|
110
122
|
firmwares = []
|
111
123
|
for ext in PORT_FWTYPES[port]:
|
112
|
-
firmwares +=
|
124
|
+
firmwares += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
|
113
125
|
|
114
126
|
for _url in firmwares:
|
115
127
|
board["firmware"] = _url
|
mpflash/downloaded.py
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict, List, Optional
|
3
|
+
|
4
|
+
import jsonlines
|
5
|
+
from loguru import logger as log
|
6
|
+
|
7
|
+
from mpflash.common import FWInfo
|
8
|
+
from mpflash.vendor.versions import clean_version
|
9
|
+
|
10
|
+
from .config import config
|
11
|
+
|
12
|
+
|
13
|
+
# #########################################################################################################
|
14
|
+
def downloaded_firmwares(fw_folder: Path) -> List[FWInfo]:
|
15
|
+
"""Load a list of locally downloaded firmwares from the jsonl file"""
|
16
|
+
firmwares: List[FWInfo] = []
|
17
|
+
try:
|
18
|
+
with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
|
19
|
+
firmwares.extend(iter(reader))
|
20
|
+
except FileNotFoundError:
|
21
|
+
log.error(f"No firmware.jsonl found in {fw_folder}")
|
22
|
+
# sort by filename
|
23
|
+
firmwares.sort(key=lambda x: x["filename"])
|
24
|
+
return firmwares
|
25
|
+
|
26
|
+
|
27
|
+
def find_downloaded_firmware(
|
28
|
+
*,
|
29
|
+
board_id: str,
|
30
|
+
version: str = "",
|
31
|
+
port: str = "",
|
32
|
+
variants: bool = False,
|
33
|
+
fw_folder: Optional[Path] = None,
|
34
|
+
trie: int = 1,
|
35
|
+
selector: Optional[Dict[str, str]] = None,
|
36
|
+
) -> List[FWInfo]:
|
37
|
+
if selector is None:
|
38
|
+
selector = {}
|
39
|
+
fw_folder = fw_folder or config.firmware_folder
|
40
|
+
# Use the information in firmwares.jsonl to find the firmware file
|
41
|
+
fw_list = downloaded_firmwares(fw_folder)
|
42
|
+
if not fw_list:
|
43
|
+
log.error("No firmware files found. Please download the firmware first.")
|
44
|
+
return []
|
45
|
+
# filter by version
|
46
|
+
version = clean_version(version, drop_v=True)
|
47
|
+
fw_list = filter_downloaded_fwlist(fw_list, board_id, version, port, variants, selector)
|
48
|
+
|
49
|
+
if not fw_list and trie < 3:
|
50
|
+
log.info(f"Try ({trie+1}) to find a firmware for the board {board_id}")
|
51
|
+
if trie == 1:
|
52
|
+
# ESP board naming conventions have changed by adding a PORT refix
|
53
|
+
if port.startswith("esp") and not board_id.startswith(port.upper()):
|
54
|
+
board_id = f"{port.upper()}_{board_id}"
|
55
|
+
# RP2 board naming conventions have changed by adding a _RPIprefix
|
56
|
+
if port == "rp2" and not board_id.startswith("RPI_"):
|
57
|
+
board_id = f"RPI_{board_id}"
|
58
|
+
elif trie == 2:
|
59
|
+
board_id = board_id.replace("_", "-")
|
60
|
+
|
61
|
+
fw_list = find_downloaded_firmware(
|
62
|
+
fw_folder=fw_folder,
|
63
|
+
board_id=board_id,
|
64
|
+
version=version,
|
65
|
+
port=port,
|
66
|
+
trie=trie + 1,
|
67
|
+
selector=selector,
|
68
|
+
)
|
69
|
+
# hope we have a match now for the board
|
70
|
+
# sort by filename
|
71
|
+
fw_list.sort(key=lambda x: x["filename"])
|
72
|
+
return fw_list
|
73
|
+
|
74
|
+
|
75
|
+
def filter_downloaded_fwlist(
|
76
|
+
fw_list: List[FWInfo],
|
77
|
+
board_id: str,
|
78
|
+
version: str,
|
79
|
+
port: str,
|
80
|
+
# preview: bool,
|
81
|
+
variants: bool,
|
82
|
+
selector: dict,
|
83
|
+
) -> List[FWInfo]:
|
84
|
+
"""Filter the downloaded firmware list based on the provided parameters"""
|
85
|
+
if "preview" in version:
|
86
|
+
# never get a preview for an older version
|
87
|
+
fw_list = [fw for fw in fw_list if fw["preview"]]
|
88
|
+
else:
|
89
|
+
fw_list = [fw for fw in fw_list if fw["version"] == version]
|
90
|
+
|
91
|
+
# filter by port
|
92
|
+
if port:
|
93
|
+
fw_list = [fw for fw in fw_list if fw["port"] == port]
|
94
|
+
|
95
|
+
if board_id:
|
96
|
+
if variants:
|
97
|
+
# any variant of this board_id
|
98
|
+
fw_list = [fw for fw in fw_list if fw["board"] == board_id]
|
99
|
+
else:
|
100
|
+
# the firmware variant should match exactly the board_id
|
101
|
+
fw_list = [fw for fw in fw_list if fw["variant"] == board_id]
|
102
|
+
if selector and port in selector:
|
103
|
+
fw_list = [fw for fw in fw_list if fw["filename"].endswith(selector[port])]
|
104
|
+
return fw_list
|
105
|
+
|
106
|
+
|
107
|
+
# #########################################################################################################
|
108
|
+
#
|
mpflash/errors.py
ADDED
mpflash/flash.py
CHANGED
@@ -1,150 +1,55 @@
|
|
1
1
|
import time
|
2
2
|
from pathlib import Path
|
3
|
-
from typing import Dict, List, Optional, Tuple
|
4
3
|
|
5
|
-
import jsonlines
|
6
4
|
from loguru import logger as log
|
7
5
|
|
6
|
+
from mpflash.common import PORT_FWTYPES
|
8
7
|
from mpflash.mpremoteboard import MPRemoteBoard
|
9
8
|
|
10
|
-
from .
|
11
|
-
from .
|
9
|
+
from .flash_esp import flash_esp
|
10
|
+
from .flash_stm32 import flash_stm32
|
11
|
+
from .flash_uf2 import flash_uf2
|
12
|
+
from .worklist import WorkList
|
12
13
|
|
13
14
|
# #########################################################################################################
|
14
|
-
WorkList = List[Tuple[MPRemoteBoard, FWInfo]]
|
15
15
|
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
"""Load a list of locally available firmwares from the jsonl file"""
|
20
|
-
firmwares: List[FWInfo] = []
|
21
|
-
try:
|
22
|
-
with jsonlines.open(fw_folder / "firmware.jsonl") as reader:
|
23
|
-
firmwares.extend(iter(reader))
|
24
|
-
except FileNotFoundError:
|
25
|
-
log.error(f"No firmware.jsonl found in {fw_folder}")
|
26
|
-
# sort by filename
|
27
|
-
firmwares.sort(key=lambda x: x["filename"])
|
28
|
-
return firmwares
|
29
|
-
|
30
|
-
|
31
|
-
def find_firmware(
|
32
|
-
*,
|
33
|
-
board: str,
|
34
|
-
version: str = "",
|
35
|
-
port: str = "",
|
36
|
-
variants: bool = False,
|
37
|
-
fw_folder: Optional[Path] = None,
|
38
|
-
trie: int = 1,
|
39
|
-
selector: Optional[Dict[str, str]] = None,
|
40
|
-
) -> List[FWInfo]:
|
41
|
-
if selector is None:
|
42
|
-
selector = {}
|
43
|
-
fw_folder = fw_folder or config.firmware_folder
|
44
|
-
# Use the information in firmwares.jsonl to find the firmware file
|
45
|
-
fw_list = local_firmwares(fw_folder)
|
46
|
-
if not fw_list:
|
47
|
-
log.error("No firmware files found. Please download the firmware first.")
|
48
|
-
return []
|
49
|
-
# filter by version
|
50
|
-
version = clean_version(version, drop_v=True)
|
51
|
-
fw_list = filter_fwlist(fw_list, board, version, port, variants, selector)
|
52
|
-
|
53
|
-
if not fw_list and trie < 2:
|
54
|
-
board_id = board.replace("_", "-")
|
55
|
-
# ESP board naming conventions have changed by adding a PORT refix
|
56
|
-
if port.startswith("esp") and not board_id.startswith(port.upper()):
|
57
|
-
board_id = f"{port.upper()}_{board_id}"
|
58
|
-
# RP2 board naming conventions have changed by adding a _RPIprefix
|
59
|
-
if port == "rp2" and not board_id.startswith("RPI_"):
|
60
|
-
board_id = f"RPI_{board_id}"
|
61
|
-
|
62
|
-
log.info(f"Try ({trie}) to find a firmware for the board {board_id}")
|
63
|
-
fw_list = find_firmware(
|
64
|
-
fw_folder=fw_folder,
|
65
|
-
board=board_id,
|
66
|
-
version=version,
|
67
|
-
port=port,
|
68
|
-
trie=trie + 1,
|
69
|
-
selector=selector,
|
70
|
-
)
|
71
|
-
# hope we have a match now for the board
|
72
|
-
# sort by filename
|
73
|
-
fw_list.sort(key=lambda x: x["filename"])
|
74
|
-
return fw_list
|
75
|
-
|
76
|
-
|
77
|
-
def filter_fwlist(
|
78
|
-
fw_list: List[FWInfo],
|
79
|
-
board: str,
|
80
|
-
version: str,
|
81
|
-
port: str,
|
82
|
-
# preview: bool,
|
83
|
-
variants: bool,
|
84
|
-
selector: dict,
|
85
|
-
) -> List[FWInfo]:
|
86
|
-
"""Filter the firmware list based on the provided parameters"""
|
87
|
-
if "preview" in version:
|
88
|
-
# never get a preview for an older version
|
89
|
-
fw_list = [fw for fw in fw_list if fw["preview"]]
|
90
|
-
else:
|
91
|
-
fw_list = [fw for fw in fw_list if fw["version"] == version]
|
92
|
-
|
93
|
-
# filter by port
|
94
|
-
if port:
|
95
|
-
fw_list = [fw for fw in fw_list if fw["port"] == port]
|
96
|
-
|
97
|
-
if board:
|
98
|
-
if variants:
|
99
|
-
fw_list = [fw for fw in fw_list if fw["board"] == board]
|
100
|
-
else:
|
101
|
-
# the variant should match exactly the board name
|
102
|
-
fw_list = [fw for fw in fw_list if fw["variant"] == board]
|
103
|
-
if selector and port in selector:
|
104
|
-
fw_list = [fw for fw in fw_list if fw["filename"].endswith(selector[port])]
|
105
|
-
return fw_list
|
106
|
-
|
107
|
-
|
108
|
-
# #########################################################################################################
|
109
|
-
#
|
110
|
-
|
111
|
-
|
112
|
-
def auto_update(
|
113
|
-
conn_boards: List[MPRemoteBoard],
|
114
|
-
target_version: str,
|
17
|
+
def flash_list(
|
18
|
+
todo: WorkList,
|
115
19
|
fw_folder: Path,
|
116
|
-
|
117
|
-
|
118
|
-
)
|
119
|
-
"""
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
log.warning(
|
126
|
-
f"Skipping {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is a MicroPython firmware"
|
127
|
-
)
|
128
|
-
continue
|
129
|
-
board_firmwares = find_firmware(
|
130
|
-
fw_folder=fw_folder,
|
131
|
-
board=mcu.board,
|
132
|
-
version=target_version,
|
133
|
-
port=mcu.port,
|
134
|
-
selector=selector,
|
135
|
-
)
|
136
|
-
|
137
|
-
if not board_firmwares:
|
138
|
-
log.error(f"No {target_version} firmware found for {mcu.board} on {mcu.serialport}.")
|
20
|
+
erase: bool,
|
21
|
+
bootloader: bool,
|
22
|
+
):
|
23
|
+
"""Flash a list of boards with the specified firmware."""
|
24
|
+
flashed = []
|
25
|
+
for mcu, fw_info in todo:
|
26
|
+
fw_file = fw_folder / fw_info["filename"] # type: ignore
|
27
|
+
if not fw_file.exists():
|
28
|
+
log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
|
139
29
|
continue
|
140
|
-
|
141
|
-
|
30
|
+
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info['version']}")
|
31
|
+
updated = None
|
32
|
+
# try:
|
33
|
+
if mcu.port in [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts] and fw_file.suffix == ".uf2":
|
34
|
+
# any .uf2 port ["samd", "rp2", "nrf"]:
|
35
|
+
if bootloader:
|
36
|
+
enter_bootloader(mcu)
|
37
|
+
updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
|
38
|
+
elif mcu.port in ["stm32"]:
|
39
|
+
if bootloader:
|
40
|
+
enter_bootloader(mcu)
|
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}")
|
142
47
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
return
|
48
|
+
if updated:
|
49
|
+
flashed.append(updated)
|
50
|
+
else:
|
51
|
+
log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
|
52
|
+
return flashed
|
148
53
|
|
149
54
|
|
150
55
|
def enter_bootloader(mcu: MPRemoteBoard, timeout: int = 10, wait_after: int = 2):
|
mpflash/flash_esp.py
CHANGED
@@ -10,11 +10,9 @@ from typing import List, Optional
|
|
10
10
|
import esptool
|
11
11
|
from loguru import logger as log
|
12
12
|
|
13
|
-
from mpflash.mpboard_id
|
13
|
+
from mpflash.mpboard_id import find_stored_board
|
14
14
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
|
-
from .common import wait_for_restart
|
17
|
-
|
18
16
|
|
19
17
|
def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optional[MPRemoteBoard]:
|
20
18
|
if mcu.port not in ["esp32", "esp8266"] or mcu.board in ["ARDUINO_NANO_ESP32"]:
|
@@ -24,27 +22,24 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
|
|
24
22
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
25
23
|
if not mcu.cpu:
|
26
24
|
# Lookup CPU based on the board name
|
27
|
-
mcu.cpu =
|
25
|
+
mcu.cpu = find_stored_board(mcu.board)["cpu"]
|
28
26
|
|
29
|
-
if mcu.port == "esp8266":
|
30
|
-
baud_rate = str(460_800)
|
31
|
-
else:
|
32
|
-
baud_rate = str(512_000)
|
33
|
-
# baud_rate = str(115_200)
|
34
27
|
cmds: List[List[str]] = []
|
35
28
|
if erase:
|
36
29
|
cmds.append(f"esptool --chip {mcu.cpu} --port {mcu.serialport} erase_flash".split())
|
37
30
|
|
38
|
-
if mcu.cpu.upper() in ("ESP32", "ESP32S2"):
|
39
|
-
start_addr = "0x1000"
|
40
|
-
elif mcu.cpu.upper() in ("ESP32S3", "ESP32C3"):
|
41
|
-
start_addr = "0x0"
|
42
31
|
if mcu.cpu.upper().startswith("ESP32"):
|
32
|
+
baud_rate = str(921_600)
|
33
|
+
if mcu.cpu.upper() in ("ESP32", "ESP32S2"):
|
34
|
+
start_addr = "0x1000"
|
35
|
+
elif mcu.cpu.upper() in ("ESP32S3", "ESP32C3"):
|
36
|
+
start_addr = "0x0"
|
43
37
|
cmds.append(
|
44
38
|
f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --compress {start_addr}".split()
|
45
39
|
+ [str(fw_file)]
|
46
40
|
)
|
47
41
|
elif mcu.cpu.upper() == "ESP8266":
|
42
|
+
baud_rate = str(460_800)
|
48
43
|
start_addr = "0x0"
|
49
44
|
cmds.append(
|
50
45
|
f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --flash_size=detect {start_addr}".split()
|
@@ -59,6 +54,6 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
|
|
59
54
|
return None
|
60
55
|
|
61
56
|
log.info("Done flashing, resetting the board and wait for it to restart")
|
62
|
-
wait_for_restart(
|
63
|
-
log.success(f"Flashed {mcu.
|
57
|
+
mcu.wait_for_restart()
|
58
|
+
log.success(f"Flashed {mcu.serialport} to {mcu.board} {mcu.version}")
|
64
59
|
return mcu
|
mpflash/flash_stm32.py
CHANGED
@@ -4,8 +4,6 @@ from pathlib import Path
|
|
4
4
|
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
from mpflash.common import wait_for_restart
|
8
|
-
|
9
7
|
# from .flash_stm32_cube import flash_stm32_cubecli
|
10
8
|
from .flash_stm32_dfu import dfu_init, flash_stm32_dfu
|
11
9
|
from mpflash.mpremoteboard import MPRemoteBoard
|
@@ -21,6 +19,6 @@ def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool, stm32_dfu: bo
|
|
21
19
|
# log.info("Using STM32CubeProgrammer CLI")
|
22
20
|
# updated = flash_stm32_cubecli(mcu, fw_file=fw_file, erase=erase)
|
23
21
|
|
24
|
-
wait_for_restart(
|
22
|
+
mcu.wait_for_restart()
|
25
23
|
log.success(f"Flashed {mcu.version} to {mcu.board}")
|
26
24
|
return updated
|
mpflash/flash_stm32_cube.py
CHANGED
@@ -16,7 +16,6 @@
|
|
16
16
|
# from rich.progress import track
|
17
17
|
# from strip_ansi import strip_ansi
|
18
18
|
|
19
|
-
# from .common import wait_for_restart
|
20
19
|
# from .mpremoteboard.mpremoteboard import MPRemoteBoard
|
21
20
|
|
22
21
|
# STM32_CLI_WIN = "C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe"
|
mpflash/flash_stm32_dfu.py
CHANGED
@@ -8,7 +8,12 @@ from mpflash.mpremoteboard import MPRemoteBoard
|
|
8
8
|
|
9
9
|
|
10
10
|
def init_libusb_windows() -> bool:
|
11
|
-
|
11
|
+
"""
|
12
|
+
Initializes the libusb backend on Windows.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
bool: True if the initialization is successful, False otherwise.
|
16
|
+
"""
|
12
17
|
import libusb # type: ignore
|
13
18
|
import usb.backend.libusb1 as libusb1
|
14
19
|
|
@@ -21,12 +26,15 @@ def init_libusb_windows() -> bool:
|
|
21
26
|
|
22
27
|
|
23
28
|
try:
|
24
|
-
from .
|
29
|
+
from .vendor import pydfu as pydfu
|
25
30
|
except ImportError:
|
26
31
|
pydfu = None
|
27
32
|
|
28
33
|
|
29
34
|
def dfu_init():
|
35
|
+
"""
|
36
|
+
Initializes the DFU (Device Firmware Upgrade) process.
|
37
|
+
"""
|
30
38
|
if not pydfu:
|
31
39
|
log.error("pydfu not found")
|
32
40
|
return None
|
@@ -40,11 +48,17 @@ def flash_stm32_dfu(
|
|
40
48
|
*,
|
41
49
|
erase: bool = True,
|
42
50
|
) -> Optional[MPRemoteBoard]:
|
51
|
+
"""
|
52
|
+
Flashes the STM32 microcontroller using DFU (Device Firmware Upgrade).
|
43
53
|
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
Args:
|
55
|
+
mcu (MPRemoteBoard): The remote board to flash.
|
56
|
+
fw_file (Path): The path to the firmware file (.dfu).
|
57
|
+
erase (bool, optional): Whether to erase the memory before flashing. Defaults to True.
|
47
58
|
|
59
|
+
Returns:
|
60
|
+
Optional[MPRemoteBoard]: The flashed remote board if successful, None otherwise.
|
61
|
+
"""
|
48
62
|
if not pydfu:
|
49
63
|
log.error("pydfu not found, please install it with 'pip install pydfu' if supported")
|
50
64
|
return None
|
mpflash/flash_uf2.py
CHANGED
@@ -56,6 +56,6 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
56
56
|
log.success("Done copying, resetting the board and wait for it to restart")
|
57
57
|
if sys.platform in ["linux", "darwin"]:
|
58
58
|
dismount_uf2()
|
59
|
-
for _ in track(range(5 + 2)):
|
59
|
+
for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True):
|
60
60
|
time.sleep(1) # 5 secs to short on linux
|
61
61
|
return mcu
|