moonraker
moonraker copied to clipboard
[FR] PSU ON/OFF trigger 🔌
Hi guys 👋
Thank you for the great API server!
I'm using power
config to control PSU and I think it would be great to have PSU trigger control with following conditions:
- Auto OFF after extruder temperature is <
40 °C
. - Auto ON when any gcode command:
G0,G1,G2,G3,G10,G11,G28,G29,G32,M104,M106,M109,M140,M190
.
Would be configurable in the settings and work with existing Power control.
Inspired by Octoprint's PSUControl.
Don't think the auto ON is possible, since for giving a gcode command Klipper needs to be connected. And klipper is not connected when the psu is off.
For the auto off you just need to place the printer off in klippers idle_timeout
Here as example my setup
[idle_timeout]
timeout = 3600
gcode:
{% if printer.pause_resume.is_paused %}
M117 "timeout ignored because printer state == paused."
{% else %}
{% if (printer.toolhead.homed_axes == 'xyz' and printer.toolhead.axis_maximum.z > (printer.toolhead.position.z + 20)) %}
G91
G0 Z10 F600
G90
G0 X150 Y150 F2400
{% endif %}
TURN_OFF_MOTORS
TURN_OFF_HEATERS
UPDATE_DELAYED_GCODE ID=DELAYED_PRINTER_OFF DURATION=60
{% endif %}
[gcode_macro POWER_OFF_PRINTER]
gcode =
SET_ALL_LED
{action_call_remote_method("set_device_power",
device="ssr",
state="off")}
{action_call_remote_method("set_device_power",
device="printer",
state="off")}
[delayed_gcode DELAYED_PRINTER_OFF]
initial_duration = 0.
gcode =
{% if printer.idle_timeout.state == "Idle" %}
{% if printer["extruder"].temperature > 50 %}
UPDATE_DELAYED_GCODE ID=DELAYED_PRINTER_OFF DURATION=60
{% else %}
POWER_OFF_PRINTER
{% endif %}
{% else %}
M118 Printer not idle, cancelled PRINTER_OFF.
{% endif %}
Hi @MarcPot
I'm using Arduino Mega + RAMPS 1.6, the board is powering with USB cable from Raspberry Pi. The main power 12V / 24V (PSU) is connect via power input terminal in the RAMPS. With this config the controller board still powered on when PSU is off. I'm using similar klipper's idle timeout config for auto turn off PSU and START_PRINT
macro to turn on PSU before print.
The probem is other command like homing, manual move, temperature pre-heat from UI or custom gcode in the console will need manual turn on PSU every time and I often forgot this step.
BTW, I don't think auto parking or move tool head like your idle timeout macro is a good idea. It can caused unexpected move when we not prepared for it yet like when you're maintaining the hotend or anything on the bed can caused collision 💥.
That auto parking thing is a personal thing, it doesn't matter for me since I don't work with the electricity on, and seeing the Z10 I'll never hit a part.
Yea okay you're the exception where that auto on would have use, for a temp solution: you can overwrite all gcode commands you need with
[gcode_macro G28]
rename_existing = BASE_G28
gcode:
PRINTER_ON
BASE_G28
Although I admit this is a very ugly solution.
Another option would be to just stop powering the board through usb, that way you have to turn it on first. But again a very ugly solution.
What you could also do is just start a delayed gcode after the idle timeout that resets every second, then when the printer status moves away from idle it'll call a PRINTER_ON. Only problem with this is that it'll error commands like G28 or comparable.
Overwrite base gcode commands is a doable solution. Stop powering through usb will need to modify hardware which I'm trying to avoid. Yes, messing with idle timeout in this case will create error first before the PSU is on.
I'm looking for something like gcode command event hook, but seem it will only work via API and not LCD menu. Klippy core or plugin implement probably is the best option IMO.
Don't think the auto ON is possible, since for giving a gcode command Klipper needs to be connected. And klipper is not connected when the psu is off.
For the auto off you just need to place the printer off in klippers idle_timeout
Here as example my setup
[idle_timeout] timeout = 3600 gcode: {% if printer.pause_resume.is_paused %} M117 "timeout ignored because printer state == paused." {% else %} {% if (printer.toolhead.homed_axes == 'xyz' and printer.toolhead.axis_maximum.z > (printer.toolhead.position.z + 20)) %} G91 G0 Z10 F600 G90 G0 X150 Y150 F2400 {% endif %} TURN_OFF_MOTORS TURN_OFF_HEATERS UPDATE_DELAYED_GCODE ID=DELAYED_PRINTER_OFF DURATION=60 {% endif %} [gcode_macro POWER_OFF_PRINTER] gcode = SET_ALL_LED {action_call_remote_method("set_device_power", device="ssr", state="off")} {action_call_remote_method("set_device_power", device="printer", state="off")} [delayed_gcode DELAYED_PRINTER_OFF] initial_duration = 0. gcode = {% if printer.idle_timeout.state == "Idle" %} {% if printer["extruder"].temperature > 50 %} UPDATE_DELAYED_GCODE ID=DELAYED_PRINTER_OFF DURATION=60 {% else %} POWER_OFF_PRINTER {% endif %} {% else %} M118 Printer not idle, cancelled PRINTER_OFF. {% endif %}
Do I need to change "printer" to my TPLINK switch name? {action_call_remote_method("set_device_power", device="printer", state="off")}
I also have a very similar printer setup, I like keeping the main power off to conserve power, however I often forget to turn it back on so the printer gets mad when its steppers won't wake up 😅
Thank @MarcPot reference config, I've using it to make the workaround solution. Here is what's I had done:
Implement custom M80
and M81
to call moonraker API for PSU on and off, put M80
on top of START_PRINT
macro which is then place in beginning of the generated gcode (config in slicer). It allows PSU kick off when print job start and turn it off after idle timeout automatically by klipper.
Moonraker config
[power PSU]
type: homeassistant
address: x.x.x.x
port: 8123
device: switch.lumi_lumi_plug_bcc03702_on_off
token: TOKEN
off_when_shutdown: True
Klipper macro
[idle_timeout]
timeout: 600
gcode:
MACHINE_IDLE_TIMEOUT
# Turn on PSU
[gcode_macro M80]
gcode:
# Moonraker action
{action_call_remote_method('set_device_power',
device='PSU',
state='on')}
# Turn off PSU
[gcode_macro M81]
gcode:
# Moonraker action
{action_call_remote_method('set_device_power',
device='PSU',
state='off')}
[gcode_macro MACHINE_IDLE_TIMEOUT]
gcode:
M84
TURN_OFF_HEATERS
M81
[gcode_macro START_PRINT]
gcode:
{% set BED_TEMP = params.BED_TEMP|default(60)|int %}
{% set EXTRUDER_TEMP = params.EXTRUDER_TEMP|default(200)|int %}
# Turn on PSU
M80
...
uocnb,
Did you ever get this working as you would prefer? I have several marcos that I use from time to time and I always forget to power the mains relay before those macros.
I use GPIO on my RPI4 to control a relay to power the M/B with mains power. I use the PI to power the Printer M/B when the mains power is off. I also used Octoprint's PSUControl, which worked perfectly.
I tried this code and instantly got an error on reboot.
[gcode_macro G28] rename_existing = BASE_G28 gcode: PRINTER_ON BASE_G28
So weird that the Home All button doesen't do a check to see if the printer is powered. Or really that any command doesn't do a power check.
After a lot more digging, I was able to use the following script to power on the mains relay via the RPI4. Now when I click the HOME ALL button, the power switches on automatically.
I will now be able to use the same script commands for things like bed leveling and bed cleaning marcos.
Added to mainsail.cfg
[gcode_macro G28] rename_existing = G9028 gcode: {action_call_remote_method('set_device_power', device='printer', state='on')} G9028
I used these instructions https://moonraker.readthedocs.io/en/latest/configuration/#power-on-g-code-uploads
Be careful, there is a typo in the linked instructions, but the code in the instructions is correct.
@Primer-Merc I'm still using START_PRINT
macro as previous comment. It is working the same way as your G28 command. I'm using this macro to all slicer software for custom head cleaner, z-layer and z-height message update for lcd controller etc...
I have just switched to Moonraker/Klipper/MainsailOS from OctoPrint and really liking it so far. I'm having issues getting this to work though. I tried the setting by @uocnb above, but for some reason it does not power on my PSU properly when a print starts. If I run START_PRINT
in the console my printer turns on (I turn it on via MQTT), but if I put START_PRINT
in my slicer software custom g-code klipper enters shutdown mode whenever I start a print with the PSU off. My printer mainboard is powered by the USB-cable to my RaspberryPi, hence it is always on, but I need to power on the PSU via MQTT to power steppers, fans etc.
Isn't there any way the PSU can be turned on without involving g-code, like PSU Control in OctoPrint? I think the settings such as on_when_job_queued
indicates that this should be possible, but I have not managed to figure out how it all comes together.
@iceaway
I have this in my moonraker.cfg , but I use the RP4 to control a relay for my mains power.
EDIT: to clarify I'm not controlling the 120VAC power. I am controlling the 24v power to the printer mainboard.
[power printer] type: gpio pin: !gpiochip0/gpio14 off_when_shutdown: True restart_klipper_when_powered: False locked_while_printing: True initial_state: off on_when_job_queued: True
@uocnb @iceaway
Because I was always forgetting to power the printer with the little "Power Printer" in the upper right-hand menu. I discovered with @uocnb 's I could edit the macros.
For instance, when I want to call the "built in" bed level macro I first call the G28 macro which powers the printer. Because G28 is also a "built in" macro, I need to temporarily rename it and then run the gcode. It may not be pretty but it works now everytime.
[gcode_macro Level_Bed] gcode: G28 ; home all axes BED_SCREWS_ADJUST
[gcode_macro G28] rename_existing = G9028 gcode: {action_call_remote_method('set_device_power', device='printer', state='on')} G9028
Hi @iceaway, AFAIK there are no plugin or event handling with the current klipper's config. The custom / overwrite (inheritable) macros is the only option we have currently with this setup. IMO, it is similar to the event driven we are looking for, just in a different form. If your macro is working with the console, it should work. Possibly you put it in the wrong custom g-code config section in your slicer software, you should check the generated gcode file to verify it. Here is my config with few slicers, the configured macro can be found in the beginning of the generated gcode file.
SuperSlicer:
Simplify3D:
Cura:
@Primer-Merc I've just recall why I'm using M80
and M81
here. The reason is following the standard command for PSU control. They are available in some firmware implementation as mentioned in this document RepRap. AFAIK it is using by other CNC controller as well.
Thanks for the ideas, I will try it some more. Every time it fails it's a bit of a hassle for some reason to get it working again. I have to go to the printer, unplug the USB-cable, restart some things etc, not really sure exactly which steps I have to do to get it back again.
In my final g-code there is only a single command before my START_PRINT
command, and that is M107
. START_PRINT
is the very first thing in PrusaSlicer "Start G-Code" so I'm not sure where that M107
comes from. I tried removing that command manually but unfortunately that changed nothing. I will try the modified G28
command to see if that makes any difference.
I tried adding a 3 second delay using G40 P3000
at the start, just in case the PSU would need some time to start up. But even using that I cannot see that the printer turns on in the power menu. START_PRINT
and M80
works perfectly from the console, but in the gcode file it does nothing. Any ideas how I could troubleshoot this further? I tried using both START_PRINT
(which in my case is the same as M80
) and M80
, but neither makes any difference.
Sound like you're having issue with klipper config, possibly in the START_PRINT
macro, you should check your klipper log file.
I created a super simple test-file with the following contents:
START_PRINT
G4 P3000
M80
G4 P3000
M81
G4 P3000
It "printed" without any errors. The klippy.log showed the following:
Starting SD card print (position 0)
Stats 336472.6: gcodein=0 mcu: mcu_awake=0.009 mcu_task_avg=0.000097 mcu_task_stddev=0.000073 bytes_write=16452 bytes_read=171430 bytes_retransmit=27 bytes_invalid=0 send_seq=2025 receive_seq=2025 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=0 stalled_bytes=0 freq=15999912 sd_pos=12 heater_bed: target=0 temp=24.0 pwm=0.000 sysload=0.06 cputime=30.300 memavail=3474388 print_time=1486.669 buffer_time=2.637 print_stall=0 extruder: target=0 temp=23.9 pwm=0.000
Stats 336473.6: gcodein=0 mcu: mcu_awake=0.009 mcu_task_avg=0.000097 mcu_task_stddev=0.000073 bytes_write=16587 bytes_read=171544 bytes_retransmit=27 bytes_invalid=0 send_seq=2029 receive_seq=2026 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=7 stalled_bytes=60 freq=15999911 sd_pos=25 heater_bed: target=0 temp=24.0 pwm=0.000 sysload=0.06 cputime=30.315 memavail=3473944 print_time=1489.669 buffer_time=4.634 print_stall=0 extruder: target=0 temp=23.9 pwm=0.000
Stats 336474.6: gcodein=0 mcu: mcu_awake=0.017 mcu_task_avg=0.000164 mcu_task_stddev=0.000268 bytes_write=16670 bytes_read=171698 bytes_retransmit=27 bytes_invalid=0 send_seq=2032 receive_seq=2032 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=0 stalled_bytes=0 freq=15999912 sd_pos=25 heater_bed: target=0 temp=23.9 pwm=0.000 sysload=0.06 cputime=30.332 memavail=3473052 print_time=1489.669 buffer_time=3.633 print_stall=0 extruder: target=0 temp=23.8 pwm=0.000
Stats 336475.6: gcodein=0 mcu: mcu_awake=0.017 mcu_task_avg=0.000164 mcu_task_stddev=0.000268 bytes_write=16676 bytes_read=171798 bytes_retransmit=27 bytes_invalid=0 send_seq=2033 receive_seq=2033 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=0 stalled_bytes=0 freq=15999912 sd_pos=25 heater_bed: target=0 temp=23.9 pwm=0.000 sysload=0.05 cputime=30.353 memavail=3472604 print_time=1489.669 buffer_time=2.632 print_stall=0 extruder: target=0 temp=24.0 pwm=0.000
Stats 336476.6: gcodein=0 mcu: mcu_awake=0.017 mcu_task_avg=0.000164 mcu_task_stddev=0.000268 bytes_write=16871 bytes_read=171917 bytes_retransmit=27 bytes_invalid=0 send_seq=2038 receive_seq=2035 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=5 stalled_bytes=7 freq=15999912 sd_pos=38 heater_bed: target=0 temp=24.0 pwm=0.000 sysload=0.05 cputime=30.382 memavail=3473028 print_time=1492.669 buffer_time=4.624 print_stall=0 extruder: target=0 temp=24.0 pwm=0.000
Stats 336477.6: gcodein=0 mcu: mcu_awake=0.017 mcu_task_avg=0.000164 mcu_task_stddev=0.000268 bytes_write=16894 bytes_read=172051 bytes_retransmit=27 bytes_invalid=0 send_seq=2040 receive_seq=2040 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=0 stalled_bytes=0 freq=15999912 sd_pos=38 heater_bed: target=0 temp=24.0 pwm=0.000 sysload=0.05 cputime=30.405 memavail=3472540 print_time=1492.669 buffer_time=3.624 print_stall=0 extruder: target=0 temp=23.9 pwm=0.000
Stats 336478.6: gcodein=0 mcu: mcu_awake=0.017 mcu_task_avg=0.000164 mcu_task_stddev=0.000268 bytes_write=16900 bytes_read=172151 bytes_retransmit=27 bytes_invalid=0 send_seq=2041 receive_seq=2041 retransmit_seq=2 srtt=0.003 rttvar=0.000 rto=0.025 ready_bytes=0 stalled_bytes=0 freq=15999912 sd_pos=38 heater_bed: target=0 temp=23.9 pwm=0.000 sysload=0.05 cputime=30.427 memavail=3472704 print_time=1492.669 buffer_time=2.623 print_stall=0 extruder: target=0 temp=24.0 pwm=0.000
Finished SD card print
Exiting SD card print (position 48)
I am not really sure what I am looking for. Any ideas?
I found it while looking an moonraker.log
. I disabled changing the power for my printer during printing with locked_while_printing: True
in moonraker.conf
. I changed it to False
and it appears to be working now :+1:
The documentation regarding this functionality has recently been updated with further details. It is now possible to add force=True
when calling the set_device_power
remote method from Klipper, allowing the request to complete if locked_while_printing
has been enabled. This allows a macro to explicitly turn on/off a peripheral during a print while protecting the device from an unintentional request to change the device state.
Separately, I thought I would address the OPs original request and explain why the way Octoprint's plugin handles this can't be implemented in Moonraker. Specifically, there is a philosophical difference between the way Octoprint and Moonraker handle G-Code commands. Octoprint generally processes each G-Code command, including those in a G-Code file, and provides hooks through which plugins can react or manipulate such commands. Moonraker does not do this. G-Code commands received through the console or API call are not inspected, they are passed directly to Klipper. Likewise G-Code files and not processed by Moonraker, Klipper's virtual_sdcard
does the heavy lifting.
The reasons for this philosophical approach are two-fold:
- Performance. Klipper has a well optimized path for gcode command processing. It isn't desirable for Moonraker to add overhead by doing its own G-Code processing.
- Klipper G-Code commands are extensible through
gcode_macos
and Jinja2 scripting. All existing G-Codes can be overridden and its easy to create new commands. As some of you have discovered, much of what can be accomplished through an Octoprint plugin that relies on inspecting G-Code commands can be implemented by overriding gcode commands in Klipper. Additionally, it is possible to have a device power off when the extruder temp drops below 40C using adelayed_gcode
. Anything that cannot be implemented in this fashion is likely a candidate for aKlippy extra
rather than an extension in Moonraker.
@Arksine That sounds great, I will try that! I like the idea of disabling it in the GUI just to avoid accidentally turning it off during a print.
I'm still not really clear on how mainsail/moonraker/klipper are all integrated with one another, but wouldn't it be possible to attach the "Turn on PSU"-hook to when a print is started? Such as when you click "Print" or "Upload and print" from i.e. PrusaSlicer?
It is possible, but not necessary as it can be accomplished by overriding SDCARD_PRINT_FILE
. The majority of printer builds don't separate logic from peripherals, in such cases it is not possible to start a print before powering on the device. Contrasting this functionality with on_when_queued
would be confusing, and the latter cannot be implemented through a gcode override.
The docs provide a specific example of this use case.
It is possible, but not necessary as it can be accomplished by overriding
SDCARD_PRINT_FILE
. The majority of printer builds don't separate logic from peripherals, in such cases it is not possible to start a print before powering on the device. Contrasting this functionality withon_when_queued
would be confusing, and the latter cannot be implemented through a gcode override.The docs provide a specific example of this use case.
The SDCARD_PRINT_FILE
was just was just what I was looking for, now everything is working as expected. Thanks!
Hi, I have similar problem. Is there any way to execute shell script after switching PSU ON ? I need to restart CANBUS to restore umbilical communication after printer shutdown, specifically, I need to exec "sudo ifdown can0 && sudo ifup can0" on RPi host (using rs485/can hat)
I've tried klipper's gcode_shell_command plugin, but klipper is dead without PSU... ;)
Thanks in advance!