Delay after fan or motor power on, before issuing steps

Basic Information:

Printer Model: Custom updated Graber i3
MCU / Printerboard: Arduino Mega 2560 + RAMPS
Host / SBC: 86Duino Zero

Is there a way to get Klipper to pause after either turning on the controller fan or activating a stepper or heater, before starting to issue steps?

Full story:

I’m upgrading an existing (old!) printer. Overall, things are going great, I’m really happy!

The printer uses an ATX power supply. 5V power to the MCU and SBC is provided by the ATX standby 5V, and the power supply is switched fully on (by toggling its PS_ON pin) when 12V power is required - so just before using the motors, hot end, beds etc.

I’d like to keep this setup because the PSU fan is pretty loud when it’s fully on and it would be nice to only run it when necessary. I guess it also saves power a bit if the printer is just left sitting.

I’ve done the trick where I configured the PS_ON pin to be controlled by a controller_fan that’s set up to be switched on when the motors or heaters are used (config snippet below). This is working pretty well! There is a small issue, though: when the power supply first switches on, it takes a little time to ramp up its 12V lines. Klipper will happily try to move the steppers during this period, which can result in at best some unpleasant noises, and occasionally homing failing. I’d like to delay everything for, say, 0.5s when the power supply is switched on (from Klipper’s point of view, when the motors, heaters, or ‘controller fan’ is enabled) to avoid this.

Is this possible?

# ATX PSU power control  
# Klipper doesn't have direct power control - but we can fake it
# by pretending the PSU is a fan that needs to be powered on if
# the extruder or steppers are active.
[controller_fan power_supply]
# PS_ON pin on RAMPS board.
pin: !ar12
max_power: 1.0
shutdown_speed: 1
kick_start_time: 1.5
idle_timeout: 300 # turn PSU off after 5 minutes
heater: extruder, heater_bed
stepper: stepper_x, stepper_y, stepper_z, extruder

G4
https://www.klipper3d.org/G-Codes.html#g-code-commands

EDIT: Sorry, I just re-read how you’re controlling the power pin. Here’s how I’m handling the same kind of setup:

[output_pin _psu]
pin: pi:gpiochip0/gpio17
shutdown_value: 1

[gcode_macro SET_PSU]
variable_value: 0
gcode:
    {% set power = params.VALUE|default(1)|int %}
    {% if power == 1 %}
        { action_respond_info('Powering up 24V PSU...') }
        SET_PIN PIN=_psu VALUE={power}
        SET_GCODE_VARIABLE MACRO=SET_PSU VARIABLE=value VALUE={power}
        G4 P4000
    {% endif %}
    {% if power == 0 %}
        {% if printer.extruder.temperature <= 50 %}
            M84
            { action_respond_info('Powering down 24V PSU...') }       
            TOGGLE_LIGHT VALUE=0
            SET_PIN PIN=_psu VALUE={power}
            SET_GCODE_VARIABLE MACRO=SET_PSU VARIABLE=value VALUE={power}
        {% else %}
            { action_respond_info('Hotend too hot to power down...') }
        {% endif %}
    {% endif %}


[gcode_macro _POWER_OFF_PRINTER]
gcode:
    UPDATE_DELAYED_GCODE ID=delayed_printer_off DURATION=0
    SET_PSU VALUE=0

[delayed_gcode delayed_printer_off]
initial_duration: 0.
gcode:
    {% if printer.extruder.temperature <= 50 %}
        _POWER_OFF_PRINTER
    {% else %}
        UPDATE_DELAYED_GCODE ID=delayed_printer_off DURATION=5
    {% endif %}

[gcode_macro PSU_OFF]
gcode:
    M106 S0 ; turn off cooling fan
    M140 S0 ; turn off bed
    M104 S0 ; turn off extruder    
    M84
    UPDATE_DELAYED_GCODE ID=delayed_printer_off DURATION=5

[idle_timeout]
gcode:
    {% if (printer["gcode_macro SET_PSU"].value)|int != 0 %}
        PSU_OFF
    {% endif %}


[gcode_macro M104]
rename_existing: M1042
gcode:
    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}
    M1042 S{params.S}

[gcode_macro SET_HEATER_TEMPERATURE]
rename_existing: _SET_HEATER_TEMPERATURE
gcode:
    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}
    {% if params.HEATER == "extruder" %}
        M1042 S{params.TARGET}
    {% elif params.HEATER == "heater_bed" %}
        M1402 S{params.TARGET}
    {% endif %}

[gcode_macro M140]
rename_existing: M1402
gcode:
    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}
    SET_HEATER_TEMPERATURE_SCALED HEATER=heater_bed TARGET={params.S|default(0)}
1 Like

That looks great - though I knew commands could be overridden, I didn’t realize such rich configuration was possible (setting and checking variables from the code and the like).

Am I correct that, from reading your code, it will automatically switch on the PSU when heaters are engaged, but not motors?

Sorry, I forgot I had a couple additional calls in another config file. In addition to the macros above, I also have:

[gcode_macro G28]
rename_existing: G2801
gcode:
    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}
    G2801 {rawparams}

[gcode_macro TOGGLE_LIGHT]
variable_value: 0
gcode:
    {% set power = params.VALUE|default((not printer['output_pin caselight'].value))|int %}
    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}
    SET_PIN PIN=caselight VALUE={power}
    SET_GCODE_VARIABLE MACRO=TOGGLE_LIGHT VARIABLE=value VALUE={power}

Plus my PRINT_START macro and a FILAMENT_UNLOAD macro contain:

    {% if (printer["gcode_macro SET_PSU"].value)|int == 0 %}
        SET_PSU VALUE=1
    {% endif %}

I also have this in my moonraker.cfg:

[power Printer]
type: klipper_device
object_name: gcode_macro SET_PSU
#off_when_shutdown: False
initial_state: on
locked_while_printing: True

G28 is my only motors-related override for power checking, the logic being that I do not make use of manual steppers, so on my setup, if the printer isn’t homed, Klipper will refuse to execute moves anyway. Homing the printer will turn the PSU on and it will stay on until the idle timeout, so I can safely assume that if the printer is homed, the PSU is on.

I’ll note that this configuration doesn’t check to make sure the PSU is on before extruding filament, but I never do that manually in normal operation anyway. If I have to do it for some reason, I have to remember to turn on the PSU or else I get an error.

1 Like

This is great, thanks!

@theophile thank you so much for this great write-up! I am planning to implement something similar in my setup (will have to deal with canbus…).

Here are a few things I thought about:

For the extruder motor, you could simply use min_extrude_temp, you then only need to be sure that the PSU is on if the extruder is higher in temp than min_extrude_temp. For this, you need to:

  • Be sure the temperature is lower than min_extrude_temp before powering off the PSU.
    This check is already performed by your macro, but could be even better if the test compared the current temp with the configured min_extrude_temp rather than always 50°C.
  • Power on the PSU whenever a heater is turned on (already performed by your macros)
  • Check at startup (via a delayed macro) if the temp is higher than min_extrude_temp to switch on the PSU, because then an extrude command could be performed.

Assuming the temp of the extruder cannot rise without its heater being powered on, this should make it impossible to have the extruder used without the PSU being on.
(You could also do that by checking the PSU state every time the extrude command is used, but this might cause performance issues?)

It doesn’t look like the macros account for fans needing 24v, something to keep in mind (if trying to use fans for other things than part cooling, like electronics chamber fans, but more importantly filter/nevermore fans that could need to stay on after the print as finished).

That’s true, though in my particular situation it probably wouldn’t help. If I’m planning to manually push filament through the nozzle, the heater will be on anyway and thus the PSU. There are scenarios where I might want to execute manual moves with the extruder stepper that don’t require the nozzle to be hot (e.g. rotation distance checking, clearing filament from the bowden tube, etc.) and I wouldn’t want to have fans or heaters on unnecessarily in that case.

Yeah, I immediately dismissed the idea of overriding the G1 command to include a power check because that would introduce significant system overhead.

That’s true. I use 24V fans for electronics case cooling but at least in my setup, I only need them when the stepper drivers are engaged. When the system is idle, passive cooling is sufficient for me. For fans that need to remain running some period of time after a print finishes, the [delayed_gcode delayed_printer_off] block would need to be amended to add a check for whatever condition must exist before those fans can be turned off.