Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

28 Commits

Folders and files

Repository files navigation

Klipper PLR

Exact Power Loss Recovery for Klipper

Klipper PLR records the toolhead position, extruder position, G-code file path and the exact virtual_sdcard.file_position at the moment a configured power-loss input changes state. After restart it builds a recovery G-code file that returns the machine to the saved physical height and continues from the saved byte position.

Important: Power-loss recovery is hardware-dependent. A backup supply/supercapacitor must keep the host and MCU alive long enough for the capture macro to execute. Test thoroughly before relying on it for valuable prints.

Features

  • Exact file-position recovery rather than replaying a complete layer.
  • Saves X/Y/Z/E, file path and file offset in Klipper save_variables.
  • Restores part-cooling fan state and extrusion mode (absolute or relative).
  • Keeps the slicer thumbnail, so the recovery file has a preview in Mainsail.
  • Survives a second power loss during a recovery print.
  • Layer-height checkpoint as a secondary recovery aid.
  • Automatic recovery-file generation after restart.
  • Automatic cleanup after a recovery print reaches END_PRINT.
  • Idempotent installer with backups; unattended re-runs for Moonraker updates.
  • Handles file names containing spaces, apostrophes and quotes.
  • Works with G-code stored in the normal printer_data/gcodes directory, including files copied from USB storage.

Requirements

  • Klipper with virtual_sdcard.file_position support.
  • Moonraker plus Mainsail or Fluidd.
  • A free MCU GPIO connected to a power-loss detection circuit.
  • Backup power that keeps the host and MCU alive during the detection/capture window.
  • Absolute extrusion (M82).

G-Code Shell Command extension

This is required, and is not bundled here because KIAUH is GPL-3.0 licensed while this repository is MIT.

The installer handles it for you, in this order:

  1. If it is already in ~/klipper/klippy/extras/, nothing happens.
  2. If you have KIAUH cloned, the local copy is used. No network needed.
  3. Otherwise it is downloaded from the KIAUH repository.

If the download fails or the KIAUH layout has changed, install it yourself and re-run install.sh:

# through KIAUH:  Advanced -> G-Code Shell Command
# or, if KIAUH is already cloned:
cp ~/kiauh/kiauh/extensions/gcode_shell_cmd/assets/gcode_shell_command.py \
   ~/klipper/klippy/extras/

Note that updating Klipper can remove this extension. If PLR suddenly stops working after a Klipper update with Unknown config object 'tgcode_shell_command', reinstall it the same way.

Installation

git clone https://github.com/Novin3dp/klipper-plr.git
cd klipper-plr
./install.sh

If the cloned files do not have executable metadata on your system, use:

chmod +x install.sh uninstall.sh
./install.sh

Updating an existing installation

If Klipper PLR was already installed from this repository, do not clone it again. Update the existing checkout and run the installer so the new files are copied into printer_data and any required configuration changes are applied:

cd ~/klipper-plr && git pull && ./install.sh

The installer keeps the previously saved printer-specific settings (such as the power-loss GPIO and Z lift) in ~/printer_data/plr/install.conf, so an existing installation normally does not require entering them again.

The installer automatically detects the current user and standard Klipper/Moonraker paths. On a fresh installation it asks only for:

  1. Power-loss GPIO, for example PB2.
  2. Z lift, measured on the actual printer.

The installer also asks for optional default bed/nozzle temperatures. Existing settings are stored locally in printer_data/plr/install.conf, so future Moonraker updates can reuse them.

Measuring Z lift

Do not guess this value. A wrong value can cause a nozzle collision.

  1. Remove the print from the bed.
  2. Home all axes and bring the nozzle to the bed.
  3. Raise Z exactly 10 mm.
  4. Restart Klipper so the MCU position is forgotten.
  5. Run G28 X0 Y0.
  6. Measure the actual nozzle-to-bed distance.
  7. Z_LIFT = measured distance - 10.

If you cannot safely measure this value, abort the installer and measure it first.

Slicer setup

The installer cannot edit a slicer profile reliably, so these two settings remain manual:

1. Extrusion mode

Either mode works. The build script reads the last M82 / M83 before the cut point and rebuilds the header to match: absolute restores the saved E position, relative emits G92 E0 and M83, because with relative deltas the absolute position is irrelevant.

If the slicer emits neither, absolute (M82) is assumed.

2. Add a layer checkpoint

In Before layer change G-code, add the equivalent of:

;BEFORE_LAYER_CHANGE
;[layer_z]
PLR_SAVE_LAYER Z=[layer_z]

The exact placeholder syntax depends on the slicer. OrcaSlicer/PrusaSlicer profiles commonly expose the layer height as [layer_z].

Verification

After installation:

RESTART
QUERY_BUTTON BUTTON=power_loss
PLR_STATUS

With normal printer power present, the input should report RELEASED. If the electrical logic is inverted, change the ! polarity in ~/printer_data/config/plr.cfg and restart Klipper.

Before a real print, follow TESTING.md and perform the dry run with the bed clear and no filament.

Safe host shutdown (optional)

Cutting power to a running Linux host can corrupt the SD card. The installer can enable an automatic halt right after the capture.

The sequence is:

24V lost -> capture writes variables.cfg -> wait N seconds -> sync -> halt

The shutdown worker is detached with setsid, because Klipper shuts itself down within moments of the power loss and would otherwise take the script with it.

Only enable this if your backup power holds the host up long enough. A halt needs roughly 10-15 seconds on top of the capture. If the supercapacitor drains first you get exactly the uncontrolled power cut you were trying to avoid, so nothing is gained. Measure how long your host actually survives before enabling it.

Enabling this installs /etc/sudoers.d/klipper-plr, allowing the Klipper user to run /sbin/shutdown without a password. The rule is validated with visudo -c before installation, so a malformed rule cannot lock you out of sudo. uninstall.sh removes it.

One behaviour to be aware of: on a brief brownout the host halts and stays halted, even if mains power returns a second later. It only comes back when its own supply is cycled. If the host is powered from the same 24V rail this resolves itself; with an independent supply you have to power it manually.

To turn it off later, comment out the RUN_SHELL_COMMAND CMD=PLR_SHUTDOWN line in ~/printer_data/config/plr.cfg and restart.


Recovery workflow

During a print, the configured power-loss input triggers PLR_CAPTURE_POSITION. The macro stores:

  • X/Y/Z/E
  • G-code file path
  • exact file offset
  • a recovery-pending flag

After the printer is powered back on and Klipper is restarted, a delayed macro builds a file under:

~/printer_data/gcodes/plr/

The build script then reports the result itself and prints how to start it:

PLR: recovery file ready  (18432 G-code lines)
  plr/<file>.gcode

  Start it with:   PLR_RESUME
  or:              SDCARD_PRINT_FILE FILENAME='plr/<file>.gcode'

PLR_RESUME is the safer of the two: it quotes the name for you, which matters when the file name contains a space or an apostrophe.

If the build fails, it says so and the previous recovery file is left untouched — it never reports success it cannot back up.

After a successful recovery print, the END_PRINT patch schedules automatic cleanup.

Commands

Command Purpose
PLR_RESUME Start the recovery print that was built
PLR_STATUS Display saved recovery information
PLR_CAPTURE_POSITION Manually capture the current position for testing
PLR_SAVE_LAYER Z=... Store a layer-height checkpoint
G31 Clear PLR data when no recovery is pending
PLR_FORCE_CLEAR Force-clear PLR data

State restored on resume

The resume header rebuilds the machine state that lived before the cut point and is therefore missing from the resumed body:

State How it is recovered
Bed and nozzle temperature parsed from START_PRINT
Position X/Y/Z from the capture
Extruder position G92 E from the capture (absolute mode)
Extrusion mode last M82/M83 before the cut point
Part cooling fan last M106/M107 before the cut point
Preview thumbnail slicer header copied from the original file

Fan recovery skips secondary fans (M106 P1, P2, ...) and only restores the default part fan. If the slicer had the fan off at that point, M107 is emitted rather than nothing.

Not currently restored: M220 speed factor, M221 flow factor, and bed mesh. Acceleration and velocity limits are not restored either, but Orca-style output re-emits SET_VELOCITY_LIMIT on almost every feature change, so they correct themselves within a few moves.


Edge cases the build script handles

Power loss during a recovery print. The captured path is then the recovery file itself, which is also where the script writes. Rebuilding in place would delete the source mid-read and leave a header with no moves — and no END_PRINT, so the heaters would stay on. The script detects this, snapshots the source first, and rebuilds from the snapshot. Layer comments are kept in the body for exactly this reason, so a chained recovery still reads the real layer height instead of falling back to the captured Z.

Power loss while idle. The detection input fires whenever 24V drops, including when you flip the mains switch on a machine that is not printing. The capture is then skipped entirely — storing a placeholder path would raise a false recovery on the next boot and, worse, overwrite a genuine pending one. The safe shutdown still runs. The console shows:

PLR power loss while idle - nothing to recover
PLR shutting down host

File names with spaces or apostrophes. Klipper parses extended parameters with shlex and then ast.literal_eval, so a path has to survive both layers. An unescaped name containing an apostrophe aborts the capture macro part-way — which means the recovery flag is never written either, and PLR silently does nothing. Both the capture macro and the reader handle every quoting case.

A slicer that emits no ;Z: comments. The script falls back to the captured Z and says so loudly, because that value may be a z-hop height rather than the real layer height. Check the G1 Z line in the resume file before starting it.

An empty or truncated body. A read error through a pipe still reports success, so the emitted body is counted rather than trusted. If nothing came out, the build fails and the existing recovery file is left alone.


Safety notes

  • Never assume a recovery is safe until the dry run has been completed on the actual printer.
  • Do not touch or manually move the machine after power loss unless your recovery procedure explicitly accounts for it.
  • Recovery near the Z travel limit is refused instead of silently clamping the height.
  • The resume builder deliberately moves X/Y before lowering Z to the saved layer height.
  • Bed mesh is not automatically restored by the generated recovery header; add the appropriate mesh-load command if your machine requires it.
  • If a recovery is cancelled midway, PLR data is intentionally retained so it can be investigated or retried.

Uninstall

cd ~/klipper-plr
./uninstall.sh

The uninstall script removes the PLR include/configuration and generated recovery files, but intentionally leaves gcode_shell_command.py and the END_PRINT snippet untouched.

Project status

This project is being developed and tested on real Klipper hardware. Compatibility depends on the printer's kinematics, homing behavior, slicer G-code and power-loss hardware.

License

MIT. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages