diff --git a/README.md b/README.md index 46c361c..4ff12f1 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ everything below, grouped by topic. Comment out what you do not use there. | **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` | +| `stealthburner_leds.cfg` | Stealthburner logo status (`_STATUS STATE=...`: heating and print progress as a filling logo, paused, error flash from `_ERROR`, part ready once the bed is cool, dim when idle and nobody is near) and the nozzle light | `led_effect`, `neopixel:bt_logo` and `rainbow_turtle` from AFC | | **sensors/** | | | -| `motion_sensor.cfg` | PIR motion sensor switching the case light | sensor on a pin, `[output_pin caselight]` | +| `motion_sensor.cfg` | PIR motion sensor: case light and nozzle LEDs on while someone is near (timeout from the last motion), `CASELIGHT MODE=AUTO\|ON\|OFF` | sensor on a pin, `[output_pin caselight]`, `leds/stealthburner_leds.cfg` | ### Klipper modules (`extras/`) diff --git a/macros/blobifier/blobifier.cfg b/macros/blobifier/blobifier.cfg index 6835bb9..af0f7ae 100644 --- a/macros/blobifier/blobifier.cfg +++ b/macros/blobifier/blobifier.cfg @@ -544,6 +544,7 @@ gcode: {% set pos = printer.gcode_move.gcode_position %} SAVE_GCODE_STATE NAME=BLOBIFIER_CLEAN_state + _STATUS_SHOW STATE=cleaning G90 @@ -576,6 +577,7 @@ gcode: {% endif %} RESTORE_GCODE_STATE NAME=BLOBIFIER_CLEAN_state + _STATUS diff --git a/macros/leds/stealthburner_leds.cfg b/macros/leds/stealthburner_leds.cfg index d439666..507921d 100644 --- a/macros/leds/stealthburner_leds.cfg +++ b/macros/leds/stealthburner_leds.cfg @@ -1,216 +1,191 @@ +# Stealthburner LEDs: logo status effects (_STATUS) and the nozzle light +# +# Based on the Rainbow Barf logo LED effects by tanaes: # https://github.com/tanaes/whopping_Voron_mods/blob/main/LEDs/Rainbow_Barf_Logo_LED/Code/stealthburner_led_effects_barf.cfg -# Macros for setting the status leds on the Voron StealthBurner toolhead (or for any neopixel-type leds). +# which builds on the Voron Stealthburner LED macros. Needs klipper-led_effect. # -# You will need to configure a neopixel (or other addressable led, such as dotstar). See -# https://www.klipper3d.org/Config_Reference.html#neopixel for configuration details. - - -##################################### -# INSTRUCTIONS # -##################################### -# How to use all this stuff: +# sb_leds 1-8 are the logo, 9-10 the nozzle LEDs. The logo shows the printer +# status (_STATUS); the nozzle LEDs are white while someone is near the +# printer (sensors/motion_sensor.cfg). led_effect adds up effects that share +# an LED, so the logo effects only cover 1-8 and never stop the nozzle light. # -# 1. Copy this .cfg file into your Klipper config directory and then add [include stealthburner_leds.cfg] -# to the top of your printer.cfg in order for register the LEDs and macros with Klipper. -# 2. Define your LEDs by editing [neopixel sb_leds] below and entering the data pin from your control board -# as well as the color order. +# _STATUS STATE= set and show a status +# _STATUS show the current status again +# _STATUS_SHOW STATE=... show a status without storing it (cleaning) +# _STATUS_FLASH STATE=... show a status for a while (_ERROR: error) # -# Note: RGB and RGBW are different and must be defined explicitly. RGB and RGBW are also not able to -# be mix-and-matched in the same chain. A separate data line would be needed for proper functioning. -# -# RGBW LEDs will have a visible yellow-ish phosphor section to the chip. If your LEDs do not have -# this yellow portion, you have RGB LEDs. -# -# 3. Save your config and restart Klipper. -# -# Note: We set RED and BLUE to 1.0 to make it easier for users and supporters to detect -# misconfigurations or miswiring. The default color format is for Neopixels with a dedicated -# white LED. On startup, all three SB LEDs should light up. -# -# If you get random colors across your LEDs, change the color_order to GRB and restart. Then -# omit the W for each suggested color_order in the next paragraph. -# -# If you get MAGENTA, your color order is correct. If you get CYAN, you need to use RGBW. If -# you get YELLOW, you need to use BRGW (note that BRG is only supported in the latest Klipper -# version). -# -# 4. Once you have confirmed that the LEDs are set up correctly, you must now decide where you want -# these macros called up...which means adding them to your existing gcode macros. NOTHING will happen -# unless you add the STATUS_????? macros to your existing gcode macros. -# -# Example: add STATUS_LEVELING to the beginning of your QGL gcode macro, and then add STATUS_READY -# to the end of it to set the logo LED and nozzle LEDs back to the `ready` state. -# -# Example: add STATUS_CLEANING to the beginning of your nozzle-cleaning macro, and then STATUS_READY -# to the end of it to return the LEDs back to `ready` state. -# -# 5. Feel free to change colors of each macro, create new ones if you have a need to. The macros provided below -# are just an example of what is possible. If you want to try some more complex animations, you will most -# likely have to use WLED with Moonraker and a small micro-controller (please see the LED thread for help inside -# of the stealthburner_beta channel on Discord). -# -##################################### -# END INSTRUCTRUCTIONS # -##################################### - +# States: ready, homing, heating_bed, heating_extruder, calibrating_z, +# leveling, meshing, cleaning, printing, paused, part_ready, error. +# ready: rainbow while someone is near, dim (logo and Box Turtle +# logo) when nobody is near +# heating_*: the logo fills up with the temperature towards the target +# printing: the logo fills up with the print progress +# part_ready: blue while the bed is above part_ready_temp, then green [neopixel sb_leds] pin: nhk:PD3 -# The pin connected to the neopixel. This parameter must be provided. chain_count: 10 -# The number of Neopixel chips that are "daisy chained" to the -# provided pin. The default is 1 (which indicates only a single -# Neopixel is connected to the pin). -# color_order: GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRBW, GRBW -color_order: GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB -# Set the pixel order required by the LED hardware. Options are GRB, -# RGB, GRBW, or RGBW. The default is GRB. +color_order: GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB, GRB +# Magenta until the first effect starts: shows that the chain and the +# colour order are right initial_RED: 1.0 initial_GREEN: 0.0 initial_BLUE: 1.0 initial_WHITE: 0.0 -# Sets the initial LED color of the Neopixel. Each value should be -# between 0.0 and 1.0. The WHITE option is only available on RGBW -# LEDs. The default for each color is 0.# - -########################## -# LED Effects Animations # -########################## +# led_effect sends the whole chain at the rate of the fastest effect running +# on it, so all sb_leds effects use 12 fps. ################## ## logo effects ## ################## -[led_effect sb_logo_busy] +[led_effect sb_logo_ready] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (1,0,0) + gradient 0.3 1 add (0.3, 0.0, 0.0),(0.0, 0.3, 0.0),(0.0, 0.0, 0.3) -[led_effect sb_logo_cleaning] +[led_effect sb_logo_night] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.0, 0.02, 0.5) + static 0 0 top (0.02, 0.02, 0.02) -[led_effect sb_logo_calibrating_z] +[led_effect bt_logo_night] autostart: false -frame_rate: 24 +frame_rate: 10 leds: - neopixel:sb_leds (2,1,3,4,5,6,7,8) + neopixel:bt_logo layers: - breathing 3 1 top (0.0, 0.0, 0.35) - -[led_effect sb_logo_heating] -leds: - neopixel:sb_leds (2,3,7,8,6,1) -autostart: false -frame_rate: 24 -layers: - comet 0.3 0 add (1, 0.18, 0) - -[led_effect sb_logo_cooling] -leds: - neopixel:sb_leds (2,3,7,8,6,1) -autostart: false -frame_rate: 24 -layers: - comet 0.3 0 add (0, 0, 1) + static 0 0 top (0.02, 0.02, 0.02) [led_effect sb_logo_homing] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.0, 0.6, 0.2) + breathing 3 1 top (0.0, 0.6, 0.2) + +# heatergauge: the LEDs light up one by one with temperature / target +[led_effect sb_logo_heating_bed] +autostart: false +frame_rate: 12 +heater: heater_bed +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + heatergauge -1 0 add (1.0, 0.18, 0.0),(1.0, 0.18, 0.0) + static 0 0 top (0.08, 0.015, 0.0) + +[led_effect sb_logo_heating_extruder] +autostart: false +frame_rate: 12 +heater: extruder +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + heatergauge -1 0 add (1.0, 0.0, 0.0),(1.0, 0.0, 0.0) + static 0 0 top (0.08, 0.0, 0.0) + +[led_effect sb_logo_calibrating_z] +autostart: false +frame_rate: 12 +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + breathing 3 1 top (0.0, 0.0, 0.35) [led_effect sb_logo_leveling] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.5, 0.1, 0.4) + breathing 3 1 top (0.5, 0.1, 0.4) [led_effect sb_logo_meshing] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.2, 1.0, 0.0) + breathing 3 1 top (0.2, 1.0, 0.0) +[led_effect sb_logo_cleaning] +autostart: false +frame_rate: 12 +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + breathing 3 1 top (0.0, 0.02, 0.5) + +# progress: the LEDs light up one by one with the print progress +# (display_status: M73 from the slicer, else the file position) [led_effect sb_logo_printing] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - gradient 0.3 1 add (0.3, 0.0, 0.0),(0.3, 0.3, 0.0),(0.3, 0.1, 0.0) + progress -1 0 add (0.3, 0.3, 0.3),(0.3, 0.3, 0.3) + static 0 0 top (0.08, 0.02, 0.0) -[led_effect sb_logo_standby] +[led_effect sb_logo_paused] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.01, 0.01, 0.01) + breathing 2 1 top (1.0, 0.35, 0.0) + +[led_effect sb_logo_cooling] +autostart: false +frame_rate: 12 +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + breathing 3 1 top (0.0, 0.0, 1.0) [led_effect sb_logo_part_ready] autostart: false -frame_rate: 24 +frame_rate: 12 leds: neopixel:sb_leds (2,1,3,4,5,6,7,8) layers: - breathing 3 1 top (0.0, 1.0, 0.0) + breathing 3 1 top (0.0, 1.0, 0.0) -#################### -## nozzle effects ## -#################### - -[led_effect sb_nozzle_heating] +[led_effect sb_logo_error] autostart: false -frame_rate: 24 +frame_rate: 12 +leds: + neopixel:sb_leds (2,1,3,4,5,6,7,8) +layers: + blink 1 0.5 top (1.0, 0.0, 0.0) + +################### +## nozzle effect ## +################### + +# Switched by _LIGHTS (sensors/motion_sensor.cfg) +[led_effect sb_nozzle_light] +autostart: false +frame_rate: 12 leds: neopixel:sb_leds (9,10) layers: - breathing 3 1 top (1.0, 0.18, 0.0, 0.0) - -[led_effect sb_nozzle_cooling] -autostart: false -frame_rate: 24 -leds: - neopixel:sb_leds (9,10) -layers: - breathing 3 1 top (0.0, 0.0, 1.0, 0.1) - -[led_effect sb_nozzle_standby] -autostart: false -frame_rate: 24 -leds: - neopixel:sb_leds (9,10) -layers: - breathing 3 1 top (0.6, 0.0, 0.0, 0.0) - -[led_effect sb_nozzle_part_ready] -autostart: false -frame_rate: 24 -leds: - neopixel:sb_leds (9,10) -layers: - breathing 3 1 top (0.6, 1.0, 0.0, 0.1) - + static 0 0 top (1.0, 1.0, 1.0) ##################### ## all led effects ## ##################### +# Klipper shutdown [led_effect sb_critical_error] leds: neopixel:sb_leds @@ -222,129 +197,72 @@ autostart: false frame_rate: 24 run_on_error: true - -[led_effect rainbow] -leds: - neopixel:sb_leds -autostart: true -frame_rate: 24 -layers: - gradient 0.3 1 add (0.3, 0.0, 0.0),(0.0, 0.3, 0.0),(0.0, 0.0, 0.3) - - -####################### -# LED Effects Statics # -####################### - -[led_effect set_nozzle_leds] -leds: - neopixel:sb_leds (9,10) - #neopixel:caselight -autostart: false -frame_rate: 24 -layers: - # static 0 0 top (0.0, 0.0, 0.0, 1.0) - static 0 0 top (1.0, 1.0, 1.0, 1.0) - -[led_effect set_logo_leds] -leds: - neopixel:sb_leds (2,1,3,4,5,6,7,8) - #neopixel:caselight -autostart: false -frame_rate: 24 -layers: - static 0 0 top (1.0, 1.0, 1.0) - ############## # The Macros # ############## -[gcode_macro _set_logo_leds_off] +[gcode_macro _STATUS] +description: Set the logo status (STATE=ready, homing, heating_bed, ...); without STATE show the current one again +variable_state: 'ready' +variable_part_ready_temp: 35 # part_ready: blue until the bed is below this (C), then green +variable_fade_time: 0.5 # s cross-fade between effects (logo and nozzle light) gcode: - SET_LED_EFFECT EFFECT=set_logo_leds STOP=1 + {% set new = params.STATE|default(state)|lower %} + {% if new != state %} + SET_GCODE_VARIABLE MACRO=_STATUS VARIABLE=state VALUE="'{new}'" + {% endif %} + _STATUS_SHOW STATE={new} -[gcode_macro _set_logo_leds_on] +[gcode_macro _STATUS_SHOW] +description: Show STATE on the logo without storing it as the current status gcode: - SET_LED_EFFECT EFFECT=set_logo_leds + {% set state = params.STATE|lower %} + {% set cfg = printer['gcode_macro _STATUS'] %} + {% set near = printer['gcode_macro _LIGHTS'].near if 'gcode_macro _LIGHTS' in printer else True %} + {% set night = state == 'ready' and not near %} + {% set cool = printer.heater_bed.temperature < cfg.part_ready_temp %} + {% if state == 'ready' %} + {% set effect = 'sb_logo_night' if night else 'sb_logo_ready' %} + {% elif state == 'part_ready' %} + {% set effect = 'sb_logo_part_ready' if cool else 'sb_logo_cooling' %} + {% else %} + {% set effect = 'sb_logo_' ~ state %} + {% endif %} + {% if ('led_effect ' ~ effect) in printer %} + # REPLACE: stops the other effects on the logo LEDs, not the nozzle light + SET_LED_EFFECT EFFECT={effect} REPLACE=1 FADETIME={cfg.fade_time} + SET_LED_EFFECT EFFECT={'bt_logo_night' if night else 'rainbow_turtle'} REPLACE=1 FADETIME={cfg.fade_time} + {% else %} + _WARN MSG="_STATUS: unknown state {state}" + {% endif %} + {% if state == 'part_ready' and not cool %} + UPDATE_DELAYED_GCODE ID=_status_part_ready_check DURATION=30 + {% endif %} -[gcode_macro _set_nozzle_leds_on] +[delayed_gcode _status_part_ready_check] gcode: - SET_LED_EFFECT EFFECT=set_nozzle_leds + {% if printer['gcode_macro _STATUS'].state == 'part_ready' %} + _STATUS + {% endif %} -[gcode_macro _set_nozzle_leds_off] +[gcode_macro _STATUS_FLASH] +description: Show STATE on the logo for DURATION seconds (default 10), then the current status again gcode: - SET_LED_EFFECT EFFECT=set_nozzle_leds STOP=1 + _STATUS_SHOW STATE={params.STATE} + UPDATE_DELAYED_GCODE ID=_status_flash_end DURATION={params.DURATION|default(10)|float} -# The _status_* macros only stop the toolhead effects (neopixel:sb_leds), so -# the Box Turtle logo (rainbow_turtle on neopixel:bt_logo) keeps running during -# prints. _status_off still stops everything; _status_ready starts the logo again. -[gcode_macro _status_off] +# After an error that ended the print (print_stats: error) the logo stays red +# until the next status (next print, CANCEL_PRINT, idle timeout) +[delayed_gcode _status_flash_end] gcode: - STOP_LED_EFFECTS + {% set state = printer['gcode_macro _STATUS'].state %} + {% if printer.print_stats.state == 'error' and state not in ['ready', 'part_ready'] %} + _STATUS STATE=error + {% else %} + _STATUS + {% endif %} -[gcode_macro _status_ready] +[delayed_gcode _status_startup] +initial_duration: 1 gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=rainbow - SET_LED_EFFECT EFFECT=rainbow_turtle - -[gcode_macro _status_part_ready] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_nozzle_part_ready - SET_LED_EFFECT EFFECT=sb_logo_part_ready - -[gcode_macro _status_busy] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_busy - _set_nozzle_leds_on - -[gcode_macro _status_heating] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_heating - SET_LED_EFFECT EFFECT=sb_nozzle_heating - -[gcode_macro _status_cooling] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_cooling - SET_LED_EFFECT EFFECT=sb_nozzle_cooling - - -[gcode_macro _status_leveling] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_leveling - _set_nozzle_leds_on - -[gcode_macro _status_homing] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_homing - _set_nozzle_leds_on - -[gcode_macro _status_cleaning] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_cleaning - _set_nozzle_leds_on - -[gcode_macro _status_meshing] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_meshing - _set_nozzle_leds_on - -[gcode_macro _status_calibrating_z] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_calibrating_z - _set_nozzle_leds_on - -[gcode_macro _status_printing] -gcode: - STOP_LED_EFFECTS LEDS="neopixel:sb_leds" - SET_LED_EFFECT EFFECT=sb_logo_printing - _set_nozzle_leds_on + _STATUS STATE=ready diff --git a/macros/messages.cfg b/macros/messages.cfg index 682d9f0..8f2c1a6 100644 --- a/macros/messages.cfg +++ b/macros/messages.cfg @@ -18,6 +18,9 @@ description: Show DISPLAY (default MSG) on the display, then raise MSG as an error (aborts the calling macros) gcode: M117 {params.DISPLAY|default(params.MSG)} + {% if 'gcode_macro _STATUS_FLASH' in printer %} + _STATUS_FLASH STATE=error + {% endif %} _RAISE_ERROR MSG="{params.MSG}" [gcode_macro _RAISE_ERROR] diff --git a/macros/print/idle_timeout.cfg b/macros/print/idle_timeout.cfg index 77f3cef..0ab8912 100644 --- a/macros/print/idle_timeout.cfg +++ b/macros/print/idle_timeout.cfg @@ -16,7 +16,7 @@ gcode: {% if printer.pause_resume.is_paused|lower == 'false' and park|lower == 'true'%} _TOOLHEAD_PARK_PAUSE_CANCEL {% endif %} - _status_ready + _STATUS STATE=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". diff --git a/macros/print/pause_resume.cfg b/macros/print/pause_resume.cfg index c589c46..58678e1 100644 --- a/macros/print/pause_resume.cfg +++ b/macros/print/pause_resume.cfg @@ -41,6 +41,7 @@ gcode: PAUSE_BASE _TOOLHEAD_PARK_PAUSE_CANCEL SET_GCODE_VARIABLE MACRO=PAUSE VARIABLE=parked VALUE=True + _STATUS STATE=paused # Set fan to S28 (28/255 ≈ 0.1098) M106 S28 {% else %} @@ -96,7 +97,7 @@ gcode: {% endif %} RESUME_BASE {get_params} - _status_printing + _STATUS STATE=printing # Raising an error (_ERROR, also shown on the display) aborts the whole # RESUME like a failed purge check: the printer stays paused and the next @@ -203,7 +204,8 @@ gcode: SET_GCODE_VARIABLE MACRO=PAUSE VARIABLE=parked VALUE=False # Clear a pause reason from the display M117 - _status_ready + _STATUS STATE=ready + _LIGHTS_ON [gcode_macro M600] # diff --git a/macros/print/print_start_end.cfg b/macros/print/print_start_end.cfg index bccfa87..0332c1e 100644 --- a/macros/print/print_start_end.cfg +++ b/macros/print/print_start_end.cfg @@ -29,7 +29,7 @@ gcode: # Clear what an earlier print or error left on the display M117 - _motion_detected + _LIGHTS_ON #SET_PIN PIN=caselight VALUE=1.00 UPDATE_DELAYED_GCODE ID=_delayed_motors_off DURATION=0 @@ -48,7 +48,7 @@ gcode: M109 S150 ; wait until nozzle reaches 150 ; wait until nozzle reaches 0 {% endif %} - _status_homing + _STATUS STATE=homing RESPOND MSG="homing" HOME RESPOND MSG="homing finished" @@ -63,7 +63,7 @@ gcode: {% endif %} G1 X175 Y175 Z2 F1200 - _status_heating + _STATUS STATE=heating_bed RESPOND MSG="heating" {% if target_bed > 0 %} M140 S{target_bed} ; start bed heater @@ -72,11 +72,14 @@ gcode: M190 S{target_bed} ; wait on bed temperature {% endif %} + _STATUS STATE=homing HOME + _STATUS STATE=leveling QUAD_GANTRY_LEVEL BLOBIFIER_CLEAN ; call another macro to wipe nozzle if available G1 X175 Y175 Z5 F1200 + _STATUS STATE=heating_extruder _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 @@ -89,6 +92,7 @@ gcode: BLOBIFIER_CLEAN ; call another macro to wipe nozzle if available G1 X175 Y175 Z5 F1200 + _STATUS STATE=heating_bed {% if target_chamber > 30 %} RESPOND MSG="waiting for chamber temperature" M141 S{target_chamber} @@ -133,7 +137,7 @@ gcode: gcode: RESPOND MSG="print start macro continued" - _motion_detected + _LIGHTS_ON {% set target_extruder = params.EXTRUDER|int %} @@ -145,14 +149,14 @@ gcode: HOME BLOBIFIER_CLEAN ; call another macro to wipe nozzle if available - _status_calibrating_z + _STATUS STATE=calibrating_z G28 Z METHOD=CONTACT CALIBRATE=1 ; calibrate z offset and beacon model hot - _status_leveling + _STATUS STATE=leveling RESPOND MSG="quad ganrty level" QUAD_GANTRY_LEVEL - _status_meshing + _STATUS STATE=meshing RESPOND MSG="meshing" BED_MESH_CALIBRATE RUNS=2 ; bed mesh in scan mode @@ -161,10 +165,10 @@ gcode: M104 S150 ; set nozzle temp to 150 M109 S150 ; wait until nozzle reaches 150 ; wait until nozzle reaches 0 - _status_calibrating_z + _STATUS STATE=calibrating_z G28 Z METHOD=CONTACT CALIBRATE=1 ; calibrate z offset and beacon model hot - _status_heating + _STATUS STATE=heating_extruder M104 S{target_extruder} ; set extruder to print temp M109 S{target_extruder} ; wait for extruder temp BLOBIFIER_PARK ; over the bucket, then undo the retracts @@ -209,7 +213,7 @@ gcode: # per-reading width compensation made the flow jump between 77 and 119%. # To use it again: SET_BDWIDTH NAME=fila_width_0 COMMAND=ENABLE_WIDTH - _status_printing + _STATUS STATE=printing [gcode_macro PRINT_END] @@ -223,7 +227,7 @@ gcode: SAVE_GCODE_STATE NAME=STATE_PRINT_END - _motion_detected + _LIGHTS_ON M400 ; wait for buffer to clear G92 E0 ; zero the extruder @@ -238,7 +242,7 @@ gcode: M107 ; turn off fan #SET_PIN PIN=caselight VALUE=0.03 - _status_part_ready + _STATUS STATE=part_ready UPDATE_DELAYED_GCODE ID=_delayed_motors_off DURATION=3600 diff --git a/macros/sensors/motion_sensor.cfg b/macros/sensors/motion_sensor.cfg index 06b9f1c..097fea3 100644 --- a/macros/sensors/motion_sensor.cfg +++ b/macros/sensors/motion_sensor.cfg @@ -1,42 +1,108 @@ -# PIR motion sensor: case light on when someone is near the printer +# PIR motion sensor (AM312): case light and nozzle LEDs while someone is near +# +# The AM312 keeps its output on as long as it sees movement: press = motion, +# release = no more motion. The timeout starts at the release, so the lights +# stay on while someone works at the printer. +# +# someone near: case light near_value, nozzle LEDs white +# nobody near, print/pause: case light print_value, nozzle LEDs off +# nobody near, idle: case light idle_value, nozzle LEDs off, +# logo and Box Turtle logo dim (_STATUS ready) +# +# CASELIGHT MODE=ON [VALUE=0.5] | OFF | AUTO overrides the sensor until the +# next Klipper restart. Setting the case light in Mainsail only lasts until +# the next motion. [gcode_button am312] pin: !PC0 press_gcode: - _motion_detected - -[gcode_macro _motion_detected] -description: Motion sensor triggered + _MOTION_START +release_gcode: + _MOTION_END + +[gcode_macro _LIGHTS] +description: Set the case light and nozzle LEDs from motion, print state and CASELIGHT mode +variable_timeout: 60 # s without motion before the lights dim or turn off +variable_near_value: 1.0 # case light while someone is near +variable_print_value: 0.1 # case light while printing or paused, nobody near +variable_idle_value: 0.0 # case light while idle, nobody near +variable_on_value: 1.0 # case light for CASELIGHT MODE=ON +variable_mode: 'auto' # auto (motion sensor), on, off: set by CASELIGHT +variable_present: False # someone near: set by the motion sensor +variable_near: False # lights on (present or MODE=ON): read by _STATUS +variable_debug: False # True: report every change in the console gcode: - {% set printing = printer.print_stats.state == "printing" %} - - # RESPOND MSG="AM312: Motion detected" - - {% if printing %} - # RESPOND MSG="AM312: Printer is printing -> light ON (100%)" - SET_PIN PIN=caselight VALUE=1.0 - _set_nozzle_leds_on + {% set busy = printer.print_stats.state in ['printing', 'paused'] %} + {% if mode == 'on' %} + {% set lit = True %} + {% set value = on_value %} + {% elif mode == 'off' %} + {% set lit = False %} + {% set value = 0.0 %} {% else %} - # RESPOND MSG="AM312: Printer idle -> light ON (10%)" - SET_PIN PIN=caselight VALUE=0.1 - _set_nozzle_leds_off + {% set lit = present %} + {% set value = near_value if present else (print_value if busy else idle_value) %} + {% endif %} + {% if debug %} + RESPOND MSG="Lights: mode={mode} present={present} print={printer.print_stats.state}: case light {value}, nozzle {'on' if lit else 'off'}" + {% endif %} + {% if printer['output_pin caselight'].value != value %} + SET_PIN PIN=caselight VALUE={value} + {% endif %} + {% if printer['led_effect sb_nozzle_light'].enabled != lit %} + SET_LED_EFFECT EFFECT=sb_nozzle_light {'' if lit else 'STOP=1'} FADETIME={printer['gcode_macro _STATUS'].fade_time} + {% endif %} + {% if near != lit %} + SET_GCODE_VARIABLE MACRO=_LIGHTS VARIABLE=near VALUE={lit} + # Night mode of the logo when idle + _STATUS {% endif %} - # RESPOND MSG="AM312: Resetting timeout to 30 seconds" - UPDATE_DELAYED_GCODE ID=_motion_timeout DURATION=30 +[gcode_macro _MOTION_START] +description: Motion sensor: someone is near +gcode: + UPDATE_DELAYED_GCODE ID=_motion_timeout DURATION=0 + {% if not printer['gcode_macro _LIGHTS'].present %} + SET_GCODE_VARIABLE MACRO=_LIGHTS VARIABLE=present VALUE=True + _LIGHTS + {% endif %} +[gcode_macro _MOTION_END] +description: Motion sensor: no more motion, lights off after the timeout +gcode: + UPDATE_DELAYED_GCODE ID=_motion_timeout DURATION={printer['gcode_macro _LIGHTS'].timeout} [delayed_gcode _motion_timeout] gcode: - {% set printing = printer.print_stats.state == "printing" %} + SET_GCODE_VARIABLE MACRO=_LIGHTS VARIABLE=present VALUE=False + _LIGHTS - # RESPOND MSG="AM312: Timeout reached, no motion" +[gcode_macro _LIGHTS_ON] +description: Lights on as if someone walked by (print start and end, startup) +gcode: + _MOTION_START + {% if printer['gcode_button am312'].state == 'RELEASED' %} + _MOTION_END + {% endif %} - {% if printing %} - # RESPOND MSG="AM312: Printing -> dim light to 10%" - SET_PIN PIN=caselight VALUE=0.1 +[delayed_gcode _lights_startup] +initial_duration: 2 +gcode: + _LIGHTS_ON + +[gcode_macro CASELIGHT] +description: Case light and nozzle LEDs: MODE=AUTO (motion sensor), ON [VALUE=0-1] or OFF; no MODE: show the mode +gcode: + {% set l = printer['gcode_macro _LIGHTS'] %} + {% set mode = params.MODE|default('')|lower %} + {% if mode == '' %} + RESPOND MSG="Case light: mode {l.mode}, {'someone near' if l.present else 'nobody near'}, value {printer['output_pin caselight'].value}" + {% elif mode not in ['auto', 'on', 'off'] %} + _ERROR MSG="CASELIGHT: MODE must be AUTO, ON or OFF" {% else %} - # RESPOND MSG="AM312: Idle -> light OFF" - SET_PIN PIN=caselight VALUE=0.0 + {% if 'VALUE' in params %} + SET_GCODE_VARIABLE MACRO=_LIGHTS VARIABLE=on_value VALUE={[[params.VALUE|float, 0.0]|max, 1.0]|min} + {% endif %} + SET_GCODE_VARIABLE MACRO=_LIGHTS VARIABLE=mode VALUE="'{mode}'" + _LIGHTS {% endif %} - _set_nozzle_leds_off