Skip to content

G28 with features#4172

Open
greatEndian wants to merge 6 commits into
LinuxCNC:masterfrom
greatEndian:g28
Open

G28 with features#4172
greatEndian wants to merge 6 commits into
LinuxCNC:masterfrom
greatEndian:g28

Conversation

@greatEndian

@greatEndian greatEndian commented Jun 16, 2026

Copy link
Copy Markdown

G-code machine homing: G28.2 / G28.3 and optional GCODE_HOMING for plain G28

What this is

Adds the ability to reference (home) a machine from G-code, instead of
only from the GUI, in three small, opt-in pieces:

  • G28.2 — run the homing cycle on all joints (in HOME_SEQUENCE order)
  • G28.3 — unhome all joints
  • [RS274NGC]GCODE_HOMING=1 (INI opt-in) — a plain G28 references the
    machine first, before its normal return move, only when the machine is
    not already fully homed

Plus two small fixes that make a queued (in-program / MDI) home actually
reach motion (see below).

G28.2 / G28.3 are non-modal (modal group 0) codes, following the
existing G28.1 / G30.1 pattern. Bare form — axis words are ignored; they
act on all joints.

What it's for

  • Unattended / scripted power-up — a program or MDI line can reference
    the machine, so an operator (or an automation layer) doesn't have to open
    the GUI and click Home All.
  • Operators who prefer a typed reference command over the GUI.
  • GCODE_HOMING=1 lets you put a plain G28 at the top of a program: on a
    cold machine it homes first, then returns; on an already-homed machine it
    is a pure return move and costs nothing.

Relation to industrial-standard G28

  • Plain G28 return-to-reference behavior is unchanged and still matches
    the Fanuc/Haas convention: go to the machine reference point through the
    intermediate point given by the axis words.
  • GCODE_HOMING=1 makes the first G28 after power-up also establish
    the reference (run the homing cycle), which mirrors how a Fanuc-style
    control without absolute encoders performs its zero-return/reference on a
    G28. Once referenced, subsequent G28s are pure returns, exactly as on
    those controls.
  • G28.2 / G28.3 (explicit home / unhome) are LinuxCNC extensions — there
    is no standard Fanuc equivalent — kept in the spirit of the existing
    G28.1 / G30.1 reference-point codes.

[RS274NGC]GCODE_HOMING=1 — behavior

Situation Plain G28 does
Flag absent / =0 (default) Stock LinuxCNC G28 (return only)
=1, machine not fully homed Home all joints, then the G28 return
=1, machine fully homed Pure return (no homing)
=1, G30 / G28.2 / G28.3 Unchanged (flag is G28-only)

Default-off is bit-identical to stock: the homing call is only emitted under
FEATURE(GCODE_HOMING) for G_28.

How it works (single channel)

  1. interp convert_home: under FEATURE_GCODE_HOMING and move == G_28,
    emit a new canon op HOME_CYCLE_IF_UNHOMED() before the waypoint /
    return moves (so it completes while joint positions are still unknown).
  2. canon queues EMC_JOINT_HOME carrying an EMC_HOME_ALL_IF_UNHOMED
    (-3) sentinel in its existing joint field.
  3. task resolves the sentinel at execution time: if all_homed(), drop the
    home (queued return alone = legacy G28); otherwise issue the normal
    home-all (emcJointHome(-1)) — the same path G28.2 and the GUI Home
    All
    already use.

The sentinel mechanism itself adds no new message field and no NML-layout
change
, and touches neither homing.c nor the motion command path — it
resolves entirely in task by reusing the existing joint field with a
reserved value. (The small command.c idle-homing fix below is a separate
prerequisite, shared with G28.2.)

Supporting fixes (also needed for G28.2)

  • task: EMC_JOINT_HOME_TYPE / EMC_JOINT_UNHOME_TYPE were missing from
    emcTaskCheckPreconditions(), so a queued home/unhome hit the default
    case and was silently dropped before reaching motion. They now return
    WAITING_FOR_MOTION (drain prior motion, then home).
  • motion: single-channel homing required motion_state == FREE; now it is
    also permitted when motion is otherwise idle (in position, queue empty), so
    a home issued from MDI or a program is honored. Refusing mid-motion still
    holds.

Testing

  • Interpreter (rs274 -i): GCODE_HOMING=1 plain G28 emits
    HOME_CYCLE_IF_UNHOMED() ahead of the return traverse; G30 does not home;
    G28.2 homes unconditionally; default-off G28 emits no homing.
  • Builds clean (interp / task / motion).
  • On-machine homing sign-off (a real reference run triggered from MDI / a
    program) is pending and will be added.

Credits

Developed in collaboration with @grandixximo, with whom I discussed the requirements that
shaped this feature. Thanks for the design discussion and for driving the
real-world G28 / homing-from-G-code use case.

greatEndian and others added 4 commits June 16, 2026 07:00
Add two non-modal (group 0) G-codes to trigger the machine homing cycle
from G-code, so a machine can reference / clear references from MDI or a
program instead of only from the GUI:

  G28.2   run the homing cycle (all joints, in HOME_SEQUENCE order)
  G28.3   unhome (all joints)

- interp: enum G_28_2/G_28_3, modal group 0, accepted in check_g_codes;
  convert_modal_0 -> convert_home_cycle (cutter-comp guard)
- canon: HOME_CYCLE()/UNHOME_AXES() -> EMC_JOINT_HOME/UNHOME(joint=-1);
  saicanon/gcodemodule stubs

Bare form only (no axis words). Useful for unattended power-up and for
operators who prefer a typed reference command.
EMC_JOINT_HOME_TYPE and EMC_JOINT_UNHOME_TYPE were missing from
emcTaskCheckPreconditions(), which is called for every command pulled
off the interp_list. A homing command that arrives via the queue (e.g.
from a G-code like G28.2, or any front-end that queues it) therefore
hit the 'default' case and returned EMC_TASK_EXEC::ERROR - the command
was dropped and never reached motion. Add both types returning
WAITING_FOR_MOTION (drain prior motion, then home).

Bug fix; independent of the G28.2/G28.3 codes (any queued home benefits).
EMCMOT_JOINT_HOME required motion_state == FREE. Allow it also when
motion is otherwise idle (in position, queue empty) so a homing command
issued from MDI or a program (G28.2) is honored, while still refusing
to home mid-motion. No change when already in free mode.
…_HOMING=1)

With [RS274NGC]GCODE_HOMING=1 (default 0 = stock), a plain G28 runs the
machine homing cycle before its natural return move whenever the machine
is not already fully homed; a fully-homed machine sees a pure legacy G28
(return only). G30 and G28.2/G28.3 are unchanged.

Lets a machine reference itself from MDI or from the top of a program
(e.g. unattended power-up) instead of only from the GUI, while a homed
machine pays nothing.

Flow: interp convert_home (FEATURE_GCODE_HOMING + G_28) emits a new canon
op HOME_CYCLE_IF_UNHOMED() before the waypoint/return moves. Canon queues
EMC_JOINT_HOME carrying the EMC_HOME_ALL_IF_UNHOMED (-3) sentinel in its
'joint' field. Task resolves the sentinel at execution time: if all_homed()
the home is dropped (the queued return alone = pure legacy G28), otherwise
it issues the normal home-all (emcJointHome(-1)) - the same path G28.2 and
the GUI Home-All already use. No new command field, no motion / homing.c /
NML-layout change.

Single-channel; built on the G28.2/G28.3 home-cycle G-codes.

Verified at the interpreter (rs274 -i): GCODE_HOMING=1 plain G28 emits
HOME_CYCLE_IF_UNHOMED() before the return traverse; G30 does not home;
G28.2 still homes unconditionally; default-off G28 is bit-identical to
stock (no homing emitted). Full build (interp/task/motion) clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Sigma1912

Copy link
Copy Markdown
Contributor

Would it be possible to add a parameter for homing individual joints?

Example:

{G28.2, G28.3} P1 to home/ unhome joint 1

This would be very useful for configurations with joints that switch between rotary and spindle use. It would also circumvent #3556 that complicates the current way of 'rehoming'

@greatEndian

Copy link
Copy Markdown
Author

Home individual axis-

G28.2XC will force home axis X and C ...
G28.3YZ will force unhome axis YZ ...

for non trivial kinematics we need to start discussion how to handle it per joint..

@Sigma1912

Copy link
Copy Markdown
Contributor

What happens if we try to run a program in unhomed state ( will that trigger an interpreter error?):

Example 1 :

G28.3
G1 x2

How are these commands handled when used inside a gcode program? (I presume it is like a queue buster command that halts the read ahead until all queued command have been executed) :

Example 2 :

G1 x1
G28.3
G28.2
G1 x2

@greatEndian

greatEndian commented Jun 16, 2026

Copy link
Copy Markdown
Author

Example 1 :
force unhome
if NO_FORCE_HOMING==1 program run
else message to home again

Example 2 :
program run
force unhome
force home
program run

@Sigma1912

Copy link
Copy Markdown
Contributor

for non trivial kinematics we need to start discussion how to handle it per joint..

Using axes is probably enough for the majority of use cases and most machine operators would likely not even know about the joint-axis mappings on their particular machines.
However, it might be useful for more exotic configurations (eg for homing/unhoming extra joints that do not have axis letters assigned).
It seems an easy enough addition to introduce an optional parameter word with the joint number (but I might be wrong about that) ;)

@grandixximo

Copy link
Copy Markdown
Contributor

I think the cleanest direction is to keep the G-code surface dead simple and put the real effort into execution. For per-joint homing, Sigma's Pn is the right primitive: the joint index rides the field that EMC_JOINT_HOME already carries, so G28.2 P1 needs no NML or motion change and behaves the same on any kinematics. Bare G28.2 stays home-all. Gantries need no extra word since Pn reuses the existing single-joint homing behavior, but that's worth a doc note: on a synchronized (negative HOME_SEQUENCE) pair, homing either joint brings its partner, while on a positive shared sequence Pn homes only the named joint and you'd use bare G28.2 to do both. I'd leave the axis-letter form (G28.2 X) out for now, since resolving axis letters to joints needs the kinematics coordinates map and isn't actually trivial even on trivkins (duplicate letters for gantries), so it's better as its own later piece.

The part that really needs rethinking isn't the syntax, it's how a queued home executes. Homing only runs in free mode, and nothing currently flips the machine into free for a G-code-triggered home, so just relaxing the motion guard means the command can silently stall in teleop. It needs to be sequenced in task: drain motion, switch to free, home, wait for it to actually finish, then restore the prior mode. And if homing fails, the program has to abort rather than carry on unreferenced. A nice consequence is the trivkins question answers itself: home-all works everywhere, but a partial home can only resume coordinated motion on identity kinematics, because non-trivkins won't re-enter teleop until everything's homed.

There's also the unhome-then-keep-running hole Sigma raised. The existing force-homing check only fires at program start, so G28.3 mid-program followed by a move slips through. Rather than checking every move, we re-apply that same NO_FORCE_HOMING gate at the home/unhome sync point the command already forces, so it's governed by exactly the same policy as starting a program, with no cost on the motion path.

Given all that, I'd split it: land the explicit G28.2/G28.3 (with Pn) first, and hold GCODE_HOMING plain-G28, which adds the home-then-return move on top, until it's been signed off on real hardware.

greatEndian and others added 2 commits June 17, 2026 05:16
Document the G-code machine-homing feature and add interpreter-level
regression tests for it.

Docs:
- g-code.adoc: new "G28.2, G28.3 Home, Unhome from G-code" section, a
  GCODE_HOMING note in the G28 section, and a quick-reference entry.
- ini-config.adoc: GCODE_HOMING entry in the [RS274NGC] section.

Tests (tests/interp/gcode-homing, rs274 canon-level):
- homing-on: with [RS274NGC]GCODE_HOMING=1, a plain G28 emits
  HOME_CYCLE_IF_UNHOMED() before its return; G28.2 -> HOME_CYCLE(),
  G28.3 -> UNHOME_AXES().
- homing-off: default (flag off) a plain G28 emits no homing op, while
  G28.2/G28.3 still home/unhome (flag-independent).

Verified: new tests pass; tests/interp + tests/abort = 86/86 (1 skipped),
build clean.

Co-authored-by: Luca Toniolo <10792599+grandixximo@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants