Please add working software endstops for Delta machines
I don't have a Delta, so I'm not sure what needs to be done here.
For all types of machines we have to find a adequate reactiong.
Drop the move - clipping the move at the point the axis is hitting the border (recalculate the other axis proportional - endpont is not the wanted destination for all axes) - just clipping the one axis (gives wrong diagonals - endpoint is correct for the not clipped axis) - split in two moves (one following the 'normal path and one the reach the other destinations with the clipped coordinate constant).
Ignore - warn - error - kill.
For delta machines there are 3 different limits:
The bed = Z_MIN
The printable radius (that's simplified - in reality it's "diagonal rod is horizontal")
At z-max we cant move in x/y direction because at least one of the carriages has to go upward. The forbidden volume can be roughly described by 3 spheres with the center at the tower tops and an a radius of the diagonal rods. In tower coordinates it's simply, for every tower, Z_MAX.
More info in
https://github.com/MarlinFirmware/Marlin/issues/2679
https://github.com/MarlinFirmware/MarlinDev/issues/304
I'm pretty sure the behavior I want is not going to be acceptable for everybody. With that said: The behavior I would like is for the nozzle to just freeze its location as it leaves the Printable Radius. So, in other words, the nozzle stops at the limit of where it is supposed to not go past. Probably, the firmware keeps tract of all the subsequent moves and when a new destination is detected to be within the Printable Radius, the nozzle is moved to that location. (And the freeze on movement is ended)
@Roxy-3DPrintBoard
That's an interesting idea. I'll have a look on it.
It's not trivial. We have to track a additional position. There is a intermediate move from exit- to entrance- point without a extrusion. Retraction? Tool-change while outside?
Too complex for this afternoon.
What is needed is a way for a host to ask the printer, "can I move to location XYZ?" in order to determine the available print volume. Or perhaps the printer can simply reply with a series of vectors and edges (a 3D wireframe of the printable area) since the volume may be irregular. Likewise, the firmware should be able to pre-scan an SD file to determine if it can accommodate all the points. (A pre-scan may allow for other kinds of optimization also.)
What is needed is a way for a host to ask the printer, "can I move to location XYZ?" in order to determine the available print volume.
Well... In the case of Delta's, we already have that as a #define DELTA_PRINTABLE_RADIUS. And for Cartesian machines, we know the MIN and Max of each axis. I think we have the information we need to do it. The problem is, this 'Simple' change has its hooks into about 37 different places in the firmware. And it would be controlling some global state variable that decides whether or not any movement of the nozzle is allowed.

// DELTA alowed volume - aproximated
// OpenSCAD
// should also use
// DELTA_EFFECTOR_OFFSET, DELTA_CARRIAGE_OFFSET,
dgrl = 225; // diagonal rod length, DELTA_DIAGONAL_ROD
h = 600; // height
dt = 250; // distance tower - tower
tr = dt * cos(60); // distance center - tower, DELTA_SMOOTH_ROD_OFFSET
$fn = 90;
// towers
color("blue") {
rotate([0,0, 0]) translate([tr,0,-5]) cylinder(h=h+5, r = 5);
rotate([0,0, 120]) translate([tr,0,-5]) cylinder(h=h+5, r = 5);
rotate([0,0,-120]) translate([tr,0,-5]) cylinder(h=h+5, r = 5);
}
// bed
color("red") translate([0,0,-3]) cube([200,200,3], center=true);
color("black") translate([0,0,-4]) cylinder(d=220, h= 3, center=true);
color([0.5,0.5,0.5,0.8]) difference() {
// d-rods horizontal
intersection() {
rotate([0,0, 0]) translate([tr,0,0]) cylinder(h=h, r = dgrl);
rotate([0,0, 120]) translate([tr,0,0]) cylinder(h=h, r = dgrl);
rotate([0,0,-120]) translate([tr,0,0]) cylinder(h=h, r = dgrl);
}
// top limit
union() {
rotate([0,0, 0]) translate([tr,0,h]) sphere(r = dgrl);
rotate([0,0, 120]) translate([tr,0,h]) sphere(r = dgrl);
rotate([0,0,-120]) translate([tr,0,h]) sphere(r = dgrl);
}
}
Yes, but if we are going to do something like software endstops for Delta's... Isn't it acceptable to trim off the very top of the print envelope to simplify the calculations? We don't want to be checking against those 3 spheres at the top on every move!
I just hard coded what I'm thinking:

And because the effector has some size to it... It can never get all the way out to any of the three towers either? The printable area has to stay some distance from the towers.
Maybe more like this?

Isn't it acceptable to trim off the very top of the print envelope to simplify the calculations?
It is - but implies the "homing point" is not inside the allowed volume.
Before drawing this i assume the tip at the top to be much more 'pointy'.
I was almost certain that we agreed upon a PRINTABLE_RADIUS to clip, because so relative simple to evaluate.

The tip of the gray shape is our old Z_MAX.
The height of the green cylinder could be our new Z-max to clip.
To get there we could add a NEW_CONSTANT = Z_MAX - Z-max to
#if ENABLED(DELTA)
// retrace by the amount specified in endstop_adj
if (endstop_adj[axis] * axis_home_dir < 0) {
#if ENABLED(DEBUG_LEVELING_FEATURE)
if (marlin_debug_flags & DEBUG_LEVELING) {
SERIAL_ECHOLNPGM("> enable_endstops(false)");
}
#endif
enable_endstops(false); // Disable endstops while moving away
sync_plan_position();
destination[axis] = endstop_adj[axis] - NEW_CONSTANT;
#if ENABLED(DEBUG_LEVELING_FEATURE)
if (marlin_debug_flags & DEBUG_LEVELING) {
SERIAL_ECHOPAIR("> endstop_adj = ", endstop_adj[axis]);
print_xyz(" > destination", destination);
}
#endif
line_to_destination();
st_synchronize();
#if ENABLED(DEBUG_LEVELING_FEATURE)
if (marlin_debug_flags & DEBUG_LEVELING) {
SERIAL_ECHOLNPGM("> enable_endstops(true)");
}
#endif
enable_endstops(true); // Enable endstops for next homing move
We'll see if we can calculate that.
I was almost certain that we agreed upon a PRINTABLE_RADIUS to clip, because so relative simple to evaluate.
Yes, and in those previous discussions I think we also agreed that it made sense to compare and clip to PRINTABLE_RADIUS^2 just because we didn't want to do the extra math to get the square root.
How does this compare with the reachable area of a SCARA, or the reachable area of a delta that has the towers in parallel, instead of at the corners? There are also deltas with the arms articulated at elbow joints, like the RepRap Simpson. What is the limit of the printable area on that machine?
For a SCARA machine the area can be described by biggest and smallest reach, and a range of angles. A segment of a ring. Probably further limited by the shape of the bed. z is simply min and max.
a delta that has the towers in parallel
Sorry. Can't visualize that - at the moment.
There are also deltas with the arms articulated at elbow joints, like the RepRap Simpson.
Simpson Lisa is a quite normal delta i guess. The others are driven by angular coordinates. I'll think about it, as soon someone tries to integrate the kinematic. Up to now they are driven by a g-code-post-processor on a firmware with Cartesian setup. Likely it can be described by the upper half of the intersection of 3 spheres with a radius of the maximum arm length, where the centers are located at the mount points. If it needs additional spheres, representing the minimal reach, depends on the exact geometry.

A can of worms… Whatever we do, I guess it's got to be pluggable.
Thank you for your interest making Marlin better.
You should re-open this topic on MarlinFirmware/MarlinDev for proper followup from the development team. We suggest you to use the MarlinFirmware/Marlin#<topic id> markdown to link back to this original topic.
@jbrazio
Super idea to direct feature requests to MarkinDev the day it is dicsussed to remove the repository (#3656). Please reopen.
@AnHardt we have to work with the tools in place, issues have being closed for a couple of weeks now, all of them with the same message.
I'm implementing this now to see how viable it is, and to get this oldest of our issues closed.
It does add a bit more overhead to all Delta printing, just to warn you. For every move on a delta we must calculate at least the distance-squared from center of the destination. If that test passes then all is good. The constraint method I would use to keep it simple to start with is just to get the distance from center of the destination, plus the ratio of the distance to the constraint radius, and apply that to the current point. In this way the point will be at the same angle as the original, but at the perimeter.
So…
void clamp_to_software_endstops(float target[XYZ]) {
if (!soft_endstops_enabled) return;
#if IS_KINEMATIC
const float dist_2 = HYPOT2(target[X_AXIS], target[Y_AXIS]);
if (dist_2 > soft_endstop_radius_2) {
const float ratio = soft_endstop_radius / SQRT(dist_2); // 200 / 300 = 0.667
target[X_AXIS] *= ratio;
target[Y_AXIS] *= ratio;
}
#else
#if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
NOLESS(target[X_AXIS], soft_endstop_min[X_AXIS]);
#endif
#if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
NOLESS(target[Y_AXIS], soft_endstop_min[Y_AXIS]);
#endif
#if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
NOMORE(target[X_AXIS], soft_endstop_max[X_AXIS]);
#endif
#if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
NOMORE(target[Y_AXIS], soft_endstop_max[Y_AXIS]);
#endif
#endif
#if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
NOLESS(target[Z_AXIS], soft_endstop_min[Z_AXIS]);
#endif
#if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
NOMORE(target[Z_AXIS], soft_endstop_max[Z_AXIS]);
#endif
}
And in update_software_endstops…
#if ENABLED(DELTA)
switch(axis) {
case X_AXIS:
case Y_AXIS:
// Get a minimum radius for clamping
const float min_radius = MIN3(FABS(max(soft_endstop_min[X_AXIS], soft_endstop_min[Y_AXIS])), soft_endstop_max[X_AXIS], soft_endstop_max[Y_AXIS]);
soft_endstop_radius = min_radius;
soft_endstop_radius_2 = sq(min_radius);
break;
case Z_AXIS:
delta_clip_start_height = soft_endstop_max[axis] - delta_safe_distance_from_top();
default:
}
#endif
Most helpful comment
So…
And in
update_software_endstops…