Files
firmware/docs/notes/60-background.md
Oleksandr ShyshatskyiandClaude Opus 5 a22f3b04ef Tool calibration speaks klipper-toolchanger's G-code
STATION_CALIBRATE is TOOL_LOCATE_SENSOR and TOOL_OFFSET_CALIBRATE is
TOOL_CALIBRATE_TOOL_OFFSET, with upstream's call shape as well as its
names: no TOOL= (the mounted tool is the one measured, so SELECT_TOOL
first, as upstream's macro does) and no PLATE_REMOVED=.

The renames are not just conformance. Upstream says "run
TOOL_LOCATE_SENSOR with tool 0" because tool 0's nozzle is the only
thing on their machine that can reach the pin -- their baseline is a
tool, so tool 0's own error rides into every other tool. Here the
carriage's probe element sits above the tool mount and reaches the
station directly with nothing installed, so the command means what it
says: locate the sensor, with no tool. That baseline cannot be spoiled
by a badly seated T0, which is what lets nozzle_x/y/z stay absolute and
per-tool independent.

PLATE_REMOVED=1 goes because the plate check that runs anyway is the
better guard: it parks the carriage and MEASURES whether the plate is
there, where the flag only asked the operator to promise. Dropping it
also removes the last non-upstream required parameter.

Losing TOOL= means losing TOOL=ALL, so CALIBRATE_TOOL_OFFSETS loops over
printer.toolchanger.tool_numbers -- the same shape as upstream's macro.
GRAB=/RELEASE= go with it: they cannot mean anything once the tool has
to be mounted already. The plate check still needs an empty carriage, so
the command parks the selected tool and picks it straight back up.

Probe sampling gets klipper-toolchanger's parameters -- SAMPLES,
SAMPLES_TOLERANCE, SAMPLES_TOLERANCE_RETRIES, SAMPLES_RESULT,
SAMPLE_RETRACT_DIST, PROBE_SPEED -- on both commands and in
[ff_tool_offset]. The fork's e_stop ALREADY samples (sub_cycle_cnt
touches, spread rejected above error_v, retried main_cycle_cnt times,
averaged); what was missing was reaching those numbers from a command
and a median. So _estop runs that loop itself over the fork's
single-touch _probe, every default is read off the [e_stop <axis>]
object, and an unparameterised command probes exactly as it did before.
Spread is judged after each touch, as the fork does, so a round already
known bad is not finished. Two things the fork does not bound are
bounded here: the 9999 move-failure sentinel it spins on forever, and an
e_stop with no _probe, which falls back to run_probe as one sample
rather than cubing the touch count.

Also fixes _plate_check reaching for z_trigger where it had bound
station_z -- a NameError left by the rename sweep in 4704ca0, on the one
path that runs before a nozzle is allowed to descend, and now the only
guard that the plate is off.

docs/calibration.md is the operator's side of all of it: the order, what
each command prints, and every refusal it can raise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 13:49:14 +03:00

3.1 KiB
Raw Permalink Blame History

Background: filament system, calibration, persistent state

Context recovered from the binary. Written when none of it was ported and the stock touchscreen still handled it; both halves of that have since changed. The runout handling, filament load/unload and the offset calibration below ARE ported now (ff-runout.cfg, ff-filament.cfg, ff_tool_offset.py), and there is no stock touchscreen on a modded printer — firmwareExe is replaced and HelixScreen is the only UI. What is genuinely still unported is marked as such per item. Kept as reference.

Filament system

  • 4 channels; per-channel presence (fd_ex0..3) and motion/clog (fm_ex0..3) sensors; manual_stepper gear_stepper is the tool lock motor, not a feed hub. Errors: E0162 runout, E0163 clog.
  • Runout auto-swap (the flagship app feature): on runout mid-print the app pauses, finds another channel with the same material/color, switches channel (possibly a head change), refeeds, purges, restores position and resumes. All app-side.
  • Load ≈ heat, G92 E0, G1 E150/G1 E145 long feed; unload ≈ feed assist then retract ramp G1 E-50 F150 … tip forming.
  • Pre-print screen parses 3MF per-tool type/color and lets the user remap slicer tools to physical channels (SDCARD_SET_GCODE_EX_USED_BASE/CHANGED).
  • Drying box: separate MCU on /dev/ttyS3, own textual protocol (DryingService), 2 chambers with temp/humidity control. Not Klipper at all.

Calibration flows (all driven by the app)

  • Bed leveling: eddy probe, 10×10 bicubic; factory mesh saved as profile MESH_DATA, per-print mesh as default; SAVE_CONFIG after.
  • Nozzle offset calibration (testEddyExtruderOffsetForward*): heats and cleans each nozzle, probes the fixed under-bed sensor with the eddy, then touches it with each nozzle; circular probing + least-squares circle fit for XY; results → tN_offset_x/y/z, z_station_pos in extruder.json. Reimplemented in Klipper as TOOL_LOCATE_SENSOR / TOOL_CALIBRATE_TOOL_OFFSET (45-tool-offset-calibration.md, 46-offset-calibration-recovered.md); the JSON is imported by FF_IMPORT_FIRMWARE_CONFIG — once per install, run for you at the first boot by bin/ff-startup.py — and is never a runtime source.
  • Input shaper: STEPPER_RESONANCE_FACTORY_CALIBRATE (fork) / SHAPER_CALIBRATE.
  • Auto PA: prints slow-fast-slow line patterns per PA candidate, scores via PA_ACTION/PA_GET (sensor unknown), stores per-tool table via SET_PA_ADVANCE.

Persistent state map (/usr/data/firmwareRes/config/)

  • extruder.json, test.json, zoffset.json — motion-critical, see 40-offsets.md. Back these up.
  • filament.json — per-channel exN_filament_type/color/diameter.
  • general.json/print.json — UI toggles: levelOpen (per-print leveling), openDoorPause, filamentDetected, aiCheck, camera, LED…
  • total.json — maintenance counters/service reminders.
  • levelData — 10×10 mesh copy the UI renders.

Also worth backing up before any experiment: /usr/data/config/printer.cfg (+ printer.base/macro/motor/override.cfg) and the whole /usr/prog/klipper/ fork tree.