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 + )}