mpflash 1.25.0.post1__py3-none-any.whl → 1.25.0.post3__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/add_firmware.py +43 -16
- mpflash/ask_input.py +4 -4
- mpflash/basicgit.py +1 -1
- mpflash/bootloader/manual.py +1 -1
- mpflash/cli_download.py +8 -5
- mpflash/cli_flash.py +31 -35
- mpflash/cli_group.py +3 -0
- mpflash/cli_list.py +8 -3
- mpflash/cli_main.py +4 -0
- mpflash/common.py +2 -38
- mpflash/config.py +21 -0
- mpflash/db/__init__.py +2 -0
- mpflash/db/core.py +61 -0
- mpflash/db/gather_boards.py +112 -0
- mpflash/db/loader.py +122 -0
- mpflash/db/meta.py +78 -0
- mpflash/db/micropython_boards.zip +0 -0
- mpflash/db/models.py +93 -0
- mpflash/db/tools.py +27 -0
- mpflash/download/__init__.py +46 -64
- mpflash/download/from_web.py +26 -36
- mpflash/download/fwinfo.py +41 -0
- mpflash/download/jid.py +56 -0
- mpflash/downloaded.py +79 -93
- mpflash/flash/__init__.py +7 -3
- mpflash/flash/esp.py +2 -1
- mpflash/flash/stm32.py +1 -1
- mpflash/flash/uf2/windows.py +3 -1
- mpflash/flash/worklist.py +16 -28
- mpflash/list.py +3 -3
- mpflash/logger.py +43 -9
- mpflash/mpboard_id/__init__.py +3 -9
- mpflash/mpboard_id/alternate.py +56 -0
- mpflash/mpboard_id/board_id.py +11 -94
- mpflash/mpboard_id/known.py +45 -57
- mpflash/mpboard_id/resolve.py +19 -0
- mpflash/mpremoteboard/__init__.py +3 -2
- mpflash/mpremoteboard/mpy_fw_info.py +1 -0
- mpflash/mpremoteboard/runner.py +5 -2
- mpflash/vendor/pydfu.py +33 -6
- mpflash/versions.py +3 -0
- {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0.post3.dist-info}/METADATA +56 -12
- mpflash-1.25.0.post3.dist-info/RECORD +69 -0
- mpflash/db/boards.py +0 -63
- mpflash/db/downloads.py +0 -87
- mpflash/mpboard_id/add_boards.py +0 -260
- mpflash/mpboard_id/board.py +0 -40
- mpflash/mpboard_id/store.py +0 -47
- mpflash-1.25.0.post1.dist-info/RECORD +0 -62
- {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0.post3.dist-info}/LICENSE +0 -0
- {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0.post3.dist-info}/WHEEL +0 -0
- {mpflash-1.25.0.post1.dist-info → mpflash-1.25.0.post3.dist-info}/entry_points.txt +0 -0
mpflash/download/from_web.py
CHANGED
@@ -5,18 +5,16 @@ from pathlib import Path
|
|
5
5
|
from typing import Dict, List, Optional
|
6
6
|
from urllib.parse import urljoin
|
7
7
|
|
8
|
-
|
9
8
|
from loguru import logger as log
|
10
9
|
from rich.progress import track
|
11
10
|
|
12
|
-
from mpflash.common import PORT_FWTYPES
|
11
|
+
from mpflash.common import PORT_FWTYPES
|
12
|
+
from mpflash.db.models import Firmware
|
13
13
|
from mpflash.downloaded import clean_downloaded_firmwares
|
14
14
|
from mpflash.errors import MPFlashError
|
15
|
-
from mpflash.mpboard_id import
|
15
|
+
from mpflash.mpboard_id import known_ports
|
16
16
|
from mpflash.versions import clean_version
|
17
17
|
|
18
|
-
|
19
|
-
|
20
18
|
MICROPYTHON_ORG_URL = "https://micropython.org/"
|
21
19
|
|
22
20
|
|
@@ -24,8 +22,8 @@ MICROPYTHON_ORG_URL = "https://micropython.org/"
|
|
24
22
|
RE_DATE = r"(-\d{8}-)"
|
25
23
|
RE_HASH = r"(.g[0-9a-f]+\.)"
|
26
24
|
# regex to extract the version and the build from the firmware filename
|
27
|
-
# group 1 is the version, group
|
28
|
-
RE_VERSION_PREVIEW = r"v([\d\.]+)
|
25
|
+
# group 1 is the version+Preview , gr2 just the version, group 3 is the build
|
26
|
+
RE_VERSION_PREVIEW = r"v(([\d\.]+)(?:-preview)?)\.?(\d+)?\."
|
29
27
|
|
30
28
|
|
31
29
|
# 'RPI_PICO_W-v1.23.uf2'
|
@@ -107,7 +105,7 @@ def board_firmware_urls(board_url: str, base_url: str, ext: str) -> List[str]:
|
|
107
105
|
# boards we are interested in ( this avoids getting a lot of boards we don't care about)
|
108
106
|
# The first run takes ~60 seconds to run for 4 ports , all boards
|
109
107
|
# so it makes sense to cache the results and skip boards as soon as possible
|
110
|
-
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[
|
108
|
+
def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[Firmware]:
|
111
109
|
# sourcery skip: use-getitem-for-re-match-groups
|
112
110
|
"""
|
113
111
|
Retrieves a list of firmware information for the specified ports and boards.
|
@@ -115,15 +113,15 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]
|
|
115
113
|
Args:
|
116
114
|
ports (List[str]): The list of ports to check for firmware.
|
117
115
|
boards (List[str]): The list of boards to retrieve firmware information for.
|
118
|
-
clean (bool):
|
116
|
+
clean (bool): Remove date and hash from the firmware name.
|
119
117
|
|
120
118
|
Returns:
|
121
119
|
List[FWInfo]: A list of firmware information for the specified ports and boards.
|
122
120
|
|
123
121
|
"""
|
124
|
-
board_urls: List[
|
122
|
+
board_urls: List[Firmware] = []
|
125
123
|
if ports is None:
|
126
|
-
ports =
|
124
|
+
ports = known_ports()
|
127
125
|
for port in ports:
|
128
126
|
download_page_url = f"{MICROPYTHON_ORG_URL}download/?port={port}"
|
129
127
|
urls = get_board_urls(download_page_url)
|
@@ -152,52 +150,44 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]
|
|
152
150
|
fname = re.sub(RE_DATE, "-", fname)
|
153
151
|
# remove hash from firmware name
|
154
152
|
fname = re.sub(RE_HASH, ".", fname)
|
155
|
-
fw_info =
|
156
|
-
|
153
|
+
fw_info = Firmware(
|
154
|
+
firmware_file=fname,
|
157
155
|
port=port,
|
158
|
-
|
159
|
-
|
160
|
-
firmware=_url,
|
156
|
+
board_id=board["board"],
|
157
|
+
source=_url,
|
161
158
|
version="",
|
159
|
+
custom=False,
|
160
|
+
description="", # todo : add description from download page
|
162
161
|
)
|
163
|
-
# board["firmware"] = _url
|
164
|
-
# board["preview"] = "preview" in _url # type: ignore
|
165
162
|
if ver_match := re.search(RE_VERSION_PREVIEW, _url):
|
166
|
-
fw_info.version = clean_version(ver_match
|
167
|
-
fw_info.build = ver_match
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
# fw_info.build = fw_info.version.split("preview.")[-1]
|
173
|
-
# else:
|
174
|
-
# fw_info.build = "0"
|
175
|
-
|
176
|
-
fw_info.ext = Path(fw_info.firmware).suffix
|
177
|
-
fw_info.variant = fw_info.filename.split("-v")[0] if "-v" in fw_info.filename else ""
|
163
|
+
fw_info.version = clean_version(ver_match[1])
|
164
|
+
fw_info.build = int(ver_match[3] or 0)
|
165
|
+
if "-v" in fname:
|
166
|
+
# get the full board_id[-variant] from the filename
|
167
|
+
# filename : 'ESP32_GENERIC-v1.25.0.bin'
|
168
|
+
fw_info.board_id = fname.split("-v")[0]
|
178
169
|
|
179
170
|
board_urls.append(fw_info)
|
180
171
|
return board_urls
|
181
172
|
|
182
173
|
|
183
|
-
|
184
|
-
def fetch_firmware_files(available_firmwares: List[FWInfo], firmware_folder: Path, force: bool):
|
174
|
+
def fetch_firmware_files(available_firmwares: List[Firmware], firmware_folder: Path, force: bool):
|
185
175
|
# Just in time import
|
186
176
|
import requests
|
187
177
|
|
188
178
|
for board in available_firmwares:
|
189
|
-
filename = firmware_folder / board.port / board.
|
179
|
+
filename = firmware_folder / board.port / board.firmware_file
|
190
180
|
filename.parent.mkdir(exist_ok=True)
|
191
181
|
if filename.exists() and not force:
|
192
182
|
log.debug(f" {filename} already exists, skip download")
|
193
183
|
continue
|
194
|
-
log.info(f"Downloading {board.
|
184
|
+
log.info(f"Downloading {board.source}")
|
195
185
|
log.info(f" to {filename}")
|
196
186
|
try:
|
197
|
-
r = requests.get(board.
|
187
|
+
r = requests.get(board.source, allow_redirects=True)
|
198
188
|
with open(filename, "wb") as fw:
|
199
189
|
fw.write(r.content)
|
200
|
-
board.
|
190
|
+
board.firmware_file = str(filename.relative_to(firmware_folder))
|
201
191
|
except requests.RequestException as e:
|
202
192
|
log.exception(e)
|
203
193
|
continue
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
|
5
|
+
@dataclass
|
6
|
+
class FWInfo:
|
7
|
+
"""
|
8
|
+
Downloaded Firmware information
|
9
|
+
is somewhat related to the BOARD class in the mpboard_id module
|
10
|
+
"""
|
11
|
+
|
12
|
+
port: str # MicroPython port
|
13
|
+
board: str # MicroPython board
|
14
|
+
filename: str = field(default="") # relative filename of the firmware image
|
15
|
+
url: str = field(default="") # url or path to original firmware image
|
16
|
+
variant: str = field(default="") # MicroPython variant
|
17
|
+
preview: bool = field(default=False) # True if the firmware is a preview version
|
18
|
+
version: str = field(default="") # MicroPython version (NO v prefix)
|
19
|
+
url: str = field(default="") # url to the firmware image download folder
|
20
|
+
build: str = field(default="0") # The build = number of commits since the last release
|
21
|
+
ext: str = field(default="") # the file extension of the firmware
|
22
|
+
family: str = field(default="micropython") # The family of the firmware
|
23
|
+
custom: bool = field(default=False) # True if the firmware is a custom build
|
24
|
+
description: str = field(default="") # Description used by this firmware (custom only)
|
25
|
+
mcu: str = field(default="")
|
26
|
+
|
27
|
+
def to_dict(self) -> dict:
|
28
|
+
"""Convert the object to a dictionary"""
|
29
|
+
return self.__dict__
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def from_dict(cls, data: dict) -> "FWInfo":
|
33
|
+
"""Create a FWInfo object from a dictionary"""
|
34
|
+
valid_keys = {field.name for field in FWInfo.__dataclass_fields__.values()}
|
35
|
+
filtered_data = {k: v for k, v in data.items() if k in valid_keys}
|
36
|
+
# add missing keys
|
37
|
+
if "ext" not in data:
|
38
|
+
data["ext"] = Path(data["filename"]).suffix
|
39
|
+
if "family" not in data:
|
40
|
+
data["family"] = "micropython"
|
41
|
+
return FWInfo(**filtered_data)
|
mpflash/download/jid.py
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Just In-time Download of firmware if not already available
|
2
|
+
from loguru import logger as log
|
3
|
+
|
4
|
+
from mpflash.common import Params
|
5
|
+
from mpflash.download import download
|
6
|
+
from mpflash.downloaded import find_downloaded_firmware
|
7
|
+
from mpflash.errors import MPFlashError
|
8
|
+
from mpflash.flash.worklist import WorkList
|
9
|
+
from mpflash.mpboard_id.alternate import alternate_board_names
|
10
|
+
|
11
|
+
|
12
|
+
def ensure_firmware_downloaded(worklist: WorkList, version: str, force: bool) -> None:
|
13
|
+
"""
|
14
|
+
Ensure all firmware in the worklist is downloaded for the given version.
|
15
|
+
|
16
|
+
Iterates over the worklist, downloads missing firmware, and updates the worklist
|
17
|
+
with the downloaded firmware.
|
18
|
+
|
19
|
+
Raises MPFlashError if download fails.
|
20
|
+
"""
|
21
|
+
# iterate over the worklist ann update missing firmware
|
22
|
+
newlist: WorkList = []
|
23
|
+
for mcu, firmware in worklist:
|
24
|
+
if force:
|
25
|
+
board_firmwares = []
|
26
|
+
else:
|
27
|
+
if firmware:
|
28
|
+
# firmware is already downloaded
|
29
|
+
newlist.append((mcu, firmware))
|
30
|
+
continue
|
31
|
+
# check if the firmware is already downloaded
|
32
|
+
board_firmwares = find_downloaded_firmware(
|
33
|
+
board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
|
34
|
+
version=version,
|
35
|
+
port=mcu.port,
|
36
|
+
)
|
37
|
+
if not board_firmwares:
|
38
|
+
# download the firmware
|
39
|
+
log.info(f"Downloading {version} firmware for {mcu.board} on {mcu.serialport}.")
|
40
|
+
download(ports=[mcu.port], boards=alternate_board_names(mcu.board, mcu.port), versions=[version], force=True, clean=True)
|
41
|
+
new_firmware = find_downloaded_firmware(
|
42
|
+
board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
|
43
|
+
version=version,
|
44
|
+
port=mcu.port,
|
45
|
+
)
|
46
|
+
if not new_firmware:
|
47
|
+
raise MPFlashError(f"Failed to download {version} firmware for {mcu.board} on {mcu.serialport}.")
|
48
|
+
newlist.append((mcu, new_firmware[0]))
|
49
|
+
else:
|
50
|
+
log.info(f"Found {version} firmware {board_firmwares[-1].firmware_file} for {mcu.board} on {mcu.serialport}.")
|
51
|
+
newlist.append((mcu, firmware))
|
52
|
+
|
53
|
+
worklist.clear()
|
54
|
+
worklist.extend(newlist)
|
55
|
+
|
56
|
+
pass
|
mpflash/downloaded.py
CHANGED
@@ -1,113 +1,99 @@
|
|
1
|
+
import os
|
1
2
|
from pathlib import Path
|
2
|
-
from typing import
|
3
|
+
from typing import List
|
3
4
|
|
4
|
-
import jsonlines
|
5
5
|
from loguru import logger as log
|
6
6
|
|
7
|
-
from mpflash.
|
8
|
-
from mpflash.db.
|
7
|
+
from mpflash.config import config
|
8
|
+
from mpflash.db.core import Session
|
9
|
+
from mpflash.db.models import Firmware
|
10
|
+
from mpflash.mpboard_id.alternate import alternate_board_names
|
9
11
|
from mpflash.versions import clean_version
|
10
12
|
|
11
|
-
from .config import config
|
12
|
-
|
13
13
|
# #########################################################################################################
|
14
14
|
|
15
15
|
|
16
|
-
def clean_downloaded_firmwares(
|
16
|
+
def clean_downloaded_firmwares() -> None:
|
17
|
+
"""
|
18
|
+
- Check if all firmware records in the database are still available on disk.
|
19
|
+
- If not, remove the record from the database.
|
20
|
+
- For all firmware files on disk that are not in the database:
|
21
|
+
- loag a warning message.
|
22
|
+
- Check if the file is a valid firmware file.
|
23
|
+
- If so, add it to the database.
|
24
|
+
|
17
25
|
"""
|
18
|
-
Remove duplicate entries from the firmware.jsonl file, keeping the latest one
|
19
|
-
uniqueness is based on the filename
|
20
26
|
"""
|
21
|
-
|
22
|
-
|
23
|
-
pass
|
27
|
+
Get all firmware files in the firmware directory and its subfolders.
|
28
|
+
"""
|
24
29
|
|
30
|
+
firmware_dir = Path(config.firmware_folder)
|
25
31
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
selector: Optional[Dict[str, str]] = None,
|
35
|
-
) -> List[FWInfo]:
|
36
|
-
if selector is None:
|
37
|
-
selector = {}
|
38
|
-
|
39
|
-
# Use the information in firmwares.jsonl to find the firmware file
|
40
|
-
log.debug(f"{trie}] Looking for firmware for {board_id} {version} ")
|
41
|
-
fw_list = downloaded()
|
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)
|
47
|
-
fw_list = filter_downloaded_fwlist(fw_list, board_id, version, port, variants, selector)
|
32
|
+
"""
|
33
|
+
Returns a set of firmware file paths (relative to firmware_dir) found on disk.
|
34
|
+
Uses a generator for performance and includes files in subfolders.
|
35
|
+
Skips files with certain extensions.
|
36
|
+
"""
|
37
|
+
firmware_files_on_disk = {
|
38
|
+
str(f.relative_to(firmware_dir)) for f in firmware_dir.rglob("*") if f.is_file() and f.suffix not in {".db", ".bak", ".jsonl"}
|
39
|
+
}
|
48
40
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# ESP board naming conventions have changed by adding a PORT prefix
|
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 _RPI prefix
|
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("_", "-")
|
41
|
+
with Session() as session:
|
42
|
+
db_firmwares = session.query(Firmware).all()
|
43
|
+
db_firmware_files = {fw.firmware_file for fw in db_firmwares}
|
60
44
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
45
|
+
# Remove DB records for files not on disk
|
46
|
+
for fw in db_firmwares:
|
47
|
+
if fw.firmware_file not in firmware_files_on_disk:
|
48
|
+
log.warning(f"Firmware file missing on disk, removing DB record: {fw.firmware_file}")
|
49
|
+
session.delete(fw)
|
50
|
+
session.commit()
|
73
51
|
|
52
|
+
# Warn about files on disk not in DB
|
53
|
+
for fw_file in firmware_files_on_disk - db_firmware_files:
|
54
|
+
log.debug(f"Found file in firmware folder but not in DB: {fw_file}")
|
74
55
|
|
75
|
-
def filter_downloaded_fwlist(
|
76
|
-
fw_list: List[FWInfo],
|
77
|
-
board_id: str,
|
78
|
-
version: str, # v1.2.3
|
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
|
-
# older FWInfo version has no v1.2.3 prefix
|
90
|
-
either = [clean_version(version, drop_v=False), clean_version(version, drop_v=True)]
|
91
|
-
fw_list = [fw for fw in fw_list if fw.version in either]
|
92
|
-
log.trace(f"Filtering firmware for {version} : {len(fw_list)} found.")
|
93
|
-
# filter by port
|
94
|
-
if port:
|
95
|
-
fw_list = [fw for fw in fw_list if fw.port == port and Path(fw.firmware).suffix in PORT_FWTYPES[port]]
|
96
|
-
log.trace(f"Filtering firmware for {port} : {len(fw_list)} found.")
|
97
56
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
57
|
+
def find_downloaded_firmware(
|
58
|
+
board_id: str,
|
59
|
+
version: str = "",
|
60
|
+
port: str = "",
|
61
|
+
variants: bool = False,
|
62
|
+
) -> List[Firmware]:
|
63
|
+
version = clean_version(version)
|
64
|
+
log.debug(f"Looking for firmware for {board_id} {version} ")
|
65
|
+
# Special handling for preview versions
|
66
|
+
with Session() as session:
|
67
|
+
if version == "preview" or "preview" in version:
|
68
|
+
# Find all preview firmwares for this board/port, return the latest (highest build)
|
69
|
+
query = session.query(Firmware).filter(Firmware.board_id == board_id)
|
70
|
+
if port:
|
71
|
+
query = query.filter(Firmware.port == port)
|
72
|
+
query = query.filter(Firmware.firmware_file.contains("preview")).order_by(Firmware.build.desc())
|
73
|
+
fw_list = query.all()
|
74
|
+
if fw_list:
|
75
|
+
return [fw_list[0]] # Return the latest preview only
|
102
76
|
else:
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
77
|
+
fw_list = session.query(Firmware).filter(Firmware.board_id == board_id, Firmware.version == version).all()
|
78
|
+
if fw_list:
|
79
|
+
return fw_list
|
80
|
+
#
|
81
|
+
more_board_ids = alternate_board_names(board_id, port)
|
82
|
+
#
|
83
|
+
log.debug(f"2nd search with renamed board_id :{board_id}")
|
84
|
+
with Session() as session:
|
85
|
+
if version == "preview" or "preview" in version:
|
86
|
+
query = session.query(Firmware).filter(Firmware.board_id.in_(more_board_ids))
|
87
|
+
if port:
|
88
|
+
query = query.filter(Firmware.port == port)
|
89
|
+
query = query.filter(Firmware.firmware_file.contains("preview")).order_by(Firmware.build.desc())
|
90
|
+
fw_list = query.all()
|
91
|
+
if fw_list:
|
92
|
+
return [fw_list[0]]
|
93
|
+
else:
|
94
|
+
fw_list = session.query(Firmware).filter(Firmware.board_id.in_(more_board_ids), Firmware.version == version).all()
|
95
|
+
if fw_list:
|
96
|
+
return fw_list
|
97
|
+
log.warning(f"No firmware files found for board {board_id} version {version}")
|
98
|
+
return []
|
111
99
|
|
112
|
-
# #########################################################################################################
|
113
|
-
#
|
mpflash/flash/__init__.py
CHANGED
@@ -3,7 +3,8 @@ from pathlib import Path
|
|
3
3
|
from loguru import logger as log
|
4
4
|
|
5
5
|
from mpflash.bootloader.activate import enter_bootloader
|
6
|
-
from mpflash.common import PORT_FWTYPES,
|
6
|
+
from mpflash.common import PORT_FWTYPES, UF2_PORTS, BootloaderMethod
|
7
|
+
from mpflash.config import config
|
7
8
|
from mpflash.errors import MPFlashError
|
8
9
|
|
9
10
|
from .esp import flash_esp
|
@@ -15,7 +16,6 @@ from .worklist import WorkList
|
|
15
16
|
|
16
17
|
def flash_list(
|
17
18
|
todo: WorkList,
|
18
|
-
fw_folder: Path,
|
19
19
|
erase: bool,
|
20
20
|
bootloader: BootloaderMethod,
|
21
21
|
**kwargs
|
@@ -23,7 +23,11 @@ def flash_list(
|
|
23
23
|
"""Flash a list of boards with the specified firmware."""
|
24
24
|
flashed = []
|
25
25
|
for mcu, fw_info in todo:
|
26
|
-
|
26
|
+
if not fw_info:
|
27
|
+
log.error(f"Firmware not found for {mcu.board} on {mcu.serialport}, skipping")
|
28
|
+
continue
|
29
|
+
|
30
|
+
fw_file = config.firmware_folder / fw_info.firmware_file
|
27
31
|
if not fw_file.exists():
|
28
32
|
log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
|
29
33
|
continue
|
mpflash/flash/esp.py
CHANGED
@@ -15,6 +15,7 @@ from mpflash.mpremoteboard import MPRemoteBoard
|
|
15
15
|
|
16
16
|
FlashMode = Literal["keep", "qio", "qout", "dio", "dout"]
|
17
17
|
|
18
|
+
|
18
19
|
def flash_esp(
|
19
20
|
mcu: MPRemoteBoard,
|
20
21
|
fw_file: Path,
|
@@ -30,7 +31,7 @@ def flash_esp(
|
|
30
31
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
31
32
|
if not mcu.cpu:
|
32
33
|
# Lookup CPU based on the board name
|
33
|
-
mcu.cpu = find_known_board(mcu.board).
|
34
|
+
mcu.cpu = find_known_board(mcu.board).mcu
|
34
35
|
|
35
36
|
cmds: List[List[str]] = []
|
36
37
|
|
mpflash/flash/stm32.py
CHANGED
@@ -15,5 +15,5 @@ def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool):
|
|
15
15
|
dfu_init()
|
16
16
|
if updated := flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase):
|
17
17
|
mcu.wait_for_restart()
|
18
|
-
log.success(f"Flashed {mcu.version} to {mcu.
|
18
|
+
log.success(f"Flashed {mcu.board_id} {mcu.version} to {mcu.serialport}.")
|
19
19
|
return updated
|
mpflash/flash/uf2/windows.py
CHANGED
@@ -7,13 +7,15 @@ import time
|
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Optional
|
9
9
|
|
10
|
-
from .boardid import get_board_id
|
11
10
|
import psutil
|
12
11
|
from rich.progress import track
|
13
12
|
|
13
|
+
from .boardid import get_board_id
|
14
|
+
|
14
15
|
|
15
16
|
def wait_for_UF2_windows(board_id: str, s_max: int = 10) -> Optional[Path]:
|
16
17
|
"""Wait for the MCU to mount as a drive"""
|
18
|
+
|
17
19
|
if s_max < 1:
|
18
20
|
s_max = 10
|
19
21
|
destination = None
|
mpflash/flash/worklist.py
CHANGED
@@ -5,7 +5,8 @@ from typing import Dict, List, Optional, Tuple
|
|
5
5
|
|
6
6
|
from loguru import logger as log
|
7
7
|
|
8
|
-
from mpflash.common import
|
8
|
+
from mpflash.common import filtered_comports
|
9
|
+
from mpflash.db.models import Firmware
|
9
10
|
from mpflash.downloaded import find_downloaded_firmware
|
10
11
|
from mpflash.errors import MPFlashError
|
11
12
|
from mpflash.list import show_mcus
|
@@ -13,45 +14,38 @@ from mpflash.mpboard_id import find_known_board
|
|
13
14
|
from mpflash.mpremoteboard import MPRemoteBoard
|
14
15
|
|
15
16
|
# #########################################################################################################
|
16
|
-
WorkList = List[Tuple[MPRemoteBoard,
|
17
|
+
WorkList = List[Tuple[MPRemoteBoard, Optional[Firmware]]]
|
17
18
|
# #########################################################################################################
|
18
19
|
|
19
20
|
|
20
21
|
def auto_update(
|
21
22
|
conn_boards: List[MPRemoteBoard],
|
22
23
|
target_version: str,
|
23
|
-
fw_folder: Path,
|
24
|
-
*,
|
25
|
-
selector: Optional[Dict[str, str]] = None,
|
26
24
|
) -> WorkList:
|
27
25
|
"""Builds a list of boards to update based on the connected boards and the firmwares available locally in the firmware folder.
|
28
26
|
|
29
27
|
Args:
|
30
28
|
conn_boards (List[MPRemoteBoard]): List of connected boards
|
31
29
|
target_version (str): Target firmware version
|
32
|
-
fw_folder (Path): Path to the firmware folder
|
33
30
|
selector (Optional[Dict[str, str]], optional): Selector for filtering firmware. Defaults to None.
|
34
31
|
|
35
32
|
Returns:
|
36
33
|
WorkList: List of boards and firmware information to update
|
37
34
|
"""
|
38
|
-
if selector is None:
|
39
|
-
selector = {}
|
40
35
|
wl: WorkList = []
|
41
36
|
for mcu in conn_boards:
|
42
37
|
if mcu.family not in ("micropython", "unknown"):
|
43
38
|
log.warning(f"Skipping flashing {mcu.family} {mcu.port} {mcu.board} on {mcu.serialport} as it is not a MicroPython firmware")
|
44
39
|
continue
|
45
40
|
board_firmwares = find_downloaded_firmware(
|
46
|
-
|
47
|
-
board_id=mcu.board if not mcu.variant else f"{mcu.board}-{mcu.variant}",
|
41
|
+
board_id=f"{mcu.board}-{mcu.variant}" if mcu.variant else mcu.board,
|
48
42
|
version=target_version,
|
49
43
|
port=mcu.port,
|
50
|
-
selector=selector,
|
51
44
|
)
|
52
45
|
|
53
46
|
if not board_firmwares:
|
54
|
-
log.
|
47
|
+
log.warning(f"No {target_version} firmware found for {mcu.board} on {mcu.serialport}.")
|
48
|
+
wl.append((mcu, None))
|
55
49
|
continue
|
56
50
|
|
57
51
|
if len(board_firmwares) > 1:
|
@@ -59,7 +53,7 @@ def auto_update(
|
|
59
53
|
|
60
54
|
# just use the last firmware
|
61
55
|
fw_info = board_firmwares[-1]
|
62
|
-
log.info(f"Found {target_version} firmware {fw_info.
|
56
|
+
log.info(f"Found {target_version} firmware {fw_info.firmware_file} for {mcu.board} on {mcu.serialport}.")
|
63
57
|
wl.append((mcu, fw_info))
|
64
58
|
return wl
|
65
59
|
|
@@ -69,7 +63,6 @@ def manual_worklist(
|
|
69
63
|
*,
|
70
64
|
board_id: str,
|
71
65
|
version: str,
|
72
|
-
fw_folder: Path,
|
73
66
|
) -> WorkList:
|
74
67
|
"""Create a worklist for a single board specified manually.
|
75
68
|
|
@@ -77,7 +70,6 @@ def manual_worklist(
|
|
77
70
|
serial (str): Serial port of the board
|
78
71
|
board (str): Board_ID
|
79
72
|
version (str): Firmware version
|
80
|
-
fw_folder (Path): Path to the firmware folder
|
81
73
|
|
82
74
|
Returns:
|
83
75
|
WorkList: List of boards and firmware information to update
|
@@ -89,13 +81,13 @@ def manual_worklist(
|
|
89
81
|
info = find_known_board(board_id)
|
90
82
|
mcu.port = info.port
|
91
83
|
# need the CPU type for the esptool
|
92
|
-
mcu.cpu = info.
|
84
|
+
mcu.cpu = info.mcu
|
93
85
|
except (LookupError, MPFlashError) as e:
|
94
86
|
log.error(f"Board {board_id} not found in board database")
|
95
87
|
log.exception(e)
|
96
88
|
return []
|
97
89
|
mcu.board = board_id
|
98
|
-
firmwares = find_downloaded_firmware(
|
90
|
+
firmwares = find_downloaded_firmware(board_id=board_id, version=version, port=mcu.port)
|
99
91
|
if not firmwares:
|
100
92
|
log.error(f"No firmware found for {mcu.port} {board_id} version {version}")
|
101
93
|
return []
|
@@ -107,32 +99,29 @@ def single_auto_worklist(
|
|
107
99
|
serial: str,
|
108
100
|
*,
|
109
101
|
version: str,
|
110
|
-
fw_folder: Path,
|
111
102
|
) -> WorkList:
|
112
103
|
"""Create a worklist for a single serial-port.
|
113
104
|
|
114
105
|
Args:
|
115
106
|
serial_port (str): Serial port of the board
|
116
107
|
version (str): Firmware version
|
117
|
-
fw_folder (Path): Path to the firmware folder
|
118
108
|
|
119
109
|
Returns:
|
120
110
|
WorkList: List of boards and firmware information to update
|
121
111
|
"""
|
122
112
|
log.trace(f"Auto updating {serial} to {version}")
|
123
113
|
conn_boards = [MPRemoteBoard(serial)]
|
124
|
-
todo = auto_update(conn_boards, version
|
125
|
-
show_mcus(conn_boards)
|
114
|
+
todo = auto_update(conn_boards, version) # type: ignore # List / list
|
115
|
+
show_mcus(conn_boards)
|
126
116
|
return todo
|
127
117
|
|
128
118
|
|
129
119
|
def full_auto_worklist(
|
130
120
|
all_boards: List[MPRemoteBoard],
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
fw_folder: Path,
|
121
|
+
*,
|
122
|
+
include: List[str],
|
123
|
+
ignore: List[str],
|
124
|
+
version: str,
|
136
125
|
) -> WorkList:
|
137
126
|
"""
|
138
127
|
Create a worklist for all connected micropython boards based on the information retrieved from the board.
|
@@ -140,14 +129,13 @@ def full_auto_worklist(
|
|
140
129
|
|
141
130
|
Args:
|
142
131
|
version (str): Firmware version
|
143
|
-
fw_folder (Path): Path to the firmware folder
|
144
132
|
|
145
133
|
Returns:
|
146
134
|
WorkList: List of boards and firmware information to update
|
147
135
|
"""
|
148
136
|
log.trace(f"Auto updating all boards to {version}")
|
149
137
|
if selected_boards := filter_boards(all_boards, include=include, ignore=ignore):
|
150
|
-
return auto_update(selected_boards, version
|
138
|
+
return auto_update(selected_boards, version)
|
151
139
|
else:
|
152
140
|
return []
|
153
141
|
|