PFW-1522 Implement filament change screen

Change in memory:
Flash: +116 bytes
SRAM: +1 byte
This commit is contained in:
Guðni Már Gilbert 2023-05-20 18:49:37 +00:00 committed by DRracer
parent cfc8ffe8a3
commit fc18f4c5d7
7 changed files with 125 additions and 37 deletions

View File

@ -3414,18 +3414,40 @@ void gcode_M123()
} }
#endif //FANCHECK and TACH_0 or TACH_1 #endif //FANCHECK and TACH_0 or TACH_1
static void mmu_M600_wait_and_beep() { /// @brief Re-use the MMU error screen UI to present choices for filament change
// Beep and wait for user to remove old filament and prepare new filament for load /// There are two button actions, Load and Eject
KEEPALIVE_STATE(PAUSED_FOR_USER); /// Load will exit the screen and continue as normally by asking the user which slot to load from
/// Eject will eject the depleted filament, very useful after FINDA runout events.
lcd_display_message_fullscreen_P(_i("Remove old filament and press the knob to start loading new filament.")); ////MSG_REMOVE_OLD_FILAMENT c=20 r=4 /// @param eject_slot the MMU slot to eject if the user selects the Eject button choice
static void mmu_M600_filament_change_screen(uint8_t eject_slot) {
while (!lcd_clicked()) { MMU2::Buttons btn;
for(;;)
{
manage_heater(); manage_heater();
manage_inactivity(true); manage_inactivity(true);
sound_wait_for_user();
btn = MMU2::mmu2.getPrinterButtonOperation();
if (btn != MMU2::Buttons::NoButton)
{
MMU2::mmu2.clearPrinterButtonOperation();
if (btn == MMU2::Buttons::Eject) {
MMU2::mmu2.eject_filament(eject_slot, true);
// The MMU will raise FILAMENT_EJECTED screen, and ask the user to confirm
// the operation is done. We must be careful to not raise FILAMENT_CHANGE
// screen too quickly
continue;
}
else if (btn == MMU2::Buttons::Load)
{
// External caller is expected to load the filament
// This event is used to exit the endless loop
return;
}
}
MMU2::mmu2.InvokeErrorScreen(ErrorCode::FILAMENT_CHANGE);
} }
sound_wait_for_user_reset();
} }
/** /**
@ -3471,7 +3493,12 @@ static void gcode_M600(const bool automatic, const float x_position, const float
st_synchronize(); st_synchronize();
float lastpos[4]; float lastpos[4];
prusa_statistics(22); // When using an MMU, save the currently use slot number
// so the firmware can know which slot to eject after the filament
// is unloaded.
uint8_t eject_slot = 0;
prusa_statistics(22);
//First backup current position and settings //First backup current position and settings
int feedmultiplyBckp = feedmultiply; int feedmultiplyBckp = feedmultiply;
@ -3499,6 +3526,7 @@ static void gcode_M600(const bool automatic, const float x_position, const float
// Unload filament // Unload filament
if (MMU2::mmu2.Enabled()) { if (MMU2::mmu2.Enabled()) {
eject_slot = MMU2::mmu2.get_current_tool();
mmu_M600_unload_filament(); mmu_M600_unload_filament();
} else { } else {
// Beep, manage nozzle heater and wait for user to start unload filament // Beep, manage nozzle heater and wait for user to start unload filament
@ -3526,13 +3554,7 @@ static void gcode_M600(const bool automatic, const float x_position, const float
} }
else // MMU is enabled else // MMU is enabled
{ {
if (!automatic) { if (!automatic) mmu_M600_filament_change_screen(eject_slot);
if (saved_printing){
// if M600 was invoked by filament senzor (FINDA) eject filament so user can easily remove it
MMU2::mmu2.eject_filament(MMU2::mmu2.get_current_tool(), false);
}
mmu_M600_wait_and_beep();
}
mmu_M600_load_filament(automatic, HotendTempBckp); mmu_M600_load_filament(automatic, HotendTempBckp);
} }
if (!automatic) if (!automatic)
@ -10820,12 +10842,14 @@ void M600_check_state(float nozzle_temp)
{ {
// Filament failed to load so load it again // Filament failed to load so load it again
case 2: case 2:
if (MMU2::mmu2.Enabled()){ if (MMU2::mmu2.Enabled()) {
uint8_t eject_slot = MMU2::mmu2.get_current_tool();
// Unload filament // Unload filament
mmu_M600_unload_filament(); mmu_M600_unload_filament();
// Ask to remove any old filament and load new // Ask to remove any old filament and load new
mmu_M600_wait_and_beep(); mmu_M600_filament_change_screen(eject_slot);
// After user clicks knob, MMU will load the filament // After user clicks knob, MMU will load the filament
mmu_M600_load_filament(false, nozzle_temp); mmu_M600_load_filament(false, nozzle_temp);

View File

@ -721,24 +721,23 @@ void MMU2::CheckUserInput() {
lastButton = Buttons::NoButton; // Clear it. lastButton = Buttons::NoButton; // Clear it.
} }
if (mmu2.MMULastErrorSource() == MMU2::ErrorSourcePrinter && btn != Buttons::NoButton)
{
// When the printer has raised an error screen, and a button was selected
// the error screen should always be dismissed.
ClearPrinterError();
// A horrible hack - clear the explicit printer error allowing manage_response to recover on MMU's Finished state
// Moreover - if the MMU is currently doing something (like the LoadFilament - see comment above)
// we'll actually wait for it automagically in manage_response and after it finishes correctly,
// we'll issue another command (like toolchange)
}
switch (btn) { switch (btn) {
case Left: case Left:
case Middle: case Middle:
case Right: case Right:
SERIAL_ECHOPGM("CheckUserInput-btnLMR "); SERIAL_ECHOPGM("CheckUserInput-btnLMR ");
SERIAL_ECHOLN(btn); SERIAL_ECHOLN(btn);
// clear the explicit printer error as soon as possible so that the MMU error screens + reporting doesn't get too confused
if (lastErrorCode == ErrorCode::LOAD_TO_EXTRUDER_FAILED) {
// A horrible hack - clear the explicit printer error allowing manage_response to recover on MMU's Finished state
// Moreover - if the MMU is currently doing something (like the LoadFilament - see comment above)
// we'll actually wait for it automagically in manage_response and after it finishes correctly,
// we'll issue another command (like toolchange)
logic.ClearPrinterError();
lastErrorCode = ErrorCode::OK;
lastErrorSource = ErrorSourceNone; // this seems to help clearing the error screen
}
ResumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else... ResumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else...
if (mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) { if (mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) {
@ -761,6 +760,11 @@ void MMU2::CheckUserInput() {
case TuneMMU: case TuneMMU:
Tune(); Tune();
break; break;
case Load:
case Eject:
// High level operation
setPrinterButtonOperation(btn);
break;
case ResetMMU: case ResetMMU:
Reset(ResetPin); // we cannot do power cycle on the MK3 Reset(ResetPin); // we cannot do power cycle on the MK3
// ... but mmu2_power.cpp knows this and triggers a soft-reset instead. // ... but mmu2_power.cpp knows this and triggers a soft-reset instead.

View File

@ -199,6 +199,37 @@ public:
inline uint16_t GetLastReadRegisterValue() const { inline uint16_t GetLastReadRegisterValue() const {
return lastReadRegisterValue; return lastReadRegisterValue;
}; };
inline void InvokeErrorScreen(ErrorCode ec) {
// The printer may not raise an error when the MMU is busy
if ( !logic.CommandInProgress() // MMU must not be busy
&& MMUCurrentErrorCode() == ErrorCode::OK // The protocol must not be in error state
&& lastErrorCode != ec) // The error code is not a duplicate
{
ReportError(ec, ErrorSource::ErrorSourcePrinter);
}
}
void ClearPrinterError() {
logic.ClearPrinterError();
lastErrorCode = ErrorCode::OK;
lastErrorSource = ErrorSource::ErrorSourceNone;
}
/// @brief Queue a button operation which the printer can act upon
/// @param btn Button operation
inline void setPrinterButtonOperation(Buttons btn) {
printerButtonOperation = btn;
}
/// @brief Get the printer button operation
/// @return currently set printer button operation, it can be NoButton if nothing is queued
inline Buttons getPrinterButtonOperation() {
return printerButtonOperation;
}
inline void clearPrinterButtonOperation() {
printerButtonOperation = Buttons::NoButton;
}
private: private:
/// Perform software self-reset of the MMU (sends an X0 command) /// Perform software self-reset of the MMU (sends an X0 command)
@ -308,6 +339,7 @@ private:
ErrorSource lastErrorSource = ErrorSource::ErrorSourceNone; ErrorSource lastErrorSource = ErrorSource::ErrorSourceNone;
Buttons lastButton = Buttons::NoButton; Buttons lastButton = Buttons::NoButton;
uint16_t lastReadRegisterValue = 0; uint16_t lastReadRegisterValue = 0;
Buttons printerButtonOperation = Buttons::NoButton;
StepStatus logicStepLastStatus; StepStatus logicStepLastStatus;

View File

@ -13,11 +13,13 @@ enum class ButtonOperations : uint8_t {
NoOperation = 0, NoOperation = 0,
Retry = 1, Retry = 1,
Continue = 2, Continue = 2,
ResetMMU = 3, ResetMMU = 3,
Unload = 4, Unload = 4,
StopPrint = 5, Load = 5,
DisableMMU = 6, Eject = 6,
Tune = 7, Tune = 7,
StopPrint = 8,
DisableMMU = 9,
}; };
/// Button codes + extended actions performed on the printer's side /// Button codes + extended actions performed on the printer's side
@ -28,6 +30,8 @@ enum Buttons : uint8_t {
// performed on the printer's side // performed on the printer's side
ResetMMU, ResetMMU,
Load,
Eject,
StopPrint, StopPrint,
DisableMMU, DisableMMU,
TuneMMU, // Printer changes MMU register value TuneMMU, // Printer changes MMU register value

View File

@ -58,7 +58,8 @@ enum class ErrorCode : uint_fast16_t {
MCU_UNDERVOLTAGE_VCC = 0x800d, ///< MCU VCC rail undervoltage. MCU_UNDERVOLTAGE_VCC = 0x800d, ///< MCU VCC rail undervoltage.
LOAD_TO_EXTRUDER_FAILED = 0x802a, ///< E32811 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle FILAMENT_CHANGE = 0x8029, ///< E32809 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle
LOAD_TO_EXTRUDER_FAILED = 0x802a, ///< E32810 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle
QUEUE_FULL = 0x802b, ///< E32811 internal logic error - attempt to move with a full queue QUEUE_FULL = 0x802b, ///< E32811 internal logic error - attempt to move with a full queue
VERSION_MISMATCH = 0x802c, ///< E32812 internal error of the printer - incompatible version of the MMU FW VERSION_MISMATCH = 0x802c, ///< E32812 internal error of the printer - incompatible version of the MMU FW
PROTOCOL_ERROR = 0x802d, ///< E32813 internal error of the printer - communication with the MMU got garbled - protocol decoder couldn't decode the incoming messages PROTOCOL_ERROR = 0x802d, ///< E32813 internal error of the printer - communication with the MMU got garbled - protocol decoder couldn't decode the incoming messages

View File

@ -77,6 +77,7 @@ typedef enum : uint16_t {
ERR_SYSTEM_FW_RUNTIME_ERROR = 505, ERR_SYSTEM_FW_RUNTIME_ERROR = 505,
ERR_SYSTEM_UNLOAD_MANUALLY = 506, ERR_SYSTEM_UNLOAD_MANUALLY = 506,
ERR_SYSTEM_FILAMENT_EJECTED = 507, ERR_SYSTEM_FILAMENT_EJECTED = 507,
ERR_SYSTEM_FILAMENT_CHANGE = 508,
ERR_OTHER_UNKNOWN_ERROR = 900 ERR_OTHER_UNKNOWN_ERROR = 900
} err_num_t; } err_num_t;
@ -129,6 +130,7 @@ static const constexpr uint16_t errorCodes[] PROGMEM = {
ERR_SYSTEM_FW_RUNTIME_ERROR, ERR_SYSTEM_FW_RUNTIME_ERROR,
ERR_SYSTEM_UNLOAD_MANUALLY, ERR_SYSTEM_UNLOAD_MANUALLY,
ERR_SYSTEM_FILAMENT_EJECTED, ERR_SYSTEM_FILAMENT_EJECTED,
ERR_SYSTEM_FILAMENT_CHANGE,
ERR_OTHER_UNKNOWN_ERROR ERR_OTHER_UNKNOWN_ERROR
}; };
@ -183,6 +185,7 @@ static const char MSG_TITLE_FW_UPDATE_NEEDED[] PROGMEM_I1 = ISTR("MMU FW
static const char MSG_TITLE_FW_RUNTIME_ERROR[] PROGMEM_I1 = ISTR("FW RUNTIME ERROR"); ////MSG_TITLE_FW_RUNTIME_ERROR c=20 static const char MSG_TITLE_FW_RUNTIME_ERROR[] PROGMEM_I1 = ISTR("FW RUNTIME ERROR"); ////MSG_TITLE_FW_RUNTIME_ERROR c=20
static const char MSG_TITLE_UNLOAD_MANUALLY[] PROGMEM_I1 = ISTR("UNLOAD MANUALLY"); ////MSG_TITLE_UNLOAD_MANUALLY c=20 static const char MSG_TITLE_UNLOAD_MANUALLY[] PROGMEM_I1 = ISTR("UNLOAD MANUALLY"); ////MSG_TITLE_UNLOAD_MANUALLY c=20
static const char MSG_TITLE_FILAMENT_EJECTED[] PROGMEM_I1 = ISTR("FILAMENT EJECTED"); ////MSG_TITLE_FILAMENT_EJECTED c=20 static const char MSG_TITLE_FILAMENT_EJECTED[] PROGMEM_I1 = ISTR("FILAMENT EJECTED"); ////MSG_TITLE_FILAMENT_EJECTED c=20
static const char MSG_TITLE_FILAMENT_CHANGE[] PROGMEM_I1 = ISTR("FILAMENT CHANGE"); ////MSG_TITLE_FILAMENT_CHANGE c=20
static const char MSG_TITLE_UNKNOWN_ERROR[] PROGMEM_I1 = ISTR("UNKNOWN ERROR"); ////MSG_TITLE_UNKNOWN_ERROR c=20 static const char MSG_TITLE_UNKNOWN_ERROR[] PROGMEM_I1 = ISTR("UNKNOWN ERROR"); ////MSG_TITLE_UNKNOWN_ERROR c=20
static const char * const errorTitles [] PROGMEM = { static const char * const errorTitles [] PROGMEM = {
@ -229,6 +232,7 @@ static const char * const errorTitles [] PROGMEM = {
_R(MSG_TITLE_FW_RUNTIME_ERROR), _R(MSG_TITLE_FW_RUNTIME_ERROR),
_R(MSG_TITLE_UNLOAD_MANUALLY), _R(MSG_TITLE_UNLOAD_MANUALLY),
_R(MSG_TITLE_FILAMENT_EJECTED), _R(MSG_TITLE_FILAMENT_EJECTED),
_R(MSG_TITLE_FILAMENT_CHANGE),
_R(MSG_TITLE_UNKNOWN_ERROR) _R(MSG_TITLE_UNKNOWN_ERROR)
}; };
@ -278,6 +282,7 @@ static const char MSG_DESC_QUEUE_FULL[] PROGMEM_I1 = ISTR("MMU Firmware internal
static const char MSG_DESC_FW_RUNTIME_ERROR[] PROGMEM_I1 = ISTR("Internal runtime error. Try resetting the MMU or updating the firmware."); ////MSG_DESC_FW_RUNTIME_ERROR c=20 r=8 static const char MSG_DESC_FW_RUNTIME_ERROR[] PROGMEM_I1 = ISTR("Internal runtime error. Try resetting the MMU or updating the firmware."); ////MSG_DESC_FW_RUNTIME_ERROR c=20 r=8
static const char MSG_DESC_UNLOAD_MANUALLY[] PROGMEM_I1 = ISTR("Filament detected unexpectedly. Ensure no filament is loaded. Check the sensors and wiring."); ////MSG_DESC_UNLOAD_MANUALLY c=20 r=8 static const char MSG_DESC_UNLOAD_MANUALLY[] PROGMEM_I1 = ISTR("Filament detected unexpectedly. Ensure no filament is loaded. Check the sensors and wiring."); ////MSG_DESC_UNLOAD_MANUALLY c=20 r=8
static const char MSG_DESC_FILAMENT_EJECTED[] PROGMEM_I1 = ISTR("Remove the ejected filament from the front of the MMU."); ////MSG_DESC_FILAMENT_EJECTED c=20 r=8 static const char MSG_DESC_FILAMENT_EJECTED[] PROGMEM_I1 = ISTR("Remove the ejected filament from the front of the MMU."); ////MSG_DESC_FILAMENT_EJECTED c=20 r=8
static const char MSG_DESC_FILAMENT_CHANGE[] PROGMEM_I1 = ISTR("Printer is running M600 command"); ////MSG_DESC_FILAMENT_CHANGE c=20 r=8
static const char MSG_DESC_UNKNOWN_ERROR[] PROGMEM_I1 = ISTR("Unexpected error occurred."); ////MSG_DESC_UNKNOWN_ERROR c=20 r=8 static const char MSG_DESC_UNKNOWN_ERROR[] PROGMEM_I1 = ISTR("Unexpected error occurred."); ////MSG_DESC_UNKNOWN_ERROR c=20 r=8
// Read explanation in mmu2_protocol_logic.cpp -> supportedMmuFWVersion // Read explanation in mmu2_protocol_logic.cpp -> supportedMmuFWVersion
@ -332,6 +337,7 @@ static const char * const errorDescs[] PROGMEM = {
_R(MSG_DESC_FW_RUNTIME_ERROR), _R(MSG_DESC_FW_RUNTIME_ERROR),
_R(MSG_DESC_UNLOAD_MANUALLY), _R(MSG_DESC_UNLOAD_MANUALLY),
_R(MSG_DESC_FILAMENT_EJECTED), _R(MSG_DESC_FILAMENT_EJECTED),
_R(MSG_DESC_FILAMENT_CHANGE),
_R(MSG_DESC_UNKNOWN_ERROR) _R(MSG_DESC_UNKNOWN_ERROR)
}; };
@ -347,6 +353,8 @@ static const char MSG_BTN_RETRY[] PROGMEM_I1 = ISTR("Retry"); ////MSG_BTN_RETRY
static const char MSG_BTN_CONTINUE[] PROGMEM_I1 = ISTR("Done"); ////MSG_BTN_CONTINUE c=8 static const char MSG_BTN_CONTINUE[] PROGMEM_I1 = ISTR("Done"); ////MSG_BTN_CONTINUE c=8
static const char MSG_BTN_RESET_MMU[] PROGMEM_I1 = ISTR("ResetMMU"); ////MSG_BTN_RESET_MMU c=8 static const char MSG_BTN_RESET_MMU[] PROGMEM_I1 = ISTR("ResetMMU"); ////MSG_BTN_RESET_MMU c=8
static const char MSG_BTN_UNLOAD[] PROGMEM_I1 = ISTR("Unload"); ////MSG_BTN_UNLOAD c=8 static const char MSG_BTN_UNLOAD[] PROGMEM_I1 = ISTR("Unload"); ////MSG_BTN_UNLOAD c=8
static const char MSG_BTN_LOAD[] PROGMEM_I1 = ISTR("Load"); ////MSG_BTN_LOAD c=8
static const char MSG_BTN_EJECT[] PROGMEM_I1 = ISTR("Eject"); ////MSG_BTN_EJECT c=8
static const char MSG_BTN_STOP[] PROGMEM_I1 = ISTR("Stop"); ////MSG_BTN_STOP c=8 static const char MSG_BTN_STOP[] PROGMEM_I1 = ISTR("Stop"); ////MSG_BTN_STOP c=8
static const char MSG_BTN_DISABLE_MMU[] PROGMEM_I1 = ISTR("Disable"); ////MSG_BTN_DISABLE_MMU c=8 static const char MSG_BTN_DISABLE_MMU[] PROGMEM_I1 = ISTR("Disable"); ////MSG_BTN_DISABLE_MMU c=8
static const char MSG_BTN_TUNE_MMU[] PROGMEM_I1 = ISTR("Tune"); ////MSG_BTN_TUNE_MMU c=8 static const char MSG_BTN_TUNE_MMU[] PROGMEM_I1 = ISTR("Tune"); ////MSG_BTN_TUNE_MMU c=8
@ -358,6 +366,8 @@ static const char * const btnOperation[] PROGMEM = {
_R(MSG_BTN_CONTINUE), _R(MSG_BTN_CONTINUE),
_R(MSG_BTN_RESET_MMU), _R(MSG_BTN_RESET_MMU),
_R(MSG_BTN_UNLOAD), _R(MSG_BTN_UNLOAD),
_R(MSG_BTN_LOAD),
_R(MSG_BTN_EJECT),
_R(MSG_BTN_STOP), _R(MSG_BTN_STOP),
_R(MSG_BTN_DISABLE_MMU), _R(MSG_BTN_DISABLE_MMU),
_R(MSG_BTN_TUNE_MMU), _R(MSG_BTN_TUNE_MMU),
@ -418,6 +428,7 @@ static const uint8_t errorButtons[] PROGMEM = {
Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation),//FW_RUNTIME_ERROR Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation),//FW_RUNTIME_ERROR
Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//UNLOAD_MANUALLY Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//UNLOAD_MANUALLY
Btns(ButtonOperations::Continue, ButtonOperations::NoOperation),//FILAMENT_EJECTED Btns(ButtonOperations::Continue, ButtonOperations::NoOperation),//FILAMENT_EJECTED
Btns(ButtonOperations::Load, ButtonOperations::Eject),//FILAMENT_CHANGE
Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation),//UNKOWN_ERROR Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation),//UNKOWN_ERROR
}; };

View File

@ -53,6 +53,8 @@ uint8_t PrusaErrorCodeIndex(uint16_t ec) {
return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED); return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED);
case (uint16_t)ErrorCode::FILAMENT_EJECTED: case (uint16_t)ErrorCode::FILAMENT_EJECTED:
return FindErrorIndex(ERR_SYSTEM_FILAMENT_EJECTED); return FindErrorIndex(ERR_SYSTEM_FILAMENT_EJECTED);
case (uint16_t)ErrorCode::FILAMENT_CHANGE:
return FindErrorIndex(ERR_SYSTEM_FILAMENT_CHANGE);
case (uint16_t)ErrorCode::STALLED_PULLEY: case (uint16_t)ErrorCode::STALLED_PULLEY:
case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED: case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED:
@ -238,6 +240,16 @@ Buttons ButtonAvailable(uint16_t ec) {
break; break;
} }
break; break;
case ERR_SYSTEM_FILAMENT_CHANGE:
switch (buttonSelectedOperation) {
case ButtonOperations::Load:
return Load;
case ButtonOperations::Eject:
return Eject;
default:
break;
}
break;
case ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT: case ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT:
case ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT: case ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT:
case ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT: case ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT: