M114 to include manual stepper?

Basic Information:

Printer Model: Custom Desktop Pick and Place machine
MCU / Printerboard: Custom running RPI CM5
Host / SBC: Mainsaill
klippy.log

Fill out above information and in all cases attach your klippy.log file (use zip to compress it, if too big). Pasting your printer.cfg is not needed
Be sure to check our “Knowledge Base” Category first. Most relevant items, e.g. error messages, are covered there

Describe your issue:

I’m designing a pick and place machine and using OpenPNP to operate it. I have CoreXY AWD setup with a head that contains 2 nozzles. The head has 4x manual_steppers to operate 2x Z motors (to bring the nozzles up/down) and 2x rotation motors to rotate the nozzles.

Kevin kindly made a custom Klipper branch to support manual stepper to have extra limit switches so that I can use the nozzles to probe the PCBs to detect component heights, etc. (this makes setting up OpenPNP much easier)

In any case, I discovered that I was missing one detail. OpenPNP checks with M114 what the probed position is after the probe (nozzle) detects something. However, Klipper currently does not include manual steppers in its reporting with M114. How can I have it be included with a custom macro or otherwise? Attached the trace log that OpenPNP produces to show the issue.

2026-01-02 15:25:10.789 GcodeAsyncDriver$WriterThread TRACE: [Rapid Board:127.0.0.1:5000] >> M114

2026-01-02 15:25:13.997 GcodeDriver$ReaderThread TRACE: [Rapid Board:127.0.0.1:5000] << ok

2026-01-02 15:25:13.998 GcodeDriver TRACE: Position report: ok

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis x letter X missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis y letter Y missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis LeftZ letter A missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis RightZ letter C missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis LeftRot letter B missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver WARNING: Rapid Board: Axis RightRot letter D missing in POSITION_REPORT_REGEX groups.

2026-01-02 15:25:13.998 GcodeDriver$ReaderThread TRACE: [Rapid Board:127.0.0.1:5000] << X:488.500 Y:355.000 Z:0.000 E:0.000

2026-01-02 15:25:13.998 GcodeDriver TRACE: Rapid Board got lastReportedLocation ()

2026-01-02 15:25:13.998 GcodeDriver TRACE: Position report: X:488.500 Y:355.000 Z:0.000 E:0.000

2026-01-02 15:25:13.999 GcodeAsyncDriver TRACE: Rapid Board confirmation complete.

klippy-12.log (2.8 MB)

AFAIK,
You have to use gcode macro to reload the M114.

Then, it is pretty simple, if your manual steppers is additional axis:

[gcode_macro M114]
rename_existing: M114_ORIGINAL
gcode:
    {% set amap = printer.gcode_move.axis_map %}
    {% set tpos = printer.gcode_move.position %}
    RESPOND TYPE=command MSG='X:tpos[amap["X"]] Y:tpos[amap["Y"]]' # & etc

Hope that helps.

-Timofey,

Did I do something wrong? It just prints out in console:

X:tpos[amap[“X”]] Y:tpos[amap[“Y”]] Z:tpos[amap[“Z”]] A:tpos[amap[“A”]]

[gcode_macro M114]
rename_existing: M114.1
gcode:
{% set amap = printer.gcode_move.axis_map %}
{% set tpos = printer.gcode_move.position %}
RESPOND TYPE=command MSG=‘X:tpos[amap[“X”]] Y:tpos[amap[“Y”]] Z:tpos[amap[“Z”]] A:tpos[amap[“A”]]’ # & etc

I ran it through Gemini AI and it says it’s wrong

The syntax is broken: The line MSG='X:tpos...' would literally print the text “tpos” instead of the actual numbers.

Do you think that AI is smart enough?

BTW:

Please use the Preformatted text feature one the whole code block, not on every single line. We can’t see if leading spaces are missing.

Hey @EddyMI3D no one ever said anything. Lets not put words in people’s mouths :slight_smile: I am simply trying to exhaust my options before I post on the forum here and ask for help. Thanks for the feedback on the text formatting, I did try to fix it several times but for some reason it didn’t work.

I did copy his text exactly using the copy function in the forum and it simply printed out the line in the console. Are we use that Klipper is even tracking manual_stepper positions in the kinematics?

1 Like

Well, I just forgot {}
Normally it should be:

M118 X:{tpos[amap[“X”]]}

I guess, this way it can be easier.

As indicated above, it is possible to override M114 with a macro. Something like the following (totally untested):

[gcode_macro M114]
rename_existing: M114.99
gcode:
    {% set amap = printer.gcode_move.axis_map %}
    {% set gpos = printer.gcode_move.gcode_position %}
    RESPOND TYPE=command MSG='X:{gpos.x} Y:{gpos.y} Z:{gpos.z} A:{gpos[amap.A]} B:{gpos[amap.B]}'

Take a look at the config reference ( Configuration reference - Klipper documentation ), the command template reference ( Commands templates - Klipper documentation ), the status reference ( Status reference - Klipper documentation ), and the RESPOND command ( G-Codes - Klipper documentation ) for more details.

Note that you’ll need to add [respond] to your config for the above to work.

Note that the above only works if you also map the desired manual_stepper objects as an “extra axis”. To do this, you’ll want to run a MANUAL_STEPPER GCODE_AXIS=A ... type command - G-Codes - Klipper documentation .

Finally, note that the above should allow M114 to report where you’ve commanded the manual stepper to move to. However, when using the MANUAL_STEPPER ... MOVE=987 STOP_ON_ENDSTOP=1 type features the code sets the final position to the requested position (eg, 987) upon contact with the endstop - so I’m not sure you can use that support to “probe” a height. Achieving the above is possible, but would likely require additional code changes to the manual_stepper module.

Maybe that helps a little,
-Kevin

Hi @koconnor so I tried the above and got this error:

Error evaluating 'gcode_macro M114:gcode': jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'axis_map'

Here is my macros.cfg setup:

#--------------------------------------------------------------------

CUSTOM M114 - Report all Manual Steppers as A/B/C/D

#--------------------------------------------------------------------

[respond]
default_type: echo

default_prefix: echo:

[gcode_macro M114]
rename_existing: M114.99
gcode:
{% set amap = printer.gcode_move.axis_map %}
{% set gpos = printer.gcode_move.gcode_position %}
RESPOND TYPE=command MSG=‘X:{gpos.x} Y:{gpos.y} Z:{gpos.z} A:{gpos[amap.A]} B:{gpos[amap.B]} C:{gpos[amap.C]} D:{gpos[amap.D]}’

I also have this in macros.cfg at the top. Shouldn’t it register the axis? They do move with those letters when commanded to.



[delayed_gcode register_stepper]
initial_duration: 0.1
gcode:
  MANUAL_STEPPER STEPPER=left_z GCODE_AXIS=A
  MANUAL_STEPPER STEPPER=left_rotation GCODE_AXIS=B
  MANUAL_STEPPER STEPPER=right_z GCODE_AXIS=C
  MANUAL_STEPPER STEPPER=right_rotation GCODE_AXIS=D

Also not sure why formatting the code/text isn’t working on Safari as expected, so apologies for that.

I think in that case this wouldn’t help either way then without this feature. I basically need the nozzle to act as a probe and report back the position it detected something. Is it possible to implement this?

Ah, it looks like you have an older version of the code. I updated the branch at GitHub - KevinOConnor/klipper-dev at work-ms-endstops-20251030 - see if you can pull the code from there again and retry.

It’s possible to implement that. I’d suggest getting your M114 macro to work and make sure it is interacting properly with your other software first.

Cheers,
-Kevin

Thanks @koconnor we seem to be getting somewhere now but it’s not showing any numbers and my X axis is homed to some weird decimal point for some reason.

3:36 PM X:488.51250000000005 Y:355.0 Z:0.0 A: B: C: D:

3:36 PM M114

3:35 PM G28

3:35 PM X:488.51250000000005 Y:355.0 Z:0.0 A: B: C: D:

3:35 PM M114

3:35 PM G28

Attached is my latest Klippy log

klippy-13.log (2.9 MB)

I will also add that I’m still having the issue with the nozzles not stopping in time when the switch is detected. I can confirm that when I set up a button and press the nozzle, it prints it in console immediately. But when doing moves with stop on endstop, it gets over-compressed.

As I mentioned in the original thread for this feature, I was pretty sure it was working at one point without issues. My only guess is that perhaps I changed something in the config and it broke it, but I don’t see what could’ve caused that. My only theory is that maybe because I increased my tiny z motors current, it has some effect on the power draw, but even then I only increased it by like 0.10 amps.

Anyway, we can figure out this issue afterwards. One problem at a time :wink:

Take a look through the documents I linked above. It should be possible to figure out where the error is. (It’s not easy to debug these things remotely, without the actual hardware.)

Also, make sure you’ve enabled the given manual_stepper objects via the GCODE_AXIS mechanism - the M114 code above definitely wont work if the motor isn’t mapped to its corresponding axis.

I answered this as best as I could at How to create multiple probes for multiple manual_steppers? - #24 by koconnor . You seem to be asking the question, “why does the Klipper software delay its response to the driver’s stall signal?” and the answer is, “it doesn’t”. The question I think you should be asking is, “why is the tmc driver only producing a stall signal after a delay?”. I’d say best would be to investigate different tmc driver configuration settings; it may also be possible that the tmc driver isn’t suitable for detecting stalls in that situation.

Maybe that helps a little,
-Kevin

Ok I’ll try my best to continue troubleshooting, but I do want to clarify again that I am NOT using the TMC2209 to detect the stall. I have an optical limit switch that detects when the nozzle’s spring gets compressed. It’s a pretty clean switch with a good pull up/down resistor setup and I tested it by creating a button setup in Klipper’s config where I pressed it manually and it triggers instantly by printing out the result in the console.

I only use the TMC2209 sensorless homing when homing upwards (endstop_pin). Downwards I use the optical switch (endstop1_pin) to probe.

Please attach the klippy.log to your next post.

Ah, okay. I misunderstood. I guess it is one of the challenges of not having the hardware on hand.

I don’t know why there would be a delay like that. You could try different homing speeds to see if it changes the behavior. You may also want to check if there is a large capacitor on the signal line for the optical sensor - we’ve seen in the past some delays can occur if it takes some time to alter the signal. I’m just guessing though.

I did notice I had an error in my original post (gcode_position is not exported for extra axes). You could try something like this instead:

[gcode_macro M114]
rename_existing: M114.99
gcode:
  {% set amap = printer.gcode_move.axis_map %}
  {% set pos = printer.gcode_move.position %}
  {% set gpos = printer.gcode_move.gcode_position %}
  RESPOND TYPE=command MSG=‘X:{gpos.x} Y:{gpos.y} Z:{gpos.z} A:{pos[amap.A]} B:{pos[amap.B]} C:{pos[amap.C]} D:{pos[amap.D]}’

Maybe that helps a little,
-Kevin

SUCCESS!!!

Small note, I had to fix the macro and change to quotes (Gemini helped figure out why it was malformed command :wink: )

[gcode_macro M114]
rename_existing: M114.99
gcode:
{% set amap = printer.gcode_move.axis_map %}
{% set pos = printer.gcode_move.position %}
{% set gpos = printer.gcode_move.gcode_position %}

Use standard straight quotes below:

RESPOND TYPE=command MSG=“X:{gpos.x} Y:{gpos.y} Z:{gpos.z} A:{pos[amap.A]|default(0.0)} B:{pos[amap.B]|default(0.0)} C:{pos[amap.C]|default(0.0)} D:{pos[amap.D]|default(0.0)}”

Also, as you mentioned, the STOP_ON_ENDSTOP doesn’t work with G1 command and it just goes to 0.0. Even if I touch the probe, it ignores it and moves all the way (no delay even, just straight up ignoring it, attached Klippy log)

10:46 AM X:0.0 Y:0.0 Z:0.0 A:0.0 B:0.0 C:21.9 D:0.0

10:46 AM m114

10:46 AM g1 A0 STOP_ON_ENDSTOP=1

10:46 AM g28a

In regards to the probe issue - since you had noticed yesterday that the wrong code version was on GitHub, is it possible that the original version I tested had something different that made it work? As I mentioned before, I am pretty sure it worked the first time as expected, but maybe my memory is just bad. I came back from a two week vacation and re-installed the firmware and that’s when I think it stopped working and started over-traveling. So just wondering if it’s possible to do some sort of code comparison with the version you had on there before November 16, 2025?

Also thanks for your continued help, just chipped in a bit on Ko-fi :wink:

klippy-14.log (537.0 KB)

Also, probing down to endstop1_pin at slower speeds does help - but doesn’t solve it completely. It compresses the nozzle less, but it’s painfully slow.

In regards to your capacitor theory, yes I would agree. I need to check the schematic if I have one on the signal line but if it works registering in console with [gcode_button] very fast, I would imagine it shouldn’t be an issue when probing?

Ah, STOP_ON_ENDSTOP is not a valid parameter of G1 - that’s only valid on the MANUAL_STEPPER command. So, any time you need to home one of these steppers you would need to use a macro that does something like:

# Unmap stepper from regular G1 commands
MANUAL_STEPPER STEPPER=xxx GCODE_AXIS=
# Home stepper
MANUAL_STEPPER STEPPER=xxx MOVE=0 STOP_ON_ENDSTOP=1
# Remap stepper to regular G1 commands
MANUAL_STEPPER STEPPER=xxx GCODE_AXIS=A

I’m not aware of anything in the software that could delay a trigger signal like that. At a low-level, during a homing/probing event, the host software instructs the mcu to periodically check the trigger pin and to stop stepper movement upon a change to that pin (ie, the voltage level changes on one of the wires going to the mcu). The same code is used for homing/probing on basically all axes on all printers.

As a suggestion, you might want to try gathering more data on the issue. For example, if you have access to an oscilloscope you could try monitoring the pin connected to the optical sensor along with the pin connected to the stepper “step” signal. That may help give some more concrete information like how long is the delay, and is the delay in the mcu or the sensor.

Cheers,
-Kevin

Thanks Kevin for the oscilloscope idea, I agree that would be a good way to determine if the signal is lagging or not. I’ll work to set that up and post the results.

Regarding STOP_ON_ENDSTOP=1 not working with G1, yeah I figured that to be the case but tried it anyway.

Would you have bandwidth to update the code so that M114 doesn’t print 0 when it probes on the endstop? Otherwise the M114 would always be wrong :frowning: Appreciate all the help you’re providing to make this work!