From 53c27cdd66f7b0b1d3ceeb784f76a6c011344546 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Mon, 15 Dec 2025 21:58:28 -0600 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20MMU3=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/src/feature/mmu3/mmu3.cpp | 1 - Marlin/src/feature/mmu3/mmu3_power.cpp | 36 +- Marlin/src/feature/mmu3/mmu3_power.h | 12 +- Marlin/src/feature/mmu3/mmu3_reporting.cpp | 21 +- Marlin/src/feature/mmu3/mmu_hw/errors_list.h | 1 - Marlin/src/feature/mmu3/strlen_cx.h | 30 -- Marlin/src/feature/mmu3/ultralcd.cpp | 326 +++++++++---------- Marlin/src/feature/mmu3/ultralcd.h | 10 +- 8 files changed, 198 insertions(+), 239 deletions(-) delete mode 100644 Marlin/src/feature/mmu3/strlen_cx.h diff --git a/Marlin/src/feature/mmu3/mmu3.cpp b/Marlin/src/feature/mmu3/mmu3.cpp index 9727c07829..dd06126724 100644 --- a/Marlin/src/feature/mmu3/mmu3.cpp +++ b/Marlin/src/feature/mmu3/mmu3.cpp @@ -38,7 +38,6 @@ #include "mmu3_progress_converter.h" #include "mmu3_reporting.h" -#include "strlen_cx.h" #include "SpoolJoin.h" #include "../../inc/MarlinConfig.h" diff --git a/Marlin/src/feature/mmu3/mmu3_power.cpp b/Marlin/src/feature/mmu3/mmu3_power.cpp index da905c6e29..1782cb49fc 100644 --- a/Marlin/src/feature/mmu3/mmu3_power.cpp +++ b/Marlin/src/feature/mmu3/mmu3_power.cpp @@ -39,26 +39,26 @@ namespace MMU3 { -// On MK3 we cannot do actual power cycle on HW. Instead trigger a hardware reset. -void power_on() { - #if PIN_EXISTS(MMU_RST) - OUT_WRITE(MMU_RST_PIN, HIGH); - #endif - power_reset(); -} + // On MK3 we cannot do actual power cycle on HW. Instead trigger a hardware reset. + void power_on() { + #if PIN_EXISTS(MMU_RST) + OUT_WRITE(MMU_RST_PIN, HIGH); + #endif + power_reset(); + } -void power_off() {} + void power_off() {} -void power_reset() { - #if PIN_EXISTS(MMU_RST) // HW - pulse reset pin - WRITE(MMU_RST_PIN, LOW); - safe_delay(100); - WRITE(MMU_RST_PIN, HIGH); - #else - mmu3.reset(MMU3::Software); // TODO: Needs redesign. This power implementation shouldn't know anything about the MMU itself - #endif - // otherwise HW reset is not available -} + void power_reset() { + #if PIN_EXISTS(MMU_RST) // HW - pulse reset pin + WRITE(MMU_RST_PIN, LOW); + safe_delay(100); + WRITE(MMU_RST_PIN, HIGH); + #else + mmu3.reset(MMU3::Software); // TODO: Needs redesign. This power implementation shouldn't know anything about the MMU itself + #endif + // otherwise HW reset is not available + } } // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_power.h b/Marlin/src/feature/mmu3/mmu3_power.h index 4f6b94f01e..77259073a1 100644 --- a/Marlin/src/feature/mmu3/mmu3_power.h +++ b/Marlin/src/feature/mmu3/mmu3_power.h @@ -26,11 +26,7 @@ */ namespace MMU3 { - -void power_on(); - -void power_off(); - -void power_reset(); - -} // MMU3 + void power_on(); + void power_off(); + void power_reset(); +} diff --git a/Marlin/src/feature/mmu3/mmu3_reporting.cpp b/Marlin/src/feature/mmu3/mmu3_reporting.cpp index b0527937ea..c9b9dbe554 100644 --- a/Marlin/src/feature/mmu3/mmu3_reporting.cpp +++ b/Marlin/src/feature/mmu3/mmu3_reporting.cpp @@ -353,17 +353,18 @@ namespace MMU3 { // Render the choices //if (two_choices) { // lcd_show_choices_prompt_P( - // LCD_LEFT_BUTTON_CHOICE, - // PrusaErrorButtonTitle(button_op_middle), - // GET_TEXT(MSG_BTN_MORE), - // 18, nullptr + // LCD_LEFT_BUTTON_CHOICE, + // PrusaErrorButtonTitle(button_op_middle), + // GET_TEXT(MSG_BTN_MORE), + // 18, nullptr // ); //} //else { - // lcd_show_choices_prompt_P(LCD_MIDDLE_BUTTON_CHOICE, - // PrusaErrorButtonTitle(button_op_middle), - // PrusaErrorButtonTitle(button_op_right), - // 9, GET_TEXT(MSG_BTN_MORE) + // lcd_show_choices_prompt_P( + // LCD_MIDDLE_BUTTON_CHOICE, + // PrusaErrorButtonTitle(button_op_middle), + // PrusaErrorButtonTitle(button_op_right), + // 9, GET_TEXT(MSG_BTN_MORE) // ); //} @@ -526,7 +527,7 @@ namespace MMU3 { #if HAS_WIRED_LCD // Set the cursor position each time in case some other // part of the firmware changes the cursor position - lcd_insert_char_into_status(col, sensorState ? LCD_STR_SOLID_BLOCK[0] : '-'); + lcd_replace_status_char(col, sensorState ? LCD_STR_SOLID_BLOCK[0] : '-'); if (ui.lcdDrawUpdate == LCDViewAction::LCDVIEW_NONE) ui.draw_status_message(false); #endif @@ -535,7 +536,7 @@ namespace MMU3 { void TryLoadUnloadReporter::Progress(bool sensorState) { // Always round up, you can only have 'whole' pixels. (floor is also an option) dpixel1 = ceil((stepper_get_machine_position_E_mm() - planner_get_current_position_E()) * pixel_per_mm); - if (dpixel1 - dpixel0) { + if (dpixel1 != dpixel0) { dpixel0 = dpixel1; if (lcd_cursor_col > (LCD_WIDTH - 1)) lcd_cursor_col = LCD_WIDTH - 1; Render(lcd_cursor_col++, sensorState); diff --git a/Marlin/src/feature/mmu3/mmu_hw/errors_list.h b/Marlin/src/feature/mmu3/mmu_hw/errors_list.h index 7fb93b0771..9cfdba2283 100644 --- a/Marlin/src/feature/mmu3/mmu_hw/errors_list.h +++ b/Marlin/src/feature/mmu3/mmu_hw/errors_list.h @@ -37,7 +37,6 @@ #include #endif #include "buttons.h" -#include "../strlen_cx.h" #include "../ultralcd.h" namespace MMU3 { diff --git a/Marlin/src/feature/mmu3/strlen_cx.h b/Marlin/src/feature/mmu3/strlen_cx.h deleted file mode 100644 index 6ac2a84b42..0000000000 --- a/Marlin/src/feature/mmu3/strlen_cx.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] - * - * Based on Sprinter and grbl. - * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -#pragma once - -/** - * strlen_cx.h - */ - -constexpr inline int strlen_constexpr(const char *str) { - return *str ? 1 + strlen_constexpr(str + 1) : 0; -} diff --git a/Marlin/src/feature/mmu3/ultralcd.cpp b/Marlin/src/feature/mmu3/ultralcd.cpp index 3b8f52beb3..d3cd3d371e 100644 --- a/Marlin/src/feature/mmu3/ultralcd.cpp +++ b/Marlin/src/feature/mmu3/ultralcd.cpp @@ -37,181 +37,175 @@ #include "../../gcode/gcode.h" #include "../../lcd/marlinui.h" - //! @brief Show a two-choice prompt on the last line of the LCD - //! @param selected Show first choice as selected if true, the second otherwise - //! @param first_choice text caption of first possible choice - //! @param second_choice text caption of second possible choice - //! @param second_col column on LCD where second choice is rendered. - //! @param third_choice text caption of third, optional, choice. - void lcd_show_choices_prompt_P(uint8_t selected, const char *first_choice, const char *second_choice, uint8_t second_col, const char *third_choice) { - lcd_put_lchar(0, 3, selected == LCD_LEFT_BUTTON_CHOICE ? '>' : ' '); - lcd_put_u8str(first_choice); - lcd_put_lchar(second_col, 3, selected == LCD_MIDDLE_BUTTON_CHOICE ? '>' : ' '); - lcd_put_u8str(second_choice); - if (third_choice) { - lcd_put_lchar(18, 3, selected == LCD_RIGHT_BUTTON_CHOICE ? '>' : ' '); - lcd_put_u8str(third_choice); +//! @brief Show a two-choice prompt on the last line of the LCD +//! @param selected Show first choice as selected if true, the second otherwise +//! @param first_choice text caption of first possible choice +//! @param second_choice text caption of second possible choice +//! @param second_col column on LCD where second choice is rendered. +//! @param third_choice text caption of third, optional, choice. +void lcd_show_choices_prompt_P(uint8_t selected, const char *first_choice, const char *second_choice, uint8_t second_col, const char *third_choice) { + lcd_put_lchar(0, 3, selected == LCD_LEFT_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(first_choice); + lcd_put_lchar(second_col, 3, selected == LCD_MIDDLE_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(second_choice); + if (third_choice) { + lcd_put_lchar(18, 3, selected == LCD_RIGHT_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(third_choice); + } +} + +void lcd_space(uint8_t n) { + while (n--) lcd_put_lchar(' '); +} + +// Print extruder status (5 chars total) +// Scenario 1: "F?" +// There is no filament loaded and no tool change is in progress +// Scenario 2: "F[nr.]" +// [nr.] ranges from 1 to 5. +// Shows which filament is loaded. No tool change is in progress +// Scenario 3: "?>[nr.]" +// [nr.] ranges from 1 to 5. +// There is no filament currently loaded, but [nr.] is currently being loaded via tool change +// Scenario 4: "[nr.]>?" +// [nr.] ranges from 1 to 5. +// This scenario indicates a bug in the firmware if ? is on the right side +// Scenario 5: "[nr1.]>[nr2.]" +// [nr1.] ranges from 1 to 5. +// [nr2.] ranges from 1 to 5. +// Filament [nr1.] was loaded, but [nr2.] is currently being loaded via tool change +// Scenario 6: "?>?" +// This scenario should not be possible and indicates a bug in the firmware +uint8_t lcdui_print_extruder(void) { + uint8_t chars = 1; + lcd_space(1); + if (mmu3.get_current_tool() == mmu3.get_tool_change_tool()) { + lcd_put_lchar('F'); + lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); + chars += 2; + } + else { + lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); + lcd_put_lchar('>'); + lcd_put_lchar(mmu3.get_tool_change_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_tool_change_tool() + '1'); + chars += 3; + } + return chars; +} + +bool is_whitespace_P(const char *c_addr) { + const char c = pgm_read_byte(c_addr); + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +bool is_punctuation_P(const char *c_addr) { + const char c = pgm_read_byte(c_addr); + return c == '.' || c == ',' || c == ':' || c == ';' || c == '?' || c == '!' || c == '/'; +} + +/** + * @brief show full screen message + * + * This function is non-blocking + * @param msg message to be displayed from PROGMEM + * @return rest of the text (to be displayed on next page) + */ +static FSTR_P const lcd_display_message_fullscreen_async(FSTR_P const fmsg) { + PGM_P msg = FTOP(fmsg); + PGM_P msgend = msg; + //bool multi_screen = false; + for (uint8_t row = 0; row < LCD_HEIGHT; ++row) { + if (pgm_read_byte(msgend) == 0) break; + SETCURSOR(0, row); + + // Previous row ended with a complete word, so the first character in the + // next row is a whitespace. We can skip the whitespace on a new line. + if (is_whitespace_P(msg) && ++msg == nullptr) break; // End of the message. + + uint8_t linelen = (strlen_P(msg) > LCD_WIDTH) ? LCD_WIDTH : strlen_P(msg); + PGM_P const msgend2 = msg + linelen; + msgend = msgend2; + if (row == 3 && linelen == LCD_WIDTH) { + // Last line of the display, full line should be displayed. + // Find out, whether this message will be split into multiple screens. + //multi_screen = pgm_read_byte(msgend) != 0; + // We do not need this... + //if (multi_screen) msgend = (msgend2 -= 2); } - } - - void lcd_space(uint8_t n) { - while (n--) lcd_put_lchar(' '); - } - - // Print extruder status (5 chars total) - // Scenario 1: "F?" - // There is no filament loaded and no tool change is in progress - // Scenario 2: "F[nr.]" - // [nr.] ranges from 1 to 5. - // Shows which filament is loaded. No tool change is in progress - // Scenario 3: "?>[nr.]" - // [nr.] ranges from 1 to 5. - // There is no filament currently loaded, but [nr.] is currently being loaded via tool change - // Scenario 4: "[nr.]>?" - // [nr.] ranges from 1 to 5. - // This scenario indicates a bug in the firmware if ? is on the right side - // Scenario 5: "[nr1.]>[nr2.]" - // [nr1.] ranges from 1 to 5. - // [nr2.] ranges from 1 to 5. - // Filament [nr1.] was loaded, but [nr2.] is currently being loaded via tool change - // Scenario 6: "?>?" - // This scenario should not be possible and indicates a bug in the firmware - uint8_t lcdui_print_extruder(void) { - uint8_t chars = 1; - lcd_space(1); - if (mmu3.get_current_tool() == mmu3.get_tool_change_tool()) { - lcd_put_lchar('F'); - lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); - chars += 2; + if (pgm_read_byte(msgend) != 0 && !is_whitespace_P(msgend) && !is_punctuation_P(msgend)) { + // Splitting a word. Find the start of the current word. + while (msgend > msg && !is_whitespace_P(msgend - 1)) --msgend; + if (msgend == msg) msgend = msgend2; // Found a single long word, which cannot be split. Just cut it. } - else { - lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); - lcd_put_lchar('>'); - lcd_put_lchar(mmu3.get_tool_change_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_tool_change_tool() + '1'); - chars += 3; - } - return chars; - } - - bool pgm_is_whitespace(const char *c_addr) { - const char c = pgm_read_byte(c_addr); - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; - } - - bool pgm_is_interpunction(const char *c_addr) { - const char c = pgm_read_byte(c_addr); - return c == '.' || c == ',' || c == ':' || c == ';' || c == '?' || c == '!' || c == '/'; - } - - /** - * @brief show full screen message - * - * This function is non-blocking - * @param msg message to be displayed from PROGMEM - * @return rest of the text (to be displayed on next page) - */ - static FSTR_P const lcd_display_message_fullscreen_nonBlocking(FSTR_P const fmsg) { - PGM_P msg = FTOP(fmsg); - PGM_P msgend = msg; - //bool multi_screen = false; - for (uint8_t row = 0; row < LCD_HEIGHT; ++row) { - if (pgm_read_byte(msgend) == 0) break; - SETCURSOR(0, row); - - // Previous row ended with a complete word, so the first character in the - // next row is a whitespace. We can skip the whitespace on a new line. - if (pgm_is_whitespace(msg) && ++msg == nullptr) break; // End of the message. - - uint8_t linelen = (strlen_P(msg) > LCD_WIDTH) ? LCD_WIDTH : strlen_P(msg); - PGM_P const msgend2 = msg + linelen; - msgend = msgend2; - if (row == 3 && linelen == LCD_WIDTH) { - // Last line of the display, full line should be displayed. - // Find out, whether this message will be split into multiple screens. - //multi_screen = pgm_read_byte(msgend) != 0; - // We do not need this... - //if (multi_screen) msgend = (msgend2 -= 2); + for (; msg < msgend; ++msg) { + const char c = char(pgm_read_byte(msg)); + if (c == '\n') { + // Abort early if '\n' is encountered. + // This character is used to force the following words to be printed on the next line. + break; } - if (pgm_read_byte(msgend) != 0 && !pgm_is_whitespace(msgend) && !pgm_is_interpunction(msgend)) { - // Splitting a word. Find the start of the current word. - while (msgend > msg && !pgm_is_whitespace(msgend - 1)) --msgend; - if (msgend == msg) msgend = msgend2; // Found a single long word, which cannot be split. Just cut it. - } - for (; msg < msgend; ++msg) { - const char c = char(pgm_read_byte(msg)); - if (c == '\n') { - // Abort early if '\n' is encountered. - // This character is used to force the following words to be printed on the next line. - break; + lcd_put_lchar(c); + } + } + return FPSTR(msgend); +} + +FSTR_P const lcd_display_message_fullscreen(FSTR_P const fmsg) { + // Disable update of the screen by the usual lcd_update(0) routine. + #if HAS_WIRED_LCD + //ui.lcdDrawUpdate = LCDViewAction::LCDVIEW_NONE; + ui.clear_lcd(); + return lcd_display_message_fullscreen_async(fmsg); + #else + return fmsg + #endif +} + +/** + * @brief show full screen message and wait + * + * This function is blocking. + * @param msg message to be displayed from PROGMEM + */ +void lcd_show_fullscreen_message_and_wait(FSTR_P const fmsg) { + LcdUpdateDisabler lcdUpdateDisabler; + FSTR_P fmsg_next = lcd_display_message_fullscreen(fmsg); + const bool multi_screen = fmsg_next != nullptr; + ui.use_click(); + KEEPALIVE_STATE(PAUSED_FOR_USER); + // Until confirmed by a button click. + for (;;) { + if (fmsg_next == nullptr) { + // Display the confirm char. + //lcd_put_lchar(LCD_WIDTH - 2, LCD_HEIGHT - 2, LCD_STR_CONFIRM[0]); + } + // Wait for 5 seconds before displaying the next text. + for (uint8_t i = 0; i < 100; ++i) { + marlin.idle(true); + safe_delay(50); + if (ui.use_click()) { + if (fmsg_next == nullptr) { + KEEPALIVE_STATE(IN_HANDLER); + return ui.go_back(); } - lcd_put_lchar(c); + if (!multi_screen) break; + if (fmsg_next == nullptr) fmsg_next = fmsg; + fmsg_next = lcd_display_message_fullscreen(fmsg_next); } } - // We do not need this part... //if (multi_screen) { - // // Display the double down arrow. - // lcd_put_lchar(LCD_WIDTH - 2, LCD_HEIGHT - 2, LCD_STR_ARROW_2_DOWN[0]); + // if (fmsg_next == nullptr) fmsg_next = fmsg; + // fmsg_next = lcd_display_message_fullscreen(fmsg_next); //} - //return multi_screen ? msgend : nullptr; - return FPSTR(msgend); } +} - FSTR_P const lcd_display_message_fullscreen(FSTR_P const fmsg) { - // Disable update of the screen by the usual lcd_update(0) routine. - #if HAS_WIRED_LCD - //ui.lcdDrawUpdate = LCDViewAction::LCDVIEW_NONE; - ui.clear_lcd(); - return lcd_display_message_fullscreen_nonBlocking(fmsg); - #else - return fmsg - #endif - } - - /** - * @brief show full screen message and wait - * - * This function is blocking. - * @param msg message to be displayed from PROGMEM - */ - void lcd_show_fullscreen_message_and_wait(FSTR_P const fmsg) { - LcdUpdateDisabler lcdUpdateDisabler; - FSTR_P fmsg_next = lcd_display_message_fullscreen(fmsg); - const bool multi_screen = fmsg_next != nullptr; - ui.use_click(); - KEEPALIVE_STATE(PAUSED_FOR_USER); - // Until confirmed by a button click. - for (;;) { - if (fmsg_next == nullptr) { - // Display the confirm char. - //lcd_put_lchar(LCD_WIDTH - 2, LCD_HEIGHT - 2, LCD_STR_CONFIRM[0]); - } - // Wait for 5 seconds before displaying the next text. - for (uint8_t i = 0; i < 100; ++i) { - marlin.idle(true); - safe_delay(50); - if (ui.use_click()) { - if (fmsg_next == nullptr) { - KEEPALIVE_STATE(IN_HANDLER); - return ui.go_back(); - } - if (!multi_screen) break; - if (fmsg_next == nullptr) fmsg_next = fmsg; - fmsg_next = lcd_display_message_fullscreen(fmsg_next); - } - } - //if (multi_screen) { - // if (fmsg_next == nullptr) fmsg_next = fmsg; - // fmsg_next = lcd_display_message_fullscreen(fmsg_next); - //} - } - } - - void lcd_insert_char_into_status(uint8_t position, const char message) { - if (position >= LCD_WIDTH) return; - //int size = ui.status_message.length(); - char *str = ui.status_message.buffer(); - str[position] = message; - ui.refresh(LCDVIEW_REDRAW_NOW); // force redraw - } +void lcd_replace_status_char(const uint8_t position, const char message) { + if (position >= MAX_MESSAGE_SIZE) return; + //int size = ui.status_message.length(); + char *str = ui.status_message.buffer(); + str[position] = message; + ui.refresh(LCDVIEW_REDRAW_NOW); // force redraw +} #endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/ultralcd.h b/Marlin/src/feature/mmu3/ultralcd.h index 2f17b61d0a..e7f06dfb1f 100644 --- a/Marlin/src/feature/mmu3/ultralcd.h +++ b/Marlin/src/feature/mmu3/ultralcd.h @@ -48,11 +48,11 @@ class LcdUpdateDisabler { public: LcdUpdateDisabler() : m_updateEnabled(ui.lcdDrawUpdate) { - TERN_(HAS_WIRED_LCD, ui.lcdDrawUpdate = LCDViewAction::LCDVIEW_NONE); + TERN_(HAS_WIRED_LCD, ui.refresh(LCDViewAction::LCDVIEW_NONE)); } ~LcdUpdateDisabler() { #if HAS_WIRED_LCD - ui.lcdDrawUpdate = m_updateEnabled; + ui.refresh(m_updateEnabled); ui.clear_lcd(); ui.update(); #endif @@ -62,11 +62,11 @@ class LcdUpdateDisabler { LCDViewAction m_updateEnabled; }; -bool pgm_is_whitespace(const char *c_addr); -bool pgm_is_interpunction(const char *c_addr); +bool is_whitespace_P(const char *c_addr); +bool is_punctuation_P(const char *c_addr); FSTR_P const lcd_display_message_fullscreen(FSTR_P const pmsg); void lcd_show_choices_prompt_P(uint8_t selected, const char *first_choice, const char *second_choice, uint8_t second_col, const char *third_choice=nullptr); void lcd_show_fullscreen_message_and_wait(FSTR_P const fmsg); uint8_t lcdui_print_extruder(void); void lcd_space(uint8_t n); -void lcd_insert_char_into_status(uint8_t position, const char message); +void lcd_replace_status_char(uint8_t position, const char message);