Skip to content

Custom Load/Unload Sequences

Concept

Happy Hare's own internal logic handles filament loading and unloading for every supported MMU design. For an unusual build that logic doesn't quite fit, the whole load/unload sequence can be replaced with your own gcode macros instead - built from the same composable movement commands the internal logic itself is built from.

This is genuinely the deepest level of customization Happy Hare offers, and it comes with a matching amount of responsibility: your macro becomes responsible for handling every starting position filament could be in, and for keeping Happy Hare's own idea of where the filament is in sync as it moves. Reach for it only once the lighter option below doesn't cover what you need.

Warning

Rewriting the sequence is expert-level customization. Read this whole page - especially the state machine below - before changing anything, and keep a backup of a working config to fall back to.

Two levels of customization

Most customization needs are met by a much lighter mechanism: a set of callback macros that fire at fixed points around every load/unload, regardless of any other setting on this page - _MMU_PRE_LOAD, _MMU_POST_LOAD, _MMU_PRE_UNLOAD, _MMU_POST_FORM_TIP, _MMU_POST_UNLOAD, _MMU_POST_PRELOAD, and _MMU_ERROR. Each one calls out to a user_*_extension gcode macro variable defined in mmu_macro_vars.cfg if you set one - the toolchange parking/z-hop/retract behaviour, timelapse-frame capture on load, and per-operation park positions are all controlled through this same file's _MMU_SEQUENCE_VARS section (see Macro Variables: Sequence/parking for the full list), without touching the sequence at all. This is the right place to start: adding a nozzle-wipe after loading, or injecting logic for an MMU-mounted cutter after unload, rarely needs more than one of these hooks.

The sequence override covered by the rest of this page is the level below that - a wholesale replacement of the movement logic itself, for a design esoteric enough that the callback hooks genuinely aren't enough.

The filament position state machine

Both the internal logic and any custom sequence macro track filament position as a single state, exposed as printer.mmu.filament_pos. A custom sequence must be able to complete correctly from any of these states - especially on unload, which can be called with filament anywhere along the path:

Value State Notes
-1 UNKNOWN
0 UNLOADED
1 HOMED_GATE Only reachable if a gate sensor is fitted
2 START_BOWDEN
3 IN_BOWDEN
4 END_BOWDEN
5 HOMED_ENTRY Only reachable if an extruder entry sensor is fitted
6 HOMED_EXTRUDER
7 PAST_EXTRUDER
8 HOMED_TS Only reachable if a toolhead sensor is fitted
9 IN_EXTRUDER Past the toolhead sensor
10 LOADED Homed to the nozzle

The state machine itself isn't extensible - a custom sequence works entirely by calling the step commands below in the right order for a given starting state, and explicitly setting the state as it goes. It also needs to respect the context it's called with (for example, the requested LENGTH may come from a test-load call, not a real toolchange) - the default sequence below is the reference for how to do that correctly.

The default sequence macros

_MMU_LOAD_SEQUENCE and _MMU_UNLOAD_SEQUENCE, shipped in mmu_sequence.cfg, reproduce the internal logic exactly using the same composable step commands documented below - they exist as a working starting point to copy and adapt, not as macros you're expected to edit in place (the file is treated as read-only and gets overwritten on upgrade).

The load sequence branches on how far the filament already got, then walks forward through the remaining steps:

_MMU_STEP_LOAD_GATE                      # only if fully unloaded
_MMU_STEP_LOAD_BOWDEN LENGTH={length}    # only if short of the end of the bowden
_MMU_STEP_HOME_EXTRUDER                  # only if HOME_EXTRUDER=1 and not yet homed
_MMU_STEP_LOAD_TOOLHEAD                  # only if not SKIP_EXTRUDER=1

The unload sequence is the mirror image, but has to additionally decide how much homing precision it can afford depending on how well the current position is actually known:

_MMU_STEP_UNLOAD_TOOLHEAD PARK_POS={park_pos}   # only if past the extruder
_MMU_STEP_UNLOAD_BOWDEN LENGTH={length}         # fast unload, if position is well known (>= END_BOWDEN)
_MMU_STEP_UNLOAD_GATE                           # ...then park in the gate
# or, if position is only loosely known (>= START_BOWDEN but < END_BOWDEN):
_MMU_STEP_UNLOAD_GATE FULL=1                    # slower full-homing unload instead, since exact position is unknown

Note

The shipped _MMU_UNLOAD_SEQUENCE passes FULL=1 to _MMU_STEP_UNLOAD_BOWDEN in the fast-unload branch above. _MMU_STEP_UNLOAD_BOWDEN doesn't have a FULL parameter (only LENGTH) - see the step reference below - so that argument is currently just ignored. Harmless as shipped (the step already runs at its default calibrated length either way), but worth knowing if you're reading this macro closely enough to copy it.

EXTRUDER_ONLY=1 (used for bypass operation) short-circuits both sequences to touch only the toolhead step, skipping the gate/bowden steps entirely.

Step command reference

Every step command is a real, independently callable MMU_* command - full parameters for each live in the Developer Command Reference, not repeated here.

Command Purpose
_MMU_STEP_LOAD_GATE Move filament from the gate to the start of the bowden
_MMU_STEP_UNLOAD_GATE Move filament from the start of the bowden back to parked in the gate
_MMU_STEP_LOAD_BOWDEN Smart bowden load, to the calibrated length or an override
_MMU_STEP_UNLOAD_BOWDEN Smart bowden unload
_MMU_STEP_HOME_EXTRUDER Home to the extruder entrance, by sensor or collision detection
_MMU_STEP_LOAD_TOOLHEAD Load from the extruder entrance to the nozzle
_MMU_STEP_UNLOAD_TOOLHEAD Unload from the nozzle back to the extruder entrance
_MMU_STEP_HOMING_MOVE Generic homing move on any motor/endstop combination
_MMU_STEP_MOVE Generic (non-homing) move on any motor combination
_MMU_STEP_SET_FILAMENT Directly set the filament position state - required bookkeeping any time a custom sequence's moves diverge from the defaults above
_MMU_STEP_SET_ACTION Set (and later restore) the printer.mmu.action status field, so UI feedback stays accurate during a custom sequence

Worked alternative examples

Two further examples live commented-out at the end of mmu_sequence.cfg, past the default sequences - both replace only the loading side, using more direct homing methods than the default's sensor/entry-based approach.

Homing to a toolhead sensor, with the gear and extruder synchronized for the final approach:

_MMU_STEP_LOAD_GATE
_MMU_STEP_LOAD_BOWDEN LENGTH={length}
_MMU_STEP_HOMING_MOVE ENDSTOP=toolhead MOVE=50 MOTOR=gear+extruder
_MMU_STEP_SET_FILAMENT STATE=8     # HOMED_TS
_MMU_STEP_MOVE MOVE=62 MOTOR=gear+extruder
_MMU_STEP_SET_FILAMENT STATE=10    # LOADED

Homing on stallguard directly to the nozzle, skipping a toolhead sensor entirely (needs an mmu_ext_touch endstop defined on the extruder stepper):

_MMU_STEP_LOAD_GATE
_MMU_STEP_LOAD_BOWDEN LENGTH={length}
_MMU_STEP_HOMING_MOVE ENDSTOP=mmu_ext_touch MOVE=100 MOTOR=extruder
_MMU_STEP_SET_FILAMENT STATE=10    # LOADED

Note

The shipped mmu_sequence.cfg's own commented-out version of this example uses MOTOR=extruder+gear - not one of _MMU_STEP_HOMING_MOVE's three valid MOTOR= values (gear, extruder, gear+extruder, checked directly against the command's own source), so running it as shipped raises "Valid motor names are..." rather than homing. It's also the wrong choice even corrected to gear+extruder - mmu_ext_touch is a stallguard (virtual) endstop on the extruder, and Happy Hare's own move-tracing docstring calls it out as "only useful for motor=extruder" specifically; synced motion isn't what triggers it. A stale leftover in Happy Hare's own reference macro either way, not a wiki or doc error this time - use MOTOR=extruder alone, as shown above.

Two related gotchas worth knowing before writing a homing move of your own: which endstops are even valid for a given MOTOR= depends on which stepper(s) it drives - toolhead/extruder endstops work with any MOTOR= that includes gear or extruder, but the two stallguard endstops are each tied to one specific stepper alone (mmu_gear_touch to MOTOR=gear, mmu_ext_touch to MOTOR=extruder), not to a synced combination of the two. And a virtual (stallguard) endstop can only home in the extrude direction - STOP_ON_ENDSTOP=-1 (retract) is rejected outright ("Cannot reverse home on virtual (TMC stallguard) endstop"), unlike a real physical switch.

Both examples still lean on the same _MMU_STEP_LOAD_GATE/_MMU_STEP_LOAD_BOWDEN building blocks for everything before the toolhead - a custom sequence rarely needs to reinvent gate/bowden handling, only the toolhead-entry portion that's actually unusual about the build.

Enabling a custom sequence

Once you have a replacement _MMU_LOAD_SEQUENCE/_MMU_UNLOAD_SEQUENCE defined in your own config (define your own copies, don't edit mmu_sequence.cfg in place - it's overwritten on upgrade), switch it on:

gcode_load_sequence   : 1   # 1 = use the macro sequence, 0 = internal logic (default)
gcode_unload_sequence : 1

Both live in mmu.cfg's shared parameters - see Parameters. If you renamed the macros themselves, point Happy Hare at the new names with load_sequence_macro/ unload_sequence_macro, also in mmu.cfg.

Troubleshooting

  • MMU_STATUS/printer.mmu.filament_pos looks wrong after a custom move - a step in your sequence changed the filament's real position without a matching _MMU_STEP_SET_FILAMENT call; every move needs the state kept in sync explicitly, it's never inferred.
  • A raised "already loaded"/"already unloaded" error from your own sequence - copy the default sequences' pattern of checking FILAMENT_POS first and raising early, rather than letting a step command fail deep into a move.
  • Works for a real toolchange but fails during calibration/testing - the sequence macros are also called for test loads, which can pass a different LENGTH than a real toolchange would; make sure the macro actually uses the passed parameters rather than a hardcoded assumption.

See also