DEV Community

suzukesan
suzukesan

Posted on • Originally published at suzuke.page

Fixing the CL2529 Error on a Creality Ender-3 V3 Plus (Klipper Z-Probe Timeout) — Traced Through Source Code

Originally published (in Japanese) on my blog: suzuke.page/posts/ender3-v3-troubleshooting

Symptom

My Creality Ender-3 V3 Plus kept throwing a CL2529 error during the first Z leveling (G28 Z) at the start of a print.

  • A warning screen pops up during the initial leveling pass
  • Error code: CL2529
  • Pressing "Continue" re-runs leveling, and the print starts fine the second time
  • It happened often enough to be genuinely annoying

Investigation

1. Reading the Klipper log

cat /usr/data/printer_data/logs/klippy.log | grep -i "error\|529\|probe\|tri_"
Enter fullscreen mode Exit fullscreen mode

This turned up:

  • Error message: PR_ERR_CODE_G28_Z_DETECTION_TIMEOUT: G28 Z try probe out of times.
  • Internal code: key529
  • The bed uses 4 strain-gauge sensors (CH0–CH3) to measure probe pressure
  • CH3's sensor readings had abnormal spikes
[PRES_CH3] [..., 324110.000, -980617.000, ...]
Enter fullscreen mode Exit fullscreen mode

While the healthy channels ramped up gradually, CH3 jumped from 324110 to -980617 — a clear noise/glitch, not a real reading.

2. Inconsistent probe results

Comparing three probe attempts:

[G28_FIRST_MMS] [2.763, 3.248, 4.132]
Enter fullscreen mode Exit fullscreen mode

The spread between 2.763mm and 4.132mm is about 1.4mm — way too inconsistent. That inconsistency is the direct cause of the timeout.

3. Disassembling the binary

The core probing logic lives in a compiled binary:

/usr/share/klipper/klippy/extras/prtouch_v1_wrapper.cpython-38-mipsel-linux-gnu.so
Enter fullscreen mode Exit fullscreen mode

Running strings on it surfaced the relevant config parameter names:

strings prtouch_v1_wrapper.cpython-38-mipsel-linux-gnu.so | grep tri_
Enter fullscreen mode Exit fullscreen mode
tri_try_max_times
tri_min_hold
tri_max_hold
tri_hftr_cut
Enter fullscreen mode Exit fullscreen mode

These turned out to be parameters read straight out of printer.cfg.

4. Finding the actual source

The breakthrough: Creality published the Python source for prtouch_v1_wrapper.py under the GPL in December 2025.

That source makes the error logic completely clear.

Error definition:

PR_ERR_CODE_G28_Z_DETECTION_TIMEOUT = {
    'code': 'key529',
    'msg': 'PR_ERR_CODE_G28_Z_DETECTION_TIMEOUT: G28 Z try probe out of times.',
    'values': []
}
Enter fullscreen mode Exit fullscreen mode

Retry limit:

self.tri_try_max_times = config.getint('tri_try_max_times', default=10, minval=0)
Enter fullscreen mode Exit fullscreen mode

If it isn't set in printer.cfg, the default is 10 retries. Setting it to 0 disables the check entirely.

Error-raising logic:

if self.tri_try_max_times != 0:
    if use_tri_times == self.tri_try_max_times:
        self.print_msg('WHY ERROR',
            "FUN:G28_Z Coarse Probe Out of max {} times:{}...".format(...))
        self.ck_and_raise_error(True, PR_ERR_CODE_G28_Z_DETECTION_TIMEOUT)
Enter fullscreen mode Exit fullscreen mode

The error is only ever raised when tri_try_max_times != 0. So setting it to 0 skips the check completely.

What "Continue" actually does

I also traced what happens when you hit "Continue" on the printer's screen after the error:

CL2529 error fires
  ↓
ck_and_raise_error() runs
  ├─ Nozzle retracts 5mm (safety measure)
  └─ raise self.printer.command_error(...)  ← shows the warning
  ↓
User presses "Continue"
  ↓
SET_KINEMATIC_POSITION → resets current position
  ↓
G28 Z restarts from scratch
  ↓
All channels read clean this time → probe succeeds → print starts
Enter fullscreen mode Exit fullscreen mode

In other words, this is a command_error (a warning-level condition), not a Klipper shutdown. Pressing "Continue" just restarts leveling from zero, and the second attempt usually succeeds normally.

The fix

Add tri_try_max_times: 0 to the [prtouch_v2] section of printer.cfg:

ssh root@192.168.0.230
vi /usr/data/printer_data/config/printer.cfg
Enter fullscreen mode Exit fullscreen mode
[prtouch_v2]
# ... existing settings ...
tri_try_max_times: 0
Enter fullscreen mode Exit fullscreen mode

Also clear the error history so it stops showing on screen:

echo '{"list":[]}' > /usr/data/creality/userdata/fault_code/fault_code_info.json
Enter fullscreen mode Exit fullscreen mode

Then restart Klipper to apply it:

/etc/init.d/S55klipper_service restart
Enter fullscreen mode Exit fullscreen mode

Making it survive firmware updates

The problem: firmware updates wipe the setting

Klipper's startup script (/etc/init.d/S55klipper_service) compares the ROM firmware version against the version recorded in printer.cfg on every boot. When a firmware update changes that version, the body of printer.cfg gets overwritten with the ROM defaults (only the SAVE_CONFIG section survives).

The ROM's default [prtouch_v2] section doesn't include tri_try_max_times, so the CL2529 error comes right back after an update.

The fix: an init script that re-applies it automatically

I added /etc/init.d/S56klipper_custom, which runs right after Klipper starts and re-injects the setting if it's missing:

cat > /etc/init.d/S56klipper_custom << 'EOF'
#!/bin/sh
#
# Ensure custom Klipper settings persist across firmware updates.
#

CFG=/usr/data/printer_data/config/printer.cfg

apply_patch() {
    if [ -f "$CFG" ] && ! grep -q "tri_try_max_times" "$CFG"; then
        sed -i "/^\[prtouch_v2\]/a tri_try_max_times: 0" "$CFG"
        return 0
    fi
    return 1
}

start() {
    if apply_patch; then
        echo "Custom settings applied, restarting Klipper..."
        /etc/init.d/S55klipper_service restart
    else
        echo "Custom settings already present, no action needed."
    fi
}

case "$1" in
  start)
        start
        ;;
  stop)
        ;;
  restart|reload)
        start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac
exit $?
EOF
chmod +x /etc/init.d/S56klipper_custom
Enter fullscreen mode Exit fullscreen mode

Flow:

  1. Printer boots → S55 starts Klipper (a firmware update may have just overwritten printer.cfg)
  2. S56 runs immediately after → checks whether tri_try_max_times is present in printer.cfg
    • If present → does nothing
    • If missing → adds tri_try_max_times: 0 back into [prtouch_v2] and restarts Klipper

If you have other settings you want to survive firmware updates, you can add the same pattern inside apply_patch.

Is this actually safe?

tri_try_max_times: 0 only disables the error check — the probing itself still runs exactly as before. In the source, setting it to 0 just skips the if block that raises the error; it doesn't touch probe accuracy or leveling quality in any way.

The fact that pressing "Continue" almost always succeeds on the retry is further evidence this is transient sensor noise, and that the probe's own retry logic already compensates for it just fine.

A hardware-level improvement worth considering

This fix addresses it in software, but the root cause is that one of the four strain-gauge sensors under the bed — specifically CH3 — is producing abnormal noise.

The config change just prevents that noise from tripping the error; it doesn't fix the sensor itself. Replacing the strain-gauge sensor would likely improve actual probe accuracy and give more consistent leveling, on top of making this error go away for good.

Top comments (0)