diff --git a/Firmware/mmu2.cpp b/Firmware/mmu2.cpp index 5797e27a5..42d20bdf7 100644 --- a/Firmware/mmu2.cpp +++ b/Firmware/mmu2.cpp @@ -273,52 +273,52 @@ bool MMU2::VerifyFilamentEnteredPTFE() } } -void MMU2::ToolChangeCommon(uint8_t slot){ - for(;;) { // while not successfully fed into extruder's PTFE tube - uint8_t retries = 3; - for(/*nothing*/; retries; --retries){ - for(;;) { - tool_change_extruder = slot; - logic.ToolChange(slot); // let the MMU pull the filament out and push a new one in - if( manage_response(true, true) ) - break; - // otherwise: failed to perform the command - unload first and then let it run again - IncrementMMUFails(); - - // just in case we stood in an error screen for too long and the hotend got cold - ResumeHotendTemp(); - // if the extruder has been parked, it will get unparked once the ToolChange command finishes OK - // - so no ResumeUnpark() at this spot - - unload(); - // if we run out of retries, we must do something ... may be raise an error screen and allow the user to do something - // but honestly - if the MMU restarts during every toolchange, - // something else is seriously broken and stopping a print is probably our best option. - } - // reset current position to whatever the planner thinks it is - plan_set_e_position(current_position[E_AXIS]); - if (VerifyFilamentEnteredPTFE()){ +bool MMU2::ToolChangeCommonOnce(uint8_t slot){ + static_assert(MAX_RETRIES > 1); // need >1 retries to do the cut in the last attempt + for(uint8_t retries = MAX_RETRIES; retries; --retries){ + for(;;) { + tool_change_extruder = slot; + logic.ToolChange(slot); // let the MMU pull the filament out and push a new one in + if( manage_response(true, true) ) break; - } else { // Prepare a retry attempt - unload(); // @@TODO cut filament - // cut_filament(slot); + // otherwise: failed to perform the command - unload first and then let it run again + IncrementMMUFails(); + + // just in case we stood in an error screen for too long and the hotend got cold + ResumeHotendTemp(); + // if the extruder has been parked, it will get unparked once the ToolChange command finishes OK + // - so no ResumeUnpark() at this spot + + unload(); + // if we run out of retries, we must do something ... may be raise an error screen and allow the user to do something + // but honestly - if the MMU restarts during every toolchange, + // something else is seriously broken and stopping a print is probably our best option. + } + // reset current position to whatever the planner thinks it is + plan_set_e_position(current_position[E_AXIS]); + if (VerifyFilamentEnteredPTFE()){ + return true; // success + } else { // Prepare a retry attempt + unload(); + if( retries == 1 && eeprom_read_byte((uint8_t*)EEPROM_MMU_CUTTER_ENABLED) == EEPROM_MMU_CUTTER_ENABLED_enabled){ + cut_filament(slot); // try cutting filament tip at the last attempt } } - if( retries ){ - // we were successful in pushing the filament into the nozzle - break; - } else { - // failed autoretry, report an error by forcing a "printer" error into the MMU infrastructure - it is a hack to leverage existing code - logic.SetPrinterError(ErrorCode::LOAD_TO_EXTRUDER_FAILED); - SaveAndPark(true); - SaveHotendTemp(true); - // We only have to wait for the user to fix the issue and press "Retry". - // @@TODO Do we need to process the return value of manage_response? - manage_response(true, true); - logic.ClearPrinterError(); - ResumeHotendTemp(); - ResumeUnpark(); - } + } + return false; // couldn't accomplish the task +} + +void MMU2::ToolChangeCommon(uint8_t slot){ + while( ! ToolChangeCommonOnce(slot) ){ // while not successfully fed into extruder's PTFE tube + // failed autoretry, report an error by forcing a "printer" error into the MMU infrastructure - it is a hack to leverage existing code + logic.SetPrinterError(ErrorCode::LOAD_TO_EXTRUDER_FAILED); + SaveAndPark(true); + SaveHotendTemp(true); + // We only have to wait for the user to fix the issue and press "Retry". + // Please see CheckUserInput() for details how we "leave" manage_response. + // If manage_response returns false at this spot (MMU operation interrupted aka MMU reset) + // we can safely continue because the MMU is not doing an operation now. + manage_response(true, true); } extruder = slot; //filament change is finished @@ -686,6 +686,11 @@ void MMU2::CheckUserInput(){ SERIAL_ECHOPGM("CheckUserInput-btnLMR "); SERIAL_ECHOLN(btn); ResumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else... + + // In case of LOAD_TO_EXTRUDER_FAILED sending a button into the MMU has an interesting side effect + // - it triggers the standalone LoadFilament function on the current active slot. + // Considering the fact, that we are recovering from a failed load to extruder, this side effect is actually quite beneficial + // - it checks if the filament is correctly loaded in the MMU (we assume the user was playing with the filament to recover from the failed load) Button(btn); // A quick hack: for specific error codes move the E-motor every time. @@ -696,6 +701,13 @@ void MMU2::CheckUserInput(){ case ErrorCode::FSENSOR_TOO_EARLY: HelpUnloadToFinda(); break; + case 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(); + break; default: break; } @@ -767,9 +779,13 @@ bool MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { // now what :D ... big bad ... ramming, unload, retry the whole command originally issued return false; case VersionMismatch: // this basically means the MMU will be disabled until reconnected - case PrinterError: CheckUserInput(); return true; + case PrinterError: + CheckUserInput(); + // if button pressed "Done", return true, otherwise stay within manage_response + // Please see CheckUserInput() for details how we "leave" manage_response + break; case CommandError: case CommunicationTimeout: case ProtocolError: diff --git a/Firmware/mmu2.h b/Firmware/mmu2.h index cf5a1aa15..1e59c6879 100644 --- a/Firmware/mmu2.h +++ b/Firmware/mmu2.h @@ -294,6 +294,7 @@ private: /// Common processing of pushing filament into the extruder - shared by tool_change, load_to_nozzle and probably others void ToolChangeCommon(uint8_t slot); + bool ToolChangeCommonOnce(uint8_t slot); void HelpUnloadToFinda(); diff --git a/Firmware/mmu2/error_codes.h b/Firmware/mmu2/error_codes.h index 3fed76f42..4750e9cb1 100644 --- a/Firmware/mmu2/error_codes.h +++ b/Firmware/mmu2/error_codes.h @@ -54,9 +54,8 @@ enum class ErrorCode : uint_fast16_t { MOVE_IDLER_FAILED = MOVE_FAILED | TMC_IDLER_BIT, ///< E33033 the Idler was unable to move - unused at the time of creation, but added for completeness MOVE_PULLEY_FAILED = MOVE_FAILED | TMC_PULLEY_BIT, ///< E32841 the Pulley was unable to move - unused at the time of creation, but added for completeness + LOAD_TO_EXTRUDER_FAILED = 0x802a, ///< E32811 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 - - LOAD_TO_EXTRUDER_FAILED = 0x802b, ///< E32811 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle 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 MMU_NOT_RESPONDING = 0x802e, ///< E32814 internal error of the printer - communication with the MMU is not working diff --git a/Firmware/mmu2/errors_list.h b/Firmware/mmu2/errors_list.h index 059df5dfc..26f66efd4 100644 --- a/Firmware/mmu2/errors_list.h +++ b/Firmware/mmu2/errors_list.h @@ -23,6 +23,7 @@ typedef enum : uint16_t { ERR_MECHANICAL_PULLEY_CANNOT_MOVE = 105, ERR_MECHANICAL_FSENSOR_TOO_EARLY = 106, ERR_MECHANICAL_INSPECT_FINDA = 107, + ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED = 108, ERR_MECHANICAL_SELECTOR_CANNOT_HOME = 115, ERR_MECHANICAL_SELECTOR_CANNOT_MOVE = 116, ERR_MECHANICAL_IDLER_CANNOT_HOME = 125, @@ -87,6 +88,7 @@ static const constexpr uint16_t errorCodes[] PROGMEM = { ERR_MECHANICAL_PULLEY_CANNOT_MOVE, ERR_MECHANICAL_FSENSOR_TOO_EARLY, ERR_MECHANICAL_INSPECT_FINDA, + ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED, ERR_MECHANICAL_SELECTOR_CANNOT_HOME, ERR_MECHANICAL_SELECTOR_CANNOT_MOVE, ERR_MECHANICAL_IDLER_CANNOT_HOME, @@ -130,6 +132,7 @@ static const char MSG_TITLE_FSENSOR_DIDNT_GO_OFF[] PROGMEM_I1 = ISTR("FSENSOR static const char MSG_TITLE_PULLEY_CANNOT_MOVE[] PROGMEM_I1 = ISTR("PULLEY CANNOT MOVE"); ////MSG_TITLE_PULLEY_CANNOT_MOVE c=20 static const char MSG_TITLE_FSENSOR_TOO_EARLY[] PROGMEM_I1 = ISTR("FSENSOR TOO EARLY"); ////MSG_TITLE_FSENSOR_TOO_EARLY c=20 static const char MSG_TITLE_INSPECT_FINDA[] PROGMEM_I1 = ISTR("INSPECT FINDA"); ////MSG_TITLE_INSPECT_FINDA c=20 +static const char MSG_TITLE_LOAD_TO_EXTRUDER_FAILED[] PROGMEM_I1 = ISTR("LOAD TO EXTR. FAILED"); ////MSG_TITLE_LOAD_TO_EXTRUDER_FAILED c=20 static const char MSG_TITLE_SELECTOR_CANNOT_MOVE[] PROGMEM_I1 = ISTR("SELECTOR CANNOT MOVE"); ////MSG_TITLE_SELECTOR_CANNOT_MOVE c=20 static const char MSG_TITLE_SELECTOR_CANNOT_HOME[] PROGMEM_I1 = ISTR("SELECTOR CANNOT HOME"); ////MSG_TITLE_SELECTOR_CANNOT_HOME c=20 static const char MSG_TITLE_IDLER_CANNOT_MOVE[] PROGMEM_I1 = ISTR("IDLER CANNOT MOVE"); ////MSG_TITLE_IDLER_CANNOT_MOVE c=20 @@ -170,6 +173,7 @@ static const char * const errorTitles [] PROGMEM = { _R(MSG_TITLE_PULLEY_CANNOT_MOVE), _R(MSG_TITLE_FSENSOR_TOO_EARLY), _R(MSG_TITLE_INSPECT_FINDA), + _R(MSG_TITLE_LOAD_TO_EXTRUDER_FAILED), _R(MSG_TITLE_SELECTOR_CANNOT_HOME), _R(MSG_TITLE_SELECTOR_CANNOT_MOVE), _R(MSG_TITLE_IDLER_CANNOT_HOME), @@ -214,6 +218,7 @@ static const char MSG_DESC_FSENSOR_DIDNT_GO_OFF[] PROGMEM_I1 = ISTR("Filament se static const char MSG_DESC_PULLEY_STALLED[] PROGMEM_I1 = ISTR("Pulley motor stalled. Ensure the pulley can move and check the wiring."); ////MSG_DESC_PULLEY_STALLED c=20 r=8 static const char MSG_DESC_FSENSOR_TOO_EARLY[] PROGMEM_I1 = ISTR("Filament sensor triggered too early while loading to extruder. Check there isn't anything stuck in PTFE tube. Check that sensor reads properly."); ////MSG_DESC_FSENSOR_TOO_EARLY c=20 r=8 static const char MSG_DESC_INSPECT_FINDA[] PROGMEM_I1 = ISTR("Selector can't move due to FINDA detecting a filament. Make sure no filament is in selector and FINDA works properly."); ////MSG_DESC_INSPECT_FINDA c=20 r=8 +static const char MSG_DESC_LOAD_TO_EXTRUDER_FAILED[] PROGMEM_I1 = ISTR("@@TODO - load to extruder failed."); ////MSG_DESC_LOAD_TO_EXTRUDER_FAILED c=20 r=8 static const char MSG_DESC_SELECTOR_CANNOT_HOME[] PROGMEM_I1 = ISTR("The Selector cannot home properly. Check for anything blocking its movement."); ////MSG_DESC_SELECTOR_CANNOT_HOME c=20 r=8 static const char MSG_DESC_CANNOT_MOVE[] PROGMEM_I1 = ISTR("Can't move Selector or Idler."); /////MSG_DESC_CANNOT_MOVE c=20 r=4 //static const char MSG_DESC_SELECTOR_CANNOT_MOVE[] PROGMEM_I1 = ISTR("The Selector cannot move. Check for anything blocking its movement. Check the wiring is correct."); @@ -255,6 +260,7 @@ static const char * const errorDescs[] PROGMEM = { _R(MSG_DESC_PULLEY_STALLED), _R(MSG_DESC_FSENSOR_TOO_EARLY), _R(MSG_DESC_INSPECT_FINDA), + _R(MSG_DESC_LOAD_TO_EXTRUDER_FAILED), _R(MSG_DESC_SELECTOR_CANNOT_HOME), _R(MSG_DESC_CANNOT_MOVE), _R(MSG_DESC_IDLER_CANNOT_HOME), @@ -332,6 +338,7 @@ static const uint8_t errorButtons[] PROGMEM = { Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//PULLEY_STALLED Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//FSENSOR_TOO_EARLY Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//INSPECT_FINDA + Btns(ButtonOperations::Continue, ButtonOperations::NoOperation),//LOAD_TO_EXTRUDER_FAILED Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//SELECTOR_CANNOT_HOME Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//SELECTOR_CANNOT_MOVE Btns(ButtonOperations::Retry, ButtonOperations::NoOperation),//IDLER_CANNOT_HOME diff --git a/Firmware/mmu2_error_converter.cpp b/Firmware/mmu2_error_converter.cpp index 579c70b83..9519d8ac8 100644 --- a/Firmware/mmu2_error_converter.cpp +++ b/Firmware/mmu2_error_converter.cpp @@ -49,6 +49,8 @@ uint8_t PrusaErrorCodeIndex(uint16_t ec) { return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY); case (uint16_t)ErrorCode::FINDA_FLICKERS: return FindErrorIndex(ERR_MECHANICAL_INSPECT_FINDA); + case (uint16_t)ErrorCode::LOAD_TO_EXTRUDER_FAILED: + return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED); case (uint16_t)ErrorCode::STALLED_PULLEY: case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED: @@ -222,7 +224,14 @@ Buttons ButtonAvailable(uint16_t ec) { break; } break; - + case ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED: + switch (buttonSelectedOperation) { + case ButtonOperations::Continue: // User solved the serious mechanical problem by hand - there is no other way around + return Middle; + default: + break; + } + break; case ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT: case ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT: case ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT: diff --git a/Firmware/mmu2_protocol_logic.cpp b/Firmware/mmu2_protocol_logic.cpp index 67f59d317..ce9f1481d 100644 --- a/Firmware/mmu2_protocol_logic.cpp +++ b/Firmware/mmu2_protocol_logic.cpp @@ -496,7 +496,8 @@ StepStatus ProtocolLogic::IdleStep() { } ProtocolLogic::ProtocolLogic(MMU2Serial *uart, uint8_t extraLoadDistance) - : currentScope(Scope::Stopped) + : explicitPrinterError(ErrorCode::OK) + , currentScope(Scope::Stopped) , scopeState(ScopeState::Ready) , plannedRq(RequestMsgCodes::unknown, 0) , lastUARTActivityMs(0)