devmaker.net
start/ 3d-printing/ klipper-corexy-two-y-endstops-homing
3D Printing

Klipper CoreXY: Two Y Endstops Fighting During Homing

Three layer shifts in ten days, each time by the same amount in X and Y – and a Klipper log without a single warning. The trail led from an over-tightened motor bearing to two Y endstops on the same belt that fought each other on every homing. This article shows how the direction of a CoreXY shift tells you which drive is at fault, how Klipper's step counters turn a noise into a measurement, and why the fix was to switch a feature off. For anyone running CoreXY with four XY motors.

Harry_im_Homelab31 (Portrait)
Harald
2026-10-17 · ~9 min read
Optical endstop on the HevORT – one of two that fought each other during homing
Optical endstop on the HevORT – one of two that fought each other during homing

A PETG part, a good hour of print time, and from 15.7 mm upwards everything is shifted – by the same amount in X and Y, towards X+ and Y−. The Klipper log is clean: no warning, no driver error. It was the third shift of this kind in ten days.

On a CoreXY, the direction tells you who is to blame. The two belt drives work along the diagonals: drive A moves the head along x + y, drive B along x − y. A shift that is equal in X and Y but opposite in sign lies exactly on the B diagonal. So drive B lost steps – and on my HevORT that drive consists of two motors on one belt: stepper_y rear left and stepper_y1 front right.

Rule of thumb: 45° = one belt

A CoreXY shift that is equal in X and Y comes from exactly one of the two drives. The sign tells you which: same signs → A, opposite signs → B.

The affected layer contained pure B moves at a belt speed of 424 mm/s. The printer itself delivered the mechanical finding: the Y1 motor wobbled, and the upper bearing cap above the pulley was tightened too hard. The bearing ran preloaded – and that friction rises with speed. The rear-left motor was loose as well. Both were taken apart and remounted; moved by hand, the head ran noticeably smoother afterwards.

The grinding during homing

The first homing after the rebuild produced a noise that should not exist: a hard grinding until the Y axis finally found its reference. The emergency stop ended it.

The explanation had been in the config since the original build: both B motors have their own endstop.

yaml
[stepper_y]              # hinten links
endstop_pin: ^PG10

[stepper_y1]             # vorn rechts, selber Riemen
endstop_pin: ^PG11

Klipper handles this as follows: both motors move towards the switches together, and each one stops as soon as its switch triggers. If both trigger at the same instant, that is harmless. If one triggers earlier, the other has to keep pulling alone – against a stationary motor connected to it by the same belt. That is the grinding.

The switches themselves were fine. A small script polled them every 50 ms while the gantry was pushed by hand – left, centre and right. Both triggered reliably, mostly simultaneously or a few millimetres apart. Only one thing stood out: stepper_y came first in every single run, even when pushing only on the right.

Ad · Affiliate link – if you buy through it, I may earn a commission. It doesn’t change the price for you.

Counting steps instead of listening

A noise cannot be compared, a number can. Klipper keeps a step counter for every motor, and GET_POSITION prints it:

bash
// mcu: stepper_x:-185186 stepper_x1:-185186 stepper_y:-70830 stepper_y1:-51360

This line comes from a run with both switches. That stepper_y and stepper_y1 are almost 20,000 steps apart is not a single event: the counters accumulate from power-on, and every homing with two switches added a few millimetres. What matters is therefore not the absolute value but the change from one homing to the next.

At 160 steps per millimetre that is a measuring instrument. The test script homes three times in a row, moves away in between and reads the counters each time. On one belt, stepper_y and stepper_y1 should always travel the same distance. If they don't, one has fought against the other.

StateHoming 2Homing 3Pattern
after the motor rebuild 15.1 mm 17.9 mm y1 drifts by 13 mm every time
belt retensioned 4.33 mm 4.37 mm y1 holds, y gives way
tensioned further 8.41 mm 5.14 mm y1 drifts again
tensioned once more 2.89 mm 8.61 mm no pattern anymore
Y1 endstop disabled 0.00 mm 0.00 mm repeatability 0.04 mm

Retensioning removed the big error, from 15 down to 4 mm. After that it did not get better, just random. Anyone looking at such a table keeps turning the tensioners – after all, it helped once. But the randomness is the actual signal: how far a stationary motor yields under tension depends on where it currently sits within its full step. You cannot tension that away.

What y − y1 actually is

A CoreXY with four motors has a degree of freedom that the kinematics do not describe. If the two motors of the same belt travel different distances, the head does not move – the gantry rotates. One end moves forward, the other backward.

Finding: y − y1 = racking

The difference between the two B counters is not step loss in the usual sense but the angular position of the X gantry. Two endstops that do not sit exactly where the gantry currently is will forcibly twist it on every homing – and the motors lose steps in the process.

This also makes clear what the two switches were meant for: they are supposed to square the gantry on every homing, as is common on machines with two Y lead screws. But that only works if both switches are mechanically adjusted exactly to the square gantry. Here they were millimetres off – so homing did not pull the gantry square but skewed, and a little differently every time.

In hindsight an old observation fits: the gantry once looked out of square with the Y rails, and back then re-tensioning one belt helped. That was no coincidence. Belt tension determines where the gantry settles as soon as the motors are de-energised.

One endstop less

The consequence is uncomfortable because it removes a feature: Y is now referenced by a single switch. stepper_y1 moves along but no longer stops on its own. Both motors therefore always stop at the same instant, and fighting each other is impossible.

yaml
[stepper_y1]
#endstop_pin: ^PG11   # 24.09.2026 abgeschaltet: zweiter Y-Schalter
#                     # kaempfte beim Homen gegen stepper_y (3-9 mm)

The switch stays connected; it can be re-enabled later once it is mechanically adjusted exactly to the square gantry. The test script afterwards: three homings, y and y1 on the same step every time, the full position reproducible to six steps – 0.04 mm.

The grinding was gone. What remained open was how skewed the gantry actually sits now – because nothing pulls it square anymore. That is the topic of the follow-up article.

What I got wrong

I suspected speed first

After the shift on 19 September I lowered the maximum velocity from 400 to 300 mm/s and ticked that off as the cause. Shift number three happened at 300. The direction of the shift – purely on the B diagonal – would have pointed to a single drive the very first time, not to a general speed limit.

I turned the wrong knob

Three rounds of belt tensioning, each with a new step count. After the first round the result did not get smaller, only different. That was the moment to question the principle instead of the setting. Instead, I kept tensioning.

I homed without expecting the grinding

The first homing after the motor rebuild ran with both switches, as always. That two switches on one belt can fight each other was already there in the config. The emergency stop ended it.

The turning point was a measurement that could disprove the existing explanation: the step counters showed no steady step loss but a random pattern.

Test setup and scope

Printer: HevORT CoreXY with four XY motors, Klipper v0.13.0-743, BTT Eddy, Phaetus Rapido 2, 0.4 mm nozzle, PETG. Slicer: OrcaSlicer 2.4.2.

All numbers come from measurements on this machine – from the step counters via the Moonraker API and the endstop log. They apply to this printer, not in general.

What comes next

Still open is the warm test of drive B: step counting on pure B moves with a hot bed should show whether the remounted motor no longer loses steps under load – only then will long prints run again. And the second endstop could come back, mechanically adjusted so that it triggers at the same time as the first one when the gantry is square. Then it would actually square the gantry instead of fighting it.

How skewed the gantry sits without the second switch and how Klipper compensates for it: Klipper skew_correction: Measuring and Correcting XY Skew.

// related posts

> echo "your thoughts" >> klipper-corexy-two-y-endstops-homing.responses

Post your comment

Required for comment verification