mpflash 0.8.8__py3-none-any.whl → 0.9.1__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 +1 -1
- mpflash/ask_input.py +1 -1
- mpflash/bootloader/__init__.py +2 -37
- mpflash/bootloader/activate.py +60 -0
- mpflash/bootloader/detect.py +82 -0
- mpflash/bootloader/manual.py +10 -11
- mpflash/bootloader/micropython.py +2 -0
- mpflash/bootloader/touch1200.py +13 -22
- mpflash/cli_download.py +1 -1
- mpflash/cli_flash.py +3 -3
- mpflash/cli_group.py +18 -5
- mpflash/cli_main.py +3 -5
- mpflash/common.py +1 -0
- mpflash/config.py +6 -9
- mpflash/connected.py +9 -5
- mpflash/download.py +9 -5
- mpflash/downloaded.py +1 -1
- mpflash/{flash.py → flash/__init__.py} +20 -17
- mpflash/{flash_esp.py → flash/esp.py} +1 -1
- mpflash/flash/stm32.py +19 -0
- mpflash/{flash_stm32_dfu.py → flash/stm32_dfu.py} +1 -1
- mpflash/{flash_uf2.py → flash/uf2/__init__.py} +44 -23
- mpflash/{flash_uf2_linux.py → flash/uf2/linux.py} +15 -8
- mpflash/{flash_uf2_macos.py → flash/uf2/macos.py} +13 -5
- mpflash/{flash_uf2_windows.py → flash/uf2/windows.py} +16 -7
- mpflash/{worklist.py → flash/worklist.py} +7 -5
- mpflash/list.py +13 -3
- mpflash/mpboard_id/__init__.py +1 -1
- mpflash/mpboard_id/add_boards.py +2 -2
- mpflash/mpboard_id/board_id.py +1 -1
- mpflash/mpremoteboard/__init__.py +5 -4
- mpflash/{vendor/versions.py → versions.py} +10 -5
- {mpflash-0.8.8.dist-info → mpflash-0.9.1.dist-info}/METADATA +16 -5
- mpflash-0.9.1.dist-info/RECORD +52 -0
- mpflash/flash_stm32.py +0 -23
- mpflash/flash_stm32_cube.py +0 -111
- mpflash/vendor/basicgit.py +0 -288
- mpflash-0.8.8.dist-info/RECORD +0 -52
- /mpflash/{flash_uf2_boardid.py → flash/uf2/boardid.py} +0 -0
- /mpflash/{uf2disk.py → flash/uf2/uf2disk.py} +0 -0
- {mpflash-0.8.8.dist-info → mpflash-0.9.1.dist-info}/LICENSE +0 -0
- {mpflash-0.8.8.dist-info → mpflash-0.9.1.dist-info}/WHEEL +0 -0
- {mpflash-0.8.8.dist-info → mpflash-0.9.1.dist-info}/entry_points.txt +0 -0
@@ -4,20 +4,21 @@ Flash SAMD and RP2 via UF2
|
|
4
4
|
|
5
5
|
import shutil
|
6
6
|
import sys
|
7
|
-
import time
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import Optional
|
10
9
|
|
10
|
+
import tenacity
|
11
11
|
from loguru import logger as log
|
12
|
-
|
12
|
+
|
13
|
+
from tenacity import stop_after_attempt, wait_fixed
|
13
14
|
|
14
15
|
from mpflash.mpremoteboard import MPRemoteBoard
|
15
16
|
|
16
|
-
from .common import PORT_FWTYPES
|
17
|
-
from .
|
18
|
-
from .
|
19
|
-
from .
|
20
|
-
from .
|
17
|
+
from mpflash.common import PORT_FWTYPES
|
18
|
+
from .boardid import get_board_id
|
19
|
+
from .linux import dismount_uf2_linux, wait_for_UF2_linux
|
20
|
+
from .macos import wait_for_UF2_macos
|
21
|
+
from .windows import wait_for_UF2_windows
|
21
22
|
|
22
23
|
|
23
24
|
def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemoteBoard]:
|
@@ -37,18 +38,9 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
37
38
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
38
39
|
return None
|
39
40
|
if erase:
|
40
|
-
log.
|
41
|
+
log.warning("Erase not (yet) supported on .UF2, skipping erase.")
|
41
42
|
|
42
|
-
|
43
|
-
destination = wait_for_UF2_linux()
|
44
|
-
elif sys.platform == "win32":
|
45
|
-
destination = wait_for_UF2_windows()
|
46
|
-
elif sys.platform == "darwin":
|
47
|
-
log.warning(f"OS {sys.platform} not tested/supported")
|
48
|
-
destination = wait_for_UF2_macos()
|
49
|
-
else:
|
50
|
-
log.warning(f"OS {sys.platform} not tested/supported")
|
51
|
-
return None
|
43
|
+
destination = waitfor_uf2(board_id=mcu.port.upper())
|
52
44
|
|
53
45
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
54
46
|
log.error("Board is not in bootloader mode")
|
@@ -57,11 +49,40 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
57
49
|
log.info("Board is in bootloader mode")
|
58
50
|
board_id = get_board_id(destination) # type: ignore
|
59
51
|
log.info(f"Board ID: {board_id}")
|
60
|
-
|
61
|
-
|
62
|
-
|
52
|
+
try:
|
53
|
+
copy_firmware_to_uf2(fw_file, destination)
|
54
|
+
log.success("Done copying, resetting the board.")
|
55
|
+
except tenacity.RetryError:
|
56
|
+
log.error("Failed to copy the firmware file to the board.")
|
57
|
+
return None
|
58
|
+
|
63
59
|
if sys.platform in ["linux"]:
|
64
60
|
dismount_uf2_linux()
|
65
|
-
|
66
|
-
|
61
|
+
|
62
|
+
mcu.wait_for_restart()
|
67
63
|
return mcu
|
64
|
+
|
65
|
+
|
66
|
+
def waitfor_uf2(board_id: str):
|
67
|
+
"""
|
68
|
+
Wait for the UF2 drive to mount
|
69
|
+
"""
|
70
|
+
if sys.platform == "linux":
|
71
|
+
return wait_for_UF2_linux(board_id=board_id)
|
72
|
+
elif sys.platform == "win32":
|
73
|
+
return wait_for_UF2_windows(board_id=board_id)
|
74
|
+
elif sys.platform == "darwin":
|
75
|
+
return wait_for_UF2_macos(board_id=board_id)
|
76
|
+
else:
|
77
|
+
log.warning(f"OS {sys.platform} not tested/supported")
|
78
|
+
return None
|
79
|
+
|
80
|
+
|
81
|
+
@tenacity.retry(stop=stop_after_attempt(3), wait=wait_fixed(1), reraise=False)
|
82
|
+
def copy_firmware_to_uf2(fw_file: Path, destination: Path):
|
83
|
+
"""
|
84
|
+
Copy the firmware file to the destination,
|
85
|
+
Retry 3 times with 1s delay
|
86
|
+
"""
|
87
|
+
log.info(f"Copying {fw_file} to {destination}.")
|
88
|
+
return shutil.copy(fw_file, destination)
|
@@ -12,7 +12,7 @@ from typing import List
|
|
12
12
|
from loguru import logger as log
|
13
13
|
from rich.progress import track
|
14
14
|
|
15
|
-
from .
|
15
|
+
from .boardid import get_board_id
|
16
16
|
from .uf2disk import UF2Disk
|
17
17
|
|
18
18
|
glb_dismount_me: List[UF2Disk] = []
|
@@ -70,7 +70,7 @@ def pmount(disk: UF2Disk):
|
|
70
70
|
log.debug(f"Mounted {disk.label} at {disk.mountpoint}")
|
71
71
|
glb_dismount_me.append(disk)
|
72
72
|
else:
|
73
|
-
log.
|
73
|
+
log.trace(f"\n{disk.label} already mounted at {disk.mountpoint}")
|
74
74
|
|
75
75
|
|
76
76
|
def pumount(disk: UF2Disk):
|
@@ -95,24 +95,31 @@ def dismount_uf2_linux():
|
|
95
95
|
glb_dismount_me = []
|
96
96
|
|
97
97
|
|
98
|
-
def wait_for_UF2_linux(s_max: int = 10):
|
98
|
+
def wait_for_UF2_linux(board_id: str, s_max: int = 10):
|
99
99
|
destination = ""
|
100
100
|
wait = 10
|
101
101
|
uf2_drives = []
|
102
102
|
# while not destination and wait > 0:
|
103
103
|
for _ in track(
|
104
|
-
range(s_max),
|
104
|
+
range(s_max),
|
105
|
+
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
106
|
+
transient=True,
|
107
|
+
show_speed=False,
|
108
|
+
refresh_per_second=1,
|
109
|
+
total=s_max,
|
105
110
|
):
|
106
|
-
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
107
111
|
uf2_drives += list(get_uf2_drives())
|
108
112
|
for drive in get_uf2_drives():
|
109
113
|
pmount(drive)
|
110
114
|
time.sleep(1)
|
111
115
|
try:
|
112
116
|
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
this_board_id = get_board_id(Path(drive.mountpoint))
|
118
|
+
if not board_id or board_id.upper() in this_board_id.upper():
|
119
|
+
# is it the correct board?
|
120
|
+
destination = Path(drive.mountpoint)
|
121
|
+
break
|
122
|
+
continue
|
116
123
|
except PermissionError:
|
117
124
|
log.debug(f"Permission error on {drive.mountpoint}")
|
118
125
|
continue
|
@@ -9,23 +9,31 @@ from typing import Optional
|
|
9
9
|
|
10
10
|
from rich.progress import track
|
11
11
|
|
12
|
+
from .boardid import get_board_id
|
12
13
|
|
13
14
|
|
14
|
-
def wait_for_UF2_macos(s_max: int = 10) -> Optional[Path]:
|
15
|
+
def wait_for_UF2_macos(board_id: str, s_max: int = 10) -> Optional[Path]:
|
15
16
|
"""Wait for the MCU to mount as a drive"""
|
16
17
|
if s_max < 1:
|
17
18
|
s_max = 10
|
18
19
|
destination = None
|
19
20
|
for _ in track(
|
20
|
-
range(s_max),
|
21
|
+
range(s_max),
|
22
|
+
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
23
|
+
transient=True,
|
24
|
+
show_speed=False,
|
25
|
+
refresh_per_second=1,
|
26
|
+
total=s_max,
|
21
27
|
):
|
22
|
-
# log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
|
23
28
|
vol_mounts = Path("/Volumes").iterdir()
|
24
29
|
for vol in vol_mounts:
|
25
30
|
try:
|
26
31
|
if Path(vol, "INFO_UF2.TXT").exists():
|
27
|
-
|
28
|
-
|
32
|
+
this_board_id = get_board_id(Path(vol))
|
33
|
+
if not board_id or board_id.upper() in this_board_id.upper():
|
34
|
+
destination = Path(vol)
|
35
|
+
break
|
36
|
+
continue
|
29
37
|
except OSError:
|
30
38
|
pass
|
31
39
|
if destination:
|
@@ -7,25 +7,34 @@ import time
|
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import Optional
|
9
9
|
|
10
|
+
from .boardid import get_board_id
|
10
11
|
import psutil
|
11
12
|
from rich.progress import track
|
12
13
|
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
def wait_for_UF2_windows(s_max: int = 10) -> Optional[Path]:
|
15
|
+
def wait_for_UF2_windows(board_id: str, s_max: int = 10)-> Optional[Path]:
|
17
16
|
"""Wait for the MCU to mount as a drive"""
|
18
17
|
if s_max < 1:
|
19
18
|
s_max = 10
|
20
19
|
destination = None
|
21
|
-
for _ in track(
|
22
|
-
|
20
|
+
for _ in track(
|
21
|
+
range(s_max),
|
22
|
+
description=f"Waiting for mcu to mount as a drive ({s_max}s)",
|
23
|
+
transient=True,
|
24
|
+
show_speed=False,
|
25
|
+
refresh_per_second=1,
|
26
|
+
total=s_max,
|
27
|
+
):
|
23
28
|
drives = [drive.device for drive in psutil.disk_partitions()]
|
24
29
|
for drive in drives:
|
25
30
|
try:
|
26
31
|
if Path(drive, "INFO_UF2.TXT").exists():
|
27
|
-
|
28
|
-
|
32
|
+
this_board_id = get_board_id(Path(drive))
|
33
|
+
if not board_id or board_id.upper() in this_board_id.upper():
|
34
|
+
# is it the correct board?
|
35
|
+
destination = Path(drive)
|
36
|
+
break
|
37
|
+
continue
|
29
38
|
except OSError:
|
30
39
|
pass
|
31
40
|
if destination:
|
@@ -1,15 +1,17 @@
|
|
1
|
+
"""Worklist for updating boards"""
|
2
|
+
|
1
3
|
from pathlib import Path
|
2
4
|
from typing import Dict, List, Optional, Tuple
|
3
5
|
|
4
6
|
from loguru import logger as log
|
5
7
|
|
6
|
-
from mpflash.common import FWInfo, filtered_comports
|
8
|
+
from mpflash.common import FWInfo, filtered_comports
|
7
9
|
from mpflash.errors import MPFlashError
|
8
10
|
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from
|
11
|
+
from ..downloaded import find_downloaded_firmware
|
12
|
+
from ..list import show_mcus
|
13
|
+
from ..mpboard_id import find_known_board
|
14
|
+
from ..mpremoteboard import MPRemoteBoard
|
13
15
|
|
14
16
|
# #########################################################################################################
|
15
17
|
WorkList = List[Tuple[MPRemoteBoard, FWInfo]]
|
mpflash/list.py
CHANGED
@@ -3,8 +3,9 @@ from typing import List
|
|
3
3
|
from rich.progress import track
|
4
4
|
from rich.table import Table
|
5
5
|
|
6
|
+
from mpflash.config import config
|
6
7
|
from mpflash.mpremoteboard import MPRemoteBoard
|
7
|
-
from mpflash.
|
8
|
+
from mpflash.versions import clean_version
|
8
9
|
|
9
10
|
from .logger import console
|
10
11
|
|
@@ -64,8 +65,15 @@ def mcu_table(
|
|
64
65
|
table.add_column("Version", overflow="fold", min_width=5, max_width=16)
|
65
66
|
if needs_build:
|
66
67
|
table.add_column("Build" if is_wide else "Bld", justify="right")
|
67
|
-
|
68
|
-
|
68
|
+
if config.usb:
|
69
|
+
table.add_column("Location", overflow="fold", max_width=40)
|
70
|
+
for mcu in track(
|
71
|
+
conn_mcus,
|
72
|
+
description="Updating board info",
|
73
|
+
transient=True,
|
74
|
+
show_speed=False,
|
75
|
+
refresh_per_second=1,
|
76
|
+
):
|
69
77
|
if refresh:
|
70
78
|
try:
|
71
79
|
mcu.get_mcu_info()
|
@@ -84,6 +92,8 @@ def mcu_table(
|
|
84
92
|
row.append(clean_version(mcu.version))
|
85
93
|
if needs_build:
|
86
94
|
row.append(mcu.build)
|
95
|
+
if config.usb:
|
96
|
+
row.append(mcu.location)
|
87
97
|
|
88
98
|
table.add_row(*row)
|
89
99
|
return table
|
mpflash/mpboard_id/__init__.py
CHANGED
@@ -10,7 +10,7 @@ from typing import List, Optional, Tuple
|
|
10
10
|
from mpflash.errors import MPFlashError
|
11
11
|
from mpflash.mpboard_id.board import Board
|
12
12
|
from mpflash.mpboard_id.store import read_known_boardinfo
|
13
|
-
from mpflash.
|
13
|
+
from mpflash.versions import clean_version
|
14
14
|
|
15
15
|
# KNOWN ports and boards are sourced from the micropython repo,
|
16
16
|
# this info is stored in the board_info.json file
|
mpflash/mpboard_id/add_boards.py
CHANGED
@@ -12,11 +12,11 @@ import rich.table
|
|
12
12
|
from rich.console import Console
|
13
13
|
from rich.progress import track
|
14
14
|
|
15
|
-
import
|
15
|
+
import basicgit as git
|
16
16
|
from mpflash.logger import log
|
17
17
|
from mpflash.mpboard_id import Board
|
18
18
|
from mpflash.mpboard_id.store import write_boardinfo_json
|
19
|
-
from mpflash.
|
19
|
+
from mpflash.versions import micropython_versions
|
20
20
|
|
21
21
|
# look for all mpconfigboard.h files and extract the board name
|
22
22
|
# from the #define MICROPY_HW_BOARD_NAME "PYBD_SF6"
|
mpflash/mpboard_id/board_id.py
CHANGED
@@ -9,7 +9,7 @@ from typing import Optional
|
|
9
9
|
from mpflash.errors import MPFlashError
|
10
10
|
from mpflash.logger import log
|
11
11
|
from mpflash.mpboard_id.store import read_known_boardinfo
|
12
|
-
from mpflash.
|
12
|
+
from mpflash.versions import clean_version, get_stable_mp_version
|
13
13
|
|
14
14
|
|
15
15
|
def find_board_id_by_description(
|
@@ -29,7 +29,7 @@ RETRIES = 3
|
|
29
29
|
class MPRemoteBoard:
|
30
30
|
"""Class to run mpremote commands"""
|
31
31
|
|
32
|
-
def __init__(self, serialport: str = "", update: bool = False):
|
32
|
+
def __init__(self, serialport: str = "", update: bool = False, *, location: str = ""):
|
33
33
|
"""
|
34
34
|
Initialize MPRemoteBoard object.
|
35
35
|
|
@@ -37,7 +37,7 @@ class MPRemoteBoard:
|
|
37
37
|
- serialport (str): The serial port to connect to. Default is an empty string.
|
38
38
|
- update (bool): Whether to update the MCU information. Default is False.
|
39
39
|
"""
|
40
|
-
self.serialport = serialport
|
40
|
+
self.serialport: str = serialport
|
41
41
|
self.firmware = {}
|
42
42
|
|
43
43
|
self.connected = False
|
@@ -51,6 +51,7 @@ class MPRemoteBoard:
|
|
51
51
|
self.arch = ""
|
52
52
|
self.mpy = ""
|
53
53
|
self.build = ""
|
54
|
+
self.location = location
|
54
55
|
if update:
|
55
56
|
self.get_mcu_info()
|
56
57
|
|
@@ -207,11 +208,11 @@ class MPRemoteBoard:
|
|
207
208
|
"""wait for the board to restart"""
|
208
209
|
for _ in track(
|
209
210
|
range(timeout),
|
210
|
-
description="Waiting for the board to restart",
|
211
|
+
description=f"Waiting for the board to restart ({timeout}s)",
|
211
212
|
transient=True,
|
212
|
-
get_time=lambda: time.time(),
|
213
213
|
show_speed=False,
|
214
214
|
refresh_per_second=1,
|
215
|
+
total=timeout,
|
215
216
|
):
|
216
217
|
time.sleep(1)
|
217
218
|
try:
|
@@ -4,12 +4,12 @@
|
|
4
4
|
#############################################################
|
5
5
|
"""
|
6
6
|
|
7
|
-
from
|
8
|
-
|
7
|
+
from cache_to_disk import cache_to_disk, NoCacheCondition
|
9
8
|
from loguru import logger as log
|
10
9
|
from packaging.version import parse
|
11
10
|
|
12
11
|
from mpflash.common import GH_CLIENT
|
12
|
+
|
13
13
|
OLDEST_VERSION = "1.16"
|
14
14
|
"This is the oldest MicroPython version to build the stubs on"
|
15
15
|
|
@@ -70,9 +70,10 @@ def clean_version(
|
|
70
70
|
return version
|
71
71
|
|
72
72
|
|
73
|
-
@
|
73
|
+
@cache_to_disk(n_days_to_cache=1)
|
74
74
|
def micropython_versions(minver: str = "v1.20", reverse: bool = False):
|
75
75
|
"""Get the list of micropython versions from github tags"""
|
76
|
+
cache_it = True
|
76
77
|
try:
|
77
78
|
gh_client = GH_CLIENT
|
78
79
|
repo = gh_client.get_repo("micropython/micropython")
|
@@ -100,10 +101,15 @@ def micropython_versions(minver: str = "v1.20", reverse: bool = False):
|
|
100
101
|
"v1.11",
|
101
102
|
"v1.10",
|
102
103
|
]
|
104
|
+
cache_it = False
|
103
105
|
versions = [v for v in versions if parse(v) >= parse(minver)]
|
104
106
|
# remove all but the most recent (preview) version
|
105
107
|
versions = versions[:1] + [v for v in versions if "preview" not in v]
|
106
|
-
|
108
|
+
versions = sorted(versions, reverse=reverse)
|
109
|
+
if cache_it:
|
110
|
+
return versions
|
111
|
+
# returns - but does not cache
|
112
|
+
raise NoCacheCondition(function_value=versions)
|
107
113
|
|
108
114
|
|
109
115
|
def get_stable_mp_version() -> str:
|
@@ -116,4 +122,3 @@ def get_preview_mp_version() -> str:
|
|
116
122
|
# read the versions from the git tags
|
117
123
|
all_versions = micropython_versions(minver=OLDEST_VERSION)
|
118
124
|
return [v for v in all_versions if v.endswith(V_PREVIEW)][-1]
|
119
|
-
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: mpflash
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.1
|
4
4
|
Summary: Flash and download tool for MicroPython firmwares
|
5
5
|
Home-page: https://github.com/Josverl/micropython-stubber/blob/main/src/mpflash/README.md
|
6
6
|
License: MIT
|
@@ -20,6 +20,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
20
20
|
Requires-Dist: beautifulsoup4 (>=4.12.3,<5.0.0)
|
21
21
|
Requires-Dist: bincopy (>=20.0.0,<21.0.0)
|
22
22
|
Requires-Dist: blkinfo (>=0.2.0,<0.3.0)
|
23
|
+
Requires-Dist: cache-to-disk (>=2.0.0,<3.0.0)
|
23
24
|
Requires-Dist: cachetools (>=5.3.0,<6.0.0)
|
24
25
|
Requires-Dist: esptool (>=4.7.0,<5.0.0)
|
25
26
|
Requires-Dist: inquirer (>=3.2.4,<4.0.0)
|
@@ -52,8 +53,8 @@ This tool was initially created to be used in a CI/CD pipeline to automate the p
|
|
52
53
|
`mpflash` has been tested on:
|
53
54
|
- OS: Windows x64, Linux X64, but not (yet) macOS.
|
54
55
|
- Micropython (hardware) ports:
|
55
|
-
- `rp2`, using `.uf2`, using filecopy
|
56
|
-
- `samd`, using ` .uf2`, using filecopy
|
56
|
+
- `rp2`, using `.uf2`, using filecopy
|
57
|
+
- `samd`, using ` .uf2`, using filecopy
|
57
58
|
- `esp32`, using `.bin`, using esptool,
|
58
59
|
- `esp8266`, using `.bin`, using esptool
|
59
60
|
- `stm32`, using ` .dfu`, using pydfu
|
@@ -66,7 +67,7 @@ Not yet implemented: `nrf`, `cc3200`, `mimxrt`
|
|
66
67
|
3. Flash one or all connected MicroPython boards with a specific firmware or version.
|
67
68
|
|
68
69
|
## Installation
|
69
|
-
To install mpflash, you can use
|
70
|
+
To install mpflash, you can use: `pipx install mpflash` or `pip install mpflash`
|
70
71
|
|
71
72
|
## Basic usage
|
72
73
|
You can use mpflash to perform various operations on your MicroPython boards. Here is an example of basic usage:
|
@@ -110,7 +111,7 @@ To download the MicroPython firmware for some boards, use the following command:
|
|
110
111
|
|
111
112
|
These will try to download the prebuilt MicroPython firmware for the boards from https://micropython.org/download/ and save it in your downloads folder in the `firmware` directory.
|
112
113
|
The stable version (default) is determined based on the most recent published release,
|
113
|
-
other
|
114
|
+
other options are `--version stable`, `--version preview` and `--version x.y.z` to download the latest stable, preview or version x.y.z respectively.
|
114
115
|
|
115
116
|
By default the firmware will be downloaded to your OS's preferred `Downloads/firmware` folder, but you can speciy a different directory using the `--dir` option.
|
116
117
|
|
@@ -133,8 +134,18 @@ After you have downloaded a firmware you can flash the firmware to a board usin
|
|
133
134
|
This will (try to) autodetect the connected boards, and determine the correct firmware to flash to each board.
|
134
135
|
|
135
136
|
- `mpflash flash` will flash the latest stable firmware to all connected boards.
|
137
|
+
If you have a board withouth a running micropython version, you will need to specify the board and the serial port to flash.
|
136
138
|
- `mpflash flash --serial ? --board ?` will prompt to select a specific serial port and board to flash. (the firmware must be dowloaded earlier)
|
137
139
|
|
140
|
+
In order to flash the firmware some boards need to be put in bootloader mode, this is done automatically by mpflash where possible and supported by the boards hardware and current bootloader.
|
141
|
+
The supported `--bootloader` options are:
|
142
|
+
|
143
|
+
- `touch1200` bootloader is activated by connecting to the board at 1200 baud
|
144
|
+
- `mpy` using micropython to enter the bootloader
|
145
|
+
- `manual` manual intervention is needed to enter the bootloader
|
146
|
+
- `none` mpflash assumes the board is ready to flash
|
147
|
+
|
148
|
+
For ESP32 and ESP8266 boards the `esptool` is used to flash the firmware, and this includes activating the bootloader.
|
138
149
|
|
139
150
|
### Flashing all connected boards with the latest stable firmware
|
140
151
|
```bash
|
@@ -0,0 +1,52 @@
|
|
1
|
+
mpflash/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
mpflash/add_firmware.py,sha256=h-Nu9AnXO6ug2CurmgLoUVG1gNy6CA1NjwQl1nUoMZ4,3330
|
3
|
+
mpflash/ask_input.py,sha256=EsPPlPS_wLC8j9f4o68yKIcqex0dV5tRH7k3rP3spG0,8710
|
4
|
+
mpflash/bootloader/__init__.py,sha256=cRIxVRtUc4jZAgtFggRNhEn0BW-9WFpzm5xRy5B4B2U,105
|
5
|
+
mpflash/bootloader/activate.py,sha256=sN2YuIuFGjLBd_PdG6P3N1yLuLyjnsVWDksrTMhzr_M,2301
|
6
|
+
mpflash/bootloader/detect.py,sha256=eXL_JPcowb8g0tpZmj9DQXYbpfZFoSrzq86lScKhkT4,2608
|
7
|
+
mpflash/bootloader/manual.py,sha256=qGInguhYil4v8EkUxPFlxzvs3m__KraPAj3LAKbV1Ts,3069
|
8
|
+
mpflash/bootloader/micropython.py,sha256=0KDrfdZPtK7fLaxh-_4VxSO-u4IUj7qCyNVw9-LQ74M,517
|
9
|
+
mpflash/bootloader/touch1200.py,sha256=tL2H4ERh0-YSdR8_v6aK_pDRPMjMevHWUGq4-TTFcWo,1083
|
10
|
+
mpflash/cli_download.py,sha256=Ip5HViXCVAiG88XgJ3Lg_hhBGIF5DKFTNpL-_gQkdV0,3520
|
11
|
+
mpflash/cli_flash.py,sha256=KFuJRNgOZ62m70I_KqxqLfLE69b4bOCj69MOfIWdZRo,7136
|
12
|
+
mpflash/cli_group.py,sha256=JFBtp4qhakJeQkuupEoObHeSFke8eWIU1HjnOZ-JfH0,2504
|
13
|
+
mpflash/cli_list.py,sha256=AvYq5_Z7l0Z4rkbr23TwFNzstuZsQgopFpdcrTLwUvs,1965
|
14
|
+
mpflash/cli_main.py,sha256=cElgh8Soq3U5TEAvrrD1sj7NDi-Eb-pei3QcMI0tlwE,1094
|
15
|
+
mpflash/common.py,sha256=h4pVuOpopn5oIy5NQdbggtOOktTL_c92cS_oUfPG4v8,5783
|
16
|
+
mpflash/config.py,sha256=h2Oq7kSe40tOnglpo02wkpaPHxHNVqyB3uGxreIu57U,1029
|
17
|
+
mpflash/connected.py,sha256=p6RMix-tFr0pfT9TErh0LD1nm24ApTr3CqVGcrnDHk8,3024
|
18
|
+
mpflash/download.py,sha256=mckR3pdMFQMYBKNmKBEJm9bJnTtQEnkuQ1vWYXcRRv8,13941
|
19
|
+
mpflash/downloaded.py,sha256=YPN0LDf8DSDvdB9gbVny61bvaJHIDbUs81XUwftTb1c,4808
|
20
|
+
mpflash/errors.py,sha256=6lUhVtECY3syV2bBGKzegGH4pPKXhevvhYQZd74sy6o,238
|
21
|
+
mpflash/flash/__init__.py,sha256=xX2ZJGNX9xpX2s8V7SEJ1It7eOEbl8WkdFzpRd3TLzk,2041
|
22
|
+
mpflash/flash/esp.py,sha256=LcOgjpE4zmPawHNwSd_6WFkaIYYazA__rf3ihGXR7I0,2228
|
23
|
+
mpflash/flash/stm32.py,sha256=W8SJ2_8FGXyMS6cRcZpfOvOM8iOC_cn4vK8xd8tKzI4,555
|
24
|
+
mpflash/flash/stm32_dfu.py,sha256=uTn06H0ZezzCv5O0hvry8ohBtuZXHe_GX2zNUfCxu58,2986
|
25
|
+
mpflash/flash/uf2/__init__.py,sha256=PWEVxzZEca3pfLx_Qy5c4zlHH4W5fAjd0BmDTkS7s6g,2766
|
26
|
+
mpflash/flash/uf2/boardid.py,sha256=2s4K3QiKWK5HKFKWYsDV3hI8alfWSxEOMeurER3eZtM,408
|
27
|
+
mpflash/flash/uf2/linux.py,sha256=4azbcx_YqLZ3RyYNWljejHG_Y6SU-wREL8hhkTYqCjI,4099
|
28
|
+
mpflash/flash/uf2/macos.py,sha256=sncXJsc2FVfm9rvLDjcEu7ZIyrDeHmazHiNQTUaf1Y0,1187
|
29
|
+
mpflash/flash/uf2/uf2disk.py,sha256=dQ8_U6e3qkFOyfXZDpWAsvEBIlMr-ZzLkzTDD8SADqM,286
|
30
|
+
mpflash/flash/uf2/windows.py,sha256=k9Yv71YswPnLx-Z5rf4KjhtVkEWr8SU8EXpeRv87h3A,1290
|
31
|
+
mpflash/flash/worklist.py,sha256=K16sbvzJCgkHNssYlBA3ezayI02RMuIcp19XAs74XeQ,5835
|
32
|
+
mpflash/list.py,sha256=O0tX4BvclmDMnnjMxCN9Zh8hdL6vnuvS9pLNxYLBya8,3112
|
33
|
+
mpflash/logger.py,sha256=BAVrSXMGZLfSDRFbtVBtvb7Rl0sTJxooCgBS5t-6bXo,1057
|
34
|
+
mpflash/mpboard_id/__init__.py,sha256=rQrPCN30GP-lfB2a2deA-lQ6iKvaKPK_xbtBoIavGsM,3716
|
35
|
+
mpflash/mpboard_id/add_boards.py,sha256=0tP-4Ibc5_BBTQx1oBFXmKo80n31BrW6E5KrWKpP2FU,9407
|
36
|
+
mpflash/mpboard_id/board.py,sha256=yQ8IDHQS09AelvTvmPhpmsL4oX3L7IXGqHorkxDOkoE,1114
|
37
|
+
mpflash/mpboard_id/board_id.py,sha256=wzGrxJu_ciOVT7n2861lhoKmPAjh1QjWnAdfcqNUUqc,2871
|
38
|
+
mpflash/mpboard_id/board_info.zip,sha256=F6YowS96DAqjten4ySe4MXgZwPtE-saZOUfY5OQkqKk,19759
|
39
|
+
mpflash/mpboard_id/store.py,sha256=lQQgHSxcaM_ZURcfZNSUv3-ZJjUKMC_xEOOSdpzVvBU,1493
|
40
|
+
mpflash/mpremoteboard/__init__.py,sha256=7uI-5HJgNsQz_EOCW3cRy2xtqXKt9kX51gDSx6HC0Kc,7522
|
41
|
+
mpflash/mpremoteboard/mpy_fw_info.py,sha256=BTupe4rZDTs3UHRqvs429XqWHCchSpwa05yACOiOt5U,4413
|
42
|
+
mpflash/mpremoteboard/runner.py,sha256=YUmo5Y0aOgMaww8CXSdNdgXD-wRKncILuMZ0OB_2qRU,4646
|
43
|
+
mpflash/vendor/click_aliases.py,sha256=K98inhtze8td1dw312kexJS7OX_0ojlptPQ5Z0SHxJY,3065
|
44
|
+
mpflash/vendor/dfu.py,sha256=jGsiD3lbSV1Ar9qJubhoY_hy-L8FI-K55aow8vgwoYQ,5590
|
45
|
+
mpflash/vendor/pydfu.py,sha256=1ObubGsPFrQ7T9M3JRlIPNIG2xx8uYffaEe0Y6bdf_g,19937
|
46
|
+
mpflash/vendor/readme.md,sha256=ZVg7kuUYyXcWcrWkaSJ0CunwebCqu2SiS2sqDadwrT8,84
|
47
|
+
mpflash/versions.py,sha256=SrXVZx6qVWcighHlg8pJ0RPRZ3gcYN7l2DnSicconAk,4098
|
48
|
+
mpflash-0.9.1.dist-info/LICENSE,sha256=xHwgxGNkI0R2iN4KNfbPbQSzRomWyRz7bJnR1O2mln8,1057
|
49
|
+
mpflash-0.9.1.dist-info/METADATA,sha256=81r3-DTSaPrLNwLUdQDjgnFT8WJhC-awFTW1FAC6sXU,16058
|
50
|
+
mpflash-0.9.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
51
|
+
mpflash-0.9.1.dist-info/entry_points.txt,sha256=Jk_visOhYOsZIcSP2Ms9hKqfKy1iorR-6dYltSoWCpY,52
|
52
|
+
mpflash-0.9.1.dist-info/RECORD,,
|
mpflash/flash_stm32.py
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
"""Flash STM32 boards using either STM32CubeProgrammer CLI or dfu-util"""
|
2
|
-
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
from loguru import logger as log
|
6
|
-
|
7
|
-
# from .flash_stm32_cube import flash_stm32_cubecli
|
8
|
-
from .flash_stm32_dfu import dfu_init, flash_stm32_dfu
|
9
|
-
from mpflash.mpremoteboard import MPRemoteBoard
|
10
|
-
|
11
|
-
|
12
|
-
def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool, stm32_dfu: bool = True):
|
13
|
-
# sourcery skip: lift-return-into-if
|
14
|
-
dfu_init()
|
15
|
-
updated = flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase)
|
16
|
-
# if stm32_dfu:
|
17
|
-
# else:
|
18
|
-
# log.info("Using STM32CubeProgrammer CLI")
|
19
|
-
# updated = flash_stm32_cubecli(mcu, fw_file=fw_file, erase=erase)
|
20
|
-
|
21
|
-
mcu.wait_for_restart()
|
22
|
-
log.success(f"Flashed {mcu.version} to {mcu.board}")
|
23
|
-
return updated
|
mpflash/flash_stm32_cube.py
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
# """
|
2
|
-
# Flash STM32 using STM32CubeProgrammer
|
3
|
-
# needs to be installed independenty from https://www.st.com/en/development-tools/stm32cubeprog.html
|
4
|
-
|
5
|
-
# On Linux needs to be run with sudo - unless udev rules are set to allow access to the device as a regular user
|
6
|
-
# """
|
7
|
-
|
8
|
-
# import subprocess
|
9
|
-
# import sys
|
10
|
-
# import time
|
11
|
-
# from pathlib import Path
|
12
|
-
# from typing import Optional
|
13
|
-
|
14
|
-
# import bincopy
|
15
|
-
# from loguru import logger as log
|
16
|
-
# from rich.progress import track
|
17
|
-
# from strip_ansi import strip_ansi
|
18
|
-
|
19
|
-
# from .mpremoteboard.mpremoteboard import MPRemoteBoard
|
20
|
-
|
21
|
-
# STM32_CLI_WIN = "C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe"
|
22
|
-
# STM32_CLI_LINUX = "~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI"
|
23
|
-
|
24
|
-
|
25
|
-
# def get_stm32_start_address(fw_file: Path):
|
26
|
-
# """
|
27
|
-
# Get the start address of the firmware file, to allow automatic restart from that address after flashing
|
28
|
-
# """
|
29
|
-
# try:
|
30
|
-
# fw_hex = bincopy.BinFile(str(fw_file))
|
31
|
-
# return f"0x{fw_hex.execution_start_address:08X}"
|
32
|
-
# except Exception:
|
33
|
-
|
34
|
-
# return ""
|
35
|
-
|
36
|
-
|
37
|
-
# def flash_stm32_cubecli(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optional[MPRemoteBoard]:
|
38
|
-
# """
|
39
|
-
# Flash STM32 devices using STM32CubeProgrammer CLI
|
40
|
-
# - Enter bootloader mode
|
41
|
-
# - wait 2s for the device to be detected
|
42
|
-
# - list the connected DFU devices
|
43
|
-
|
44
|
-
# On Linux: requires udev rules to allow access to the device as a regular user
|
45
|
-
# """
|
46
|
-
# if sys.platform == "linux":
|
47
|
-
# STM32_CLI = Path(STM32_CLI_LINUX).expanduser().as_posix()
|
48
|
-
# elif sys.platform == "win32":
|
49
|
-
# STM32_CLI = str(Path(STM32_CLI_WIN).expanduser())
|
50
|
-
# else:
|
51
|
-
# log.error(f"OS {sys.platform} not supported")
|
52
|
-
# return None
|
53
|
-
|
54
|
-
# if not Path(STM32_CLI).exists():
|
55
|
-
# log.error(
|
56
|
-
# f"STM32CubeProgrammer not found at {STM32_CLI}\nPlease install it from https://www.st.com/en/development-tools/stm32cubeprog.html"
|
57
|
-
# )
|
58
|
-
# return None
|
59
|
-
|
60
|
-
# # run STM32_Programmer_CLI.exe --list
|
61
|
-
# cmd = [
|
62
|
-
# STM32_CLI,
|
63
|
-
# "--list",
|
64
|
-
# ]
|
65
|
-
# results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
66
|
-
# results = [strip_ansi(line) for line in results]
|
67
|
-
# if not any(["Product ID : STM32 BOOTLOADER" in l for l in results]):
|
68
|
-
# log.error("No STM32 BOOTLOADER detected")
|
69
|
-
# return None
|
70
|
-
# echo = False
|
71
|
-
# for line in results:
|
72
|
-
# if line.startswith("===== DFU Interface"):
|
73
|
-
# echo = True
|
74
|
-
# if line.startswith("===== STLink"):
|
75
|
-
# echo = False
|
76
|
-
# if echo:
|
77
|
-
# print(line)
|
78
|
-
# # Try to connect - no action
|
79
|
-
# cmd = [
|
80
|
-
# STM32_CLI,
|
81
|
-
# "--connect",
|
82
|
-
# "port=USB1",
|
83
|
-
# ]
|
84
|
-
# results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
85
|
-
# if erase:
|
86
|
-
# log.info("Erasing flash")
|
87
|
-
# cmd = [
|
88
|
-
# STM32_CLI,
|
89
|
-
# "--connect",
|
90
|
-
# "port=USB1",
|
91
|
-
# "--erase",
|
92
|
-
# "all",
|
93
|
-
# ]
|
94
|
-
# results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
95
|
-
# results = [strip_ansi(line) for line in results]
|
96
|
-
# log.info(f"Flashing {fw_file.name} using STM32CubeProgrammer CLI")
|
97
|
-
# start_address = get_stm32_start_address(fw_file)
|
98
|
-
|
99
|
-
# log.trace(f"STM32_Programmer_CLI --connect port=USB1 --write {str(fw_file)} --go {start_address}")
|
100
|
-
# cmd = [
|
101
|
-
# STM32_CLI,
|
102
|
-
# "--connect",
|
103
|
-
# "port=USB1",
|
104
|
-
# "--write",
|
105
|
-
# str(fw_file),
|
106
|
-
# "--go",
|
107
|
-
# start_address,
|
108
|
-
# ]
|
109
|
-
# results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
110
|
-
# log.success("Done flashing, resetting the board and wait for it to restart")
|
111
|
-
# return mcu
|