@koconnor - (bow) it took me a while to consider Klipper, to be honest, as I avoided to add another complexity and point of failure with an SBC; so it took me a while to arrive to consider Klipper - I’m deeply impressed of the work and clarity you put into; it shows, and the adoption shows; it brings value to otherwise “dumb” MCUs I just grew tired to recompile so often to change and test small things, so I was mainly using RRF for advanced stuff, and now thought to give Klipper a try to do something more sophisticated as well.
I will dive into those PR to see if it helps - I might take me a while to understand the full working of Klipper.
My considerations have been:
- using other letters to address more axes (as I wrote) and stay in G-code semantics what is performed at a given time (one line = one task) – OR –
- add more flow control like
QUEUE
/M2000
and EXEC
/M2001
to mark & end of a or some motions (transaction like in SQL), then I would keep T0-T3 on separate lines with G[0123] statements afterwards, but having those custom code (macros) ensure stepper motor stage in the pipeline of Klipper knows what to start at the same time - it would be my task to make sure that involved F/speed and distances orchestrated two or more axes sharing the same Y gantry
This would mean, T0-T3 are declarative, and G[0123] following (even multiple) would a matter of “orchestration”:
- let’s assume T0 goes
G0 X0 Y0
, G1 X100 Y100 E10
- let’s assume T1 does
G0 X200
(no Y as we share same Y), G1 X300 E10
The G0 I would not care to be atomic, but the beginning of the extrusion I am.
T0
G0 X0 Y0 ; done is motion queue 0
M400
T1
G0 X200 ; done is motion queue 1 => T0 & T1 move at the same time
M400
; at this point both T0 & T1 are done
M2000 ; start of atomicity
T0
G1 X100 Y10 E10
T1
G1 X300 E10 ; same Y
M2001 ; end of atomicity
The nice side-effect would be, M2001
would sync multiple tools (instead to run M400
for each one).
For my tests (prototype stage) I introduced “SYNC
” (like M400
) but takes arguments of T0, T1, etc, to state which tools are awaited; this way I sync multiple gantries and the IDEX to each other.
So, M2000
would be a start of atomicity, and M2001
internally sees the mentioned T0, T1, etc to await to finish - so those are two different semantics: a) declaring atomicity around lines of G-code, or SYNCing axes and have a clear defined state in time; I’m honestly not sure which is better.
Declaring atomicity would perhaps cleaner: “this is what needs to happen”, and it fails, e.g. axes colliding or some other mishap, it falls into a defined state back (before the M2000
state) and move back into a “safe” state (me just loud thinking).
So, let me see, having 6 axes in G1
as your PR states:
- T0: X1, Y1, Z1, E1 (4 axes)
- T1: X2, E2 (2 axes)
- T2: X3, Y2, E3 (3 axes)
- T3: X4, E4 (2 axes)
total 11 axes: XYZ
(3) UVW
(3) ABC
(3) HI
(2) using one letter per axis;
EDIT: but I could put T0 & T1 in one Klipper instance & MCU (6 axis) and the T2 & T3 on the 2nd Klipper/MCU (5 axes) - it would work/help indeed.
The question is, if something like multi_axes.py
would help (alike dual_carriage.py
) in which we switch from one “extruder” to another.
SET_MULTI_AXIS_EXTRUDER EXTRUDER=0,1,2,3
as the long(er) version of T0, T1 … that would tell, which tool/extruder does what, but the core problem I face is, I like to declare when those are executed (not sure what SYNC_EXTRUDER_MOTION
does, haven’t studied the very details).
I care of the atomicity, two motions starting the same time.
I’m aware, that look-ahead like direction changes and [a/de]cceleration are lower level in Klipper and not knowable at the G-code level - but it’s something I do care as well, let me elaborate to give more context (for those who care):
Here the core challenge of my project:
- I move two extruders in X while having the same Y (gantry)
- and I like to plan so exact, that the T1 extruder might do 3
G1
statements, while T0 does one G1 E
move, geometrically both do the same distances, something like:
T0
G0 X0 Y0
G1 X100 Y100 E10 F1000
T1
G0 X200
G0 X210 F1000 ; Step 1
G1 X280 E8 F1000 ; Step 2
G1 X300 F1000 ; Step 3
Both T0 and T1 move the same distance, but T1 has only a partial shorter extrusion (delayed start of extrusion and earlier stop).
I’m aware that is a hell of demand here, but I was hoping to be able to reverse calculate from the inner working of Klipper, and since the T1 have the same XY vector, that Klipper would time both motions nearly the same (T0: 2 lines, T1: 4 lines), and if not, I would find a way to anticipate and compensate (changing the speeds of Step 1, 2 and 3 or even change X positions to compensate de/acceleration of E motor).
