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.
Files changed (83) hide show
  1. {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/METADATA +7 -6
  2. micropython_stubber-1.20.0.dist-info/RECORD +147 -0
  3. mpflash/README.md +18 -2
  4. mpflash/libusb_flash.ipynb +203 -0
  5. mpflash/mpflash/ask_input.py +234 -0
  6. mpflash/mpflash/cli_download.py +107 -0
  7. mpflash/mpflash/cli_flash.py +170 -0
  8. mpflash/mpflash/cli_group.py +40 -7
  9. mpflash/mpflash/cli_list.py +41 -0
  10. mpflash/mpflash/cli_main.py +13 -8
  11. mpflash/mpflash/common.py +33 -121
  12. mpflash/mpflash/config.py +9 -0
  13. mpflash/mpflash/{downloader.py → download.py} +110 -96
  14. mpflash/mpflash/downloaded.py +108 -0
  15. mpflash/mpflash/errors.py +5 -0
  16. mpflash/mpflash/flash.py +69 -0
  17. mpflash/mpflash/flash_esp.py +17 -23
  18. mpflash/mpflash/flash_stm32.py +16 -113
  19. mpflash/mpflash/flash_stm32_cube.py +111 -0
  20. mpflash/mpflash/flash_stm32_dfu.py +101 -0
  21. mpflash/mpflash/flash_uf2.py +8 -8
  22. mpflash/mpflash/flash_uf2_linux.py +13 -6
  23. mpflash/mpflash/flash_uf2_windows.py +24 -12
  24. mpflash/mpflash/list.py +56 -39
  25. mpflash/mpflash/logger.py +12 -13
  26. mpflash/mpflash/mpboard_id/__init__.py +96 -0
  27. mpflash/mpflash/mpboard_id/board_id.py +63 -0
  28. mpflash/mpflash/mpboard_id/board_info.csv +2213 -0
  29. mpflash/mpflash/mpboard_id/board_info.json +19910 -0
  30. mpflash/mpflash/mpremoteboard/__init__.py +209 -0
  31. mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -0
  32. {stubber/bulk → mpflash/mpflash/mpremoteboard}/runner.py +19 -4
  33. mpflash/mpflash/vendor/dfu.py +164 -0
  34. mpflash/mpflash/vendor/pydfu.py +605 -0
  35. mpflash/mpflash/vendor/readme.md +3 -0
  36. mpflash/mpflash/vendor/versions.py +113 -0
  37. mpflash/mpflash/worklist.py +147 -0
  38. mpflash/poetry.lock +427 -610
  39. mpflash/pyproject.toml +22 -6
  40. mpflash/stm32_udev_rules.md +63 -0
  41. stubber/__init__.py +1 -1
  42. stubber/basicgit.py +1 -0
  43. stubber/board/createstubs.py +11 -4
  44. stubber/board/createstubs_db.py +11 -5
  45. stubber/board/createstubs_db_min.py +61 -58
  46. stubber/board/createstubs_db_mpy.mpy +0 -0
  47. stubber/board/createstubs_mem.py +11 -5
  48. stubber/board/createstubs_mem_min.py +56 -53
  49. stubber/board/createstubs_mem_mpy.mpy +0 -0
  50. stubber/board/createstubs_min.py +54 -51
  51. stubber/board/createstubs_mpy.mpy +0 -0
  52. stubber/board/modulelist.txt +1 -0
  53. stubber/bulk/mcu_stubber.py +9 -5
  54. stubber/codemod/_partials/db_main.py +14 -25
  55. stubber/codemod/_partials/lvgl_main.py +2 -2
  56. stubber/codemod/board.py +10 -3
  57. stubber/commands/clone_cmd.py +7 -7
  58. stubber/commands/config_cmd.py +3 -0
  59. stubber/commands/{mcu_cmd.py → get_mcu_cmd.py} +20 -3
  60. stubber/freeze/get_frozen.py +0 -2
  61. stubber/publish/candidates.py +1 -1
  62. stubber/publish/package.py +1 -1
  63. stubber/publish/pathnames.py +1 -1
  64. stubber/publish/stubpackage.py +1 -0
  65. stubber/rst/lookup.py +1 -1
  66. stubber/stubber.py +1 -9
  67. stubber/tools/manifestfile.py +5 -3
  68. stubber/update_fallback.py +104 -104
  69. stubber/utils/config.py +32 -36
  70. stubber/utils/repos.py +2 -2
  71. stubber/utils/versions.py +1 -0
  72. micropython_stubber-1.17.6.dist-info/RECORD +0 -132
  73. mpflash/mpflash/flasher.py +0 -276
  74. stubber/bulk/board_id.py +0 -40
  75. stubber/bulk/mpremoteboard.py +0 -141
  76. stubber/commands/get_lobo_cmd.py +0 -58
  77. stubber/commands/minify_cmd.py +0 -60
  78. stubber/commands/upd_fallback_cmd.py +0 -36
  79. stubber/commands/upd_module_list_cmd.py +0 -18
  80. {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/LICENSE +0 -0
  81. {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/WHEEL +0 -0
  82. {micropython_stubber-1.17.6.dist-info → micropython_stubber-1.20.0.dist-info}/entry_points.txt +0 -0
  83. /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 .cli_group import cli
21
- from .common import DEFAULT_FW_PATH, PORT_FWTYPES, clean_version
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 firmware_list(board_url: str, base_url: str, ext: str) -> List[str]:
72
- """Get the urls to all the firmware files for a board."""
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(fw_types: Dict[str, str], board_list: List[str], clean: bool) -> List[FirmwareInfo]:
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 fw_types:
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 board_list]
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 = firmware_list(board["url"], MICROPYTHON_ORG_URL, fw_types[port])
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 key_fw_variant_ver(x: FirmwareInfo):
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 key_fw_variant(x: FirmwareInfo):
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
- board_list: List[str],
146
- version_list: Optional[List[str]] = None,
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 version_list is None:
154
- version_list = []
155
- unique_boards = get_firmware_list(board_list, version_list, preview, clean)
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", encoding="utf-8", buffering=1) as f_jsonl:
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']} to {filename}")
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
- # add the firmware to the jsonl file
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(board_list: List[str], version_list: List[str], preview: bool, clean: bool):
190
- log.trace("Checking MicroPython download pages")
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
- board_urls = sorted(get_boards(PORT_FWTYPES, board_list, clean), key=key_fw_variant_ver)
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 board_list and (board["version"] in version_list or board["preview"] and preview)
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=key_fw_variant):
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
- @cli.command(
213
- "download",
214
- help="Download MicroPython firmware for specific ports, boards and versions.",
215
- )
216
- @click.option(
217
- "--destination",
218
- "-d",
219
- type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
220
- default=DEFAULT_FW_PATH,
221
- show_default=True,
222
- help="The folder to download the firmware to.",
223
- )
224
- @click.option(
225
- "--version",
226
- "-v",
227
- "versions",
228
- multiple=True,
229
- help="The version of MicroPython to to download. Use 'preview' to include preview versions.",
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
- boards = list(boards) or DEFAULT_BOARDS
261
- versions = [clean_version(v, drop_v=True) for v in versions] # remove leading v from version
262
- destination.mkdir(exist_ok=True)
263
- download_firmwares(destination, boards, versions, preview=preview, force=force, clean=clean)
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
+ #
@@ -0,0 +1,5 @@
1
+ """Custom exceptions for the MPFlash package."""
2
+
3
+
4
+ class MPFlashError(Exception):
5
+ pass
@@ -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
@@ -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 stubber.bulk.mpremoteboard import MPRemoteBoard
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.port == "esp8266":
28
- baud_rate = str(460_800)
29
- else:
30
- baud_rate = str(512_000)
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
- time.sleep(5)
63
- mcu.get_mcu_info()
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