diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index c993bf80039c..f783ab7441b7 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -326,7 +326,14 @@ */ //#define ELECTROMAGNETIC_SWITCHING_TOOLHEAD + +// Safe toolchange start Z position. +//#define SAFE_TOOLCHANGE_START_Z 200 + #if ANY(SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD) + //#define SWITCHING_TOOLHEAD_Z_POS 100 // (mm) Z position of the toolhead dock. + // Leave this option disabled if the bed can move in Z direction + //#define SWITCHING_TOOLHEAD_Z_CLEAR 60 // (mm) Minimum distance from dock along Z for unobstructed X axis if the tools are placed onto the dock in Z direction #define SWITCHING_TOOLHEAD_Y_POS 235 // (mm) Y position of the toolhead dock #define SWITCHING_TOOLHEAD_Y_SECURITY 10 // (mm) Security distance Y axis #define SWITCHING_TOOLHEAD_Y_CLEAR 60 // (mm) Minimum distance from dock for unobstructed X axis @@ -371,7 +378,7 @@ // Offset of the extruders (uncomment if using more than one and relying on firmware to position when changing). // The offset has to be X=0, Y=0 for the extruder 0 hotend (default extruder). -// For the other hotends it is their distance from the extruder 0 hotend. +// For the other hotends it is their distance from the extruder 0 hotend in positive axis direction. //#define HOTEND_OFFSET_X { 0.0, 20.00 } // (mm) relative X-offset for each nozzle //#define HOTEND_OFFSET_Y { 0.0, 5.00 } // (mm) relative Y-offset for each nozzle //#define HOTEND_OFFSET_Z { 0.0, 0.00 } // (mm) relative Z-offset for each nozzle @@ -1117,6 +1124,75 @@ #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly #endif +// @section PENTA_AXIS_TRT + +/** + * For a 5 axis CNC machine in tilting rotary table configuration. + * This machine has a rotary table (C axis) mounted on a tilting table + * (A axis parallel to the X axis, or B axis parallel to the Y axis). + * More information can be found at https://github.com/DerAndere1/Marlin/wiki/Marlin2ForPipetBot:-five-axis-CNC + */ +//#define PENTA_AXIS_TRT +#if ENABLED(PENTA_AXIS_TRT) + + // Machine rotary zero point offsets + // The distance along the X axis from machine zero point to the center of rotation. The center of rotation is + // usually the center of the top surface of the table when all axes are at machine position 0. + #define DEFAULT_MRZP_OFFSET_X 0.0 // (mm) + + // The distance along the Y axis from machine zero point to the center of rotation. The center of rotation is + // usually the center of the top surface of the table when all axes are at machine position 0. + #define DEFAULT_MRZP_OFFSET_Y 0.0 // (mm) + + // The distance along the Z axis from machine zero point to the center of rotation. The center of rotation is + // usually the center of the top surface of the table when all axes are at machine position 0. + #define DEFAULT_MRZP_OFFSET_Z 0.0 // (mm) + + // For a machine with XYZBC axes, this is the distance along the x axis from the vertical centerline of the + // joint of the horizontal rotary table to the horizontal centerline of the joint that tilts the table. + // Measured when the table is oriented horizontally. + #define DEFAULT_ROTATIONAL_JOINT_OFFSET_X 0.0 // (mm) + + // For a machine with XYZAC axes, this is the distance along the y axis from the vertical centerline of the + // joint of the horizontal table to the horizontal centerline of the joint that tilts the table. + // Measured when the table is oriented horizontally. + #define DEFAULT_ROTATIONAL_JOINT_OFFSET_Y 0.0 // (mm) + + // This is the distance along the Z axis from the surface at the top of the table to the horizontal + // centerline of the joint that tilts the table when the table is oriented horizontally. + #define DEFAULT_ROTATIONAL_JOINT_OFFSET_Z 0.0 // (mm) + + // Moves involving rotational axes is broken up into small straight segments (linear interpolation). + // This is a trade-off between visible corners (not enough segments) + // and processor overload (too many expensive sqrt calls). + #define DEFAULT_SEGMENTS_PER_SECOND 200 + + // Print surface diameter/2 + #define PRINTABLE_RADIUS 100.0 // (mm) +#endif + +// @section PENTA_AXIS_HT + +/** + * For a 5 axis CNC machine in head-table configuration. + * This machine has a swivel head and a horizontal rotary table. + */ +//#define PENTA_AXIS_HT +#if ENABLED(PENTA_AXIS_HT) + + // Machine rotary zero point offset is the distance from the gage line at the tool head (tip of the nozzle of tool 0) to the horizontal + // centerline of the joint that tilts the tool head. Measured when all axes are at machine position 0. + #define DEFAULT_MRZP_OFFSET_Z 100.0 // (mm) + + // Moves involving rotational axes is broken up into small straight segments (linear interpolation). + // This is a trade-off between visible corners (not enough segments) + // and processor overload (too many expensive sqrt calls). + #define DEFAULT_SEGMENTS_PER_SECOND 200 + + // Print surface diameter/2 + #define PRINTABLE_RADIUS 100.0 // (mm) +#endif + // @section machine // Articulated robot (arm). Joints are directly mapped to axes with no kinematics. @@ -1902,6 +1978,13 @@ #endif #if ANY(MIN_SOFTWARE_ENDSTOPS, MAX_SOFTWARE_ENDSTOPS) + /** + * Abort printing when any software endstop is triggered. + * This feature is enabled with 'M541 S1' or from the LCD menu. + * Software endstops must be activated for this option to work. + */ + //#define ABORT_ON_SOFTWARE_ENDSTOP + //#define SOFT_ENDSTOPS_MENU_ITEM // Enable/Disable software endstops from the LCD #endif diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 7cda16dc0f30..ac6c3b0f03aa 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -935,6 +935,9 @@ //#define XY_COUNTERPART_BACKOFF_MM 0 // (mm) Backoff X after homing Y, and vice-versa //#define QUICK_HOME // If G28 contains XY do a diagonal move first +#if ENABLED(QUICK_HOME) + //#define QUICK_HOME_SECONDARY_AXES // If G28 contains XYABCUVW, first do a coordinated move of axes XYABCUVW towards the limit used for homing. +#endif //#define HOME_Y_BEFORE_X // If G28 contains XY home Y before X //#define HOME_Z_FIRST // Home Z first. Requires a real endstop (not a probe). //#define CODEPENDENT_XY_HOMING // If X/Y can't home without homing Y/X first @@ -2784,7 +2787,7 @@ * Universal tool change settings. * Applies to all types of extruders except where explicitly noted. */ -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS // Z raise distance for tool-change, as needed for some extruders #define TOOLCHANGE_ZRAISE 2 // (mm) //#define TOOLCHANGE_ZRAISE_BEFORE_RETRACT // Apply raise before swap retraction (if enabled) @@ -2891,7 +2894,7 @@ //#define TOOLCHANGE_MIGRATION_DO_PARK // Force park (or no-park) on migration #endif #endif -#endif // HAS_MULTI_EXTRUDER +#endif // HAS_MULTI_TOOLS // @section advanced pause diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 0cf5728110ec..03c8af3cd300 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -177,6 +177,10 @@ #include "module/scara.h" #elif ENABLED(POLAR) #include "module/polar.h" +#elif ENABLED(PENTA_AXIS_TRT) + #include "module/penta_axis_trt.h" +#elif ENABLED(PENTA_AXIS_HT) + #include "module/penta_axis_ht.h" #endif #if HAS_LEVELING diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 2736a7833ed5..f133d6e758e8 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -186,6 +186,7 @@ #define STR_SD_CANT_ENTER_SUBDIR "Cannot enter subdir: " #define STR_ENDSTOPS_HIT "endstops hit: " +#define STR_ERR_SW_ENDSTOP "software endstops hit" #define STR_ERR_COLD_EXTRUDE_STOP " cold extrusion prevented" #define STR_ERR_LONG_EXTRUDE_STOP " too long extrusion prevented" #define STR_ERR_HOTEND_TOO_COLD "Hotend too cold" @@ -283,6 +284,8 @@ #define STR_SCARA_SETTINGS "SCARA" #define STR_POLAR_SETTINGS "Polar" #define STR_POLARGRAPH_SETTINGS "Polargraph" +#define STR_PAX_TRT_SETTINGS "Penta-Axis-TRT" +#define STR_PAX_HT_SETTINGS "Penta-Axis-Head-Table" #define STR_SCARA_P_T_Z "P T Z" #define STR_ENDSTOP_ADJUSTMENT "Endstop adjustment" #define STR_SKEW_FACTOR "Skew Factor" diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index 7793f1f99561..3bb35fdd00d0 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -345,6 +345,16 @@ enum AxisEnum : uint8_t { #define LOOP_DISTINCT_AXES(VAR) for (uint8_t VAR = 0; VAR < DISTINCT_AXES; ++VAR) #define LOOP_DISTINCT_E(VAR) for (uint8_t VAR = 0; VAR < DISTINCT_E; ++VAR) + +// +// Enumerates tool types +// +enum ToolTypeEnum : uint8_t { + TYPE_EXTRUDER, + TYPE_LASER, + TYPE_SPINLDE +}; + // // feedRate_t is just a humble float // diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp index 4637bf87e828..92223a1f185b 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp @@ -1281,8 +1281,13 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() { if (!isnan(z_values[i][j])) continue; // Skip valid mesh points // Skip unreachable points - if (!probe.can_reach(get_mesh_x(i), get_mesh_y(j))) - continue; + #if ANY(PENTA_AXIS_HT, PENTA_AXIS_TRT) + if (!probe.can_reach(NUM_AXIS_LIST(get_mesh_x(i), get_mesh_y(j), TERN0(SAFE_BED_LEVELING_START_Z, SAFE_BED_LEVELING_START_Z), TERN0(SAFE_BED_LEVELING_START_I, SAFE_BED_LEVELING_START_I), TERN0(SAFE_BED_LEVELING_START_J, SAFE_BED_LEVELING_START_J), TERN0(SAFE_BED_LEVELING_START_K, SAFE_BED_LEVELING_START_K), TERN0(SAFE_BED_LEVELING_START_U, SAFE_BED_LEVELING_START_U), TERN0(SAFE_BED_LEVELING_START_V, SAFE_BED_LEVELING_START_V), TERN0(SAFE_BED_LEVELING_START_W, SAFE_BED_LEVELING_START_W)))) + continue; + #else + if (!probe.can_reach(get_mesh_x(i), get_mesh_y(j))) + continue; + #endif found_a_NAN = true; diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp index b7dcf0038548..15b1422b3413 100644 --- a/Marlin/src/feature/powerloss.cpp +++ b/Marlin/src/feature/powerloss.cpp @@ -516,7 +516,7 @@ void PrintJobRecovery::resume() { #endif // Restore the previously active tool (with no_move) - #if HAS_MULTI_EXTRUDER || HAS_MULTI_HOTEND + #if HAS_MULTI_TOOLS || HAS_MULTI_HOTEND PROCESS_SUBCOMMANDS_NOW(TS('T', info.active_extruder, 'S')); #endif @@ -638,7 +638,7 @@ void PrintJobRecovery::resume() { DEBUG_EOL(); #endif - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS DEBUG_ECHOLNPGM("active_extruder: ", info.active_extruder); #endif diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h index 238f276c1bc8..64f95cbeabdc 100644 --- a/Marlin/src/feature/powerloss.h +++ b/Marlin/src/feature/powerloss.h @@ -75,7 +75,7 @@ typedef struct { #if HAS_WORKSPACE_OFFSET xyz_pos_t workspace_offset; #endif - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS uint8_t active_extruder; #endif diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp index c0635c72206f..68415490a463 100644 --- a/Marlin/src/feature/spindle_laser.cpp +++ b/Marlin/src/feature/spindle_laser.cpp @@ -39,6 +39,7 @@ #endif SpindleLaser cutter; +uint8_t SpindleLaser::active_tool_type; // Tool type: 0 for extruder, 1 for spindle, 2 for laser bool SpindleLaser::enable_state; // Virtual enable state, controls enable pin if present and or apply power if > 0 uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on" SpindleLaser::last_power_applied; // = 0 // Basic power state tracking @@ -65,21 +66,36 @@ cutter_frequency_t SpindleLaser::frequency; // PWM fre * Init the cutter to a safe OFF state */ void SpindleLaser::init() { + #if ENABLED(LASER_FEATURE) && LASER_TOOL == 0 + active_tool_type = TYPE_LASER; + #elif ENABLED(SPINDLE_FEATURE) && LASER_TOOL == 0 + active_tool_type = TYPE_LASER; + #else + active_tool_type = TYPE_EXTRUDER; + #endif #if ENABLED(SPINDLE_SERVO) servo[SPINDLE_SERVO_NR].move(SPINDLE_SERVO_MIN); #elif PIN_EXISTS(SPINDLE_LASER_ENA) OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Init spindle to off #endif + #if PIN_EXISTS(LASER_ENA) + OUT_WRITE(LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Init spindle to off + #endif #if ENABLED(SPINDLE_CHANGE_DIR) OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR); // Init rotation to clockwise (M3) #endif #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY frequency = SPINDLE_LASER_FREQUENCY; hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY); + hal.set_pwm_frequency(pin_t(LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY); #endif #if ENABLED(SPINDLE_LASER_USE_PWM) SET_PWM(SPINDLE_LASER_PWM_PIN); hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed + #if PIN_EXISTS(LASER_PWM) + SET_PWM(SPINDLE_LASER_PWM_PIN); + hal.set_pwm_duty(pin_t(LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed + #endif #endif #if ENABLED(AIR_EVACUATION) OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); // Init Vacuum/Blower OFF @@ -98,21 +114,37 @@ void SpindleLaser::init() { */ void SpindleLaser::_set_ocr(const uint8_t ocr) { #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY - hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); + if (active_tool_type == TYPE_LASER) + hal.set_pwm_frequency(pin_t(LASER_PWM_PIN), frequency); + else + hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); #endif - hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + if (active_tool_type == TYPE_LASER) + hal.set_pwm_duty(pin_t(LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + elif (active_tool_type == TYPE_SPINDLE) + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); } void SpindleLaser::set_ocr(const uint8_t ocr) { + #if PIN_EXISTS(LASER_ENA) + if (tool_type == TYPE_LASER) + WRITE(LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_STATE); // Cutter ON + #endif #if PIN_EXISTS(SPINDLE_LASER_ENA) - WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_STATE); // Cutter ON + if (tool_type == TYPE_SPINDLE) + WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_STATE); // Cutter ON #endif _set_ocr(ocr); } void SpindleLaser::ocr_off() { + #if PIN_EXISTS(LASER_ENA) + if (active_tool_type == TYPE_LASER) + WRITE(LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF + #endif #if PIN_EXISTS(SPINDLE_LASER_ENA) - WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF + if (active_tool_type TYPE_SPINDLE) + WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF #endif _set_ocr(0); } @@ -143,13 +175,21 @@ void SpindleLaser::apply_power(const uint8_t opwr) { #elif ENABLED(SPINDLE_SERVO) servo[SPINDLE_SERVO_NR].move(opwr); #else - WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); + if (active_tool_type == TYPE_LASER) + WRITE(LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); + elif (active_tool_type == TYPE_SPINDLE) + WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); isReadyForUI = true; #endif } else { + #if PIN_EXISTS(LASER_ENA) + if (active_tool_type == TYPE_LASER) + WRITE(LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); + #endif #if PIN_EXISTS(SPINDLE_LASER_ENA) - WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); + if (active_tool_type == TYPE_SPINDLE) + WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); #endif isReadyForUI = false; // Only used for UI display updates. TERN_(SPINDLE_LASER_USE_PWM, ocr_off()); diff --git a/Marlin/src/feature/spindle_laser.h b/Marlin/src/feature/spindle_laser.h index a443d7df5e0c..3aa409dfe49a 100644 --- a/Marlin/src/feature/spindle_laser.h +++ b/Marlin/src/feature/spindle_laser.h @@ -117,7 +117,14 @@ class SpindleLaser { static void init(); #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY - static void refresh_frequency() { hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); } + static void refresh_frequency() { + if (active_tool_type == TYPE_LASER) { + hal.set_pwm_frequency(pin_t(LASER_PWM_PIN), frequency); + } + elif (active_tool_type == TYPE_SPINDLE) { + hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); + } + } #endif // Modifying this function should update everywhere @@ -211,6 +218,9 @@ class SpindleLaser { enable = false; apply_power(0); } + #if PIN_EXISTS(LASER_ENA) + WRITE(LASER_ENA_PIN, enable ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); + #endif #if PIN_EXISTS(SPINDLE_LASER_ENA) WRITE(SPINDLE_LASER_ENA_PIN, enable ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); #endif diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index 03fd2c80ed9d..79a7241392e4 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -126,6 +126,93 @@ #endif // QUICK_HOME +#if ENABLED(QUICK_HOME_SECONDARY_AXES) + + static void quick_home_xyijkuvw() { + + // Pretend the current position is 0,0 + current_position.set(0.0, 0.0); + SECONDARY_AXIS_CODE( + current_position.i = 0.0, + current_position.j = 0.0, + current_position.k = 0.0, + current_position.u = 0.0, + current_position.v = 0.0, + current_position.w = 0.0 + ); + + sync_plan_position(); + + const int x_axis_home_dir = TOOL_X_HOME_DIR(active_extruder); + + // Use a higher diagonal feedrate so axes move at homing speed + const float minfr = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)), + fr_mm_s = HYPOT(minfr, minfr); + + feedRate_t old_max_speeds[NUM_AXES]; + LOOP_NUM_AXES(i) { + old_max_speeds[i] = planner.settings.max_feedrate_mm_s[i]; + planner.set_max_feedrate((AxisEnum)i, homing_feedrate((AxisEnum)i)); + } + #if ENABLED(SENSORLESS_HOMING) + sensorless_t stealth_states { + NUM_AXIS_LIST( + TERN0(X_SENSORLESS, tmc_enable_stallguard(stepperX)), + TERN0(Y_SENSORLESS, tmc_enable_stallguard(stepperY)), + false, + tmc_enable_stallguard(stepperI), + tmc_enable_stallguard(stepperJ), + tmc_enable_stallguard(stepperK), + tmc_enable_stallguard(stepperU), + tmc_enable_stallguard(stepperV), + tmc_enable_stallguard(stepperW) + ) + , false + , TERN0(X2_SENSORLESS, tmc_enable_stallguard(stepperX2)), + , TERN0(Y2_SENSORLESS, tmc_enable_stallguard(stepperX2)) + }; + #endif + + do_blocking_move_to(1.5 * max_length(X_AXIS) * x_axis_home_dir, 1.5 * max_length(Y_AXIS) * Y_HOME_DIR, current_position.z, + SECONDARY_AXIS_LIST(1.5 * max_length(I_AXIS) * I_HOME_DIR, 1.5 * max_length(J_AXIS) * J_HOME_DIR , 1.5 * max_length(K_AXIS) * K_HOME_DIR, 1.5 * max_length(U_AXIS) * U_HOME_DIR, 1.5 * max_length(V_AXIS) * V_HOME_DIR, 1.5 * max_length(W_AXIS) * W_HOME_DIR), + fr_mm_s + ); + + endstops.validate_homing_move(); + + current_position.set(0.0, 0.0); + + SECONDARY_AXIS_CODE( + current_position.i = 0.0, + current_position.j = 0.0, + current_position.k = 0.0, + current_position.u = 0.0, + current_position.v = 0.0, + current_position.w = 0.0 + ); + + LOOP_NUM_AXES(i) { + planner.set_max_feedrate((AxisEnum)i, old_max_speeds[i]); + } + + #if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT) + TERN_(X_SENSORLESS, tmc_disable_stallguard(stepperX, stealth_states.x)); + TERN_(X2_SENSORLESS, tmc_disable_stallguard(stepperY, stealth_states.y)); + TERN_(Y_SENSORLESS, tmc_disable_stallguard(stepperX2, stealth_states.x2)); + TERN_(Y2_SENSORLESS, tmc_disable_stallguard(stepperY2, stealth_states.y2)); + SECONDARY_AXIS_CODE( + tmc_disable_stallguard(stepperI, stealth_states.i), + tmc_disable_stallguard(stepperJ, stealth_states.j), + tmc_disable_stallguard(stepperK, stealth_states.k), + tmc_disable_stallguard(stepperU, stealth_states.u), + tmc_disable_stallguard(stepperV, stealth_states.v), + tmc_disable_stallguard(stepperW, stealth_states.w) + ); + #endif + } + +#endif // QUICK_HOME_SECONDARY_AXES + #if ENABLED(Z_SAFE_HOMING) inline void home_z_safely() { @@ -396,7 +483,21 @@ void GcodeSuite::G28() { #endif // HAS_Z_AXIS // Diagonal move first if both are homing - TERN_(QUICK_HOME, if (doX && doY) quick_home_xy()); + + #if ENABLED(QUICK_HOME) + if (doX && doY) { + #if ENABLED(QUICK_HOME_SECONDARY_AXES) + // move all axes except Z towards 0 first if all are homing + if (SECONDARY_AXIS_GANG(doI, && doJ, && doK, && doU, && doV, && doW)) + quick_home_xyijkuvw(); + else + quick_home_xy(); + #else + // Diagonal move first if both x and y but not not all secondary axes are homing + quick_home_xy(); + #endif + } + #endif #if HAS_Y_AXIS // Home Y (before X) diff --git a/Marlin/src/gcode/calibrate/G425.cpp b/Marlin/src/gcode/calibrate/G425.cpp index 6338873ea775..7439750171a2 100644 --- a/Marlin/src/gcode/calibrate/G425.cpp +++ b/Marlin/src/gcode/calibrate/G425.cpp @@ -174,7 +174,7 @@ inline void park_above_object(measurements_t &m, const float uncertainty) { #if HAS_HOTEND_OFFSET inline void normalize_hotend_offsets() { - for (uint8_t e = 1; e < HOTENDS; ++e) + for (uint8_t e = 1; e < TOOLS; ++e) hotend_offset[e] -= hotend_offset[0]; hotend_offset[0].reset(); } @@ -617,7 +617,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { // This function requires normalize_hotend_offsets() to be called // inline void report_hotend_offsets() { - for (uint8_t e = 1; e < HOTENDS; ++e) + for (uint8_t e = 1; e < TOOLS; ++e) SERIAL_ECHOLNPGM_P(PSTR("T"), e, PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z); } #endif diff --git a/Marlin/src/gcode/calibrate/M665.cpp b/Marlin/src/gcode/calibrate/M665.cpp index 5409ff42324e..45e165d1b201 100644 --- a/Marlin/src/gcode/calibrate/M665.cpp +++ b/Marlin/src/gcode/calibrate/M665.cpp @@ -207,6 +207,69 @@ SERIAL_ECHOLNPGM_P(PSTR(" M665 S"), segments_per_second); } -#endif // POLAR +#elif ENABLED(PENTA_AXIS_TRT) + + #include "../../module/penta_axis_trt.h" + + /** + * M665: Set PENTA_AXIS_TRT settings + * Parameters: + * S[segments] - Segments-per-second + * X[mrzp_offset_x] - mrzp_offset_x + * Y[mrzp_offset_y] - mrzp_offset_y + * Z[mrzp_offset_z] - mrzp_offset_z + * I[rotational_offset_x] - rotational_offset_x + * J[rotational_offset_y] - rotational_offset_y + * K[rotational_offset_z] - rotational_offset_z + */ + void GcodeSuite::M665() { + if (!parser.seen_any()) return M665_report(); + if (parser.seenval('S')) segments_per_second = parser.value_float(); + if (parser.seenval('X')) mrzp_offset_x = parser.value_linear_units(); + if (parser.seenval('Y')) mrzp_offset_y = parser.value_linear_units(); + if (parser.seenval('Z')) mrzp_offset_z = parser.value_linear_units(); + if (parser.seenval('I')) rotational_offset_x = parser.value_linear_units(); + if (parser.seenval('J')) rotational_offset_y = parser.value_linear_units(); + if (parser.seenval('K')) rotational_offset_z = parser.value_linear_units(); + } + + void GcodeSuite::M665_report(const bool forReplay/*=true*/) { + report_heading_etc(forReplay, F(STR_PAX_TRT_SETTINGS)); + SERIAL_ECHOLNPGM_P( + PSTR(" M665 S"), LINEAR_UNIT(segments_per_second), + PSTR(" X"), LINEAR_UNIT(mrzp_offset_x), + PSTR(" Y"), LINEAR_UNIT(mrzp_offset_y), + PSTR(" Z"), LINEAR_UNIT(mrzp_offset_z), + PSTR(" I"), LINEAR_UNIT(rotational_offset_x), + PSTR(" J"), LINEAR_UNIT(rotational_offset_y), + PSTR(" K"), LINEAR_UNIT(rotational_offset_z) + ); + } + +#elif ENABLED(PENTA_AXIS_HT) + + #include "../../module/penta_axis_ht.h" + + /** + * M665: Set PENTA_AXIS_HT settings + * Parameters: + * S[segments] - Segments-per-second + * Z[mrzp_offset_z] - mrzp_offset_z + */ + void GcodeSuite::M665() { + if (!parser.seen_any()) return M665_report(); + if (parser.seenval('S')) segments_per_second = parser.value_float(); + if (parser.seenval('Z')) mrzp_offset_z = parser.value_linear_units(); + } + + void GcodeSuite::M665_report(const bool forReplay/*=true*/) { + report_heading_etc(forReplay, F(STR_PAX_HT_SETTINGS)); + SERIAL_ECHOLNPGM_P( + PSTR(" M665 S"), LINEAR_UNIT(segments_per_second), + PSTR(" Z"), LINEAR_UNIT(mrzp_offset_z) + ); + } + +#endif // PENTA_AXIS_HT #endif // IS_KINEMATIC diff --git a/Marlin/src/gcode/config/M217.cpp b/Marlin/src/gcode/config/M217.cpp index df275c2d3193..e11b093b86b4 100644 --- a/Marlin/src/gcode/config/M217.cpp +++ b/Marlin/src/gcode/config/M217.cpp @@ -22,7 +22,7 @@ #include "../../inc/MarlinConfigPre.h" -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS #include "../gcode.h" @@ -220,4 +220,4 @@ void GcodeSuite::M217_report(const bool forReplay/*=true*/) { SERIAL_ECHOLNPGM_P(SP_Z_STR, LINEAR_UNIT(toolchange_settings.z_raise)); } -#endif // HAS_MULTI_EXTRUDER +#endif // HAS_MULTI_TOOLS diff --git a/Marlin/src/gcode/config/M218.cpp b/Marlin/src/gcode/config/M218.cpp index 006f9a1d2c1b..5c5903c0206e 100644 --- a/Marlin/src/gcode/config/M218.cpp +++ b/Marlin/src/gcode/config/M218.cpp @@ -66,7 +66,7 @@ void GcodeSuite::M218_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading_etc(forReplay, F(STR_HOTEND_OFFSETS)); - for (uint8_t e = 1; e < HOTENDS; ++e) { + for (uint8_t e = 1; e < TOOLS; ++e) { report_echo_start(forReplay); SERIAL_ECHOLNPGM_P( PSTR(" M218 T"), e, diff --git a/Marlin/src/gcode/control/M211.cpp b/Marlin/src/gcode/control/M211.cpp index 471ca6c448de..431dba1c44a0 100644 --- a/Marlin/src/gcode/control/M211.cpp +++ b/Marlin/src/gcode/control/M211.cpp @@ -27,15 +27,34 @@ #include "../gcode.h" #include "../../module/motion.h" + +#if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + #include "../../module/planner.h" +#endif + /** * M211: Enable, Disable, and/or Report software endstops * - * Usage: M211 S1 to enable, M211 S0 to disable, M211 alone for report + * Parameters + * S: flag to enable (1, default) or disable (0) software endstops. + * H: flag to abort print on software endstops (1) or clamp movement to software endstops (0, default) + * if software endstops are enabled. Requires ABORT_ON_SOFTWARE_ENDSTOP + * + * Report the software endstop status if no parameter is specified, + * + * Examples: + * M211 S0 to disable software endstops + * M211 S1 H1 to enable software endstops and halt printer on the next move beyond software endstops + * M211 S1 H0 to enable software endstops and clamp movement to software endstops (default) */ void GcodeSuite::M211() { if (parser.seen('S')) soft_endstop._enabled = parser.value_bool(); - else + #if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + if (parser.seen('H')) + planner.abort_on_software_endstop = parser.value_bool(); + #endif + if (!(parser.seen('S') || TERN0(HABORT_ON_SOFTWARE_ENDSTOP, parser.seen('H')))) M211_report(); } diff --git a/Marlin/src/gcode/control/M605.cpp b/Marlin/src/gcode/control/M605.cpp index 167cdae4a931..784e29512bc5 100644 --- a/Marlin/src/gcode/control/M605.cpp +++ b/Marlin/src/gcode/control/M605.cpp @@ -142,7 +142,7 @@ "\nDEFAULT_DUPLICATION_X_OFFSET=", DEFAULT_DUPLICATION_X_OFFSET ); - HOTEND_LOOP() { + for (int8_t i = 0; i < TOOLS; i++) DEBUG_ECHOPGM_P(SP_T_STR, e); LOOP_NUM_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", C(AXIS_CHAR(a) | 0x20), "=", hotend_offset[e][a]); DEBUG_EOL(); diff --git a/Marlin/src/gcode/control/T.cpp b/Marlin/src/gcode/control/T.cpp index 3b0c173195d1..558a69031064 100644 --- a/Marlin/src/gcode/control/T.cpp +++ b/Marlin/src/gcode/control/T.cpp @@ -27,7 +27,7 @@ #include "../gcode.h" #include "../../module/tool_change.h" -#if ANY(HAS_MULTI_EXTRUDER, DEBUG_LEVELING_FEATURE) +#if ANY(HAS_MULTI_TOOLS, DEBUG_LEVELING_FEATURE) #include "../../module/motion.h" #endif @@ -54,7 +54,7 @@ */ void GcodeSuite::T(const int8_t tool_index) { - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS // For 'T' with no parameter report the current tool. if (parser.string_arg && *parser.string_arg == '*') { SERIAL_ECHOLNPGM(STR_ACTIVE_EXTRUDER, active_extruder); @@ -81,7 +81,7 @@ void GcodeSuite::T(const int8_t tool_index) { #endif tool_change(tool_index - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS , parser.boolval('S') || TERN(PARKING_EXTRUDER, false, tool_index == active_extruder) // For PARKING_EXTRUDER motion is decided in tool_change() #endif diff --git a/Marlin/src/gcode/feature/fwretract/G10_G11.cpp b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp index 1889f83d6211..c5346789e2a2 100644 --- a/Marlin/src/gcode/feature/fwretract/G10_G11.cpp +++ b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp @@ -21,18 +21,146 @@ */ #include "../../../inc/MarlinConfig.h" - #if ENABLED(FWRETRACT) + #include "../../feature/fwretract.h" +#endif -#include "../../../feature/fwretract.h" -#include "../../gcode.h" -#include "../../../module/motion.h" +#if ANY(FWRETRACT, CNC_COORDINATE_SYSTEMS) + #include "../../gcode.h" + #include "../../../module/motion.h" /** - * G10 - Retract filament according to settings of M207 - * TODO: Handle 'G10 P' for tool settings and 'G10 L' for workspace settings + * G10 + * S# - Retract filament according to settings of M207 + * L1 P# X# Y# Z#... - Sets the tool length of the tool specified by the P parameter (0 to (TOOLS - 1)) + * L10 P# Z# - Set the tool length of the tool specified by the P parameter (0 to (TOOLS - 1)) so that if the tool offset is reloaded, with the machine in its + * current position and with the current G5x and G52/G92 offsets active, the current coordinates for the given axes + * will become the given Z value. + * L2/L20 P# X# Y# Z#... - Sets the origin of the coordinate system, specified by the P parameter (1 to 9) + * L2/L20 P0 X# Y# Z#... - Sets the origin of the current coordinate system + * + * + * Beta L2/L20 implementation based on https://github.com/MarlinFirmware/Marlin/issues/14734 + * and http://linuxcnc.org/docs/2.6/html/gcode/gcode.html#sec:G10-L1_ + * + * !Beta - Use with caution. */ -void GcodeSuite::G10() { fwretract.retract(true E_OPTARG(parser.boolval('S'))); } + +void GcodeSuite::G10() { + #if ENABLED(FWRETRACT) + fwretract.retract(true E_OPTARG(parser.boolval('S'))); + #endif + + #if ANY(CNC_COORDINATE_SYSTEMS, HAS_TOOL_LENGTH_COMPENSATION) + uint8_t index, + offset_type; + + if (parser.seenval('P')) + index = parser.value_byte(); + else + return; + if (parser.seenval('L')) + offset_type = parser.value_byte(); + else + return; + + #if HAS_HOTEND_OFFSET && HAS_TOOL_LENGTH_COMPENSATION + if ((offset_type == 1 || offset_type == 10) && WITHIN(index, 1, TOOLS)) { + switch (offset_type) { + + default: break; // Ignore unknown G10 Lx + + case 1: // Sets the tool offset + LOOP_NUM_AXES(i) { + if (parser.seen(axis_codes[i])) { + const float axis_value = parser.value_axis_units((AxisEnum)i); + if (i == Z_AXIS) { + hotend_offset[index][i] = -axis_value; + } + else { + hotend_offset[index][i] = axis_value; + } + } + } + break; + + // G10 L10 changes the tool table entry for tool P so that if the tool offset is reloaded, with the machine in its + // current position and with the current G5x and G52/G92 offsets active, the current coordinates for the given axes + // will become the given values. (Intended to work the same as LinuxCNC). + // e.g. G10 L10 P1 Z1.5 followed by G43 sets the current position for Z to be 1.5. + case 10: + LOOP_NUM_AXES(i) { + if (parser.seen(axis_codes[i])) { + const float axis_value = parser.value_axis_units((AxisEnum)i); + hotend_offset[index][i] = current_position[i] - axis_value; + } + } + break; + } + } + #endif + #if ENABLED(CNC_COORDINATE_SYSTEMS) + if (offset_type == 2 || offset_type == 20) { + const int8_t target_system = (index == 0) ? gcode.active_coordinate_system : (index - 1); // P0 selects current coordinate system. P1 is G54, which is Marlin coordinate_system 0 + const int8_t current_system = gcode.active_coordinate_system; // Store current coord system + if (WITHIN(target_system, 0, MAX_COORDINATE_SYSTEMS - 1)) { + if (current_system != target_system) { + gcode.select_coordinate_system(target_system); // Select new coordinate system if needed + #if ENABLED(DEBUG_G10) + SERIAL_ECHOLNPGM("Switching to workspace ", target_system); + report_current_position(); + #endif + } + } + switch (offset_type) { + default: break; // Ignore unknown G10 Lx + + // Sets the work offsets of the specified (P[1-9]) coordinate system, by subtracting the specified X, Y, Z... values + // from the current machine coordinate X, Y, Z... values (Works the same as LinuxCNC) + // eg: If G53's X=50, Y=100 then G10 P1 L2 X10 Y2 will set G54's X=40, Y=98 + case 2: + LOOP_LOGICAL_AXES(i) { + if (parser.seenval(axis_codes[i])) { + if (TERN1(HAS_EXTRUDERS, i != E_AXIS)) { + const float axis_shift = parser.value_axis_units((AxisEnum)i); + workspace_offset[i] = -axis_shift; + } + } + } + break; + + // Sets the work coordinates of the specified (P[1-9]) coordinate system, by matching the specified X, Y, Z... values + // Works similar to G92, except you can specifiy a coordinate system directly (Works the same as LinuxCNC) + // eg: G10 P2 L20 X10 Y50 will set G55's X=10, Y=50 no matter which coordinate system is currently selected + case 20: + LOOP_LOGICAL_AXES(i) { + if (parser.seenval(axis_codes[i])) { + if (TERN1(HAS_EXTRUDERS, i != E_AXIS)) { + const float axis_value = parser.value_axis_units((AxisEnum)i); + workspace_offset[i] = axis_value - current_position[i]; + } + } + } + break; + } + if (WITHIN(active_coordinate_system, 0, MAX_COORDINATE_SYSTEMS - 1)) { + coordinate_system[active_coordinate_system] = workspace_offset; + } + #if ENABLED(DEBUG_G10) + SERIAL_ECHOLNPGM("New position for workspace ", target_system); + report_current_position(); + #endif + if (current_system != target_system) { + gcode.select_coordinate_system(current_system); + } + } + #endif // ENABLED(CNC_COORDINATE_SYSTEMS) + #endif // ANY(CNC_COORDINATE_SYSTEMS, HAS_TOOL_LENGTH_COMPENSATION) +} +#endif // ANY(FWRETRACT, CNC_COORDINATE_SYSTEMS) + + +#if ENABLED(FWRETRACT) /** * G11 - Recover filament according to settings of M208 diff --git a/Marlin/src/gcode/feature/pause/M600.cpp b/Marlin/src/gcode/feature/pause/M600.cpp index 293179ba6f7e..2a2d558e84c7 100644 --- a/Marlin/src/gcode/feature/pause/M600.cpp +++ b/Marlin/src/gcode/feature/pause/M600.cpp @@ -34,7 +34,7 @@ #include "../../../lcd/sovol_rts/sovol_rts.h" #endif -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS #include "../../../module/tool_change.h" #endif @@ -124,7 +124,7 @@ void GcodeSuite::M600() { // If needed, home before parking for filament change TERN_(HOME_BEFORE_FILAMENT_CHANGE, home_if_needed(true)); - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS // Change toolhead if specified const uint8_t active_extruder_before_filament_change = active_extruder; if (active_extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !idex_is_duplicating())) @@ -186,7 +186,7 @@ void GcodeSuite::M600() { } } - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS // Restore toolhead if it was changed if (active_extruder_before_filament_change != active_extruder) tool_change(active_extruder_before_filament_change); diff --git a/Marlin/src/gcode/feature/pause/M701_M702.cpp b/Marlin/src/gcode/feature/pause/M701_M702.cpp index 29c8fe9a4524..8bf4756c2467 100644 --- a/Marlin/src/gcode/feature/pause/M701_M702.cpp +++ b/Marlin/src/gcode/feature/pause/M701_M702.cpp @@ -31,7 +31,7 @@ #include "../../../feature/pause.h" #include "../../../lcd/marlinui.h" -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS #include "../../../module/tool_change.h" #endif @@ -83,7 +83,7 @@ void GcodeSuite::M701() { // Show initial "wait for load" message ui.pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder); - #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU) + #if (HAS_MULTI_TOOLS) && (HAS_PRUSA_MMU1 || !HAS_MMU) // Change toolhead if specified uint8_t active_extruder_before_filament_change = active_extruder; if (active_extruder != target_extruder) @@ -125,7 +125,7 @@ void GcodeSuite::M701() { // Restore Z axis move_z_by(-park_raise); - #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU) + #if (HAS_MULTI_TOOLS) && (HAS_PRUSA_MMU1 || !HAS_MMU) // Restore toolhead if it was changed if (active_extruder_before_filament_change != active_extruder) tool_change(active_extruder_before_filament_change); @@ -188,7 +188,7 @@ void GcodeSuite::M702() { // Show initial "wait for unload" message ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder); - #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU) + #if (HAS_MULTI_TOOLS) && (HAS_PRUSA_MMU1 || !HAS_MMU) // Change toolhead if specified uint8_t active_extruder_before_filament_change = active_extruder; if (active_extruder != target_extruder) @@ -205,7 +205,7 @@ void GcodeSuite::M702() { #elif HAS_PRUSA_MMU2 mmu2.unload(); #else - #if ALL(HAS_MULTI_EXTRUDER, FILAMENT_UNLOAD_ALL_EXTRUDERS) + #if ALL(HAS_MULTI_EXTRUDER, FILAMENT_UNLOAD_ALL_EXTRUDERS) || ALL(HAS_MULTI_TOOLS, FILAMENT_UNLOAD_ALL_EXTRUDERS) if (!parser.seenval('T')) { HOTEND_LOOP() { if (e != active_extruder) tool_change(e); @@ -231,7 +231,7 @@ void GcodeSuite::M702() { if (park_point.z > 0) do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); - #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU) + #if (HAS_MULTI_TOOLS) && (HAS_PRUSA_MMU1 || !HAS_MMU) // Restore toolhead if it was changed if (active_extruder_before_filament_change != active_extruder) tool_change(active_extruder_before_filament_change); diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 1ccb10753cbc..3f6a7e156a76 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -746,7 +746,7 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { case 211: M211(); break; // M211: Enable, Disable, and/or Report software endstops #endif - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS case 217: M217(); break; // M217: Set filament swap parameters #endif diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 421dda9aec0f..b13ad5c430c6 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -523,8 +523,11 @@ class GcodeSuite { static void G6(); #endif - #if ENABLED(FWRETRACT) +#if ANY(FWRETRACT, CNC_COORDINATE_SYSTEMS) static void G10(); + #endif + + #if ENABLED(FWRETRACT) static void G11(); #endif @@ -900,7 +903,7 @@ class GcodeSuite { static void M211(); static void M211_report(const bool forReplay=true); - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS static void M217(); static void M217_report(const bool forReplay=true); #endif diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp index 3975cea50488..d686d0164834 100644 --- a/Marlin/src/gcode/parser.cpp +++ b/Marlin/src/gcode/parser.cpp @@ -190,7 +190,7 @@ void GCodeParser::parse(char *p) { // Bail if there's no command code number if (!TERN(SIGNED_CODENUM, NUMERIC_SIGNED(*p), NUMERIC(*p))) { - if (TERN0(HAS_MULTI_EXTRUDER, letter == 'T')) { + if (TERN0(HAS_MULTI_TOOLS, letter == 'T')) { p[0] = '*'; p[1] = '\0'; string_arg = p; // Convert 'T' alone into 'T*' command_letter = letter; } diff --git a/Marlin/src/gcode/probe/G30.cpp b/Marlin/src/gcode/probe/G30.cpp index 29d6c1ad2ba7..f25648f9115e 100644 --- a/Marlin/src/gcode/probe/G30.cpp +++ b/Marlin/src/gcode/probe/G30.cpp @@ -26,6 +26,7 @@ #include "../gcode.h" #include "../../module/motion.h" +#include "../../MarlinCore.h" #include "../../module/probe.h" #include "../../feature/bedlevel/bedlevel.h" #include "../../lcd/marlinui.h" @@ -50,6 +51,8 @@ */ void GcodeSuite::G30() { + if (!MOTION_CONDITIONS) return; + xy_pos_t probepos = current_position; const bool seenX = parser.seenval('X'); diff --git a/Marlin/src/gcode/probe/G38.cpp b/Marlin/src/gcode/probe/G38.cpp index d57eb9b59e9d..635ea4bc2cbb 100644 --- a/Marlin/src/gcode/probe/G38.cpp +++ b/Marlin/src/gcode/probe/G38.cpp @@ -28,6 +28,7 @@ #include "../../module/endstops.h" #include "../../module/motion.h" +#include "../../MarlinCore.h" #include "../../module/planner.h" #include "../../module/probe.h" @@ -106,6 +107,8 @@ inline bool G38_run_probe() { */ void GcodeSuite::G38(const int8_t subcode) { + if (!MOTION_CONDITIONS) return; + // Get X Y Z E F get_destination_from_command(); diff --git a/Marlin/src/inc/Conditionals-1-axes.h b/Marlin/src/inc/Conditionals-1-axes.h index 1ea88cec865c..7e71d13bb572 100644 --- a/Marlin/src/inc/Conditionals-1-axes.h +++ b/Marlin/src/inc/Conditionals-1-axes.h @@ -47,6 +47,7 @@ #define HAS_EXTRUDERS 1 #if EXTRUDERS > 1 #define HAS_MULTI_EXTRUDER 1 + #define HAS_MULTI_TOOLS 1 #endif #define E_AXIS_N(E) AxisEnum(E_AXIS + E_INDEX_N(E)) #else @@ -198,6 +199,9 @@ // More than one hotend... #if HOTENDS > 1 #define HAS_MULTI_HOTEND 1 + #ifndef HAS_MULTI_TOOLS + #define HAS_MULTI_TOOLS 1 + #endif #define HAS_HOTEND_OFFSET 1 #ifndef HOTEND_OFFSET_X #define HOTEND_OFFSET_X { 0 } // X offsets for each extruder @@ -214,6 +218,23 @@ #undef HOTEND_OFFSET_Z #endif +/** + * Tools include non-extruder/hotend-tools and extruder/hotend-tools. Non-extruder/hotend-tools have no stepper motor and no hotend. + * + * TOOLS - Number of Selectable Tools + */ +#if !defined(TOOLS) + #define TOOLS HOTENDS +#else + #if TOOLS > 0 + #define HAS_TOOL_LENGTH_COMPENSATION 1 + #endif + #if TOOLS > 1 + #define HAS_MULTI_TOOLS 1 + #define HAS_HOTEND_OFFSET 1 + #endif +#endif + /** * Number of Linear Axes (e.g., XYZIJKUVW) * All the logical axes except for the tool (E) axis diff --git a/Marlin/src/inc/Conditionals-3-etc.h b/Marlin/src/inc/Conditionals-3-etc.h index 3c685ad58720..66e94c812222 100644 --- a/Marlin/src/inc/Conditionals-3-etc.h +++ b/Marlin/src/inc/Conditionals-3-etc.h @@ -151,7 +151,7 @@ #endif // Probing tool change -#if !HAS_MULTI_EXTRUDER +#if !HAS_MULTI_TOOLS #undef PROBING_TOOL #endif #if HAS_BED_PROBE && defined(PROBING_TOOL) @@ -642,6 +642,9 @@ #define IS_KINEMATIC 1 #elif ANY(DELTA, POLARGRAPH, POLAR) #define IS_KINEMATIC 1 +#elif ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + #define IS_KINEMATIC 1 + #define HAS_TOOL_CENTERPOINT_CONTROL 1 #else #define IS_CARTESIAN 1 #if !IS_CORE diff --git a/Marlin/src/inc/Conditionals-4-adv.h b/Marlin/src/inc/Conditionals-4-adv.h index 1401dab57031..21a1857ba231 100644 --- a/Marlin/src/inc/Conditionals-4-adv.h +++ b/Marlin/src/inc/Conditionals-4-adv.h @@ -868,7 +868,7 @@ #undef WATCH_COOLER_TEMP_INCREASE #endif -#if HAS_MULTI_EXTRUDER || HAS_MULTI_HOTEND || HAS_PRUSA_MMU2 || HAS_PRUSA_MMU3 || (ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1) +#if HAS_MULTI_TOOLS || HAS_MULTI_HOTEND || HAS_PRUSA_MMU2 || HAS_PRUSA_MMU3 || (ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1) #define HAS_TOOLCHANGE 1 #endif @@ -1065,6 +1065,9 @@ // #if ANY(SPINDLE_FEATURE, LASER_FEATURE) #define HAS_CUTTER 1 + #if ENABLED(LASER_FEATURE) + #define LASER_TOOL EXTRUDERS + #endif #define _CUTTER_POWER_PWM255 1 #define _CUTTER_POWER_PERCENT 2 #define _CUTTER_POWER_RPM 3 @@ -1431,7 +1434,7 @@ #endif // Toolchange Event G-code -#if !HAS_MULTI_EXTRUDER || !(defined(EVENT_GCODE_TOOLCHANGE_T0) || defined(EVENT_GCODE_TOOLCHANGE_T1) || defined(EVENT_GCODE_TOOLCHANGE_T2) || defined(EVENT_GCODE_TOOLCHANGE_T3) || defined(EVENT_GCODE_TOOLCHANGE_T4) || defined(EVENT_GCODE_TOOLCHANGE_T5) || defined(EVENT_GCODE_TOOLCHANGE_T6) || defined(EVENT_GCODE_TOOLCHANGE_T7)) +#if !HAS_MULTI_TOOLS || !(defined(EVENT_GCODE_TOOLCHANGE_T0) || defined(EVENT_GCODE_TOOLCHANGE_T1) || defined(EVENT_GCODE_TOOLCHANGE_T2) || defined(EVENT_GCODE_TOOLCHANGE_T3) || defined(EVENT_GCODE_TOOLCHANGE_T4) || defined(EVENT_GCODE_TOOLCHANGE_T5) || defined(EVENT_GCODE_TOOLCHANGE_T6) || defined(EVENT_GCODE_TOOLCHANGE_T7)) #undef TC_GCODE_USE_GLOBAL_X #undef TC_GCODE_USE_GLOBAL_Y #undef TC_GCODE_USE_GLOBAL_Z diff --git a/Marlin/src/inc/Conditionals-5-post.h b/Marlin/src/inc/Conditionals-5-post.h index 4884d7df0a9b..ce0b0272c777 100644 --- a/Marlin/src/inc/Conditionals-5-post.h +++ b/Marlin/src/inc/Conditionals-5-post.h @@ -3243,7 +3243,7 @@ #if ENABLED(POLAR) #undef MIN_SOFTWARE_ENDSTOP_Y #undef MAX_SOFTWARE_ENDSTOP_Y -#elif IS_KINEMATIC +#elif IS_KINEMATIC && NONE(PENTA_AXIS_TRT, PENTA_AXIS_HT) #undef MIN_SOFTWARE_ENDSTOP_X #undef MIN_SOFTWARE_ENDSTOP_Y #undef MAX_SOFTWARE_ENDSTOP_X @@ -3254,7 +3254,7 @@ * Bed Probing bounds */ -#if IS_KINEMATIC +#if IS_KINEMATIC && NONE(PENTA_AXIS_TRT, PENTA_AXIS_HT) #undef PROBING_MARGIN_LEFT #undef PROBING_MARGIN_RIGHT #undef PROBING_MARGIN_FRONT diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 5f7be17b9b5a..d917a5009b03 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -893,7 +893,11 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L */ #if ANY(SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD) constexpr float thpx[] = SWITCHING_TOOLHEAD_X_POS; - static_assert(COUNT(thpx) == EXTRUDERS, "SWITCHING_TOOLHEAD_X_POS must be an array EXTRUDERS long."); + #if HAS_TOOL_LENGTH_COMPENSATION + static_assert(COUNT(thpx) == TOOLS, "SWITCHING_TOOLHEAD_X_POS must be an array TOOLS long."); + #else + static_assert(COUNT(thpx) == EXTRUDERS, "SWITCHING_TOOLHEAD_X_POS must be an array EXTRUDERS long."); + #endif #endif /** @@ -902,7 +906,9 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L #if ENABLED(SWITCHING_TOOLHEAD) #ifndef SWITCHING_TOOLHEAD_SERVO_NR #error "SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_SERVO_NR." - #elif EXTRUDERS < 2 + #elif HAS_TOOL_LENGTH_COMPENSATION && (TOOLS < 2) + #error "SWITCHING_TOOLHEAD requires at least 2 TOOLS." + #elif (!HAS_TOOL_LENGTH_COMPENSATION) && (EXTRUDERS < 2) #error "SWITCHING_TOOLHEAD requires at least 2 EXTRUDERS." #elif NUM_SERVOS < (SWITCHING_TOOLHEAD_SERVO_NR - 1) #if SWITCHING_TOOLHEAD_SERVO_NR == 0 @@ -1174,8 +1180,8 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i /** * Allow only one kinematic type to be defined */ -#if MANY(DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, POLAR) - #error "Please enable only one of DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, or POLAR." +#if MANY(DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, POLAR, PENTA_AXIS_TRT, PENTA_AXIS_HT) + #error "Please enable only one of DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, POLAR, PENTA_AXIS_TRT or PENTA_AXIS_HT." #endif /** @@ -2241,7 +2247,7 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i #endif // HAS_MULTI_HOTEND #endif // HAS_HOTEND -#if DO_TOOLCHANGE_FOR_PROBING && PROBING_TOOL >= EXTRUDERS +#if DO_TOOLCHANGE_FOR_PROBING && PROBING_TOOL >= TOOLS #error "PROBING_TOOL must be a valid tool index." #endif @@ -3910,9 +3916,7 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive." #endif #define _PIN_CONFLICT(P) (PIN_EXISTS(P) && P##_PIN == SPINDLE_LASER_PWM_PIN) - #if ALL(SPINDLE_FEATURE, LASER_FEATURE) - #error "Enable only one of SPINDLE_FEATURE or LASER_FEATURE." - #elif NONE(SPINDLE_SERVO, SPINDLE_LASER_USE_PWM) && !PIN_EXISTS(SPINDLE_LASER_ENA) + #if NONE(SPINDLE_SERVO, SPINDLE_LASER_USE_PWM) && !PIN_EXISTS(SPINDLE_LASER_ENA) #error "(SPINDLE|LASER)_FEATURE requires SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_USE_PWM, or SPINDLE_SERVO to control the power." #elif ENABLED(SPINDLE_CHANGE_DIR) && !PIN_EXISTS(SPINDLE_DIR) #error "SPINDLE_DIR_PIN is required for SPINDLE_CHANGE_DIR." diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp index b8e11b5b4d34..1e0029a94ea4 100644 --- a/Marlin/src/lcd/marlinui.cpp +++ b/Marlin/src/lcd/marlinui.cpp @@ -831,7 +831,7 @@ void MarlinUI::init() { #if IS_KINEMATIC - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS REMEMBER(ae, active_extruder); #if MULTI_E_MANUAL if (axis == E_AXIS) active_extruder = e_index; diff --git a/Marlin/src/lcd/menu/menu_advanced.cpp b/Marlin/src/lcd/menu/menu_advanced.cpp index e1072f3ee41c..fb8e1f35fb9f 100644 --- a/Marlin/src/lcd/menu/menu_advanced.cpp +++ b/Marlin/src/lcd/menu/menu_advanced.cpp @@ -731,6 +731,11 @@ void menu_advanced_settings() { EDIT_ITEM(bool, MSG_ENDSTOP_ABORT, &planner.abort_on_endstop_hit); #endif + // M211 H - Abort on software endstop hit + #if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + EDIT_ITEM(bool, MSG_SOFTWARE_ENDSTOP_ABORT, &planner.abort_on_software_endstop); + #endif + #if ENABLED(SD_FIRMWARE_UPDATE) EDIT_ITEM(bool, MSG_MEDIA_UPDATE, &sd_update_state, []{ // diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 999e07ea7431..98471094f41c 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -58,6 +58,10 @@ #include "probe.h" #endif +#if ANY(SD_ABORT_ON_ENDSTOP_HIT, ABORT_ON_SOFTWARE_ENDSTOP) && HAS_CUTTER + #include "../feature/spindle_laser.h" +#endif + #define DEBUG_OUT ALL(USE_SENSORLESS, DEBUG_LEVELING_FEATURE) #include "../core/debug_out.h" @@ -394,6 +398,21 @@ void Endstops::event_handler() { #endif print_job_timer.stop(); } + + #elif ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + if (planner.abort_on_software_endstop) { + TERN_(ADVANCED_PAUSE_FEATURE, did_pause_print = 0); + quickstop_stepper(); + #if HAS_CUTTER + TERN_(SPINDLE_FEATURE, safe_delay(1000)); + cutter.kill(); + #endif + #ifdef SD_ABORT_ON_ENDSTOP_HIT_GCODE + queue.clear(); + queue.inject(F(SD_ABORT_ON_ENDSTOP_HIT_GCODE)); + #endif + stop(); + } #endif } } diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index af26cfff02ac..81ee3bc59a71 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -70,6 +70,10 @@ #include "../feature/babystep.h" #endif +#if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) && HAS_CUTTER + #include "../feature/spindle_laser.h" +#endif + #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) #include "../core/debug_out.h" @@ -114,7 +118,7 @@ xyze_pos_t destination; // {0} #endif // The active extruder (tool). Set with T command. -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS uint8_t active_extruder = 0; // = 0 #endif @@ -124,15 +128,19 @@ xyze_pos_t destination; // {0} // Extruder offsets #if HAS_HOTEND_OFFSET - xyz_pos_t hotend_offset[HOTENDS]; // Initialized by settings.load() + xyz_pos_t hotend_offset[TOOLS]; // Initialized by settings.load() void reset_hotend_offsets() { - constexpr float tmp[XYZ][HOTENDS] = { HOTEND_OFFSET_X, HOTEND_OFFSET_Y, HOTEND_OFFSET_Z }; + constexpr float tmp[XYZ][TOOLS] = { HOTEND_OFFSET_X, HOTEND_OFFSET_Y, HOTEND_OFFSET_Z }; static_assert( !tmp[X_AXIS][0] && !tmp[Y_AXIS][0] && !tmp[Z_AXIS][0], "Offsets for the first hotend must be 0.0." ); // Transpose from [XYZ][HOTENDS] to [HOTENDS][XYZ] - HOTEND_LOOP() LOOP_ABC(a) hotend_offset[e][a] = tmp[a][e]; + for (uint8_t e = 0; e < TOOLS; e++) { + for (uint8_t a = 0; a < XYZ; a++) { + hotend_offset[e][a] = tmp[a][e]; + } + } TERN_(DUAL_X_CARRIAGE, hotend_offset[1].x = _MAX(X2_HOME_POS, X2_MAX_POS)); } #endif @@ -150,9 +158,13 @@ int16_t feedrate_percentage = 100; // Cartesian conversion result goes here: xyz_pos_t cartes; +#if HAS_TOOL_LENGTH_COMPENSATION + bool simple_tool_length_compensation = false; +#endif + #if IS_KINEMATIC - abce_pos_t delta; + abce_pos_t delta = LOGICAL_AXIS_ARRAY(0, X_HOME_POS, Y_HOME_POS, Z_INIT_POS, I_HOME_POS, J_HOME_POS, K_HOME_POS, U_HOME_POS, V_HOME_POS, W_HOME_POS); #if HAS_SCARA_OFFSET abc_pos_t scara_home_offset; @@ -171,6 +183,11 @@ xyz_pos_t cartes; delta_max_radius_2 = sq(PRINTABLE_RADIUS); #endif + + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + bool tool_centerpoint_control = true; + #endif + #endif /** @@ -871,7 +888,7 @@ void report_current_position_projected() { #endif // REALTIME_REPORTING_COMMANDS -#if IS_KINEMATIC +#if IS_KINEMATIC && NONE(PENTA_AXIS_TRT, PENTA_AXIS_HT) bool position_is_reachable(const_float_t rx, const_float_t ry, const float inset/*=0*/) { @@ -939,6 +956,28 @@ void report_current_position_projected() { #endif // CARTESIAN +#if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + bool position_is_reachable_xyijkuvw(NUM_AXIS_LIST(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t ri, const_float_t rj, const_float_t rk, const_float_t ru, const_float_t rv, const_float_t rw)) { + + const bool can_reach = ( + NUM_AXIS_GANG( + COORDINATE_OKAY(rx, X_MIN_POS - fslop, X_MAX_POS + fslop), + && COORDINATE_OKAY(ry, Y_MIN_POS - fslop, Y_MAX_POS + fslop), + && true, + && COORDINATE_OKAY(ri, I_MIN_POS - fslop, I_MAX_POS + fslop), + && COORDINATE_OKAY(rj, J_MIN_POS - fslop, J_MAX_POS + fslop), + && COORDINATE_OKAY(rk, K_MIN_POS - fslop, K_MAX_POS + fslop), + && COORDINATE_OKAY(ru, U_MIN_POS - fslop, U_MAX_POS + fslop), + && COORDINATE_OKAY(rv, V_MIN_POS - fslop, V_MAX_POS + fslop), + && COORDINATE_OKAY(rw, W_MIN_POS - fslop, W_MAX_POS + fslop) + ) + ); + + return can_reach; + } +#endif + + void home_if_needed(const bool keeplev/*=false*/) { if (!all_axes_trusted()) gcode.home_all_axes(keeplev); } @@ -1136,10 +1175,13 @@ void do_blocking_move_to(NUM_AXIS_ARGS_(const_float_t) const_feedRate_t fr_mm_s/ const feedRate_t z_feedrate = fr_mm_s ?: homing_feedrate(Z_AXIS); #endif - #if IS_KINEMATIC && DISABLED(POLARGRAPH) + #if IS_KINEMATIC && NONE(POLARGRAPH, PENTA_AXIS_TRT, PENTA_AXIS_HT) // kinematic machines are expected to home to a point 1.5x their range? never reachable. if (!position_is_reachable(x, y)) return; destination = current_position; // sync destination at the start + #elif ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + if (!position_is_reachable_xyijkuvw(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w))) return; + destination = current_position; // sync destination at the start #endif #if ENABLED(DELTA) @@ -1498,6 +1540,51 @@ void restore_feedrate_and_scaling() { SERIAL_ECHOLNPGM("Axis ", C(AXIS_CHAR(axis)), " min:", soft_endstop.min[axis], " max:", soft_endstop.max[axis]); } + + void handle_min_software_endstop(const AxisEnum axis, xyz_pos_t &target_pos) { + #if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + if (planner.abort_on_software_endstop) { + if (target_pos[axis] < soft_endstop.min[axis]) { + NOLESS(target_pos[axis], soft_endstop.min[axis]); + SERIAL_ERROR_MSG(STR_ERR_SW_ENDSTOP); + quickstop_stepper(); + #if HAS_CUTTER + TERN_(SPINDLE_FEATURE, safe_delay(1000)); + cutter.kill(); + #endif + stop(); + } + else { + NOLESS(target_pos[axis], soft_endstop.min[axis]); + } + } + #else + NOLESS(target_pos[axis], soft_endstop.min[axis]); + #endif + } + + void handle_max_software_endstop(const AxisEnum axis, xyz_pos_t &target_pos) { + #if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + if (planner.abort_on_software_endstop) { + if (target_pos[axis] > soft_endstop.max[axis]) { + NOMORE(target_pos[axis], soft_endstop.max[axis]); + SERIAL_ERROR_MSG(STR_ERR_SW_ENDSTOP); + quickstop_stepper(); + #if HAS_CUTTER + TERN_(SPINDLE_FEATURE, safe_delay(1000)); + cutter.kill(); + #endif + stop(); + } + else { + NOMORE(target_pos[axis], soft_endstop.max[axis]); + } + } + #else + NOMORE(target_pos[axis], soft_endstop.max[axis]); + #endif + } + /** * Constrain the given coordinates to the software endstops. * @@ -1508,7 +1595,7 @@ void restore_feedrate_and_scaling() { if (!soft_endstop._enabled) return; - #if IS_KINEMATIC + #if IS_KINEMATIC && NONE(PENTA_AXIS_HT, PENTA_AXIS_TRT) if (TERN0(DELTA, !all_axes_homed())) return; @@ -1542,10 +1629,10 @@ void restore_feedrate_and_scaling() { #if HAS_X_AXIS if (axis_was_homed(X_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_X) - NOLESS(target.x, soft_endstop.min.x); + handle_min_software_endstop(X_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_X) - NOMORE(target.x, soft_endstop.max.x); + handle_max_software_endstop(X_AXIS, target); #endif } #endif @@ -1553,10 +1640,10 @@ void restore_feedrate_and_scaling() { #if HAS_Y_AXIS if (axis_was_homed(Y_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_Y) - NOLESS(target.y, soft_endstop.min.y); + handle_min_software_endstop(Y_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_Y) - NOMORE(target.y, soft_endstop.max.y); + handle_max_software_endstop(Y_AXIS, target); #endif } #endif @@ -1566,70 +1653,70 @@ void restore_feedrate_and_scaling() { #if HAS_Z_AXIS if (axis_was_homed(Z_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_Z) - NOLESS(target.z, soft_endstop.min.z); + handle_min_software_endstop(Z_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_Z) - NOMORE(target.z, soft_endstop.max.z); + handle_max_software_endstop(Z_AXIS, target); #endif } #endif #if HAS_I_AXIS if (axis_was_homed(I_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_I) - NOLESS(target.i, soft_endstop.min.i); + handle_min_software_endstop(I_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_I) - NOMORE(target.i, soft_endstop.max.i); + handle_max_software_endstop(I_AXIS, target); #endif } #endif #if HAS_J_AXIS if (axis_was_homed(J_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_J) - NOLESS(target.j, soft_endstop.min.j); + handle_min_software_endstop(J_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_J) - NOMORE(target.j, soft_endstop.max.j); + handle_max_software_endstop(J_AXIS, target); #endif } #endif #if HAS_K_AXIS if (axis_was_homed(K_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_K) - NOLESS(target.k, soft_endstop.min.k); + handle_min_software_endstop(K_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_K) - NOMORE(target.k, soft_endstop.max.k); + handle_max_software_endstop(K_AXIS, target); #endif } #endif #if HAS_U_AXIS if (axis_was_homed(U_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_U) - NOLESS(target.u, soft_endstop.min.u); + handle_min_software_endstop(U_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_U) - NOMORE(target.u, soft_endstop.max.u); + handle_max_software_endstop(U_AXIS, target); #endif } #endif #if HAS_V_AXIS if (axis_was_homed(V_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_V) - NOLESS(target.v, soft_endstop.min.v); + handle_min_software_endstop(V_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_V) - NOMORE(target.v, soft_endstop.max.v); + handle_max_software_endstop(V_AXIS, target); #endif } #endif #if HAS_W_AXIS if (axis_was_homed(W_AXIS)) { #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MIN_SOFTWARE_ENDSTOP_W) - NOLESS(target.w, soft_endstop.min.w); + handle_min_software_endstop(W_AXIS, target); #endif #if !HAS_SOFTWARE_ENDSTOPS || ENABLED(MAX_SOFTWARE_ENDSTOP_W) - NOMORE(target.w, soft_endstop.max.w); + handle_max_software_endstop(W_AXIS, target); #endif } #endif @@ -1783,14 +1870,35 @@ float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool const xyze_float_t diff = destination - current_position; - // If the move is only in Z/E don't split up the move - if (!diff.x && !diff.y) { + + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + // If the move is only in X/Y/Z/E don't split up the move + if ((!tool_centerpoint_control) || (NEAR_ZERO(diff.i) && TERN1(HAS_J_AXIS, NEAR_ZERO(diff.j)))) { + #else + // If the move is only in Z/E don't split up the move + if (!diff.x && !diff.y) { + #endif planner.buffer_line(destination, scaled_fr_mm_s); return false; // caller will update current_position } // Fail if attempting move outside printable radius - if (!position_is_reachable(destination)) return true; + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + // Abort if attempting move outside printable radius + if (!position_is_reachable(destination)) { + SERIAL_ERROR_MSG("Position not reachable."); + quickstop_stepper(); + #if HAS_CUTTER + TERN_(SPINDLE_FEATURE, safe_delay(1000)); + cutter.kill(); + #endif + stop(); + return true; + } + #else + // Fail if attempting move outside printable radius + if (!position_is_reachable(destination)) return true; + #endif // Get the linear distance in XYZ #if HAS_ROTATIONAL_AXES @@ -3057,6 +3165,12 @@ void set_axis_is_at_home(const AxisEnum axis) { current_position[axis] = (axis == Z_AXIS) ? DIFF_TERN(HAS_BED_PROBE, delta_height, probe.offset.z) : base_home_pos(axis); #else current_position[axis] = SUM_TERN(HAS_HOME_OFFSET, base_home_pos(axis), home_offset[axis]); + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + // TODO (DerAndere): Introduce a function like scara_set_axis_is_at_home. + delta[axis] = current_position[axis]; + if (axis == J_AXIS) + delta = SUM_TERN(HAS_HOME_OFFSET, base_home_pos(axis), home_offset[axis]); + #endif #endif /** diff --git a/Marlin/src/module/motion.h b/Marlin/src/module/motion.h index ef3a1026e033..1b05dc1b4ba1 100644 --- a/Marlin/src/module/motion.h +++ b/Marlin/src/module/motion.h @@ -57,9 +57,16 @@ extern xyze_pos_t current_position, // High-level current tool position // Scratch space for a cartesian result extern xyz_pos_t cartes; +#if HAS_TOOL_LENGTH_COMPENSATION + extern bool simple_tool_length_compensation; +#endif + // Until kinematics.cpp is created, declare this here #if IS_KINEMATIC extern abce_pos_t delta; + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + extern bool tool_centerpoint_control; + #endif #endif #if HAS_ABL_NOT_UBL @@ -112,7 +119,7 @@ extern int16_t feedrate_percentage; #define MMS_SCALED(V) ((V) * 0.01f * feedrate_percentage) // The active extruder (tool). Set with T command. -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS extern uint8_t active_extruder; #else constexpr uint8_t active_extruder = 0; @@ -155,10 +162,10 @@ inline float home_bump_mm(const AxisEnum axis) { } #if HAS_HOTEND_OFFSET - extern xyz_pos_t hotend_offset[HOTENDS]; + extern xyz_pos_t hotend_offset[TOOLS]; void reset_hotend_offsets(); #elif HOTENDS - constexpr xyz_pos_t hotend_offset[HOTENDS] = { { TERN_(HAS_X_AXIS, 0) } }; + constexpr xyz_pos_t hotend_offset[TOOLS] = { { TERN_(HAS_X_AXIS, 0) } }; #else constexpr xyz_pos_t hotend_offset[1] = { { TERN_(HAS_X_AXIS, 0) } }; #endif @@ -554,7 +561,7 @@ void home_if_needed(const bool keeplev=false); /** * position_is_reachable family of functions */ -#if IS_KINEMATIC // (DELTA or SCARA) +#if IS_KINEMATIC && NONE(PENTA_AXIS_TRT, PENTA_AXIS_HT) // (DELTA or SCARA) #if HAS_SCARA_OFFSET extern abc_pos_t scara_home_offset; // A and B angular offsets, Z mm offset @@ -577,6 +584,14 @@ void home_if_needed(const bool keeplev=false); #endif +#if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + + bool position_is_reachable_xyijkuvw(NUM_AXIS_LIST(const_float_t rx, const_float_t ry, const_float_t rz, const_float_t ri, const_float_t rj, const_float_t rk, const_float_t ru, const_float_t rv, const_float_t rw)); + inline bool position_is_reachable_xyijkuvw(const xyz_pos_t &pos) { + return position_is_reachable_xyijkuvw(NUM_AXIS_LIST(pos.x, pos.y, pos.z, pos.i, pos.j, pos.k, pos.u, pos.v, pos.w)); + } +#endif + /** * Duplication mode */ diff --git a/Marlin/src/module/penta_axis_ht.cpp b/Marlin/src/module/penta_axis_ht.cpp new file mode 100644 index 000000000000..6cc33229059f --- /dev/null +++ b/Marlin/src/module/penta_axis_ht.cpp @@ -0,0 +1,117 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/** + * @file penta_axis_ht.cpp + * @author DerAndere + * @brief Kinematics for a 5 axis CNC machine in head-table configuration. + * + * This machine has a tilting head and a horizontal rotary table. + * + * Copyright 2024 DerAndere + * + * Based on a relicensed verion of LinuxCNC file maxkins.c + * Author: Chris Radek + * + * Copyright (c) 2007, 2022 Chris Radek + */ + +#include "../inc/MarlinConfig.h" + +#if ENABLED(PENTA_AXIS_HT) + +#include "penta_axis_ht.h" +#include "motion.h" + + +// Initialized by settings.load() +float segments_per_second; +float mrzp_offset_z; + +/** + * penta axis head table inverse kinematics + * + * Calculate the joints positions for a given position, storing the result in the global delta[] array. + * The raw position is interpreted as native machine position using native_to_joint(). + */ +void inverse_kinematics(const xyz_pos_t &raw) { + delta = native_to_joint(raw); +} + +/** + * Calculate the joints positions for a given position. + * + * This is an expensive calculation. + */ +xyz_pos_t native_to_joint(const xyz_pos_t &native) { + if (!tool_centerpoint_control) return native; + + // X and Y hotend offsets must be applied in Cartesian space with no "spoofing" + xyz_pos_t pos = NUM_AXIS_ARRAY( + DIFF_TERN(HAS_HOTEND_OFFSET, native.x, hotend_offset[active_extruder].x), + DIFF_TERN(HAS_HOTEND_OFFSET, native.y, hotend_offset[active_extruder].y), + native.z, + native.i, + native.j + ); + + const float pivot_length = DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_z, hotend_offset[active_extruder].z); + const float i_rad = RADIANS(pos.i); + + #if HAS_J_AXIS || AXIS4_NAME == 'B' + // B correction + const float zb = pivot_length * cos(i_rad) - mrzp_offset_z; + const float xb = pivot_length * sin(i_rad); + + #if HAS_J_AXIS + // C correction + const float xyr = HYPOT(pos.x, pos.y); + const float xytheta = ATAN2(pos.y, pos.x) - RADIANS(pos.j); + #endif + + const xyz_pos_t joints_pos = NUM_AXIS_ARRAY( + TERN(HAS_J_AXIS, xyr * cos(xytheta) + xb, pos.x + xb), + TERN(HAS_J_AXIS, xyr * sin(xytheta), pos.y), + pos.z + zb, + pos.i, + pos.j + ); + + #elif (!HAS_J_AXIS) && (AXIS4_NAME == 'C') + // C correction + const float xyr = HYPOT(pos.x, pos.y); + const float xytheta = ATAN2(pos.y, pos.x) - i_rad; + + const xyz_pos_t joints_pos = NUM_AXIS_ARRAY( + xyr * cos(xytheta), + xyr * sin(xytheta), + DIFF_TERN(HAS_HOTEND_OFFSET, pos.z, hotend_offset[active_extruder].z), + pos.i, + pos.j + ); + #endif + + return joints_pos; +} + +#endif //PENTA_AXIS_HT diff --git a/Marlin/src/module/penta_axis_ht.h b/Marlin/src/module/penta_axis_ht.h new file mode 100644 index 000000000000..7325eb704f93 --- /dev/null +++ b/Marlin/src/module/penta_axis_ht.h @@ -0,0 +1,60 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +/** + * @file penta_axis_ht.h + * @author DerAndere + * @brief Kinematics for a 5 axis CNC machine in head-table configuration. + * + * This machine has a swivel head and a horizontal rotary table. + * + * Copyright 2024 DerAndere + * + * Based on a relicensed verion of LinuxCNC file maxkins.c + * Author: Chris Radek + * + * Copyright (c) 2007, 2022 Chris Radek + */ + +#include "../core/types.h" + +// Machine rotary zero point Z offset is the distance from the center of rotation of the joint that tilts the toolhead to the gage line. +extern float mrzp_offset_z; +extern bool tool_centerpoint_control; +extern float segments_per_second; + +/** + * penta axis head table inverse kinematics + * + * Calculate the joints positions for a given position, storing the result in the global delta[] array. + * The raw position is interpreted as native machine position using native_to_joint(). + */ +void inverse_kinematics(const xyz_pos_t &raw); + +/** + * Calculate the joints positions for a given position. + * + * This is an expensive calculation. + */ +xyz_pos_t native_to_joint(const xyz_pos_t &native); diff --git a/Marlin/src/module/penta_axis_trt.cpp b/Marlin/src/module/penta_axis_trt.cpp new file mode 100644 index 000000000000..c37fc18f95dd --- /dev/null +++ b/Marlin/src/module/penta_axis_trt.cpp @@ -0,0 +1,147 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/** + * @file penta_axis_trt.cpp + * @author DerAndere + * @brief Kinematics for a 5 axis CNC machine in tilting rotary table configuration. + * + * A) XYZAC_TRT: + * This machine has a tilting table (A axis parallel to the X axis) and horizontal rotary + * mounted to the table (C axis). + * B) XYZBC_TRT: + * This machine has a tilting table (B axis parallel to the Y axis) and horizontal rotary + * mounted to the table (C axis). + * + * Copyright 2024 DerAndere + * + * Based on the file trtfuncs.c from LinuxCNC (https://github.com/LinuxCNC/linuxcnc/blob/master/src/emc/kinematics/trtfuncs.c) + * Copyright 2016 Rudy du Preez + * Author: Rudy du Preez + */ + +#include "../inc/MarlinConfig.h" + +#if ENABLED(PENTA_AXIS_TRT) + +#include "penta_axis_trt.h" +#include "motion.h" + +// Initialized by settings.load() +float segments_per_second; +float mrzp_offset_x; +float mrzp_offset_y; +float mrzp_offset_z; +float rotational_offset_x; +float rotational_offset_y; +float rotational_offset_z; + +/** + * 5 axis tilting rotary table inverse kinematics + * + * Calculate the joints positions for a given position, storing the result in the global delta[] array. + * The raw position is interpreted as machine position using native_to_joint(). + */ +void inverse_kinematics(const xyz_pos_t &raw) { + delta = native_to_joint(raw); +} + +/** + * Calculate the joints positions for a given position. + * + * This is an expensive calculation. + */ +xyz_pos_t native_to_joint(const xyz_pos_t &native) { + if (!tool_centerpoint_control) return native; + + const float pivot_length_x = native.x - mrzp_offset_x; + const float pivot_length_y = native.y - mrzp_offset_y; + const float pivot_length_z = native.z - mrzp_offset_z; + + const float i_rad = RADIANS(native.i); + const float cos_i = cos(i_rad); + const float sin_i = sin(i_rad); + + const float j_rad = TERN0(HAS_J_AXIS, RADIANS(native.j)); + const float cos_j = TERN1(HAS_J_AXIS, cos(j_rad)); + const float sin_j = TERN0(HAS_J_AXIS, sin(j_rad)); + + #if AXIS4_NAME == 'A' + // computed position + const xyz_pos_t joints_pos = NUM_AXIS_ARRAY( + cos_j * pivot_length_x + - sin_j * pivot_length_y + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_x, hotend_offset[active_extruder].x), + + sin_j * cos_i * pivot_length_x + + cos_j * cos_i * pivot_length_y + + sin_i * pivot_length_z + - cos_i * rotational_offset_y + - sin_i * rotational_offset_z + + rotational_offset_y + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_y, hotend_offset[active_extruder].y), + + - sin_j * sin_i * pivot_length_x + - cos_j * sin_i * pivot_length_y + + cos_i * pivot_length_z + + sin_i * rotational_offset_y + - cos_i * rotational_offset_z + + rotational_offset_z + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_z, hotend_offset[active_extruder].z), + + native.i, + + native.j + ); + #elif AXIS4_NAME == 'B' + // computed position + const xyz_pos_t joints_pos = NUM_AXIS_ARRAY( + cos_j * cos_i * pivot_length_x + + sin_j * cos_i * pivot_length_y + - sin_i * pivot_length_z + - cos_i * rotational_offset_x + + sin_i * rotational_offset_z + + rotational_offset_x + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_x, hotend_offset[active_extruder].x), + + - sin_j * pivot_length_x + + cos_j * pivot_length_y + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_y, hotend_offset[active_extruder].y), + + cos_j * sin_i * pivot_length_x + + sin_j * sin_i * pivot_length_y + + cos_i * pivot_length_z + - sin_i * rotational_offset_x + - cos_i * rotational_offset_z + + rotational_offset_z + + DIFF_TERN(HAS_HOTEND_OFFSET, mrzp_offset_z, hotend_offset[active_extruder].z), + + native.i, + + native.j + ); + #endif + return joints_pos; +} + +#endif // PENTA_AXIS_TRT diff --git a/Marlin/src/module/penta_axis_trt.h b/Marlin/src/module/penta_axis_trt.h new file mode 100644 index 000000000000..7ef8021964b0 --- /dev/null +++ b/Marlin/src/module/penta_axis_trt.h @@ -0,0 +1,72 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +/** + * @file penta_axis_trt.h + * @author DerAndere + * @brief Kinematics for a 5 axis CNC machine in tilting rotating table configuration. + * + * A) XYZAC_TRT: + * This machine has a tilting table (A axis paralel to the X axis) and horizontal rotary + * mounted to the table (C axis). + * B) XYZBC_TRT: + * This machine has a tilting table (B axis paralel to the Y axis) and horizontal rotary + * mounted to the table (C axis). + * + * Copyright 2024 DerAndere + * + * Based on the file trtfuncs.c from LinuxCNC (https://github.com/LinuxCNC/linuxcnc/blob/master/src/emc/kinematics/trtfuncs.c) + * Copyright 2016 Rudy du Preez + * Author: Rudy du Preez + */ + +#include "../core/types.h" + +extern bool tool_centerpoint_control; +extern float segments_per_second; + +// Center of rotation of the tilting rotating table, given as native machine coorinates when all axes are at 0. +extern float mrzp_offset_x; +extern float mrzp_offset_y; +extern float mrzp_offset_z; + +// Offsets between the Centerlines of the rotational joints. +extern float rotational_offset_x; // For a machine with XYZBC axes, this is the x offset between the centerlines of the rotational joints +extern float rotational_offset_y; // For a machine with XYZAC axes, this is the y offset between the centerlines of the rotational joints +extern float rotational_offset_z; // This is the Z offset between the table surface and the joint that tilts the table + +/** + * 5 axis tilting rotary table inverse kinematics + * + * Calculate the joints positions for a given position, storing the result in the global delta[] array. + * The raw position is interpreted as machine position using native_to_joint(). + */ +void inverse_kinematics(const xyz_pos_t &raw); + +/** + * Calculate the joints positions for a given position. + * + * This is an expensive calculation. + */ +xyz_pos_t native_to_joint(const xyz_pos_t &native); diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 7c746fa5f0a7..30feb31d5bd8 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -164,6 +164,10 @@ uint32_t Planner::max_acceleration_steps_per_s2[DISTINCT_AXES]; // (steps/s^2) D bool Planner::abort_on_endstop_hit = false; #endif +#if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + bool Planner::abort_on_software_endstop = false; +#endif + #if ENABLED(DISTINCT_E_FACTORS) uint8_t Planner::last_extruder = 0; // Respond to extruder change #endif @@ -2977,13 +2981,20 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s ); #endif - // Cartesian XYZ to kinematic ABC, stored in global 'delta' - inverse_kinematics(machine); - PlannerHints ph = hints; if (!hints.millimeters) ph.millimeters = get_move_distance(xyze_pos_t(cart_dist_mm) OPTARG(HAS_ROTATIONAL_AXES, ph.cartesian_move)); + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + if (NEAR_ZERO(cart_dist_mm.i) && TERN1(HAS_J_AXIS, NEAR_ZERO(cart_dist_mm.j))) { + delta += cart_dist_mm; + } + else + #endif + + // Cartesian XYZ to kinematic ABC, stored in global 'delta' + inverse_kinematics(machine); + #if DISABLED(FEEDRATE_SCALING) const feedRate_t feedrate = fr_mm_s; diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 5b37d7fdda5b..6f0cdf01d3c6 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -52,6 +52,10 @@ #include "polargraph.h" #elif ENABLED(POLAR) #include "polar.h" +#elif ENABLED(PENTA_AXIS_TRT) + #include "penta_axis_trt.h" +#elif ENABLED(PENTA_AXIS_HT) + #include "penta_axis_ht.h" #endif #if ABL_PLANAR @@ -534,6 +538,11 @@ class Planner { #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) static bool abort_on_endstop_hit; #endif + + #if ENABLED(ABORT_ON_SOFTWARE_ENDSTOP) + static bool abort_on_software_endstop; + #endif + #ifdef XY_FREQUENCY_LIMIT static int8_t xy_freq_limit_hz; // Minimum XY frequency setting static float xy_freq_min_speed_factor; // Minimum speed factor setting diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index 08a02b4d4004..64414e61cccd 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -95,7 +95,7 @@ class Probe { static bool set_deployed(const bool deploy, const bool no_return=false); - #if IS_KINEMATIC + #if IS_KINEMATIC && NONE(PENTA_AXIS_TRT, PENTA_AXIS_HT) #if HAS_PROBE_XY_OFFSET // Return true if the both nozzle and the probe can reach the given point. diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index 52057ceab51e..8e69e656a47b 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -125,7 +125,7 @@ extern float other_extruder_advance_K[DISTINCT_E]; #endif -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS #include "tool_change.h" void M217_report(const bool eeprom); #endif @@ -249,7 +249,7 @@ typedef struct SettingsDataStruct { // Hotend Offset // #if HAS_HOTEND_OFFSET - xyz_pos_t hotend_offset[HOTENDS - 1]; // M218 XYZ + xyz_pos_t hotend_offset[TOOLS - 1]; // M218 XYZ #endif // @@ -354,7 +354,16 @@ typedef struct SettingsDataStruct { // #if IS_KINEMATIC float segments_per_second; // M665 S - #if ENABLED(DELTA) + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + #if ENABLED(PENTA_AXIS_TRT) + float mrzp_offset_x; // M665 X + float mrzp_offset_y; // M665 Y + float rotational_offset_x; // M665 I + float rotational_offset_y; // M665 J + float rotational_offset_z; // M665 K + #endif + float mrzp_offset_z; // M665 Z + #elif ENABLED(DELTA) float delta_height; // M666 H abc_float_t delta_endstop_adj; // M666 X Y Z float delta_radius, // M665 R @@ -529,7 +538,7 @@ typedef struct SettingsDataStruct { // // Tool-change settings // - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS toolchange_settings_t toolchange_settings; // M217 S P R #endif @@ -925,7 +934,7 @@ void MarlinSettings::postprocess() { { #if HAS_HOTEND_OFFSET // Skip hotend 0 which must be 0 - for (uint8_t e = 1; e < HOTENDS; ++e) + for (uint8_t e = 1; e < TOOLS; ++e) EEPROM_WRITE(hotend_offset[e]); #endif } @@ -1135,7 +1144,22 @@ void MarlinSettings::postprocess() { #if IS_KINEMATIC { EEPROM_WRITE(segments_per_second); - #if ENABLED(DELTA) + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + #if ENABLED(PENTA_AXIS_TRT) + _FIELD_TEST(mrzp_offset_x); + EEPROM_WRITE(mrzp_offset_x); // 1 float + _FIELD_TEST(mrzp_offset_y); + EEPROM_WRITE(mrzp_offset_y); // 1 float + _FIELD_TEST(rotational_offset_x); + EEPROM_WRITE(rotational_offset_x); // 1 float + _FIELD_TEST(rotational_offset_y); + EEPROM_WRITE(rotational_offset_y); // 1 float + _FIELD_TEST(rotational_offset_z); + EEPROM_WRITE(rotational_offset_z); // 1 float + #endif + _FIELD_TEST(mrzp_offset_z); + EEPROM_WRITE(mrzp_offset_z); // 1 float + #elif ENABLED(DELTA) _FIELD_TEST(delta_height); EEPROM_WRITE(delta_height); // 1 float EEPROM_WRITE(delta_endstop_adj); // 3 floats @@ -1610,7 +1634,7 @@ void MarlinSettings::postprocess() { // Multiple Extruders // - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS _FIELD_TEST(toolchange_settings); EEPROM_WRITE(toolchange_settings); #endif @@ -1990,7 +2014,7 @@ void MarlinSettings::postprocess() { { #if HAS_HOTEND_OFFSET // Skip hotend 0 which must be 0 - for (uint8_t e = 1; e < HOTENDS; ++e) + for (uint8_t e = 1; e < TOOLS; ++e) EEPROM_READ(hotend_offset[e]); #endif } @@ -2218,7 +2242,22 @@ void MarlinSettings::postprocess() { #if IS_KINEMATIC { EEPROM_READ(segments_per_second); - #if ENABLED(DELTA) + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + #if ENABLED(PENTA_AXIS_TRT) + _FIELD_TEST(mrzp_offset_x); + EEPROM_READ(mrzp_offset_x); + _FIELD_TEST(mrzp_offset_y); + EEPROM_READ(mrzp_offset_y); + _FIELD_TEST(rotational_offset_x); + EEPROM_READ(rotational_offset_x); + _FIELD_TEST(rotational_offset_y); + EEPROM_READ(rotational_offset_y); + _FIELD_TEST(rotational_offset_z); + EEPROM_READ(rotational_offset_z); + #endif + _FIELD_TEST(mrzp_offset_z); + EEPROM_READ(mrzp_offset_z); + #elif ENABLED(DELTA) _FIELD_TEST(delta_height); EEPROM_READ(delta_height); // 1 float EEPROM_READ(delta_endstop_adj); // 3 floats @@ -2724,7 +2763,7 @@ void MarlinSettings::postprocess() { // // Tool-change settings // - #if HAS_MULTI_EXTRUDER + #if HAS_MULTI_TOOLS _FIELD_TEST(toolchange_settings); EEPROM_READ(toolchange_settings); #endif @@ -3445,7 +3484,16 @@ void MarlinSettings::reset() { #if IS_KINEMATIC segments_per_second = DEFAULT_SEGMENTS_PER_SECOND; - #if ENABLED(DELTA) + #if ANY(PENTA_AXIS_TRT, PENTA_AXIS_HT) + #if ENABLED(PENTA_AXIS_TRT) + mrzp_offset_x = DEFAULT_MRZP_OFFSET_X; + mrzp_offset_y = DEFAULT_MRZP_OFFSET_Y; + rotational_offset_x = DEFAULT_ROTATIONAL_JOINT_OFFSET_X; + rotational_offset_y = DEFAULT_ROTATIONAL_JOINT_OFFSET_Y; + rotational_offset_z = DEFAULT_ROTATIONAL_JOINT_OFFSET_Z; + #endif + mrzp_offset_z = DEFAULT_MRZP_OFFSET_Z; + #elif ENABLED(DELTA) const abc_float_t adj = DELTA_ENDSTOP_ADJ, dta = DELTA_TOWER_ANGLE_TRIM, ddr = DELTA_DIAGONAL_ROD_TRIM_TOWER; delta_height = DELTA_HEIGHT; delta_endstop_adj = adj; diff --git a/Marlin/src/module/tool_change.cpp b/Marlin/src/module/tool_change.cpp index 5d34dc3aa5a4..8536a5e62408 100644 --- a/Marlin/src/module/tool_change.cpp +++ b/Marlin/src/module/tool_change.cpp @@ -36,7 +36,7 @@ //#define DEBUG_TOOL_CHANGE //#define DEBUG_TOOLCHANGE_FILAMENT_SWAP -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS toolchange_settings_t toolchange_settings; // Initialized by settings.load() #endif @@ -96,6 +96,10 @@ #include "probe.h" #endif +#if HAS_CUTTER + #include "../feature/spindle_laser.h" +#endif + #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) #if TOOLCHANGE_FS_WIPE_RETRACT <= 0 #undef TOOLCHANGE_FS_WIPE_RETRACT @@ -517,19 +521,58 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. DEBUG_POS("Start ST Tool-Change", current_position); - current_position.x = placexpos; + current_position.x = SUM_TERN(HAS_HOTEND_OFFSET, placexpos, hotend_offset[active_extruder].x); DEBUG_ECHOLNPGM("(1) Place old tool ", active_extruder); DEBUG_POS("Move X SwitchPos", current_position); fast_line_to_current(X_AXIS); - current_position.y = SWITCHING_TOOLHEAD_Y_POS - (SWITCHING_TOOLHEAD_Y_SECURITY); + current_position.y = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Y_POS, hotend_offset[active_extruder].y) - (SWITCHING_TOOLHEAD_Y_CLEAR); DEBUG_SYNCHRONIZE(); - DEBUG_POS("Move Y SwitchPos + Security", current_position); + DEBUG_POS("Move Y SwitchPos - Y Clear", current_position); - slow_line_to_current(Y_AXIS); + fast_line_to_current(Y_AXIS); // move Y in front of the toolhead dock + + #if defined(SWITCHING_TOOLHEAD_Z_CLEAR) + current_position.z = SWITCHING_TOOLHEAD_Z_POS + (SWITCHING_TOOLHEAD_Z_CLEAR); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Z SwitchPos + Z Clear", current_position); + + fast_line_to_current(Z_AXIS); // move Z on top of the toolhead dock + + current_position.y = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Y_POS, hotend_offset[active_extruder].y); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Y SwitchPos", current_position); + + fast_line_to_current(Y_AXIS); // move Y to the toolhead dock + + current_position.z = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Z_POS, hotend_offset[active_extruder].z); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Z SwitchPos", current_position); + + slow_line_to_current(Z_AXIS); // place tool in the toolhead dock + + #else + #if defined(SWITCHING_TOOLHEAD_Z_POS) + current_position.z = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Z_POS, hotend_offset[active_extruder].z); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Z SwitchPos", current_position); + + fast_line_to_current(Z_AXIS); // move Z to the toolhead dock + #endif + current_position.y = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Y_POS, hotend_offset[active_extruder].y) - (SWITCHING_TOOLHEAD_Y_SECURITY); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Y SwitchPos + Security", current_position); + + slow_line_to_current(Y_AXIS); + #endif // 2. Unlock tool and drop it in the dock TERN_(TOOL_SENSOR, tool_sensor_disabled = true); @@ -538,24 +581,35 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. DEBUG_ECHOLNPGM("(2) Unlock and Place Toolhead"); switching_toolhead_lock(false); safe_delay(500); - - current_position.y = SWITCHING_TOOLHEAD_Y_POS; - DEBUG_POS("Move Y SwitchPos", current_position); - slow_line_to_current(Y_AXIS); + #if !defined(SWITCHING_TOOLHEAD_Z_CLEAR) + current_position.y += (SWITCHING_TOOLHEAD_Y_SECURITY); + DEBUG_POS("Move Y SwitchPos", current_position); + slow_line_to_current(Y_AXIS); + #endif // Wait for move to complete, then another 0.2s planner.synchronize(); safe_delay(200); - current_position.y -= SWITCHING_TOOLHEAD_Y_CLEAR; - DEBUG_POS("Move back Y clear", current_position); - slow_line_to_current(Y_AXIS); // move away from docked toolhead + #if defined(SWITCHING_TOOLHEAD_Z_CLEAR) + current_position.z += (SWITCHING_TOOLHEAD_Z_CLEAR); + + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move back Z Clear", current_position); + + slow_line_to_current(Z_AXIS); // move away from docked toolhead + #else + current_position.y -= SWITCHING_TOOLHEAD_Y_CLEAR; + + DEBUG_POS("Move back Y clear", current_position); + slow_line_to_current(Y_AXIS); // move away from docked toolhead + #endif (void)check_tool_sensor_stats(active_extruder); // 3. Move to the new toolhead - current_position.x = grabxpos; + current_position.x = SUM_TERN(HAS_HOTEND_OFFSET, grabxpos, hotend_offset[active_extruder].x); DEBUG_SYNCHRONIZE(); DEBUG_ECHOLNPGM("(3) Move to new toolhead position"); @@ -563,22 +617,32 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. fast_line_to_current(X_AXIS); - current_position.y = SWITCHING_TOOLHEAD_Y_POS - (SWITCHING_TOOLHEAD_Y_SECURITY); + // 4. Grab and lock the new toolhead - DEBUG_SYNCHRONIZE(); - DEBUG_POS("Move Y SwitchPos + Security", current_position); + #if defined(SWITCHING_TOOLHEAD_Z_CLEAR) + current_position.z = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Z_POS, hotend_offset[active_extruder].z); - slow_line_to_current(Y_AXIS); + DEBUG_SYNCHRONIZE(); + DEBUG_ECHOLNPGM("(4) Grab and lock new toolhead"); + DEBUG_POS("Move Z SwitchPos", current_position); - // 4. Grab and lock the new toolhead + slow_line_to_current(Z_AXIS); + #else + current_position.y = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Y_POS, hotend_offset[active_extruder].y) - SWITCHING_TOOLHEAD_Y_SECURITY; - current_position.y = SWITCHING_TOOLHEAD_Y_POS; + DEBUG_SYNCHRONIZE(); + DEBUG_POS("Move Y SwitchPos + Security", current_position); - DEBUG_SYNCHRONIZE(); - DEBUG_ECHOLNPGM("(4) Grab and lock new toolhead"); - DEBUG_POS("Move Y SwitchPos", current_position); + slow_line_to_current(Y_AXIS); - slow_line_to_current(Y_AXIS); + current_position.y = SUM_TERN(HAS_HOTEND_OFFSET, SWITCHING_TOOLHEAD_Y_POS, hotend_offset[active_extruder].y); + + DEBUG_SYNCHRONIZE(); + DEBUG_ECHOLNPGM("(4) Grab and lock new toolhead"); + DEBUG_POS("Move Y SwitchPos", current_position); + + slow_line_to_current(Y_AXIS); + #endif // Wait for move to finish, pause 0.2s, move servo, pause 0.5s planner.synchronize(); @@ -588,10 +652,24 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. switching_toolhead_lock(true); safe_delay(500); + #if defined(SWITCHING_TOOLHEAD_Z_CLEAR) + #if HAS_HOTEND_OFFSET + current_position.z = SWITCHING_TOOLHEAD_Z_POS + hotend_offset[active_extruder].z - hotend_offset[new_tool].z + (SWITCHING_TOOLHEAD_Z_CLEAR); + #else + current_position.z = SWITCHING_TOOLHEAD_Z_POS + (SWITCHING_TOOLHEAD_Z_CLEAR); + #endif + DEBUG_POS("Move back Z clear", current_position); + slow_line_to_current(Z_AXIS); // Move away from docked toolhead - current_position.y -= SWITCHING_TOOLHEAD_Y_CLEAR; - DEBUG_POS("Move back Y clear", current_position); - slow_line_to_current(Y_AXIS); // Move away from docked toolhead + current_position.y -= SWITCHING_TOOLHEAD_Y_CLEAR; + DEBUG_POS("Move back Y clear", current_position); + slow_line_to_current(Y_AXIS); // Move away from docked toolhead + + #else + current_position.y -= SWITCHING_TOOLHEAD_Y_CLEAR; + DEBUG_POS("Move back Y clear", current_position); + slow_line_to_current(Y_AXIS); // Move away from docked toolhead + #endif planner.synchronize(); // Always sync the final move (void)check_tool_sensor_stats(new_tool, true, true); @@ -822,7 +900,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. #endif // ELECTROMAGNETIC_SWITCHING_TOOLHEAD -#if HAS_EXTRUDERS +#if HAS_EXTRUDERS || HAS_TOOL_LENGTH_COMPENSATION inline void invalid_extruder_error(const uint8_t e) { SERIAL_ECHO_START(); SERIAL_CHAR('T'); SERIAL_ECHO(e); @@ -1136,19 +1214,19 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { mmu2.tool_change(new_tool); - #elif EXTRUDERS == 0 + #elif !TOOLS // Nothing to do UNUSED(new_tool); UNUSED(no_move); - #elif EXTRUDERS < 2 + #elif TOOLS < 2 UNUSED(no_move); if (new_tool) invalid_extruder_error(new_tool); return; - #elif HAS_MULTI_EXTRUDER + #elif HAS_MULTI_TOOLS planner.synchronize(); @@ -1157,7 +1235,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { return invalid_extruder_error(new_tool); #endif - if (new_tool >= EXTRUDERS) + if (new_tool >= TERN(HAS_TOOL_LENGTH_COMPENSATION, TOOLS, EXTRUDERS)) return invalid_extruder_error(new_tool); if (!no_move && homing_needed()) { @@ -1192,6 +1270,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { if (new_tool != old_tool || TERN0(PARKING_EXTRUDER, extruder_parked)) { // PARKING_EXTRUDER may need to attach old_tool when homing destination = current_position; + #if ALL(TOOLCHANGE_FILAMENT_SWAP, HAS_FAN) && TOOLCHANGE_FS_FAN >= 0 // Store and stop fan. Restored on any exit. REMEMBER(fan, thermalManager.fan_speed[TOOLCHANGE_FS_FAN], 0); @@ -1249,6 +1328,39 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { } #endif + // Raise to safe Z + #if defined(SAFE_TOOLCHANGE_START_Z) + if (can_move_away && TERN1(TOOLCHANGE_PARK, toolchange_settings.enable_park)) { + current_position.z = SUM_TERN(HAS_HOTEND_OFFSET, (SAFE_TOOLCHANGE_START_Z), hotend_offset[old_tool].z); + TERN_(HAS_SOFTWARE_ENDSTOPS, NOMORE(current_position.z, soft_endstop.max.z)); + fast_line_to_current(Z_AXIS); + planner.synchronize(); + } + #endif + + #if HAS_CUTTER + // Store cutter state and stop cutter + bool old_cutter_state = cutter.enable_state; + uint8_t old_cutter_power = cutter.power; + uint8_t old_tool_type = cutter.active_tool_type; + cutter.power = 0; + cutter.apply_power(0); + #if ENABLED(LASER_FEATURE) + cutter.inline_power(cutter.power); + #endif + cutter.set_enabled(false); + TERN_(SPINDLE_FEATURE, safe_delay(1000)); + #endif + + #if ENABLED(COOLANT_MIST) + bool old_mist_coolant_state = TERN(COOLANT_MIST_INVERT, !READ(COOLANT_MIST_PIN), READ(COOLANT_MIST_PIN)); + WRITE(COOLANT_MIST_PIN, COOLANT_MIST_INVERT); // Turn off Mist coolant + #endif + #if ENABLED(COOLANT_FLOOD) + bool old_flood_coolant_state = TERN(COOLANT_FLOOD_INVERT, !READ(COOLANT_FLOOD_PIN), READ(COOLANT_FLOOD_PIN)); + WRITE(COOLANT_FLOOD_PIN, COOLANT_FLOOD_INVERT); // Turn off Flood coolant + #endif + // Toolchange park #if ENABLED(TOOLCHANGE_PARK) && !HAS_SWITCHING_NOZZLE if (can_move_away && toolchange_settings.enable_park) { @@ -1369,22 +1481,30 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { #else // Move back to the original (or adjusted) position DEBUG_POS("Move back", destination); + const xyze_pos_t toolchange_destination = destination; + + // Raise to safe Z + #if defined(SAFE_TOOLCHANGE_START_Z) + if (TERN1(TOOLCHANGE_PARK, toolchange_settings.enable_park)) { + do_blocking_move_to_z(SUM_TERN(HAS_HOTEND_OFFSET, (SAFE_TOOLCHANGE_START_Z), hotend_offset[new_tool].z), planner.settings.max_feedrate_mm_s[Z_AXIS]); + } + #endif #if ENABLED(TOOLCHANGE_PARK) - if (toolchange_settings.enable_park) do_blocking_move_to_xy_z(destination, destination.z, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE)); + if (toolchange_settings.enable_park) do_blocking_move_to_xy_z(toolchange_destination, toolchange_destination.z, MMM_TO_MMS(TOOLCHANGE_PARK_XY_FEEDRATE)); #else - do_blocking_move_to_xy(destination, planner.settings.max_feedrate_mm_s[X_AXIS]* 0.5f); + do_blocking_move_to_xy(toolchange_destination, planner.settings.max_feedrate_mm_s[X_AXIS]* 0.5f); // If using MECHANICAL_SWITCHING extruder/nozzle, set HOTEND_OFFSET in Z axis after running EVENT_GCODE_TOOLCHANGE below. #if NONE(MECHANICAL_SWITCHING_EXTRUDER, MECHANICAL_SWITCHING_NOZZLE) - do_blocking_move_to_z(destination.z, planner.settings.max_feedrate_mm_s[Z_AXIS]); + do_blocking_move_to_z(toolchange_destination.z, planner.settings.max_feedrate_mm_s[Z_AXIS]); SECONDARY_AXIS_CODE( - do_blocking_move_to_i(destination.i, planner.settings.max_feedrate_mm_s[I_AXIS]), - do_blocking_move_to_j(destination.j, planner.settings.max_feedrate_mm_s[J_AXIS]), - do_blocking_move_to_k(destination.k, planner.settings.max_feedrate_mm_s[K_AXIS]), - do_blocking_move_to_u(destination.u, planner.settings.max_feedrate_mm_s[U_AXIS]), - do_blocking_move_to_v(destination.v, planner.settings.max_feedrate_mm_s[V_AXIS]), - do_blocking_move_to_w(destination.w, planner.settings.max_feedrate_mm_s[W_AXIS]) + do_blocking_move_to_i(toolchange_destination.i, planner.settings.max_feedrate_mm_s[I_AXIS]), + do_blocking_move_to_j(toolchange_destination.j, planner.settings.max_feedrate_mm_s[J_AXIS]), + do_blocking_move_to_k(toolchange_destination.k, planner.settings.max_feedrate_mm_s[K_AXIS]), + do_blocking_move_to_u(toolchange_destination.u, planner.settings.max_feedrate_mm_s[U_AXIS]), + do_blocking_move_to_v(toolchange_destination.v, planner.settings.max_feedrate_mm_s[V_AXIS]), + do_blocking_move_to_w(toolchange_destination.w, planner.settings.max_feedrate_mm_s[W_AXIS]) ); #endif #endif @@ -1408,6 +1528,63 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { TERN_(DUAL_X_CARRIAGE, idex_set_parked(false)); } // should_move + #if HAS_CUTTER + + #if ALL(SPINDLE_FEATURE, LASER_FEATURE) + if (new_tool == LASER_TOOL) { + cutter.active_tool_type = TYPE_LASER; + } + elif (new_tool < LASER_TOOL) { + cutter.active_tool_type = TYPE_EXTRUDER; + } + else { + cutter.active_tool_type = TYPE_SPINDLE; + } + // Restore cutter state + if (old_cutter_state && (old_tool == TYPE_SPINDLE) && new_tool > LASER_TOOL) { + #elif ENABLED(SPINDLE_FEATURE) + if (new_tool < EXTRUDERS) { + cutter.active_tool_type = TYPE_EXTRUDER; + } + else { + cutter.active_tool_type = TYPE_SPINDLE; + } + if (old_cutter_state && cutter.active_tool_type != TYPE_EXTRUDER) { + #elif ENABLED(LASER_FEATURE) + if (new_tool < LASER_TOOL) { + cutter.active_tool_type = TYPE_EXTRUDER; + } + else { + cutter.active_tool_type = TYPE_LASER; + } + if (old_cutter_state && cutter.active_tool_type != TYPE_EXTRUDER) { + #endif + cutter.power = old_cutter_power; + cutter.apply_power(cutter.power); + #if ENABLED(LASER_FEATURE) + if ((cutter.active_tool_type == TYPE_LASER ) && (cutter.cutter_mode != CUTTER_MODE_STANDARD)) { + cutter.inline_power(cutter.power); + } + #endif + cutter.set_enabled(true); + if (TERN0(SPINDLE_FEATURE, cutter.active_tool_type = TYPE_SPINDLE)) { + safe_delay(1000); + } + } + #endif + + + #if ENABLED(COOLANT_MIST) + if old_mist_coolant_state { + WRITE(COOLANT_MIST_PIN, !COOLANT_MIST_INVERT); // Turn on Mist coolant + } + #endif + #if ENABLED(COOLANT_FLOOD) + if old_flood_coolant_state { + WRITE(COOLANT_FLOOD_PIN, !COOLANT_FLOOD_INVERT); // Turn on Flood coolant + } + #endif + #if HAS_SWITCHING_NOZZLE // Move back down. (Including when the new tool is higher.) if (!should_move) @@ -1514,7 +1691,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { SERIAL_ECHOLNPGM(STR_ACTIVE_EXTRUDER, active_extruder); - #endif // HAS_MULTI_EXTRUDER + #endif // HAS_MULTI_TOOLS } #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) diff --git a/Marlin/src/module/tool_change.h b/Marlin/src/module/tool_change.h index 3cb8b4a63784..2cea1601d05e 100644 --- a/Marlin/src/module/tool_change.h +++ b/Marlin/src/module/tool_change.h @@ -25,7 +25,7 @@ //#define DEBUG_TOOLCHANGE_MIGRATION_FEATURE -#if HAS_MULTI_EXTRUDER +#if HAS_MULTI_TOOLS typedef struct { #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) diff --git a/ini/features.ini b/ini/features.ini index 420ba8e131f7..16f73b80b4a0 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -239,6 +239,7 @@ HAS_FANCHECK = build_src_filter=+ FILAMENT_WIDTH_SENSOR = build_src_filter=+ + FWRETRACT = build_src_filter=+ + +HAS_TOOL_LENGTH_COMPENSATION = build_src_filter=+ HOST_ACTION_COMMANDS = build_src_filter=+ HOTEND_IDLE_TIMEOUT = build_src_filter=+ + JOYSTICK = build_src_filter=+ @@ -285,7 +286,7 @@ SKEW_CORRECTION_GCODE = build_src_filter=+ + PINS_DEBUGGING = build_src_filter=+ EDITABLE_STEPS_PER_UNIT = build_src_filter=+ -HAS_MULTI_EXTRUDER = build_src_filter=+ +HAS_MULTI_TOOLS = build_src_filter=+ HAS_HOTEND_OFFSET = build_src_filter=+ EDITABLE_SERVO_ANGLES = build_src_filter=+ PIDTEMP = build_src_filter=+ @@ -366,6 +367,8 @@ NOZZLE_CLEAN_FEATURE = build_src_filter=+ DELTA = build_src_filter=+ + POLAR = build_src_filter=+ POLARGRAPH = build_src_filter=+ +PENTA_AXIS_TRT = build_src_filter=+ +PENTA_AXIS_HT = build_src_filter=+ BEZIER_CURVE_SUPPORT = build_src_filter=+ + PRINTCOUNTER = build_src_filter=+ HAS_BED_PROBE = build_src_filter=+ + + +