Automatic Tool Migration feature (#17248)
This commit is contained in:
		| @@ -1847,19 +1847,55 @@ | ||||
|  */ | ||||
| #if EXTRUDERS > 1 | ||||
|   // Z raise distance for tool-change, as needed for some extruders | ||||
|   #define TOOLCHANGE_ZRAISE     2  // (mm) | ||||
|   //#define TOOLCHANGE_NO_RETURN   // Never return to the previous position on tool-change | ||||
|   #define TOOLCHANGE_ZRAISE                 2 // (mm) | ||||
|   //#define TOOLCHANGE_ZRAISE_BEFORE_RETRACT  // Apply raise before swap retraction (if enabled) | ||||
|   //#define TOOLCHANGE_NO_RETURN              // Never return to previous position on tool-change | ||||
|   #if ENABLED(TOOLCHANGE_NO_RETURN) | ||||
|     //#define EVENT_GCODE_AFTER_TOOLCHANGE "G12X"   // G-code to run after tool-change is complete | ||||
|     //#define EVENT_GCODE_AFTER_TOOLCHANGE "G12X"   // Extra G-code to run after tool-change | ||||
|   #endif | ||||
|  | ||||
|   // Retract and prime filament on tool-change | ||||
|   /** | ||||
|    * Retract and prime filament on tool-change to reduce | ||||
|    * ooze and stringing and to get cleaner transitions. | ||||
|    */ | ||||
|   //#define TOOLCHANGE_FILAMENT_SWAP | ||||
|   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|     #define TOOLCHANGE_FIL_SWAP_LENGTH          12  // (mm) | ||||
|     #define TOOLCHANGE_FIL_EXTRA_PRIME           2  // (mm) | ||||
|     #define TOOLCHANGE_FIL_SWAP_RETRACT_SPEED 3600  // (mm/m) | ||||
|     #define TOOLCHANGE_FIL_SWAP_PRIME_SPEED   3600  // (mm/m) | ||||
|     // Load / Unload | ||||
|     #define TOOLCHANGE_FS_LENGTH              12  // (mm) Load / Unload length | ||||
|     #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH  0  // (mm) Extra length for better restart, fine tune by LCD/Gcode) | ||||
|     #define TOOLCHANGE_FS_RETRACT_SPEED   (50*60) // (mm/m) (Unloading) | ||||
|     #define TOOLCHANGE_FS_UNRETRACT_SPEED (25*60) // (mm/m) (On SINGLENOZZLE or Bowden loading must be slowed down) | ||||
|  | ||||
|     // Longer prime to clean out a SINGLENOZZLE | ||||
|     #define TOOLCHANGE_FS_EXTRA_PRIME          0  // (mm) Extra priming length | ||||
|     #define TOOLCHANGE_FS_PRIME_SPEED    (4.6*60) // (mm/m) Extra priming feedrate | ||||
|     #define TOOLCHANGE_FS_WIPE_RETRACT         0  // (mm/m) Retract before cooling for less stringing, better wipe, etc. | ||||
|  | ||||
|     // Cool after prime to reduce stringing | ||||
|     #define TOOLCHANGE_FS_FAN                 -1  // Fan index or -1 to skip | ||||
|     #define TOOLCHANGE_FS_FAN_SPEED          255  // 0-255 | ||||
|     #define TOOLCHANGE_FS_FAN_TIME            10  // (seconds) | ||||
|  | ||||
|     // Swap uninitialized extruder with TOOLCHANGE_FS_PRIME_SPEED for all lengths (recover + prime) | ||||
|     // (May break filament if not retracted beforehand.) | ||||
|     //#define TOOLCHANGE_FS_INIT_BEFORE_SWAP | ||||
|  | ||||
|     // Prime on the first T command even if the same or no toolchange / swap | ||||
|     // Enable it (M217 V[0/1]) before printing, to avoid unwanted priming on host connect | ||||
|     //#define TOOLCHANGE_FS_PRIME_FIRST_USED | ||||
|  | ||||
|     /** | ||||
|      * Tool Change Migration | ||||
|      * This feature provides G-code and LCD options to switch tools mid-print. | ||||
|      * All applicable tool properties are migrated so the print can continue. | ||||
|      * Tools must be closely matching and other restrictions may apply. | ||||
|      * Useful to: | ||||
|      *   - Change filament color without interruption | ||||
|      *   - Switch spools automatically on filament runout | ||||
|      *   - Switch to a different nozzle on an extruder jam | ||||
|      */ | ||||
|     #define TOOLCHANGE_MIGRATION_FEATURE | ||||
|  | ||||
|   #endif | ||||
|  | ||||
|   /** | ||||
| @@ -1870,8 +1906,10 @@ | ||||
|   #if ENABLED(TOOLCHANGE_PARK) | ||||
|     #define TOOLCHANGE_PARK_XY    { X_MIN_POS + 10, Y_MIN_POS + 10 } | ||||
|     #define TOOLCHANGE_PARK_XY_FEEDRATE 6000  // (mm/m) | ||||
|     //#define TOOLCHANGE_PARK_X_ONLY          // X axis only move | ||||
|     //#define TOOLCHANGE_PARK_Y_ONLY          // Y axis only move | ||||
|   #endif | ||||
| #endif | ||||
| #endif // EXTRUDERS > 1 | ||||
|  | ||||
| /** | ||||
|  * Advanced Pause | ||||
|   | ||||
| @@ -39,6 +39,10 @@ bool FilamentMonitorBase::enabled = true, | ||||
|   bool FilamentMonitorBase::host_handling; // = false | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|   #include "../module/tool_change.h" | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Called by FilamentSensorSwitch::run when filament is detected. | ||||
|  * Called by FilamentSensorEncoder::block_completed when motion is detected. | ||||
| @@ -76,6 +80,11 @@ void event_filament_runout() { | ||||
|  | ||||
|   if (TERN0(ADVANCED_PAUSE_FEATURE, did_pause_print)) return;  // Action already in progress. Purge triggered repeated runout. | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|     if (migration.in_progress) return;  // Action already in progress. Purge triggered repeated runout. | ||||
|     if (migration.automode) { extruder_migration(); return; } | ||||
|   #endif | ||||
|  | ||||
|   TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getActiveTool())); | ||||
|  | ||||
|   #if EITHER(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS) | ||||
|   | ||||
| @@ -27,6 +27,10 @@ | ||||
| #include "../gcode.h" | ||||
| #include "../../module/tool_change.h" | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|   #include "../../module/motion.h" | ||||
| #endif | ||||
|  | ||||
| #include "../../MarlinCore.h" // for SP_X_STR, etc. | ||||
|  | ||||
| extern const char SP_X_STR[], SP_Y_STR[], SP_Z_STR[]; | ||||
| @@ -35,16 +39,30 @@ void M217_report(const bool eeprom=false) { | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|     serialprintPGM(eeprom ? PSTR("  M217") : PSTR("Toolchange:")); | ||||
|     SERIAL_ECHOPAIR(" S", LINEAR_UNIT(toolchange_settings.swap_length)); | ||||
|     SERIAL_ECHOPAIR_P(SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime)); | ||||
|     SERIAL_ECHOPAIR_P(SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed)); | ||||
|     SERIAL_ECHOPAIR(" R", LINEAR_UNIT(toolchange_settings.retract_speed)); | ||||
|     SERIAL_ECHOPAIR(" S", LINEAR_UNIT(toolchange_settings.swap_length), | ||||
|                     " B", LINEAR_UNIT(toolchange_settings.extra_resume)); | ||||
|     SERIAL_ECHOPAIR_P(SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime), | ||||
|                       SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed)); | ||||
|     SERIAL_ECHOPAIR(" R", LINEAR_UNIT(toolchange_settings.retract_speed), | ||||
|                     " U", LINEAR_UNIT(toolchange_settings.unretract_speed), | ||||
|                     " F", toolchange_settings.fan_speed, | ||||
|                     " G", toolchange_settings.fan_time); | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|       SERIAL_ECHOPAIR(" N", int(migration.automode)); | ||||
|       SERIAL_ECHOPAIR(" L", LINEAR_UNIT(migration.last)); | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_PARK) | ||||
|       SERIAL_ECHOPAIR(" W", LINEAR_UNIT(toolchange_settings.enable_park)); | ||||
|       SERIAL_ECHOPAIR_P(SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x)); | ||||
|       SERIAL_ECHOPAIR_P(SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y)); | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) | ||||
|       SERIAL_ECHOPAIR(" V", LINEAR_UNIT(enable_first_prime)); | ||||
|     #endif | ||||
|  | ||||
|   #else | ||||
|  | ||||
|     UNUSED(eeprom); | ||||
| @@ -58,48 +76,98 @@ void M217_report(const bool eeprom=false) { | ||||
| /** | ||||
|  * M217 - Set SINGLENOZZLE toolchange parameters | ||||
|  * | ||||
|  *  // Tool change command | ||||
|  *  Q           Prime active tool and exit | ||||
|  * | ||||
|  *  // Tool change settings | ||||
|  *  S[linear]   Swap length | ||||
|  *  E[linear]   Purge length | ||||
|  *  B[linear]   Extra Swap length | ||||
|  *  E[linear]   Prime length | ||||
|  *  P[linear/m] Prime speed | ||||
|  *  R[linear/m] Retract speed | ||||
|  *  U[linear/m] UnRetract speed | ||||
|  *  V[linear]   0/1 Enable auto prime first extruder used | ||||
|  *  W[linear]   0/1 Enable park & Z Raise | ||||
|  *  X[linear]   Park X (Requires TOOLCHANGE_PARK) | ||||
|  *  Y[linear]   Park Y (Requires TOOLCHANGE_PARK) | ||||
|  *  Z[linear]   Z Raise | ||||
|  *  F[linear]   Fan Speed 0-255 | ||||
|  *  G[linear/s] Fan time | ||||
|  * | ||||
|  * Tool migration settings | ||||
|  *  A[0|1]      Enable auto-migration on runout | ||||
|  *  L[index]    Last extruder to use for auto-migration | ||||
|  * | ||||
|  * Tool migration command | ||||
|  *  T[index]    Migrate to next extruder or the given extruder | ||||
|  */ | ||||
| void GcodeSuite::M217() { | ||||
|  | ||||
|   #define SPR_PARAM | ||||
|   #define XY_PARAM | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|  | ||||
|     #undef SPR_PARAM | ||||
|     #define SPR_PARAM "SPRE" | ||||
|     static constexpr float max_extrude = TERN(PREVENT_LENGTHY_EXTRUDE, EXTRUDE_MAXLENGTH, 500); | ||||
|  | ||||
|     static constexpr float max_extrude = | ||||
|       #if ENABLED(PREVENT_LENGTHY_EXTRUDE) | ||||
|         EXTRUDE_MAXLENGTH | ||||
|       #else | ||||
|         500 | ||||
|       #endif | ||||
|     ; | ||||
|     if (parser.seen('Q')) { tool_change_prime(); return; } | ||||
|  | ||||
|     if (parser.seenval('S')) { const float v = parser.value_linear_units(); toolchange_settings.swap_length = constrain(v, 0, max_extrude); } | ||||
|     if (parser.seenval('B')) { const float v = parser.value_linear_units(); toolchange_settings.extra_resume = constrain(v, -10, 10); } | ||||
|     if (parser.seenval('E')) { const float v = parser.value_linear_units(); toolchange_settings.extra_prime = constrain(v, 0, max_extrude); } | ||||
|     if (parser.seenval('P')) { const int16_t v = parser.value_linear_units(); toolchange_settings.prime_speed = constrain(v, 10, 5400); } | ||||
|     if (parser.seenval('R')) { const int16_t v = parser.value_linear_units(); toolchange_settings.retract_speed = constrain(v, 10, 5400); } | ||||
|     if (parser.seenval('U')) { const int16_t v = parser.value_linear_units(); toolchange_settings.unretract_speed = constrain(v, 10, 5400); } | ||||
|     #if TOOLCHANGE_FS_FAN >= 0 && FAN_COUNT > 0 | ||||
|       if (parser.seenval('F')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_speed = constrain(v, 0, 255); } | ||||
|       if (parser.seenval('G')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_time = constrain(v, 1, 30); } | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) | ||||
|     if (parser.seenval('V')) { enable_first_prime = parser.value_linear_units(); } | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_PARK) | ||||
|     #undef XY_PARAM | ||||
|     #define XY_PARAM "XY" | ||||
|     if (parser.seenval('X')) { toolchange_settings.change_point.x = parser.value_linear_units(); } | ||||
|     if (parser.seenval('Y')) { toolchange_settings.change_point.y = parser.value_linear_units(); } | ||||
|     if (parser.seenval('W')) { toolchange_settings.enable_park = parser.value_linear_units(); } | ||||
|     if (parser.seenval('X')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.x = constrain(v, X_MIN_POS, X_MAX_POS); } | ||||
|     if (parser.seenval('Y')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.y = constrain(v, Y_MIN_POS, Y_MAX_POS); } | ||||
|   #endif | ||||
|  | ||||
|   if (parser.seenval('Z')) { toolchange_settings.z_raise = parser.value_linear_units(); } | ||||
|  | ||||
|   if (!parser.seen(SPR_PARAM XY_PARAM "Z")) M217_report(); | ||||
|   #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|     migration.target = 0;       // 0 = disabled | ||||
|  | ||||
|     if (parser.seenval('L')) {  // Last | ||||
|       const int16_t lval = parser.value_int(); | ||||
|       if (WITHIN(lval, 0, EXTRUDERS - 1)) { | ||||
|         migration.last = lval; | ||||
|         migration.automode = (active_extruder < migration.last); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (parser.seen('A'))       // Auto on/off | ||||
|       migration.automode = parser.value_bool(); | ||||
|  | ||||
|     if (parser.seen('T')) {     // Migrate now | ||||
|       if (parser.has_value()) { | ||||
|         const int16_t tval = parser.value_int(); | ||||
|         if (WITHIN(tval, 0, EXTRUDERS - 1) && tval != active_extruder) { | ||||
|           migration.target = tval + 1; | ||||
|           extruder_migration(); | ||||
|           migration.target = 0; // disable | ||||
|           return; | ||||
|         } | ||||
|         else | ||||
|           migration.target = 0; // disable | ||||
|       } | ||||
|       else { | ||||
|         extruder_migration(); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   #endif | ||||
|  | ||||
|   M217_report(); | ||||
| } | ||||
|  | ||||
| #endif // EXTRUDERS > 1 | ||||
|   | ||||
| @@ -33,10 +33,8 @@ void GcodeSuite::M221() { | ||||
|   const int8_t target_extruder = get_target_extruder_from_command(); | ||||
|   if (target_extruder < 0) return; | ||||
|  | ||||
|   if (parser.seenval('S')) { | ||||
|     planner.flow_percentage[target_extruder] = parser.value_int(); | ||||
|     planner.refresh_e_factor(target_extruder); | ||||
|   } | ||||
|   if (parser.seenval('S')) | ||||
|     planner.set_flow(target_extruder, parser.value_int()); | ||||
|   else { | ||||
|     SERIAL_ECHO_START(); | ||||
|     SERIAL_CHAR('E', '0' + target_extruder); | ||||
|   | ||||
| @@ -31,10 +31,6 @@ | ||||
|   #include "../queue.h" | ||||
| #endif | ||||
|  | ||||
| #if HAS_LEDS_OFF_FLAG | ||||
|   #include "../../MarlinCore.h" | ||||
| #endif | ||||
|  | ||||
| #if EITHER(LCD_SET_PROGRESS_MANUALLY, SD_REPRINT_LAST_SELECTED_FILE) | ||||
|   #include "../../lcd/ultralcd.h" | ||||
| #endif | ||||
| @@ -44,6 +40,7 @@ | ||||
| #endif | ||||
|  | ||||
| #if HAS_LEDS_OFF_FLAG | ||||
|   #include "../../MarlinCore.h" // for wait_for_user_response | ||||
|   #include "../../feature/leds/printer_event_leds.h" | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -2222,8 +2222,8 @@ | ||||
|   #define FILAMENT_CHANGE_SLOW_LOAD_LENGTH 0 | ||||
| #endif | ||||
|  | ||||
| #if EXTRUDERS > 1 && !defined(TOOLCHANGE_FIL_EXTRA_PRIME) | ||||
|   #define TOOLCHANGE_FIL_EXTRA_PRIME 0 | ||||
| #if EXTRUDERS > 1 && !defined(TOOLCHANGE_FS_EXTRA_PRIME) | ||||
|   #define TOOLCHANGE_FS_EXTRA_PRIME 0 | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -833,14 +833,15 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|     #ifndef TOOLCHANGE_FIL_SWAP_LENGTH | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FIL_SWAP_LENGTH. Please update your Configuration." | ||||
|     #elif !defined(TOOLCHANGE_FIL_SWAP_RETRACT_SPEED) | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FIL_SWAP_RETRACT_SPEED. Please update your Configuration." | ||||
|     #elif !defined(TOOLCHANGE_FIL_SWAP_PRIME_SPEED) | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FIL_SWAP_PRIME_SPEED. Please update your Configuration." | ||||
|     #ifndef TOOLCHANGE_FS_LENGTH | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_LENGTH. Please update your Configuration_adv.h." | ||||
|     #elif !defined(TOOLCHANGE_FS_RETRACT_SPEED) | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_RETRACT_SPEED. Please update your Configuration_adv.h." | ||||
|     #elif !defined(TOOLCHANGE_FS_PRIME_SPEED) | ||||
|       #error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_PRIME_SPEED. Please update your Configuration_adv.h." | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_PARK) | ||||
|     #ifndef TOOLCHANGE_PARK_XY | ||||
|       #error "TOOLCHANGE_PARK requires TOOLCHANGE_PARK_XY. Please update your Configuration." | ||||
|   | ||||
| @@ -573,8 +573,7 @@ void DGUSScreenVariableHandler::HandleFlowRateChanged(DGUS_VP_Variable &var, voi | ||||
|       #endif | ||||
|     } | ||||
|  | ||||
|     planner.flow_percentage[target_extruder] = newvalue; | ||||
|     planner.refresh_e_factor(target_extruder); | ||||
|     planner.set_flow(target_extruder, newvalue); | ||||
|     ScreenHandler.skipVP = var.VP; // don't overwrite value the next update time as the display might autoincrement in parallel | ||||
|   #else | ||||
|     UNUSED(var); UNUSED(val_ptr); | ||||
|   | ||||
| @@ -669,10 +669,7 @@ namespace ExtUI { | ||||
|   float getRetractAcceleration_mm_s2()                { return planner.settings.retract_acceleration; } | ||||
|   float getTravelAcceleration_mm_s2()                 { return planner.settings.travel_acceleration; } | ||||
|   void setFeedrate_mm_s(const feedRate_t fr)          { feedrate_mm_s = fr; } | ||||
|   void setFlow_percent(const int16_t flow, const extruder_t extr) { | ||||
|     planner.flow_percentage[extr] = flow; | ||||
|     planner.refresh_e_factor(extr); | ||||
|   } | ||||
|   void setFlow_percent(const int16_t flow, const extruder_t extr) { planner.set_flow(extr, flow); } | ||||
|   void setMinFeedrate_mm_s(const feedRate_t fr)       { planner.settings.min_feedrate_mm_s = fr; } | ||||
|   void setMinTravelFeedrate_mm_s(const feedRate_t fr) { planner.settings.min_travel_feedrate_mm_s = fr; } | ||||
|   void setPrintingAcceleration_mm_s2(const float acc) { planner.settings.acceleration = acc; } | ||||
|   | ||||
| @@ -357,8 +357,8 @@ namespace Language_cz { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Délka zavedení"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Výměna nástroje"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Zdvih Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Rychlost primár."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Rychlost retrak."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Rychlost primár."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Rychlost retrak."); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Tryska standby"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Vyměnit filament"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Vyměnit filament *"); | ||||
|   | ||||
| @@ -347,9 +347,9 @@ namespace Language_de { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Entladelänge"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Werkzeugwechsel"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Z anheben"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Prime-Geschwin."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Einzug-Geschwin."); | ||||
|  | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Prime-Geschwin."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Einzug-Geschwin."); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Düsen-Standby"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Filament wechseln"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Filament wechseln *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD                    = _UxGT("Filament laden"); | ||||
|   | ||||
| @@ -371,11 +371,22 @@ namespace Language_en { | ||||
|   PROGMEM Language_Str MSG_CONTROL_RETRACT_RECOVER_SWAPF   = _UxGT("S UnRet V"); | ||||
|   PROGMEM Language_Str MSG_AUTORETRACT                     = _UxGT("AutoRetr."); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_LENGTH            = _UxGT("Swap Length"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_EXTRA             = _UxGT("Swap Extra"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Purge Length"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Tool Change"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Z Raise"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Prime Speed"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Retract Speed"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Prime Speed"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Retract Speed"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PARK_ENABLED           = _UxGT("Park Head"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_UNRETRACT_SPEED    = _UxGT("Recover Speed"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_FAN_SPEED          = _UxGT("Fan Speed"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_FAN_TIME           = _UxGT("Fan Time"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_ON               = _UxGT("Auto ON"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_OFF              = _UxGT("Auto OFF"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION                  = _UxGT("Tool Migration"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_AUTO             = _UxGT("Auto-migration"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_END              = _UxGT("Last Extruder"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_SWAP             = _UxGT("Migrate to *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Change Filament"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Change Filament *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD                    = _UxGT("Load Filament"); | ||||
|   | ||||
| @@ -367,8 +367,8 @@ namespace Language_es { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Purgar longitud"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Cambiar Herramienta"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Aumentar Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Vel. de Cebado"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Vel. de retracción"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Vel. de Cebado"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Vel. de retracción"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Cambiar filamento"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Cambiar filamento *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD                    = _UxGT("Cargar filamento"); | ||||
|   | ||||
| @@ -53,7 +53,7 @@ namespace Language_fr { | ||||
|   PROGMEM Language_Str MSG_MAIN                            = _UxGT("Menu principal"); | ||||
|   PROGMEM Language_Str MSG_ADVANCED_SETTINGS               = _UxGT("Config. avancée"); | ||||
|   PROGMEM Language_Str MSG_CONFIGURATION                   = _UxGT("Configuration"); | ||||
|   PROGMEM Language_Str MSG_AUTOSTART                       = _UxGT("Exéc. auto#.gcode"); | ||||
|   PROGMEM Language_Str MSG_AUTOSTART                       = _UxGT("Exéc. auto.gcode"); | ||||
|   PROGMEM Language_Str MSG_DISABLE_STEPPERS                = _UxGT("Arrêter moteurs"); | ||||
|   PROGMEM Language_Str MSG_DEBUG_MENU                      = _UxGT("Menu debug"); | ||||
|   PROGMEM Language_Str MSG_PROGRESS_BAR_TEST               = _UxGT("Test barre progress."); | ||||
| @@ -329,10 +329,21 @@ namespace Language_fr { | ||||
|   PROGMEM Language_Str MSG_AUTORETRACT                     = _UxGT("Rétraction auto"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Changement outil"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Augmenter Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Vitesse primaire"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Vitesse rétract°"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Vitesse primaire"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Vitesse rétract°"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PARK_ENABLED           = _UxGT("Garer Extrudeur"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_UNRETRACT_SPEED    = _UxGT("Vitesse reprise"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_FAN_SPEED          = _UxGT("Vit.  ventil."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_FAN_TIME           = _UxGT("Temps ventil."); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_ON               = _UxGT("Auto ON"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_OFF              = _UxGT("Auto OFF"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION                  = _UxGT("Migration d'outil"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_AUTO             = _UxGT("Migration auto"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_END              = _UxGT("Extrudeur Final"); | ||||
|   PROGMEM Language_Str MSG_TOOL_MIGRATION_SWAP             = _UxGT("Migrer vers *"); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Attente buse"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_LENGTH            = _UxGT("Longueur retrait"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_EXTRA             = _UxGT("Longueur Extra"); | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Longueur de purge"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Changer filament"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Changer filament *"); | ||||
|   | ||||
| @@ -371,8 +371,8 @@ namespace Language_gl { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Lonxitude de Purga"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Cambiar Ferramenta"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Levantar Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Velocidade prim."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Vel. de Retracción"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Velocidade prim."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Vel. de Retracción"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Cambiar Filamento"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Cambiar Filamento *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD                    = _UxGT("Cargar Filamento"); | ||||
|   | ||||
| @@ -370,8 +370,8 @@ namespace Language_it { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Lunghezza spurgo"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Cambio utensile"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Risalita Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Velocità innesco"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Velocità retrazione"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Velocità innesco"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Velocità retrazione"); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_PARKED                   = _UxGT("Ugello Parcheggiato"); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Standby ugello"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Cambia filamento"); | ||||
|   | ||||
| @@ -339,8 +339,8 @@ namespace Language_pl { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Długość oczyszczania"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Zmiana narzędzia"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Podniesienie Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Prędkość napełniania"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Prędkość wycofania"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Prędkość napełniania"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Prędkość wycofania"); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Dysza w oczekiwaniu"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Zmień filament"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Zmień filament *"); | ||||
|   | ||||
| @@ -286,8 +286,8 @@ namespace Language_pt_br { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_LENGTH            = _UxGT("Distancia Retração"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Mudar Ferramenta"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Levantar Z"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Preparar Veloc."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Veloc. Retração"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Preparar Veloc."); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Veloc. Retração"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Trocar Filamento"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Trocar Filamento *"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD_E                  = _UxGT("Carregar Filamento *"); | ||||
|   | ||||
| @@ -365,8 +365,8 @@ namespace Language_tr { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("Tasfiye uzunluğu"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Takım Değişimi"); | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Z Yükselt"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Birincil Hız"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Geri Çekme Hızı"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Birincil Hız"); | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Geri Çekme Hızı"); | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("Nozul Beklemede"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Filaman Değiştir"); | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Filaman Değiştir *"); | ||||
|   | ||||
| @@ -299,8 +299,8 @@ namespace Language_vi { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_SWAP_LENGTH            = _UxGT("Khoảng Cách Rút");                      // Retract Distance | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("Thay Đổi Công Cụ");                     // Tool Change | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Đưa Lên Z");                            // Z Raise | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("Tốc Độ Tuôn Ra");                       // Prime Speed | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("Tốc Độ Rút Lại");                       // Retract Speed | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("Tốc Độ Tuôn Ra");                       // Prime Speed | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("Tốc Độ Rút Lại");                       // Retract Speed | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("Thay dây nhựa");                        // change filament | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("Thay dây nhựa *");                      // change filament | ||||
|   PROGMEM Language_Str MSG_FILAMENTLOAD                    = _UxGT("Nạp dây nhựa");                         // load filament | ||||
|   | ||||
| @@ -346,8 +346,8 @@ namespace Language_zh_TW { | ||||
|   PROGMEM Language_Str MSG_FILAMENT_PURGE_LENGTH           = _UxGT("清除長度");   //"Purge Length" | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE                     = _UxGT("交換工具"); //"Tool Change" | ||||
|   PROGMEM Language_Str MSG_TOOL_CHANGE_ZLIFT               = _UxGT("Z軸提昇");    //"Z Raise" | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPD          = _UxGT("最高速度");    //"Prime Speed" | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPD        = _UxGT("收回速度");  //"Retract Speed" | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_PRIME_SPEED        = _UxGT("最高速度");    //"Prime Speed" | ||||
|   PROGMEM Language_Str MSG_SINGLENOZZLE_RETRACT_SPEED      = _UxGT("收回速度");  //"Retract Speed" | ||||
|   PROGMEM Language_Str MSG_NOZZLE_STANDBY                  = _UxGT("噴嘴待機"); //"Nozzle Standby" | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE                  = _UxGT("更換絲料");     //"Change filament" | ||||
|   PROGMEM Language_Str MSG_FILAMENTCHANGE_E                = _UxGT("更換絲料 *"); | ||||
|   | ||||
| @@ -104,22 +104,50 @@ void menu_advanced_settings(); | ||||
|     START_MENU(); | ||||
|     BACK_ITEM(MSG_CONFIGURATION); | ||||
|     #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|       static constexpr float max_extrude = | ||||
|         #if ENABLED(PREVENT_LENGTHY_EXTRUDE) | ||||
|           EXTRUDE_MAXLENGTH | ||||
|         #else | ||||
|           500 | ||||
|         #endif | ||||
|       ; | ||||
|       static constexpr float max_extrude = TERN(PREVENT_LENGTHY_EXTRUDE, EXTRUDE_MAXLENGTH, 500); | ||||
|       #if ENABLED(TOOLCHANGE_PARK) | ||||
|         EDIT_ITEM(bool, MSG_FILAMENT_PARK_ENABLED, &toolchange_settings.enable_park); | ||||
|       #endif | ||||
|       EDIT_ITEM(float3, MSG_FILAMENT_SWAP_LENGTH, &toolchange_settings.swap_length, 0, max_extrude); | ||||
|       EDIT_ITEM(float41sign, MSG_FILAMENT_SWAP_EXTRA, &toolchange_settings.extra_resume, -10, 10); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_RETRACT_SPEED, &toolchange_settings.retract_speed, 10, 5400); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_UNRETRACT_SPEED, &toolchange_settings.unretract_speed, 10, 5400); | ||||
|       EDIT_ITEM(float3, MSG_FILAMENT_PURGE_LENGTH, &toolchange_settings.extra_prime, 0, max_extrude); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_RETRACT_SPD, &toolchange_settings.retract_speed, 10, 5400); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_PRIME_SPD, &toolchange_settings.prime_speed, 10, 5400); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_PRIME_SPEED, &toolchange_settings.prime_speed, 10, 5400); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_FAN_SPEED, &toolchange_settings.fan_speed, 0, 255); | ||||
|       EDIT_ITEM_FAST(int4, MSG_SINGLENOZZLE_FAN_TIME, &toolchange_settings.fan_time, 1, 30); | ||||
|     #endif | ||||
|     EDIT_ITEM(float3, MSG_TOOL_CHANGE_ZLIFT, &toolchange_settings.z_raise, 0, 10); | ||||
|     END_MENU(); | ||||
|   } | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|  | ||||
|     #include "../../module/motion.h" // for active_extruder | ||||
|  | ||||
|     void menu_toolchange_migration() { | ||||
|       START_MENU(); | ||||
|       BACK_ITEM(MSG_CONFIGURATION); | ||||
|  | ||||
|       // Auto mode ON/OFF | ||||
|       EDIT_ITEM(bool, MSG_TOOL_MIGRATION_AUTO, &migration.automode); | ||||
|       EDIT_ITEM(uint8, MSG_TOOL_MIGRATION_END, &migration.last, 0, EXTRUDERS - 1); | ||||
|  | ||||
|       // Migrate to a chosen extruder | ||||
|       PGM_P const msg_migrate = GET_TEXT(MSG_TOOL_MIGRATION_SWAP); | ||||
|       LOOP_L_N(s, EXTRUDERS) { | ||||
|         if (s != active_extruder) { | ||||
|           ACTION_ITEM_N_P(s, msg_migrate, []{ | ||||
|             char cmd[12]; | ||||
|             sprintf_P(cmd, PSTR("M217 T%i"), int(MenuItemBase::itemIndex)); | ||||
|             queue.inject(cmd); | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|       END_MENU(); | ||||
|     } | ||||
|   #endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if HAS_HOTEND_OFFSET | ||||
| @@ -373,6 +401,9 @@ void menu_configuration() { | ||||
|   // | ||||
|   #if EXTRUDERS > 1 | ||||
|     SUBMENU(MSG_TOOL_CHANGE, menu_tool_change); | ||||
|     #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|       SUBMENU(MSG_TOOL_MIGRATION, menu_toolchange_migration); | ||||
|     #endif | ||||
|   #endif | ||||
|  | ||||
|   // | ||||
|   | ||||
| @@ -2423,16 +2423,32 @@ void MarlinSettings::reset() { | ||||
|  | ||||
|   #if EXTRUDERS > 1 | ||||
|     #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|       toolchange_settings.swap_length = TOOLCHANGE_FIL_SWAP_LENGTH; | ||||
|       toolchange_settings.extra_prime = TOOLCHANGE_FIL_EXTRA_PRIME; | ||||
|       toolchange_settings.prime_speed = TOOLCHANGE_FIL_SWAP_PRIME_SPEED; | ||||
|       toolchange_settings.retract_speed = TOOLCHANGE_FIL_SWAP_RETRACT_SPEED; | ||||
|       toolchange_settings.swap_length     = TOOLCHANGE_FS_LENGTH; | ||||
|       toolchange_settings.extra_resume    = TOOLCHANGE_FS_EXTRA_RESUME_LENGTH; | ||||
|       toolchange_settings.retract_speed   = TOOLCHANGE_FS_RETRACT_SPEED; | ||||
|       toolchange_settings.unretract_speed = TOOLCHANGE_FS_UNRETRACT_SPEED; | ||||
|       toolchange_settings.extra_prime     = TOOLCHANGE_FS_EXTRA_PRIME; | ||||
|       toolchange_settings.prime_speed     = TOOLCHANGE_FS_PRIME_SPEED; | ||||
|       toolchange_settings.fan_speed       = TOOLCHANGE_FS_FAN_SPEED; | ||||
|       toolchange_settings.fan_time        = TOOLCHANGE_FS_FAN_TIME; | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) | ||||
|       enable_first_prime = false; | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_PARK) | ||||
|       constexpr xyz_pos_t tpxy = TOOLCHANGE_PARK_XY; | ||||
|       toolchange_settings.enable_park = true; | ||||
|       toolchange_settings.change_point = tpxy; | ||||
|     #endif | ||||
|  | ||||
|     toolchange_settings.z_raise = TOOLCHANGE_ZRAISE; | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|       migration = migration_defaults; | ||||
|     #endif | ||||
|  | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(BACKLASH_GCODE) | ||||
|   | ||||
| @@ -423,12 +423,14 @@ class Planner { | ||||
|  | ||||
|     #if EXTRUDERS | ||||
|       FORCE_INLINE static void refresh_e_factor(const uint8_t e) { | ||||
|         e_factor[e] = (flow_percentage[e] * 0.01f | ||||
|           #if DISABLED(NO_VOLUMETRICS) | ||||
|             * volumetric_multiplier[e] | ||||
|           #endif | ||||
|         ); | ||||
|         e_factor[e] = flow_percentage[e] * 0.01f * TERN(NO_VOLUMETRICS, 1.0f, volumetric_multiplier[e]); | ||||
|       } | ||||
|  | ||||
|       static inline void set_flow(const uint8_t e, const int16_t flow) { | ||||
|         flow_percentage[e] = flow; | ||||
|         refresh_e_factor(e); | ||||
|       } | ||||
|  | ||||
|     #endif | ||||
|  | ||||
|     // Manage fans, paste pressure, etc. | ||||
|   | ||||
| @@ -38,6 +38,15 @@ | ||||
|   toolchange_settings_t toolchange_settings;  // Initialized by settings.load() | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|   migration_settings_t migration = migration_defaults; | ||||
|   bool enable_first_prime; | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP) | ||||
|   bool toolchange_extruder_ready[EXTRUDERS]; | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(SINGLENOZZLE) | ||||
|   uint16_t singlenozzle_temp[EXTRUDERS]; | ||||
|   #if FAN_COUNT > 0 | ||||
| @@ -85,6 +94,14 @@ | ||||
|   #include "../feature/pause.h" | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|   #include "../gcode/gcode.h" | ||||
|   #if TOOLCHANGE_FS_WIPE_RETRACT <= 0 | ||||
|     #undef TOOLCHANGE_FS_WIPE_RETRACT | ||||
|     #define TOOLCHANGE_FS_WIPE_RETRACT 0 | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #if DO_SWITCH_EXTRUDER | ||||
|  | ||||
|   #if EXTRUDERS > 3 | ||||
| @@ -759,6 +776,77 @@ inline void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_a | ||||
|  | ||||
| #endif // DUAL_X_CARRIAGE | ||||
|  | ||||
| /** | ||||
|  * Prime active tool using TOOLCHANGE_FILAMENT_SWAP settings | ||||
|  */ | ||||
| #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|  | ||||
| void tool_change_prime() { | ||||
|   if (toolchange_settings.extra_prime > 0 | ||||
|     && TERN(PREVENT_COLD_EXTRUSION, !thermalManager.targetTooColdToExtrude(active_extruder), 1) | ||||
|   ) { | ||||
|     destination = current_position; // Remember the old position | ||||
|  | ||||
|     const bool ok = TERN1(TOOLCHANGE_PARK, all_axes_homed() && toolchange_settings.enable_park); | ||||
|  | ||||
|     // Z raise | ||||
|     if (ok) { | ||||
|       // Do a small lift to avoid the workpiece in the move back (below) | ||||
|       current_position.z += toolchange_settings.z_raise; | ||||
|       #if HAS_SOFTWARE_ENDSTOPS | ||||
|         NOMORE(current_position.z, soft_endstop.max.z); | ||||
|       #endif | ||||
|       fast_line_to_current(Z_AXIS); | ||||
|       planner.synchronize(); | ||||
|     } | ||||
|  | ||||
|     // Park | ||||
|     #if ENABLED(TOOLCHANGE_PARK) | ||||
|       if (ok) { | ||||
|         TERN(TOOLCHANGE_PARK_Y_ONLY,,current_position.x = toolchange_settings.change_point.x); | ||||
|         TERN(TOOLCHANGE_PARK_X_ONLY,,current_position.y = toolchange_settings.change_point.y); | ||||
|         planner.buffer_line(current_position, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE), active_extruder); | ||||
|         planner.synchronize(); | ||||
|       } | ||||
|     #endif | ||||
|  | ||||
|     // Prime (All distances are added and slowed down to ensure secure priming in all circumstances) | ||||
|     unscaled_e_move(toolchange_settings.swap_length + toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); | ||||
|  | ||||
|     // Cutting retraction | ||||
|     #if TOOLCHANGE_FS_WIPE_RETRACT | ||||
|       unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed)); | ||||
|     #endif | ||||
|  | ||||
|     // Cool down with fan | ||||
|     #if TOOLCHANGE_FS_FAN >= 0 && FAN_COUNT > 0 | ||||
|       const int16_t fansp = thermalManager.fan_speed[TOOLCHANGE_FS_FAN]; | ||||
|       thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed; | ||||
|       safe_delay(toolchange_settings.fan_time * 1000); | ||||
|       thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = fansp; | ||||
|     #endif | ||||
|  | ||||
|     // Move back | ||||
|     #if ENABLED(TOOLCHANGE_PARK) | ||||
|       if (ok) { | ||||
|         #if ENABLED(TOOLCHANGE_NO_RETURN) | ||||
|           do_blocking_move_to_z(destination.z, planner.settings.max_feedrate_mm_s[Z_AXIS]); | ||||
|         #else | ||||
|           do_blocking_move_to(destination, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE)); | ||||
|         #endif | ||||
|       } | ||||
|     #endif | ||||
|  | ||||
|     // Cutting recover | ||||
|     unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed)); | ||||
|  | ||||
|     planner.synchronize(); | ||||
|     current_position.e = destination.e; | ||||
|     sync_plan_position_e(); // Resume at the old E position | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Perform a tool-change, which may result in moving the | ||||
|  * previous tool out of the way and the new tool into place. | ||||
| @@ -826,39 +914,52 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|     const uint8_t old_tool = active_extruder; | ||||
|     const bool can_move_away = !no_move && !idex_full_control; | ||||
|  | ||||
|     #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|       const bool should_swap = can_move_away && toolchange_settings.swap_length; | ||||
|       #if ENABLED(PREVENT_COLD_EXTRUSION) | ||||
|         const bool too_cold = !DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(old_tool) || thermalManager.targetTooColdToExtrude(new_tool)); | ||||
|       #else | ||||
|         constexpr bool too_cold = false; | ||||
|       #endif | ||||
|       if (should_swap) { | ||||
|         if (too_cold) { | ||||
|           SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD); | ||||
|           #if ENABLED(SINGLENOZZLE) | ||||
|             active_extruder = new_tool; | ||||
|             return; | ||||
|           #endif | ||||
|         } | ||||
|         else { | ||||
|           #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|             unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed)); | ||||
|           #else | ||||
|             current_position.e -= toolchange_settings.swap_length / planner.e_factor[old_tool]; | ||||
|             planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.retract_speed), old_tool); | ||||
|             planner.synchronize(); | ||||
|           #endif | ||||
|         } | ||||
|       } | ||||
|     #endif // TOOLCHANGE_FILAMENT_SWAP | ||||
|  | ||||
|     #if HAS_LEVELING && DISABLED(SINGLENOZZLE) | ||||
|       // Set current position to the physical position | ||||
|       TEMPORARY_BED_LEVELING_STATE(false); | ||||
|     #endif | ||||
|  | ||||
|     // First tool priming. To prime again, reboot the machine. | ||||
|     #if BOTH(TOOLCHANGE_FILAMENT_SWAP, TOOLCHANGE_FS_PRIME_FIRST_USED) | ||||
|       static bool first_tool_is_primed = false; | ||||
|       if (new_tool == old_tool && !first_tool_is_primed && enable_first_prime) { | ||||
|         tool_change_prime(); | ||||
|         first_tool_is_primed = true; | ||||
|         toolchange_extruder_ready[old_tool] = true; // Primed and initialized | ||||
|       } | ||||
|     #endif | ||||
|  | ||||
|     if (new_tool != old_tool) { | ||||
|       destination = current_position; | ||||
|  | ||||
|       // Z raise before retraction | ||||
|       #if ENABLED(TOOLCHANGE_ZRAISE_BEFORE_RETRACT) && DISABLED(SWITCHING_NOZZLE) | ||||
|         if (can_move_away && TERN1(toolchange_settings.enable_park)) { | ||||
|           // Do a small lift to avoid the workpiece in the move back (below) | ||||
|           current_position.z += toolchange_settings.z_raise; | ||||
|           #if HAS_SOFTWARE_ENDSTOPS | ||||
|             NOMORE(current_position.z, soft_endstop.max.z); | ||||
|           #endif | ||||
|           fast_line_to_current(Z_AXIS); | ||||
|           planner.synchronize(); | ||||
|         } | ||||
|       #endif | ||||
|  | ||||
|       // Unload / Retract | ||||
|       #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|         const bool should_swap = can_move_away && toolchange_settings.swap_length; | ||||
|                    too_cold = TERN0(PREVENT_COLD_EXTRUSION, | ||||
|                      !DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(old_tool) || thermalManager.targetTooColdToExtrude(new_tool)) | ||||
|                    ); | ||||
|         if (should_swap) { | ||||
|           if (too_cold) { | ||||
|             SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD); | ||||
|             if (ENABLED(SINGLENOZZLE)) { active_extruder = new_tool; return; } | ||||
|           } | ||||
|           else | ||||
|             unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed)); | ||||
|         } | ||||
|       #endif | ||||
|  | ||||
|       TERN_(SWITCHING_NOZZLE_TWO_SERVOS, raise_nozzle(old_tool)); | ||||
|  | ||||
| @@ -877,18 +978,23 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|         #endif | ||||
|       #endif | ||||
|  | ||||
|       destination = current_position; | ||||
|  | ||||
|       #if DISABLED(SWITCHING_NOZZLE) | ||||
|         if (can_move_away) { | ||||
|       #if DISABLED(TOOLCHANGE_ZRAISE_BEFORE_RETRACT) && DISABLED(SWITCHING_NOZZLE) | ||||
|         if (can_move_away && TERN1(TOOLCHANGE_PARK, toolchange_settings.enable_park)) { | ||||
|           // Do a small lift to avoid the workpiece in the move back (below) | ||||
|           current_position.z += toolchange_settings.z_raise; | ||||
|           #if HAS_SOFTWARE_ENDSTOPS | ||||
|             NOMORE(current_position.z, soft_endstop.max.z); | ||||
|           #endif | ||||
|           fast_line_to_current(Z_AXIS); | ||||
|           TERN_(TOOLCHANGE_PARK, current_position = toolchange_settings.change_point); | ||||
|           planner.buffer_line(current_position, feedrate_mm_s, old_tool); | ||||
|         } | ||||
|       #endif | ||||
|  | ||||
|       // Toolchange park | ||||
|       #if ENABLED(TOOLCHANGE_PARK) && DISABLED(SWITCHING_NOZZLE) | ||||
|         if (can_move_away && toolchange_settings.enable_park) { | ||||
|           TERN(TOOLCHANGE_PARK_Y_ONLY,,current_position.x = toolchange_settings.change_point.x); | ||||
|           TERN(TOOLCHANGE_PARK_X_ONLY,,current_position.y = toolchange_settings.change_point.y); | ||||
|           planner.buffer_line(current_position, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE), old_tool); | ||||
|           planner.synchronize(); | ||||
|         } | ||||
|       #endif | ||||
| @@ -932,9 +1038,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|         move_nozzle_servo(new_tool); | ||||
|       #endif | ||||
|  | ||||
|       #if DISABLED(DUAL_X_CARRIAGE) | ||||
|         active_extruder = new_tool; // Set the new active extruder | ||||
|       #endif | ||||
|       // Set the new active extruder | ||||
|       if (DISABLED(DUAL_X_CARRIAGE)) active_extruder = new_tool; | ||||
|  | ||||
|       // The newly-selected extruder XYZ is actually at... | ||||
|       if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Offset Tool XYZ by { ", diff.x, ", ", diff.y, ", ", diff.z, " }"); | ||||
| @@ -951,7 +1056,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|       #endif | ||||
|  | ||||
|       // Return to position and lower again | ||||
|       if (safe_to_move && !no_move && IsRunning()) { | ||||
|       const bool should_move = safe_to_move && !no_move && IsRunning(); | ||||
|       if (should_move) { | ||||
|  | ||||
|         #if ENABLED(SINGLENOZZLE) | ||||
|           #if FAN_COUNT > 0 | ||||
| @@ -969,17 +1075,35 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|  | ||||
|         #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|           if (should_swap && !too_cold) { | ||||
|             #if ENABLED(ADVANCED_PAUSE_FEATURE) | ||||
|               unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.prime_speed)); | ||||
|               unscaled_e_move(toolchange_settings.extra_prime, ADVANCED_PAUSE_PURGE_FEEDRATE); | ||||
|             #else | ||||
|               current_position.e += toolchange_settings.swap_length / planner.e_factor[new_tool]; | ||||
|               planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.prime_speed), new_tool); | ||||
|               current_position.e += toolchange_settings.extra_prime / planner.e_factor[new_tool]; | ||||
|               planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.prime_speed * 0.2f), new_tool); | ||||
|  | ||||
|             float fr = toolchange_settings.unretract_speed; | ||||
|  | ||||
|             #if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP) | ||||
|               if (!toolchange_extruder_ready[new_tool]) { | ||||
|                 toolchange_extruder_ready[new_tool] = true; | ||||
|                 fr = toolchange_settings.prime_speed;       // Next move is a prime | ||||
|                 unscaled_e_move(0, MMM_TO_MMS(fr));         // Init planner with 0 length move | ||||
|               } | ||||
|             #endif | ||||
|  | ||||
|             // Unretract (or Prime) | ||||
|             unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(fr)); | ||||
|  | ||||
|             // Extra Prime | ||||
|             unscaled_e_move(toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); | ||||
|  | ||||
|             // Cutting retraction | ||||
|             #if TOOLCHANGE_FS_WIPE_RETRACT | ||||
|               unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed)); | ||||
|             #endif | ||||
|  | ||||
|             // Cool down with fan | ||||
|             #if TOOLCHANGE_FS_FAN >= 0 && FAN_COUNT > 0 | ||||
|               const int16_t fansp = thermalManager.fan_speed[TOOLCHANGE_FS_FAN]; | ||||
|               thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed; | ||||
|               safe_delay(toolchange_settings.fan_time * 1000); | ||||
|               thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = fansp; | ||||
|             #endif | ||||
|             planner.synchronize(); | ||||
|             planner.set_e_position_mm((destination.e = current_position.e = current_position.e - (TOOLCHANGE_FIL_EXTRA_PRIME))); | ||||
|           } | ||||
|         #endif | ||||
|  | ||||
| @@ -1000,22 +1124,43 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|           #if ENABLED(TOOLCHANGE_NO_RETURN) | ||||
|             // Just move back down | ||||
|             if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Move back Z only"); | ||||
|  | ||||
|             #if ENABLED(TOOLCHANGE_PARK) | ||||
|               if (toolchange_settings.enable_park) | ||||
|             #endif | ||||
|             do_blocking_move_to_z(destination.z, planner.settings.max_feedrate_mm_s[Z_AXIS]); | ||||
|  | ||||
|           #else | ||||
|             // Move back to the original (or adjusted) position | ||||
|             if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination); | ||||
|             do_blocking_move_to(destination); | ||||
|  | ||||
|             #if ENABLED(TOOLCHANGE_PARK) | ||||
|               if (toolchange_settings.enable_park) do_blocking_move_to_xy_z(destination, destination.z, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE)); | ||||
|             #else | ||||
|               do_blocking_move_to_xy(destination); | ||||
|             #endif | ||||
|  | ||||
|           #endif | ||||
|         } | ||||
|  | ||||
|         else if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Move back skipped"); | ||||
|  | ||||
|         #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|           if (should_swap && !too_cold) { | ||||
|             // Cutting recover | ||||
|             unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed)); | ||||
|             current_position.e = 0; | ||||
|             sync_plan_position_e(); // New extruder primed and set to 0 | ||||
|           } | ||||
|         #endif | ||||
|  | ||||
|         TERN_(DUAL_X_CARRIAGE, active_extruder_parked = false); | ||||
|       } | ||||
|  | ||||
|       #if ENABLED(SWITCHING_NOZZLE) | ||||
|         else { | ||||
|           // Move back down. (Including when the new tool is higher.) | ||||
|         // Move back down. (Including when the new tool is higher.) | ||||
|         if (!should_move) | ||||
|           do_blocking_move_to_z(destination.z, planner.settings.max_feedrate_mm_s[Z_AXIS]); | ||||
|         } | ||||
|       #endif | ||||
|  | ||||
|       TERN_(PRUSA_MMU2, mmu2.tool_change(new_tool)); | ||||
| @@ -1053,3 +1198,79 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { | ||||
|  | ||||
|   #endif // EXTRUDERS > 1 | ||||
| } | ||||
|  | ||||
| #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|  | ||||
|   void extruder_migration() { | ||||
|  | ||||
|     #if ENABLED(PREVENT_COLD_EXTRUSION) | ||||
|       if (thermalManager.targetTooColdToExtrude(active_extruder)) return; | ||||
|     #endif | ||||
|  | ||||
|     // No auto-migration or specified target? | ||||
|     if (!migration.target && active_extruder >= migration.last) { | ||||
|       migration.automode = false; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Migrate to a target or the next extruder | ||||
|  | ||||
|     uint8_t migration_extruder = active_extruder; | ||||
|  | ||||
|     if (migration.target) { | ||||
|       // Specified target ok? | ||||
|       const int16_t t = migration.target - 1; | ||||
|       if (t != active_extruder) migration_extruder = t; | ||||
|     } | ||||
|     else if (migration.automode && migration_extruder < migration.last && migration_extruder < EXTRUDERS - 1) | ||||
|       migration_extruder++; | ||||
|  | ||||
|     if (migration_extruder == active_extruder) return; | ||||
|  | ||||
|     // Migration begins | ||||
|  | ||||
|     migration.in_progress = true; // Prevent runout script | ||||
|     planner.synchronize(); | ||||
|  | ||||
|     // Remember position before migration | ||||
|     const float resume_current_e = current_position.e; | ||||
|  | ||||
|     // Migrate the flow | ||||
|     planner.set_flow(migration_extruder, planner.flow_percentage[active_extruder]); | ||||
|  | ||||
|     // Migrate the retracted state | ||||
|     #if ENABLED(FWRETRACT) | ||||
|       fwretract.retracted[migration_extruder] = fwretract.retracted[active_extruder]; | ||||
|     #endif | ||||
|  | ||||
|     // Migrate the temperature to the new hotend | ||||
|     #if HOTENDS > 1 | ||||
|       thermalManager.setTargetHotend(thermalManager.degTargetHotend(active_extruder), migration_extruder); | ||||
|       #if HAS_DISPLAY | ||||
|         thermalManager.set_heating_message(0); | ||||
|       #endif | ||||
|       thermalManager.wait_for_hotend(active_extruder); | ||||
|     #endif | ||||
|  | ||||
|     // Perform the tool change | ||||
|     tool_change(migration_extruder); | ||||
|  | ||||
|     // Retract if previously retracted | ||||
|     #if ENABLED(FWRETRACT) | ||||
|       if (fwretract.retracted[active_extruder]) | ||||
|         unscaled_e_move(-fwretract.settings.retract_length, fwretract.settings.retract_feedrate_mm_s); | ||||
|     #endif | ||||
|  | ||||
|     // If no available extruder | ||||
|     if (EXTRUDERS < 2 || active_extruder >= EXTRUDERS - 2 || active_extruder == migration.last) | ||||
|       migration.automode = false; | ||||
|  | ||||
|     migration.in_progress = false; | ||||
|  | ||||
|     current_position.e = resume_current_e; | ||||
|  | ||||
|     planner.synchronize(); | ||||
|     planner.set_e_position_mm(current_position.e); // New extruder primed and ready | ||||
|   } | ||||
|  | ||||
| #endif // TOOLCHANGE_MIGRATION_FEATURE | ||||
|   | ||||
| @@ -28,15 +28,39 @@ | ||||
|  | ||||
|   typedef struct { | ||||
|     #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|       float swap_length, extra_prime; | ||||
|       int16_t prime_speed, retract_speed; | ||||
|       float swap_length, extra_prime, extra_resume; | ||||
|       int16_t prime_speed, retract_speed, unretract_speed, fan, fan_speed, fan_time; | ||||
|     #endif | ||||
|     #if ENABLED(TOOLCHANGE_PARK) | ||||
|       bool enable_park; | ||||
|       xy_pos_t change_point; | ||||
|     #endif | ||||
|     TERN_(TOOLCHANGE_PARK, xy_pos_t change_point); | ||||
|     float z_raise; | ||||
|   } toolchange_settings_t; | ||||
|  | ||||
|   extern toolchange_settings_t toolchange_settings; | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) | ||||
|     extern void tool_change_prime(); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) | ||||
|     extern bool enable_first_prime; | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP) | ||||
|     extern bool toolchange_extruder_ready[EXTRUDERS]; | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) | ||||
|     typedef struct { | ||||
|       uint8_t target, last; | ||||
|       bool automode, in_progress; | ||||
|     } migration_settings_t; | ||||
|     constexpr migration_settings_t migration_defaults = { 0, 0, false, false }; | ||||
|     extern migration_settings_t migration; | ||||
|     void extruder_migration(); | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #if DO_SWITCH_EXTRUDER | ||||
|   | ||||
| @@ -33,6 +33,7 @@ opt_set TEMP_SENSOR_3 1 | ||||
| opt_set TEMP_SENSOR_4 1 | ||||
| opt_set TEMP_SENSOR_5 1 | ||||
| opt_set NUM_Z_STEPPER_DRIVERS 3 | ||||
| opt_enable TOOLCHANGE_FILAMENT_SWAP TOOLCHANGE_MIGRATION_FEATURE TOOLCHANGE_FS_INIT_BEFORE_SWAP TOOLCHANGE_FS_PRIME_FIRST_USED | ||||
| exec_test $1 $2 "BigTreeTech GTR 6 Extruders Triple Z" | ||||
|  | ||||
| # clean up | ||||
|   | ||||
		Reference in New Issue
	
	Block a user