diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 7472c39a2..e01f71d7e 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -3267,6 +3267,25 @@ { 10.0, 700 }, \ { -10.0, 400 }, \ { -50.0, 2000 } + #endif + + // Using a sensor like the MMU2S + //#define PRUSA_MMU2_S_MODE + #if ENABLED(PRUSA_MMU2_S_MODE) + #define MMU2_C0_RETRY 5 // Number of retries (total time = timeout*retries) + + #define MMU2_CAN_LOAD_FEEDRATE 800 // (mm/m) + #define MMU2_CAN_LOAD_SEQUENCE \ + { 0.1, MMU2_CAN_LOAD_FEEDRATE }, \ + { 60.0, MMU2_CAN_LOAD_FEEDRATE }, \ + { -52.0, MMU2_CAN_LOAD_FEEDRATE } + + #define MMU2_CAN_LOAD_RETRACT 6.0 // (mm) Keep under the distance between Load Sequence values + #define MMU2_CAN_LOAD_DEVIATION 0.8 // (mm) Acceptable deviation + + #define MMU2_CAN_LOAD_INCREMENT 0.2 // (mm) To reuse within MMU2 module + #define MMU2_CAN_LOAD_INCREMENT_SEQUENCE \ + { -MMU2_CAN_LOAD_INCREMENT, MMU2_CAN_LOAD_FEEDRATE } #endif diff --git a/Marlin/src/feature/mmu2/mmu2.cpp b/Marlin/src/feature/mmu2/mmu2.cpp index e0b738c5f..3bb5d9e7e 100644 --- a/Marlin/src/feature/mmu2/mmu2.cpp +++ b/Marlin/src/feature/mmu2/mmu2.cpp @@ -91,6 +91,9 @@ MMU2 mmu2; #define mmuSerial MMU2_SERIAL bool MMU2::enabled, MMU2::ready, MMU2::mmu_print_saved; +#if ENABLED(PRUSA_MMU2_S_MODE) + bool MMU2::mmu2s_triggered; +#endif uint8_t MMU2::cmd, MMU2::cmd_arg, MMU2::last_cmd, MMU2::extruder; int8_t MMU2::state = 0; volatile int8_t MMU2::finda = 1; @@ -106,8 +109,14 @@ char MMU2::rx_buffer[MMU_RX_SIZE], MMU2::tx_buffer[MMU_TX_SIZE]; feedRate_t feedRate; //!< feed rate in mm/s }; - static constexpr E_Step ramming_sequence[] PROGMEM = { MMU2_RAMMING_SEQUENCE }; - static constexpr E_Step load_to_nozzle_sequence[] PROGMEM = { MMU2_LOAD_TO_NOZZLE_SEQUENCE }; + static constexpr E_Step + ramming_sequence[] PROGMEM = { MMU2_RAMMING_SEQUENCE } + , load_to_nozzle_sequence[] PROGMEM = { MMU2_LOAD_TO_NOZZLE_SEQUENCE } + #if ENABLED(PRUSA_MMU2_S_MODE) + , can_load_sequence[] PROGMEM = { MMU2_CAN_LOAD_SEQUENCE } + , can_load_increment_sequence[] PROGMEM = { MMU2_CAN_LOAD_INCREMENT_SEQUENCE } + #endif + ; #endif // MMU2_MENUS @@ -228,6 +237,7 @@ void MMU2::mmu_loop() { enabled = true; state = 1; + TERN_(PRUSA_MMU2_S_MODE, mmu2s_triggered = false); } break; @@ -291,6 +301,8 @@ void MMU2::mmu_loop() { tx_str_P(PSTR("P0\n")); state = 2; // wait for response } + + TERN_(PRUSA_MMU2_S_MODE, check_filament()); break; case 2: // response to command P0 @@ -309,6 +321,7 @@ void MMU2::mmu_loop() { else if (ELAPSED(millis(), last_request + MMU_P0_TIMEOUT)) // Resend request after timeout (3s) state = 1; + TERN_(PRUSA_MMU2_S_MODE, check_filament()); break; case 3: // response to mmu commands @@ -327,6 +340,7 @@ void MMU2::mmu_loop() { } state = 1; } + TERN_(PRUSA_MMU2_S_MODE, check_filament()); break; } } @@ -437,6 +451,33 @@ void MMU2::check_version() { } } +static bool mmu2_not_responding() { + LCD_MESSAGEPGM(MSG_MMU2_NOT_RESPONDING); + BUZZ(100, 659); + BUZZ(200, 698); + BUZZ(100, 659); + BUZZ(300, 440); + BUZZ(100, 659); +} + +#if ENABLED(PRUSA_MMU2_S_MODE) + + bool MMU2::load_to_gears() { + command(MMU_CMD_C0); + manage_response(true, true); + LOOP_L_N(i, MMU2_C0_RETRY) { // Keep loading until filament reaches gears + if (mmu2s_triggered) break; + command(MMU_CMD_C0); + manage_response(true, true); + check_filament(); + } + const bool success = mmu2s_triggered && can_load(); + if (!success) mmu2_not_responding(); + return success; + } + +#endif + /** * Handle tool change */ @@ -452,18 +493,15 @@ void MMU2::tool_change(uint8_t index) { ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); command(MMU_CMD_T0 + index); - manage_response(true, true); - command(MMU_CMD_C0); - extruder = index; //filament change is finished - active_extruder = 0; - - ENABLE_AXIS_E0(); - - SERIAL_ECHO_START(); - SERIAL_ECHOLNPAIR(STR_ACTIVE_EXTRUDER, int(extruder)); - + if (load_to_gears()) { + extruder = index; // filament change is finished + active_extruder = 0; + ENABLE_AXIS_E0(); + SERIAL_ECHO_START(); + SERIAL_ECHOLNPAIR(STR_ACTIVE_EXTRUDER, int(extruder)); + } ui.reset_status(); } @@ -500,12 +538,13 @@ void MMU2::tool_change(const char* special) { DISABLE_AXIS_E0(); command(MMU_CMD_T0 + index); manage_response(true, true); - command(MMU_CMD_C0); - mmu_loop(); - ENABLE_AXIS_E0(); - extruder = index; - active_extruder = 0; + if (load_to_gears()) { + mmu_loop(); + ENABLE_AXIS_E0(); + extruder = index; + active_extruder = 0; + } } break; case 'c': { @@ -579,12 +618,7 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { if (turn_off_nozzle) thermalManager.setTargetHotend(0, active_extruder); - LCD_MESSAGEPGM(MSG_MMU2_NOT_RESPONDING); - BUZZ(100, 659); - BUZZ(200, 698); - BUZZ(100, 659); - BUZZ(300, 440); - BUZZ(100, 659); + mmu2_not_responding(); } } else if (mmu_print_saved) { @@ -632,6 +666,39 @@ void MMU2::filament_runout() { planner.synchronize(); } +#if ENABLED(PRUSA_MMU2_S_MODE) + void MMU2::check_filament() { + const bool runout = READ(FIL_RUNOUT_PIN) ^ (FIL_RUNOUT_INVERTING); + if (runout && !mmu2s_triggered) { + DEBUG_ECHOLNPGM("MMU <= 'A'"); + tx_str_P(PSTR("A\n")); + } + mmu2s_triggered = runout; + } + + bool MMU2::can_load() { + execute_extruder_sequence((const E_Step *)can_load_sequence, COUNT(can_load_sequence)); + + int filament_detected_count = 0; + const int steps = MMU2_CAN_LOAD_RETRACT / MMU2_CAN_LOAD_INCREMENT; + DEBUG_ECHOLNPGM("MMU can_load:"); + LOOP_L_N(i, steps) { + execute_extruder_sequence((const E_Step *)can_load_increment_sequence, COUNT(can_load_increment_sequence)); + check_filament(); // Don't trust the idle function + DEBUG_CHAR(mmu2s_triggered ? 'O' : 'o'); + if (mmu2s_triggered) ++filament_detected_count; + } + + if (filament_detected_count <= steps - (MMU2_CAN_LOAD_DEVIATION / MMU2_CAN_LOAD_INCREMENT)) { + DEBUG_ECHOLNPGM(" failed."); + return false; + } + + DEBUG_ECHOLNPGM(" succeeded."); + return true; + } +#endif + #if BOTH(HAS_LCD_MENU, MMU2_MENUS) // Load filament into MMU2 @@ -656,20 +723,19 @@ void MMU2::filament_runout() { LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD); return false; } - else { - command(MMU_CMD_T0 + index); - manage_response(true, true); - command(MMU_CMD_C0); - mmu_loop(); + command(MMU_CMD_T0 + index); + manage_response(true, true); + + const bool success = load_to_gears(); + if (success) { + mmu_loop(); extruder = index; active_extruder = 0; - load_to_nozzle(); - BUZZ(200, 404); - return true; } + return success; } /** diff --git a/Marlin/src/feature/mmu2/mmu2.h b/Marlin/src/feature/mmu2/mmu2.h index a88764447..8dd07f884 100644 --- a/Marlin/src/feature/mmu2/mmu2.h +++ b/Marlin/src/feature/mmu2/mmu2.h @@ -80,7 +80,17 @@ private: static void filament_runout(); + #if ENABLED(PRUSA_MMU2_S_MODE) + static bool mmu2s_triggered; + static void check_filament(); + static bool can_load(); + static bool load_to_gears(); + #else + FORCE_INLINE static bool load_to_gears() { return true; } + #endif + static bool enabled, ready, mmu_print_saved; + static uint8_t cmd, cmd_arg, last_cmd, extruder; static int8_t state; static volatile int8_t finda; diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 8d4b9d33b..181ca8b88 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -2740,6 +2740,8 @@ static_assert( _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2) #error "PRUSA_MMU2 requires NOZZLE_PARK_FEATURE." #elif EXTRUDERS != 5 #error "PRUSA_MMU2 requires EXTRUDERS = 5." + #elif ENABLED(PRUSA_MMU2_S_MODE) && DISABLED(FILAMENT_RUNOUT_SENSOR) + #error "PRUSA_MMU2_S_MODE requires FILAMENT_RUNOUT_SENSOR. Enable it to continue." #elif DISABLED(ADVANCED_PAUSE_FEATURE) static_assert(nullptr == strstr(MMU2_FILAMENT_RUNOUT_SCRIPT, "M600"), "ADVANCED_PAUSE_FEATURE is required to use M600 with PRUSA_MMU2."); #endif diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 94d9d44cf..e9cd043e6 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -528,21 +528,21 @@ namespace Language_en { PROGMEM Language_Str MSG_MMU2_MENU = _UxGT("MMU"); PROGMEM Language_Str MSG_KILL_MMU2_FIRMWARE = _UxGT("Update MMU Firmware!"); PROGMEM Language_Str MSG_MMU2_NOT_RESPONDING = _UxGT("MMU Needs Attention."); - PROGMEM Language_Str MSG_MMU2_RESUME = _UxGT("Resume Print"); - PROGMEM Language_Str MSG_MMU2_RESUMING = _UxGT("Resuming..."); - PROGMEM Language_Str MSG_MMU2_LOAD_FILAMENT = _UxGT("Load Filament"); - PROGMEM Language_Str MSG_MMU2_LOAD_ALL = _UxGT("Load All"); - PROGMEM Language_Str MSG_MMU2_LOAD_TO_NOZZLE = _UxGT("Load to Nozzle"); - PROGMEM Language_Str MSG_MMU2_EJECT_FILAMENT = _UxGT("Eject Filament"); - PROGMEM Language_Str MSG_MMU2_EJECT_FILAMENT_N = _UxGT("Eject Filament ~"); - PROGMEM Language_Str MSG_MMU2_UNLOAD_FILAMENT = _UxGT("Unload Filament"); + PROGMEM Language_Str MSG_MMU2_RESUME = _UxGT("MMU Resume"); + PROGMEM Language_Str MSG_MMU2_RESUMING = _UxGT("MMU Resuming..."); + PROGMEM Language_Str MSG_MMU2_LOAD_FILAMENT = _UxGT("MMU Load"); + PROGMEM Language_Str MSG_MMU2_LOAD_ALL = _UxGT("MMU Load All"); + PROGMEM Language_Str MSG_MMU2_LOAD_TO_NOZZLE = _UxGT("MMU Load to Nozzle"); + PROGMEM Language_Str MSG_MMU2_EJECT_FILAMENT = _UxGT("MMU Eject"); + PROGMEM Language_Str MSG_MMU2_EJECT_FILAMENT_N = _UxGT("MMU Eject ~"); + PROGMEM Language_Str MSG_MMU2_UNLOAD_FILAMENT = _UxGT("MMU Unload"); PROGMEM Language_Str MSG_MMU2_LOADING_FILAMENT = _UxGT("Loading Fil. %i..."); PROGMEM Language_Str MSG_MMU2_EJECTING_FILAMENT = _UxGT("Ejecting Fil. ..."); PROGMEM Language_Str MSG_MMU2_UNLOADING_FILAMENT = _UxGT("Unloading Fil...."); PROGMEM Language_Str MSG_MMU2_ALL = _UxGT("All"); PROGMEM Language_Str MSG_MMU2_FILAMENT_N = _UxGT("Filament ~"); PROGMEM Language_Str MSG_MMU2_RESET = _UxGT("Reset MMU"); - PROGMEM Language_Str MSG_MMU2_RESETTING = _UxGT("Resetting MMU..."); + PROGMEM Language_Str MSG_MMU2_RESETTING = _UxGT("MMU Resetting..."); PROGMEM Language_Str MSG_MMU2_EJECT_RECOVER = _UxGT("Remove, click"); PROGMEM Language_Str MSG_MIX = _UxGT("Mix"); diff --git a/buildroot/share/tests/mega2560-tests b/buildroot/share/tests/mega2560-tests index 45dbcb9ff..b17d60ba8 100755 --- a/buildroot/share/tests/mega2560-tests +++ b/buildroot/share/tests/mega2560-tests @@ -71,7 +71,7 @@ opt_set NUM_SERVOS 1 opt_enable ZONESTAR_LCD Z_PROBE_SERVO_NR Z_SERVO_ANGLES DEACTIVATE_SERVOS_AFTER_MOVE BOOT_MARLIN_LOGO_ANIMATED \ AUTO_BED_LEVELING_3POINT DEBUG_LEVELING_FEATURE EEPROM_SETTINGS EEPROM_CHITCHAT M114_DETAIL \ NO_VOLUMETRICS EXTENDED_CAPABILITIES_REPORT AUTO_REPORT_TEMPERATURES AUTOTEMP G38_PROBE_TARGET JOYSTICK \ - PRUSA_MMU2 MMU2_MENUS NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING + PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING exec_test $1 $2 "RAMPS | ZONESTAR_LCD | MMU2 | Servo Probe | ABL 3-Pt | Debug Leveling | EEPROM | G38 ..." #