From eaf9c826173be3dbee15c4a9cb8a34adf9764627 Mon Sep 17 00:00:00 2001 From: MangaValk Date: Sat, 10 Oct 2026 14:24:37 +0200 Subject: [PATCH] Group macros by topic, add Blobifier, fix first tool change - Split the macro files into folders by topic (print, heating, afc, pressure_advance, spoolman, leds, sensors); klipper-macros.cfg is the one file printer.cfg includes. Macros moved unchanged; the effective config is identical apart from the paths of the PA shell scripts. - Add the modified Blobifier (Turtleblobifier for AFC) and define [save_variables] once in save_variables.cfg instead of in both blobifier.cfg and the PA macros (same file, bd_pa_variables.cfg). - PRINT_WARMUP: heat to the loaded lane's temperature before the first tool change. AFC skips heating when the loaded lane's tool number is not in the print file's temperature list, and the unload then failed below min_extrude_temp. Co-Authored-By: Claude Opus 5.5 --- README.md | 82 +- install.sh | 3 +- macros/afc/lane_tools.cfg | 100 ++ macros/{ => afc}/purge_check.cfg | 0 macros/{ => afc}/purge_until_empty.cfg | 0 macros/afc/xy_safety_check.cfg | 70 ++ macros/afc_macros.cfg | 193 ---- macros/blobifier/blobifier.cfg | 919 ++++++++++++++++++ macros/blobifier/blobifier_hw.cfg | 26 + macros/{bedfans.cfg => heating/bed_fans.cfg} | 2 + .../chamber_iheater.cfg} | 2 + .../filament_drying.cfg} | 4 +- .../{heatsoak.cfg => heating/heat_soak.cfg} | 4 +- macros/heating/soak_my_bed.cfg | 22 + macros/klipper-macros.cfg | 60 +- .../stealthburner_leds.cfg} | 2 +- .../bd_pa_create_spoolman_field.sh | 0 .../bd_pa_spoolman.cfg} | 32 +- .../bd_pa_write_spoolman.sh | 0 macros/print/filament.cfg | 28 + macros/print/homing.cfg | 8 + macros/print/idle_timeout.cfg | 23 + .../pause_resume.cfg} | 31 +- .../{macros.cfg => print/print_start_end.cfg} | 67 +- macros/save_variables.cfg | 6 + .../{motion.cfg => sensors/motion_sensor.cfg} | 2 + macros/spoolman/active_spool.cfg | 20 + 27 files changed, 1366 insertions(+), 340 deletions(-) create mode 100644 macros/afc/lane_tools.cfg rename macros/{ => afc}/purge_check.cfg (100%) rename macros/{ => afc}/purge_until_empty.cfg (100%) create mode 100644 macros/afc/xy_safety_check.cfg delete mode 100644 macros/afc_macros.cfg create mode 100644 macros/blobifier/blobifier.cfg create mode 100644 macros/blobifier/blobifier_hw.cfg rename macros/{bedfans.cfg => heating/bed_fans.cfg} (98%) rename macros/{iHeater.cfg => heating/chamber_iheater.cfg} (99%) rename macros/{drying.cfg => heating/filament_drying.cfg} (99%) rename macros/{heatsoak.cfg => heating/heat_soak.cfg} (99%) create mode 100644 macros/heating/soak_my_bed.cfg rename macros/{stealthburner_led_effects_barf.cfg => leds/stealthburner_leds.cfg} (99%) rename macros/{ => pressure_advance}/bd_pa_create_spoolman_field.sh (100%) rename macros/{bd_pa_afc_spoolman.cfg => pressure_advance/bd_pa_spoolman.cfg} (95%) rename macros/{ => pressure_advance}/bd_pa_write_spoolman.sh (100%) create mode 100644 macros/print/filament.cfg create mode 100644 macros/print/homing.cfg create mode 100644 macros/print/idle_timeout.cfg rename macros/{macros_klipperscreen.cfg => print/pause_resume.cfg} (82%) rename macros/{macros.cfg => print/print_start_end.cfg} (86%) create mode 100644 macros/save_variables.cfg rename macros/{motion.cfg => sensors/motion_sensor.cfg} (93%) create mode 100644 macros/spoolman/active_spool.cfg diff --git a/README.md b/README.md index 52d1d47..de3fd0b 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,42 @@ Installs with one script and updates through Moonraker's update manager. ## Contents -### Macros (`macros/`, included through `klipper-macros.cfg`) +### Macros (`macros/`) + +`printer.cfg` includes only `macros/klipper-macros.cfg`; that file includes +everything below, grouped by topic. Comment out what you do not use there. | File | Macros | Needs | |---|---|---| -| `macros.cfg` | `PRINT_WARMUP`, `PRINT_START`, `PRINT_END`, `SOAK_MY_BED`, `[idle_timeout]` | Beacon, AFC, Blobifier, `HEAT_SOAK`, iHeater, bdwidth, bd_pressure PA macros, soak-my-bed | -| `afc_macros.cfg` | `AFC_XY_SAFETY_CHECK`, `_AFC_STORE_HOME_REFERENCE`, `AFC_XY_RESUME`, `LANE_FEED_UNTIL_HUB_OPEN`, `LANE_FEED_ABORT`, `HOME`, `SET_ACTIVE_SPOOL`, `CLEAR_ACTIVE_SPOOL` | AFC, `extras/xy_drift.py` (CoreXY) | +| `save_variables.cfg` | the one `[save_variables]` for all macros (file `bd_pa_variables.cfg` in the config folder) | — | +| **blobifier/** | | | +| `blobifier.cfg` | `BLOBIFIER`, `BLOBIFIER_CLEAN`, `BLOBIFIER_PARK`, `BLOBIFIER_SHAKE_BUCKET`, ...: purge bucket and nozzle brush for AFC (Turtleblobifier, heavily modified) | Blobifier hardware, AFC | +| `blobifier_hw.cfg` | servo and bucket switch pins | — | +| **print/** | | | +| `print_start_end.cfg` | `PRINT_WARMUP`, `PRINT_START`, `PRINT_END` | Beacon, AFC, Blobifier, `HEAT_SOAK`, iHeater, bdwidth, bd_pressure PA macros | +| `pause_resume.cfg` | `PAUSE`, `RESUME`, `CANCEL_PRINT`, `M600` | — | +| `filament.cfg` | `LOAD_FILAMENT`, `UNLOAD_FILAMENT` | — | +| `homing.cfg` | `HOME` | — | +| `idle_timeout.cfg` | `[idle_timeout]`: heaters, chamber heater and motors off | iHeater macros | +| **heating/** | | | +| `heat_soak.cfg` | `HEAT_SOAK`, `STOP_HEAT_SOAK`, `CANCEL_HEAT_SOAK`, `HEAT_SOAK_RESUME`, `RESUME` | — | +| `soak_my_bed.cfg` | `SOAK_MY_BED` (homes and levels first) | soak-my-bed | +| `bed_fans.cfg` | Bed fans through `SET_HEATER_TEMPERATURE`/`M190`/`M140`/`TURN_OFF_HEATERS` overrides | `[fan_generic BedFans]` | +| `chamber_iheater.cfg` | iHeater MCU, heater and sensors, `M141`, `M191`, `iHEATER_ON`, `iHEATER_OFF` | iHeater chamber heater | +| `filament_drying.cfg` | `START_DRYING`, `STOP_DRYING`, `STATUS_DRYING` | bed fans, chamber heater (optional) | +| **afc/** | | | +| `xy_safety_check.cfg` | `AFC_XY_SAFETY_CHECK`, `_AFC_STORE_HOME_REFERENCE`, `AFC_XY_RESUME`: lost-step check around tool changes | `extras/xy_drift.py` (CoreXY) | +| `lane_tools.cfg` | `LANE_FEED_UNTIL_HUB_OPEN`, `LANE_FEED_ABORT` | AFC | | `purge_check.cfg` | `BLOBIFIER_CHECKED`: checks every AFC purge with bd_pressure (and optionally bdwidth motion) | Blobifier, AFC, `extras/bdpressure_check.py` + bd_pressure load-check firmware, `extras/bdwidth_status.py` | | `purge_until_empty.cfg` | `PURGE_UNTIL_EMPTY`: purges a snapped filament out until there is no more nozzle pressure | same as `purge_check.cfg` | -| `bd_pa_afc_spoolman.cfg` + `bd_pa_*.sh` | `LANE_CALIBRATE_PA`, `PA_CALIBRATE_ALL_LANES`, `LANE_RECAL_PA`, `SET_SPOOL_ID`, `PA_STATUS`: pressure advance per lane/spool with bd_pressure, cached locally and in Spoolman | bd_pressure, AFC, Spoolman, `gcode_shell_command` | -| `motion.cfg` | PIR motion sensor (`[gcode_button am312]`) switching the case light | motion sensor on a pin, `[output_pin caselight]` | -| `drying.cfg` | `START_DRYING`, `STOP_DRYING`, `STATUS_DRYING`: dry filament on the heated bed | bed fans macros, chamber heater (optional) | -| `bedfans.cfg` | Bed fan control through `SET_HEATER_TEMPERATURE`/`M190`/`M140` overrides | `[fan_generic BedFans]` | -| `heatsoak.cfg` | `HEAT_SOAK`, `STOP_HEAT_SOAK`, `CANCEL_HEAT_SOAK`, `HEAT_SOAK_RESUME`, `RESUME` | — | -| `iHeater.cfg` | iHeater MCU, heater, sensors, `M141`, `M191`, `iHEATER_ON`, `iHEATER_OFF` | iHeater chamber heater | -| `stealthburner_led_effects_barf.cfg` | Stealthburner LED status macros (`_status_*`) | `led_effect`, `[neopixel sb_leds]` | -| `macros_klipperscreen.cfg` | `PAUSE`, `RESUME`, `CANCEL_PRINT`, `LOAD_FILAMENT`, `UNLOAD_FILAMENT`, `M600` | — | +| **pressure_advance/** | | | +| `bd_pa_spoolman.cfg` + `bd_pa_*.sh` | `LANE_CALIBRATE_PA`, `PA_CALIBRATE_ALL_LANES`, `LANE_RECAL_PA`, `SET_SPOOL_ID`, `PA_STATUS`: pressure advance per lane/spool, cached locally and in Spoolman | bd_pressure, AFC, Spoolman, `gcode_shell_command` | +| **spoolman/** | | | +| `active_spool.cfg` | `SET_ACTIVE_SPOOL`, `CLEAR_ACTIVE_SPOOL` | Spoolman in Moonraker | +| **leds/** | | | +| `stealthburner_leds.cfg` | Stealthburner LED effects and `_status_*` macros | `led_effect` | +| **sensors/** | | | +| `motion_sensor.cfg` | PIR motion sensor switching the case light | sensor on a pin, `[output_pin caselight]` | ### Klipper modules (`extras/`) @@ -88,14 +108,20 @@ original printer: `Macros/*.cfg` and `bd_pa_afc_spoolman.cfg`), remove those includes when adding `klipper-macros.cfg`, otherwise the macros are defined twice and the old copy may win: -- remove `[include bd_pa_afc_spoolman.cfg]` from `printer.cfg`, +- remove `[include bd_pa_afc_spoolman.cfg]` and + `[include ./blobifier/blobifier.cfg]` from `printer.cfg`, - replace `[include ./Macros/macros.cfg]` with `[include klipper-macros/klipper-macros.cfg]`, - keep including what is not in this repo yourself, for example `[include Macros/timelapse.cfg]` and `[include Macros/TEST_SPEED.cfg]`. -`bd_pa_variables.cfg` (saved PA values) stays where it is, in the config -folder; it is data, not part of the repo. +`bd_pa_variables.cfg` (saved PA values and the Blobifier blob count) stays +where it is, in the config folder; it is data, not part of the repo. Before, +both `blobifier.cfg` and `bd_pa_afc_spoolman.cfg` had their own +`[save_variables]`; Klipper merged them and the last one won, so the blob +count was already stored in `bd_pa_variables.cfg` and +`blobifier/blob_vars.cfg` was no longer used. Now it is defined once, in +`save_variables.cfg`. ## Adapting to another printer @@ -103,14 +129,16 @@ These are the settings of one printer. Before using a file, check: - **Names:** `fila_width_0` (bdwidth), `bd_pa` (bd_pressure), `sb_leds`, `caselight`, `BedFans`, `iHeater_H`, lane names `lane1..lane5`. -- **Pins and serial IDs:** `motion.cfg` (`PC0`), `iHeater.cfg` (serial ID). -- **Positions:** the bed centre `X175 Y175` (350 mm bed) in `HOME` - (`afc_macros.cfg`) and in `PRINT_WARMUP`/`PRINT_START` (`macros.cfg`). +- **Pins and serial IDs:** `blobifier/blobifier_hw.cfg` (`PC1`, `PC3`), + `sensors/motion_sensor.cfg` (`PC0`), + `heating/chamber_iheater.cfg` (serial ID). +- **Positions:** the bed centre `X175 Y175` (350 mm bed) in `print/homing.cfg` + and `print/print_start_end.cfg`. `AFC_XY_SAFETY_CHECK` uses the axis limits from your config. -- **Settings at the top of a file** (`variable_...`): `purge_check.cfg` +- **Settings at the top of a file** (`variable_...`): `afc/purge_check.cfg` (`mode: "enforce"`, `motion_check: False` because this printer's bdwidth - motion reading is unreliable), `bd_pa_afc_spoolman.cfg` (`spoolman_url`), - `bedfans.cfg`, `drying.cfg`. + motion reading is unreliable), `pressure_advance/bd_pa_spoolman.cfg` + (`spoolman_url`), `heating/bed_fans.cfg`, `heating/filament_drying.cfg`. - `xy_drift.py` is for CoreXY only. Comment out files you do not need in `klipper-macros.cfg`. @@ -132,17 +160,17 @@ original licenses apply to those parts: | File | Based on | |---|---| -| `stealthburner_led_effects_barf.cfg` | [tanaes/whopping_Voron_mods](https://github.com/tanaes/whopping_Voron_mods) (Rainbow Barf logo LEDs) | -| `bedfans.cfg` | Ellis' bed fan macros ([ellis3dp.com](https://ellis3dp.com/Print-Tuning-Guide/)) | -| `heatsoak.cfg` | community `HEAT_SOAK` macro | -| `iHeater.cfg` | iHeater vendor configuration | -| `drying.cfg` | community filament drying macro | -| `macros_klipperscreen.cfg` | [KlipperScreen recommended macros](https://klipperscreen.readthedocs.io/en/latest/macros/#recommended) | +| `blobifier/` | [Turtleblobifier](https://github.com/ImSundee/Turtleblobifier) (AFC version of [Dendrowen/Blobifier](https://github.com/Dendrowen/Blobifier)) | +| `leds/stealthburner_leds.cfg` | [tanaes/whopping_Voron_mods](https://github.com/tanaes/whopping_Voron_mods) (Rainbow Barf logo LEDs) | +| `heating/bed_fans.cfg` | Ellis' bed fan macros ([ellis3dp.com](https://ellis3dp.com/Print-Tuning-Guide/)) | +| `heating/heat_soak.cfg` | community `HEAT_SOAK` macro | +| `heating/chamber_iheater.cfg` | iHeater vendor configuration | +| `heating/filament_drying.cfg` | community filament drying macro | +| `print/pause_resume.cfg`, `print/filament.cfg` | [KlipperScreen recommended macros](https://klipperscreen.readthedocs.io/en/latest/macros/#recommended) | | `scripts/` | [Voron docs: automating MCU updates](https://docs.vorondesign.com/community/howto/drachenkatze/automating_klipper_mcu_updates.html), [Esoterical's CANBus guide](https://canbus.esoterical.online/) | Uses: [Klipper](https://github.com/Klipper3d/klipper), [AFC-Klipper-Add-On](https://github.com/ArmoredTurtle/AFC-Klipper-Add-On), -[Blobifier](https://github.com/Dendrowen/Blobifier), [bd_pressure](https://github.com/markniu/bd_pressure), [bdwidth](https://github.com/markniu/bdwidth), [klipper-led_effect](https://github.com/julianschill/klipper-led_effect), diff --git a/install.sh b/install.sh index 8650dca..66309af 100755 --- a/install.sh +++ b/install.sh @@ -69,7 +69,8 @@ ln -sfn "$REPO_DIR/macros" "$LINK_NAME" echo "linked $LINK_NAME" # 3. Scripts -chmod +x "$REPO_DIR"/macros/*.sh "$REPO_DIR"/scripts/*.sh "$REPO_DIR"/install.sh +find "$REPO_DIR/macros" "$REPO_DIR/scripts" -name "*.sh" -exec chmod +x {} + +chmod +x "$REPO_DIR/install.sh" # 4. Moonraker update manager if [[ -f $MOONRAKER_CONF ]] && ! grep -q '^\[update_manager klipper-macros\]' "$MOONRAKER_CONF"; then diff --git a/macros/afc/lane_tools.cfg b/macros/afc/lane_tools.cfg new file mode 100644 index 0000000..dee5828 --- /dev/null +++ b/macros/afc/lane_tools.cfg @@ -0,0 +1,100 @@ +# AFC lane tools: LANE_FEED_UNTIL_HUB_OPEN, LANE_FEED_ABORT + +##################################################################### +## unloading help +##################################################################### + +[gcode_macro LANE_FEED_UNTIL_HUB_OPEN] +variable_lane: 1 +variable_step: -50 +variable_max_dist: 2000 +variable_moved: 0 +variable_active: 0 + +gcode: + {% set lane = params.LANE|default(1)|int %} + {% set step = params.STEP|default(-50)|float %} + {% set max_dist = params.MAX_DIST|default(2000)|float %} + + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=lane VALUE={lane} + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=step VALUE={step} + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=max_dist VALUE={max_dist} + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=moved VALUE=0 + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=1 + + RESPOND PREFIX="info" MSG="START lane={lane} step={step} max={max_dist}" + + UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0.05 + +[delayed_gcode _lane_feed_loop] +gcode: + {% set m = printer["gcode_macro LANE_FEED_UNTIL_HUB_OPEN"] %} + + {% if m.active == 1 %} + + LANE_MOVE LANE=lane{m.lane} DISTANCE={m.step} + + {% set new_moved = m.moved + m.step %} + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=moved VALUE={new_moved} + + RESPOND PREFIX="info" MSG="MOVE lane={m.lane} step={m.step} moved={new_moved}" + + QUERY_ENDSTOPS + + UPDATE_DELAYED_GCODE ID=_lane_eval DURATION=0.03 + + {% else %} + RESPOND PREFIX="info" MSG="feed_loop inactive" + {% endif %} + +[delayed_gcode _lane_eval] +gcode: + {% set m = printer["gcode_macro LANE_FEED_UNTIL_HUB_OPEN"] %} + + {% if m.active == 1 %} + + {% set hub = printer.query_endstops.last_query.get("hub", "unknown") %} + + # RESPOND PREFIX="info" MSG="QUERY hub={hub} raw={printer.query_endstops.last_query.get('hub', 'MISSING')}" + # RESPOND PREFIX="info" MSG="last_query: {printer.query_endstops.last_query}" + + {% if hub == "open" or hub == 0 %} + RESPOND PREFIX="info" MSG="HUB TRIGGERED → STOP" + + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 + + LANE_MOVE LANE=lane{m.lane} DISTANCE=-100 + + {% elif m.moved|abs >= m.max_dist %} + + RESPOND PREFIX="warning" MSG="MAX DIST STOP reached={m.moved}" + + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 + + {% else %} + + RESPOND PREFIX="info" MSG="CONTINUE" + + UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0.03 + + {% endif %} + + {% else %} + RESPOND PREFIX="info" MSG="eval inactive" + {% endif %} + +[gcode_macro LANE_FEED_ABORT] +gcode: + SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 + UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0 + UPDATE_DELAYED_GCODE ID=_lane_eval DURATION=0 + RESPOND PREFIX="warning" MSG="ABORTED" + +# [gcode_macro TEST_AMS_STRESS] +# gcode: +# {% for i in range(500) %} +# T0 ; Load tool 0 +# G4 P5000 ; Wait 5s +# T1 ; Unload +# G4 P3000 ; Wait 3s +# {% endfor %} diff --git a/macros/purge_check.cfg b/macros/afc/purge_check.cfg similarity index 100% rename from macros/purge_check.cfg rename to macros/afc/purge_check.cfg diff --git a/macros/purge_until_empty.cfg b/macros/afc/purge_until_empty.cfg similarity index 100% rename from macros/purge_until_empty.cfg rename to macros/afc/purge_until_empty.cfg diff --git a/macros/afc/xy_safety_check.cfg b/macros/afc/xy_safety_check.cfg new file mode 100644 index 0000000..9495074 --- /dev/null +++ b/macros/afc/xy_safety_check.cfg @@ -0,0 +1,70 @@ +# AFC_XY_SAFETY_CHECK: lost-step check around tool changes (needs extras/xy_drift.py) + +##################################################################### +## XY lost-step check (needs xy_drift.py + [xy_drift]) +##################################################################### +# Homing resets the toolhead position, so the position after G28 cannot +# show lost steps. [xy_drift] compares the stepper step counts at the X and +# Y endstop triggers instead: every lost step shows up as drift. +# _AFC_STORE_HOME_REFERENCE (PRINT_START) stores the reference, every +# AFC_XY_SAFETY_CHECK compares against it and then stores a new reference, +# so each check reports the drift since the previous one. + +[xy_drift] + +[gcode_macro _AFC_STORE_HOME_REFERENCE] +gcode: + G28 X Y + M400 + XY_DRIFT_STORE + +[gcode_macro AFC_XY_SAFETY_CHECK] +description: Re-home XY and report steps lost since the last check +variable_allowed_mm: 0.1 # one motor skipping one cycle (0.4 mm) shows as 0.2 mm in X and Y; endstop noise ~0.02 +variable_soft_fail: 1 # 1 = PAUSE on drift, 0 = M112 +gcode: + {% set th = printer.toolhead %} + M117 AFC XY check... + SAVE_GCODE_STATE NAME=_AFC_XY_CHECK + G90 + G1 Z{[th.position.z + 10, th.axis_maximum.z]|min} F3000 + G28 X Y + M400 + _AFC_CHECK_CAPTURE + RESTORE_GCODE_STATE NAME=_AFC_XY_CHECK + +[gcode_macro _AFC_CHECK_CAPTURE] +gcode: + {% set d = printer.xy_drift %} + {% set cfg = printer["gcode_macro AFC_XY_SAFETY_CHECK"] %} + {% set th = printer.toolhead %} + {% set lim = cfg.allowed_mm|float %} + {% set drift = d.ref_set and (d.x|abs > lim or d.y|abs > lim) %} + {% if not d.ref_set %} + RESPOND PREFIX="AFC" MSG="XY check: no reference yet (Klipper restarted?), storing one now" + {% else %} + RESPOND PREFIX="AFC" MSG="XY drift since last check: X={'%.3f' % d.x} Y={'%.3f' % d.y} mm (limit {lim})" + {% endif %} + # New reference from this homing, so the next check reports only new drift + XY_DRIFT_STORE + {% if not drift %} + {% if d.ref_set %} + RESPOND PREFIX="AFC" MSG="Position OK" + {% endif %} + G1 X{th.axis_maximum.x - 10} Y{th.axis_maximum.y - 10} F5000 + {% elif cfg.soft_fail|int == 1 %} + RESPOND TYPE=error MSG="AFC: XY drift detected! X={'%.3f' % d.x} Y={'%.3f' % d.y} mm. Axes are re-homed, print PAUSED. Run AFC_XY_RESUME to continue, or CANCEL_PRINT." + M117 AFC: XY drift! See console. Paused. + PAUSE + {% else %} + RESPOND TYPE=error MSG="AFC: XY drift X={'%.3f' % d.x} Y={'%.3f' % d.y} mm -> M112" + M112 + {% endif %} + +[gcode_macro AFC_XY_RESUME] +description: Acknowledge XY drift warning and resume print +gcode: + RESPOND PREFIX="AFC" MSG="User acknowledged drift, resuming." + # Axes were re-homed by the check, so AFC's resume returns to the + # correct print position. + RESUME diff --git a/macros/afc_macros.cfg b/macros/afc_macros.cfg deleted file mode 100644 index be08815..0000000 --- a/macros/afc_macros.cfg +++ /dev/null @@ -1,193 +0,0 @@ -##################################################################### -## XY lost-step check (needs xy_drift.py + [xy_drift]) -##################################################################### -# Homing resets the toolhead position, so the position after G28 cannot -# show lost steps. [xy_drift] compares the stepper step counts at the X and -# Y endstop triggers instead: every lost step shows up as drift. -# _AFC_STORE_HOME_REFERENCE (PRINT_START) stores the reference, every -# AFC_XY_SAFETY_CHECK compares against it and then stores a new reference, -# so each check reports the drift since the previous one. - -[xy_drift] - -[gcode_macro _AFC_STORE_HOME_REFERENCE] -gcode: - G28 X Y - M400 - XY_DRIFT_STORE - -[gcode_macro AFC_XY_SAFETY_CHECK] -description: Re-home XY and report steps lost since the last check -variable_allowed_mm: 0.1 # one motor skipping one cycle (0.4 mm) shows as 0.2 mm in X and Y; endstop noise ~0.02 -variable_soft_fail: 1 # 1 = PAUSE on drift, 0 = M112 -gcode: - {% set th = printer.toolhead %} - M117 AFC XY check... - SAVE_GCODE_STATE NAME=_AFC_XY_CHECK - G90 - G1 Z{[th.position.z + 10, th.axis_maximum.z]|min} F3000 - G28 X Y - M400 - _AFC_CHECK_CAPTURE - RESTORE_GCODE_STATE NAME=_AFC_XY_CHECK - -[gcode_macro _AFC_CHECK_CAPTURE] -gcode: - {% set d = printer.xy_drift %} - {% set cfg = printer["gcode_macro AFC_XY_SAFETY_CHECK"] %} - {% set th = printer.toolhead %} - {% set lim = cfg.allowed_mm|float %} - {% set drift = d.ref_set and (d.x|abs > lim or d.y|abs > lim) %} - {% if not d.ref_set %} - RESPOND PREFIX="AFC" MSG="XY check: no reference yet (Klipper restarted?), storing one now" - {% else %} - RESPOND PREFIX="AFC" MSG="XY drift since last check: X={'%.3f' % d.x} Y={'%.3f' % d.y} mm (limit {lim})" - {% endif %} - # New reference from this homing, so the next check reports only new drift - XY_DRIFT_STORE - {% if not drift %} - {% if d.ref_set %} - RESPOND PREFIX="AFC" MSG="Position OK" - {% endif %} - G1 X{th.axis_maximum.x - 10} Y{th.axis_maximum.y - 10} F5000 - {% elif cfg.soft_fail|int == 1 %} - RESPOND TYPE=error MSG="AFC: XY drift detected! X={'%.3f' % d.x} Y={'%.3f' % d.y} mm. Axes are re-homed, print PAUSED. Run AFC_XY_RESUME to continue, or CANCEL_PRINT." - M117 AFC: XY drift! See console. Paused. - PAUSE - {% else %} - RESPOND TYPE=error MSG="AFC: XY drift X={'%.3f' % d.x} Y={'%.3f' % d.y} mm -> M112" - M112 - {% endif %} - -[gcode_macro AFC_XY_RESUME] -description: Acknowledge XY drift warning and resume print -gcode: - RESPOND PREFIX="AFC" MSG="User acknowledged drift, resuming." - # Axes were re-homed by the check, so AFC's resume returns to the - # correct print position. - RESUME - -##################################################################### -## unloading help -##################################################################### - -[gcode_macro LANE_FEED_UNTIL_HUB_OPEN] -variable_lane: 1 -variable_step: -50 -variable_max_dist: 2000 -variable_moved: 0 -variable_active: 0 - -gcode: - {% set lane = params.LANE|default(1)|int %} - {% set step = params.STEP|default(-50)|float %} - {% set max_dist = params.MAX_DIST|default(2000)|float %} - - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=lane VALUE={lane} - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=step VALUE={step} - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=max_dist VALUE={max_dist} - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=moved VALUE=0 - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=1 - - RESPOND PREFIX="info" MSG="START lane={lane} step={step} max={max_dist}" - - UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0.05 - -[delayed_gcode _lane_feed_loop] -gcode: - {% set m = printer["gcode_macro LANE_FEED_UNTIL_HUB_OPEN"] %} - - {% if m.active == 1 %} - - LANE_MOVE LANE=lane{m.lane} DISTANCE={m.step} - - {% set new_moved = m.moved + m.step %} - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=moved VALUE={new_moved} - - RESPOND PREFIX="info" MSG="MOVE lane={m.lane} step={m.step} moved={new_moved}" - - QUERY_ENDSTOPS - - UPDATE_DELAYED_GCODE ID=_lane_eval DURATION=0.03 - - {% else %} - RESPOND PREFIX="info" MSG="feed_loop inactive" - {% endif %} - -[delayed_gcode _lane_eval] -gcode: - {% set m = printer["gcode_macro LANE_FEED_UNTIL_HUB_OPEN"] %} - - {% if m.active == 1 %} - - {% set hub = printer.query_endstops.last_query.get("hub", "unknown") %} - - # RESPOND PREFIX="info" MSG="QUERY hub={hub} raw={printer.query_endstops.last_query.get('hub', 'MISSING')}" - # RESPOND PREFIX="info" MSG="last_query: {printer.query_endstops.last_query}" - - {% if hub == "open" or hub == 0 %} - RESPOND PREFIX="info" MSG="HUB TRIGGERED → STOP" - - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 - - LANE_MOVE LANE=lane{m.lane} DISTANCE=-100 - - {% elif m.moved|abs >= m.max_dist %} - - RESPOND PREFIX="warning" MSG="MAX DIST STOP reached={m.moved}" - - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 - - {% else %} - - RESPOND PREFIX="info" MSG="CONTINUE" - - UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0.03 - - {% endif %} - - {% else %} - RESPOND PREFIX="info" MSG="eval inactive" - {% endif %} - -[gcode_macro LANE_FEED_ABORT] -gcode: - SET_GCODE_VARIABLE MACRO=LANE_FEED_UNTIL_HUB_OPEN VARIABLE=active VALUE=0 - UPDATE_DELAYED_GCODE ID=_lane_feed_loop DURATION=0 - UPDATE_DELAYED_GCODE ID=_lane_eval DURATION=0 - RESPOND PREFIX="warning" MSG="ABORTED" - -[gcode_macro HOME] -gcode: - G90 - G4 P2000 - G28 X0 Y0 Z0 - G1 X175 Y175 Z10 F1200 - -[gcode_macro SET_ACTIVE_SPOOL] -gcode: - {% if params.ID %} - {% set id = params.ID|int %} - {action_call_remote_method( - "spoolman_set_active_spool", - spool_id=id - )} - {% else %} - {action_respond_info("Parameter 'ID' is required")} - {% endif %} - -[gcode_macro CLEAR_ACTIVE_SPOOL] -gcode: - {action_call_remote_method( - "spoolman_set_active_spool", - spool_id=None - )} - -# [gcode_macro TEST_AMS_STRESS] -# gcode: -# {% for i in range(500) %} -# T0 ; Load tool 0 -# G4 P5000 ; Wait 5s -# T1 ; Unload -# G4 P3000 ; Wait 3s -# {% endfor %} \ No newline at end of file diff --git a/macros/blobifier/blobifier.cfg b/macros/blobifier/blobifier.cfg new file mode 100644 index 0000000..46da6a7 --- /dev/null +++ b/macros/blobifier/blobifier.cfg @@ -0,0 +1,919 @@ +# Include servo hardware definition separately to allow for automatic upgrade +[include ./blobifier_hw.cfg] + +########################################################################################## + +# Sample config to be used in conjunction with Blobifier Purge Tray, Bucket & Nozzle +# Scrubber mod. Created by Dendrowen (dendrowen on Discord). The Macro is based on a +# version, and Nozzle Scrubber is made by Hernsl (hernsl#8860 on Discord). The device is +# designed around a Voron V2.4 300mm, but should work for 250mm and 350mm too. This +# version only supports the assembly on the rear-left of the bed. If you decide to change +# that, please consider contributing to the project by creating a pull request with the +# needed changes. + +# IMPORTANT: The rear-left part of your bed becomes unusable by this mod because the +# toolhead needs to lower down to 0. Be sure not to use the left-rear 130x35mm. + +# The goals of this combination of devices is to dispose of purged filament during a +# multicolored print without the need of a purge block and without the flurries of +# filament poops consuming your entire 3D printer room. The Blobifier achieves that by +# purging onto a retractable tray which causes the filament to turn into a tiny blob +# rather then a large spiral. This keeps the waste relativly small. The bucket should be +# able to account for up to 200 filament swaps (for the 300mm V2). + +# The Blobifier uses some room at the back-left side of your printer, depending on your +# printer limits and positions. (usually max_pos.y - toolhead_y and brush_start + +# brush_width + toolhead_x). If you do place objects within this region, Blobifier will +# skip purging automatically. It does this by extending the EXCLUDE_OBJECT_* macro's, so +# make sure you have exclude objects enabled in your slicer. + +# If your using Blobifier in conjunction with the filament cutter on the stealthburner +# toolhead, you can place the pin at max_pos.y - 7 (e.g., max pos y is 307, place it at +# 300). The pin will then poke through the cavity in your toolhead. (Be careful with +# manually moving the toolhead. I have broken many filament cutter pins) + +# It is advised to use the start_gcode from Happy Hare. Then you will be able to fully +# and efficiently use this mod. Check the Happy Hare document at gcode_preprocessing.md +# in the Happy Hare github for more details. + +###################################### DISCLAIMER ######################################## + +# You, and you alone, are responsible for the correct execution of these macros and +# gcodes. Any damage that may occur to your machine remains your responsibility. +# Especially when executing this macro for the first few times, keep an eye on your +# printer and the +# emergency stop. + +########################################################################################## + +########################################################################################## +# Main macro. Usually you should only need to call this one or place it in the Happy Hare +# _MMU_POST_LOAD macro using the variable_user_post_load_extension: +# +# variable_user_post_load_extension : `BLOBIFIER` +# +# Notes on parameters: +# PURGE_LENGTH=[float] (optional) The length to purge. If ommited (default) it will check +# the purge_volumes matrix or variable_purge_length. This can be used +# to override and for testing. +# +[gcode_macro BLOBIFIER] +# These parameters define your filament purging. +# Note that the control of retraction is set in 'mmu_macro_vars.cfg' which can be increased +# if you experience excessive oozing. +# variable_purge_spd: 250 #300 # Speed, in mm/min, of the purge. +variable_purge_spd: 150 # Speed, in mm/min, of the purge. +variable_purge_temp_min: 200 # Minimum nozzle purge temperature. +variable_toolhead_x: 70 # From the nozzle to the left of your toolhead +variable_toolhead_y: 50 # From the nozzle to the front of your toolhead + +variable_retract_before_park: 3.0 # amount in mm. 0 to disable +variable_retract_speed: 2000 # mm/m + + +##custom +variable_retracted_length: 0.4 # Amount of current retraction + +# This macro will prevent a gcode movement downward while 'blobbing' if there might be a +# print in the way (e.g. You print something large and need the area where Blobifier does +# its... 'business'). However, at low heights (or at print start) this might not be +# desireable. You can force a 'safe descend' with this variable. Keep in mind that the +# height of the print is an estimation based on previous heights and certain assumptions +# so it might be wise to include a safety margin of 0.2mm +variable_force_safe_descend_height_until: 2.0 + +# Adjust this so that your nozzle scrubs within the brush. Be careful not to go too low! +# Start out with a high value (like, 6) and go +# down from there. +variable_brush_top: -0.5 + +# These parameters define your scrubbing, travel speeds, safe z clearance and how many +# times you want to wipe. Update as necessary. +variable_clearance_z: 5 # When traveling, but not cleaning, the + # clearance along the z-axis between nozzle + # and brush. +variable_wipe_qty: 4 # Number of complete (A complete wipe: left, + # right, left OR right, left, right) wipes. +variable_travel_spd_xy: 25000 # Travel (not cleaning) speed along x and + # y-axis in mm/min. +variable_travel_spd_z: 2000 # Travel (not cleaning) speed along z axis + # in mm/min. +variable_wipe_spd_xy: 30000 # Nozzle wipe speed in mm/min. + +# The acceleration to use when using the brush action. If set to 0, it uses the already +# set acceleration. However, in some cases this is not desirable for the last motion +# could be an 'outer contour' acceleration which is usually lower. +variable_brush_accel: 10000 + +# Blobifier sends the toolhead to the maximum y position during purge oeprations and +# minimum x position during shake operations. This can cause issues when skew correction +# is set up. If you have skew correction enabled and get 'move out of range' errors +# regarding blobifier while skew is enabled, try increasing this value. Keep the +# adjustments small though! (0.1mm - 0.5mm) and increase it until it works. +variable_skew_correction: 0.1 + +# These parameters define the size of the brush. Update as necessary. A visual reference +# is provided below. +# +# ← brush_width → +# _________________ +# | | ↑ Y position is acquired from your +# brush_start (x) | | brush_depth stepper_y position_max. Adjust +# |_________________| ↓ your brush physically in Y so +# (y) that the nozzle scrubs within the +# brush_front brush. +# __________________________________________________________ +# PRINTER FRONT +# +# +# Start location of the brush. Defaults for 250, 300 and 350mm are provided below. +# Uncomment as necessary +#variable_brush_start: 34 # For 250mm build +#variable_brush_start: 52 # For 300mm build +# variable_brush_start: 84 # for 350mm build +variable_brush_start: 48 # for 350mm build + +# width of the brush +variable_brush_width: 45 + +# Location of where to purge. The tray is 15mm in length, so if you assemble it against +# the side of the bed (default), 10mm is a good location +variable_purge_x: 2 + +# Height of the tray. If it's below your bed, give this a negative number equal to the +# difference. If it's above your bed, give it a positive number. You can find this number +# by homing, optional QGL or equivalent, and moving you toolhead above the tray, and +# lowering it with the paper method. +variable_tray_top: 0 + +# Servo angles for tray positions +variable_tray_angle_out: 0 +variable_tray_angle_in: 180 + +# ======================================================================================== +# ==================== BLOB TUNING ======================================================= +# ======================================================================================== + +# The following section defines how the purging sequence is executed. This is where you +# tune the purging to create pretty blobs. Refer to the visual reference for a better +# understanding. The visual is populated with example values. Below are some guides +# provided to help with tuning. +# +# \_____________/ +# |___|___| +# \_/ ______________ < End of third iteration. +# / \ HEIGHT: 3 x iteration_z_raise - (2 + 1) x iteration_z_change (3 x 5 - 2 x 1.2 = 11.4) +# | | EXTRUDED: 3 x max_iteration_length (3 x 50 = 150) +# / \ ______________ < End of second iteration. +# | \ HEIGHT: 2 x iteration_z_raise - 1 x iteration_z_change (2 x 5 - 1 x 1.2 = 8.8) +# / | EXTRUDED: 2 x max_iteration_length (2 x 50 = 100) +# | \ ______________ < End of first iteration. +# / \ HEIGHT: 1 x iteration_z_raise (1 x 5 = 5) +# | | EXTRUDED: 1 x max_iteration_length (1 x 50 = 50) +#___________ \ / ______________ < Start height of the nozzle. default value: 1.5mm +# |_______________\___________/_ ______________ < Bottom of the tray +# |_____________________________| +# | +# +########################### BLOB TUNING ############################## +# +-------------------------------------+----------------------------+ +# | Filament sticks to the nozzle at | Incr. purge start | +# | initial purge (first few mm) | | +# +-------------------------------------+----------------------------+ +# | Filament scoots out from under | Incr. temperature | +# | the nozzle at the first iteration | Decr. z_raise | +# | | Incr. purge_length_maximum | +# +-------------------------------------+----------------------------+ +# | Filament scoots out from under the | Decr. purge_spd | +# | the nozzle at later iterations | Decr. z_raise_exp | +# | | Decr. z_raise | +# | | Incr. purge_length_maximum | +# +-------------------------------------+----------------------------+ +# | Filament sticks to the nozzle at | Incr. z_raise_exp | +# | later iterations | (Not above 1) | +# +-------------------------------------+----------------------------+ +# + +# The height to raise the nozzle above the tray before purging. This allows any built up +# pressure to escape before the purge. +variable_purge_start: -0.8 #0.2 + +# The amount to raise Z +variable_z_raise: 12 #12 + +# As the nozzle gets higher and the blob wider, the Z raise needs to be reduced, this +# follows the following formula: +# (extruded_amount/max_purge_length)^z_raise_exp * z_raise +# 1 is linear, below 1 will cause z to raise less quickly over time, above 1 will make it +# raise quicker over time. 0.85 is a good starting point and you should not have it above 1 +variable_z_raise_exp: 0.85 #0.85 + +# Lift the nozzle slightly after creating the blob te release pressure on the tray. +variable_eject_hop: 6.0 + +# Dwell time (ms) after purging and before cleaning to relieve pressure from the nozzle. +variable_pressure_release_time: 3000 #1000 + +# Set the part cooling fan speed. Disabling can help prevent the nozzle from cooling down +# and stimulate flow, Enabling it can prevent blobs from sticking together. Values range +# from 0 .. 1, or -1 if you don't want it changed. +#variable_part_cooling_fan: -1 # Leave it unchanged +#variable_part_cooling_fan: 0 # Disable the fan +variable_part_cooling_fan: 1 # Run it at full speed + + +# ======================================================================================== +# ==================== PURGE LENGTH TUNING =============================================== +# ======================================================================================== + +# The absolute minimum to purge, even if you don't changed tools. This is to prime the +# nozzle before printing +variable_purge_length_minimum: 35 + +# The maximum amount of filament (in mm¹) to purge in a single blob. Blobifier will +# automatically purge multiple blobs if the purge amount exeeds this. +variable_purge_length_maximum: 80 #120 + +# Default purge length to fall back on when neither the tool map purge_volumes or +# parameter PURGE_LENGTH is set. +variable_purge_length: 150 + +# The slicer values often are a bit too wastefull. Tune it here to get optimal values. +# 0.6 (60%) is a good starting point. +variable_purge_length_modifier: 0.5 + +# Fixed length of filament to add after the purge volume calculation. Happy Hare already +# shares info on the extra amount of filament to purge based on known residual filament, +# tip cutting fragment and initial retraction setting. However this setting can add a fixed +# amount on top on that if necessary although it is recommended to start with 0 and tune +# slicer purge matrix first. +# When should you alter this value: +# INCREASE: When the dark to light swaps are good, but light to dark aren't. +# DECREASE: When the light to dark swaps are good, but dark to light aren't. Don't +# forget to increase the purge_length_modifier +variable_purge_length_addition: 20 # 30 + +# ======================================================================================== +# ==================== BUCKET ============================================================ +# ======================================================================================== + +# Maximum number of blobs that fit in the bucket. Pauses the print if it exceeds this +# number. +variable_max_blobs: 1300 +# Enable the bucket shaker. You need to have the shaker.stl installed +variable_enable_shaker: 1 +# The number of back-and-forth motions of one shake +variable_bucket_shakes: 10 +# During shaking acceleration can often be higher because you don't neeed to keep print +# quality in mind. Higher acceleration helps better with dispersing the blobs. +variable_shake_accel: 10000 + +# The frequency at which to shake the bucket. A decimal value ranging from 0 to 1, where 0 +# is never, and 1 is every time. This way the shaking occurs more often as the bucket +# fills up. Sensible values range from 0.75 to 0.95 +variable_bucket_shake_frequency: 0.95 + +# Height of the shaker arm. If your hotend hits your tray during shaking, increase. +variable_shaker_arm_z: 4 + +gcode: + + # ====================================================================================== + # ==================== RECORD STATE (INCL. FANS, SPEEDS, ETC...) ======================= + # ====================================================================================== + + # General state + SAVE_GCODE_STATE NAME=BLOBIFIER_state + + + # ====================================================================================== + # ==================== CHECK HOMING STATUS ============================================= + # ====================================================================================== + + {% if "xyz" not in printer.toolhead.homed_axes %} + RESPOND MSG="BLOBIFIER: Not homed! Home xyz before blobbing" + {% elif printer.quad_gantry_level and printer.quad_gantry_level.applied == False %} + RESPOND MSG="BLOBIFIER: QGL not applied! run quad_gantry_level before blobbing" + {% else %} + + # Part cooling fan + {% if part_cooling_fan >= 0 %} + # Save the part cooling fan speed to be enabled again later + {% set backup_fan_speed = printer.fan.speed %} + # Set part cooling fan speed + M106 S{part_cooling_fan * 255} + {% endif %} + + # Set feedrate to 100% for correct speed purging + {% set backup_feedrate = printer.gcode_move.speed_factor %} + M220 S100 + + # ====================================================================================== + # ==================== DEFINE BASIC VARIABLES ========================================== + # ====================================================================================== + + {% set park_vars = printer['gcode_macro AFC_PARK'] %} + {% set filament_diameter = printer.configfile.config.extruder.filament_diameter|float %} + {% set filament_cross_section = (filament_diameter/2) ** 2 * 3.1415 %} + {% set bl_count = printer['gcode_macro _BLOBIFIER_COUNT'] %} + {% set pos = printer.gcode_move.gcode_position %} + {% set safe = printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'] %} + {% set ignore_safe = safe.print_height < force_safe_descend_height_until %} + {% set restore_z = [printer['gcode_macro BLOBIFIER_PARK'].restore_z,pos.z]|max %} + {% set pos_max = printer.toolhead.axis_maximum %} + {% set position_y = pos_max.y - skew_correction %} + {% set bl = printer['gcode_macro BLOBIFIER'] %} + + # Get purge volumes from the slicer (if set up right. see + # https://github.com/moggieuk/Happy-Hare/wiki/Gcode-Preprocessing) + # {% set pv = printer.mmu.slicer_tool_map.purge_volumes %} + + + + # ====================================================================================== + # ==================== DETERMINE PURGE LENGTH ========================================== + # ====================================================================================== + + {% if params.PURGE_LENGTH %} # =============== PARAM PURGE LENGTH ====================== + {action_respond_info("BLOBIFIER: param PURGE_LENGTH provided")} + {% set purge_len = params.PURGE_LENGTH|float %} + # {% elif from_tool == to_tool and to_tool >= 0 %} # ==== TOOL DIDN'T CHANGE ============= + # {action_respond_info("BLOBIFIER: Tool didn't change (T%s > T%s), %s" % (from_tool, to_tool, "priming" if purge_length_minimum else "skipping"))} + # {% set purge_len = 0 %} + + # {% elif pv %} # ============== FETCH FROM HAPPY HARE (LIKELY FROM SLICER) ============== + # {% if from_tool < 0 and to_tool >= 0%} + # {action_respond_info("BLOBIFIER: from tool unknown. Finding largest value for T? > T%d" % to_tool)} + # {% set purge_vol = pv|map(attribute=to_tool)|max %} + # {% elif to_tool < 0 %} + # {action_respond_info("BLOBIFIER: tool(s) unknown. Finding largest value")} + # {% set purge_vol = pv|map('max')|max %} + # {% else %} + # {% set purge_vol = pv[from_tool][to_tool]|float * purge_length_modifier %} + # {action_respond_info("BLOBIFIER: Swapped T%s > T%s" % (from_tool, to_tool))} + # {% endif %} + # {% set purge_len = purge_vol / filament_cross_section %} + + # {% set purge_len = purge_len + printer.mmu.extruder_filament_remaining + park_vars.retracted_length + purge_length_addition %} + + {% else %} # ========================= USE CONFIG VARIABLE ============================= + {action_respond_info("BLOBIFIER: No toolmap or PURGE_LENGTH. Using default")} + {% set purge_len = purge_length|float + bl.retracted_length %} + {% endif %} + + # # ==================================== APPLY PURGE MINIMUM ============================= + {% set purge_len = [purge_len,purge_length_minimum]|max|round(0, 'ceil')|int %} + {action_respond_info("BLOBIFIER: Purging %dmm of filament" % (purge_len))} + + {% if params.PURGE_LENGTH %} + {% set purge_len = params.PURGE_LENGTH|float %} + {% else %} + {% set purge_len = purge_length %} + {% endif %} + + # Apply purge minimum + #{% set purge_len = [purge_len, purge_length_minimum]|max %} + {% set purge_len = [purge_len,purge_length_minimum]|max|round(0, 'ceil')|int %} + {action_respond_info("BLOBIFIER: Purging %dmm of filament" % (purge_len))} + + # ====================================================================================== + # ==================== PURGING SEQUENCE ================================================ + # ====================================================================================== + + # Set to absolute positioning. + G90 + + # Check for purge length and purge if necessary. + {% if purge_len|float > 0 %} + + # ==================================================================================== + # ==================== POSITIONING =================================================== + # ==================================================================================== + + # Move to the assembly, first a bit more to the right (brush_start) to avoid a + # potential filametrix pin if it's not already on the same Y coordinate. + {% if printer.toolhead.position.y != position_y %} + G1 X{brush_start} Y{position_y} F{travel_spd_xy} + {% endif %} + # Move over to the tray + G1 X{purge_x} F{travel_spd_xy} + + # ==================================================================================== + # ==================== BUCKET SHAKE ================================================== + # ==================================================================================== + + {% if enable_shaker and (safe.shake or ignore_safe) %} + {% if (bl_count.current_blobs + 1) >= bl_count.next_shake %} + BLOBIFIER_SHAKE_BUCKET SHAKES={bucket_shakes} + _BLOBIFIER_CALCULATE_NEXT_SHAKE + {% endif %} + {% endif %} + + # ==================================================================================== + # ==================== POSITIONING ON TRAY =========================================== + # ==================================================================================== + {% if safe.tray or ignore_safe %} + G1 Z{tray_top + purge_start} F{travel_spd_z} + + # Extend the blobifier + BLOBIFIER_SERVO POS=out + {% endif %} + + # ==================================================================================== + # ==================== HEAT HOTEND =================================================== + # ==================================================================================== + + {% if printer.extruder.temperature < purge_temp_min %} + {% if printer.extruder.target < purge_temp_min %} + M109 S{purge_temp_min} + {% else %} + TEMPERATURE_WAIT SENSOR=extruder MINIMUM={purge_temp_min} + {% endif %} + {% endif %} + + # ==================================================================================== + # ==================== START ITERATING =============================================== + # ==================================================================================== + + # Calculate total number of iterations based on the purge length and the max_iteration + # length. + {% set blobs = (purge_len / purge_length_maximum)|round(0, 'ceil')|int %} + {% set purge_per_blob = purge_len|float / blobs %} + {% set retracts_per_blob = (purge_per_blob / 40)|round(0, 'ceil')|int %} + {% set purge_per_retract = (purge_per_blob / retracts_per_blob)|int %} + {% set pulses_per_retract = (purge_per_blob / retracts_per_blob / 5)|round(0, 'ceil')|int %} + {% set pulses_per_blob = (purge_per_blob / 5)|round(0, 'ceil')|int %} + {% set purge_per_pulse = purge_per_blob / pulses_per_blob %} + {% set pulse_time_constant = purge_per_pulse * 0.95 / purge_spd / (purge_per_pulse * 0.95 / purge_spd + purge_per_pulse * 0.05 / 50) %} + {% set pulse_duration = purge_per_pulse / purge_spd %} + + # Repeat the process until purge_len is reached + {% for blob in range(blobs) %} + RESPOND MSG={"'BLOBIFIER: Blob %d of %d (%.1fmm)'" % (blob + 1, blobs, purge_per_blob)} + + {% if safe.tray or ignore_safe %} + G1 Z{tray_top + purge_start} F{travel_spd_z} + {% endif %} + + # relative positioning + G91 + # relative extrusion + M83 + + # Purge filament in a pulsating motion to purge the filament quicker and better + {% for pulse in range(pulses_per_blob) %} + # Calculations to determine z-speed + {% set purged_this_blob = pulse * purge_per_pulse %} + {% set z_last_pos = purge_start + ((purged_this_blob)/purge_length_maximum)**z_raise_exp * z_raise %} + {% set z_pos = purge_start + ((purged_this_blob + purge_per_pulse)/purge_length_maximum)**z_raise_exp * z_raise %} + {% set z_up = z_pos - z_last_pos %} + {% set speed = z_up / pulse_duration %} + + # Purge quickly + G1 Z{z_up * pulse_time_constant} E{purge_per_pulse * 0.95} F{speed} + # Purge a tiny bit slowly + G1 Z{z_up * (1 - pulse_time_constant)} E{purge_per_pulse * 0.05} F{speed} + + # retract and unretract filament every now and then for thourough cleaning + {% if pulse % pulses_per_retract == 0 and pulse > 0 %} + G1 E-2 F1800 + G1 E2 F800 + {% endif %} + + {% endfor %} + + # Retract to match what Happy Hare is expecting + G1 E-{bl.retracted_length} F{bl.retract_speed * 60} + + # ================================================================================== + # ==================== DEPOSIT BLOB ================================================ + # ================================================================================== + {% if safe.tray or ignore_safe %} + # Raise z a bit to relieve pressure on the blob preventing it to go sideways + G1 Z{eject_hop} F{travel_spd_z} + # Retract the tray + BLOBIFIER_SERVO POS=in + # Move the toolhead down to purge_start height lowering the blob below the tray + G90 # absolute positioning + G1 Z{tray_top} F{travel_spd_z} + # Extend the tray to 'cut off' the blob and prepare for the next blob + BLOBIFIER_SERVO POS=out + BLOBIFIER_SERVO POS=in + BLOBIFIER_SERVO POS=out + # Keep track of the # of blobs + _BLOBIFIER_COUNT + {% endif %} + {% endfor %} + {% endif %} + {% if safe.tray or ignore_safe %} + G1 Z{tray_top + 1} F{travel_spd_z} + G4 P{pressure_release_time} + {% endif %} + {% if safe.brush or ignore_safe %} + BLOBIFIER_CLEAN + {% else %} + G1 X{brush_start} F{travel_spd_xy} + {% endif %} + + # ====================================================================================== + # ==================== RESTORE STATE =================================================== + # ====================================================================================== + G90 # absolute positioning + G1 Z{restore_z} F{travel_spd_z} + + {% if part_cooling_fan >= 0 %} + # Reset part cooling fan if it was changed + M106 S{(backup_fan_speed * 255)|int} + {% endif %} + + M220 S{(backup_feedrate * 100)|int} + {% endif %} + + RESTORE_GCODE_STATE NAME=BLOBIFIER_state + + +########################################################################################## +# Wipes the nozzle on the brass brush +# +[gcode_macro BLOBIFIER_CLEAN] +gcode: + {% set bb = printer['gcode_macro BLOBIFIER'] %} + {% set position_y = printer.configfile.config["stepper_y"]["position_max"]|float %} + {% set original_accel = printer.toolhead.max_accel %} + {% set original_minimum_cruise_ratio = printer.toolhead.minimum_cruise_ratio %} + {% set pos = printer.gcode_move.gcode_position %} + + SAVE_GCODE_STATE NAME=BLOBIFIER_CLEAN_state + + G90 + + {% if bb.brush_accel > 0 %} + SET_VELOCITY_LIMIT ACCEL={bb.brush_accel} MINIMUM_CRUISE_RATIO=0.1 + {% endif %} + + {% if pos.z < bb.brush_top + bb.clearance_z %} + G1 Z{bb.brush_top + bb.clearance_z} F{bb.travel_spd_z} + {% endif %} + G1 X{bb.brush_start} F{bb.travel_spd_xy} + G1 Y{position_y} + G1 Z{bb.brush_top + bb.clearance_z} F{bb.travel_spd_z} + + # Move nozzle down into brush. + G1 Z{bb.brush_top} F{bb.travel_spd_z} + + SET_VELOCITY_LIMIT ACCEL={original_accel} MINIMUM_CRUISE_RATIO={original_minimum_cruise_ratio} + + # Perform wipe. Wipe direction based off bucket_pos for cool random scrubby routine. + {% for wipes in range(1, (bb.wipe_qty + 1)) %} + G1 X{bb.brush_start + bb.brush_width} F{bb.wipe_spd_xy} + G1 X{bb.brush_start} F{bb.wipe_spd_xy} + {% endfor %} + + {% if pos.z < 10 %} + G1 Z10 + {% else %} + G1 Z{pos.z} + {% endif %} + + RESTORE_GCODE_STATE NAME=BLOBIFIER_CLEAN_state + + + +########################################################################################## +# Park the nozzle on the tray to prevent oozing during filament swaps. Place this +# extension in the post_form_tip extension in mmu_macro_vars.cfg: +# variable_user_post_form_tip_extension: "BLOBIFIER_PARK" +# +[gcode_macro BLOBIFIER_PARK] +variable_restore_z: 0 +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set pos = printer.gcode_move.gcode_position %} + {% set safe = printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'] %} + {% set pos_max = printer.toolhead.axis_maximum %} + {% set position_y = pos_max.y - bl.skew_correction %} + + SET_GCODE_VARIABLE MACRO=BLOBIFIER_PARK VARIABLE=restore_z VALUE={pos.z} + + SAVE_GCODE_STATE NAME=blobifier_park_state + + {% if "xyz" in printer.toolhead.homed_axes and printer.quad_gantry_level and printer.quad_gantry_level.applied %} + G90 + + G1 X{bl.brush_start} Y{position_y} F{bl.travel_spd_xy} + G1 X{bl.purge_x} + {% if safe.tray or ignore_safe %} + G1 Z{bl.tray_top} F{bl.travel_spd_z} + {% endif %} + {% else %} + RESPOND MSG="Please home (and QGL) before parking" + {% endif %} + + RESTORE_GCODE_STATE NAME=blobifier_park_state + +########################################################################################## +# Retract or extend the tray +# POS=[in|out] Retractor extend the tray +# +[gcode_macro BLOBIFIER_SERVO] +# Increase this value if the servo doesn't have enough time to fully retract or extend +# variable_dwell_time: 200 +variable_dwell_time: 1000 +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set pos = params.POS %} + {% if pos == "in" %} + SET_SERVO SERVO=blobifier ANGLE={bl.tray_angle_in} + G4 P{dwell_time} + {% elif pos == "out" %} + SET_SERVO SERVO=blobifier ANGLE={bl.tray_angle_out} + G4 P{dwell_time} + {% else %} + {action_respond_info("BLOBIFIER: provide POS=[in|out]")} + {% endif %} + SET_SERVO SERVO=blobifier WIDTH=0 + +########################################################################################## +# Define exclude objects for those who haven't already +# +[exclude_object] + +########################################################################################## +# Overwrite the existing EXCLUDE_OBJECT_DEFINE to also check for safe descend. +# +[gcode_macro EXCLUDE_OBJECT_DEFINE] +rename_existing: _EXCLUDE_OBJECT_DEFINE +gcode: + # only reset on the first object at the beginning of a print + {% if printer.exclude_object.objects|length < 1 %} + _BLOBIFIER_RESET_SAFE_DESCEND + {% endif %} + _EXCLUDE_OBJECT_DEFINE {rawparams} + _BLOBIFIER_SAFE_DESCEND + UPDATE_DELAYED_GCODE ID=BLOBIFIER_SHOW_SAFE_DESCEND DURATION=1 + +[delayed_gcode BLOBIFIER_SHOW_SAFE_DESCEND] +gcode: + {% set safe = printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'] %} + {action_respond_info( + "BLOBIIER: Safe descend possible:\n - tray: %s\n - brush: %s\n - shake: %s" % + ( + "yes" if safe.tray else "no", + "yes" if safe.brush else "no", + "yes" if safe.shake else "no" + ) + )} + +########################################################################################## +# Use the EXCLUDE_OBJECT_START gcode macro to record the current height +# +[gcode_macro EXCLUDE_OBJECT_START] +rename_existing: _EXCLUDE_OBJECT_START +gcode: + _EXCLUDE_OBJECT_START {rawparams} + {% if printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'].first_layer %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=first_layer VALUE=False + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=print_height VALUE={printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'].print_layer_height} + {% else %} + {% set pos = printer.gcode_move.gcode_position %} + {% set last_height = printer['gcode_macro _BLOBIFIER_SAFE_DESCEND'].print_previous_height|float %} + {% if pos.z > last_height %} + {% set last_layer = (pos.z - last_height)|round(2) %} + {% set print_height = (pos.z + last_layer)|round(2) %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=print_previous_height VALUE={pos.z} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=print_height VALUE={print_height} + {% endif %} + {% endif %} + + +########################################################################################## +# Reset the safe descend variables. +# +[gcode_macro _BLOBIFIER_RESET_SAFE_DESCEND] +gcode: + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=tray VALUE=True + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=brush VALUE=True + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=shake VALUE=True + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=first_layer VALUE=True + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=print_height VALUE=0 + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=print_previous_height VALUE=0 + +########################################################################################## +# Determine if it is safe to drop the toolhead (e.g. not hit a print) +# +[gcode_macro _BLOBIFIER_SAFE_DESCEND] +variable_tray: True # Assume it is safe +variable_brush: True +variable_shake: True +variable_first_layer: True +variable_print_height: 0 +variable_print_previous_height: 0 +variable_print_layer_height: 0.3 +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set pos_max = printer.toolhead.axis_maximum %} + {% set position_y = pos_max.y - bl.skew_correction %} + {% set tray = [bl.purge_x + bl.toolhead_x, position_y - bl.toolhead_y] %} + {% set brush = [bl.brush_start + bl.brush_width + bl.toolhead_x, position_y - bl.toolhead_y] %} + {% set shake = [bl.purge_x + bl.toolhead_x, position_y - bl.toolhead_y - 4] %} + {% set objects = printer.exclude_object.objects | map(attribute='polygon') %} + + {% for polygon in objects %} + {% for point in polygon %} + {% if point[0] < tray[0] and point[1] > tray[1] %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=tray VALUE=False + {% endif %} + {% if point[0] < brush[0] and point[1] > brush[1] %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=brush VALUE=False + {% endif %} + {% if point[0] < shake[0] and point[1] > shake[1] %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_SAFE_DESCEND VARIABLE=shake VALUE=False + {% endif %} + {% endfor %} + {% endfor %} + +########################################################################################## +# Increment the blob count with 1 and check if the bucket is full. Pause +# the printer if it is. +# +[gcode_macro _BLOBIFIER_COUNT] +# Don't change these variables +variable_current_blobs: 0 +variable_last_shake: 0 +variable_next_shake: 0 +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set count = printer['gcode_macro _BLOBIFIER_COUNT'] %} + {% if current_blobs >= bl.max_blobs %} + {action_respond_info("BLOBIFIER: Empty purge bucket!")} + M117 Empty purge bucket! + PAUSE + {% else %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=current_blobs VALUE={current_blobs + 1} + _BLOBIFIER_SAVE_STATE + {action_respond_info( + "BLOBIFIER: Blobs in bucket: %s/%s. Next shake @ %s" + % (current_blobs + 1, bl.max_blobs, next_shake) + )} + {% endif %} + +########################################################################################## +# Reset the blob count to 0 +# +[gcode_macro _BLOBIFIER_COUNT_RESET] +gcode: + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=current_blobs VALUE=0 + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=last_shake VALUE=0 + _BLOBIFIER_SAVE_STATE + + _BLOBIFIER_CALCULATE_NEXT_SHAKE + +########################################################################################## +# Shake the blob bucket to disperse the blobs +# +[gcode_macro BLOBIFIER_SHAKE_BUCKET] +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set count = printer['gcode_macro _BLOBIFIER_COUNT'] %} + {% set original_accel = printer.toolhead.max_accel %} + {% set original_minimum_cruise_ratio = printer.toolhead.minimum_cruise_ratio %} + {% set position_x = bl.skew_correction %} + + {% if "xyz" not in printer.toolhead.homed_axes %} + {action_raise_error("BLOBIFIER: Not homed. Home xyz first")} + {% endif %} + + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=last_shake VALUE={count.current_blobs} + _BLOBIFIER_SAVE_STATE + SAVE_GCODE_STATE NAME=shake_bucket + + M400 + M117 (^_^) + + G90 + {% set shakes = params.SHAKES|default(10)|int %} + {% set position_y = printer.configfile.config["stepper_y"]["position_max"]|float %} + + # move to save y if not already there + {% if printer.toolhead.position.y != position_y %} + G1 X{bl.brush_start} Y{position_y} F{bl.travel_spd_xy} + {% endif %} + + # move up a bit to prevent oozing on base + G1 Z{bl.shaker_arm_z} F{bl.travel_spd_z} + # slide into the slot + # G1 X{position_x} F{bl.travel_spd_xy} + G1 X4 + + M400 + M117 (+(+_+)+) + + SET_VELOCITY_LIMIT ACCEL={bl.shake_accel} MINIMUM_CRUISE_RATIO=0.1 + + # Shake away! + {% for shake in range(1, shakes) %} + G1 Y{position_y - 10} F3600 + G1 Y{position_y} F30000 + {% endfor %} + + SET_VELOCITY_LIMIT ACCEL={original_accel} MINIMUM_CRUISE_RATIO={original_minimum_cruise_ratio} + # move out of slot + G1 X{bl.purge_x} + + M400 + M117 (X_x) + + RESTORE_GCODE_STATE NAME=shake_bucket + +########################################################################################## +# Calculate when the bucket should be shaken. +# +[gcode_macro _BLOBIFIER_CALCULATE_NEXT_SHAKE] +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + {% set count = printer['gcode_macro _BLOBIFIER_COUNT'] %} + + {% set remaining_blobs = bl.max_blobs - count.last_shake %} + {% set next_shake = (1 - bl.bucket_shake_frequency) * remaining_blobs + count.last_shake %} + _BLOBIFIER_SAVE_STATE + _BLOBIFIER_SET_NEXT_SHAKE VALUE={next_shake|int} + +########################################################################################## +# Set when the bucket should be shaken next +# VALUE=[int] At what amount of blobs should it be shaken +# +[gcode_macro _BLOBIFIER_SET_NEXT_SHAKE] +gcode: + {% if params.VALUE %} + {% set next_shake = params.VALUE %} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=next_shake VALUE={next_shake} + _BLOBIFIER_SAVE_STATE + {% else %} + {action_respond_info("BLOBIFIER: Provide parameter VALUE=")} + {% endif %} + +########################################################################################## +# Some sanity checks +# +[delayed_gcode BLOBIFIER_INIT] +initial_duration: 5.0 +gcode: + _BLOBIFIER_INIT + BLOBIFIER_SERVO POS=out + +[gcode_macro _BLOBIFIER_INIT] +gcode: + {% set bl = printer['gcode_macro BLOBIFIER'] %} + + # Valid part cooling fan setting + {% if bl.part_cooling_fan != -1 and (bl.part_cooling_fan < 0 or bl.part_cooling_fan > 1) %} + {action_emergency_stop("BLOBIFIER: Value %f is invalid for variable part_cooling_fan. Either -1 or a value from 0 .. 1 is valid." % (bl.part_cooling_fan))} + {% endif %} + + # Valid bucket shake frequency + {% if bl.bucket_shake_frequency < 0 or bl.bucket_shake_frequency > 1 %} + {action_emergency_stop("BLOBIFIER: Value %f is invalid for variable bucket_shake_frequency. Change it to a value between 0 .. 1" % (bl.bucket_shake_frequency))} + {% endif %} + + # Check if position is on 'next' + {% if printer.mmu %} + {% if printer['gcode_macro _MMU_SEQUENCE_VARS'].restore_xy_pos != 'next' %} + {action_respond_info("BLOBIFIER: If not using a wipe tower, consider setting restore_xy_pos: 'next' in mmu_macro_vars.cfg")} + {% endif %} + {% endif %} + + # Check the z_raise variable for normal values + {% if bl.z_raise < 3 %} + {action_respond_info("BLOBIFIER: variable_z_raise: %f is very low. This is the value z raises in total on a single blob. Make sure the value is correct before continuing." % (bl.z_raise))} + {% endif %} + + # Z raise exponent + {% if bl.z_raise_exp > 1 or bl.z_raise_exp < 0.5 %} + {action_respond_info("BLOBIFIER: variable_z_raise_exp has value: %f. This value is out of spec (0.5 ... 1.0)." % (bl.z_raise))} + {% endif %} + + + +[delayed_gcode BLOBIFIER_LOAD_STATE] +initial_duration: 2.0 # Give it some time to boot up +gcode: + {% set sv = printer.save_variables.variables.blobifier %} + + {% if sv %} + # Restore state + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=last_shake VALUE={sv.last_shake} + SET_GCODE_VARIABLE MACRO=_BLOBIFIER_COUNT VARIABLE=current_blobs VALUE={sv.current_blobs} + {% endif %} + _BLOBIFIER_CALCULATE_NEXT_SHAKE + +[gcode_macro _BLOBIFIER_SAVE_STATE] +gcode: + {% set count = printer['gcode_macro _BLOBIFIER_COUNT'] %} + {% set sv = {'current_blobs': count.current_blobs, 'last_shake': count.last_shake} %} + SAVE_VARIABLE VARIABLE=blobifier VALUE="{sv}" + + + +[respond] + +# [save_variables] is defined once in ../save_variables.cfg (shared with the PA macros) diff --git a/macros/blobifier/blobifier_hw.cfg b/macros/blobifier/blobifier_hw.cfg new file mode 100644 index 0000000..c8e8d55 --- /dev/null +++ b/macros/blobifier/blobifier_hw.cfg @@ -0,0 +1,26 @@ +########################################################################################## +# The servo hardware configuration. Change the values to your needs. +# +[servo blobifier] +# Pin for the servo. +pin: PC1 +# Adjust this value until a 'BLOBIFIER_SERVO POS=in' retracts the tray fully without a +# buzzing sound +minimum_pulse_width: 0.00053 +# Adjust this value until a 'BLOBIFIER_SERVO POS=out' extends the tray fully without a +# buzzing sound +maximum_pulse_width: 0.0023 +# Leave this value at 180 +maximum_servo_angle: 180 + +########################################################################################## +# The bucket hardware configuration. Change the pin to whatever pin you've connected the +# switch to. +# +[gcode_button bucket] +pin: ^PC3 # The pullup ( ^ ) is important here. +press_gcode: + M117 bucket installed +release_gcode: + M117 bucket removed + _BLOBIFIER_COUNT_RESET diff --git a/macros/bedfans.cfg b/macros/heating/bed_fans.cfg similarity index 98% rename from macros/bedfans.cfg rename to macros/heating/bed_fans.cfg index 9ca3654..4f8c064 100644 --- a/macros/bedfans.cfg +++ b/macros/heating/bed_fans.cfg @@ -1,3 +1,5 @@ +# Bed fans driven by the bed heater (based on Ellis' bed fan macros) + ############### Config options ################## [gcode_macro _BEDFANVARS] diff --git a/macros/iHeater.cfg b/macros/heating/chamber_iheater.cfg similarity index 99% rename from macros/iHeater.cfg rename to macros/heating/chamber_iheater.cfg index d9e0d0f..4f336ad 100644 --- a/macros/iHeater.cfg +++ b/macros/heating/chamber_iheater.cfg @@ -1,3 +1,5 @@ +# iHeater chamber heater: MCU, heater, sensors and M141/M191 + #======================================================== #=====================iHeater cfg======================== #======================================================== diff --git a/macros/drying.cfg b/macros/heating/filament_drying.cfg similarity index 99% rename from macros/drying.cfg rename to macros/heating/filament_drying.cfg index 8eda43d..040b0f8 100644 --- a/macros/drying.cfg +++ b/macros/heating/filament_drying.cfg @@ -1,3 +1,5 @@ +# Filament drying on the heated bed: START_DRYING, STOP_DRYING, STATUS_DRYING + ########################################################### # Filament Drying # ########################################################### @@ -297,4 +299,4 @@ gcode: [delayed_gcode DRYING_TIMER] gcode: UPDATE_DELAYED_GCODE ID=DRYING_TIMER DURATION=1 - STATUS_DRYING \ No newline at end of file + STATUS_DRYING diff --git a/macros/heatsoak.cfg b/macros/heating/heat_soak.cfg similarity index 99% rename from macros/heatsoak.cfg rename to macros/heating/heat_soak.cfg index 62da12d..195b11d 100644 --- a/macros/heatsoak.cfg +++ b/macros/heating/heat_soak.cfg @@ -1,3 +1,5 @@ +# HEAT_SOAK: wait for a sensor to reach thermal equilibrium (community macro) + ## ## HEAT_SOAK HEATER= TARGET= SOAKER= [RATE=] [TIMEOUT=] ## @@ -298,4 +300,4 @@ gcode: {% endif %} # save ending value of stage - SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'{stage}'" \ No newline at end of file + SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'{stage}'" diff --git a/macros/heating/soak_my_bed.cfg b/macros/heating/soak_my_bed.cfg new file mode 100644 index 0000000..d43e469 --- /dev/null +++ b/macros/heating/soak_my_bed.cfg @@ -0,0 +1,22 @@ +# SOAK_MY_BED wrapper (soak-my-bed plugin): home and QGL first + +# [gcode_macro QUAD_GANTRY_LEVEL] +# rename_existing: _QUAD_GANTRY_LEVEL +# gcode: +# _QUAD_GANTRY_LEVEL +# _QUAD_GANTRY_LEVEL PROBE_METHOD=contact + +### bed warp visualizer +[gcode_macro SOAK_MY_BED] +description: Heat soak bed and live meassure +rename_existing: SOAK_MY_BED_BASE +gcode: + {% set temp = params.TEMPERATURE|default(60) %} + {% set duration = params.DURATION|default(10) %} + {% set heater = params.HEATER|default("heater_bed") %} + {% set mesh = params.MESH_COMMAND|default("BED_MESH_CALIBRATE METHOD=beacon") %} + + G28 + QUAD_GANTRY_LEVEL + + SOAK_MY_BED_BASE TEMPERATURE={temp} DURATION={duration} HEATER={heater} MESH_COMMAND="{mesh}" diff --git a/macros/klipper-macros.cfg b/macros/klipper-macros.cfg index 0d702eb..d9ef007 100644 --- a/macros/klipper-macros.cfg +++ b/macros/klipper-macros.cfg @@ -1,26 +1,46 @@ ##################################################################### -# klipper-macros: include this one file from printer.cfg +# klipper-macros: include this one file from printer.cfg, after +# [include mainsail.cfg] (it needs [pause_resume], [virtual_sdcard] and +# [respond] from there, and replaces Mainsail's PAUSE/RESUME/CANCEL_PRINT): # # [include klipper-macros/klipper-macros.cfg] # -# The order below matters: several files rename PAUSE/RESUME/CANCEL_PRINT -# (rename_existing), and the last definition wins. It is the order these -# files had on the original printer. Comment out what you do not use. -# -# Not part of this repo, include them yourself if you use them: -# timelapse.cfg (moonraker-timelapse), TEST_SPEED.cfg (Ellis), -# mainsail.cfg (before this file), blobifier, AFC, KAMP +# Comment out what you do not use. Keep the order where noted: some +# macros (PAUSE, RESUME, CANCEL_PRINT) are defined in more than one file +# and the last definition wins. ##################################################################### -[include bedfans.cfg] -[include stealthburner_led_effects_barf.cfg] -[include macros_klipperscreen.cfg] -[include iHeater.cfg] -[include drying.cfg] -[include afc_macros.cfg] -[include motion.cfg] -[include heatsoak.cfg] -[include purge_until_empty.cfg] -[include purge_check.cfg] -[include macros.cfg] -[include bd_pa_afc_spoolman.cfg] +[include save_variables.cfg] # the one [save_variables] for all macros + +# Blobifier purge bucket (Turtleblobifier for AFC, modified) +[include blobifier/blobifier.cfg] + +# Printing +[include print/pause_resume.cfg] # before heating/heat_soak.cfg +[include print/filament.cfg] +[include print/homing.cfg] +[include print/idle_timeout.cfg] +[include print/print_start_end.cfg] + +# Heating +[include heating/bed_fans.cfg] +[include heating/chamber_iheater.cfg] +[include heating/heat_soak.cfg] # its RESUME wraps the one above +[include heating/soak_my_bed.cfg] +[include heating/filament_drying.cfg] + +# AFC (Box Turtle) +[include afc/xy_safety_check.cfg] +[include afc/lane_tools.cfg] +[include afc/purge_check.cfg] +[include afc/purge_until_empty.cfg] + +# Pressure advance per lane/spool (bd_pressure + Spoolman) +[include pressure_advance/bd_pa_spoolman.cfg] + +# Spoolman +[include spoolman/active_spool.cfg] + +# LEDs and sensors +[include leds/stealthburner_leds.cfg] +[include sensors/motion_sensor.cfg] diff --git a/macros/stealthburner_led_effects_barf.cfg b/macros/leds/stealthburner_leds.cfg similarity index 99% rename from macros/stealthburner_led_effects_barf.cfg rename to macros/leds/stealthburner_leds.cfg index 77739cc..d439666 100644 --- a/macros/stealthburner_led_effects_barf.cfg +++ b/macros/leds/stealthburner_leds.cfg @@ -347,4 +347,4 @@ gcode: gcode: STOP_LED_EFFECTS LEDS="neopixel:sb_leds" SET_LED_EFFECT EFFECT=sb_logo_printing - _set_nozzle_leds_on \ No newline at end of file + _set_nozzle_leds_on diff --git a/macros/bd_pa_create_spoolman_field.sh b/macros/pressure_advance/bd_pa_create_spoolman_field.sh similarity index 100% rename from macros/bd_pa_create_spoolman_field.sh rename to macros/pressure_advance/bd_pa_create_spoolman_field.sh diff --git a/macros/bd_pa_afc_spoolman.cfg b/macros/pressure_advance/bd_pa_spoolman.cfg similarity index 95% rename from macros/bd_pa_afc_spoolman.cfg rename to macros/pressure_advance/bd_pa_spoolman.cfg index efb0973..48543da 100644 --- a/macros/bd_pa_afc_spoolman.cfg +++ b/macros/pressure_advance/bd_pa_spoolman.cfg @@ -129,16 +129,9 @@ # REQUIRED SETUP # --------------------------------------------------------------------- -# 1) [save_variables] is declared below in this same file (see the -# [save_variables] section right after this header) -- you do NOT -# need to add it to printer.cfg separately. -# IMPORTANT: if you already have a [save_variables] section -# somewhere else in your config (printer.cfg or another include), -# DELETE the one below -- Klipper will error on a duplicate section -# if it appears twice anywhere in your merged config. Only one -# [save_variables] section is allowed printer-wide, but it stores -# ALL variables from ALL macros in one shared file, so having it -# declared once, here, works fine for everything in your setup. +# 1) [save_variables] is declared once for the whole repo in +# save_variables.cfg (klipper-macros.cfg includes it). It stores the +# variables of all macros (this file and Blobifier) in one file. # # 2) Make sure moonraker.conf has [spoolman] configured so AFC can pull # nozzle_temp/material from Spoolman when a lane is loaded: @@ -180,14 +173,7 @@ # PA_STATUS ######################################################################## -# --------------------------------------------------------------------------- -# Persistent variable storage -- required by SAVE_VARIABLE/printer.save_variables -# used throughout this file for the PA cache and lane->spool_id tracking. -# DELETE THIS SECTION if you already have a [save_variables] section -# defined anywhere else in your config (only one is allowed printer-wide). -# --------------------------------------------------------------------------- -[save_variables] -filename: ~/printer_data/config/bd_pa_variables.cfg +# [save_variables] is defined once in ../save_variables.cfg (shared with Blobifier) # The file above does not need to exist yet -- Klipper creates it on # first SAVE_VARIABLE call and reloads it on every restart. @@ -261,9 +247,9 @@ gcode: # intentionally empty -- variables only # file sidesteps this entirely, since that file is never touched by # Klipper's config parser. # -# The script ships in this folder (klipper-macros/bd_pa_write_spoolman.sh); +# The script ships in this folder (klipper-macros/pressure_advance/bd_pa_write_spoolman.sh); # install.sh makes it executable. -command: bash ~/printer_data/config/klipper-macros/bd_pa_write_spoolman.sh +command: bash ~/printer_data/config/klipper-macros/pressure_advance/bd_pa_write_spoolman.sh verbose: False @@ -274,7 +260,7 @@ verbose: False # (un-indented), which is also a config parsing error in Klipper's # parser -- every line of a multi-line value must be indented, # including the very last line. -command: bash ~/printer_data/config/klipper-macros/bd_pa_create_spoolman_field.sh +command: bash ~/printer_data/config/klipper-macros/pressure_advance/bd_pa_create_spoolman_field.sh verbose: False @@ -368,7 +354,7 @@ gcode: # --------------------------------------------------------------------------- # PA cache, keyed by spool_id, stored via Klipper's own save_variables. -# Requires a [save_variables] section in printer.cfg (see header notes). +# Requires [save_variables] (save_variables.cfg, see header notes). # # IMPORTANT: printer.save_variables.variables is read DIRECTLY inline in # each macro below (not via a helper sub-macro + SET_GCODE_VARIABLE @@ -651,4 +637,4 @@ gcode: {% endif %} {% endif %} {% endfor %} - {action_respond_info("Current extruder PA: %.4f" % printer.extruder.pressure_advance|default(0)|float)} \ No newline at end of file + {action_respond_info("Current extruder PA: %.4f" % printer.extruder.pressure_advance|default(0)|float)} diff --git a/macros/bd_pa_write_spoolman.sh b/macros/pressure_advance/bd_pa_write_spoolman.sh similarity index 100% rename from macros/bd_pa_write_spoolman.sh rename to macros/pressure_advance/bd_pa_write_spoolman.sh diff --git a/macros/print/filament.cfg b/macros/print/filament.cfg new file mode 100644 index 0000000..2fe0e5e --- /dev/null +++ b/macros/print/filament.cfg @@ -0,0 +1,28 @@ +# LOAD_FILAMENT, UNLOAD_FILAMENT (based on the KlipperScreen recommended macros) + +[gcode_macro LOAD_FILAMENT] +variable_load_distance: 60 +variable_purge_distance: 20 +gcode: + {% set speed = params.SPEED|default(15) %} + {% set max_velocity = printer.configfile.settings['extruder'].max_extrude_only_velocity * 15 %} + SAVE_GCODE_STATE NAME=load_state + G91 + G92 E0 + G1 E{load_distance} F{max_velocity} # fast-load + G1 E{purge_distance} F{speed} # purge + RESTORE_GCODE_STATE NAME=load_state + +[gcode_macro UNLOAD_FILAMENT] +variable_unload_distance: 100 +variable_purge_distance: 0 +gcode: + {% set speed = params.SPEED|default(15) %} + {% set max_velocity = printer.configfile.settings['extruder'].max_extrude_only_velocity * 30 %} + SAVE_GCODE_STATE NAME=unload_state + G91 + G92 E0 + G1 E{purge_distance} F{speed} # purge + G1 E-20 F100# unload + G1 E-{unload_distance} F{max_velocity} # fast-unload + RESTORE_GCODE_STATE NAME=unload_state diff --git a/macros/print/homing.cfg b/macros/print/homing.cfg new file mode 100644 index 0000000..2a78703 --- /dev/null +++ b/macros/print/homing.cfg @@ -0,0 +1,8 @@ +# HOME: home all axes and move to the bed centre + +[gcode_macro HOME] +gcode: + G90 + G4 P2000 + G28 X0 Y0 Z0 + G1 X175 Y175 Z10 F1200 diff --git a/macros/print/idle_timeout.cfg b/macros/print/idle_timeout.cfg new file mode 100644 index 0000000..77f3cef --- /dev/null +++ b/macros/print/idle_timeout.cfg @@ -0,0 +1,23 @@ +# Idle timeout: heaters and motors off when the printer is idle + +# [dynamicmacros] +# configs: Macros/dynamicmacros.cfg + +# [gcode_button kill_switch] +# pin: gpio7 +# press_gcode: M112 + +[idle_timeout] +gcode: + TURN_OFF_HEATERS + iHEATER_OFF ; Turns off the chamber heater + M84 + # reset paused flag + {% if printer.pause_resume.is_paused|lower == 'false' and park|lower == 'true'%} + _TOOLHEAD_PARK_PAUSE_CANCEL + {% endif %} + _status_ready +# A list of G-Code commands to execute on an idle timeout. See +# docs/Command_Templates.md for G-Code format. The default is to run +# "TURN_OFF_HEATERS" and "M84". +timeout: 85400 # 24 hours diff --git a/macros/macros_klipperscreen.cfg b/macros/print/pause_resume.cfg similarity index 82% rename from macros/macros_klipperscreen.cfg rename to macros/print/pause_resume.cfg index bdd6a1d..d908c37 100644 --- a/macros/macros_klipperscreen.cfg +++ b/macros/print/pause_resume.cfg @@ -1,3 +1,5 @@ +# PAUSE, RESUME, CANCEL_PRINT, M600 (based on the KlipperScreen recommended macros) + # https://klipperscreen.readthedocs.io/en/latest/macros/#recommended [gcode_macro PAUSE] description: Pause the actual running print @@ -132,33 +134,6 @@ gcode: TURN_OFF_HEATERS CANCEL_PRINT_BASE_KLIPPERSCREEN -[gcode_macro LOAD_FILAMENT] -variable_load_distance: 60 -variable_purge_distance: 20 -gcode: - {% set speed = params.SPEED|default(15) %} - {% set max_velocity = printer.configfile.settings['extruder'].max_extrude_only_velocity * 15 %} - SAVE_GCODE_STATE NAME=load_state - G91 - G92 E0 - G1 E{load_distance} F{max_velocity} # fast-load - G1 E{purge_distance} F{speed} # purge - RESTORE_GCODE_STATE NAME=load_state - -[gcode_macro UNLOAD_FILAMENT] -variable_unload_distance: 100 -variable_purge_distance: 0 -gcode: - {% set speed = params.SPEED|default(15) %} - {% set max_velocity = printer.configfile.settings['extruder'].max_extrude_only_velocity * 30 %} - SAVE_GCODE_STATE NAME=unload_state - G91 - G92 E0 - G1 E{purge_distance} F{speed} # purge - G1 E-20 F100# unload - G1 E-{unload_distance} F{max_velocity} # fast-unload - RESTORE_GCODE_STATE NAME=unload_state - [gcode_macro M600] @@ -171,4 +146,4 @@ description: Pause for filament change gcode: PAUSE M117 Paused - M118 Print paused for filament change... type "Resume" to continue \ No newline at end of file + M118 Print paused for filament change... type "Resume" to continue diff --git a/macros/macros.cfg b/macros/print/print_start_end.cfg similarity index 86% rename from macros/macros.cfg rename to macros/print/print_start_end.cfg index b980093..aaae04e 100644 --- a/macros/macros.cfg +++ b/macros/print/print_start_end.cfg @@ -1,26 +1,5 @@ -# Print start/end macros. Included by klipper-macros.cfg. +# Print start and end: PRINT_WARMUP, PRINT_START, PRINT_END -# [dynamicmacros] -# configs: Macros/dynamicmacros.cfg - -# [gcode_button kill_switch] -# pin: gpio7 -# press_gcode: M112 - -[idle_timeout] -gcode: - TURN_OFF_HEATERS - iHEATER_OFF ; Turns off the chamber heater - M84 - # reset paused flag - {% if printer.pause_resume.is_paused|lower == 'false' and park|lower == 'true'%} - _TOOLHEAD_PARK_PAUSE_CANCEL - {% endif %} - _status_ready -# A list of G-Code commands to execute on an idle timeout. See -# docs/Command_Templates.md for G-Code format. The default is to run -# "TURN_OFF_HEATERS" and "M84". -timeout: 85400 # 24 hours # Idle time (in seconds) to wait before running the above G-Code # commands. The default is 600 seconds. @@ -46,7 +25,7 @@ gcode: {% set chamber_temp = printer["temperature_sensor chamber"].temperature|float %} RESET_FAILURE - CLEAR_PAUSE + CLEAR_PAUSE _motion_detected @@ -96,6 +75,7 @@ gcode: BLOBIFIER_CLEAN ; call another macro to wipe nozzle if available G1 X175 Y175 Z5 F1200 + _HEAT_FOR_LOADED_LANE TOOL={initial_tool} ; unload needs the loaded filament's temperature T{initial_tool} #PURGE_LENGTH=100 #Load Initial Tool M104 S{target_extruder} ; set extruder to print temp M109 S{target_extruder} ; wait for extruder temp @@ -124,6 +104,25 @@ gcode: # (CANCEL='_NOTHING') and having unreachable SOAK_TEMP makes it run till the TIMEOUT and continue without canceling the print. {% endif %} +# AFC heats for a tool change from the print file's per-tool temperatures. +# When the loaded lane's tool number is not in that list (for example lane5 = +# T4 loaded, single-colour print with only T0), AFC does not heat at all and +# the unload fails below min_extrude_temp. Heat for the loaded filament first. +[gcode_macro _HEAT_FOR_LOADED_LANE] +description: Heat to the loaded lane's filament temperature before changing to another tool +gcode: + {% set tool = 'T' ~ params.TOOL|default(0)|int %} + {% set cur = printer.AFC.current_load %} + {% if cur and ('AFC_stepper ' ~ cur) in printer %} + {% set lane = printer['AFC_stepper ' ~ cur] %} + {% if lane.map != tool %} + {% set min_temp = printer.configfile.settings.extruder.min_extrude_temp|int + 20 %} + {% set temp = [lane.extruder_temp|default(0, true)|int, min_temp]|max %} + RESPOND MSG="Heating to {temp}C to unload {cur} ({lane.material}) before {tool}" + M109 S{temp} + {% endif %} + {% endif %} + [gcode_macro _NOTHING] gcode: _HEAT_SOAK__BASE_RESUME @@ -254,25 +253,3 @@ gcode: [delayed_gcode _delayed_motors_off] gcode: M84 - -# [gcode_macro QUAD_GANTRY_LEVEL] -# rename_existing: _QUAD_GANTRY_LEVEL -# gcode: -# _QUAD_GANTRY_LEVEL -# _QUAD_GANTRY_LEVEL PROBE_METHOD=contact - -### bed warp visualizer -[gcode_macro SOAK_MY_BED] -description: Heat soak bed and live meassure -rename_existing: SOAK_MY_BED_BASE -gcode: - {% set temp = params.TEMPERATURE|default(60) %} - {% set duration = params.DURATION|default(10) %} - {% set heater = params.HEATER|default("heater_bed") %} - {% set mesh = params.MESH_COMMAND|default("BED_MESH_CALIBRATE METHOD=beacon") %} - - G28 - QUAD_GANTRY_LEVEL - - SOAK_MY_BED_BASE TEMPERATURE={temp} DURATION={duration} HEATER={heater} MESH_COMMAND="{mesh}" - diff --git a/macros/save_variables.cfg b/macros/save_variables.cfg new file mode 100644 index 0000000..7906233 --- /dev/null +++ b/macros/save_variables.cfg @@ -0,0 +1,6 @@ +# Klipper allows one [save_variables] section; every SAVE_VARIABLE goes to +# this one file. Used by the PA cache (pressure_advance/) and the Blobifier +# blob counter (blobifier/). The file stays in the config folder: it is data, +# not part of the repo. +[save_variables] +filename: ~/printer_data/config/bd_pa_variables.cfg diff --git a/macros/motion.cfg b/macros/sensors/motion_sensor.cfg similarity index 93% rename from macros/motion.cfg rename to macros/sensors/motion_sensor.cfg index f0a5443..06b9f1c 100644 --- a/macros/motion.cfg +++ b/macros/sensors/motion_sensor.cfg @@ -1,3 +1,5 @@ +# PIR motion sensor: case light on when someone is near the printer + [gcode_button am312] pin: !PC0 press_gcode: diff --git a/macros/spoolman/active_spool.cfg b/macros/spoolman/active_spool.cfg new file mode 100644 index 0000000..2a0a152 --- /dev/null +++ b/macros/spoolman/active_spool.cfg @@ -0,0 +1,20 @@ +# Spoolman: SET_ACTIVE_SPOOL, CLEAR_ACTIVE_SPOOL + +[gcode_macro SET_ACTIVE_SPOOL] +gcode: + {% if params.ID %} + {% set id = params.ID|int %} + {action_call_remote_method( + "spoolman_set_active_spool", + spool_id=id + )} + {% else %} + {action_respond_info("Parameter 'ID' is required")} + {% endif %} + +[gcode_macro CLEAR_ACTIVE_SPOOL] +gcode: + {action_call_remote_method( + "spoolman_set_active_spool", + spool_id=None + )}