TinyG skipped a job's last short move and reported it done

If a TinyG job ended with a very short move made at speed, the board could skip that move and still report the tool at the programmed endpoint. The same happened just before a dwell, an M5, a G92 or a probe. The error is at most the feed rate times 2.5 milliseconds: about 0.03 mm at F800, 0.1 mm at F2400, 0.6 mm at a 15 m/min rapid. Every TinyG build has it, including master 440.21 and the 450.03 release. It is fixed on edge-next in commit a3b4e6b3 (issue #301).

How we found it

One of our test benches has a logic analyzer on every step and direction line. It runs real CAM files and generated G-code around the clock, counts every step pulse the board sends, and compares the count with the position the board reports.

One morning a rotary file failed that check. Over a 176-line stretch, where each line moved X, Y and Z by tens of microns and the rotary axis by up to two degrees, the Z motor ended 20 steps (16 microns) away from where the board said it was. A rerun gave exactly the same numbers.

The first suspect was the measurement. But every direction change came at least 66 microseconds before the next step pulse, which is plenty for both the analyzer and a stepper driver. Then we read the firmware's own step counter with a debugger. It agreed with the motors, not with the report. The motors had stopped at the end of the second-to-last line. The board reported the last line as done.

Animation: a carriage makes two 1 mm moves, then the board reports a further 0.017 mm that the carriage never makes, leaving 21 steps unsent; with the fix the carriage covers the last 0.017 mm.
Two 1 mm moves, then a 0.0167 mm move at speed with nothing after it. Before the fix, the position readout moves on and the carriage does not. With the fix, all the steps are sent.

Why the planner skipped the move

TinyG's planner has a minimum block time of 2.5 ms. A move it estimates will take less than that is refused, because a block that short cannot be planned properly. That refusal is meant to be harmless: the next move starts from the last planned position, so it covers the skipped distance as part of its own path. On a dense CAM file this happens all the time and nobody notices.

The trouble is the case with no next move. At the end of a job, or right before a dwell, a spindle stop, a G92 or a probe, the skipped distance is never covered. Meanwhile the board's G-code model has already moved on to the endpoint, the line was answered ok, and at rest the board reports the model's position. So every host showed the tool where the program said it should be.

Chart of Z position over time: the reported position ends at 2.017 mm while the motors stop at 2.000 mm; below, the planner refuses the 1.25 ms final move.
The bench measurement before the fix. The final move would take 1.25 ms at the speed the machine was already moving, so it was refused and replied ok.

Why it went unnoticed for years

It matters more than the size suggests in two cases. A G92 or G10 L20 issued at that point sets the work offset from a position the machine never reached, and every later job inherits the error. And a laser or plotter job made of many tiny segments at high speed is exactly where a final short move happens.

The fix

The planner still folds a refused move into the next one while there are more moves queued, so dense files run as fast as before. What changed is that the firmware now remembers a refused move, and runs it at the minimum block time when nothing else will cover it:

On the bench, the same sequence sent 2560 steps to the motor before the fix and 2581 after, where 2.0167 mm at 1280 steps/mm is 2581.4. Both cases are now regression tests that run on a real board before every merge.

Try it

The fix is merged into edge-next after the 450.03 release, in commit a3b4e6b3 (the fix itself is 456b4516). Update to the latest edge-next build to get it. See our edge-next post for what else changed and how to upgrade, and issue #301 for the details and the test.

However good the firmware gets, software is not an emergency stop. Every machine needs a hardware E-stop that cuts motor power, within reach of the operator.