From cf8283818be06821edbe0b4838863439e7ad92da Mon Sep 17 00:00:00 2001 From: Zia-research <309084419+Zia-research@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:23:30 +0200 Subject: [PATCH 1/5] docs: initf example funct is lcec.activate, not lcec.0.activate The linuxcnc-ethercat driver exports its activation funct globally: rtapi_snprintf(name, HAL_NAME_LEN, %s.activate, LCEC_MODULE_NAME); (src/lcec_main.c:408 in linuxcnc-ethercat at 87a72a8). Only the cyclic read/write functs are per-master (lcec..read / .write). A user copying the manual's example into a .hal file gets a funct that does not exist. Co-Authored-By: Claude Opus 5 --- docs/src/hal/basic-hal.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/hal/basic-hal.adoc b/docs/src/hal/basic-hal.adoc index b23a0f465a8..5cf2078d30b 100644 --- a/docs/src/hal/basic-hal.adoc +++ b/docs/src/hal/basic-hal.adoc @@ -97,7 +97,7 @@ addf mux4.0 servo-thread (((HAL initf,initf))) The `initf` command registers a function to run once in realtime context, on a dedicated init cycle of the thread before the cyclic function list runs. -It is the realtime-thread analogue of `addf`, intended for one-shot setup that must execute in the realtime task (for example EtherCAT master activation via `lcec.0.activate`). +It is the realtime-thread analogue of `addf`, intended for one-shot setup that must execute in the realtime task (for example EtherCAT master activation via `lcec.activate`). `initf` adds function _functname_ to the init list of thread _threadname_. The init list runs once on the first cycle after `start`, then is drained. @@ -109,7 +109,7 @@ Once the init cycle has run, further `initf` calls on that thread are rejected. [source,{hal}] ---- initf -initf lcec.0.activate servo-thread +initf lcec.activate servo-thread ---- [[sub:hal-loadusr]] From 2dd96e3a9cf026e5eae8696eeabe112a6618c302 Mon Sep 17 00:00:00 2001 From: Zia-research <309084419+Zia-research@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:23:31 +0200 Subject: [PATCH 2/5] docs: code-notes - correct stale and self-contradictory statements Seven corrections, each verified against the current source: - Buffer line types: only SHMEM, LOCMEM and PHANTOM are implemented. FILEMEM and GLOBMEM are recognised nowhere in src/libnml/; RTLMEM is rejected explicitly by cms_config(). (cms_cfg.cc:729,819,844,849) - OVERRIDE_LIMITS: the "currently broken" note described a bug fixed long ago. Limits are automatically re-enabled at the end of the next jog, and only tripped limits are overridden, via a mask built from the joints' hard-limit flags. (command.c:702-730) - The 2020 command-count note: figures updated 73/70 -> 76/73 (the same three commands still lack handlers). - PAUSE: documented the actual semantics - deceleration to a stop within the current segment at that segment's acceleration limit, and the exception that pause is bypassed during position-synchronized (threading / rigid tapping) segments. (tp.c:243-252,2782-2787) The run-down is jerk-limited only where the S-curve planner is selected: [TRAJ]PLANNER_TYPE defaults to 0 = trapezoidal (emccfg.h:57, initraj.cc:157) and is forced back to 0 when jerk is below 1.0 (initraj.cc:159-162, inihal.cc:302,320), while MAX_JERK itself defaults to 0 (emccfg.h:51,70,87). tp.c enters the S-curve branch only for type 1 (tp.c:3660,3664), so a stock machine never plans one. Velocity-synchronized segments are the G95 feed-per-revolution case, not G96. G_95 enqueues SET_FEED_MODE(spindle, 1) (interp_convert.cc:2841-2846); SET_FEED_MODE is the only writer of canon.feed_mode (emccanon.cc:521), and every call that passes velocity_mode = 1 to START_SPEED_FEED_SYNCH is guarded by it (emccanon.cc:530 and the feed-mode restore sites), which yields TC_SYNC_VELOCITY (tp.c:4159-4162). The interpreter's own calls for G33, G33.1 and G76 all pass 0 (interp_convert.cc:5505,5520, 5644-5662), and G96 goes through SET_SPINDLE_MODE instead (interp_convert.cc:5075-5087). Thanks to grandixximo for three catches in this one paragraph: the first wording implied jerk limiting always applies; its replacement gave the threshold as "non-zero" when a jerk below 1.0 forces the planner back to trapezoidal; and the velocity-synchronized sentence said G96. - ENABLE / STEP Requirements: neither is "always accepted". ENABLE is rejected while the motion.enable HAL pin is low (command.c:1366); STEP is rejected unless the planner is already paused (command.c:1261). - EMCIO chapter: removed the leftover "iocontrol main loop process" wording that contradicted the chapter's own first line; noted the iocontrol.0 / [EMCIO] names are kept for compatibility. - Reckoning of joints and axes: the initraj.cc:loadTraj() example pointed at code that has since been fixed to handle all nine axes. Co-Authored-By: Claude Opus 5 --- docs/src/code/code-notes.adoc | 89 +++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/docs/src/code/code-notes.adoc b/docs/src/code/code-notes.adoc index 35ef4fd93bf..f08571f2931 100644 --- a/docs/src/code/code-notes.adoc +++ b/docs/src/code/code-notes.adoc @@ -366,8 +366,10 @@ The ENABLE command enables the motion controller. ==== Requirements -None. The command can be issued at any time, and will always be -accepted. +The command is rejected with an error message ("can't enable motion, +enable input is false") if the motion.enable HAL pin is low. The +hardware enable chain must be satisfied before the controller can be +enabled. ==== Results @@ -464,10 +466,26 @@ modify the joint's amp enable pin. === PAUSE -The PAUSE command stops the trajectory planner. It has no effect in -free or teleop mode. At this point I don't know if it pauses all motion -immediately, or if it completes the current move and then pauses before -pulling another move from the queue. +The PAUSE command pauses the trajectory planner. It has no effect in +free or teleop mode. + +The machine neither stops instantly nor completes the current move: the +planner treats pause as a request to bring the feed to zero, so the +machine decelerates to a stop within the current segment, at that +segment's acceleration limit, and halts wherever the deceleration ramp +ends. The run-down is jerk-limited only on machines configured for the +S-curve planner ([TRAJ]PLANNER_TYPE = 1 and MAX_JERK at 1.0 or above; a +smaller jerk forces the planner back to trapezoidal). Both default to off, +so a stock machine decelerates on a trapezoidal profile. + +Pause is ignored while the active segment is position-synchronized with +the spindle (spindle-synchronized motion such as G33 threading and +rigid tapping): for those segments the planner forces full feed and +bypasses both pause and feed override, because tool position is slaved +to spindle angle and pausing mid-thread would destroy the work. +The pause takes effect when the synchronized segment ends. +Velocity-synchronized segments (G95 feed per revolution) can be paused +normally. ==== Requirements @@ -476,7 +494,10 @@ accepted. ==== Results -The trajectory planner pauses. +The planner's pausing flag is set. Motion ramps down to zero velocity +within the current segment (except during position-synchronized +segments, see above) and the planner stops pulling new segments from +the queue until RESUME or STEP. === RESUME @@ -495,15 +516,15 @@ The trajectory planner resumes. === STEP The STEP command restarts the trajectory planner if it is paused, and -tells the planner to stop again when it reaches a specific point. It -has no effect in free or teleop mode. At this point I don't know -exactly how this works. I'll add more documentation here when I dig -deeper into the trajectory planner. +tells the planner to pause again when the executing motion id changes +(i.e. after the current line of the program completes). It has no +effect in free or teleop mode. ==== Requirements -None. The command can be issued at any time, and will always be -accepted. +The planner must already be paused. If motion is executing, the +command is rejected with an error message ("can't STEP while already +executing"). ==== Results @@ -541,10 +562,12 @@ accepted. (I think it should only work in free mode.) ==== Results -Limits on all joints are over-ridden until the end of the next JOG -command. (This is currently broken... once an OVERRIDE_LIMITS command -is received, limits are ignored until another OVERRIDE_LIMITS command -re-enables them.) +Limits that are currently tripped are over-ridden until the end of the +next jog command, at which point they are automatically re-enabled. +Only the tripped limits are over-ridden: the command handler builds a +mask from each joint's negative/positive hard-limit flags, so joints +that are not on a limit keep their protection. Issuing the command +with a negative joint number cancels an override explicitly. === HOME @@ -718,10 +741,14 @@ iocontrol. These are relatively low speed events, high speed coordinated I/O is emctaskmain.cc sends I/O commands via taskclass.cc. -iocontrol main loop process: +Historically this was a separate process named iocontrol, talking to +task over its own NML channels; the HAL component is still named +iocontrol.0 (and the INI section [EMCIO]) for compatibility, but the +code now runs inside the task process. On each pass through the task +loop it: -- checks to see it HAL inputs have changed -- checks if read_tool_inputs() indicates the tool change is finished and set emcioStatus.status +- checks whether the iocontrol HAL input pins have changed +- checks if read_tool_inputs() indicates the tool change is finished and sets emcioStatus.status == User Interfaces @@ -844,7 +871,10 @@ The original NIST format of the buffer line is: * 'B name type host size neut RPC# buffer# max_procs key [type specific configs]' * 'B' - identifies this line as a Buffer configuration. * 'name' - is the identifier of the buffer. -* 'type' - describes the buffer type - SHMEM, LOCMEM, FILEMEM, PHANTOM, or GLOBMEM. +* 'type' - describes the buffer type - SHMEM, LOCMEM, or PHANTOM. + (Older RCS documents also list FILEMEM, GLOBMEM and RTLMEM; none of these is + implemented in src/libnml/ - cms_config() rejects RTLMEM explicitly and does + not recognise the other two at all.) * 'host' - is either an IP address or host name for the NML server * 'size' - is the size of the buffer * 'neut' - a boolean to indicate if the data in the buffer is encoded in a @@ -902,12 +932,10 @@ The original NIST format of the process line is: === Configuration Comments Some of the configuration combinations are invalid, whilst others -imply certain constraints. On a Linux system, GLOBMEM is obsolete, -whilst PHANTOM is only really useful in the testing stage of an -application, likewise for FILEMEM. LOCMEM is of little use for a -multi-process application, and only offers limited performance -advantages over SHMEM. This leaves SHMEM as the only buffer type to use -with LinuxCNC. +imply certain constraints. PHANTOM is only really useful in the testing +stage of an application. LOCMEM is of little use for a multi-process +application, and only offers limited performance advantages over SHMEM. +This leaves SHMEM as the only buffer type to use with LinuxCNC. The neut option is only of use in a multi-processor system where different (and incompatible) architectures are sharing a block of @@ -1344,9 +1372,10 @@ FIXME: `axis_mask` and `axes` overspecify the number of axes An array of `EMCMOT_MAX_JOINTS` joint structures. `joint[0]` through `joint[joints-1]` are valid, the others do not exist on this machine and must be ignored. -Things are not this way currently in the joints-axes branch, but deviations from this design are considered bugs. -For an example of such a bug, see the treatment of axes in src/emc/ini/initraj.cc:loadTraj(). -There are undoubtedly more, and I need your help to find them and fix them. +Deviations from this design are considered bugs; if you find one, +please report it. (An earlier revision of this document pointed at the +treatment of axes in src/emc/ini/initraj.cc:loadTraj() as an example; +that code has since been fixed to handle all nine axes.) === In Motion From 7fa8e6dfda3b257ee4c307799fda110f564c597e Mon Sep 17 00:00:00 2001 From: Zia-research <309084419+Zia-research@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:23:31 +0200 Subject: [PATCH 3/5] docs: g-code - align G33/G33.1, G64 and G96 error lists with the interpreter Each change verified against src/emc/rs274ngc at the current source: - G33 and G33.1: the error lists claimed "The requested linear motion exceeds machine velocity limits due to the spindle speed". No such check exists anywhere - the interpreter's only checks are axis word present, K present, F absent, valid $, and spindle commanded turning (interp_check.cc:375-378, interp_convert.cc:5496-5529). Replaced the phantom error with the two real ones that were missing (K word required, F word forbidden), and stated that "spindle not turning" tests the commanded state (M3/M4), which is what settings->spindle_turning holds. - G33.1: documented that an I word below 1 is treated as 1 (interp_convert.cc:5522-5527). - G64: documented that changing path control mode is an error while cutter radius compensation is active ("Cannot change control mode with cutter radius compensation on", interp_convert.cc:2221). - G96: removed "A feed move is specified in G96 mode while the spindle is not turning" from the error list. No such check exists in the interpreter, task or motion; the spindle-not-turning checks apply only to G33/G33.1/G76 and the tapping cycles. Documented that without D the interpreter applies no RPM limit in CSS mode (SET_SPINDLE_MODE is called with 1e30, interp_convert.cc:5087). Co-Authored-By: Claude Opus 5 --- docs/src/gcode/g-code.adoc | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/src/gcode/g-code.adoc b/docs/src/gcode/g-code.adoc index 4670688041d..e070e0a2548 100644 --- a/docs/src/gcode/g-code.adoc +++ b/docs/src/gcode/g-code.adoc @@ -1109,9 +1109,10 @@ M2 (end program) It is an error if: * All axis words are omitted. -* The spindle is not turning when this command is executed. -* The requested linear motion exceeds machine velocity limits - due to the spindle speed. +* No K word is given. +* An F word is given (the feed follows from K and the spindle speed). +* The selected spindle is not commanded to turn (M3 or M4 active) when + this command is executed. [[gcode:g33.1]] == G33.1 Rigid Tapping(((G33.1 Rigid Tapping))) @@ -1123,6 +1124,7 @@ G33.1 X- Y- Z- K- I- $- * 'K' - distance per revolution * 'I' - optional spindle speed multiplier for faster return move + (values less than 1 are treated as 1) * '$' - optional spindle selector [WARNING] @@ -1175,9 +1177,10 @@ M2 (end program) It is an error if: * All axis words are omitted. -* The spindle is not turning when this command is executed -* The requested linear motion exceeds machine velocity limits - due to the spindle speed +* No K word is given. +* An F word is given (the feed follows from K and the spindle speed). +* The selected spindle is not commanded to turn (M3 or M4 active) when + this command is executed. [[gcode:g38]] == G38._n_ Straight Probe(((G38.n Probe))) @@ -1719,6 +1722,11 @@ Behavior details: It is a good idea to include a path control specification in the preamble of each G-code file. +It is an error if: + +* The path control mode is changed (G61, G61.1 or G64) while cutter + radius compensation is active. + .G64 P- Q- Example Line [source,ngc] ---- @@ -2762,7 +2770,8 @@ G96 S- <$-> (Constant Surface Speed Mode) G97 S- <$-> (RPM Mode) ---- -. 'D' - maximum rotation speed (RPM), optional +. 'D' - maximum rotation speed (RPM), optional. Without D the spindle + speed in CSS mode is not limited by the interpreter. . 'S' - spindle speed . '$' - the spindle of which the speed will be varied, optional. @@ -2789,7 +2798,6 @@ G96 D2500 S250 (set CSS with a max rpm of 2500 and a surface speed of 250) It is an error if: * S is not specified with G96 -* A feed move is specified in G96 mode while the spindle is not turning [[gcode:g98-g99]] == G98, G99 Canned Cycle Return Level(((G98, G99 Canned Cycle Return))) From 4452c5190b4b216231576102d3ea7914f615012a Mon Sep 17 00:00:00 2001 From: Zia-research <309084419+Zia-research@users.noreply.github.com> Date: Sun, 9 Aug 2026 19:26:36 +0200 Subject: [PATCH 4/5] docs: apply the review on #4349 Six of the eight review comments, applied. - the command note now names ENABLE_WATCHDOG, DISABLE_WATCHDOG and SET_TELEOP_VECTOR and carries no counts and no date, so it cannot go stale the way 73/70 and 6/5/2020 did - the ENABLE requirement no longer claims a hardware enable chain must be satisfied; motion.enable defaults to TRUE and an unconnected machine is never refused - PAUSE cut to the two sentences the behaviour needs plus the synchronized-motion exception; the jerk sentence goes with it, which also removes the duplicate of ini-config.adoc - the two parentheticals that had the document tracking its own history are gone, and the only British spelling in the file went with the first of them - the velocity-limit line comes back to g-code.adoc as a NOTE in both the G33 and the G33.1 lists, worded to say only what has been read: there is no interpreter check and no error message for that case Co-Authored-By: Claude Opus 5 --- docs/src/code/code-notes.adoc | 32 +++++++++----------------------- docs/src/gcode/g-code.adoc | 10 ++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/docs/src/code/code-notes.adoc b/docs/src/code/code-notes.adoc index f08571f2931..3874fd37002 100644 --- a/docs/src/code/code-notes.adoc +++ b/docs/src/code/code-notes.adoc @@ -367,9 +367,9 @@ The ENABLE command enables the motion controller. ==== Requirements The command is rejected with an error message ("can't enable motion, -enable input is false") if the motion.enable HAL pin is low. The -hardware enable chain must be satisfied before the controller can be -enabled. +enable input is false") if the motion.enable HAL pin is low. That pin is +created with a default value of TRUE, so a machine that leaves it +unconnected is never refused. ==== Results @@ -470,22 +470,13 @@ The PAUSE command pauses the trajectory planner. It has no effect in free or teleop mode. The machine neither stops instantly nor completes the current move: the -planner treats pause as a request to bring the feed to zero, so the -machine decelerates to a stop within the current segment, at that -segment's acceleration limit, and halts wherever the deceleration ramp -ends. The run-down is jerk-limited only on machines configured for the -S-curve planner ([TRAJ]PLANNER_TYPE = 1 and MAX_JERK at 1.0 or above; a -smaller jerk forces the planner back to trapezoidal). Both default to off, -so a stock machine decelerates on a trapezoidal profile. +planner brings the feed to zero within the current segment, at that +segment's acceleration limit, and halts wherever the ramp ends. Pause is ignored while the active segment is position-synchronized with -the spindle (spindle-synchronized motion such as G33 threading and -rigid tapping): for those segments the planner forces full feed and -bypasses both pause and feed override, because tool position is slaved -to spindle angle and pausing mid-thread would destroy the work. -The pause takes effect when the synchronized segment ends. -Velocity-synchronized segments (G95 feed per revolution) can be paused -normally. +the spindle (G33 threading, rigid tapping): there the planner forces full +feed and bypasses both pause and feed override, and the pause takes effect only when the segment ends. Velocity-synchronized segments (G95 feed per revolution) can be +paused normally. ==== Requirements @@ -872,9 +863,6 @@ The original NIST format of the buffer line is: * 'B' - identifies this line as a Buffer configuration. * 'name' - is the identifier of the buffer. * 'type' - describes the buffer type - SHMEM, LOCMEM, or PHANTOM. - (Older RCS documents also list FILEMEM, GLOBMEM and RTLMEM; none of these is - implemented in src/libnml/ - cms_config() rejects RTLMEM explicitly and does - not recognise the other two at all.) * 'host' - is either an IP address or host name for the NML server * 'size' - is the size of the buffer * 'neut' - a boolean to indicate if the data in the buffer is encoded in a @@ -1373,9 +1361,7 @@ FIXME: `axis_mask` and `axes` overspecify the number of axes `joint[0]` through `joint[joints-1]` are valid, the others do not exist on this machine and must be ignored. Deviations from this design are considered bugs; if you find one, -please report it. (An earlier revision of this document pointed at the -treatment of axes in src/emc/ini/initraj.cc:loadTraj() as an example; -that code has since been fixed to handle all nine axes.) +please report it. === In Motion diff --git a/docs/src/gcode/g-code.adoc b/docs/src/gcode/g-code.adoc index e070e0a2548..6253e57afb2 100644 --- a/docs/src/gcode/g-code.adoc +++ b/docs/src/gcode/g-code.adoc @@ -1114,6 +1114,11 @@ It is an error if: * The selected spindle is not commanded to turn (M3 or M4 active) when this command is executed. +[NOTE] +The pitch and the spindle speed together set the axis feed. If they ask for +more than the machine can deliver, the program is not rejected: there is no +interpreter check and no error message for that case. + [[gcode:g33.1]] == G33.1 Rigid Tapping(((G33.1 Rigid Tapping))) @@ -1182,6 +1187,11 @@ It is an error if: * The selected spindle is not commanded to turn (M3 or M4 active) when this command is executed. +[NOTE] +The pitch and the spindle speed together set the axis feed. If they ask for +more than the machine can deliver, the program is not rejected: there is no +interpreter check and no error message for that case. + [[gcode:g38]] == G38._n_ Straight Probe(((G38.n Probe))) From c951b47eea755adc92d3e8c849786250702fbc7d Mon Sep 17 00:00:00 2001 From: Zia-research <309084419+Zia-research@users.noreply.github.com> Date: Sun, 9 Aug 2026 19:48:35 +0200 Subject: [PATCH 5/5] docs: ini-config - the S-curve jerk threshold is 1.0, not non-zero The PLANNER_TYPE entry said S-curve planning is active when PLANNER_TYPE = 1 AND MAX_LINEAR_JERK > 0. The threshold is 1.0: both routes that set the planner type force it back to trapezoidal below that, inihal.cc:302 and :320 on the HAL side and initraj.cc:159 on the INI side, the latter under its own "FIXME: Should write a warning message to the user". So a configuration with PLANNER_TYPE = 1 and MAX_LINEAR_JERK = 0.5 satisfies the documented condition and does not get S-curve planning, without being told. Co-Authored-By: Claude Opus 5 --- docs/src/config/ini-config.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/config/ini-config.adoc b/docs/src/config/ini-config.adoc index 9753c69c5df..9f932eb01cb 100644 --- a/docs/src/config/ini-config.adoc +++ b/docs/src/config/ini-config.adoc @@ -946,7 +946,8 @@ Finally, no amount of tweaking will speed up a tool path with lots of small, tig The value shown equals 300 units per minute. * `MAX_LINEAR_ACCELERATION = 20.0` - (((MAX ACCELERATION))) The maximum acceleration for any axis or coordinated axis move, in 'machine units' per second per second. * `PLANNER_TYPE = 0` - (((PLANNER TYPE))) Selects the trajectory planner type: 0 = trapezoidal (default), 1 = S-curve with jerk limiting. - S-curve planning is only active when `PLANNER_TYPE = 1` AND `MAX_LINEAR_JERK > 0`. + S-curve planning is only active when `PLANNER_TYPE = 1` AND `MAX_LINEAR_JERK` is 1.0 + or above; a smaller value falls back to trapezoidal, with an operator message. The planner can be changed at runtime via the `ini.traj-planner-type` HAL pin or the <>; a switch requested while the machine is moving is deferred and applied when motion next becomes idle (never aborts), with an operator message.