Streamlined filament change with preserved preheat

Updated 2026-06-11
CoreOne

The stock firmware's Change Filament command (M1600) treats the unload phase as LoadType::unload_confirm, which always parks the toolhead, homes if needed, and waits for the nozzle to reach extrusion temperature before unloading. The standalone Unload command (M702) already skips these steps when the filament is auto-retracted — the filament tip sits behind the heatbreak and can be pulled cold. Change Filament does not use the same fast path.

This affects the Core One (auto-retraction since firmware 6.3.1), MK4, and XL (auto-retraction since 6.4.0). The impact is largest on the Core One because its enclosed chamber gives the bed the longest preheat time.

The community firmware fixes three related issues:

  1. Change Filament parks and heats unnecessarilyLoadType::unload_confirm is not included in the cold-unload fast path
  2. Preheat menu does not focus the loaded filament — the menu always opens at the top of the list (PLA)
  3. Unload cancels the preheat sequence — a cold unload ends with a full cooldown, stopping the bed

How the system works

The firmware has three filament operations: Preheat (M1700), Load/Unload (M701/M702), and Change Filament (M1600).

Preheat (M1700)

The Preheat menu presents a list of filament types. Selecting one sets the nozzle to a standby temperature (lower than the full extrusion temperature), displays the full target temperature, and starts heating the bed and chamber. The nozzle remains at standby so that subsequent load heats quickly to the display temperature.

Load (M701) and Unload (M702)

Unload checks whether the filament is auto-retracted:

bool Pause::should_park() {
    switch (load_type) {
    case Pause::LoadType::unload:
#if HAS_AUTO_RETRACT()
        if (auto_retract().is_safely_retracted_for_unload(hotend_from_extruder(active_extruder))) {
            return false; // Cold unload — skip parking
        }
#endif
        // ... hot unload: park, then heat, then pull
    }
}

When the filament is safely retracted, Unload skips parking and heating and pulls the filament cold. The nozzle temperature is unchanged, so a running preheat sequence continues.

Change Filament (M1600)

Change Filament combines an unload phase and a load phase. The unload phase uses LoadType::unload_confirm, which was not included in the cold-unload fast path:

bool Pause::should_park() {
    switch (load_type) {
    case Pause::LoadType::unload_from_gears:
        return false;
    case Pause::LoadType::unload:
        // Only this type was fast-pathed for auto-retracted filament
#if HAS_AUTO_RETRACT()
        if (auto_retract().is_safely_retracted_for_unload(...)) {
            return false;
        }
#endif
        return true;
    case Pause::LoadType::unload_confirm:
        // Change Filament always lands here — always parks and heats
        return true;
    // ...
    }
}

is_target_temperature_safe() has the same omission: it only skips the temperature check for LoadType::unload, not unload_confirm. The result is that Change Filament always parks, homes if needed, and waits for the nozzle to heat — even when the filament is safely retracted.

Change Filament parks and heats unnecessarily

When the filament is auto-retracted, Change Filament performs a sequence that the standalone Unload command skips:

  1. Parks the toolhead (homing first if axes are not homed)
  2. Heats the nozzle to extrusion temperature
  3. Waits for the temperature to be reached
  4. Unloads the filament

Steps 1–3 are unnecessary when the filament is auto-retracted — the tip is behind the heatbreak and can be pulled cold. If the bed was preheating, the user also loses the preheat time spent during steps 1–3.

The fix is to include LoadType::unload_confirm in the same fast paths that LoadType::unload already uses:

case Pause::LoadType::unload:
+   case Pause::LoadType::unload_confirm:
#if HAS_AUTO_RETRACT()
    if (auto_retract().is_safely_retracted_for_unload(...)) {
        return false; // Cold unload
    }
#endif

PR #4 implements this for should_park() and is_target_temperature_safe().

Preheat menu does not focus the loaded filament

The Preheat menu always opens with the first item (PLA) focused. The printer already knows which filament is loaded; the menu does not use this information. The user must navigate the list every time.

The same issue occurs with the autoload dialog: when filament is inserted after preheating, the type-selection dialog starts from scratch even though the preheat menu choice indicates which filament is expected.

PR #10 fixes both: the Preheat menu focuses the currently loaded filament on open, and the firmware records the preheated type (filament::get_preheated_type()) for the autoload dialog to use.

static FilamentType preheated_type = FilamentType::none;

FilamentType filament::get_preheated_type() {
    return preheated_type;
}

void filament::set_preheated_type(FilamentType filament) {
    preheated_type = filament;
}

The preheated type is cleared by the Cooldown command.

Unload cancels the preheat sequence

When a preheat sequence is running and the user unloads filament, the unload operation ends with a full cooldown: nozzle temperature drops to zero and the bed target is lost. On the Core One, where bed preheat takes several minutes, this forces the user to restart the preheat after swapping spools.

The root cause is that M702 (Unload) ends with a full cooldown regardless of whether the unload was hot or cold. A cold unload does not touch the nozzle temperature, so there is no reason to cool anything down.

PR #23 makes cold unload end with DoneNoFilament (temperatures untouched) instead of a full cooldown. The bed continues heating uninterrupted.

Additionally, PR #23 makes the load phase re-arm the preheat sequence: if the loaded filament matches the preheated type, M1700_apply_preheat() re-triggers the full preheat state (nozzle at standby with display temperature, bed and chamber targets). After Preheat → Change Filament → (cold unload, swap, load), the printer is back in the preheat state.

History and status

Comparison with the manual flow

The fastest workflow on stock firmware is the manual sequence: Preheat → Unload → swap → Load. Unload cold-pulls instantly when auto-retracted, and Load heats during the load phase. Change Filament is slower because it performs unnecessary steps.

Step Stock Change Filament Manual Unload → Load Community Change Filament
Unload Park → home → heat → wait → unload Cold unload (instant) Cold unload (instant)
Swap Swap while nozzle cools Swap spool Swap while nozzle at standby
Load Heat → load Heat → load Heat → load
Preheat Lost — bed cools Lost — bed cools Preserved — bed continues
After load Nozzle at extrusion temp Nozzle at standby Nozzle at standby (preheat re-armed)

The community firmware's Change Filament now matches the manual flow and preserves the preheat state.

What the correct implementation looks like

The three PRs align Change Filament with the manual Unload → Load sequence.

1. Cold unload when auto-retracted (PR #4)

Include LoadType::unload_confirm in the same fast paths as LoadType::unload:

// Before (stock):
case Pause::LoadType::unload:
    if (auto_retract().is_safely_retracted_for_unload(...)) {
        return false; // Only unload was fast-pathed
    }

// After (community edition):
case Pause::LoadType::unload:
+   case Pause::LoadType::unload_confirm:
    if (auto_retract().is_safely_retracted_for_unload(...)) {
        return false; // Both unload types skip parking/heat
    }

The same change applies to is_target_temperature_safe().

2. Pre-select loaded filament in Preheat (PR #10)

Store the preheated filament type and use it:

// When the Preheat menu opens, focus the currently loaded filament
void WindowMenuPreheat::focus_loaded_filament() {
    const auto loaded = filament::get_type_to_load();
    // ... scroll to and focus the loaded filament's item
}

// When preheating completes, record the type for autoload to use
void M1700_preheat(const M1700Args &args) {
    filament::set_preheated_type(filament);
    // ...
}

3. Preserve preheat across Change Filament (PR #23)

Cold unload doesn't cool down. M702 ends with DoneNoFilament (temperatures untouched) instead of a full cooldown when the filament was safely retracted.

Autoload behaves like menu Load. M1701 parks at the load position (homing unhomed axes) and finishes via M70X_process_user_response(DoneHasFilament), dropping the nozzle to standby — matching the menu Load behavior.

Loading re-arms preheat. All three load flows (M701, M1600 phase 2, M1701) capture the pre-load nozzle state. When the loaded filament matches the preheated type and the preheat is still active, M1700_apply_preheat() re-triggers the preheat:

// Extracted from M1700 so load flows can re-trigger preheat
void M1700_apply_preheat(FilamentType filament, const M1700Args &args) {
    if (filament == FilamentType::none) {
        // Cooldown
    } else {
        const auto fil_cnf = filament.parameters();
        Temperature::setTargetHotend(fil_cnf.nozzle_temperature, extruder);
        marlin_server::set_temp_to_display(fil_cnf.nozzle_temperature, display_temp_extruder(extruder));
        // ... bed, chamber, heatbreak
    }
}

References

Research Notes

The three PRs were authored by FilipDolnik and merged in quick succession (June 6–7, 2026), suggesting they were developed and tested as a coordinated set. PR #4 is a minimal two-line fix; PR #10 is a focused UX improvement; PR #23 is the architectural change that depends on both.

The LoadType enum in pause.hpp defines the variants: load, unload, unload_confirm, unload_from_gears, change, and mmu_load. The distinction between unload (standalone Unload/M702) and unload_confirm (Change Filament/M1600 phase 1) existed because unload_confirm was expected to transition directly to a load phase and needed the nozzle hot for the ramming sequence. However, when auto-retraction is active, the ramming sequence is unnecessary — the filament is already safely behind the heatbreak, and the cold pull is identical for both load types.

The M1700_apply_preheat() extraction in PR #23 is notable because it splits the "set temperatures" part of M1700_preheat() from the "ask user which filament" part. This allows load flows to re-trigger the temperature settings without going through the menu dialog again.

Prusa Research has not publicly addressed the Change Filament slowness on any affected printer. The upstream firmware (6.4.2 as of June 2026) still has the unload_confirm fast-path gap and the preheat-destroying unload behavior.