🎨 MMU3 cleanup

This commit is contained in:
Scott Lahteine
2025-12-15 21:58:28 -06:00
parent 3ba633a9e2
commit 53c27cdd66
8 changed files with 198 additions and 239 deletions
-1
View File
@@ -38,7 +38,6 @@
#include "mmu3_progress_converter.h"
#include "mmu3_reporting.h"
#include "strlen_cx.h"
#include "SpoolJoin.h"
#include "../../inc/MarlinConfig.h"
+18 -18
View File
@@ -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
+4 -8
View File
@@ -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();
}
+11 -10
View File
@@ -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);
@@ -37,7 +37,6 @@
#include <avr/pgmspace.h>
#endif
#include "buttons.h"
#include "../strlen_cx.h"
#include "../ultralcd.h"
namespace MMU3 {
-30
View File
@@ -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 <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* strlen_cx.h
*/
constexpr inline int strlen_constexpr(const char *str) {
return *str ? 1 + strlen_constexpr(str + 1) : 0;
}
+160 -166
View File
@@ -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
+5 -5
View File
@@ -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);