Using a Secondary Klipper for Printer Power Control

I recently got a 3D printer, a Sovol SV06 Plus. Remarkably, this has been a net positive for almost every aspect of my life. My kids are into it, my spouse is browsing filament colors, and I can print whatever little project boxes my heart desires.

We're not here to talk about that, though. We're here to talk about controlling the power for this printer in possibly the most ridiculous way possible.

Printer Software

The software stack for my printer consists of:

  1. Klipper is the firmware and software that takes the sliced 3d models and tells the printer how/where/when to move and extrude.
  2. Moonraker sits athwart Klipper and mediates API requests from higher up layers to Klipper's somewhat weird JSON-over-BSD-sockets API. Importantly, it also has the capability to control printer power a few different ways.
  3. Mainsail is a web-based frontend for Moonraker that allows one to upload, select, and print gcode files, along with controlling every other aspect of the printer itself.

Most of these three components run on a Dell Wyse 5070 thin client that I have sitting next to the printer. The only thing that runs on the printer hardware controller is the Klipper firmware.

Klipper is interesting when compared to more conventional 3D printer firmware like Marlin, in so far as it doesn't try to do everything on the limited embedded microcontroller on the printer controller board. Instead, it splits it into two: the firmware exposes the printer hardware over a serial port using a compact binary API, and the "host" software (written primarily in Python) runs on a more powerful computer, typically a Raspberry Pi. The host software interprets gcode, turning it into an optimized set of movement commands and shipping those commands to the printer hardware on a precise timeline.

What does this have to do with power control?

The biggest difference between my setup and most Klipper setups is that I'm running the Klipper host software on a used thin client PC (specifically a Dell Wyse 5070) instead of a Raspberry Pi, which means I don't have direct access to GPIO pins (A GPIO pin can, say, flip a relay on and off). GPIO is one way that Moonraker can control power, but not the only way. Moonraker can instead use a generic HTTP client to talk to some other thing that can control power, for example Home Assistant or Tasmota-powered smart switches.

I saw this, and I says to myself, I says "Self, why not make one Moonraker talk to another Moonraker which talks to another Klipper which talks to a microcontroller dedicated to GPIO which then flips a relay to control my printer power?"

The advantages of this setup:

  1. I can use the same secondary Klipper (in this case a Raspberry Pi Pico) to control power/lights for anything on my bench separately from that machine's specific controller
  2. I don't have to switch to a Raspberry Pi.

That's weird. Just switch to a Raspberry Pi.

No.

Sigh. Ok fine. Show us the configs.

The config is remarkably small, once you get two Klippers and two Moonrakers installed. I used KIAUH which has the ability to do this baked right in.

In printer_1_data/config/moonraker.cfg I have appended the following bits:

[power SV06_Plus]
type: http
locked_while_printing: true
restart_klipper_when_powered: true
restart_delay: 2
on_url: http://localhost:7126/machine/device_power/device?device=sv06_plus&action=on
off_url: http://localhost:7126/machine/device_power/device?device=sv06_plus&action=off
status_url: http://localhost:7126/machine/device_power/device?device=sv06_plus
request_template:
  {% if command in ["on", "off"] %}
    {% do http_request.set_method("POST") %}
    {% do http_request.set_body("") %}
  {% endif %}
  {% do http_request.send() %}
response_template:
  {% set resp = http_request.last_response().json() %}
  {resp["result"]["sv06_plus"]}

This is pretty dense but basically it's saying, use the generic HTTP client with three URLs: on, off, and status. Requests need to be a POST if we're telling the remote Moonraker to turn the printer on or off. The response_template extracts the status that the secondary Moonraker returns from each command.

Three additional settings control the safety interlock, so the printer can't get turned off in the middle of a print, and also tells the primary Moonraker to restart the primary Klipper when the power gets switched on.

For printer_2_data/config/moonraker.cfg I added the following:

[power sv06_plus]
type: klipper_device
object_name: output_pin relay1

This states that the secondary Moonraker can control the power of a device named sv06_plus by telling the secondary Klipper to toggle an output pin named relay1.

Finally, this is printer_2_data/config/printer.cfg:

[mcu]
serial: /dev/serial/by-id/usb-Klipper_rp2040_E661AC8863625425-if00 #don't copypasta this, find your own

[printer]
kinematics: none
max_velocity: 1000
max_accel: 1000

[output_pin relay1]
pin: gpio4

In other words, the MCU is a Raspberry Pi Pico, I have a "printer" that can't actually print, and I have a single output pin on gpio4 named relay1.

I built and uploaded the Klipper firmware to the Pico following the package instructions and now Mainsail has a little toggle to control the printer power! And it works!

Screenshot of Mainsail with a big red arrow pointing at the SV06_Plus power control

Pretty cool, right?


Updated 2023-09-26 to use Moonraker's native power APIs rather than toggling the relay directly.

Posted in: 3D Printing