Browse Source

Automatic Tool Migration feature (#17248)

vanilla_fb_2.0.x
studiodyne 4 years ago
committed by GitHub
parent
commit
2f6262c27b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      Marlin/Configuration_adv.h
  2. 9
      Marlin/src/feature/runout.cpp
  3. 112
      Marlin/src/gcode/config/M217.cpp
  4. 6
      Marlin/src/gcode/config/M221.cpp
  5. 5
      Marlin/src/gcode/sd/M1001.cpp
  6. 4
      Marlin/src/inc/Conditionals_post.h
  7. 13
      Marlin/src/inc/SanityCheck.h
  8. 3
      Marlin/src/lcd/extui/lib/dgus/DGUSDisplay.cpp
  9. 5
      Marlin/src/lcd/extui/ui_api.cpp
  10. 4
      Marlin/src/lcd/language/language_cz.h
  11. 6
      Marlin/src/lcd/language/language_de.h
  12. 15
      Marlin/src/lcd/language/language_en.h
  13. 4
      Marlin/src/lcd/language/language_es.h
  14. 17
      Marlin/src/lcd/language/language_fr.h
  15. 4
      Marlin/src/lcd/language/language_gl.h
  16. 4
      Marlin/src/lcd/language/language_it.h
  17. 4
      Marlin/src/lcd/language/language_pl.h
  18. 4
      Marlin/src/lcd/language/language_pt_br.h
  19. 4
      Marlin/src/lcd/language/language_tr.h
  20. 4
      Marlin/src/lcd/language/language_vi.h
  21. 4
      Marlin/src/lcd/language/language_zh_TW.h
  22. 49
      Marlin/src/lcd/menu/menu_configuration.cpp
  23. 24
      Marlin/src/module/configuration_store.cpp
  24. 12
      Marlin/src/module/planner.h
  25. 323
      Marlin/src/module/tool_change.cpp
  26. 30
      Marlin/src/module/tool_change.h
  27. 1
      buildroot/share/tests/BIGTREE_GTR_V1_0-tests

56
Marlin/Configuration_adv.h

@ -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

9
Marlin/src/feature/runout.cpp

@ -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)

112
Marlin/src/gcode/config/M217.cpp

@ -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

6
Marlin/src/gcode/config/M221.cpp

@ -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);

5
Marlin/src/gcode/sd/M1001.cpp

@ -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

4
Marlin/src/inc/Conditionals_post.h

@ -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
/**

13
Marlin/src/inc/SanityCheck.h

@ -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."

3
Marlin/src/lcd/extui/lib/dgus/DGUSDisplay.cpp

@ -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);

5
Marlin/src/lcd/extui/ui_api.cpp

@ -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; }

4
Marlin/src/lcd/language/language_cz.h

@ -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 *");

6
Marlin/src/lcd/language/language_de.h

@ -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");

15
Marlin/src/lcd/language/language_en.h

@ -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");

4
Marlin/src/lcd/language/language_es.h

@ -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");

17
Marlin/src/lcd/language/language_fr.h

@ -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 *");

4
Marlin/src/lcd/language/language_gl.h

@ -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");

4
Marlin/src/lcd/language/language_it.h

@ -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");

4
Marlin/src/lcd/language/language_pl.h

@ -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 *");

4
Marlin/src/lcd/language/language_pt_br.h

@ -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 *");

4
Marlin/src/lcd/language/language_tr.h

@ -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 *");

4
Marlin/src/lcd/language/language_vi.h

@ -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

4
Marlin/src/lcd/language/language_zh_TW.h

@ -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("更換絲料 *");

49
Marlin/src/lcd/menu/menu_configuration.cpp

@ -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
//

24
Marlin/src/module/configuration_store.cpp

@ -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)

12
Marlin/src/module/planner.h

@ -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.

323
Marlin/src/module/tool_change.cpp

@ -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

30
Marlin/src/module/tool_change.h

@ -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

1
buildroot/share/tests/BIGTREE_GTR_V1_0-tests

@ -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

Loading…
Cancel
Save