Skip to content

feat(mcu): support Kalico non-critical MCU reconnection - #474

Merged
Jomik merged 7 commits into
mainfrom
feat/idempotent-initialize
Apr 26, 2026
Merged

Jomik merged 7 commits into
mainfrom
feat/idempotent-initialize

Conversation

@Jomik

@Jomik Jomik commented Apr 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds support for Kalico's non-critical MCU reconnection lifecycle. When a Cartographer MCU configured as is_non_critical disconnects and reconnects at runtime, the plugin now re-initializes firmware constants, re-lookups commands, and re-validates/reloads models — without leaking resources or accumulating stale callbacks.

This builds on #472 (graceful startup when disconnected) to complete the reconnection story.

Fixes #473

Decisions & callouts

  • _initialize is now idempotent: KlipperCartographerConstants and KlipperCartographerCommands are constructed once (guarded by is None), but their initialize() methods are called on every _initialize to re-read firmware values and re-lookup commands.
  • Kalico event registration uses getattr detection for get_non_critical_reconnect_event_name / get_non_critical_disconnect_event_name. Both must be present for handlers to register — this matches Kalico's API where both are always provided together.
  • Reconnect callbacks are wrapped in try/except to prevent a failing callback from breaking Kalico's reconnection flow.
  • _register_data_response() is called on every _initialize. This is safe because Klipper's register_serial_response replaces existing handlers rather than accumulating them.

Testing

Install this branch for testing:

~/klippy-env/bin/pip install --no-cache-dir --force-reinstall "git+https://github.com/Cartographer3D/cartographer3d-plugin.git@feat/idempotent-initialize"
sudo systemctl restart klipper
  • Start with Cartographer MCU connected — everything should work normally (no regression)
  • With is_non_critical = true, disconnect and reconnect the Cartographer MCU — plugin should re-initialize and resume normal operation
  • Check klippy.log after reconnect — should see "Cartographer MCU reconnected" info message and successful model reloading
  • Disconnect the MCU — should see "Cartographer MCU disconnected" warning in klippy.log and probe operations should fail with clear errors

Known issue

If the Cartographer MCU is disconnected, Klipper firmware is restarted, and then the MCU is plugged back in, Kalico's reactor crashes with Failed automated reset of MCU 'cartographer' raised from non_critical_recon_event. This is a Kalico-side bug — the timer callback at klippy/mcu.py::non_critical_recon_event does not catch the error raised by recon_mcu() / _connect(), so it escapes to printer.run and shuts klippy down. Tracked upstream as KalicoCrew/kalico#870.

The reverse order (disconnect → reconnect, no FW restart in between) works correctly with this PR.

@github-actions github-actions Bot added the enhancement New feature or request label Apr 13, 2026
@Jomik
Jomik force-pushed the feat/idempotent-initialize branch 2 times, most recently from 43c09a0 to a264bd8 Compare April 25, 2026 19:54
@Jomik
Jomik force-pushed the feat/idempotent-initialize branch from a264bd8 to fe4827d Compare April 25, 2026 20:03
@yell3D

yell3D commented Apr 25, 2026

Copy link
Copy Markdown
  • Unhandled exception when query_probe while carto disconnected
  • Unhandled except when klipper + firmware restart with disconnected carto as soon as carto gets connected.

Klippy log with minimal logging:
klippy.log

image image

When the Cartographer MCU is configured as is_non_critical=true on Kalico
and is currently disconnected, sending any cartographer_* command raised
klippy.serialhdl.error, which klippy treats as an internal error and shuts
down the printer.

Pre-check mcu.non_critical_disconnected before each command send and raise
RuntimeError instead. The integrator's _catch_macro_errors wrapper converts
RuntimeError to gcmd.error, producing a clean user-facing message without
shutdown.

The check is a no-op on stock Klipper (attribute does not exist; getattr
default is False).
@Jomik
Jomik force-pushed the feat/idempotent-initialize branch from b18833d to 63752c8 Compare April 26, 2026 09:30
@yell3D

yell3D commented Apr 26, 2026

Copy link
Copy Markdown

lgtm
image

i've also updated the code in KalicoCrew/kalico#870

image image

Also did 2 regular prints after the reconnect which were fine too

Move the non_critical_disconnected check from KlipperCartographerCommands
into the KlipperCartographerMcu.commands property getter. Single guard
site at the property boundary; every command access naturally short-
circuits without per-call _check_connected() calls.

Rename 'Mcu not initialized' to 'Cartographer MCU not initialized' for
consistency with the disconnect message and to identify which MCU is
affected when multiple are configured.
@Jomik
Jomik merged commit f7b1b36 into main Apr 26, 2026
10 checks passed
@Jomik
Jomik deleted the feat/idempotent-initialize branch April 26, 2026 18:14
@ScheglyakStas

ScheglyakStas commented Jul 6, 2026 •

Copy link
Copy Markdown

Today I got "Unhandled exception during run" when my Cartographer disconnected mid-print, with plugin version 1.8.0. Seems like the issue is still present.

@yell3D

yell3D commented Jul 10, 2026

Copy link
Copy Markdown

Today I got "Unhandled exception during run" when my Cartographer disconnected mid-print, with plugin version 1.8.0. Seems like the issue is still present.

your issue is likely not the plugin but Kalico itself.
check out this and the linked issue

KalicoCrew/kalico#905

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCU: validate and load models on non-critical MCU reconnect

3 participants