refactor: extend firmware flashing functionalities
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
# ======================================================================= #
|
||||||
|
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
||||||
|
# https://github.com/dw-0/kiauh #
|
||||||
|
# #
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||||
|
# ======================================================================= #
|
||||||
|
|
||||||
|
from components.klipper import KLIPPER_DIR
|
||||||
|
|
||||||
|
SD_FLASH_SCRIPT = KLIPPER_DIR.joinpath("scripts/flash-sdcard.sh")
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ from typing import Union, List
|
|||||||
|
|
||||||
|
|
||||||
class FlashMethod(Enum):
|
class FlashMethod(Enum):
|
||||||
REGULAR = "REGULAR"
|
REGULAR = "Regular"
|
||||||
SD_CARD = "SD_CARD"
|
SD_CARD = "SD Card"
|
||||||
|
|
||||||
|
|
||||||
class FlashCommand(Enum):
|
class FlashCommand(Enum):
|
||||||
@@ -24,7 +24,7 @@ class FlashCommand(Enum):
|
|||||||
|
|
||||||
class ConnectionType(Enum):
|
class ConnectionType(Enum):
|
||||||
USB = "USB"
|
USB = "USB"
|
||||||
USB_DFU = "USB_DFU"
|
USB_DFU = "USB (DFU)"
|
||||||
UART = "UART"
|
UART = "UART"
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ class FlashOptions:
|
|||||||
_mcu_list: List[str] = field(default_factory=list)
|
_mcu_list: List[str] = field(default_factory=list)
|
||||||
_selected_mcu: str = ""
|
_selected_mcu: str = ""
|
||||||
_selected_board: str = ""
|
_selected_board: str = ""
|
||||||
|
_selected_baudrate: int = 250000
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if not cls._instance:
|
if not cls._instance:
|
||||||
@@ -93,3 +94,11 @@ class FlashOptions:
|
|||||||
@selected_board.setter
|
@selected_board.setter
|
||||||
def selected_board(self, value: str) -> None:
|
def selected_board(self, value: str) -> None:
|
||||||
self._selected_board = value
|
self._selected_board = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected_baudrate(self) -> int:
|
||||||
|
return self._selected_baudrate
|
||||||
|
|
||||||
|
@selected_baudrate.setter
|
||||||
|
def selected_baudrate(self, value: int) -> None:
|
||||||
|
self._selected_baudrate = value
|
||||||
|
|||||||
@@ -7,15 +7,35 @@
|
|||||||
# This file may be distributed under the terms of the GNU GPLv3 license #
|
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||||
# ======================================================================= #
|
# ======================================================================= #
|
||||||
|
|
||||||
|
import subprocess
|
||||||
from subprocess import CalledProcessError, check_output, Popen, PIPE, STDOUT
|
from subprocess import CalledProcessError, check_output, Popen, PIPE, STDOUT
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from components.klipper import KLIPPER_DIR
|
from components.klipper import KLIPPER_DIR
|
||||||
from components.klipper_firmware.flash_options import FlashOptions, FlashCommand
|
from components.klipper_firmware import SD_FLASH_SCRIPT
|
||||||
|
from components.klipper_firmware.flash_options import (
|
||||||
|
FlashOptions,
|
||||||
|
FlashMethod,
|
||||||
|
)
|
||||||
from utils.logger import Logger
|
from utils.logger import Logger
|
||||||
from utils.system_utils import log_process
|
from utils.system_utils import log_process
|
||||||
|
|
||||||
|
|
||||||
|
def find_firmware_file(method: FlashMethod) -> bool:
|
||||||
|
target = KLIPPER_DIR.joinpath("out")
|
||||||
|
target_exists = target.exists()
|
||||||
|
if method is FlashMethod.REGULAR:
|
||||||
|
f1 = "klipper.elf.hex"
|
||||||
|
f2 = "klipper.elf"
|
||||||
|
fw_file_exists = target.joinpath(f1).exists() and target.joinpath(f2).exists()
|
||||||
|
elif method is FlashMethod.SD_CARD:
|
||||||
|
fw_file_exists = target.joinpath("klipper.bin").exists()
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown flash method")
|
||||||
|
|
||||||
|
return target_exists and fw_file_exists
|
||||||
|
|
||||||
|
|
||||||
def find_usb_device_by_id() -> List[str]:
|
def find_usb_device_by_id() -> List[str]:
|
||||||
try:
|
try:
|
||||||
command = "find /dev/serial/by-id/* 2>/dev/null"
|
command = "find /dev/serial/by-id/* 2>/dev/null"
|
||||||
@@ -49,28 +69,62 @@ def find_usb_dfu_device() -> List[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def flash_device(flash_options: FlashOptions) -> None:
|
def get_sd_flash_board_list() -> List[str]:
|
||||||
|
if not KLIPPER_DIR.exists() or not SD_FLASH_SCRIPT.exists():
|
||||||
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
cmd = f"{SD_FLASH_SCRIPT} -l"
|
||||||
|
blist = subprocess.check_output(cmd, shell=True, text=True)
|
||||||
|
return blist.splitlines()[1:]
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
Logger.print_error(f"An unexpected error occured:\n{e}")
|
||||||
|
|
||||||
|
|
||||||
|
def start_flash_process(flash_options: FlashOptions) -> None:
|
||||||
|
Logger.print_status(f"Flashing '{flash_options.selected_mcu}' ...")
|
||||||
|
try:
|
||||||
|
if not flash_options.flash_method:
|
||||||
|
raise Exception("Missing value for flash_method!")
|
||||||
|
if not flash_options.flash_command:
|
||||||
|
raise Exception("Missing value for flash_command!")
|
||||||
if not flash_options.selected_mcu:
|
if not flash_options.selected_mcu:
|
||||||
raise Exception("Missing value for selected_mcu!")
|
raise Exception("Missing value for selected_mcu!")
|
||||||
|
if not flash_options.connection_type:
|
||||||
|
raise Exception("Missing value for connection_type!")
|
||||||
|
if (
|
||||||
|
flash_options.flash_method == FlashMethod.SD_CARD
|
||||||
|
and not flash_options.selected_board
|
||||||
|
):
|
||||||
|
raise Exception("Missing value for selected_board!")
|
||||||
|
|
||||||
if flash_options.flash_command is FlashCommand.FLASH:
|
if flash_options.flash_method is FlashMethod.REGULAR:
|
||||||
command = [
|
cmd = [
|
||||||
"make",
|
"make",
|
||||||
flash_options.flash_command.value,
|
flash_options.flash_command.value,
|
||||||
f"FLASH_DEVICE={flash_options.selected_mcu}",
|
f"FLASH_DEVICE={flash_options.selected_mcu}",
|
||||||
]
|
]
|
||||||
process = Popen(
|
elif flash_options.flash_method is FlashMethod.SD_CARD:
|
||||||
command, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True
|
if not SD_FLASH_SCRIPT.exists():
|
||||||
)
|
raise Exception("Unable to find Klippers sdcard flash script!")
|
||||||
|
cmd = [
|
||||||
|
SD_FLASH_SCRIPT,
|
||||||
|
"-b",
|
||||||
|
flash_options.selected_baudrate,
|
||||||
|
flash_options.selected_mcu,
|
||||||
|
flash_options.selected_board,
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
raise Exception("Invalid value for flash_method!")
|
||||||
|
|
||||||
log_process(process)
|
process = Popen(cmd, cwd=KLIPPER_DIR, stdout=PIPE, stderr=STDOUT, text=True)
|
||||||
|
log_process(process)
|
||||||
|
|
||||||
rc = process.returncode
|
rc = process.returncode
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
raise Exception(f"Flashing failed with returncode: {rc}")
|
raise Exception(f"Flashing failed with returncode: {rc}")
|
||||||
else:
|
else:
|
||||||
Logger.print_ok("Flashing successfull!", start="\n", end="\n\n")
|
Logger.print_ok("Flashing successfull!", start="\n", end="\n\n")
|
||||||
|
|
||||||
except (Exception, CalledProcessError):
|
except (Exception, CalledProcessError):
|
||||||
Logger.print_error("Flashing failed!", start="\n")
|
Logger.print_error("Flashing failed!", start="\n")
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# ======================================================================= #
|
||||||
|
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
||||||
|
# https://github.com/dw-0/kiauh #
|
||||||
|
# #
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||||
|
# ======================================================================= #
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from components.klipper_firmware.flash_options import FlashOptions, FlashMethod
|
||||||
|
from core.menus import FooterType
|
||||||
|
from core.menus.base_menu import BaseMenu
|
||||||
|
from utils.constants import COLOR_RED, RESET_FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
class KlipperNoFirmwareErrorMenu(BaseMenu):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.flash_options = FlashOptions()
|
||||||
|
self.options = {"": self.go_back}
|
||||||
|
self.default_options = self.go_back
|
||||||
|
self.footer_type = FooterType.BLANK
|
||||||
|
self.input_label_txt = "Press ENTER to go back to [Advanced Menu]"
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = "!!! NO FIRMWARE FILE FOUND !!!"
|
||||||
|
color = COLOR_RED
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
line1 = f"{color}Unable to find a compiled firmware file!{RESET_FORMAT}"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| {line1:<62} |
|
||||||
|
| |
|
||||||
|
| Make sure, that: |
|
||||||
|
| ● the folder '~/klipper/out' and its content exist |
|
||||||
|
| ● the folder contains the following file: |
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
|
||||||
|
if self.flash_options.flash_method is FlashMethod.REGULAR:
|
||||||
|
menu += "| ● 'klipper.elf' |\n"
|
||||||
|
menu += "| ● 'klipper.elf.hex' |\n"
|
||||||
|
else:
|
||||||
|
menu += "| ● 'klipper.bin' |\n"
|
||||||
|
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
def go_back(self, **kwargs) -> None:
|
||||||
|
from core.menus.advanced_menu import AdvancedMenu
|
||||||
|
|
||||||
|
AdvancedMenu().run()
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
class KlipperNoBoardTypesErrorMenu(BaseMenu):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.options = {"": self.go_back}
|
||||||
|
self.default_options = self.go_back
|
||||||
|
self.footer_type = FooterType.BLANK
|
||||||
|
self.input_label_txt = "Press ENTER to go back to [Main Menu]"
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = "!!! ERROR GETTING BOARD LIST !!!"
|
||||||
|
color = COLOR_RED
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
line1 = f"{color}Reading the list of supported boards failed!{RESET_FORMAT}"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| {line1:<62} |
|
||||||
|
| |
|
||||||
|
| Make sure, that: |
|
||||||
|
| ● the folder '~/klipper' and all its content exist |
|
||||||
|
| ● the content of folder '~/klipper' is not currupted |
|
||||||
|
| ● the file '~/klipper/scripts/flash-sd.py' exist |
|
||||||
|
| ● your current user has access to those files/folders |
|
||||||
|
| |
|
||||||
|
| If in doubt or this process continues to fail, please |
|
||||||
|
| consider to download Klipper again. |
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
def go_back(self, **kwargs) -> None:
|
||||||
|
from core.menus.main_menu import MainMenu
|
||||||
|
|
||||||
|
MainMenu().run()
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# ======================================================================= #
|
||||||
|
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This file is part of KIAUH - Klipper Installation And Update Helper #
|
||||||
|
# https://github.com/dw-0/kiauh #
|
||||||
|
# #
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license #
|
||||||
|
# ======================================================================= #
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from core.menus.base_menu import BaseMenu
|
||||||
|
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW
|
||||||
|
|
||||||
|
|
||||||
|
class KlipperFlashMethodHelpMenu(BaseMenu):
|
||||||
|
def __init__(self, previous_menu: BaseMenu):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.previous_menu: BaseMenu = previous_menu
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = " < ? > Help: Flash MCU < ? > "
|
||||||
|
color = COLOR_YELLOW
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
subheader1 = f"{COLOR_CYAN}Regular flashing method:{RESET_FORMAT}"
|
||||||
|
subheader2 = f"{COLOR_CYAN}Updating via SD-Card Update:{RESET_FORMAT}"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:~^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| {subheader1:<62} |
|
||||||
|
| The default method to flash controller boards which |
|
||||||
|
| are connected and updated over USB and not by placing |
|
||||||
|
| a compiled firmware file onto an internal SD-Card. |
|
||||||
|
| |
|
||||||
|
| Common controllers that get flashed that way are: |
|
||||||
|
| - Arduino Mega 2560 |
|
||||||
|
| - Fysetc F6 / S6 (used without a Display + SD-Slot) |
|
||||||
|
| |
|
||||||
|
| {subheader2:<62} |
|
||||||
|
| Many popular controller boards ship with a bootloader |
|
||||||
|
| capable of updating the firmware via SD-Card. |
|
||||||
|
| Choose this method if your controller board supports |
|
||||||
|
| this way of updating. This method ONLY works for up- |
|
||||||
|
| grading firmware. The initial flashing procedure must |
|
||||||
|
| be done manually per the instructions that apply to |
|
||||||
|
| your controller board. |
|
||||||
|
| |
|
||||||
|
| Common controllers that can be flashed that way are: |
|
||||||
|
| - BigTreeTech SKR 1.3 / 1.4 (Turbo) / E3 / Mini E3 |
|
||||||
|
| - Fysetc F6 / S6 (used with a Display + SD-Slot) |
|
||||||
|
| - Fysetc Spider |
|
||||||
|
| |
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
|
||||||
|
class KlipperFlashCommandHelpMenu(BaseMenu):
|
||||||
|
def __init__(self, previous_menu: BaseMenu):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.previous_menu: BaseMenu = previous_menu
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = " < ? > Help: Flash MCU < ? > "
|
||||||
|
color = COLOR_YELLOW
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
subheader1 = f"{COLOR_CYAN}make flash:{RESET_FORMAT}"
|
||||||
|
subheader2 = f"{COLOR_CYAN}make serialflash:{RESET_FORMAT}"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:~^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| {subheader1:<62} |
|
||||||
|
| The default command to flash controller board, it |
|
||||||
|
| will detect selected microcontroller and use suitable |
|
||||||
|
| tool for flashing it. |
|
||||||
|
| |
|
||||||
|
| {subheader2:<62} |
|
||||||
|
| Special command to flash STM32 microcontrollers in |
|
||||||
|
| DFU mode but connected via serial. stm32flash command |
|
||||||
|
| will be used internally. |
|
||||||
|
| |
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
|
||||||
|
class KlipperMcuConnectionHelpMenu(BaseMenu):
|
||||||
|
def __init__(self, previous_menu: BaseMenu):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.previous_menu: BaseMenu = previous_menu
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = " < ? > Help: Flash MCU < ? > "
|
||||||
|
color = COLOR_YELLOW
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
subheader1 = f"{COLOR_CYAN}USB:{RESET_FORMAT}"
|
||||||
|
subheader2 = f"{COLOR_CYAN}UART:{RESET_FORMAT}"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:~^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| {subheader1:<62} |
|
||||||
|
| Selecting USB as the connection method will scan the |
|
||||||
|
| USB ports for connected controller boards. This will |
|
||||||
|
| be similar to the 'ls /dev/serial/by-id/*' command |
|
||||||
|
| suggested by the official Klipper documentation for |
|
||||||
|
| determining successfull USB connections! |
|
||||||
|
| |
|
||||||
|
| {subheader2:<62} |
|
||||||
|
| Selecting UART as the connection method will list all |
|
||||||
|
| possible UART serial ports. Note: This method ALWAYS |
|
||||||
|
| returns something as it seems impossible to determine |
|
||||||
|
| if a valid Klipper controller board is connected or |
|
||||||
|
| not. Because of that, you MUST know which UART serial |
|
||||||
|
| port your controller board is connected to when using |
|
||||||
|
| this connection method. |
|
||||||
|
| |
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
print(menu, end="")
|
||||||
@@ -20,13 +20,24 @@ from components.klipper_firmware.flash_utils import (
|
|||||||
find_usb_device_by_id,
|
find_usb_device_by_id,
|
||||||
find_uart_device,
|
find_uart_device,
|
||||||
find_usb_dfu_device,
|
find_usb_dfu_device,
|
||||||
flash_device,
|
get_sd_flash_board_list,
|
||||||
|
start_flash_process,
|
||||||
|
find_firmware_file,
|
||||||
|
)
|
||||||
|
from components.klipper_firmware.menus.klipper_flash_error_menu import (
|
||||||
|
KlipperNoBoardTypesErrorMenu,
|
||||||
|
KlipperNoFirmwareErrorMenu,
|
||||||
|
)
|
||||||
|
from components.klipper_firmware.menus.klipper_flash_help_menu import (
|
||||||
|
KlipperMcuConnectionHelpMenu,
|
||||||
|
KlipperFlashCommandHelpMenu,
|
||||||
|
KlipperFlashMethodHelpMenu,
|
||||||
)
|
)
|
||||||
from core.menus import FooterType
|
from core.menus import FooterType
|
||||||
|
|
||||||
from core.menus.base_menu import BaseMenu
|
from core.menus.base_menu import BaseMenu
|
||||||
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW, COLOR_RED
|
from utils.constants import COLOR_CYAN, RESET_FORMAT, COLOR_YELLOW, COLOR_RED
|
||||||
from utils.input_utils import get_confirm
|
from utils.input_utils import get_number_input
|
||||||
from utils.logger import Logger
|
from utils.logger import Logger
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +59,11 @@ class KlipperFlashMethodMenu(BaseMenu):
|
|||||||
self.flash_options = FlashOptions()
|
self.flash_options = FlashOptions()
|
||||||
|
|
||||||
def print_menu(self) -> None:
|
def print_menu(self) -> None:
|
||||||
header = " [ Flash MCU ] "
|
header = " [ MCU Flash Menu ] "
|
||||||
|
subheader = f"{COLOR_YELLOW}ATTENTION:{RESET_FORMAT}"
|
||||||
|
subline1 = f"{COLOR_YELLOW}Make sure to select the correct method for the MCU!{RESET_FORMAT}"
|
||||||
|
subline2 = f"{COLOR_YELLOW}Not all MCUs support both methods!{RESET_FORMAT}"
|
||||||
|
|
||||||
color = COLOR_CYAN
|
color = COLOR_CYAN
|
||||||
count = 62 - len(color) - len(RESET_FORMAT)
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
menu = textwrap.dedent(
|
menu = textwrap.dedent(
|
||||||
@@ -56,9 +71,11 @@ class KlipperFlashMethodMenu(BaseMenu):
|
|||||||
/=======================================================\\
|
/=======================================================\\
|
||||||
| {color}{header:~^{count}}{RESET_FORMAT} |
|
| {color}{header:~^{count}}{RESET_FORMAT} |
|
||||||
|-------------------------------------------------------|
|
|-------------------------------------------------------|
|
||||||
| Please select the flashing method to flash your MCU. |
|
| Select the flash method for flashing the MCU. |
|
||||||
| Make sure to only select a method your MCU supports. |
|
| |
|
||||||
| Not all MCUs support both methods! |
|
| {subheader:<62} |
|
||||||
|
| {subline1:<62} |
|
||||||
|
| {subline2:<62} |
|
||||||
|-------------------------------------------------------|
|
|-------------------------------------------------------|
|
||||||
| |
|
| |
|
||||||
| 1) Regular flashing method |
|
| 1) Regular flashing method |
|
||||||
@@ -77,7 +94,10 @@ class KlipperFlashMethodMenu(BaseMenu):
|
|||||||
self.goto_next_menu()
|
self.goto_next_menu()
|
||||||
|
|
||||||
def goto_next_menu(self, **kwargs):
|
def goto_next_menu(self, **kwargs):
|
||||||
KlipperFlashCommandMenu(previous_menu=self).run()
|
if find_firmware_file(self.flash_options.flash_method):
|
||||||
|
KlipperFlashCommandMenu(previous_menu=self).run()
|
||||||
|
else:
|
||||||
|
KlipperNoFirmwareErrorMenu().run()
|
||||||
|
|
||||||
def help_menu(self, **kwargs):
|
def help_menu(self, **kwargs):
|
||||||
KlipperFlashMethodHelpMenu(previous_menu=self).run()
|
KlipperFlashMethodHelpMenu(previous_menu=self).run()
|
||||||
@@ -256,132 +276,139 @@ class KlipperSelectMcuIdMenu(BaseMenu):
|
|||||||
selected_mcu = self.mcu_list[index]
|
selected_mcu = self.mcu_list[index]
|
||||||
self.flash_options.selected_mcu = selected_mcu
|
self.flash_options.selected_mcu = selected_mcu
|
||||||
|
|
||||||
print(f"{COLOR_CYAN}###### You selected:{RESET_FORMAT}")
|
if self.flash_options.flash_method == FlashMethod.SD_CARD:
|
||||||
print(f"● MCU #{index}: {selected_mcu}\n")
|
KlipperSelectSDFlashBoardMenu(previous_menu=self).run()
|
||||||
|
elif self.flash_options.flash_method == FlashMethod.REGULAR:
|
||||||
|
KlipperFlashOverviewMenu(previous_menu=self).run()
|
||||||
|
|
||||||
if get_confirm("Continue", allow_go_back=True):
|
|
||||||
Logger.print_status(f"Flashing '{selected_mcu}' ...")
|
|
||||||
flash_device(self.flash_options)
|
|
||||||
|
|
||||||
self.goto_next_menu()
|
# noinspection PyUnusedLocal
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
class KlipperSelectSDFlashBoardMenu(BaseMenu):
|
||||||
|
def __init__(self, previous_menu: BaseMenu):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
def goto_next_menu(self, **kwargs):
|
self.previous_menu: BaseMenu = previous_menu
|
||||||
from core.menus.main_menu import MainMenu
|
self.flash_options = FlashOptions()
|
||||||
|
self.available_boards = get_sd_flash_board_list()
|
||||||
|
self.input_label_txt = "Select board type"
|
||||||
|
|
||||||
|
options = {f"{i}": self.board_select for i in range(len(self.available_boards))}
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
if len(self.available_boards) < 1:
|
||||||
|
KlipperNoBoardTypesErrorMenu().run()
|
||||||
|
else:
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
/=======================================================\\
|
||||||
|
| Please select the type of board that corresponds to |
|
||||||
|
| the currently selected MCU ID you chose before. |
|
||||||
|
| |
|
||||||
|
| The following boards are currently supported: |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
|
||||||
|
for i, board in enumerate(self.available_boards):
|
||||||
|
line = f" {i}) {board}"
|
||||||
|
menu += f"|{line:<55}|\n"
|
||||||
|
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
def board_select(self, **kwargs):
|
||||||
|
board = int(kwargs.get("opt_index"))
|
||||||
|
self.flash_options.selected_board = self.available_boards[board]
|
||||||
|
self.baudrate_select()
|
||||||
|
|
||||||
|
def baudrate_select(self, **kwargs):
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
/=======================================================\\
|
||||||
|
| If your board is flashed with firmware that connects |
|
||||||
|
| at a custom baud rate, please change it now. |
|
||||||
|
| |
|
||||||
|
| If you are unsure, stick to the default 250000! |
|
||||||
|
\\=======================================================/
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
print(menu, end="")
|
||||||
|
self.flash_options.selected_baudrate = get_number_input(
|
||||||
|
question="Please set the baud rate",
|
||||||
|
default=250000,
|
||||||
|
min_count=0,
|
||||||
|
allow_go_back=True,
|
||||||
|
)
|
||||||
|
KlipperFlashOverviewMenu(previous_menu=self).run()
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
class KlipperFlashOverviewMenu(BaseMenu):
|
||||||
|
def __init__(self, previous_menu: BaseMenu):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.previous_menu: BaseMenu = previous_menu
|
||||||
|
self.flash_options = FlashOptions()
|
||||||
|
self.options = {"Y": self.execute_flash, "N": self.abort_process}
|
||||||
|
self.input_label_txt = "Perform action (default=Y)"
|
||||||
|
self.default_option = self.execute_flash
|
||||||
|
|
||||||
|
def print_menu(self) -> None:
|
||||||
|
header = "!!! ATTENTION !!!"
|
||||||
|
color = COLOR_RED
|
||||||
|
count = 62 - len(color) - len(RESET_FORMAT)
|
||||||
|
|
||||||
|
method = self.flash_options.flash_method.value
|
||||||
|
command = self.flash_options.flash_command.value
|
||||||
|
conn_type = self.flash_options.connection_type.value
|
||||||
|
mcu = self.flash_options.selected_mcu
|
||||||
|
board = self.flash_options.selected_board
|
||||||
|
baudrate = self.flash_options.selected_baudrate
|
||||||
|
subheader = f"[{COLOR_CYAN}Overview{RESET_FORMAT}]"
|
||||||
|
menu = textwrap.dedent(
|
||||||
|
f"""
|
||||||
|
/=======================================================\\
|
||||||
|
| {color}{header:^{count}}{RESET_FORMAT} |
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| Before contuining the flashing process, please check |
|
||||||
|
| if all parameters were set correctly! Once you made |
|
||||||
|
| sure everything is correct, start the process. If any |
|
||||||
|
| parameter needs to be changed, you can go back (B) |
|
||||||
|
| step by step or abort and start from the beginning. |
|
||||||
|
|{subheader:-^64}|
|
||||||
|
|
||||||
|
"""
|
||||||
|
)[1:]
|
||||||
|
|
||||||
|
menu += f" ● MCU: {COLOR_CYAN}{mcu}{RESET_FORMAT}\n"
|
||||||
|
menu += f" ● Connection: {COLOR_CYAN}{conn_type}{RESET_FORMAT}\n"
|
||||||
|
menu += f" ● Flash method: {COLOR_CYAN}{method}{RESET_FORMAT}\n"
|
||||||
|
menu += f" ● Flash command: {COLOR_CYAN}{command}{RESET_FORMAT}\n"
|
||||||
|
|
||||||
|
if self.flash_options.flash_method is FlashMethod.SD_CARD:
|
||||||
|
menu += f" ● Board type: {COLOR_CYAN}{board}{RESET_FORMAT}\n"
|
||||||
|
menu += f" ● Baudrate: {COLOR_CYAN}{baudrate}{RESET_FORMAT}\n"
|
||||||
|
|
||||||
|
menu += textwrap.dedent(
|
||||||
|
"""
|
||||||
|
|-------------------------------------------------------|
|
||||||
|
| Y) Start flash process |
|
||||||
|
| N) Abort - Return to Advanced Menu |
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
print(menu, end="")
|
||||||
|
|
||||||
|
def execute_flash(self, **kwargs):
|
||||||
from core.menus.advanced_menu import AdvancedMenu
|
from core.menus.advanced_menu import AdvancedMenu
|
||||||
|
|
||||||
AdvancedMenu(previous_menu=MainMenu()).run()
|
start_flash_process(self.flash_options)
|
||||||
|
Logger.print_info("Returning to MCU Flash Menu in 5 seconds ...")
|
||||||
|
time.sleep(5)
|
||||||
|
KlipperFlashMethodMenu(previous_menu=AdvancedMenu()).run()
|
||||||
|
|
||||||
|
def abort_process(self, **kwargs):
|
||||||
|
from core.menus.advanced_menu import AdvancedMenu
|
||||||
|
|
||||||
class KlipperFlashMethodHelpMenu(BaseMenu):
|
AdvancedMenu().run()
|
||||||
def __init__(self, previous_menu: BaseMenu):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.previous_menu: BaseMenu = previous_menu
|
|
||||||
|
|
||||||
def print_menu(self) -> None:
|
|
||||||
header = " < ? > Help: Flash MCU < ? > "
|
|
||||||
color = COLOR_YELLOW
|
|
||||||
count = 62 - len(color) - len(RESET_FORMAT)
|
|
||||||
subheader1 = f"{COLOR_CYAN}Regular flashing method:{RESET_FORMAT}"
|
|
||||||
subheader2 = f"{COLOR_CYAN}Updating via SD-Card Update:{RESET_FORMAT}"
|
|
||||||
menu = textwrap.dedent(
|
|
||||||
f"""
|
|
||||||
/=======================================================\\
|
|
||||||
| {color}{header:~^{count}}{RESET_FORMAT} |
|
|
||||||
|-------------------------------------------------------|
|
|
||||||
| {subheader1:<62} |
|
|
||||||
| The default method to flash controller boards which |
|
|
||||||
| are connected and updated over USB and not by placing |
|
|
||||||
| a compiled firmware file onto an internal SD-Card. |
|
|
||||||
| |
|
|
||||||
| Common controllers that get flashed that way are: |
|
|
||||||
| - Arduino Mega 2560 |
|
|
||||||
| - Fysetc F6 / S6 (used without a Display + SD-Slot) |
|
|
||||||
| |
|
|
||||||
| {subheader2:<62} |
|
|
||||||
| Many popular controller boards ship with a bootloader |
|
|
||||||
| capable of updating the firmware via SD-Card. |
|
|
||||||
| Choose this method if your controller board supports |
|
|
||||||
| this way of updating. This method ONLY works for up- |
|
|
||||||
| grading firmware. The initial flashing procedure must |
|
|
||||||
| be done manually per the instructions that apply to |
|
|
||||||
| your controller board. |
|
|
||||||
| |
|
|
||||||
| Common controllers that can be flashed that way are: |
|
|
||||||
| - BigTreeTech SKR 1.3 / 1.4 (Turbo) / E3 / Mini E3 |
|
|
||||||
| - Fysetc F6 / S6 (used with a Display + SD-Slot) |
|
|
||||||
| - Fysetc Spider |
|
|
||||||
| |
|
|
||||||
"""
|
|
||||||
)[1:]
|
|
||||||
print(menu, end="")
|
|
||||||
|
|
||||||
|
|
||||||
class KlipperFlashCommandHelpMenu(BaseMenu):
|
|
||||||
def __init__(self, previous_menu: BaseMenu):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.previous_menu: BaseMenu = previous_menu
|
|
||||||
|
|
||||||
def print_menu(self) -> None:
|
|
||||||
header = " < ? > Help: Flash MCU < ? > "
|
|
||||||
color = COLOR_YELLOW
|
|
||||||
count = 62 - len(color) - len(RESET_FORMAT)
|
|
||||||
subheader1 = f"{COLOR_CYAN}make flash:{RESET_FORMAT}"
|
|
||||||
subheader2 = f"{COLOR_CYAN}make serialflash:{RESET_FORMAT}"
|
|
||||||
menu = textwrap.dedent(
|
|
||||||
f"""
|
|
||||||
/=======================================================\\
|
|
||||||
| {color}{header:~^{count}}{RESET_FORMAT} |
|
|
||||||
|-------------------------------------------------------|
|
|
||||||
| {subheader1:<62} |
|
|
||||||
| The default command to flash controller board, it |
|
|
||||||
| will detect selected microcontroller and use suitable |
|
|
||||||
| tool for flashing it. |
|
|
||||||
| |
|
|
||||||
| {subheader2:<62} |
|
|
||||||
| Special command to flash STM32 microcontrollers in |
|
|
||||||
| DFU mode but connected via serial. stm32flash command |
|
|
||||||
| will be used internally. |
|
|
||||||
| |
|
|
||||||
"""
|
|
||||||
)[1:]
|
|
||||||
print(menu, end="")
|
|
||||||
|
|
||||||
|
|
||||||
class KlipperMcuConnectionHelpMenu(BaseMenu):
|
|
||||||
def __init__(self, previous_menu: BaseMenu):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.previous_menu: BaseMenu = previous_menu
|
|
||||||
|
|
||||||
def print_menu(self) -> None:
|
|
||||||
header = " < ? > Help: Flash MCU < ? > "
|
|
||||||
color = COLOR_YELLOW
|
|
||||||
count = 62 - len(color) - len(RESET_FORMAT)
|
|
||||||
subheader1 = f"{COLOR_CYAN}USB:{RESET_FORMAT}"
|
|
||||||
subheader2 = f"{COLOR_CYAN}UART:{RESET_FORMAT}"
|
|
||||||
menu = textwrap.dedent(
|
|
||||||
f"""
|
|
||||||
/=======================================================\\
|
|
||||||
| {color}{header:~^{count}}{RESET_FORMAT} |
|
|
||||||
|-------------------------------------------------------|
|
|
||||||
| {subheader1:<62} |
|
|
||||||
| Selecting USB as the connection method will scan the |
|
|
||||||
| USB ports for connected controller boards. This will |
|
|
||||||
| be similar to the 'ls /dev/serial/by-id/*' command |
|
|
||||||
| suggested by the official Klipper documentation for |
|
|
||||||
| determining successfull USB connections! |
|
|
||||||
| |
|
|
||||||
| {subheader2:<62} |
|
|
||||||
| Selecting UART as the connection method will list all |
|
|
||||||
| possible UART serial ports. Note: This method ALWAYS |
|
|
||||||
| returns something as it seems impossible to determine |
|
|
||||||
| if a valid Klipper controller board is connected or |
|
|
||||||
| not. Because of that, you MUST know which UART serial |
|
|
||||||
| port your controller board is connected to when using |
|
|
||||||
| this connection method. |
|
|
||||||
| |
|
|
||||||
"""
|
|
||||||
)[1:]
|
|
||||||
print(menu, end="")
|
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ class FooterType(Enum):
|
|||||||
QUIT = "QUIT"
|
QUIT = "QUIT"
|
||||||
BACK = "BACK"
|
BACK = "BACK"
|
||||||
BACK_HELP = "BACK_HELP"
|
BACK_HELP = "BACK_HELP"
|
||||||
|
BLANK = "BLANK"
|
||||||
|
|
||||||
|
|
||||||
NAVI_OPTIONS = {
|
NAVI_OPTIONS = {
|
||||||
FooterType.QUIT: ["q"],
|
FooterType.QUIT: ["q"],
|
||||||
FooterType.BACK: ["b"],
|
FooterType.BACK: ["b"],
|
||||||
FooterType.BACK_HELP: ["b", "h"],
|
FooterType.BACK_HELP: ["b", "h"],
|
||||||
|
FooterType.BLANK: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ from utils.constants import COLOR_YELLOW, RESET_FORMAT
|
|||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
class AdvancedMenu(BaseMenu):
|
class AdvancedMenu(BaseMenu):
|
||||||
def __init__(self, previous_menu: BaseMenu):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.previous_menu: BaseMenu = previous_menu
|
from core.menus.main_menu import MainMenu
|
||||||
|
|
||||||
|
self.previous_menu: BaseMenu = MainMenu()
|
||||||
self.options = {
|
self.options = {
|
||||||
"1": None,
|
"1": None,
|
||||||
"2": None,
|
"2": None,
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ def print_back_help_footer():
|
|||||||
print(footer, end="")
|
print(footer, end="")
|
||||||
|
|
||||||
|
|
||||||
|
def print_blank_footer():
|
||||||
|
print("\=======================================================/")
|
||||||
|
|
||||||
|
|
||||||
Options = Dict[str, Callable]
|
Options = Dict[str, Callable]
|
||||||
|
|
||||||
|
|
||||||
@@ -119,6 +123,8 @@ class BaseMenu(ABC):
|
|||||||
print_back_footer()
|
print_back_footer()
|
||||||
elif self.footer_type is FooterType.BACK_HELP:
|
elif self.footer_type is FooterType.BACK_HELP:
|
||||||
print_back_help_footer()
|
print_back_help_footer()
|
||||||
|
elif self.footer_type is FooterType.BLANK:
|
||||||
|
print_blank_footer()
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Method for printing footer not implemented.")
|
raise NotImplementedError("Method for printing footer not implemented.")
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class MainMenu(BaseMenu):
|
|||||||
RemoveMenu(previous_menu=self).run()
|
RemoveMenu(previous_menu=self).run()
|
||||||
|
|
||||||
def advanced_menu(self, **kwargs):
|
def advanced_menu(self, **kwargs):
|
||||||
AdvancedMenu(previous_menu=self).run()
|
AdvancedMenu().run()
|
||||||
|
|
||||||
def backup_menu(self, **kwargs):
|
def backup_menu(self, **kwargs):
|
||||||
BackupMenu(previous_menu=self).run()
|
BackupMenu(previous_menu=self).run()
|
||||||
|
|||||||
Reference in New Issue
Block a user