"Virtual Axis" break stall detection?
-
People in this category have hinted that you can define an axis that you are not using and then hijack its offset to store a floating point number per-tool for use in expressions. The axis has to have limits define, it cant be hidden and needs to be homed. So, easy enough:
M584 A ; Use the 'A' axis M208 A0:400 ; define travel limits from 0 to 400 G92 A0 ; home it
Then you can assign 'A' a value per-tool using its G10 offset:
G10 P{state.currentTool} A240
And wherever you need that value you can read it out of the current tool (assuming that A was the 5th axis to get defined):
M109 S{tools[state.currentTool].offsets[4]}
So the above works, but when I home my Core X/Y machine it refuses to detect a stall in the X axis! Y stall detect works just fine. Stall detection worked before I added the axis. Same motors and settings, nothing changed.
So I wondered if maybe I had to assign a drive to the axis and tried this:
M584 A6 ; Use the 'A' axis
Drive 6 does not exist, so I get an error in the console:
Error: M584: Driver 0.6 does not exist
Fair enough, but when I home the machine anyway everything is fine, just as it was before I added the axis.
So I have no idea whats going on. The console error shows up in 3.2_RC2 but not 3.1.1. The stall detection failure happens on both versions if no drive number is given. Feels like a bug but I know I'm doing something pretty far outside the box. Anyone else run into this?
(Duet 3, Pi SBC attached, 1 expansion board, running 3.2_RC2. E3D Tool Changer motion system.)
-
You don't need to create another axis to store numbers.
Use the workplace coordinates to store values. In 3d printing we only use the first one so there's another 8 sets of coordinates, one for each axis that can be used. Look at G54 to G59.3 -
My original suggestion was to use the G10 offsets of dummy tools, not extra axes.
-
So the thing I'm trying to simulate is a variable that can be looked up by the tool's index. When we get variables I'm expecting to do something like this:
var global.toolOffsetX = [302, 68, 125, 197] var global.toolOffsetY = [300, 301.0, 300.54, 300.3]
then use a tool number to look that up:
; move to final docking position G1 X{global.toolOffsetX[state.currentTool]} Y {global.toolOffsetY[state.currentTool]} F{global.dockingSpeed}
Until that day arrives its hacks all the time
I want to store this info for each tool:
- Filament temp for loading/unloading and printing Z-calibration test patterns
- Extrude speed for the filament for the above reasons.
- Nozzle diameter for printing Z-calibration test patterns (to correct the flow rate) and checking that a prints expected nozzle diameter matches the one installed
- Each tools X, Y and offsets in machine coordinates for docking
So long as the above is going to work I don't really care how broken this is right now so long as I don't damage anything. I'm investigating the workplace coordinate options as a less hacky alternative.
-
@jay_s_uk Thank you for the suggestion!
For my machine I think this will work, I have 4 tools and 4 axes, what are the odds?
Sorry if I'm being very pedantic beyond this point, I'm documenting how it works for the next person that wants to try it out:
To make accessing the values easy I'm storing all of a tool's properties in a single axis, so X=Tool 0, Y=Tool 1, Z=Tool 2 and C=Tool 3. Lets say I want to record the nozzle diameter for each tool, that would looks like this:
; store tool nozzle diameters in coordinate system 5 G10 L2 P5 X0.4 Y0.6 Z0.4 C0.8
Then the object model makes retrieving the values by tool painless because the axes are in an array:
echo "Current tool nozzle diameter is: ", {move.axes[state.currentTool].workplaceOffsets[4]}, "mm"
You get from
G10 L2 P2
toG10 L2 P9
to store stuff in, enough for 8 per-tool properties.G10 L2 P1
isG54
so that's already in use.For me its a drop in replacement for the axis hack and likely to be supported long term. If your machine has more tools than axes this wouldn't work out quite as well for you.
-
This is really turning out to be the right answer. One of the benefits of using the work offsets is that they are one of the few things that can be stored with
M500
!(I thought I could store things from the object model by exploiting
M28
/M29
. But no expressions between those commands gets evaluated, so you can't store the result of an expression.)So I explained above how to store things by writing gcode to a file you run on startup. But what if you want to store something in the work offsets during operation of the printer? You have a tool number, say 2, and you need to write a gcode expression with an axis letter,
G10 L2 P5 Z...
. How do you get thatZ
?Well, you can write an if block to do that but that code is pretty long and you wouldn't want that cluttering up your all your macros. So I wrapped that up into macro itself so I can re-use it everywhere I need to store something. The trick is we don't have a way to pass variables into macros yet. So I just write the tool #, value to store and desired coordinate system index into coordinate system #9. Basically coordinate system #9 is now the stack for my program:
; copy_var.g ; ; this macro copies a variable from the temporary variable slot and stores it in the requested slot for the specified tool: ; ; How To use: ; ; G10 L2 P9 X{state.currentTool} Y{value_to_store} Z{target_coordinate_system_index} ; M98 P"/macros/scripts/copy_var.g" ; ; Inputs: ; ; move.axes[0].workplaceOffsets[8] - tool number to store the value for ; move.axes[1].workplaceOffsets[8] - the value to store ; move.axes[2].workplaceOffsets[8] - the coordinate system number to store it in if move.axes[0].workplaceOffsets[8] == 0 G10 L2 P{floor(move.axes[2].workplaceOffsets[8])} X{move.axes[1].workplaceOffsets[8]} elif move.axes[0].workplaceOffsets[8] == 1 G10 L2 P{floor(move.axes[2].workplaceOffsets[8])} Y{move.axes[1].workplaceOffsets[8]} elif move.axes[0].workplaceOffsets[8] == 2 G10 L2 P{floor(move.axes[2].workplaceOffsets[8])} Z{move.axes[1].workplaceOffsets[8]} elif move.axes[0].workplaceOffsets[8] == 3 G10 L2 P{floor(move.axes[2].workplaceOffsets[8])} C{move.axes[1].workplaceOffsets[8]} else abort ; if you pass -1 you call at the wrong time!
Now that is solved I can combine it with
M500
and I give you per-tool baby stepping adjustment that persists between reboots:call this in tfreeN.g
; save_babystep.g ; writes the baby stepping value into the work offset 7 and then stores it with M500 G10 L2 P9 X{state.currentTool} Y{move.axes[2].babystep} Z6 M98 P"/macros/scripts/copy_var.g" M500
and call this in tpostN.g
; restore_babystep.g ; restore the absolute babystepping value from its storage slot M290 R0 S{move.axes[state.currentTool].workplaceOffsets[5]}
Then call
M501
at startup before you write any config values as described in the post above.