micropython-stubber 1.17.5__py3-none-any.whl → 1.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/METADATA +7 -6
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/RECORD +71 -52
- mpflash/README.md +22 -3
- mpflash/libusb_flash.ipynb +203 -0
- mpflash/mpflash/ask_input.py +234 -0
- mpflash/mpflash/cli_download.py +107 -0
- mpflash/mpflash/cli_flash.py +165 -0
- mpflash/mpflash/cli_group.py +41 -8
- mpflash/mpflash/cli_list.py +41 -0
- mpflash/mpflash/cli_main.py +13 -8
- mpflash/mpflash/common.py +33 -122
- mpflash/mpflash/config.py +9 -0
- mpflash/mpflash/{downloader.py → download.py} +112 -120
- mpflash/mpflash/downloaded.py +108 -0
- mpflash/mpflash/errors.py +5 -0
- mpflash/mpflash/flash.py +69 -0
- mpflash/mpflash/flash_esp.py +17 -23
- mpflash/mpflash/flash_stm32.py +16 -113
- mpflash/mpflash/flash_stm32_cube.py +111 -0
- mpflash/mpflash/flash_stm32_dfu.py +101 -0
- mpflash/mpflash/flash_uf2.py +8 -8
- mpflash/mpflash/flash_uf2_linux.py +25 -12
- mpflash/mpflash/flash_uf2_windows.py +24 -12
- mpflash/mpflash/list.py +34 -37
- mpflash/mpflash/logger.py +12 -13
- mpflash/mpflash/mpboard_id/__init__.py +96 -0
- mpflash/mpflash/mpboard_id/board_id.py +63 -0
- mpflash/mpflash/mpboard_id/board_info.csv +2213 -0
- mpflash/mpflash/mpboard_id/board_info.json +19910 -0
- mpflash/mpflash/mpremoteboard/__init__.py +208 -0
- mpflash/mpflash/mpremoteboard/mpy_fw_info.py +141 -0
- {stubber/bulk → mpflash/mpflash/mpremoteboard}/runner.py +22 -5
- mpflash/mpflash/vendor/dfu.py +164 -0
- mpflash/mpflash/vendor/pydfu.py +605 -0
- mpflash/mpflash/vendor/readme.md +3 -0
- mpflash/mpflash/vendor/versions.py +113 -0
- mpflash/mpflash/worklist.py +147 -0
- mpflash/poetry.lock +411 -595
- mpflash/pyproject.toml +24 -8
- mpflash/stm32_udev_rules.md +63 -0
- stubber/__init__.py +1 -1
- stubber/basicgit.py +1 -0
- stubber/board/createstubs.py +10 -4
- stubber/board/createstubs_db.py +11 -5
- stubber/board/createstubs_db_min.py +61 -58
- stubber/board/createstubs_db_mpy.mpy +0 -0
- stubber/board/createstubs_mem.py +11 -5
- stubber/board/createstubs_mem_min.py +56 -53
- stubber/board/createstubs_mem_mpy.mpy +0 -0
- stubber/board/createstubs_min.py +54 -51
- stubber/board/createstubs_mpy.mpy +0 -0
- stubber/bulk/mcu_stubber.py +9 -5
- stubber/codemod/_partials/db_main.py +14 -25
- stubber/codemod/_partials/lvgl_main.py +2 -2
- stubber/codemod/board.py +10 -3
- stubber/commands/clone_cmd.py +7 -7
- stubber/commands/config_cmd.py +3 -0
- stubber/freeze/get_frozen.py +0 -2
- stubber/publish/candidates.py +1 -1
- stubber/publish/package.py +1 -1
- stubber/publish/pathnames.py +1 -1
- stubber/publish/stubpackage.py +1 -0
- stubber/rst/lookup.py +1 -1
- stubber/tools/manifestfile.py +5 -3
- stubber/utils/config.py +26 -36
- stubber/utils/repos.py +2 -2
- stubber/utils/versions.py +1 -0
- mpflash/mpflash/flasher.py +0 -287
- stubber/bulk/board_id.py +0 -40
- stubber/bulk/mpremoteboard.py +0 -141
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/LICENSE +0 -0
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/WHEEL +0 -0
- {micropython_stubber-1.17.5.dist-info → micropython_stubber-1.19.0.dist-info}/entry_points.txt +0 -0
- /mpflash/mpflash/{uf2_boardid.py → flash_uf2_boardid.py} +0 -0
mpflash/mpflash/flash.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
import time
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from loguru import logger as log
|
5
|
+
|
6
|
+
from mpflash.common import PORT_FWTYPES
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
9
|
+
from .flash_esp import flash_esp
|
10
|
+
from .flash_stm32 import flash_stm32
|
11
|
+
from .flash_uf2 import flash_uf2
|
12
|
+
from .worklist import WorkList
|
13
|
+
|
14
|
+
# #########################################################################################################
|
15
|
+
|
16
|
+
|
17
|
+
def flash_list(
|
18
|
+
todo: WorkList,
|
19
|
+
fw_folder: Path,
|
20
|
+
erase: bool,
|
21
|
+
bootloader: bool,
|
22
|
+
):
|
23
|
+
"""Flash a list of boards with the specified firmware."""
|
24
|
+
flashed = []
|
25
|
+
for mcu, fw_info in todo:
|
26
|
+
fw_file = fw_folder / fw_info["filename"] # type: ignore
|
27
|
+
if not fw_file.exists():
|
28
|
+
log.error(f"File {fw_file} does not exist, skipping {mcu.board} on {mcu.serialport}")
|
29
|
+
continue
|
30
|
+
log.info(f"Updating {mcu.board} on {mcu.serialport} to {fw_info['version']}")
|
31
|
+
updated = None
|
32
|
+
# try:
|
33
|
+
if mcu.port in [port for port, exts in PORT_FWTYPES.items() if ".uf2" in exts] and fw_file.suffix == ".uf2":
|
34
|
+
# any .uf2 port ["samd", "rp2", "nrf"]:
|
35
|
+
if bootloader:
|
36
|
+
enter_bootloader(mcu)
|
37
|
+
updated = flash_uf2(mcu, fw_file=fw_file, erase=erase)
|
38
|
+
elif mcu.port in ["stm32"]:
|
39
|
+
if bootloader:
|
40
|
+
enter_bootloader(mcu)
|
41
|
+
updated = flash_stm32(mcu, fw_file, erase=erase)
|
42
|
+
elif mcu.port in ["esp32", "esp8266"]:
|
43
|
+
# bootloader is handled by esptool for esp32/esp8266
|
44
|
+
updated = flash_esp(mcu, fw_file=fw_file, erase=erase)
|
45
|
+
else:
|
46
|
+
log.error(f"Don't (yet) know how to flash {mcu.port}-{mcu.board} on {mcu.serialport}")
|
47
|
+
|
48
|
+
if updated:
|
49
|
+
flashed.append(updated)
|
50
|
+
else:
|
51
|
+
log.error(f"Failed to flash {mcu.board} on {mcu.serialport}")
|
52
|
+
return flashed
|
53
|
+
|
54
|
+
|
55
|
+
def enter_bootloader(mcu: MPRemoteBoard, timeout: int = 10, wait_after: int = 2):
|
56
|
+
"""Enter the bootloader mode for the board"""
|
57
|
+
log.info(f"Entering bootloader on {mcu.board} on {mcu.serialport}")
|
58
|
+
mcu.run_command("bootloader", timeout=timeout)
|
59
|
+
time.sleep(wait_after)
|
60
|
+
|
61
|
+
|
62
|
+
# TODO:
|
63
|
+
# flash from some sort of queue to allow different images to be flashed to the same board
|
64
|
+
# - flash variant 1
|
65
|
+
# - stub variant 1
|
66
|
+
# - flash variant 2
|
67
|
+
# - stub variant 2
|
68
|
+
#
|
69
|
+
# JIT download / download any missing firmwares based on the detected boards
|
mpflash/mpflash/flash_esp.py
CHANGED
@@ -4,47 +4,42 @@
|
|
4
4
|
# #########################################################################################################
|
5
5
|
"""
|
6
6
|
|
7
|
-
import time
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import List, Optional
|
10
9
|
|
11
10
|
import esptool
|
12
11
|
from loguru import logger as log
|
13
12
|
|
14
|
-
from
|
13
|
+
from mpflash.mpboard_id import find_stored_board
|
14
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
15
15
|
|
16
16
|
|
17
|
-
def flash_esp(
|
18
|
-
mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True
|
19
|
-
) -> Optional[MPRemoteBoard]:
|
17
|
+
def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optional[MPRemoteBoard]:
|
20
18
|
if mcu.port not in ["esp32", "esp8266"] or mcu.board in ["ARDUINO_NANO_ESP32"]:
|
21
|
-
log.error(
|
22
|
-
f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}"
|
23
|
-
)
|
19
|
+
log.error(f"esptool not supported for {mcu.port} {mcu.board} on {mcu.serialport}")
|
24
20
|
return None
|
25
21
|
|
26
22
|
log.info(f"Flashing {fw_file} on {mcu.board} on {mcu.serialport}")
|
27
|
-
if mcu.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# baud_rate = str(115_200)
|
23
|
+
if not mcu.cpu:
|
24
|
+
# Lookup CPU based on the board name
|
25
|
+
mcu.cpu = find_stored_board(mcu.board)["cpu"]
|
26
|
+
|
32
27
|
cmds: List[List[str]] = []
|
33
28
|
if erase:
|
34
|
-
cmds.append(
|
35
|
-
f"esptool --chip {mcu.cpu} --port {mcu.serialport} erase_flash".split()
|
36
|
-
)
|
29
|
+
cmds.append(f"esptool --chip {mcu.cpu} --port {mcu.serialport} erase_flash".split())
|
37
30
|
|
38
|
-
if mcu.cpu.upper() in ("ESP32", "ESP32S2"):
|
39
|
-
start_addr = "0x1000"
|
40
|
-
elif mcu.cpu.upper() in ("ESP32S3", "ESP32C3"):
|
41
|
-
start_addr = "0x0"
|
42
31
|
if mcu.cpu.upper().startswith("ESP32"):
|
32
|
+
baud_rate = str(921_600)
|
33
|
+
if mcu.cpu.upper() in ("ESP32", "ESP32S2"):
|
34
|
+
start_addr = "0x1000"
|
35
|
+
elif mcu.cpu.upper() in ("ESP32S3", "ESP32C3"):
|
36
|
+
start_addr = "0x0"
|
43
37
|
cmds.append(
|
44
38
|
f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --compress {start_addr}".split()
|
45
39
|
+ [str(fw_file)]
|
46
40
|
)
|
47
41
|
elif mcu.cpu.upper() == "ESP8266":
|
42
|
+
baud_rate = str(460_800)
|
48
43
|
start_addr = "0x0"
|
49
44
|
cmds.append(
|
50
45
|
f"esptool --chip {mcu.cpu} --port {mcu.serialport} -b {baud_rate} write_flash --flash_size=detect {start_addr}".split()
|
@@ -59,7 +54,6 @@ def flash_esp(
|
|
59
54
|
return None
|
60
55
|
|
61
56
|
log.info("Done flashing, resetting the board and wait for it to restart")
|
62
|
-
|
63
|
-
mcu.
|
64
|
-
log.success(f"Flashed {mcu.version} to {mcu.board} on {mcu.serialport} done")
|
57
|
+
mcu.wait_for_restart()
|
58
|
+
log.success(f"Flashed {mcu.serialport} to {mcu.board} {mcu.version}")
|
65
59
|
return mcu
|
mpflash/mpflash/flash_stm32.py
CHANGED
@@ -1,121 +1,24 @@
|
|
1
|
-
"""
|
2
|
-
Flash STM32 using STM32CubeProgrammer
|
3
|
-
needs to be installed independenty from https://www.st.com/en/development-tools/stm32cubeprog.html
|
1
|
+
"""Flash STM32 boards using either STM32CubeProgrammer CLI or dfu-util"""
|
4
2
|
|
5
|
-
On Linux needs to be run with sudo - because ?
|
6
|
-
"""
|
7
|
-
|
8
|
-
import subprocess
|
9
|
-
import sys
|
10
|
-
import time
|
11
3
|
from pathlib import Path
|
12
|
-
from typing import Optional
|
13
4
|
|
14
|
-
import bincopy
|
15
5
|
from loguru import logger as log
|
16
|
-
from strip_ansi import strip_ansi
|
17
|
-
|
18
|
-
from stubber.bulk.mpremoteboard import MPRemoteBoard
|
19
|
-
|
20
|
-
STM32_CLI_WIN = "C:\\Program Files\\STMicroelectronics\\STM32Cube\\STM32CubeProgrammer\\bin\\STM32_Programmer_CLI.exe"
|
21
|
-
STM32_CLI_LINUX = "/home/jos/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI"
|
22
|
-
|
23
|
-
|
24
|
-
def get_stm32_start_address(fw_file: Path):
|
25
|
-
"""
|
26
|
-
Get the start address of the firmware file, to allow automatic restart from that address after flashing
|
27
|
-
"""
|
28
|
-
try:
|
29
|
-
fw_hex = bincopy.BinFile(str(fw_file))
|
30
|
-
return f"0x{fw_hex.execution_start_address:08X}"
|
31
|
-
except Exception:
|
32
|
-
|
33
|
-
return ""
|
34
|
-
|
35
|
-
|
36
|
-
def flash_stm32(
|
37
|
-
mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True
|
38
|
-
) -> Optional[MPRemoteBoard]:
|
39
|
-
"""
|
40
|
-
Flash STM32 devices using STM32CubeProgrammer CLI
|
41
|
-
- Enter bootloader mode
|
42
|
-
- wait 2s for the device to be detected
|
43
|
-
- list the connected DFU devices
|
44
6
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
STM32_CLI = STM32_CLI_LINUX
|
49
|
-
elif sys.platform == "win32":
|
50
|
-
STM32_CLI = STM32_CLI_WIN
|
51
|
-
else:
|
52
|
-
log.error(f"OS {sys.platform} not supported")
|
53
|
-
return None
|
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
|
54
10
|
|
55
|
-
if not Path(STM32_CLI).exists():
|
56
|
-
log.error(
|
57
|
-
f"STM32CubeProgrammer not found at {STM32_CLI}\nPlease install it from https://www.st.com/en/development-tools/stm32cubeprog.html"
|
58
|
-
)
|
59
|
-
return None
|
60
11
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
results = [strip_ansi(line) for line in results]
|
71
|
-
if not any(["Product ID : STM32 BOOTLOADER" in l for l in results]):
|
72
|
-
log.error("No STM32 BOOTLOADER detected")
|
73
|
-
return None
|
74
|
-
echo = False
|
75
|
-
for line in results:
|
76
|
-
if line.startswith("===== DFU Interface"):
|
77
|
-
echo = True
|
78
|
-
if line.startswith("===== STLink"):
|
79
|
-
echo = False
|
80
|
-
if echo:
|
81
|
-
print(line)
|
82
|
-
# Try to connect - no action
|
83
|
-
cmd = [
|
84
|
-
STM32_CLI,
|
85
|
-
"--connect",
|
86
|
-
"port=USB1",
|
87
|
-
]
|
88
|
-
results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
89
|
-
if erase:
|
90
|
-
log.info("Erasing flash")
|
91
|
-
cmd = [
|
92
|
-
STM32_CLI,
|
93
|
-
"--connect",
|
94
|
-
"port=USB1",
|
95
|
-
"--erase",
|
96
|
-
"all",
|
97
|
-
]
|
98
|
-
results = subprocess.run(
|
99
|
-
cmd, capture_output=True, text=True
|
100
|
-
).stdout.splitlines()
|
101
|
-
results = [strip_ansi(line) for line in results]
|
102
|
-
log.info("Flashing")
|
103
|
-
start_address = get_stm32_start_address(fw_file)
|
12
|
+
def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool, stm32_dfu: bool = True):
|
13
|
+
# sourcery skip: lift-return-into-if
|
14
|
+
log.info("Using dfu-util")
|
15
|
+
dfu_init()
|
16
|
+
updated = flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase)
|
17
|
+
# if stm32_dfu:
|
18
|
+
# else:
|
19
|
+
# log.info("Using STM32CubeProgrammer CLI")
|
20
|
+
# updated = flash_stm32_cubecli(mcu, fw_file=fw_file, erase=erase)
|
104
21
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
cmd = [
|
109
|
-
STM32_CLI,
|
110
|
-
"--connect",
|
111
|
-
"port=USB1",
|
112
|
-
"--write",
|
113
|
-
str(fw_file),
|
114
|
-
"--go",
|
115
|
-
start_address,
|
116
|
-
]
|
117
|
-
results = subprocess.run(cmd, capture_output=True, text=True).stdout.splitlines()
|
118
|
-
log.success("Done flashing, resetting the board and wait for it to restart")
|
119
|
-
time.sleep(5)
|
120
|
-
mcu.get_mcu_info()
|
121
|
-
return mcu
|
22
|
+
mcu.wait_for_restart()
|
23
|
+
log.success(f"Flashed {mcu.version} to {mcu.board}")
|
24
|
+
return updated
|
@@ -0,0 +1,111 @@
|
|
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
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import platform
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from loguru import logger as log
|
6
|
+
|
7
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
8
|
+
|
9
|
+
|
10
|
+
def init_libusb_windows() -> bool:
|
11
|
+
"""
|
12
|
+
Initializes the libusb backend on Windows.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
bool: True if the initialization is successful, False otherwise.
|
16
|
+
"""
|
17
|
+
import libusb # type: ignore
|
18
|
+
import usb.backend.libusb1 as libusb1
|
19
|
+
|
20
|
+
arch = "x64" if platform.architecture()[0] == "64bit" else "x86"
|
21
|
+
libusb1_dll = Path(libusb.__file__).parent / f"_platform\\_windows\\{arch}\\libusb-1.0.dll"
|
22
|
+
if not libusb1_dll.exists():
|
23
|
+
raise FileNotFoundError(f"libusb1.dll not found at {libusb1_dll}")
|
24
|
+
backend = libusb1.get_backend(find_library=lambda x: libusb1_dll.as_posix())
|
25
|
+
return backend is not None
|
26
|
+
|
27
|
+
|
28
|
+
try:
|
29
|
+
from .vendor import pydfu as pydfu
|
30
|
+
except ImportError:
|
31
|
+
pydfu = None
|
32
|
+
|
33
|
+
|
34
|
+
def dfu_init():
|
35
|
+
"""
|
36
|
+
Initializes the DFU (Device Firmware Upgrade) process.
|
37
|
+
"""
|
38
|
+
if not pydfu:
|
39
|
+
log.error("pydfu not found")
|
40
|
+
return None
|
41
|
+
if platform.system() == "Windows":
|
42
|
+
init_libusb_windows()
|
43
|
+
|
44
|
+
|
45
|
+
def flash_stm32_dfu(
|
46
|
+
mcu: MPRemoteBoard,
|
47
|
+
fw_file: Path,
|
48
|
+
*,
|
49
|
+
erase: bool = True,
|
50
|
+
) -> Optional[MPRemoteBoard]:
|
51
|
+
"""
|
52
|
+
Flashes the STM32 microcontroller using DFU (Device Firmware Upgrade).
|
53
|
+
|
54
|
+
Args:
|
55
|
+
mcu (MPRemoteBoard): The remote board to flash.
|
56
|
+
fw_file (Path): The path to the firmware file (.dfu).
|
57
|
+
erase (bool, optional): Whether to erase the memory before flashing. Defaults to True.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Optional[MPRemoteBoard]: The flashed remote board if successful, None otherwise.
|
61
|
+
"""
|
62
|
+
if not pydfu:
|
63
|
+
log.error("pydfu not found, please install it with 'pip install pydfu' if supported")
|
64
|
+
return None
|
65
|
+
|
66
|
+
if not fw_file.exists():
|
67
|
+
log.error(f"File {fw_file} not found")
|
68
|
+
return None
|
69
|
+
|
70
|
+
if fw_file.suffix != ".dfu":
|
71
|
+
log.error(f"File {fw_file} is not a .dfu file")
|
72
|
+
return None
|
73
|
+
|
74
|
+
kwargs = {"idVendor": 0x0483, "idProduct": 0xDF11}
|
75
|
+
log.debug("List SPECIFIED DFU devices...")
|
76
|
+
try:
|
77
|
+
pydfu.list_dfu_devices(**kwargs)
|
78
|
+
except ValueError as e:
|
79
|
+
log.error(f"Insuffient permissions to access usb DFU devices: {e}")
|
80
|
+
return None
|
81
|
+
|
82
|
+
# Needs to be a list of serial ports
|
83
|
+
log.debug("Inititialize pydfu...")
|
84
|
+
pydfu.init(**kwargs)
|
85
|
+
|
86
|
+
if erase:
|
87
|
+
log.info("Mass erase...")
|
88
|
+
pydfu.mass_erase()
|
89
|
+
|
90
|
+
log.debug("Read DFU file...")
|
91
|
+
elements = pydfu.read_dfu_file(fw_file)
|
92
|
+
if not elements:
|
93
|
+
print("No data in dfu file")
|
94
|
+
return
|
95
|
+
log.info("Writing memory...")
|
96
|
+
pydfu.write_elements(elements, False, progress=pydfu.cli_progress)
|
97
|
+
|
98
|
+
log.debug("Exiting DFU...")
|
99
|
+
pydfu.exit_dfu()
|
100
|
+
log.success("Done flashing, resetting the board and wait for it to restart")
|
101
|
+
return mcu
|
mpflash/mpflash/flash_uf2.py
CHANGED
@@ -9,8 +9,9 @@ from pathlib import Path
|
|
9
9
|
from typing import Optional
|
10
10
|
|
11
11
|
from loguru import logger as log
|
12
|
+
from rich.progress import track
|
12
13
|
|
13
|
-
from
|
14
|
+
from mpflash.mpremoteboard import MPRemoteBoard
|
14
15
|
|
15
16
|
from .common import PORT_FWTYPES
|
16
17
|
from .flash_uf2_linux import dismount_uf2, wait_for_UF2_linux
|
@@ -30,21 +31,19 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
30
31
|
pmount and pumount are used to mount and unmount the drive
|
31
32
|
as this is not done automatically by the OS in headless mode.
|
32
33
|
"""
|
33
|
-
if
|
34
|
+
if ".uf2" not in PORT_FWTYPES[mcu.port]:
|
34
35
|
log.error(f"UF2 not supported on {mcu.board} on {mcu.serialport}")
|
35
36
|
return None
|
36
37
|
if erase:
|
37
38
|
log.info("Erasing not yet implemented for UF2 flashing.")
|
38
39
|
|
39
|
-
log.info(f"Entering UF2 bootloader on {mcu.board} on {mcu.serialport}")
|
40
|
-
mcu.run_command("bootloader", timeout=10)
|
41
|
-
|
42
40
|
if sys.platform == "linux":
|
43
41
|
destination = wait_for_UF2_linux()
|
44
42
|
elif sys.platform == "win32":
|
45
43
|
destination = wait_for_UF2_windows()
|
46
44
|
else:
|
47
|
-
log.
|
45
|
+
log.warning(f"OS {sys.platform} not tested/supported")
|
46
|
+
destination = wait_for_UF2_linux()
|
48
47
|
return None
|
49
48
|
|
50
49
|
if not destination or not destination.exists() or not (destination / "INFO_UF2.TXT").exists():
|
@@ -55,7 +54,8 @@ def flash_uf2(mcu: MPRemoteBoard, fw_file: Path, erase: bool) -> Optional[MPRemo
|
|
55
54
|
log.info(f"Copying {fw_file} to {destination}.")
|
56
55
|
shutil.copy(fw_file, destination)
|
57
56
|
log.success("Done copying, resetting the board and wait for it to restart")
|
58
|
-
if sys.platform
|
57
|
+
if sys.platform in ["linux", "darwin"]:
|
59
58
|
dismount_uf2()
|
60
|
-
|
59
|
+
for _ in track(range(5 + 2)):
|
60
|
+
time.sleep(1) # 5 secs to short on linux
|
61
61
|
return mcu
|
@@ -1,12 +1,18 @@
|
|
1
|
+
""" Flashing UF2 based MCU on Linux"""
|
2
|
+
|
3
|
+
# sourcery skip: snake-case-functions
|
1
4
|
from __future__ import annotations
|
2
|
-
|
5
|
+
|
3
6
|
import subprocess
|
4
7
|
import sys
|
5
8
|
import time
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import List
|
11
|
+
|
6
12
|
from loguru import logger as log
|
13
|
+
from rich.progress import track
|
7
14
|
|
8
|
-
from
|
9
|
-
from .uf2_boardid import get_board_id
|
15
|
+
from .flash_uf2_boardid import get_board_id
|
10
16
|
|
11
17
|
glb_dismount_me: List[UF2Disk] = []
|
12
18
|
|
@@ -74,10 +80,10 @@ def pmount(disk: UF2Disk):
|
|
74
80
|
except FileNotFoundError:
|
75
81
|
log.error("pmount not found, please install it using 'sudo apt install pmount'")
|
76
82
|
return
|
77
|
-
log.
|
83
|
+
log.debug(f"Mounted {disk.label} at {disk.mountpoint}")
|
78
84
|
glb_dismount_me.append(disk)
|
79
85
|
else:
|
80
|
-
log.
|
86
|
+
log.debug(f"\n{disk.label} already mounted at {disk.mountpoint}")
|
81
87
|
|
82
88
|
|
83
89
|
def pumount(disk: UF2Disk):
|
@@ -102,20 +108,27 @@ def dismount_uf2():
|
|
102
108
|
glb_dismount_me = []
|
103
109
|
|
104
110
|
|
105
|
-
def wait_for_UF2_linux():
|
111
|
+
def wait_for_UF2_linux(s_max: int = 10):
|
106
112
|
destination = ""
|
107
113
|
wait = 10
|
108
114
|
uf2_drives = []
|
109
|
-
while not destination and wait > 0:
|
110
|
-
|
115
|
+
# while not destination and wait > 0:
|
116
|
+
for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True):
|
117
|
+
# log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
111
118
|
uf2_drives += list(get_uf2_drives())
|
112
119
|
for drive in get_uf2_drives():
|
113
120
|
pmount(drive)
|
114
121
|
time.sleep(1)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
122
|
+
try:
|
123
|
+
if Path(drive.mountpoint, "INFO_UF2.TXT").exists():
|
124
|
+
board_id = get_board_id(Path(drive.mountpoint)) # type: ignore
|
125
|
+
destination = Path(drive.mountpoint)
|
126
|
+
break
|
127
|
+
except PermissionError:
|
128
|
+
log.debug(f"Permission error on {drive.mountpoint}")
|
129
|
+
continue
|
130
|
+
if destination:
|
131
|
+
break
|
119
132
|
time.sleep(1)
|
120
133
|
wait -= 1
|
121
134
|
return destination
|
@@ -1,22 +1,34 @@
|
|
1
|
+
# sourcery skip: snake-case-functions
|
2
|
+
"""Flash a MCU with a UF2 bootloader on Windows"""
|
3
|
+
|
1
4
|
from __future__ import annotations
|
2
|
-
|
5
|
+
|
3
6
|
import time
|
7
|
+
from pathlib import Path
|
8
|
+
|
4
9
|
import psutil
|
5
|
-
from
|
6
|
-
|
10
|
+
from rich.progress import track
|
11
|
+
|
12
|
+
from .flash_uf2_boardid import get_board_id
|
7
13
|
|
8
14
|
|
9
|
-
def wait_for_UF2_windows():
|
15
|
+
def wait_for_UF2_windows(s_max: int = 10):
|
16
|
+
"""Wait for the MCU to mount as a drive"""
|
17
|
+
if s_max < 1:
|
18
|
+
s_max = 10
|
10
19
|
destination = ""
|
11
|
-
|
12
|
-
|
13
|
-
log.info(f"Waiting for mcu to mount as a drive : {wait} seconds left")
|
20
|
+
for _ in track(range(s_max), description="Waiting for mcu to mount as a drive", transient=True):
|
21
|
+
# log.info(f"Waiting for mcu to mount as a drive : {n} seconds left")
|
14
22
|
drives = [drive.device for drive in psutil.disk_partitions()]
|
15
23
|
for drive in drives:
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
try:
|
25
|
+
if Path(drive, "INFO_UF2.TXT").exists():
|
26
|
+
board_id = get_board_id(Path(drive)) # type: ignore
|
27
|
+
destination = Path(drive)
|
28
|
+
break
|
29
|
+
except OSError:
|
30
|
+
pass
|
31
|
+
if destination:
|
32
|
+
break
|
20
33
|
time.sleep(1)
|
21
|
-
wait -= 1
|
22
34
|
return destination
|