##################################################################### # PURGE_UNTIL_EMPTY - purge a broken filament out through the nozzle # # When the filament snaps inside the PTFE tube, the piece below the break # is still in the extruder. This macro repeats the Blobifier purge and # measures each one with bd_pressure (load-check firmware). It stops after # the first purge without sustained nozzle pressure: the extruder no longer # has filament to push. # # What stays behind: the extruder can only push filament its gears still # grip. Once the tail has passed the gears, the last bit between the gears # and the nozzle stays in the hotend. The next normal load pushes it out. # # The AFC lane is unsynced first (UNSET_LANE_LOADED). Otherwise AFC keeps # feeding the lane together with the extruder, the spool side of the break # can catch up and the purge never ends. Afterwards AFC sees no lane loaded: # eject or reload the lane as usual. # # bdwidth sits at the start of the tube, so after the break it sees no # filament while the extruder keeps pushing. Its runout response (motion or # width) would pause the printer mid-purge, so it is switched off during the # purges (SET_FILAMENT_SENSOR) and restored afterwards. bdwidth is not used # to decide when to stop: only the nozzle pressure can see the last piece. # # Needs: bdpressure_check.py + [bdpressure_check] (see purge_check.cfg) # Blobifier, homed printer, hot nozzle (the macro heats if needed) # # Usage (printer idle or paused, not while printing): # PURGE_UNTIL_EMPTY defaults below # PURGE_UNTIL_EMPTY TEMP=240 MAX=10 filament temperature, purge limit # PURGE_UNTIL_EMPTY LENGTH=100 mm per purge (default: Blobifier's) # PURGE_UNTIL_EMPTY UNSYNC=0 keep the AFC lane synced # # Each purge prints one line; the end result says whether the nozzle ran # empty or MAX purges were reached (lane still feeding, or nozzle blocked). ##################################################################### [gcode_macro PURGE_UNTIL_EMPTY] description: Repeat the Blobifier purge until bd_pressure measures no more filament variable_max_purges: 15 # safety limit: ~1.2 m of bowden is ~8 purges of 150 mm variable_empty_ms: 5000 # longest pressure below this = no filament (good purge ~17 s, empty ~30 ms) variable_done: 0 variable_count: 0 gcode: {% set cfg = printer["gcode_macro PURGE_UNTIL_EMPTY"] %} {% set max_purges = params.MAX|default(cfg.max_purges)|int %} {% set length = params.LENGTH|default(0)|float %} {% set unsync = params.UNSYNC|default(1)|int %} {% set min_temp = printer.configfile.settings.extruder.min_extrude_temp|float %} {% set temp = params.TEMP|default([printer.extruder.target, 0]|max)|float %} {% if 'bdpressure_check' not in printer %} _ERROR MSG="PURGE_UNTIL_EMPTY: needs bdpressure_check.py and [bdpressure_check]" {% elif printer.print_stats.state == "printing" %} _ERROR MSG="PURGE_UNTIL_EMPTY: printer is printing, pause the print first" {% elif "xyz" not in printer.toolhead.homed_axes %} _ERROR MSG="PURGE_UNTIL_EMPTY: home the printer first" {% elif temp < min_temp %} _ERROR MSG="PURGE_UNTIL_EMPTY: give the filament temperature, e.g. PURGE_UNTIL_EMPTY TEMP=240" DISPLAY="PURGE_UNTIL_EMPTY: give TEMP=..." {% else %} BDP_CHECK_QUERY _PURGE_UNTIL_EMPTY_RUN MAX={max_purges} LENGTH={length} UNSYNC={unsync} TEMP={temp} {% endif %} # Rendered after BDP_CHECK_QUERY, so 'available' is current. [gcode_macro _PURGE_UNTIL_EMPTY_RUN] gcode: {% if not printer.bdpressure_check.available %} _ERROR MSG="PURGE_UNTIL_EMPTY: bd_pressure load-check firmware not found" DISPLAY="PURGE_UNTIL_EMPTY: no bd_pressure" {% endif %} SET_GCODE_VARIABLE MACRO=PURGE_UNTIL_EMPTY VARIABLE=done VALUE=0 SET_GCODE_VARIABLE MACRO=PURGE_UNTIL_EMPTY VARIABLE=count VALUE=0 {% if params.UNSYNC|int and printer.AFC.current_load %} RESPOND MSG="PURGE_UNTIL_EMPTY: unsyncing {printer.AFC.current_load} from the extruder (UNSET_LANE_LOADED)" UNSET_LANE_LOADED {% endif %} {% set bdw = printer.bdwidth_status.name if 'bdwidth_status' in printer and printer.bdwidth_status.present|default(true) else '' %} {% set runout_was = 1 if bdw and printer.bdwidth_status.enabled else 0 %} {% if runout_was %} RESPOND MSG="PURGE_UNTIL_EMPTY: bdwidth runout response off during the purges" SET_FILAMENT_SENSOR SENSOR={bdw} ENABLE=0 {% endif %} M109 S{params.TEMP} {% for i in range(params.MAX|int) %} _PURGE_UNTIL_EMPTY_STEP LENGTH={params.LENGTH} {% endfor %} _PURGE_UNTIL_EMPTY_RESULT MAX={params.MAX} BDW={bdw if runout_was else '-'} # Rendered when it runs, so it sees the result of the previous purge. [gcode_macro _PURGE_UNTIL_EMPTY_STEP] gcode: {% if not printer["gcode_macro PURGE_UNTIL_EMPTY"].done %} {% set length = params.LENGTH|default(0)|float %} M400 G4 P500 BDP_CHECK_START BLOBIFIER {'PURGE_LENGTH=%.1f' % length if length > 0 else ''} M400 BDP_CHECK_STOP _PURGE_UNTIL_EMPTY_EVAL {% endif %} [gcode_macro _PURGE_UNTIL_EMPTY_EVAL] gcode: {% set cfg = printer["gcode_macro PURGE_UNTIL_EMPTY"] %} {% set p = printer.bdpressure_check %} {% set n = cfg.count + 1 %} {% if not p.available %} _ERROR MSG="PURGE_UNTIL_EMPTY: lost the bd_pressure load-check reading, stopped. bdwidth runout response may still be off: SET_FILAMENT_SENSOR SENSOR= ENABLE=1" DISPLAY="PURGE_UNTIL_EMPTY: lost bd_pressure, stopped" {% endif %} {% set empty = p.longest_ms < cfg.empty_ms %} SET_GCODE_VARIABLE MACRO=PURGE_UNTIL_EMPTY VARIABLE=count VALUE={n} RESPOND MSG="PURGE_UNTIL_EMPTY purge {n}: pressure longest {p.longest_ms} ms, total {p.above_ms} ms, peak {p.peak} -> {'empty' if empty else 'filament'}" {% if empty %} SET_GCODE_VARIABLE MACRO=PURGE_UNTIL_EMPTY VARIABLE=done VALUE=1 {% endif %} [gcode_macro _PURGE_UNTIL_EMPTY_RESULT] gcode: {% set cfg = printer["gcode_macro PURGE_UNTIL_EMPTY"] %} {% if params.BDW|default('-') != '-' %} SET_FILAMENT_SENSOR SENSOR={params.BDW} ENABLE=1 {% endif %} {% if cfg.done %} RESPOND MSG="PURGE_UNTIL_EMPTY: no more filament after {cfg.count} purge{'s' if cfg.count != 1 else ''}. The last bit between the extruder gears and the nozzle is pushed out by the next load." {% else %} _WARN MSG="PURGE_UNTIL_EMPTY: still pressure after {params.MAX} purges. The lane may still be feeding, or the nozzle is blocked - check before running it again." DISPLAY="PURGE_UNTIL_EMPTY: still pressure, check lane and nozzle" {% endif %}