Add probe-length compensation to tool-change offset math - #1
Draft
WhiteRott wants to merge 23 commits into
Draft
Conversation
Backing field for a new global probe-stylus-length setting that compensates for touch probes whose contact point sits below the tool tip.
Adds the stylus stickout compensation to the existing touch-off formulas: the probe's contact point sits below the tool tip, so the true tool-tip Z at trigger is touch_plate_height + tool_probe_length above the reported position.
Mirrors touchPlateHeight's load/save/unit-conversion path: action, controller:state sync, loadToolConfig, and componentDidUpdate persistence.
Adds an input next to Touch Plate Height for the touch probe's stylus/stickout length, and updates the generated G-code preview (GRBL/Smoothie/Marlin/TinyG branches) plus the custom-commands example templates to include tool_probe_length in the Z offset math.
- Environment section now reflects the Node 22 migration (nvm default, yarn reinstalled under 22, the nvm auto-use PATH fix, gh CLI auth) - Current-task section reflects actual progress: widget review done, toolProbeLength shipped on this branch with a draft PR open, tool library and probing cycles still not started - Records the widget review findings and grblHAL capability notes that were previously only in conversation, not the repo
Auto-generated by the i18n tooling picking up the two new i18n._() calls added to Tool.jsx (label + explanatory text). English has the real values; other locales get empty placeholders for translators.
Port 8080 was occupied by a Docker container on this machine (docker-proxy, part of the OpenClaw stack) — already documented in CLAUDE.md, just never committed.
New CRUD API for a tools table (number, name, type, diameter, fluteLength, length, flutes, notes), modeled on api.macros.js's list-record pattern rather than api.tool.js's single-flat-config pattern -- this stores many tool records, not one global config. Routes: GET/POST /api/tool-library, GET/PUT/DELETE /api/tool-library/:id. Verified end-to-end (create, fetch, read, update with partial-field merge, delete, 404/400 error paths) against a live dev server.
Mirrors the macros client wrapper's list-CRUD pattern (fetch/ create/read/update/delete) against the new /api/tool-library routes.
New widget (list view + add/edit modals) for managing a table of tools: number, name, type, diameter, flute length, overall length, flute count, notes. Modeled on the Macro widget's UI structure (closest existing analog -- a record-list widget with CRUD), backed by the new /api/tool-library endpoints. Registered at the standard 3 touchpoints every widget needs: Widget.jsx (id -> component map), WidgetManager.jsx (on/off toggle list), defaultState.js (secondary-container placement + per-widget config slot). Verified end-to-end against a live dev server under Node 22 -- builds clean, widget code ships in the bundle, API works through the full stack. Deliberately out of scope for this commit: per-tool Z offsets (item 2) and probing-cycle integration (item 3) both depend on this existing first. The top-of-window "Windows" dropdown menu is also still pending, per plan, until last.
New POST /api/tool-library/:id/activate endpoint sets one tool's
active flag true and clears it on all others (atomic, single
config.set call). Tool Library widget gets a per-row select control
(green checkmark) and publishes the active tool over pubsub
('tool:active') on every fetch, so other widgets can react to
whichever tool is currently marked as mounted.
Replaces the free-text "type" field with a dropdown of 15 shape types (Endmill, Ball End, Bullnose, Chamfer, V-Bit, Tapered Ball Nose, Drill, Reamer, Dovetail, Radius, Tap, Thread Mill, Slitting Saw, Probe, Custom), matching FreeCAD's CAM workbench tool-bit naming for consistent terminology. Feeds directly into the Visualizer's procedural per-shape 3D geometry (next commit).
tool geometry, and always-on camera rotation This is one connected debugging session against real grblHAL hardware -- each finding below builds on the last, so it's committed together rather than as artificially separated pieces. Coordinate-frame fix (the core bug): - The grid/limits box was built in raw MACHINE coordinates (rebuildCoordinateSystems), but the cutting tool/pointer were positioned from WORK coordinates. These only agree when work-zero happens to sit near machine-zero -- with a real WCS offset (this machine's is 240/-130/-60mm), the tool rendered far outside the drawn work area. Switched updateCuttingToolPosition/ updateCuttingPointerPosition to use machinePosition, matching the grid's own convention. - Added a work-zero origin marker (small RGB axis flag) that stays behind at wherever X0/Y0/Z0 currently is, computed from machinePosition - workPosition (= WCO) minus the pivot -- unlike the tool, it doesn't track live position, so it's a persistent reference distinct from the moving tool indicator. - The loaded G-code toolpath had the same class of bug: it was self-centering on its own bounding box instead of anchoring to work-zero like everything else. Added updateGCodePosition() using the same formula, so a loaded file now overlays the stock/marker correctly instead of floating near machine-home. - Also flagged (not a code fix, a data fix applied via the API): the stored machine profile had zmin/zmax with the wrong sign relative to this machine's actual negative-going Z travel, which is what made the fixed code still look wrong until corrected. Stock/material visualization (StockSize.jsx, PrimaryToolbar.jsx): - New translucent stock box anchored the same way as the origin marker (front-left-top corner at work-zero), sized via a new "Edit Stock Size" dialog (Width/Length/Thickness), with a Show/Hide toggle alongside the other display-object toggles. Procedural cutting-tool geometry (CuttingToolGeometry.js): - Replaces the single fixed-shape bit.stl (uniformly scaled regardless of tool type) with type-aware Three.js primitives (cylinder/cone/rounded-tip combinations) sized to the active tool's actual diameter/length, dispatched by the new shape-type field. Removed the now-unused STL/texture loading path from helpers.js. Always-on free camera rotation (SecondaryToolbar.jsx, constants.js): - Removed the Pan/Rotate mode toggle -- TrackballControls already defaults to left=rotate, right=pan, wheel=zoom simultaneously (matching Candle's scheme) when nothing overrides the mouse button state. The toggle was actively fighting that default. Touch/tablet input is handled by a separate code path and is unaffected.
Auto-generated by the i18n tooling for the active-tool selection, shape-type dropdown, stock size dialog, and stock visibility toggle strings added this session. English has real values; other locales get empty placeholders for translators.
Same theory throughout: compute known sample points in JS, probe them one at a time via G38.2 (staying in G90, absolute targets = point + signed search distance), accumulate contacts as GrblController already does for the existing PRB report handling, fit geometry once all points are in. No dependence on grblHAL macros/subroutines -- checked grblHAL's own changelog, M98/M99 support was only added in build 20250202, and this machine's firmware (1.1f, 2023-01-29) predates it by two years, so this had to be done in JS regardless. - edgeprobe.js: N-point line fit (createEdgeProbePoints, fitLine) -- the foundation. A single edge, 2+ points, least-squares angle. - probecycles.js: fitCircle (Kasa algebraic least-squares, 3+ points, shared by Bore/Boss), createCirclePoints, fitRectangle (4-wall center/width/length, shared by Rectangular Pocket/Solid), and intersectLines (two fitted edge lines -> a corner point without assuming a perfect 90°). - GrblController.js: edgeProbeState/cornerProbeState/circleProbeState/ rectProbeState (mirroring the existing autolevel probeState shape), a shared trackProbeResult() helper factoring the "accumulate PRB report, check completion, emit :update" bookkeeping common to all of them, and edgeprobe/cornerprobe/circleprobe/rectprobe start/stop/getProbeState socket handlers. Corner Probe specifically: reuses edgeprobe's point-generation and line-fit twice (once per edge, 2+ points each) rather than a single touch per edge, then intersects the two fitted lines -- handles a non-90° corner correctly instead of assuming one. Server-side only in this commit; only Edge/Skew Probe has a client widget so far (next commit). Corner/Bore/Boss/Rectangular Pocket/ Rectangular Solid are probe-able via the socket API but have no UI yet.
First cycle with a UI, on top of the server-side infrastructure from the previous commit: pick a probe direction (+X/-X/+Y/-Y, which determines the line/probe axis split), a start and end point (with pin-current-position buttons), point count, probe distance/feedrate, and retract distance. Shows live progress while probing and, on completion, the computed skew angle plus a table of the individual contact points -- flags a warning if the edge isn't aligned to the machine axis. Registered at the standard 3 widget touchpoints (Widget.jsx, WidgetManager.jsx, defaultState.js), placed in the secondary container next to Probe/Autolevel.
Ball-tip stylus contact points are reported at the ball center, not the true surface, so edge/corner probes correct each point by the stylus radius along its probe axis before fitting lines/corners. Also add a global Max Probe Deflection setting (alongside the existing Probe Length stylus stickout) so Probing Cycles can warn about reduced Z clearance and block a run whose configured Probe Distance would exceed the machine's safe deflection limit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ng it Each sample point is now probed twice: a fast approach to find the edge, then -- after backing off and a short settle delay -- a slower touch to the same target for an accurate, repeatable reading. Only the slow touch feeds the line fit. Both touches are acknowledged to the client (live phase status + a running touch log) so an in-progress cycle is never ambiguous with a stalled one. The real bug behind tonight's stalls: cncjs's periodic $G parser-state poll writes to the serial port on its own timer, independent of the feeder's send/wait-for-ok sequencing. A $G query already in flight when a probe cycle starts has its [GC:...]+ok reply land mid-cycle, and since that reply is consumed by matching a flag rather than by content, it can swallow the probe's own 'ok' -- the machine goes idle but the next queued line (e.g. the retract right after a touch) never gets sent, with no error reported. Fixed by giving an active probe cycle unconditional priority in the 'ok' handler, checked before the $G-reply tracking, plus suppressing new $G polls entirely while a cycle is running. Also surfaces the live probe pin state (Pn:) in the widget so a stuck or disconnected probe input is visible at a glance, and handles a probe that fails to trigger (G38.2 reaches its target with no contact) instead of leaving the cycle hanging with no feedback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The generic "Probe" name was ambiguous once Probing Cycles existed alongside it. This widget's job is specifically single-axis touch-off to set a work/tool zero, so "Zero Probe" describes it more precisely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds Corner as a selectable probe type alongside Edge/Skew, with UI for configuring both edges (start/end points, direction, point count, probe distance, retract distance) plus the shared feed/backoff/settle/tip fields. Server-side, corner probe is reworked to the same 2-touch technique as edge probe (fast find, back off, settle, slow confirm), sharing the pendingAcks-based 'ok' priority fix so it can't stall the same way edge probe did earlier tonight. Also fixes a real safety issue found during hardware testing: the move to each probe point's approach position was a single diagonal G0, which can cut straight across the corner when transitioning between edges whose points are far apart in both axes. Both edge and corner probe now always move one axis at a time -- line axis first, probe axis last, so the final approach leg is always a straight move along the direction about to be probed. Not yet fully validated on hardware: an in-progress test hit a probe that failed to find the edge at one sample point (likely a real geometry/configuration mismatch, not a code issue -- the failure was caught and reported cleanly, no stall). Machine left in Alarm state, untouched, pending the operator's inspection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two hardware-driven fixes from today's testing: Retry on missed contact: a G38.2 that reaches its target without triggering raises a hard alarm in grblHAL, which previously ended the cycle and left the machine locked for manual inspection. Spring-loaded styli miss intermittently at slow feeds, so a single miss shouldn't end an otherwise-good run. Failed touches now retry the whole point -- unlock, requeue, same search distance first, then +1mm, +2mm... up to +5mm total before giving up and leaving it alarmed as before. The feeder queue is explicitly cleared before requeuing so stale motion from the failed attempt can't leak out after the unlock. Z-lift on edge transitions: the move from the last X-edge point to the first Y-edge point is the one point-to-point travel that crosses a real, unpredictable distance across the part -- yesterday's axis-by-axis fix only removed the diagonal, it couldn't know whether the resulting path was clear, and it wasn't (bent a probe stylus). That transition now retracts to a configurable safe Z, travels, and plunges back down. Within an edge, consecutive points stay axis-by-axis at probing depth. Verified on hardware: full 6-point corner probe completed end-to-end, including two retries that recovered correctly and a clean edge transition. Corner repeatable to ~0.01mm across runs. Known issue, not yet fixed: the widget's Run button stays disabled after a cycle completes normally until Stop is pressed. Tried clearing probe state on completion; that reopened the ok-routing race that stalls the final retract, so it was reverted -- the real cause is still open. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both sections had drifted badly out of date -- tool library and probing cycles were still marked "not started" despite being substantially complete. Rewrites the current-task and widget-map sections to match actual repo state.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three-part effort on top of this fork's existing Probe/Autolevel/Tool widgets:
shape type, diameter, flute length, overall length, flutes, notes, active-tool flag).
Server:
src/server/api/api.toolLibrary.js(CRUD + paging). Client:ToolLibrarywidgetwith add/edit modals.
toolProbeLength,wired into Grbl WCS/TLO offset math and the Tool widget's G-code preview). Per-tool Z
offsets, now that the tool library exists to back them, are still to come.
ProbingCycleswidget with Edge/Skew Probe and Corner Probecycles (2-touch fast-find/slow-confirm technique, least-squares line/circle fitting,
probe-tip diameter compensation, configurable safety limits, retry-on-miss for
spring-loaded styli, Z-lift on edge transitions to avoid cutting across stock). The
original
Probewidget was renamed to Zero Probe to disambiguate from this.Context
Builds a tool library, tool length offset support, and extended probing cycles on top of
this fork's existing Probe/Autolevel/Tool widgets. Tool library and probing cycles are
largely complete; per-tool Z offsets are the remaining piece.
Test plan
eslintpasses clean on all changed filesyarn devbuilds and serves cleanly under Node 22the retry-on-miss and Z-lift fixes that came out of that testing
toolProbeLength) still not dry-run tested inisolation on real hardware
🤖 Generated with Claude Code