So, before I realized Klipper doesn’t actually allow this “out of the box” I converted my Voron 2.4 (CoreXY) to a purely CANBus setup with a seperate mcu for each stepper.
My current setup is…
Using the Pi as a secondary “main” MCU so I could alias my Z drives to make it easier to keep track. Plus since the main MCU sets the timing for the rest I thought it might help the issue (it didn’t).
Z Drives (4) - 3 Mellow Fly SHT42 and 1 BTT EBB42 (I was dumb and only bought 3 SHT42 cause I had the EBB42 on hand, I bought another SHT42 to replace it to make it easy to update klipper on all MCUs).
X and Y axis - A Huvud board on each (I did this portion first when I built the printer)
Toolhead - Mellowfly SB2040, controlling the extruder, fans and a Voron Tap probe.
Of course when I fixed up my printer.cfg I immediately got the “Multi-mcu homing not supported on multi-mcu shared axis” error message. For fun I went in and commented out the check for this and just ran it anyways. I now see why it’s not allowed.
It caused my probing tolerance to become unacceptable (but not as high as you’d think, it was just barely over the edge of workable).
My srtt, rttvar and other stats are consistently low. My max srtt I see is .001-.002 so “hypothetically” I should get .015-.03 mm resolution (15 mm/s * .001 (.002) s) but in reality it’s more like .01 - .1
Below is a snapshot of my klippy log when probing showing the timings and stats. They’re all fairly good. Oddly enough Z3 is a little slower (srtt of .002) than the other Z drives even though they’re in close proximity. The furthest away is the sb2040 on the physical toolhead on the gantry.
Stats 22023.6: gcodein=0
mcu: mcu_awake=0.001 mcu_task_avg=0.000009 mcu_task_stddev=0.000015 bytes_write=1128 bytes_read=5493 bytes_retransmit=0 bytes_invalid=0 send_seq=163 receive_seq=163 retransmit_seq=0 srtt=0.000 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=50001503
sb2040: mcu_awake=0.024 mcu_task_avg=0.000012 mcu_task_stddev=0.000019 bytes_write=23310 bytes_read=44222 bytes_retransmit=0 bytes_invalid=0 send_seq=1963 receive_seq=1963 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=11999921 adj=11999595
HuvudY: mcu_awake=0.001 mcu_task_avg=0.000010 mcu_task_stddev=0.000008 bytes_write=8599 bytes_read=15683 bytes_retransmit=0 bytes_invalid=0 send_seq=629 receive_seq=629 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=72000471 adj=71998558
HuvudX: mcu_awake=0.001 mcu_task_avg=0.000010 mcu_task_stddev=0.000008 bytes_write=9003 bytes_read=15728 bytes_retransmit=0 bytes_invalid=0 send_seq=638 receive_seq=638 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=72000203 adj=71998483
Z: mcu_awake=0.026 mcu_task_avg=0.000018 mcu_task_stddev=0.000018 bytes_write=31917 bytes_read=40399 bytes_retransmit=0 bytes_invalid=0 send_seq=2179 receive_seq=2179 retransmit_seq=0 srtt=0.001 rttvar=0.001 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47999140 adj=47996930
Z1: mcu_awake=0.025 mcu_task_avg=0.000012 mcu_task_stddev=0.000013 bytes_write=32337 bytes_read=40360 bytes_retransmit=0 bytes_invalid=0 send_seq=2191 receive_seq=2191 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=63998932 adj=63997553
Z2: mcu_awake=0.027 mcu_task_avg=0.000018 mcu_task_stddev=0.000018 bytes_write=32011 bytes_read=42735 bytes_retransmit=0 bytes_invalid=0 send_seq=2194 receive_seq=2194 retransmit_seq=0 srtt=0.001 rttvar=0.001 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47998909 adj=47997401
Z3: mcu_awake=0.026 mcu_task_avg=0.000017 mcu_task_stddev=0.000018 bytes_write=31775 bytes_read=40149 bytes_retransmit=0 bytes_invalid=0 send_seq=2178 receive_seq=2178 retransmit_seq=0 srtt=0.002 rttvar=0.002 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47999196 adj=47997478
FLY-SB2040: temp=22.9 HuvudY: temp=43.3 HuvudX: temp=47.1 Z: temp=46.8 Z1: temp=38.4 Z2: temp=50.0 Z3: temp=46.7 heater_bed: target=0 temp=18.8 pwm=0.000 sysload=0.45 cputime=103.123 memavail=1678616 print_time=56.226 buffer_time=0.000 print_stall=0 extruder: target=0 temp=19.8 pwm=0.000
probe at 330.000,330.000 is z=-1.517500
Stats 22024.6: gcodein=0
mcu: mcu_awake=0.001 mcu_task_avg=0.000009 mcu_task_stddev=0.000015 bytes_write=1140 bytes_read=5525 bytes_retransmit=0 bytes_invalid=0 send_seq=165 receive_seq=165 retransmit_seq=0 srtt=0.000 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=50001492
sb2040: mcu_awake=0.024 mcu_task_avg=0.000012 mcu_task_stddev=0.000019 bytes_write=23854 bytes_read=45162 bytes_retransmit=0 bytes_invalid=0 send_seq=2007 receive_seq=2007 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=11999921 adj=11999576
HuvudY: mcu_awake=0.001 mcu_task_avg=0.000010 mcu_task_stddev=0.000008 bytes_write=8633 bytes_read=15805 bytes_retransmit=0 bytes_invalid=0 send_seq=632 receive_seq=632 retransmit_seq=0 srtt=0.000 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=72000464 adj=71998482
HuvudX: mcu_awake=0.001 mcu_task_avg=0.000010 mcu_task_stddev=0.000008 bytes_write=9037 bytes_read=15850 bytes_retransmit=0 bytes_invalid=0 send_seq=641 receive_seq=641 retransmit_seq=0 srtt=0.001 rttvar=0.000 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=72000190 adj=71998237
Z: mcu_awake=0.026 mcu_task_avg=0.000018 mcu_task_stddev=0.000018 bytes_write=32848 bytes_read=41413 bytes_retransmit=0 bytes_invalid=0 send_seq=2239 receive_seq=2239 retransmit_seq=0 srtt=0.001 rttvar=0.001 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47999116 adj=47996901
Z1: mcu_awake=0.025 mcu_task_avg=0.000012 mcu_task_stddev=0.000013 bytes_write=33257 bytes_read=41370 bytes_retransmit=0 bytes_invalid=0 send_seq=2251 receive_seq=2251 retransmit_seq=0 srtt=0.001 rttvar=0.001 rto=0.025 ready_bytes=28 upcoming_bytes=0 freq=63999032 adj=63997178
Z2: mcu_awake=0.027 mcu_task_avg=0.000018 mcu_task_stddev=0.000018 bytes_write=32934 bytes_read=43809 bytes_retransmit=0 bytes_invalid=0 send_seq=2254 receive_seq=2254 retransmit_seq=0 srtt=0.001 rttvar=0.001 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47998908 adj=47997354
Z3: mcu_awake=0.026 mcu_task_avg=0.000017 mcu_task_stddev=0.000018 bytes_write=32706 bytes_read=41149 bytes_retransmit=0 bytes_invalid=0 send_seq=2238 receive_seq=2238 retransmit_seq=0 srtt=0.002 rttvar=0.002 rto=0.025 ready_bytes=0 upcoming_bytes=0 freq=47999231 adj=47997403
FLY-SB2040: temp=22.6 HuvudY: temp=43.3 HuvudX: temp=47.1 Z: temp=46.7 Z1: temp=38.2 Z2: temp=50.2 Z3: temp=46.8 heater_bed: target=0 temp=18.8 pwm=0.000 sysload=0.41 cputime=103.391 memavail=1678740 print_time=57.286 buffer_time=0.000 print_stall=0 extruder: target=0 temp=19.9 pwm=0.000
probe at 330.000,330.000 is z=-1.495000
Probe samples exceed tolerance. Retrying...
So my question is… Is the barrier to the multi_mcu on a shared axis just purely due to communication latency concerns? Is there a deeper issue at play with keeping indenpendant z axis movements synchronized.
@koconnor mentioned in his outlook for Klipper in 2024 redoing the homing/probing code and I feel like this is pretty intimately connected but I’m trying to understand the “big picture” of the factors at play.
I had a few potential thoughts on possible fixes, but I’ll be the first to tell you I don’t fully understand the klipper source code. I’m building up a mental model as I read through and debug it but any deeper insight from those with experience would be very helpful.
Ideas:
1.) Speed up the mcu code/reduce latency - I noticed in the mcu firmware code there are places where code can be streamlined a bit. Some bounds checking and things like that could be sped up by using some fairly easy bit manipulation checks. Mainly I was looking in the command.c and basecmd.c but any place the mcu code could be optimized would potentially reduce communication latency on the mcus by speeding up their processing on tasks.
2.) Additional sync communication during homing/probing moves - I’m still wrapping my head around the drip movement portion and the fact that “homing” is spread across like 4 different files (homing.py, probe.py, mcu.py and toolhead.py for the drip move portion). But my thought was possibly introducing an additional parameter or flag to tell the mcus “This movement is part of homing, so send continous updates of your position/steps every x microseconds (100s of microseconds) until the endstop is triggered” and then work out a way on the Pi to keep a reference of them and if a stepper starts lagging/leading send communication along the lines of “WAIT! Z3, You’re x number of steps behind. Here’s your new movement command to adjust for you being so slow.”
Obviously that’s a major undertaking but I feel like it would have the potential for a major upgrade to accurate homing/probing.
3.) Allowing multi_mcu probing on a shared axis if an endstop probe is defined in the stepper portion. For example, if I split the output of my Voron Tap probe and connected it to an endstop pin on all my Z axis drives so they all received the endstop pulse at the same time. Of course I’d have to check the fanout capability on the opto-interrupter, this may require a buffer to keep the signal strong enough. I just realized this is actually possible, I’ll try it out tomorrow.
4.) This isn’t a total solution, but I see that there has been work done on FD CAN. I’ve thought about creating a few custom boards with an FD CAN Transceiver and seeing just how far I can push down any communication latency. I know this isn’t something that would work on the mainline klipper because it isn’t applicable on every printer. Unless we decide it’s okay to do that only if there is something in the printer.cfg that specifies FD CAN is available and active.
I know that’s a lot of info, questions and words in general.
I’d like to do my best to help make Klipper better and more awesome, Any guidance on the current state of affairs on how the code is working at the lowest level during homing, probing or stepper synchronization would help me immensely in getting up to speed.
Thanks.