micropython-stubber 1.17.6__py3-none-any.whl → 1.20.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.
- {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/METADATA +7 -6
- micropython_stubber-1.20.0.dist-info/RECORD +147 -0
- mpflash/README.md +18 -2
- mpflash/libusb_flash.ipynb +203 -0
- mpflash/mpflash/ask_input.py +234 -0
- mpflash/mpflash/cli_download.py +107 -0
- mpflash/mpflash/cli_flash.py +170 -0
- mpflash/mpflash/cli_group.py +40 -7
- mpflash/mpflash/cli_list.py +41 -0
- mpflash/mpflash/cli_main.py +13 -8
- mpflash/mpflash/common.py +33 -121
- mpflash/mpflash/config.py +9 -0
- mpflash/mpflash/{downloader.py → download.py} +110 -96
- mpflash/mpflash/downloaded.py +108 -0
- mpflash/mpflash/errors.py +5 -0
- mpflash/mpflash/flash.py +69 -0
- mpflash/mpflash/flash_esp.py +17 -23
- mpflash/mpflash/flash_stm32.py +16 -113
- mpflash/mpflash/flash_stm32_cube.py +111 -0
- mpflash/mpflash/flash_stm32_dfu.py +101 -0
- mpflash/mpflash/flash_uf2.py +8 -8
- mpflash/mpflash/flash_uf2_linux.py +13 -6
- mpflash/mpflash/flash_uf2_windows.py +24 -12
- mpflash/mpflash/list.py +56 -39
- mpflash/mpflash/logger.py +12 -13
- mpflash/mpflash/mpboard_id/__init__.py +96 -0
- mpflash/mpflash/mpboard_id/board_id.py +63 -0
- mpflash/mpflash/mpboard_id/board_info.csv +2213 -0
- mpflash/mpflash/mpboard_id/board_info.json +19910 -0
- mpflash/mpflash/mpremoteboard/__init__.py +209 -0
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -0
- {stubber/bulk → mpflash/mpflash/mpremoteboard}/runner.py +19 -4
- mpflash/mpflash/vendor/dfu.py +164 -0
- mpflash/mpflash/vendor/pydfu.py +605 -0
- mpflash/mpflash/vendor/readme.md +3 -0
- mpflash/mpflash/vendor/versions.py +113 -0
- mpflash/mpflash/worklist.py +147 -0
- mpflash/poetry.lock +427 -610
- mpflash/pyproject.toml +22 -6
- mpflash/stm32_udev_rules.md +63 -0
- stubber/__init__.py +1 -1
- stubber/basicgit.py +1 -0
- stubber/board/createstubs.py +11 -4
- stubber/board/createstubs_db.py +11 -5
- stubber/board/createstubs_db_min.py +61 -58
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +11 -5
- stubber/board/createstubs_mem_min.py +56 -53
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +54 -51
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/board/modulelist.txt +1 -0
- stubber/bulk/mcu_stubber.py +9 -5
- stubber/codemod/_partials/db_main.py +14 -25
- stubber/codemod/_partials/lvgl_main.py +2 -2
- stubber/codemod/board.py +10 -3
- stubber/commands/clone_cmd.py +7 -7
- stubber/commands/config_cmd.py +3 -0
- stubber/commands/{mcu_cmd.py → get_mcu_cmd.py} +20 -3
- stubber/freeze/get_frozen.py +0 -2
- stubber/publish/candidates.py +1 -1
- stubber/publish/package.py +1 -1
- stubber/publish/pathnames.py +1 -1
- stubber/publish/stubpackage.py +1 -0
- stubber/rst/lookup.py +1 -1
- stubber/stubber.py +1 -9
- stubber/tools/manifestfile.py +5 -3
- stubber/update_fallback.py +104 -104
- stubber/utils/config.py +32 -36
- stubber/utils/repos.py +2 -2
- stubber/utils/versions.py +1 -0
- micropython_stubber-1.17.6.dist-info/RECORD +0 -132
- mpflash/mpflash/flasher.py +0 -276
- stubber/bulk/board_id.py +0 -40
- stubber/bulk/mpremoteboard.py +0 -141
- stubber/commands/get_lobo_cmd.py +0 -58
- stubber/commands/minify_cmd.py +0 -60
- stubber/commands/upd_fallback_cmd.py +0 -36
- stubber/commands/upd_module_list_cmd.py +0 -18
- {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/entry_points.txt +0 -0
- /mpflash/mpflash/{uf2_boardid.py → flash_uf2_boardid.py} +0 -0
@@ -5,20 +5,24 @@ Uses the micropython.org website to get the available versions and locations to
|
|
5
5
|
|
6
6
|
import functools
|
7
7
|
import itertools
|
8
|
-
import json
|
9
8
|
import re
|
10
9
|
from pathlib import Path
|
11
10
|
from typing import Dict, List, Optional
|
12
11
|
from urllib.parse import urljoin
|
13
12
|
|
13
|
+
# #########################################################################################################
|
14
|
+
# make sure that jsonlines does not mistake the MicroPython ujson for the CPython ujson
|
15
|
+
import jsonlines
|
14
16
|
import requests
|
15
|
-
import rich_click as click
|
16
17
|
from bs4 import BeautifulSoup
|
17
18
|
from loguru import logger as log
|
18
19
|
from rich.progress import track
|
19
20
|
|
20
|
-
from .
|
21
|
-
|
21
|
+
from mpflash.common import PORT_FWTYPES
|
22
|
+
|
23
|
+
jsonlines.ujson = None # type: ignore
|
24
|
+
# #########################################################################################################
|
25
|
+
|
22
26
|
|
23
27
|
MICROPYTHON_ORG_URL = "https://micropython.org/"
|
24
28
|
|
@@ -28,21 +32,6 @@ RE_HASH = r"(.g[0-9a-f]+\.)"
|
|
28
32
|
# regex to extract the version from the firmware filename
|
29
33
|
RE_VERSION_PREVIEW = r"(\d+\.\d+(\.\d+)?(-\w+.\d+)?)"
|
30
34
|
|
31
|
-
# boards we are interested in ( this avoids getting a lot of boards that are not relevant)
|
32
|
-
DEFAULT_BOARDS = [
|
33
|
-
"PYBV11",
|
34
|
-
"ESP8266_GENERIC",
|
35
|
-
"ESP32_GENERIC",
|
36
|
-
"ESP32_GENERIC_S3",
|
37
|
-
"RPI_PICO",
|
38
|
-
"RPI_PICO_W",
|
39
|
-
"ADAFRUIT_QTPY_RP2040",
|
40
|
-
"ARDUINO_NANO_RP2040_CONNECT",
|
41
|
-
"PIMORONI_PICOLIPO_16MB",
|
42
|
-
"SEEED_WIO_TERMINAL",
|
43
|
-
"PARTICLE_XENON",
|
44
|
-
]
|
45
|
-
|
46
35
|
|
47
36
|
# use functools.lru_cache to avoid needing to download pages multiple times
|
48
37
|
@functools.lru_cache(maxsize=500)
|
@@ -57,6 +46,9 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
57
46
|
"""
|
58
47
|
Get the urls to all the board pages listed on this page.
|
59
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.
|
60
52
|
"""
|
61
53
|
downloads_html = get_page(page_url)
|
62
54
|
soup = BeautifulSoup(downloads_html, "html.parser")
|
@@ -68,8 +60,17 @@ def get_board_urls(page_url: str) -> List[Dict[str, str]]:
|
|
68
60
|
return [{"board": board, "url": page_url + board} for board in boards]
|
69
61
|
|
70
62
|
|
71
|
-
def
|
72
|
-
"""
|
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
|
+
"""
|
73
74
|
html = get_page(board_url)
|
74
75
|
soup = BeautifulSoup(html, "html.parser")
|
75
76
|
# get all the a tags:
|
@@ -93,20 +94,35 @@ FirmwareInfo = Dict[str, str]
|
|
93
94
|
# boards we are interested in ( this avoids getting a lot of boards we don't care about)
|
94
95
|
# The first run takes ~60 seconds to run for 4 ports , all boards
|
95
96
|
# so it makes sense to cache the results and skip boards as soon as possible
|
96
|
-
def get_boards(
|
97
|
+
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FirmwareInfo]:
|
98
|
+
"""
|
99
|
+
Retrieves a list of firmware information for the specified ports and boards.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
ports (List[str]): The list of ports to check for firmware.
|
103
|
+
boards (List[str]): The list of boards to retrieve firmware information for.
|
104
|
+
clean (bool): A flag indicating whether to perform a clean retrieval.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
List[FirmwareInfo]: A list of firmware information for the specified ports and boards.
|
108
|
+
|
109
|
+
"""
|
97
110
|
board_urls: List[FirmwareInfo] = []
|
98
|
-
for port in
|
111
|
+
for port in ports:
|
99
112
|
download_page_url = f"{MICROPYTHON_ORG_URL}download/?port={port}"
|
100
113
|
_urls = get_board_urls(download_page_url)
|
101
114
|
# filter out boards we don't care about
|
102
|
-
_urls = [board for board in _urls if board["board"] in
|
115
|
+
_urls = [board for board in _urls if board["board"] in boards]
|
103
116
|
# add the port to the board urls
|
104
117
|
for board in _urls:
|
105
118
|
board["port"] = port
|
106
119
|
|
107
120
|
for board in track(_urls, description=f"Checking {port} download pages", transient=True):
|
108
121
|
# add a board to the list for each firmware found
|
109
|
-
firmwares =
|
122
|
+
firmwares = []
|
123
|
+
for ext in PORT_FWTYPES[port]:
|
124
|
+
firmwares += board_firmware_urls(board["url"], MICROPYTHON_ORG_URL, ext)
|
125
|
+
|
110
126
|
for _url in firmwares:
|
111
127
|
board["firmware"] = _url
|
112
128
|
board["preview"] = "preview" in _url # type: ignore
|
@@ -125,34 +141,35 @@ def get_boards(fw_types: Dict[str, str], board_list: List[str], clean: bool) ->
|
|
125
141
|
# remove hash from firmware name
|
126
142
|
fname = re.sub(RE_HASH, ".", fname)
|
127
143
|
board["filename"] = fname
|
144
|
+
board["ext"] = Path(board["firmware"]).suffix
|
128
145
|
board["variant"] = board["filename"].split("-v")[0] if "-v" in board["filename"] else ""
|
129
146
|
board_urls.append(board.copy())
|
130
147
|
return board_urls
|
131
148
|
|
132
149
|
|
133
|
-
def
|
150
|
+
def key_fw_ver_pre_ext_bld(x: FirmwareInfo):
|
134
151
|
"sorting key for the retrieved board urls"
|
135
|
-
return x["variant"], x["version"], x["preview"], x["build"]
|
152
|
+
return x["variant"], x["version"], x["preview"], x["ext"], x["build"]
|
136
153
|
|
137
154
|
|
138
|
-
def
|
155
|
+
def key_fw_var_pre_ext(x: FirmwareInfo):
|
139
156
|
"Grouping key for the retrieved board urls"
|
140
|
-
return x["variant"], x["preview"]
|
157
|
+
return x["variant"], x["preview"], x["ext"]
|
141
158
|
|
142
159
|
|
143
160
|
def download_firmwares(
|
144
161
|
firmware_folder: Path,
|
145
|
-
|
146
|
-
|
162
|
+
ports: List[str],
|
163
|
+
boards: List[str],
|
164
|
+
versions: Optional[List[str]] = None,
|
147
165
|
*,
|
148
|
-
preview: bool = False,
|
149
166
|
force: bool = False,
|
150
167
|
clean: bool = True,
|
151
168
|
):
|
152
169
|
skipped = downloaded = 0
|
153
|
-
if
|
154
|
-
|
155
|
-
unique_boards = get_firmware_list(
|
170
|
+
if versions is None:
|
171
|
+
versions = []
|
172
|
+
unique_boards = get_firmware_list(ports, boards, versions, clean)
|
156
173
|
|
157
174
|
for b in unique_boards:
|
158
175
|
log.debug(b["filename"])
|
@@ -162,7 +179,7 @@ def download_firmwares(
|
|
162
179
|
|
163
180
|
firmware_folder.mkdir(exist_ok=True)
|
164
181
|
|
165
|
-
with open(firmware_folder / "firmware.jsonl", "a"
|
182
|
+
with jsonlines.open(firmware_folder / "firmware.jsonl", "a") as writer:
|
166
183
|
for board in unique_boards:
|
167
184
|
filename = firmware_folder / board["port"] / board["filename"]
|
168
185
|
filename.parent.mkdir(exist_ok=True)
|
@@ -170,7 +187,8 @@ def download_firmwares(
|
|
170
187
|
skipped += 1
|
171
188
|
log.debug(f" {filename} already exists, skip download")
|
172
189
|
continue
|
173
|
-
log.info(f"Downloading {board['firmware']}
|
190
|
+
log.info(f"Downloading {board['firmware']}")
|
191
|
+
log.info(f" to {filename}")
|
174
192
|
try:
|
175
193
|
r = requests.get(board["firmware"], allow_redirects=True)
|
176
194
|
with open(filename, "wb") as fw:
|
@@ -179,29 +197,41 @@ def download_firmwares(
|
|
179
197
|
except requests.RequestException as e:
|
180
198
|
log.exception(e)
|
181
199
|
continue
|
182
|
-
|
183
|
-
json_str = json.dumps(board) + "\n"
|
184
|
-
f_jsonl.write(json_str)
|
200
|
+
writer.write(board)
|
185
201
|
downloaded += 1
|
186
202
|
log.info(f"Downloaded {downloaded} firmwares, skipped {skipped} existing files.")
|
187
203
|
|
188
204
|
|
189
|
-
def get_firmware_list(
|
190
|
-
|
205
|
+
def get_firmware_list(ports: List[str], boards: List[str], versions: List[str], clean: bool):
|
206
|
+
"""
|
207
|
+
Retrieves a list of unique firmware information based on the specified ports, boards, versions, and clean flag.
|
208
|
+
|
209
|
+
Args:
|
210
|
+
ports : The list of ports to check for firmware.
|
211
|
+
boards : The list of boards to filter the firmware by.
|
212
|
+
versions : The list of versions to filter the firmware by.
|
213
|
+
clean : A flag indicating whether to perform a clean check.
|
191
214
|
|
192
|
-
|
215
|
+
Returns:
|
216
|
+
List[FirmwareInfo]: A list of unique firmware information.
|
217
|
+
|
218
|
+
"""
|
219
|
+
|
220
|
+
log.trace("Checking MicroPython download pages")
|
221
|
+
preview = "preview" in versions
|
222
|
+
board_urls = sorted(get_boards(ports, boards, clean), key=key_fw_ver_pre_ext_bld)
|
193
223
|
|
194
224
|
log.debug(f"Total {len(board_urls)} firmwares")
|
195
225
|
relevant = [
|
196
226
|
board
|
197
227
|
for board in board_urls
|
198
|
-
if board["board"] in
|
228
|
+
if board["board"] in boards and (board["version"] in versions or board["preview"] and preview)
|
199
229
|
# and b["port"] in ["esp32", "rp2"]
|
200
230
|
]
|
201
231
|
log.debug(f"Matching firmwares: {len(relevant)}")
|
202
232
|
# select the unique boards
|
203
233
|
unique_boards: List[FirmwareInfo] = []
|
204
|
-
for _, g in itertools.groupby(relevant, key=
|
234
|
+
for _, g in itertools.groupby(relevant, key=key_fw_var_pre_ext):
|
205
235
|
# list is aleady sorted by build so we can just get the last item
|
206
236
|
sub_list = list(g)
|
207
237
|
unique_boards.append(sub_list[-1])
|
@@ -209,55 +239,39 @@ def get_firmware_list(board_list: List[str], version_list: List[str], preview: b
|
|
209
239
|
return unique_boards
|
210
240
|
|
211
241
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
show_default=True,
|
231
|
-
default=["stable"],
|
232
|
-
)
|
233
|
-
@click.option(
|
234
|
-
"--board",
|
235
|
-
"-b",
|
236
|
-
"boards",
|
237
|
-
multiple=True,
|
238
|
-
show_default=True,
|
239
|
-
help="The board(s) to download the firmware for.", # Use '--board all' to download all boards.",
|
240
|
-
)
|
241
|
-
@click.option(
|
242
|
-
"--clean/--no-clean",
|
243
|
-
default=True,
|
244
|
-
show_default=True,
|
245
|
-
help="""Remove dates and hashes from the downloaded firmware filenames.""",
|
246
|
-
)
|
247
|
-
@click.option(
|
248
|
-
"--force",
|
249
|
-
default=False,
|
250
|
-
is_flag=True,
|
251
|
-
help="""Force download of firmware even if it already exists.""",
|
252
|
-
show_default=True,
|
253
|
-
)
|
254
|
-
def download(destination: Path, boards: List[str], versions: List[str], force: bool, clean: bool):
|
255
|
-
versions = list(versions)
|
256
|
-
# preview is not a version, it is an option to include preview versions
|
257
|
-
preview = "preview" in versions
|
258
|
-
versions = [v for v in versions if v != "preview"]
|
242
|
+
def download(
|
243
|
+
destination: Path,
|
244
|
+
ports: List[str],
|
245
|
+
boards: List[str],
|
246
|
+
versions: List[str],
|
247
|
+
force: bool,
|
248
|
+
clean: bool,
|
249
|
+
):
|
250
|
+
"""
|
251
|
+
Downloads firmware files based on the specified destination, ports, boards, versions, force flag, and clean flag.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
destination : The destination folder to save the downloaded firmware files.
|
255
|
+
ports : The list of ports to check for firmware.
|
256
|
+
boards : The list of boards to download firmware for.
|
257
|
+
versions : The list of versions to download firmware for.
|
258
|
+
force : A flag indicating whether to force the download even if the firmware file already exists.
|
259
|
+
clean : A flag indicating whether to perform a clean download.
|
259
260
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
261
|
+
Returns:
|
262
|
+
None
|
263
|
+
|
264
|
+
Raises:
|
265
|
+
SystemExit: If no boards are found or specified.
|
266
|
+
|
267
|
+
"""
|
268
|
+
if not boards:
|
269
|
+
log.critical("No boards found, please connect a board or specify boards to download firmware for.")
|
270
|
+
exit(1)
|
271
|
+
# versions = [clean_version(v, drop_v=True) for v in versions] # remove leading v from version
|
272
|
+
try:
|
273
|
+
destination.mkdir(exist_ok=True, parents=True)
|
274
|
+
except (PermissionError, FileNotFoundError) as e:
|
275
|
+
log.critical(f"Could not create folder {destination}\n{e}")
|
276
|
+
exit(1)
|
277
|
+
download_firmwares(destination, ports, boards, versions, force=force, clean=clean)
|
@@ -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/mpflash/flash.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
import time
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from loguru import logger as log
|
5
|
+
|
6
|
+
from mpflash.common import PORT_FWTYPES
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
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
|
13
|
+
|
14
|
+
# #########################################################################################################
|
15
|
+
|
16
|
+
|
17
|
+
def flash_list(
|
18
|
+
todo: WorkList,
|
19
|
+
fw_folder: Path,
|
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}")
|
29
|
+
continue
|
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}")
|
47
|
+
|
48
|
+
if updated:
|
49
|
+
flashed.append(updated)
|
50
|
+
else:
|
51
|
+
log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
|
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/mpflash/flash_esp.py
CHANGED
@@ -4,47 +4,42 @@
|
|
4
4
|
# #########################################################################################################
|
5
5
|
"""
|
6
6
|
|
7
|
-
import time
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import List, Optional
|
10
9
|
|
11
10
|
import esptool
|
12
11
|
from loguru import logger as log
|
13
12
|
|
14
|
-
from
|
13
|
+
from mpflash.mpboard_id import find_stored_board
|
14
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
16
|
|
17
|
-
def flash_esp(
|
18
|
-
mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True
|
19
|
-
) -> Optional[MPRemoteBoard]:
|
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"]:
|
21
|
-
log.error(
|
22
|
-
f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}"
|
23
|
-
)
|
19
|
+
log.error(f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}")
|
24
20
|
return None
|
25
21
|
|
26
22
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
27
|
-
if mcu.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# baud_rate = str(115_200)
|
23
|
+
if not mcu.cpu:
|
24
|
+
# Lookup CPU based on the board name
|
25
|
+
mcu.cpu = find_stored_board(mcu.board)["cpu"]
|
26
|
+
|
32
27
|
cmds: List[List[str]] = []
|
33
28
|
if erase:
|
34
|
-
cmds.append(
|
35
|
-
f"esptool --chip {mcu.cpu} --port {mcu.serialport} erase_flash".split()
|
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,7 +54,6 @@ def flash_esp(
|
|
59
54
|
return None
|
60
55
|
|
61
56
|
log.info("Done flashing, resetting the board and wait for it to restart")
|
62
|
-
|
63
|
-
mcu.
|
64
|
-
log.success(f"Flashed {mcu.version} to {mcu.board} on {mcu.serialport} done")
|
57
|
+
mcu.wait_for_restart()
|
58
|
+
log.success(f"Flashed {mcu.serialport} to {mcu.board} {mcu.version}")
|
65
59
|
return mcu
|