mpflash 0.7.6__py3-none-any.whl → 0.8.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/add_firmware.py +98 -0
- mpflash/ask_input.py +74 -111
- mpflash/cli_download.py +31 -21
- mpflash/cli_flash.py +64 -34
- mpflash/cli_group.py +14 -12
- mpflash/cli_list.py +39 -3
- mpflash/cli_main.py +16 -9
- mpflash/common.py +125 -12
- mpflash/config.py +2 -0
- mpflash/connected.py +74 -0
- mpflash/download.py +52 -42
- mpflash/downloaded.py +9 -9
- mpflash/flash.py +2 -2
- mpflash/flash_esp.py +1 -1
- mpflash/flash_uf2.py +15 -7
- mpflash/flash_uf2_linux.py +5 -16
- mpflash/flash_uf2_macos.py +78 -0
- mpflash/list.py +4 -45
- mpflash/mpboard_id/__init__.py +28 -38
- mpflash/mpboard_id/add_boards.py +255 -0
- mpflash/mpboard_id/board.py +37 -0
- mpflash/mpboard_id/board_id.py +18 -23
- mpflash/mpboard_id/board_info.zip +0 -0
- mpflash/mpboard_id/store.py +42 -0
- mpflash/uf2disk.py +12 -0
- mpflash/vendor/basicgit.py +288 -0
- mpflash/vendor/dfu.py +1 -0
- mpflash/vendor/versions.py +7 -3
- mpflash/worklist.py +70 -47
- {mpflash-0.7.6.dist-info → mpflash-0.8.0.dist-info}/METADATA +20 -7
- mpflash-0.8.0.dist-info/RECORD +47 -0
- mpflash/mpboard_id/board_info.csv +0 -2213
- mpflash/mpboard_id/board_info.json +0 -19910
- mpflash-0.7.6.dist-info/RECORD +0 -40
- {mpflash-0.7.6.dist-info → mpflash-0.8.0.dist-info}/LICENSE +0 -0
- {mpflash-0.7.6.dist-info → mpflash-0.8.0.dist-info}/WHEEL +0 -0
- {mpflash-0.7.6.dist-info → mpflash-0.8.0.dist-info}/entry_points.txt +0 -0
mpflash/flash_esp.py
CHANGED
@@ -22,7 +22,7 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
|
|
22
22
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
23
23
|
if not mcu.cpu:
|
24
24
|
# Lookup CPU based on the board name
|
25
|
-
mcu.cpu = find_known_board(mcu.board)
|
25
|
+
mcu.cpu = find_known_board(mcu.board).cpu
|
26
26
|
|
27
27
|
cmds: List[List[str]] = []
|
28
28
|
if erase:
|
mpflash/flash_uf2.py
CHANGED
@@ -14,7 +14,9 @@ from rich.progress import track
|
|
14
14
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
16
|
from .common import PORT_FWTYPES
|
17
|
-
from .
|
17
|
+
from .config import config
|
18
|
+
from .flash_uf2_linux import dismount_uf2_linux, wait_for_UF2_linux
|
19
|
+
from .flash_uf2_macos import wait_for_UF2_macos
|
18
20
|
from .flash_uf2_windows import wait_for_UF2_windows
|
19
21
|
|
20
22
|
|
@@ -27,9 +29,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
27
29
|
- copy the firmware file to the drive
|
28
30
|
- wait for the device to restart (5s)
|
29
31
|
|
30
|
-
for
|
31
|
-
|
32
|
-
|
32
|
+
for Linux - to support headless operation ( GH Actions ) :
|
33
|
+
pmount and pumount are used to mount and unmount the drive
|
34
|
+
as this is not done automatically by the OS in headless mode.
|
33
35
|
"""
|
34
36
|
if ".uf2" not in PORT_FWTYPES[mcu.port]:
|
35
37
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
@@ -41,9 +43,15 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
41
43
|
destination = wait_for_UF2_linux()
|
42
44
|
elif sys.platform == "win32":
|
43
45
|
destination = wait_for_UF2_windows()
|
46
|
+
elif sys.platform == "darwin":
|
47
|
+
log.warning(f"OS {sys.platform} not tested/supported")
|
48
|
+
# TODO: test which of the options is best
|
49
|
+
if "macos_uf2" in config.tests:
|
50
|
+
destination = wait_for_UF2_macos()
|
51
|
+
else:
|
52
|
+
destination = wait_for_UF2_linux()
|
44
53
|
else:
|
45
54
|
log.warning(f"OS {sys.platform} not tested/supported")
|
46
|
-
destination = wait_for_UF2_linux()
|
47
55
|
return None
|
48
56
|
|
49
57
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
@@ -54,8 +62,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
54
62
|
log.info(f"Copying {fw_file} to {destination}.")
|
55
63
|
shutil.copy(fw_file, destination)
|
56
64
|
log.success("Done copying, resetting the board and wait for it to restart")
|
57
|
-
if sys.platform in ["linux"
|
58
|
-
|
65
|
+
if sys.platform in ["linux"]:
|
66
|
+
dismount_uf2_linux()
|
59
67
|
for _ in track(range(5 + 2), description="Waiting for the board to restart", transient=True, refresh_per_second=2):
|
60
68
|
time.sleep(1) # 5 secs to short on linux
|
61
69
|
return mcu
|
mpflash/flash_uf2_linux.py
CHANGED
@@ -13,28 +13,15 @@ from loguru import logger as log
|
|
13
13
|
from rich.progress import track
|
14
14
|
|
15
15
|
from .flash_uf2_boardid import get_board_id
|
16
|
+
from .uf2disk import UF2Disk
|
16
17
|
|
17
18
|
glb_dismount_me: List[UF2Disk] = []
|
18
19
|
|
19
20
|
|
20
|
-
class UF2Disk:
|
21
|
-
"""Info to support mounting and unmounting of UF2 drives on linux"""
|
22
|
-
|
23
|
-
device_path: str
|
24
|
-
label: str
|
25
|
-
mountpoint: str
|
26
|
-
|
27
|
-
def __repr__(self):
|
28
|
-
return repr(self.__dict__)
|
29
|
-
|
30
|
-
|
31
21
|
def get_uf2_drives():
|
32
22
|
"""
|
33
23
|
Get a list of all the (un)mounted UF2 drives
|
34
24
|
"""
|
35
|
-
if sys.platform != "linux":
|
36
|
-
log.error("pumount only works on Linux")
|
37
|
-
return
|
38
25
|
# import blkinfo only on linux
|
39
26
|
from blkinfo import BlkDiskInfo
|
40
27
|
|
@@ -101,7 +88,7 @@ def pumount(disk: UF2Disk):
|
|
101
88
|
log.warning(f"{disk.label} already dismounted")
|
102
89
|
|
103
90
|
|
104
|
-
def
|
91
|
+
def dismount_uf2_linux():
|
105
92
|
global glb_dismount_me
|
106
93
|
for disk in glb_dismount_me:
|
107
94
|
pumount(disk)
|
@@ -113,7 +100,9 @@ def wait_for_UF2_linux(s_max: int = 10):
|
|
113
100
|
wait = 10
|
114
101
|
uf2_drives = []
|
115
102
|
# while not destination and wait > 0:
|
116
|
-
for _ in track(
|
103
|
+
for _ in track(
|
104
|
+
range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
|
105
|
+
):
|
117
106
|
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
118
107
|
uf2_drives += list(get_uf2_drives())
|
119
108
|
for drive in get_uf2_drives():
|
@@ -0,0 +1,78 @@
|
|
1
|
+
""" Flashing UF2 based MCU on macos"""
|
2
|
+
|
3
|
+
# sourcery skip: snake-case-functions
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
import sys
|
7
|
+
import time
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from loguru import logger as log
|
11
|
+
from rich.progress import track
|
12
|
+
|
13
|
+
from .flash_uf2_boardid import get_board_id
|
14
|
+
from .uf2disk import UF2Disk
|
15
|
+
|
16
|
+
|
17
|
+
def get_uf2_drives():
|
18
|
+
"""
|
19
|
+
Get a list of all the (un)mounted UF2 drives
|
20
|
+
"""
|
21
|
+
if sys.platform != "linux":
|
22
|
+
log.error("pumount only works on Linux")
|
23
|
+
return
|
24
|
+
# import blkinfo only on linux
|
25
|
+
from blkinfo import BlkDiskInfo
|
26
|
+
|
27
|
+
myblkd = BlkDiskInfo()
|
28
|
+
filters = {
|
29
|
+
"tran": "usb",
|
30
|
+
}
|
31
|
+
usb_disks = myblkd.get_disks(filters)
|
32
|
+
for disk in usb_disks:
|
33
|
+
if disk["fstype"] == "vfat":
|
34
|
+
uf2_part = disk
|
35
|
+
# unpartioned usb disk or partition (e.g. /dev/sdb )
|
36
|
+
# SEEED WIO Terminal is unpartioned
|
37
|
+
# print( json.dumps(uf2_part, indent=4))
|
38
|
+
uf2 = UF2Disk()
|
39
|
+
uf2.device_path = "/dev/" + uf2_part["name"]
|
40
|
+
uf2.label = uf2_part["label"]
|
41
|
+
uf2.mountpoint = uf2_part["mountpoint"]
|
42
|
+
yield uf2
|
43
|
+
elif disk["type"] == "disk" and disk.get("children") and len(disk.get("children")) > 0:
|
44
|
+
if disk.get("children")[0]["type"] == "part" and disk.get("children")[0]["fstype"] == "vfat":
|
45
|
+
uf2_part = disk.get("children")[0]
|
46
|
+
# print( json.dumps(uf2_part, indent=4))
|
47
|
+
uf2 = UF2Disk()
|
48
|
+
uf2.device_path = "/dev/" + uf2_part["name"]
|
49
|
+
uf2.label = uf2_part["label"]
|
50
|
+
uf2.mountpoint = uf2_part["mountpoint"]
|
51
|
+
yield uf2
|
52
|
+
|
53
|
+
|
54
|
+
def wait_for_UF2_macos(s_max: int = 10):
|
55
|
+
destination = ""
|
56
|
+
wait = 10
|
57
|
+
uf2_drives = []
|
58
|
+
# while not destination and wait > 0:
|
59
|
+
for _ in track(
|
60
|
+
range(s_max), description="Waiting for mcu to mount as a drive", transient=True, refresh_per_second=2
|
61
|
+
):
|
62
|
+
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
63
|
+
uf2_drives += list(get_uf2_drives())
|
64
|
+
for drive in get_uf2_drives():
|
65
|
+
time.sleep(1)
|
66
|
+
try:
|
67
|
+
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
68
|
+
board_id = get_board_id(Path(drive.mountpoint)) # type: ignore
|
69
|
+
destination = Path(drive.mountpoint)
|
70
|
+
break
|
71
|
+
except PermissionError:
|
72
|
+
log.debug(f"Permission error on {drive.mountpoint}")
|
73
|
+
continue
|
74
|
+
if destination:
|
75
|
+
break
|
76
|
+
time.sleep(1)
|
77
|
+
wait -= 1
|
78
|
+
return destination
|
mpflash/list.py
CHANGED
@@ -1,52 +1,13 @@
|
|
1
1
|
from typing import List
|
2
2
|
|
3
|
-
from rich import
|
4
|
-
from rich.
|
5
|
-
from rich.table import Column, Table
|
3
|
+
from rich.progress import track
|
4
|
+
from rich.table import Table
|
6
5
|
|
7
6
|
from mpflash.mpremoteboard import MPRemoteBoard
|
8
7
|
from mpflash.vendor.versions import clean_version
|
9
8
|
|
10
|
-
from .config import config
|
11
9
|
from .logger import console
|
12
10
|
|
13
|
-
rp_spinner = SpinnerColumn(finished_text="✅")
|
14
|
-
rp_text = TextColumn("{task.description} {task.fields[device]}", table_column=Column())
|
15
|
-
rp_bar = BarColumn(bar_width=None, table_column=Column())
|
16
|
-
|
17
|
-
|
18
|
-
def list_mcus(bluetooth: bool = False):
|
19
|
-
"""
|
20
|
-
Retrieves information about connected microcontroller boards.
|
21
|
-
|
22
|
-
Returns:
|
23
|
-
List[MPRemoteBoard]: A list of MPRemoteBoard instances with board information.
|
24
|
-
Raises:
|
25
|
-
ConnectionError: If there is an error connecting to a board.
|
26
|
-
"""
|
27
|
-
conn_mcus = [MPRemoteBoard(sp) for sp in MPRemoteBoard.connected_boards(bluetooth) if sp not in config.ignore_ports]
|
28
|
-
|
29
|
-
# a lot of boilerplate to show a progress bar with the comport currently scanned
|
30
|
-
# low update rate to facilitate screen readers/narration
|
31
|
-
with Progress(rp_spinner, rp_text, rp_bar, TimeElapsedColumn(), refresh_per_second=2) as progress:
|
32
|
-
tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
|
33
|
-
progress.tasks[tsk_scan].fields["device"] = "..."
|
34
|
-
progress.tasks[tsk_scan].visible = True
|
35
|
-
progress.start_task(tsk_scan)
|
36
|
-
try:
|
37
|
-
for mcu in conn_mcus:
|
38
|
-
progress.update(tsk_scan, device=mcu.serialport.replace("/dev/", ""))
|
39
|
-
try:
|
40
|
-
mcu.get_mcu_info()
|
41
|
-
except ConnectionError as e:
|
42
|
-
print(f"Error: {e}")
|
43
|
-
continue
|
44
|
-
finally:
|
45
|
-
# transient
|
46
|
-
progress.stop_task(tsk_scan)
|
47
|
-
progress.tasks[tsk_scan].visible = False
|
48
|
-
return conn_mcus
|
49
|
-
|
50
11
|
|
51
12
|
def show_mcus(
|
52
13
|
conn_mcus: List[MPRemoteBoard],
|
@@ -57,11 +18,9 @@ def show_mcus(
|
|
57
18
|
|
58
19
|
|
59
20
|
def abbrv_family(family: str, is_wide: bool) -> str:
|
60
|
-
ABRV = {"micropython": "upy", "circuitpython": "cpy"}
|
61
21
|
if not is_wide:
|
62
|
-
|
63
|
-
|
64
|
-
return family[:4]
|
22
|
+
ABRV = {"micropython": "upy", "circuitpython": "cpy", "unknown": "?"}
|
23
|
+
return ABRV.get(family, family[:4])
|
65
24
|
return family
|
66
25
|
|
67
26
|
|
mpflash/mpboard_id/__init__.py
CHANGED
@@ -4,56 +4,38 @@ that is included in the module.
|
|
4
4
|
|
5
5
|
"""
|
6
6
|
|
7
|
-
import json
|
8
7
|
from functools import lru_cache
|
9
|
-
from
|
10
|
-
from typing import List, Optional, Tuple, TypedDict, Union
|
8
|
+
from typing import List, Optional, Tuple
|
11
9
|
|
12
|
-
from mpflash.common import PORT_FWTYPES
|
13
10
|
from mpflash.errors import MPFlashError
|
11
|
+
from mpflash.mpboard_id.board import Board
|
12
|
+
from mpflash.mpboard_id.store import read_known_boardinfo
|
14
13
|
from mpflash.vendor.versions import clean_version
|
15
14
|
|
16
15
|
# KNOWN ports and boards are sourced from the micropython repo,
|
17
16
|
# this info is stored in the board_info.json file
|
18
17
|
|
19
18
|
|
20
|
-
# Board based on the dataclass Board but changed to TypedDict
|
21
|
-
# - source : get_boardnames.py
|
22
|
-
class Board(TypedDict):
|
23
|
-
"""MicroPython Board definition"""
|
24
|
-
|
25
|
-
description: str
|
26
|
-
port: str
|
27
|
-
board: str
|
28
|
-
board_name: str
|
29
|
-
mcu_name: str
|
30
|
-
path: Union[Path, str]
|
31
|
-
version: str
|
32
|
-
cpu: str
|
33
|
-
|
34
|
-
|
35
|
-
@lru_cache(maxsize=None)
|
36
|
-
def read_known_boardinfo() -> List[Board]:
|
37
|
-
"""Reads the board_info.json file and returns the data as a list of Board objects"""
|
38
|
-
with open(Path(__file__).parent / "board_info.json", "r") as file:
|
39
|
-
return json.load(file)
|
40
|
-
|
41
|
-
|
42
19
|
def get_known_ports() -> List[str]:
|
43
20
|
# TODO: Filter for Version
|
44
21
|
mp_boards = read_known_boardinfo()
|
45
22
|
# select the unique ports from info
|
46
|
-
ports = set({board
|
23
|
+
ports = set({board.port for board in mp_boards if board.port})
|
47
24
|
return sorted(list(ports))
|
48
25
|
|
49
26
|
|
50
|
-
def get_known_boards_for_port(port: str, versions: Optional[List[str]] = None):
|
27
|
+
def get_known_boards_for_port(port: Optional[str] = "", versions: Optional[List[str]] = None) -> List[Board]:
|
51
28
|
"""
|
52
29
|
Returns a list of boards for the given port and version(s)
|
53
30
|
|
54
|
-
port
|
55
|
-
versions
|
31
|
+
port: The Micropython port to filter for
|
32
|
+
versions: Optional, The Micropython versions to filter for (actual versions required)
|
33
|
+
"""
|
56
34
|
mp_boards = read_known_boardinfo()
|
35
|
+
if versions:
|
36
|
+
preview_or_stable = "preview" in versions or "stable" in versions
|
37
|
+
else:
|
38
|
+
preview_or_stable = False
|
57
39
|
|
58
40
|
# filter for 'preview' as they are not in the board_info.json
|
59
41
|
# instead use stable version
|
@@ -65,9 +47,17 @@ def get_known_boards_for_port(port: str, versions: Optional[List[str]] = None):
|
|
65
47
|
# make sure of the v prefix
|
66
48
|
versions = [clean_version(v) for v in versions]
|
67
49
|
# filter for the version(s)
|
68
|
-
mp_boards = [board for board in mp_boards if board
|
50
|
+
mp_boards = [board for board in mp_boards if board.version in versions]
|
51
|
+
if not mp_boards and preview_or_stable:
|
52
|
+
# nothing found - perhaps there is a newer version for which we do not have the board info yet
|
53
|
+
# use the latest known version from the board info
|
54
|
+
mp_boards = read_known_boardinfo()
|
55
|
+
last_known_version = sorted({b.version for b in mp_boards})[-1]
|
56
|
+
mp_boards = [board for board in mp_boards if board.version == last_known_version]
|
57
|
+
|
69
58
|
# filter for the port
|
70
|
-
|
59
|
+
if port:
|
60
|
+
mp_boards = [board for board in mp_boards if board.port == port]
|
71
61
|
return mp_boards
|
72
62
|
|
73
63
|
|
@@ -80,7 +70,7 @@ def known_stored_boards(port: str, versions: Optional[List[str]] = None) -> List
|
|
80
70
|
"""
|
81
71
|
mp_boards = get_known_boards_for_port(port, versions)
|
82
72
|
|
83
|
-
boards = set({(f
|
73
|
+
boards = set({(f"{board.version} {board.description}", board.board_id) for board in mp_boards})
|
84
74
|
return sorted(list(boards))
|
85
75
|
|
86
76
|
|
@@ -89,11 +79,11 @@ def find_known_board(board_id: str) -> Board:
|
|
89
79
|
"""Find the board for the given BOARD_ID or 'board description' and return the board info as a Board object"""
|
90
80
|
info = read_known_boardinfo()
|
91
81
|
for board_info in info:
|
92
|
-
if board_id in (board_info
|
93
|
-
if
|
94
|
-
if " with " in board_info
|
95
|
-
board_info
|
82
|
+
if board_id in (board_info.board_id, board_info.description):
|
83
|
+
if not board_info.cpu:
|
84
|
+
if " with " in board_info.description:
|
85
|
+
board_info.cpu = board_info.description.split(" with ")[-1]
|
96
86
|
else:
|
97
|
-
board_info
|
87
|
+
board_info.cpu = board_info.port
|
98
88
|
return board_info
|
99
89
|
raise MPFlashError(f"Board {board_id} not found")
|
@@ -0,0 +1,255 @@
|
|
1
|
+
"""
|
2
|
+
Collects board name and description information from MicroPython and writes it to JSON and CSV files.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import re
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import List, Optional
|
8
|
+
|
9
|
+
import inquirer
|
10
|
+
import rich
|
11
|
+
import rich.table
|
12
|
+
from rich.console import Console
|
13
|
+
from rich.progress import track
|
14
|
+
|
15
|
+
import mpflash.vendor.basicgit as git
|
16
|
+
from mpflash.logger import log
|
17
|
+
from mpflash.mpboard_id import Board
|
18
|
+
from mpflash.mpboard_id.store import write_boardinfo_json
|
19
|
+
from mpflash.vendor.versions import micropython_versions
|
20
|
+
|
21
|
+
# look for all mpconfigboard.h files and extract the board name
|
22
|
+
# from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
|
23
|
+
# and the #define MICROPY_HW_MCU_NAME "STM32F767xx"
|
24
|
+
RE_H_MICROPY_HW_BOARD_NAME = re.compile(r"#define\s+MICROPY_HW_BOARD_NAME\s+\"(.+)\"")
|
25
|
+
RE_H_MICROPY_HW_MCU_NAME = re.compile(r"#define\s+MICROPY_HW_MCU_NAME\s+\"(.+)\"")
|
26
|
+
# find in the mpconfigboard.cmake files
|
27
|
+
|
28
|
+
RE_CMAKE_MICROPY_HW_BOARD_NAME = re.compile(r"MICROPY_HW_BOARD_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
|
29
|
+
RE_CMAKE_MICROPY_HW_MCU_NAME = re.compile(r"MICROPY_HW_MCU_NAME\s?=\s?\"(?P<variant>[\w\s\S]*)\"")
|
30
|
+
# TODO: normal make files
|
31
|
+
|
32
|
+
|
33
|
+
def boards_from_repo(mpy_path: Path, version: str, family: Optional[str] = None) -> List[Board]:
|
34
|
+
"""Collects board name and decriptions from mpconfigboard.h files.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
mpy_path (Path): The path to the MicroPython repository.
|
38
|
+
version (str): The version of MicroPython.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
List[Board]: A list of Board objects containing the board information.
|
42
|
+
"""
|
43
|
+
if not mpy_path.exists() or not mpy_path.is_dir():
|
44
|
+
raise FileNotFoundError(f"MicroPython path {mpy_path} does not exist.")
|
45
|
+
if not family:
|
46
|
+
family = "micropython"
|
47
|
+
if not version:
|
48
|
+
version = git.get_local_tag() # type: ignore
|
49
|
+
if not version:
|
50
|
+
raise ValueError("No version provided and no local tag found.")
|
51
|
+
|
52
|
+
board_list: List[Board] = []
|
53
|
+
# look in mpconfigboard.h files
|
54
|
+
board_list = boards_from_cmake(mpy_path, version, family)
|
55
|
+
|
56
|
+
# look for variants in the .cmake files
|
57
|
+
board_list.extend(boards_from_headers(mpy_path, version, family))
|
58
|
+
# TODO:? look for variants in the Makefile files
|
59
|
+
|
60
|
+
return board_list
|
61
|
+
|
62
|
+
|
63
|
+
def boards_from_cmake(mpy_path: Path, version: str, family: str):
|
64
|
+
"""Get boards from the mpconfigboard.cmake files to the board_list."""
|
65
|
+
board_list = []
|
66
|
+
for path in mpy_path.glob("ports/**/mpconfigboard.cmake"):
|
67
|
+
board = path.parent.name
|
68
|
+
port = path.parent.parent.parent.name
|
69
|
+
with open(path, "r") as f:
|
70
|
+
board_name = mcu_name = "-"
|
71
|
+
for line in f:
|
72
|
+
line = line.strip()
|
73
|
+
if match := RE_CMAKE_MICROPY_HW_BOARD_NAME.match(line):
|
74
|
+
description = match["variant"]
|
75
|
+
board_list.append(
|
76
|
+
Board(
|
77
|
+
board_id=board,
|
78
|
+
port=port,
|
79
|
+
board_name=board_name,
|
80
|
+
mcu_name=mcu_name,
|
81
|
+
description=description,
|
82
|
+
path=path.relative_to(mpy_path),
|
83
|
+
version=version,
|
84
|
+
family=family,
|
85
|
+
)
|
86
|
+
)
|
87
|
+
elif match := RE_CMAKE_MICROPY_HW_MCU_NAME.match(line):
|
88
|
+
description = match["variant"]
|
89
|
+
board_list.append(
|
90
|
+
Board(
|
91
|
+
board_id=board,
|
92
|
+
port=port,
|
93
|
+
board_name=board_name,
|
94
|
+
mcu_name=mcu_name,
|
95
|
+
description=description,
|
96
|
+
path=path.relative_to(mpy_path),
|
97
|
+
version=version,
|
98
|
+
family=family,
|
99
|
+
)
|
100
|
+
)
|
101
|
+
return board_list
|
102
|
+
|
103
|
+
|
104
|
+
def boards_from_headers(mpy_path: Path, version: str, family: str):
|
105
|
+
"""Get boards from the mpconfigboard.h files to the board_list."""
|
106
|
+
board_list = []
|
107
|
+
for path in mpy_path.glob("ports/**/mpconfigboard.h"):
|
108
|
+
board = path.parent.name
|
109
|
+
port = path.parent.parent.parent.name
|
110
|
+
with open(path, "r") as f:
|
111
|
+
board_name = mcu_name = "-"
|
112
|
+
found = 0
|
113
|
+
for line in f:
|
114
|
+
if match := RE_H_MICROPY_HW_BOARD_NAME.match(line):
|
115
|
+
board_name = match[1]
|
116
|
+
found += 1
|
117
|
+
elif match := RE_H_MICROPY_HW_MCU_NAME.match(line):
|
118
|
+
mcu_name = match[1]
|
119
|
+
found += 1
|
120
|
+
if found == 2:
|
121
|
+
description = f"{board_name} with {mcu_name}" if mcu_name != "-" else board_name
|
122
|
+
board_list.append(
|
123
|
+
Board(
|
124
|
+
board_id=board,
|
125
|
+
port=port,
|
126
|
+
board_name=board_name,
|
127
|
+
mcu_name=mcu_name,
|
128
|
+
description=description,
|
129
|
+
path=path.relative_to(mpy_path),
|
130
|
+
version=version,
|
131
|
+
family=family,
|
132
|
+
)
|
133
|
+
)
|
134
|
+
found = 0
|
135
|
+
if found == 1:
|
136
|
+
description = board_name
|
137
|
+
board_list.append(
|
138
|
+
Board(
|
139
|
+
board_id=board,
|
140
|
+
port=port,
|
141
|
+
board_name=board_name,
|
142
|
+
mcu_name=mcu_name,
|
143
|
+
description=description,
|
144
|
+
path=path.relative_to(mpy_path),
|
145
|
+
version=version,
|
146
|
+
family=family,
|
147
|
+
)
|
148
|
+
)
|
149
|
+
return board_list
|
150
|
+
|
151
|
+
|
152
|
+
def boards_for_versions(versions: List[str], mpy_path: Path):
|
153
|
+
"""Gets the list of boards for multiple versions of MicroPython.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
versions (List[str]): The list of MicroPython versions.
|
157
|
+
mpy_path (Path): The path to the MicroPython repository.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
List[Board]: The list of Board objects.
|
161
|
+
"""
|
162
|
+
board_list: List[Board] = []
|
163
|
+
for version in track(versions, description="Searching MicroPython versions"):
|
164
|
+
if git.checkout_tag(tag=version, repo=mpy_path):
|
165
|
+
new_ones = boards_from_repo(mpy_path, version, family="micropython")
|
166
|
+
print(f"Found {len(new_ones)} board definitions for {version}.")
|
167
|
+
board_list += new_ones
|
168
|
+
else:
|
169
|
+
print(f"Could not checkout version {version}.")
|
170
|
+
|
171
|
+
# sort the board_list by description and board
|
172
|
+
print("Total number of boards found:", len(board_list))
|
173
|
+
|
174
|
+
board_list = unique_boards(board_list)
|
175
|
+
print("Unique board descriptions found:", len(board_list))
|
176
|
+
return board_list
|
177
|
+
|
178
|
+
|
179
|
+
def unique_boards(board_list: List[Board], *, key_version: bool = True):
|
180
|
+
"""Remove duplicate boards by 'BOARD_ID description' from the list."""
|
181
|
+
seen = set()
|
182
|
+
result = []
|
183
|
+
for x in board_list:
|
184
|
+
if key_version:
|
185
|
+
key = f"{x.board_id}|{x.version}|{x.description}"
|
186
|
+
else:
|
187
|
+
key = f"{x.board_id}|{x.description}"
|
188
|
+
if key not in seen:
|
189
|
+
result.append(x)
|
190
|
+
seen.add(key)
|
191
|
+
result.sort(key=lambda x: x.description.lower())
|
192
|
+
return result
|
193
|
+
|
194
|
+
|
195
|
+
def make_table(board_list: List[Board]) -> rich.table.Table:
|
196
|
+
"""Creates a rich table with board information."""
|
197
|
+
is_wide = True
|
198
|
+
|
199
|
+
table = rich.table.Table(title="MicroPython Board Information")
|
200
|
+
table.add_column("BOARD_ID", justify="left", style="green")
|
201
|
+
table.add_column("Description", justify="left", style="cyan")
|
202
|
+
table.add_column("Port", justify="left", style="magenta")
|
203
|
+
table.add_column("Board Name", justify="left", style="blue")
|
204
|
+
if is_wide:
|
205
|
+
table.add_column("MCU Name", justify="left", style="blue")
|
206
|
+
table.add_column("Detection", justify="left", style="yellow")
|
207
|
+
table.add_column("Version", justify="left", style="blue")
|
208
|
+
if is_wide:
|
209
|
+
table.add_column("Family", justify="left", style="blue")
|
210
|
+
|
211
|
+
for board in board_list:
|
212
|
+
row = [board.board_id, board.description, *(board.port, board.board_name)]
|
213
|
+
if is_wide:
|
214
|
+
row.append(board.mcu_name)
|
215
|
+
row.extend((str(Path(board.path).suffix), board.version))
|
216
|
+
if is_wide:
|
217
|
+
row.append(board.family)
|
218
|
+
table.add_row(*row)
|
219
|
+
|
220
|
+
return table
|
221
|
+
|
222
|
+
|
223
|
+
def ask_mpy_path():
|
224
|
+
"""Ask the user for the path to the MicroPython repository."""
|
225
|
+
questions = [
|
226
|
+
inquirer.Text(
|
227
|
+
"mpy_path", message="Enter the path to the MicroPython repository", default=".\\repos\\micropython"
|
228
|
+
)
|
229
|
+
]
|
230
|
+
if answers := inquirer.prompt(questions):
|
231
|
+
return Path(answers["mpy_path"])
|
232
|
+
else:
|
233
|
+
raise ValueError("No path provided.")
|
234
|
+
|
235
|
+
|
236
|
+
def main():
|
237
|
+
"""Main function to collect and write board information."""
|
238
|
+
|
239
|
+
console = Console()
|
240
|
+
|
241
|
+
mpy_path = ask_mpy_path()
|
242
|
+
versions = micropython_versions(minver="v1.10") + ["master"]
|
243
|
+
board_list = boards_for_versions(versions, mpy_path)
|
244
|
+
|
245
|
+
here = Path(__file__).parent
|
246
|
+
log.info(write_boardinfo_json(board_list, folder=here))
|
247
|
+
# write_files(board_list, folder=CONFIG.board_path)
|
248
|
+
|
249
|
+
# table of when the board was added
|
250
|
+
table = make_table(unique_boards(board_list, key_version=False))
|
251
|
+
console.print(table)
|
252
|
+
|
253
|
+
|
254
|
+
if __name__ == "__main__":
|
255
|
+
main()
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
|
6
|
+
# - source : get_boardnames.py
|
7
|
+
@dataclass
|
8
|
+
class Board:
|
9
|
+
"""
|
10
|
+
MicroPython Board definitions, parsed from the make and header files
|
11
|
+
"""
|
12
|
+
|
13
|
+
port: str # micropython port
|
14
|
+
board_id: str # BOARD_ID (Foldername) as used in the make files
|
15
|
+
board_name: str # Short board description
|
16
|
+
description: str # Long board description
|
17
|
+
path: Union[Path, str]
|
18
|
+
version: str = field(default="") # version of MicroPython""
|
19
|
+
# versions: List[str] = field(default=[]) # version of MicroPython""
|
20
|
+
family: str = field(default="micropython")
|
21
|
+
mcu_name: str = field(default="")
|
22
|
+
cpu: str = field(default="")
|
23
|
+
# TODO: add variant
|
24
|
+
|
25
|
+
def __post_init__(self):
|
26
|
+
if not self.cpu:
|
27
|
+
if " with " in self.description:
|
28
|
+
self.cpu = self.description.split(" with ")[-1]
|
29
|
+
else:
|
30
|
+
self.cpu = self.port
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def from_dict(data: dict) -> "Board":
|
34
|
+
return Board(**data)
|
35
|
+
|
36
|
+
def to_dict(self) -> dict:
|
37
|
+
return self.__dict__
|