mpflash 0.9.0__tar.gz → 0.9.1__tar.gz

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 (55) hide show
  1. {mpflash-0.9.0 → mpflash-0.9.1}/PKG-INFO +16 -5
  2. {mpflash-0.9.0 → mpflash-0.9.1}/README.md +14 -4
  3. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/add_firmware.py +1 -1
  4. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/ask_input.py +1 -1
  5. mpflash-0.9.1/mpflash/bootloader/__init__.py +2 -0
  6. mpflash-0.9.1/mpflash/bootloader/activate.py +60 -0
  7. mpflash-0.9.1/mpflash/bootloader/detect.py +82 -0
  8. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/bootloader/manual.py +10 -11
  9. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/bootloader/micropython.py +2 -0
  10. mpflash-0.9.1/mpflash/bootloader/touch1200.py +36 -0
  11. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/cli_download.py +1 -1
  12. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/cli_flash.py +3 -3
  13. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/cli_group.py +18 -5
  14. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/cli_main.py +3 -5
  15. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/common.py +1 -0
  16. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/config.py +2 -1
  17. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/connected.py +9 -5
  18. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/download.py +9 -5
  19. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/downloaded.py +1 -1
  20. mpflash-0.9.0/mpflash/flash.py → mpflash-0.9.1/mpflash/flash/__init__.py +3 -3
  21. mpflash-0.9.0/mpflash/flash_esp.py → mpflash-0.9.1/mpflash/flash/esp.py +1 -1
  22. mpflash-0.9.0/mpflash/flash_stm32.py → mpflash-0.9.1/mpflash/flash/stm32.py +4 -3
  23. mpflash-0.9.0/mpflash/flash_stm32_dfu.py → mpflash-0.9.1/mpflash/flash/stm32_dfu.py +1 -1
  24. mpflash-0.9.0/mpflash/flash_uf2.py → mpflash-0.9.1/mpflash/flash/uf2/__init__.py +19 -20
  25. mpflash-0.9.0/mpflash/flash_uf2_linux.py → mpflash-0.9.1/mpflash/flash/uf2/linux.py +12 -11
  26. mpflash-0.9.0/mpflash/flash_uf2_macos.py → mpflash-0.9.1/mpflash/flash/uf2/macos.py +11 -6
  27. mpflash-0.9.0/mpflash/flash_uf2_windows.py → mpflash-0.9.1/mpflash/flash/uf2/windows.py +11 -6
  28. {mpflash-0.9.0/mpflash → mpflash-0.9.1/mpflash/flash}/worklist.py +7 -5
  29. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/list.py +13 -3
  30. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/__init__.py +1 -1
  31. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/add_boards.py +2 -2
  32. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/board_id.py +1 -1
  33. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpremoteboard/__init__.py +7 -11
  34. {mpflash-0.9.0/mpflash/vendor → mpflash-0.9.1/mpflash}/versions.py +10 -5
  35. {mpflash-0.9.0 → mpflash-0.9.1}/pyproject.toml +2 -1
  36. mpflash-0.9.0/mpflash/bootloader/__init__.py +0 -37
  37. mpflash-0.9.0/mpflash/bootloader/touch1200.py +0 -45
  38. mpflash-0.9.0/mpflash/flash_stm32_cube.py +0 -111
  39. mpflash-0.9.0/mpflash/vendor/basicgit.py +0 -288
  40. {mpflash-0.9.0 → mpflash-0.9.1}/LICENSE +0 -0
  41. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/__init__.py +0 -0
  42. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/cli_list.py +0 -0
  43. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/errors.py +0 -0
  44. /mpflash-0.9.0/mpflash/flash_uf2_boardid.py → /mpflash-0.9.1/mpflash/flash/uf2/boardid.py +0 -0
  45. {mpflash-0.9.0/mpflash → mpflash-0.9.1/mpflash/flash/uf2}/uf2disk.py +0 -0
  46. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/logger.py +0 -0
  47. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/board.py +0 -0
  48. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/board_info.zip +0 -0
  49. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpboard_id/store.py +0 -0
  50. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpremoteboard/mpy_fw_info.py +0 -0
  51. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/mpremoteboard/runner.py +0 -0
  52. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/vendor/click_aliases.py +0 -0
  53. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/vendor/dfu.py +0 -0
  54. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/vendor/pydfu.py +0 -0
  55. {mpflash-0.9.0 → mpflash-0.9.1}/mpflash/vendor/readme.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mpflash
3
- Version: 0.9.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 (macos not tested yet)
56
- - `samd`, using ` .uf2`, using filecopy (macos not tested yet)
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 pip: `pip install mpflash`
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 versions are `--version preview` and `--version x.y.z` to download the latest preview or version x.y.z respectively.
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
@@ -11,8 +11,8 @@ This tool was initially created to be used in a CI/CD pipeline to automate the p
11
11
  `mpflash` has been tested on:
12
12
  - OS: Windows x64, Linux X64, but not (yet) macOS.
13
13
  - Micropython (hardware) ports:
14
- - `rp2`, using `.uf2`, using filecopy (macos not tested yet)
15
- - `samd`, using ` .uf2`, using filecopy (macos not tested yet)
14
+ - `rp2`, using `.uf2`, using filecopy
15
+ - `samd`, using ` .uf2`, using filecopy
16
16
  - `esp32`, using `.bin`, using esptool,
17
17
  - `esp8266`, using `.bin`, using esptool
18
18
  - `stm32`, using ` .dfu`, using pydfu
@@ -25,7 +25,7 @@ Not yet implemented: `nrf`, `cc3200`, `mimxrt`
25
25
  3. Flash one or all connected MicroPython boards with a specific firmware or version.
26
26
 
27
27
  ## Installation
28
- To install mpflash, you can use pip: `pip install mpflash`
28
+ To install mpflash, you can use: `pipx install mpflash` or `pip install mpflash`
29
29
 
30
30
  ## Basic usage
31
31
  You can use mpflash to perform various operations on your MicroPython boards. Here is an example of basic usage:
@@ -69,7 +69,7 @@ To download the MicroPython firmware for some boards, use the following command:
69
69
 
70
70
  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.
71
71
  The stable version (default) is determined based on the most recent published release,
72
- other versions are `--version preview` and `--version x.y.z` to download the latest preview or version x.y.z respectively.
72
+ other options are `--version stable`, `--version preview` and `--version x.y.z` to download the latest stable, preview or version x.y.z respectively.
73
73
 
74
74
  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.
75
75
 
@@ -92,8 +92,18 @@ After you have downloaded a firmware you can flash the firmware to a board usin
92
92
  This will (try to) autodetect the connected boards, and determine the correct firmware to flash to each board.
93
93
 
94
94
  - `mpflash flash` will flash the latest stable firmware to all connected boards.
95
+ If you have a board withouth a running micropython version, you will need to specify the board and the serial port to flash.
95
96
  - `mpflash flash --serial ? --board ?` will prompt to select a specific serial port and board to flash. (the firmware must be dowloaded earlier)
96
97
 
98
+ 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.
99
+ The supported `--bootloader` options are:
100
+
101
+ - `touch1200` bootloader is activated by connecting to the board at 1200 baud
102
+ - `mpy` using micropython to enter the bootloader
103
+ - `manual` manual intervention is needed to enter the bootloader
104
+ - `none` mpflash assumes the board is ready to flash
105
+
106
+ For ESP32 and ESP8266 boards the `esptool` is used to flash the firmware, and this includes activating the bootloader.
97
107
 
98
108
  ### Flashing all connected boards with the latest stable firmware
99
109
  ```bash
@@ -11,7 +11,7 @@ from mpremote.mip import _rewrite_url as rewrite_url # type: ignore
11
11
 
12
12
  from mpflash.common import FWInfo
13
13
  from mpflash.config import config
14
- from mpflash.vendor.versions import get_preview_mp_version, get_stable_mp_version
14
+ from mpflash.versions import get_preview_mp_version, get_stable_mp_version
15
15
 
16
16
 
17
17
  def add_firmware(
@@ -14,7 +14,7 @@ from .config import config
14
14
  from .mpboard_id import (get_known_boards_for_port, get_known_ports,
15
15
  known_stored_boards)
16
16
  from .mpremoteboard import MPRemoteBoard
17
- from .vendor.versions import micropython_versions
17
+ from .versions import micropython_versions
18
18
 
19
19
 
20
20
  def ask_missing_params(
@@ -0,0 +1,2 @@
1
+ from .activate import enter_bootloader # type: ignore
2
+ from .detect import in_bootloader # type: ignore
@@ -0,0 +1,60 @@
1
+ import time
2
+
3
+ from mpflash.bootloader.detect import in_bootloader
4
+ from mpflash.common import BootloaderMethod
5
+ from mpflash.errors import MPFlashError
6
+ from mpflash.logger import log
7
+ from mpflash.mpremoteboard import MPRemoteBoard
8
+
9
+ from .manual import enter_bootloader_manual
10
+ from .micropython import enter_bootloader_mpy
11
+ from .touch1200 import enter_bootloader_touch_1200bps
12
+
13
+ BL_OPTIONS = {
14
+ "stm32": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
15
+ "rp2": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
16
+ "samd": [BootloaderMethod.TOUCH_1200, BootloaderMethod.MPY, BootloaderMethod.MANUAL],
17
+ "esp32": [BootloaderMethod.NONE],
18
+ "esp8266": [BootloaderMethod.NONE],
19
+ }
20
+
21
+
22
+ def enter_bootloader(
23
+ mcu: MPRemoteBoard,
24
+ method: BootloaderMethod = BootloaderMethod.MPY,
25
+ timeout: int = 10,
26
+ wait_after: int = 2,
27
+ ):
28
+ """Enter the bootloader mode for the board"""
29
+ if method == BootloaderMethod.NONE:
30
+ # NO bootloader requested, so must be OK to flash
31
+ return True
32
+ elif method == BootloaderMethod.AUTO:
33
+ # build a list of options to try for this board
34
+ bl_list = BL_OPTIONS.get(mcu.port, [BootloaderMethod.MPY, BootloaderMethod.MANUAL])
35
+ else:
36
+ bl_list = [method, BootloaderMethod.MANUAL]
37
+ log.info(f"Entering bootloader on {mcu.serialport} using methods {[bl.value for bl in bl_list]}")
38
+ for method in bl_list:
39
+ try:
40
+ if method == BootloaderMethod.MPY:
41
+ result = enter_bootloader_mpy(mcu, timeout=timeout)
42
+ elif method == BootloaderMethod.MANUAL:
43
+ result = enter_bootloader_manual(mcu, timeout=timeout)
44
+ elif method == BootloaderMethod.TOUCH_1200:
45
+ result = enter_bootloader_touch_1200bps(mcu, timeout=timeout)
46
+ except MPFlashError as e:
47
+ log.warning(f"Failed to enter bootloader on {mcu.serialport} using {method.value}")
48
+ log.exception(e)
49
+ result = False
50
+ if not result:
51
+ # try a next method
52
+ continue
53
+
54
+ # todo - check every second or so for up to max wait time
55
+ time.sleep(wait_after)
56
+ # check if bootloader was entered
57
+ if in_bootloader(mcu):
58
+ return True
59
+
60
+ return result
@@ -0,0 +1,82 @@
1
+ """ Detect if a board is in bootloader mode
2
+ """
3
+
4
+ import os
5
+
6
+ from mpflash.common import PORT_FWTYPES
7
+ from mpflash.flash.uf2 import waitfor_uf2
8
+ from mpflash.logger import log
9
+ from mpflash.mpremoteboard import MPRemoteBoard
10
+
11
+
12
+ def in_bootloader(mcu: MPRemoteBoard) -> bool:
13
+ """Check if the board is in bootloader mode"""
14
+ if ".uf2" in PORT_FWTYPES[mcu.port]:
15
+ return in_uf2_bootloader(mcu.port.upper())
16
+ elif mcu.port in ["stm32"]:
17
+ return in_stm32_bootloader()
18
+ elif mcu.port in ["esp32", "esp8266"]:
19
+ log.debug("esp32/esp8266 does not have a bootloader mode, Assume OK to flash")
20
+ return True
21
+
22
+ log.error(f"Bootloader mode not supported on {mcu.board} on {mcu.serialport}")
23
+ return False
24
+
25
+
26
+ def in_uf2_bootloader(board_id: str) -> bool:
27
+ """
28
+ Check if the board is in UF2 bootloader mode.
29
+
30
+ :param board_id: The board ID to check for (SAMD or RP2)
31
+ """
32
+ return bool(waitfor_uf2(board_id=board_id))
33
+
34
+
35
+ def in_stm32_bootloader() -> bool:
36
+ """Check if the board is in STM32 bootloader mode"""
37
+ if os.name == "nt":
38
+ driver_installed, status = check_for_stm32_bootloader_device()
39
+ if not driver_installed:
40
+ log.warning("STM32 BOOTLOADER device not found.")
41
+ return False
42
+ print()
43
+ if status != "OK":
44
+ log.warning(f"STM32 BOOTLOADER device found, Device status: {status}")
45
+ log.error("Please use Zadig to install a WinUSB (libusb) driver.\nhttps://github.com/pbatard/libwdi/wiki/Zadig")
46
+ return False
47
+ return check_dfu_devices()
48
+
49
+
50
+ def check_dfu_devices():
51
+ """Check if there are any DFU devices connected"""
52
+ # JIT import
53
+ from mpflash.flash.stm32_dfu import dfu_init
54
+ from mpflash.vendor.pydfu import get_dfu_devices
55
+
56
+ # need to init on windows to get the right usb backend
57
+ dfu_init()
58
+ devices = get_dfu_devices()
59
+ return len(devices) > 0
60
+
61
+
62
+ def check_for_stm32_bootloader_device():
63
+ import win32com.client
64
+
65
+ # Windows only
66
+ # Create a WMI interface object
67
+ wmi = win32com.client.GetObject("winmgmts:")
68
+
69
+ # Query for USB devices
70
+ for usb_device in wmi.InstancesOf("Win32_PnPEntity"):
71
+ try:
72
+ # Check if device name or description contains "STM32 BOOTLOADER"
73
+ if str(usb_device.Name).strip() in {
74
+ "STM32 BOOTLOADER",
75
+ "STM BOOTLOADER",
76
+ }:
77
+ # Just the first match is enough
78
+ return True, usb_device.Status
79
+ except Exception:
80
+ pass
81
+ # If no matching device was found
82
+ return False, "Not found."
@@ -12,13 +12,14 @@ from mpflash.mpremoteboard import MPRemoteBoard
12
12
 
13
13
 
14
14
  class MCUHighlighter(RegexHighlighter):
15
- """Apply style to anything that looks like an email."""
15
+ """Apply style to things that should stand out."""
16
16
 
17
17
  base_style = "mcu."
18
18
  highlights = [
19
19
  r"(?P<bold>Method[\s\d\:]*)",
20
20
  r"(?P<bold> \d.)", # numbered items
21
21
  r"(?P<bold> - )", # bullets
22
+ # mcu things
22
23
  r"(?P<pad>GPIO[\d]*)",
23
24
  r"(?P<pad>GPI[\d]*)",
24
25
  r"(?P<pad>IO[\d]*)",
@@ -33,21 +34,19 @@ class MCUHighlighter(RegexHighlighter):
33
34
  r"(?P<button>reset)",
34
35
  # other
35
36
  r"(?P<cable>USB)",
36
- # r"(?P<mcu>SAMD[\d]*)",
37
- # r"(?P<mcu>ESP[\d]*)",
38
- # r"(?P<mcu>rp2)",
39
- # r"(?P<mcu>rp2040)",
37
+ r"(?P<cable>USB-C)",
38
+ r"(?P<cable>Serial)",
40
39
  ]
41
40
 
42
41
 
43
42
  # https://rich.readthedocs.io/en/stable/appendix/colors.html?highlight=colors#standard-colors
43
+ # use 3 colors to keep things simple but clear
44
44
  mcu_theme = Theme(
45
45
  {
46
- "mcu.bold": "orange3",
47
- "mcu.mcu": "orange3",
48
- "mcu.button": "bold green",
49
- "mcu.pad": "dodger_blue2",
50
- "mcu.cable": "dodger_blue2",
46
+ "mcu.bold": "orange3", # readers guidance
47
+ "mcu.button": "bold green", # things to press
48
+ "mcu.pad": "dodger_blue2", # things to connect
49
+ "mcu.cable": "dodger_blue2", # things to connect
51
50
  }
52
51
  )
53
52
 
@@ -84,7 +83,7 @@ Please put your {mcu.port.upper()} device into bootloader mode by:
84
83
  """
85
84
 
86
85
  # todo: would be nice to re-use the console instance from logger
87
- console = Console(highlighter=MCUHighlighter(), theme=mcu_theme)
86
+ console = Console(highlighter=MCUHighlighter(), theme=mcu_theme) # type: ignore
88
87
  message += "\nIf you are unsure how to enter bootloader mode, please refer to the device documentation."
89
88
  console.print(
90
89
  Panel(
@@ -1,10 +1,12 @@
1
1
  """Module for handling the bootloader mode for micropython boards"""
2
2
 
3
+ from mpflash.logger import log
3
4
  from mpflash.mpremoteboard import MPRemoteBoard
4
5
 
5
6
 
6
7
  def enter_bootloader_mpy(mcu: MPRemoteBoard, timeout: int = 10):
7
8
  """Enter the bootloader mode for the board using mpremote and micropython on the board"""
9
+ log.info(f"Attempting bootloader on {mcu.serialport} using 'mpremote bootloader'")
8
10
  mcu.run_command("bootloader", timeout=timeout)
9
11
  # todo: check if mpremote command was successful
10
12
  return True
@@ -0,0 +1,36 @@
1
+ """
2
+ Enter bootloader using Touch 1200Bd for boards with bootloaders that support this.
3
+
4
+ """
5
+
6
+ import time
7
+
8
+ import serial
9
+
10
+ from mpflash.errors import MPFlashError
11
+ from mpflash.logger import log
12
+ from mpflash.mpremoteboard import MPRemoteBoard
13
+
14
+
15
+ def enter_bootloader_touch_1200bps(mcu: MPRemoteBoard, timeout: int = 10):
16
+ if not mcu.serialport:
17
+ raise MPFlashError("No serial port specified")
18
+ log.info(f"Attempting bootloader on {mcu.serialport} using 'Touch 1200Bd'")
19
+ # if port argument is present perform soft reset
20
+ # try to initiate serial port connection on PORT with 1200 baudrate
21
+ try:
22
+ com = serial.Serial(mcu.serialport, 1200, dsrdtr=True)
23
+ com.rts = False # required
24
+ com.dtr = False # might as well
25
+ time.sleep(0.2)
26
+ com.close()
27
+
28
+ except serial.SerialException as e:
29
+ log.exception(e)
30
+ raise MPFlashError("pySerial error: " + str(e) + "\n") from e
31
+ except Exception as e:
32
+ log.exception(e)
33
+ raise MPFlashError("Error: " + str(e) + "\n") from e
34
+
35
+ # be optimistic
36
+ return True
@@ -8,7 +8,7 @@ from loguru import logger as log
8
8
  from mpflash.connected import connected_ports_boards
9
9
  from mpflash.errors import MPFlashError
10
10
  from mpflash.mpboard_id import find_known_board
11
- from mpflash.vendor.versions import clean_version
11
+ from mpflash.versions import clean_version
12
12
 
13
13
  from .ask_input import ask_missing_params
14
14
  from .cli_group import cli
@@ -8,7 +8,7 @@ from mpflash.common import BootloaderMethod
8
8
  from mpflash.errors import MPFlashError
9
9
  from mpflash.mpboard_id import find_known_board
10
10
  from mpflash.mpremoteboard import MPRemoteBoard
11
- from mpflash.vendor.versions import clean_version
11
+ from mpflash.versions import clean_version
12
12
 
13
13
  from .ask_input import ask_missing_params
14
14
  from .cli_download import connected_ports_boards
@@ -17,7 +17,7 @@ from .cli_list import show_mcus
17
17
  from .common import FlashParams
18
18
  from .config import config
19
19
  from .flash import flash_list
20
- from .worklist import (WorkList, full_auto_worklist, manual_worklist,
20
+ from .flash.worklist import (WorkList, full_auto_worklist, manual_worklist,
21
21
  single_auto_worklist)
22
22
 
23
23
  # #########################################################################################################
@@ -106,7 +106,7 @@ from .worklist import (WorkList, full_auto_worklist, manual_worklist,
106
106
  "-bl",
107
107
  "bootloader",
108
108
  type=click.Choice([e.value for e in BootloaderMethod]),
109
- default="mpy",
109
+ default="auto",
110
110
  show_default=True,
111
111
  help="""How to enter the (MicroPython) bootloader before flashing.""",
112
112
  )
@@ -27,7 +27,7 @@ def cb_verbose(ctx, param, value):
27
27
  return value
28
28
 
29
29
 
30
- def cb_interactive(ctx, param, value:bool):
30
+ def cb_interactive(ctx, param, value: bool):
31
31
  log.trace(f"Setting interactive mode to {value}")
32
32
  config.interactive = value
33
33
  return value
@@ -39,6 +39,10 @@ def cb_test(ctx, param, value):
39
39
  config.tests = value
40
40
  return value
41
41
 
42
+ def cb_usb(ctx, param, value: bool):
43
+ config.usb = bool(value)
44
+ return value
45
+
42
46
 
43
47
  def cb_quiet(ctx, param, value):
44
48
  log.trace(f"Setting quiet mode to {value}")
@@ -78,19 +82,28 @@ def cb_quiet(ctx, param, value):
78
82
  help="Enables verbose mode.",
79
83
  callback=cb_verbose,
80
84
  )
85
+ @click.option(
86
+ "--usb",
87
+ "-u",
88
+ is_eager=True,
89
+ is_flag=True,
90
+ default=False,
91
+ help="Shows USB location of the connected boards.",
92
+ callback=cb_usb,
93
+ show_default=True,
94
+ )
81
95
  @click.option(
82
96
  "--test",
83
97
  is_eager=True,
84
- help="test a specific feature",
98
+ help="Test a specific feature.",
85
99
  callback=cb_test,
86
100
  multiple=True,
87
101
  default=[],
88
102
  envvar="MPFLASH_TEST",
89
- show_default=True,
90
- metavar="TEST",
103
+ metavar="FLAG",
91
104
  )
92
105
  def cli(**kwargs):
93
- """mpflash - MicroPython Tool.
106
+ """mpflash - MicroPython flashing tool.
94
107
 
95
108
  A CLI to download and flash MicroPython firmware to different ports and boards.
96
109
  """
@@ -1,10 +1,8 @@
1
1
  """mpflash is a CLI to download and flash MicroPython firmware to various boards."""
2
2
 
3
- # import rich_click as click
4
-
5
3
  import os
6
4
 
7
- import click
5
+ import click.exceptions as click_exceptions
8
6
  from loguru import logger as log
9
7
 
10
8
  from .cli_download import cli_download
@@ -29,10 +27,10 @@ def mpflash():
29
27
  except AttributeError as e:
30
28
  log.error(f"Error: {e}")
31
29
  exit(-1)
32
- except click.exceptions.ClickException as e:
30
+ except click_exceptions.ClickException as e:
33
31
  log.error(f"Error: {e}")
34
32
  exit(-2)
35
- except click.exceptions.Abort as e:
33
+ except click_exceptions.Abort as e:
36
34
  # Aborted - Ctrl-C
37
35
  exit(-3)
38
36
 
@@ -92,6 +92,7 @@ class DownloadParams(Params):
92
92
 
93
93
 
94
94
  class BootloaderMethod(Enum):
95
+ AUTO = "auto"
95
96
  MANUAL = "manual"
96
97
  MPY = "mpy"
97
98
  TOUCH_1200 = "touch1200"
@@ -1,11 +1,11 @@
1
1
  """centralized configuration for mpflash"""
2
2
 
3
3
  import os
4
+ from importlib.metadata import version
4
5
  from pathlib import Path
5
6
  from typing import List
6
7
 
7
8
  import platformdirs
8
- from importlib.metadata import version
9
9
 
10
10
  from mpflash.logger import log
11
11
 
@@ -20,6 +20,7 @@ class MPtoolConfig:
20
20
 
21
21
  quiet: bool = False
22
22
  verbose: bool = False
23
+ usb: bool = False
23
24
  ignore_ports: List[str] = []
24
25
  firmware_folder: Path = platformdirs.user_downloads_path() / "firmware"
25
26
  # test options specified on the commandline
@@ -9,9 +9,7 @@ from mpflash.mpremoteboard import MPRemoteBoard
9
9
  from .common import filtered_comports
10
10
 
11
11
 
12
- def connected_ports_boards(
13
- *, include: List[str], ignore: List[str]
14
- ) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
12
+ def connected_ports_boards(*, include: List[str], ignore: List[str]) -> Tuple[List[str], List[str], List[MPRemoteBoard]]:
15
13
  """
16
14
  Returns a tuple containing lists of unique ports and boards from the connected MCUs.
17
15
  Boards that are physically connected, but give no tangible response are ignored.
@@ -50,11 +48,17 @@ def list_mcus(*, ignore: List[str], include: List[str], bluetooth: bool = False)
50
48
  include=include,
51
49
  bluetooth=bluetooth,
52
50
  )
53
- conn_mcus = [MPRemoteBoard(c.device) for c in comports]
51
+ conn_mcus = [MPRemoteBoard(c.device, location=c.location or "?") for c in comports]
54
52
 
55
53
  # a lot of boilerplate to show a progress bar with the comport currently scanned
56
54
  # low update rate to facilitate screen readers/narration
57
- with Progress(rp_spinner, rp_text, rp_bar, TimeElapsedColumn(), refresh_per_second=2) as progress:
55
+ with Progress(
56
+ rp_spinner,
57
+ rp_text,
58
+ rp_bar,
59
+ TimeElapsedColumn(),
60
+ refresh_per_second=1,
61
+ ) as progress:
58
62
  tsk_scan = progress.add_task("[green]Scanning", visible=False, total=None)
59
63
  progress.tasks[tsk_scan].fields["device"] = "..."
60
64
  progress.tasks[tsk_scan].visible = True
@@ -21,7 +21,7 @@ from rich.progress import track
21
21
  from mpflash.common import PORT_FWTYPES, FWInfo
22
22
  from mpflash.errors import MPFlashError
23
23
  from mpflash.mpboard_id import get_known_ports
24
- from mpflash.vendor.versions import clean_version
24
+ from mpflash.versions import clean_version
25
25
 
26
26
  # avoid conflict with the ujson used by MicroPython
27
27
  jsonlines.ujson = None # type: ignore
@@ -134,7 +134,13 @@ def get_boards(ports: List[str], boards: List[str], clean: bool) -> List[FWInfo]
134
134
  for board in urls:
135
135
  board["port"] = port
136
136
 
137
- for board in track(urls, description=f"Checking {port} download pages", transient=True, refresh_per_second=2):
137
+ for board in track(
138
+ urls,
139
+ description=f"Checking {port} download pages",
140
+ transient=True,
141
+ refresh_per_second=1,
142
+ show_speed=False,
143
+ ):
138
144
  # add a board to the list for each firmware found
139
145
  firmware_urls: List[str] = []
140
146
  for ext in PORT_FWTYPES[port]:
@@ -273,9 +279,7 @@ def get_firmware_list(ports: List[str], boards: List[str], versions: List[str],
273
279
  log.debug(f"Total {len(board_urls)} firmwares")
274
280
 
275
281
  relevant = [
276
- board
277
- for board in board_urls
278
- if board.version in versions and board.build == "0" and board.board in boards and not board.preview
282
+ board for board in board_urls if board.version in versions and board.build == "0" and board.board in boards and not board.preview
279
283
  ]
280
284
 
281
285
  if preview:
@@ -5,7 +5,7 @@ import jsonlines
5
5
  from loguru import logger as log
6
6
 
7
7
  from mpflash.common import PORT_FWTYPES, FWInfo
8
- from mpflash.vendor.versions import clean_version
8
+ from mpflash.versions import clean_version
9
9
 
10
10
  from .config import config
11
11
 
@@ -5,9 +5,9 @@ from loguru import logger as log
5
5
  from mpflash.bootloader import enter_bootloader
6
6
  from mpflash.common import PORT_FWTYPES, BootloaderMethod
7
7
 
8
- from .flash_esp import flash_esp
9
- from .flash_stm32 import flash_stm32
10
- from .flash_uf2 import flash_uf2
8
+ from .esp import flash_esp
9
+ from .stm32 import flash_stm32
10
+ from .uf2 import flash_uf2
11
11
  from .worklist import WorkList
12
12
 
13
13
  # #########################################################################################################
@@ -53,7 +53,7 @@ def flash_esp(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool = True) -> Optio
53
53
  log.error(f"Failed to flash {mcu.board} on {mcu.serialport} : {e}")
54
54
  return None
55
55
 
56
- log.info("Done flashing, resetting the board and wait for it to restart")
56
+ log.info("Done flashing, resetting the board...")
57
57
  mcu.wait_for_restart()
58
58
  log.success(f"Flashed {mcu.serialport} to {mcu.board} {mcu.version}")
59
59
  return mcu
@@ -4,12 +4,13 @@ from pathlib import Path
4
4
 
5
5
  from loguru import logger as log
6
6
 
7
- # from .flash_stm32_cube import flash_stm32_cubecli
8
- from .flash_stm32_dfu import dfu_init, flash_stm32_dfu
9
7
  from mpflash.mpremoteboard import MPRemoteBoard
10
8
 
9
+ # from .flash_stm32_cube import flash_stm32_cubecli
10
+ from .stm32_dfu import dfu_init, flash_stm32_dfu
11
+
11
12
 
12
- def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool, stm32_dfu: bool = True):
13
+ def flash_stm32(mcu: MPRemoteBoard, fw_file: Path, *, erase: bool):
13
14
  # sourcery skip: lift-return-into-if
14
15
  dfu_init()
15
16
  if updated := flash_stm32_dfu(mcu, fw_file=fw_file, erase=erase):
@@ -26,7 +26,7 @@ def init_libusb_windows() -> bool:
26
26
 
27
27
 
28
28
  try:
29
- from .vendor import pydfu as pydfu
29
+ from ..vendor import pydfu as pydfu
30
30
  except ImportError:
31
31
  pydfu = None
32
32