🏗️ Support for up to 6 linear axes (#19112)
Co-authored-by: Scott Lahteine <github@thinkyhead.com>
This commit is contained in:
		
				
					committed by
					
						
						Scott Lahteine
					
				
			
			
				
	
			
			
			
						parent
						
							d3c56a76e7
						
					
				
				
					commit
					c1fca91103
				
			@@ -689,7 +689,7 @@ G29_TYPE GcodeSuite::G29() {
 | 
			
		||||
        TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/3"), GET_TEXT(MSG_PROBING_MESH), int(i + 1)));
 | 
			
		||||
 | 
			
		||||
        // Retain the last probe position
 | 
			
		||||
        abl.probePos = points[i];
 | 
			
		||||
        abl.probePos = xy_pos_t(points[i]);
 | 
			
		||||
        abl.measured_z = faux ? 0.001 * random(-100, 101) : probe.probe_at_point(abl.probePos, raise_after, abl.verbose_level);
 | 
			
		||||
        if (isnan(abl.measured_z)) {
 | 
			
		||||
          set_bed_leveling_enabled(abl.reenable);
 | 
			
		||||
@@ -795,7 +795,7 @@ G29_TYPE GcodeSuite::G29() {
 | 
			
		||||
              const int ind = abl.indexIntoAB[xx][yy];
 | 
			
		||||
              xyz_float_t tmp = { abl.eqnAMatrix[ind + 0 * abl.abl_points],
 | 
			
		||||
                                  abl.eqnAMatrix[ind + 1 * abl.abl_points], 0 };
 | 
			
		||||
              planner.bed_level_matrix.apply_rotation_xyz(tmp);
 | 
			
		||||
              planner.bed_level_matrix.apply_rotation_xyz(tmp.x, tmp.y, tmp.z);
 | 
			
		||||
              if (get_min) NOMORE(min_diff, abl.eqnBVector[ind] - tmp.z);
 | 
			
		||||
              const float subval = get_min ? abl.mean : tmp.z + min_diff,
 | 
			
		||||
                            diff = abl.eqnBVector[ind] - subval;
 | 
			
		||||
 
 | 
			
		||||
@@ -323,42 +323,44 @@ void GcodeSuite::G28() {
 | 
			
		||||
 | 
			
		||||
    #define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS))))
 | 
			
		||||
 | 
			
		||||
    const bool homeZ = parser.seen_test('Z'),
 | 
			
		||||
               LINEAR_AXIS_LIST( // Other axes should be homed before Z safe-homing
 | 
			
		||||
                 needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false // UNUSED
 | 
			
		||||
    const bool homeZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')),
 | 
			
		||||
               LINEAR_AXIS_LIST(              // Other axes should be homed before Z safe-homing
 | 
			
		||||
                 needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false, // UNUSED
 | 
			
		||||
                 needI = _UNSAFE(I), needJ = _UNSAFE(J), needK = _UNSAFE(K)
 | 
			
		||||
               ),
 | 
			
		||||
               LINEAR_AXIS_LIST( // Home each axis if needed or flagged
 | 
			
		||||
               LINEAR_AXIS_LIST(              // Home each axis if needed or flagged
 | 
			
		||||
                 homeX = needX || parser.seen_test('X'),
 | 
			
		||||
                 homeY = needY || parser.seen_test('Y'),
 | 
			
		||||
                 homeZZ = homeZ // UNUSED
 | 
			
		||||
                 homeZZ = homeZ,
 | 
			
		||||
                 homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME),
 | 
			
		||||
               ),
 | 
			
		||||
               // Home-all if all or none are flagged
 | 
			
		||||
               home_all = true LINEAR_AXIS_GANG(&& homeX == homeX, && homeX == homeY, && homeX == homeZ),
 | 
			
		||||
               LINEAR_AXIS_LIST(doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ);
 | 
			
		||||
 | 
			
		||||
   UNUSED(needZ);
 | 
			
		||||
   UNUSED(homeZZ);
 | 
			
		||||
 | 
			
		||||
    #if ENABLED(HOME_Z_FIRST)
 | 
			
		||||
 | 
			
		||||
      if (doZ) homeaxis(Z_AXIS);
 | 
			
		||||
               home_all = LINEAR_AXIS_GANG(   // Home-all if all or none are flagged
 | 
			
		||||
                    homeX == homeX, && homeY == homeX, && homeZ == homeX,
 | 
			
		||||
                 && homeI == homeX, && homeJ == homeX, && homeK == homeX
 | 
			
		||||
               ),
 | 
			
		||||
               LINEAR_AXIS_LIST(
 | 
			
		||||
                 doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ,
 | 
			
		||||
                 doI = home_all || homeI, doJ = home_all || homeJ, doK = home_all || homeK
 | 
			
		||||
               );
 | 
			
		||||
 | 
			
		||||
    #if HAS_Z_AXIS
 | 
			
		||||
      UNUSED(needZ); UNUSED(homeZZ);
 | 
			
		||||
    #else
 | 
			
		||||
      constexpr bool doZ = false;
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    TERN_(HOME_Z_FIRST, if (doZ) homeaxis(Z_AXIS));
 | 
			
		||||
 | 
			
		||||
    const float z_homing_height = parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT;
 | 
			
		||||
 | 
			
		||||
    if (z_homing_height && (0 LINEAR_AXIS_GANG(|| doX, || doY, || TERN0(Z_SAFE_HOMING, doZ)))) {
 | 
			
		||||
    if (z_homing_height && (0 LINEAR_AXIS_GANG(|| doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK))) {
 | 
			
		||||
      // Raise Z before homing any other axes and z is not already high enough (never lower z)
 | 
			
		||||
      if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) by ", z_homing_height);
 | 
			
		||||
      do_z_clearance(z_homing_height);
 | 
			
		||||
      TERN_(BLTOUCH, bltouch.init());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #if ENABLED(QUICK_HOME)
 | 
			
		||||
 | 
			
		||||
      if (doX && doY) quick_home_xy();
 | 
			
		||||
 | 
			
		||||
    #endif
 | 
			
		||||
    TERN_(QUICK_HOME, if (doX && doY) quick_home_xy());
 | 
			
		||||
 | 
			
		||||
    // Home Y (before X)
 | 
			
		||||
    if (ENABLED(HOME_Y_BEFORE_X) && (doY || TERN0(CODEPENDENT_XY_HOMING, doX)))
 | 
			
		||||
@@ -397,7 +399,7 @@ void GcodeSuite::G28() {
 | 
			
		||||
    TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(slow_homing));
 | 
			
		||||
 | 
			
		||||
    // Home Z last if homing towards the bed
 | 
			
		||||
    #if DISABLED(HOME_Z_FIRST)
 | 
			
		||||
    #if HAS_Z_AXIS && DISABLED(HOME_Z_FIRST)
 | 
			
		||||
      if (doZ) {
 | 
			
		||||
        #if EITHER(Z_MULTI_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
 | 
			
		||||
          stepper.set_all_z_lock(false);
 | 
			
		||||
@@ -409,6 +411,16 @@ void GcodeSuite::G28() {
 | 
			
		||||
      }
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    #if LINEAR_AXES >= 4
 | 
			
		||||
      if (doI) homeaxis(I_AXIS);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 5
 | 
			
		||||
      if (doJ) homeaxis(J_AXIS);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 6
 | 
			
		||||
      if (doK) homeaxis(K_AXIS);
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    sync_plan_position();
 | 
			
		||||
 | 
			
		||||
  #endif
 | 
			
		||||
@@ -480,6 +492,15 @@ void GcodeSuite::G28() {
 | 
			
		||||
    #if HAS_CURRENT_HOME(Y2)
 | 
			
		||||
      stepperY2.rms_current(tmc_save_current_Y2);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_CURRENT_HOME(I)
 | 
			
		||||
      stepperI.rms_current(tmc_save_current_I);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_CURRENT_HOME(J)
 | 
			
		||||
      stepperJ.rms_current(tmc_save_current_J);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_CURRENT_HOME(K)
 | 
			
		||||
      stepperK.rms_current(tmc_save_current_K);
 | 
			
		||||
    #endif
 | 
			
		||||
  #endif // HAS_HOMING_CURRENT
 | 
			
		||||
 | 
			
		||||
  ui.refresh();
 | 
			
		||||
@@ -498,11 +519,13 @@ void GcodeSuite::G28() {
 | 
			
		||||
    // Set L6470 absolute position registers to counts
 | 
			
		||||
    // constexpr *might* move this to PROGMEM.
 | 
			
		||||
    // If not, this will need a PROGMEM directive and an accessor.
 | 
			
		||||
    #define _EN_ITEM(N) , E_AXIS
 | 
			
		||||
    static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = {
 | 
			
		||||
      X_AXIS, Y_AXIS, Z_AXIS,
 | 
			
		||||
      X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS,
 | 
			
		||||
      E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
 | 
			
		||||
      LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS),
 | 
			
		||||
      X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS, Z_AXIS
 | 
			
		||||
      REPEAT(E_STEPPERS, _EN_ITEM)
 | 
			
		||||
    };
 | 
			
		||||
    #undef _EN_ITEM
 | 
			
		||||
    for (uint8_t j = 1; j <= L64XX::chain[0]; j++) {
 | 
			
		||||
      const uint8_t cv = L64XX::chain[j];
 | 
			
		||||
      L64xxManager.set_param((L64XX_axis_t)cv, L6470_ABS_POS, stepper.position(L64XX_axis_xref[cv]));
 | 
			
		||||
 
 | 
			
		||||
@@ -73,11 +73,23 @@
 | 
			
		||||
#if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT)
 | 
			
		||||
  #define HAS_X_CENTER 1
 | 
			
		||||
#endif
 | 
			
		||||
#if BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
 | 
			
		||||
#if HAS_Y_AXIS && BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
 | 
			
		||||
  #define HAS_Y_CENTER 1
 | 
			
		||||
#endif
 | 
			
		||||
#if LINEAR_AXES >= 4 && BOTH(CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX)
 | 
			
		||||
  #define HAS_I_CENTER 1
 | 
			
		||||
#endif
 | 
			
		||||
#if LINEAR_AXES >= 5 && BOTH(CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX)
 | 
			
		||||
  #define HAS_J_CENTER 1
 | 
			
		||||
#endif
 | 
			
		||||
#if LINEAR_AXES >= 6 && BOTH(CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX)
 | 
			
		||||
  #define HAS_K_CENTER 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES };
 | 
			
		||||
enum side_t : uint8_t {
 | 
			
		||||
  TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES,
 | 
			
		||||
  LIST_N(DOUBLE(SUB3(LINEAR_AXES)), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
 | 
			
		||||
static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
 | 
			
		||||
@@ -105,7 +117,7 @@ struct measurements_t {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
inline void calibration_move() {
 | 
			
		||||
  do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
 | 
			
		||||
  do_blocking_move_to((xyz_pos_t)current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -174,7 +186,7 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
 | 
			
		||||
  destination = current_position;
 | 
			
		||||
  for (float travel = 0; travel < limit; travel += step) {
 | 
			
		||||
    destination[axis] += dir * step;
 | 
			
		||||
    do_blocking_move_to(destination, mms);
 | 
			
		||||
    do_blocking_move_to((xyz_pos_t)destination, mms);
 | 
			
		||||
    planner.synchronize();
 | 
			
		||||
    if (read_calibration_pin() == stop_state) break;
 | 
			
		||||
  }
 | 
			
		||||
@@ -209,7 +221,7 @@ inline float measure(const AxisEnum axis, const int dir, const bool stop_state,
 | 
			
		||||
  // Move back to the starting position
 | 
			
		||||
  destination = current_position;
 | 
			
		||||
  destination[axis] = start_pos;
 | 
			
		||||
  do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
 | 
			
		||||
  do_blocking_move_to((xyz_pos_t)destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
 | 
			
		||||
  return measured_pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -230,7 +242,15 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
 | 
			
		||||
  park_above_object(m, uncertainty);
 | 
			
		||||
 | 
			
		||||
  switch (side) {
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(X)
 | 
			
		||||
      case RIGHT: dir = -1;
 | 
			
		||||
      case LEFT:  axis = X_AXIS; break;
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 2 && AXIS_CAN_CALIBRATE(Y)
 | 
			
		||||
      case BACK:  dir = -1;
 | 
			
		||||
      case FRONT: axis = Y_AXIS; break;
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
      case TOP: {
 | 
			
		||||
        const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
 | 
			
		||||
        m.obj_center.z = measurement - dimensions.z / 2;
 | 
			
		||||
@@ -238,13 +258,17 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(X)
 | 
			
		||||
      case RIGHT: dir = -1;
 | 
			
		||||
      case LEFT:  axis = X_AXIS; break;
 | 
			
		||||
    #if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
 | 
			
		||||
      case IMINIMUM: dir = -1;
 | 
			
		||||
      case IMAXIMUM: axis = I_AXIS; break;
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(Y)
 | 
			
		||||
      case BACK:  dir = -1;
 | 
			
		||||
      case FRONT: axis = Y_AXIS; break;
 | 
			
		||||
    #if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
 | 
			
		||||
      case JMINIMUM: dir = -1;
 | 
			
		||||
      case JMAXIMUM: axis = J_AXIS; break;
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
 | 
			
		||||
      case KMINIMUM: dir = -1;
 | 
			
		||||
      case KMAXIMUM: axis = K_AXIS; break;
 | 
			
		||||
    #endif
 | 
			
		||||
    default: return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -289,14 +313,23 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
    probe_side(m, uncertainty, TOP);
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_LEFT,  probe_side(m, uncertainty, LEFT,  probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_BACK,  probe_side(m, uncertainty, BACK,  probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT,    probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT,    probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_LEFT,  probe_side(m, uncertainty, LEFT,     probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_BACK,  probe_side(m, uncertainty, BACK,     probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_IMIN,  probe_side(m, uncertainty, IMINIMUM, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_IMAX,  probe_side(m, uncertainty, IMAXIMUM, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_JMIN,  probe_side(m, uncertainty, JMINIMUM, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_JMAX,  probe_side(m, uncertainty, JMAXIMUM, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_KMIN,  probe_side(m, uncertainty, KMINIMUM, probe_top_at_edge));
 | 
			
		||||
  TERN_(CALIBRATION_MEASURE_KMAX,  probe_side(m, uncertainty, KMAXIMUM, probe_top_at_edge));
 | 
			
		||||
 | 
			
		||||
  // Compute the measured center of the calibration object.
 | 
			
		||||
  TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2);
 | 
			
		||||
  TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2);
 | 
			
		||||
  TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT]     + m.obj_side[RIGHT])    / 2);
 | 
			
		||||
  TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT]    + m.obj_side[BACK])     / 2);
 | 
			
		||||
  TERN_(HAS_I_CENTER, m.obj_center.i = (m.obj_side[IMINIMUM] + m.obj_side[IMAXIMUM]) / 2);
 | 
			
		||||
  TERN_(HAS_J_CENTER, m.obj_center.j = (m.obj_side[JMINIMUM] + m.obj_side[JMAXIMUM]) / 2);
 | 
			
		||||
  TERN_(HAS_K_CENTER, m.obj_center.k = (m.obj_side[KMINIMUM] + m.obj_side[KMAXIMUM]) / 2);
 | 
			
		||||
 | 
			
		||||
  // Compute the outside diameter of the nozzle at the height
 | 
			
		||||
  // at which it makes contact with the calibration object
 | 
			
		||||
@@ -310,14 +343,17 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
  LINEAR_AXIS_CODE(
 | 
			
		||||
    m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x),
 | 
			
		||||
    m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y),
 | 
			
		||||
    m.pos_error.z = true_center.z - m.obj_center.z
 | 
			
		||||
    m.pos_error.z = true_center.z - m.obj_center.z,
 | 
			
		||||
    m.pos_error.i = TERN0(HAS_I_CENTER, true_center.i - m.obj_center.i),
 | 
			
		||||
    m.pos_error.j = TERN0(HAS_J_CENTER, true_center.j - m.obj_center.j),
 | 
			
		||||
    m.pos_error.k = TERN0(HAS_K_CENTER, true_center.k - m.obj_center.k)
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLED(CALIBRATION_REPORTING)
 | 
			
		||||
  inline void report_measured_faces(const measurements_t &m) {
 | 
			
		||||
    SERIAL_ECHOLNPGM("Sides:");
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
    #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
      SERIAL_ECHOLNPAIR("  Top: ", m.obj_side[TOP]);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if ENABLED(CALIBRATION_MEASURE_LEFT)
 | 
			
		||||
@@ -326,11 +362,37 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
    #if ENABLED(CALIBRATION_MEASURE_RIGHT)
 | 
			
		||||
      SERIAL_ECHOLNPAIR("  Right: ", m.obj_side[RIGHT]);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if ENABLED(CALIBRATION_MEASURE_FRONT)
 | 
			
		||||
      SERIAL_ECHOLNPAIR("  Front: ", m.obj_side[FRONT]);
 | 
			
		||||
    #if HAS_Y_AXIS
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_FRONT)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  Front: ", m.obj_side[FRONT]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_BACK)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  Back: ", m.obj_side[BACK]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if ENABLED(CALIBRATION_MEASURE_BACK)
 | 
			
		||||
      SERIAL_ECHOLNPAIR("  Back: ", m.obj_side[BACK]);
 | 
			
		||||
    #if LINEAR_AXES >= 4
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_IMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_I_MIN ": ", m.obj_side[IMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_IMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_I_MAX ": ", m.obj_side[IMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 5
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_JMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_J_MIN ": ", m.obj_side[JMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_JMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_J_MAX ": ", m.obj_side[JMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 6
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_KMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_K_MIN ": ", m.obj_side[KMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_KMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_K_MAX ": ", m.obj_side[KMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_EOL();
 | 
			
		||||
  }
 | 
			
		||||
@@ -344,6 +406,15 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.obj_center.y);
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.obj_center.z);
 | 
			
		||||
    #if HAS_I_CENTER
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_I_STR, m.obj_center.i);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_J_CENTER
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_J_STR, m.obj_center.j);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_K_CENTER
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_K_STR, m.obj_center.k);
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_EOL();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -357,7 +428,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  Right: ", m.backlash[RIGHT]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(Y)
 | 
			
		||||
    #if HAS_Y_AXIS && AXIS_CAN_CALIBRATE(Y)
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_FRONT)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  Front: ", m.backlash[FRONT]);
 | 
			
		||||
      #endif
 | 
			
		||||
@@ -365,9 +436,33 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  Back: ", m.backlash[BACK]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
    #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
      SERIAL_ECHOLNPAIR("  Top: ", m.backlash[TOP]);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 4 AXIS_CAN_CALIBRATE(I)
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_IMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_I_MIN ": ", m.backlash[IMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_IMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_I_MAX ": ", m.backlash[IMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 5 AXIS_CAN_CALIBRATE(J)
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_JMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_J_MIN ": ", m.backlash[JMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_JMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_J_MAX ": ", m.backlash[JMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    #if LINEAR_AXES >= 6 AXIS_CAN_CALIBRATE(K)
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_KMIN)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_K_MIN ": ", m.backlash[KMINIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if ENABLED(CALIBRATION_MEASURE_KMAX)
 | 
			
		||||
        SERIAL_ECHOLNPAIR("  " STR_K_MAX ": ", m.backlash[KMAXIMUM]);
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_EOL();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -375,29 +470,37 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
 | 
			
		||||
    SERIAL_CHAR('T');
 | 
			
		||||
    SERIAL_ECHO(active_extruder);
 | 
			
		||||
    SERIAL_ECHOLNPGM(" Positional Error:");
 | 
			
		||||
    #if HAS_X_CENTER
 | 
			
		||||
    #if HAS_X_CENTER && AXIS_CAN_CALIBRATE(X)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_X_STR, m.pos_error.x);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_Y_CENTER
 | 
			
		||||
    #if HAS_Y_CENTER && AXIS_CAN_CALIBRATE(Y)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y);
 | 
			
		||||
    #endif
 | 
			
		||||
    if (AXIS_CAN_CALIBRATE(Z)) SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
 | 
			
		||||
    #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_I_CENTER && AXIS_CAN_CALIBRATE(I)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_I_STR, m.pos_error.i);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_J_CENTER && AXIS_CAN_CALIBRATE(J)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_J_STR, m.pos_error.j);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_K_CENTER && AXIS_CAN_CALIBRATE(K)
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_EOL();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inline void report_measured_nozzle_dimensions(const measurements_t &m) {
 | 
			
		||||
    SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
 | 
			
		||||
    #if HAS_X_CENTER || HAS_Y_CENTER
 | 
			
		||||
      #if HAS_X_CENTER
 | 
			
		||||
        SERIAL_ECHOLNPAIR_P(SP_X_STR, m.nozzle_outer_dimension.x);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if HAS_Y_CENTER
 | 
			
		||||
        SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.nozzle_outer_dimension.y);
 | 
			
		||||
      #endif
 | 
			
		||||
    #else
 | 
			
		||||
      UNUSED(m);
 | 
			
		||||
    #if HAS_X_CENTER
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_X_STR, m.nozzle_outer_dimension.x);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_Y_CENTER
 | 
			
		||||
      SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.nozzle_outer_dimension.y);
 | 
			
		||||
    #endif
 | 
			
		||||
    SERIAL_EOL();
 | 
			
		||||
    UNUSED(m);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #if HAS_HOTEND_OFFSET
 | 
			
		||||
@@ -446,8 +549,33 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
 | 
			
		||||
        backlash.distance_mm.y = m.backlash[BACK];
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP];
 | 
			
		||||
    #endif
 | 
			
		||||
      TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]);
 | 
			
		||||
 | 
			
		||||
      #if HAS_I_CENTER
 | 
			
		||||
        backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2;
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_IMIN)
 | 
			
		||||
        backlash.distance_mm.i = m.backlash[IMINIMUM];
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_IMAX)
 | 
			
		||||
        backlash.distance_mm.i = m.backlash[IMAXIMUM];
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if HAS_J_CENTER
 | 
			
		||||
        backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2;
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_JMIN)
 | 
			
		||||
        backlash.distance_mm.j = m.backlash[JMINIMUM];
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_JMAX)
 | 
			
		||||
        backlash.distance_mm.j = m.backlash[JMAXIMUM];
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if HAS_K_CENTER
 | 
			
		||||
        backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2;
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_KMIN)
 | 
			
		||||
        backlash.distance_mm.k = m.backlash[KMINIMUM];
 | 
			
		||||
      #elif ENABLED(CALIBRATION_MEASURE_KMAX)
 | 
			
		||||
        backlash.distance_mm.k = m.backlash[KMAXIMUM];
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
    #endif // BACKLASH_GCODE
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #if ENABLED(BACKLASH_GCODE)
 | 
			
		||||
@@ -458,7 +586,8 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
 | 
			
		||||
      TEMPORARY_BACKLASH_CORRECTION(all_on);
 | 
			
		||||
      TEMPORARY_BACKLASH_SMOOTHING(0.0f);
 | 
			
		||||
      const xyz_float_t move = LINEAR_AXIS_ARRAY(
 | 
			
		||||
        AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3
 | 
			
		||||
        AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3,
 | 
			
		||||
        AXIS_CAN_CALIBRATE(I) * 3, AXIS_CAN_CALIBRATE(J) * 3, AXIS_CAN_CALIBRATE(K) * 3
 | 
			
		||||
      );
 | 
			
		||||
      current_position += move; calibration_move();
 | 
			
		||||
      current_position -= move; calibration_move();
 | 
			
		||||
@@ -487,11 +616,7 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
 | 
			
		||||
  TEMPORARY_BACKLASH_CORRECTION(all_on);
 | 
			
		||||
  TEMPORARY_BACKLASH_SMOOTHING(0.0f);
 | 
			
		||||
 | 
			
		||||
  #if HAS_MULTI_HOTEND
 | 
			
		||||
    set_nozzle(m, extruder);
 | 
			
		||||
  #else
 | 
			
		||||
    UNUSED(extruder);
 | 
			
		||||
  #endif
 | 
			
		||||
  TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder));
 | 
			
		||||
 | 
			
		||||
  probe_sides(m, uncertainty);
 | 
			
		||||
 | 
			
		||||
@@ -510,6 +635,10 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
 | 
			
		||||
  if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
 | 
			
		||||
                           if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
 | 
			
		||||
 | 
			
		||||
  TERN_(HAS_I_CENTER, update_measurements(m, I_AXIS));
 | 
			
		||||
  TERN_(HAS_J_CENTER, update_measurements(m, J_AXIS));
 | 
			
		||||
  TERN_(HAS_K_CENTER, update_measurements(m, K_AXIS));
 | 
			
		||||
 | 
			
		||||
  sync_plan_position();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,10 @@ void GcodeSuite::M425() {
 | 
			
		||||
      LINEAR_AXIS_CODE(
 | 
			
		||||
        case X_AXIS: return AXIS_CAN_CALIBRATE(X),
 | 
			
		||||
        case Y_AXIS: return AXIS_CAN_CALIBRATE(Y),
 | 
			
		||||
        case Z_AXIS: return AXIS_CAN_CALIBRATE(Z)
 | 
			
		||||
        case Z_AXIS: return AXIS_CAN_CALIBRATE(Z),
 | 
			
		||||
        case I_AXIS: return AXIS_CAN_CALIBRATE(I),
 | 
			
		||||
        case J_AXIS: return AXIS_CAN_CALIBRATE(J),
 | 
			
		||||
        case K_AXIS: return AXIS_CAN_CALIBRATE(K),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 
 | 
			
		||||
@@ -154,6 +154,9 @@ void GcodeSuite::M205() {
 | 
			
		||||
  if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units();
 | 
			
		||||
  if (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
 | 
			
		||||
  #if HAS_JUNCTION_DEVIATION
 | 
			
		||||
    #if HAS_CLASSIC_JERK && (AXIS4_NAME == 'J' || AXIS5_NAME == 'J' || AXIS6_NAME == 'J')
 | 
			
		||||
      #error "Can't set_max_jerk for 'J' axis because 'J' is used for Junction Deviation."
 | 
			
		||||
    #endif
 | 
			
		||||
    if (parser.seenval('J')) {
 | 
			
		||||
      const float junc_dev = parser.value_linear_units();
 | 
			
		||||
      if (WITHIN(junc_dev, 0.01f, 0.3f)) {
 | 
			
		||||
@@ -170,7 +173,10 @@ void GcodeSuite::M205() {
 | 
			
		||||
      if (parser.seenval('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if (parser.seenval('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if (parser.seenval('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if ((seenZ = parser.seenval('Z'))) planner.set_max_jerk(Z_AXIS, parser.value_linear_units())
 | 
			
		||||
      if ((seenZ = parser.seenval('Z'))) planner.set_max_jerk(Z_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if (parser.seenval(AXIS4_NAME)) planner.set_max_jerk(I_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if (parser.seenval(AXIS5_NAME)) planner.set_max_jerk(J_AXIS, parser.value_linear_units()),
 | 
			
		||||
      if (parser.seenval(AXIS6_NAME)) planner.set_max_jerk(K_AXIS, parser.value_linear_units())
 | 
			
		||||
    );
 | 
			
		||||
    #if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
 | 
			
		||||
      if (seenZ && planner.max_jerk.z <= 0.1f)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,11 @@ void report_M92(const bool echo=true, const int8_t e=-1) {
 | 
			
		||||
  SERIAL_ECHOPAIR_P(LIST_N(DOUBLE(LINEAR_AXES),
 | 
			
		||||
    PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
 | 
			
		||||
    SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
 | 
			
		||||
    SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS])
 | 
			
		||||
  ));
 | 
			
		||||
    SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]),
 | 
			
		||||
    SP_I_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[I_AXIS]),
 | 
			
		||||
    SP_J_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[J_AXIS]),
 | 
			
		||||
    SP_K_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[K_AXIS]))
 | 
			
		||||
  );
 | 
			
		||||
  #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
 | 
			
		||||
    SERIAL_ECHOPAIR_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS]));
 | 
			
		||||
  #endif
 | 
			
		||||
@@ -67,7 +70,7 @@ void GcodeSuite::M92() {
 | 
			
		||||
 | 
			
		||||
  // No arguments? Show M92 report.
 | 
			
		||||
  if (!parser.seen(
 | 
			
		||||
    LOGICAL_AXIS_GANG("E", "X", "Y", "Z")
 | 
			
		||||
    LOGICAL_AXIS_GANG("E", "X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR)
 | 
			
		||||
    TERN_(MAGIC_NUMBERS_GCODE, "HL")
 | 
			
		||||
  )) return report_M92(true, target_extruder);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,12 +33,15 @@
 | 
			
		||||
 * M17: Enable stepper motors
 | 
			
		||||
 */
 | 
			
		||||
void GcodeSuite::M17() {
 | 
			
		||||
  if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"))) {
 | 
			
		||||
  if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR))) {
 | 
			
		||||
    LOGICAL_AXIS_CODE(
 | 
			
		||||
      if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) enable_e_steppers(),
 | 
			
		||||
      if (parser.seen_test('X'))        ENABLE_AXIS_X(),
 | 
			
		||||
      if (parser.seen_test('Y'))        ENABLE_AXIS_Y(),
 | 
			
		||||
      if (parser.seen_test('Z'))        ENABLE_AXIS_Z()
 | 
			
		||||
      if (parser.seen_test('Z'))        ENABLE_AXIS_Z(),
 | 
			
		||||
      if (parser.seen_test(AXIS4_NAME)) ENABLE_AXIS_I(),
 | 
			
		||||
      if (parser.seen_test(AXIS5_NAME)) ENABLE_AXIS_J(),
 | 
			
		||||
      if (parser.seen_test(AXIS6_NAME)) ENABLE_AXIS_K()
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
@@ -56,13 +59,16 @@ void GcodeSuite::M18_M84() {
 | 
			
		||||
    stepper_inactive_time = parser.value_millis_from_seconds();
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"))) {
 | 
			
		||||
    if (parser.seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR))) {
 | 
			
		||||
      planner.synchronize();
 | 
			
		||||
      LOGICAL_AXIS_CODE(
 | 
			
		||||
        if (TERN0(HAS_E_STEPPER_ENABLE, parser.seen_test('E'))) disable_e_steppers(),
 | 
			
		||||
        if (parser.seen_test('X'))        DISABLE_AXIS_X(),
 | 
			
		||||
        if (parser.seen_test('Y'))        DISABLE_AXIS_Y(),
 | 
			
		||||
        if (parser.seen_test('Z'))        DISABLE_AXIS_Z()
 | 
			
		||||
        if (parser.seen_test('Z'))        DISABLE_AXIS_Z(),
 | 
			
		||||
        if (parser.seen_test(AXIS4_NAME)) DISABLE_AXIS_I(),
 | 
			
		||||
        if (parser.seen_test(AXIS5_NAME)) DISABLE_AXIS_J(),
 | 
			
		||||
        if (parser.seen_test(AXIS6_NAME)) DISABLE_AXIS_K()
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
 
 | 
			
		||||
@@ -70,26 +70,12 @@
 | 
			
		||||
      dual_x_carriage_mode = (DualXMode)parser.value_byte();
 | 
			
		||||
      idex_set_mirrored_mode(false);
 | 
			
		||||
 | 
			
		||||
      if (dual_x_carriage_mode == DXC_MIRRORED_MODE) {
 | 
			
		||||
        if (previous_mode != DXC_DUPLICATION_MODE) {
 | 
			
		||||
          SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
 | 
			
		||||
          SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
 | 
			
		||||
          dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        idex_set_mirrored_mode(true);
 | 
			
		||||
        float x_jog = current_position.x - .1;
 | 
			
		||||
        for (uint8_t i = 2; --i;) {
 | 
			
		||||
          planner.buffer_line(x_jog, current_position.y, current_position.z, current_position.e, feedrate_mm_s, 0);
 | 
			
		||||
          x_jog += .1;
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      switch (dual_x_carriage_mode) {
 | 
			
		||||
 | 
			
		||||
        case DXC_FULL_CONTROL_MODE:
 | 
			
		||||
        case DXC_AUTO_PARK_MODE:
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case DXC_DUPLICATION_MODE:
 | 
			
		||||
          // Set the X offset, but no less than the safety gap
 | 
			
		||||
          if (parser.seen('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS));
 | 
			
		||||
@@ -97,10 +83,29 @@
 | 
			
		||||
          // Always switch back to tool 0
 | 
			
		||||
          if (active_extruder != 0) tool_change(0);
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case DXC_MIRRORED_MODE: {
 | 
			
		||||
          if (previous_mode != DXC_DUPLICATION_MODE) {
 | 
			
		||||
            SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
 | 
			
		||||
            SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
 | 
			
		||||
            dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          idex_set_mirrored_mode(true);
 | 
			
		||||
 | 
			
		||||
          // Do a small 'jog' motion in the X axis
 | 
			
		||||
          xyze_pos_t dest = current_position; dest.x -= 0.1f;
 | 
			
		||||
          for (uint8_t i = 2; --i;) {
 | 
			
		||||
            planner.buffer_line(dest, feedrate_mm_s, 0);
 | 
			
		||||
            dest.x += 0.1f;
 | 
			
		||||
          }
 | 
			
		||||
        } return;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
          dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      idex_set_parked(false);
 | 
			
		||||
      set_duplication_enabled(false);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -253,7 +253,7 @@ void GcodeSuite::M906() {
 | 
			
		||||
        #endif
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      #if LINEAR_AXES >= XY
 | 
			
		||||
      #if HAS_Y_AXIS
 | 
			
		||||
        case Y_AXIS:
 | 
			
		||||
          #if AXIS_IS_L64XX(Y)
 | 
			
		||||
            if (index == 0) L6470_SET_KVAL_HOLD(Y);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,12 +47,13 @@ void GcodeSuite::G60() {
 | 
			
		||||
  SBI(saved_slots[slot >> 3], slot & 0x07);
 | 
			
		||||
 | 
			
		||||
  #if ENABLED(SAVED_POSITIONS_DEBUG)
 | 
			
		||||
    const xyze_pos_t &pos = stored_position[slot];
 | 
			
		||||
    DEBUG_ECHOPAIR(STR_SAVED_POS " S", slot);
 | 
			
		||||
    DEBUG_ECHOPAIR_F(" : X", pos.x);
 | 
			
		||||
    DEBUG_ECHOPAIR_F_P(SP_Y_STR, pos.y);
 | 
			
		||||
    DEBUG_ECHOPAIR_F_P(SP_Z_STR, pos.z);
 | 
			
		||||
    DEBUG_ECHOLNPAIR_F_P(SP_E_STR, pos.e);
 | 
			
		||||
    const xyze_pos_t &pos = stored_position[slot];
 | 
			
		||||
    DEBUG_ECHOLNPAIR_F_P(
 | 
			
		||||
      LIST_N(DOUBLE(LOGICAL_AXES), SP_E_STR, pos.e,
 | 
			
		||||
      PSTR(" : X"), pos.x, SP_Y_STR, pos.y, SP_Z_STR, pos.z,
 | 
			
		||||
      SP_I_STR, pos.i, SP_J_STR, pos.j, SP_K_STR, pos.k)
 | 
			
		||||
    );
 | 
			
		||||
  #endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ void GcodeSuite::G61(void) {
 | 
			
		||||
 | 
			
		||||
  const uint8_t slot = parser.byteval('S');
 | 
			
		||||
 | 
			
		||||
  #define SYNC_E(POINT) planner.set_e_position_mm((destination.e = current_position.e = (POINT)))
 | 
			
		||||
  #define SYNC_E(POINT) TERN_(HAS_EXTRUDERS, planner.set_e_position_mm((destination.e = current_position.e = (POINT))))
 | 
			
		||||
 | 
			
		||||
  #if SAVED_POSITIONS < 256
 | 
			
		||||
    if (slot >= SAVED_POSITIONS) {
 | 
			
		||||
@@ -68,7 +68,7 @@ void GcodeSuite::G61(void) {
 | 
			
		||||
    SYNC_E(stored_position[slot].e);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    if (parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z"))) {
 | 
			
		||||
    if (parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR))) {
 | 
			
		||||
      DEBUG_ECHOPAIR(STR_RESTORING_POS " S", slot);
 | 
			
		||||
      LOOP_LINEAR_AXES(i) {
 | 
			
		||||
        destination[i] = parser.seen(AXIS_CHAR(i))
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ void GcodeSuite::M122() {
 | 
			
		||||
  xyze_bool_t print_axis = ARRAY_N_1(LOGICAL_AXES, false);
 | 
			
		||||
 | 
			
		||||
  bool print_all = true;
 | 
			
		||||
  LOOP_LOGICAL_AXES(i) if (parser.seen(axis_codes[i])) { print_axis[i] = true; print_all = false; }
 | 
			
		||||
  LOOP_LOGICAL_AXES(i) if (parser.seen_test(axis_codes[i])) { print_axis[i] = true; print_all = false; }
 | 
			
		||||
 | 
			
		||||
  if (print_all) LOOP_LOGICAL_AXES(i) print_axis[i] = true;
 | 
			
		||||
 | 
			
		||||
@@ -49,21 +49,13 @@ void GcodeSuite::M122() {
 | 
			
		||||
      tmc_set_report_interval(interval);
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    if (parser.seen_test('V')) {
 | 
			
		||||
      tmc_get_registers(
 | 
			
		||||
        LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      tmc_report_all(
 | 
			
		||||
        LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    if (parser.seen_test('V'))
 | 
			
		||||
      tmc_get_registers(LOGICAL_AXIS_ELEM(print_axis));
 | 
			
		||||
    else
 | 
			
		||||
      tmc_report_all(LOGICAL_AXIS_ELEM(print_axis));
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  test_tmc_connection(
 | 
			
		||||
    LOGICAL_AXIS_LIST(print_axis.e, print_axis.x, print_axis.y, print_axis.z)
 | 
			
		||||
  );
 | 
			
		||||
  test_tmc_connection(LOGICAL_AXIS_ELEM(print_axis));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // HAS_TRINAMIC_CONFIG
 | 
			
		||||
 
 | 
			
		||||
@@ -43,81 +43,56 @@ void tmc_set_stealthChop(TMC &st, const bool enable) {
 | 
			
		||||
static void set_stealth_status(const bool enable, const int8_t target_extruder) {
 | 
			
		||||
  #define TMC_SET_STEALTH(Q) tmc_set_stealthChop(stepper##Q, enable)
 | 
			
		||||
 | 
			
		||||
  #if    AXIS_HAS_STEALTHCHOP(X)  || AXIS_HAS_STEALTHCHOP(X2) \
 | 
			
		||||
      || AXIS_HAS_STEALTHCHOP(Y)  || AXIS_HAS_STEALTHCHOP(Y2) \
 | 
			
		||||
      || AXIS_HAS_STEALTHCHOP(Z)  || AXIS_HAS_STEALTHCHOP(Z2) \
 | 
			
		||||
      || AXIS_HAS_STEALTHCHOP(Z3) || AXIS_HAS_STEALTHCHOP(Z4)
 | 
			
		||||
  #if    X_HAS_STEALTHCHOP  || Y_HAS_STEALTHCHOP  || Z_HAS_STEALTHCHOP \
 | 
			
		||||
      || I_HAS_STEALTHCHOP  || J_HAS_STEALTHCHOP  || K_HAS_STEALTHCHOP \
 | 
			
		||||
      || X2_HAS_STEALTHCHOP || Y2_HAS_STEALTHCHOP || Z2_HAS_STEALTHCHOP || Z3_HAS_STEALTHCHOP || Z4_HAS_STEALTHCHOP
 | 
			
		||||
    const uint8_t index = parser.byteval('I');
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  LOOP_LOGICAL_AXES(i) if (parser.seen(axis_codes[i])) {
 | 
			
		||||
    switch (i) {
 | 
			
		||||
      case X_AXIS:
 | 
			
		||||
        #if AXIS_HAS_STEALTHCHOP(X)
 | 
			
		||||
          if (index == 0) TMC_SET_STEALTH(X);
 | 
			
		||||
        #endif
 | 
			
		||||
        #if AXIS_HAS_STEALTHCHOP(X2)
 | 
			
		||||
          if (index == 1) TMC_SET_STEALTH(X2);
 | 
			
		||||
        #endif
 | 
			
		||||
        TERN_(X_HAS_STEALTHCHOP,  if (index == 0) TMC_SET_STEALTH(X));
 | 
			
		||||
        TERN_(X2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(X2));
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      #if LINEAR_AXES >= XY
 | 
			
		||||
      #if HAS_Y_AXIS
 | 
			
		||||
        case Y_AXIS:
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Y)
 | 
			
		||||
            if (index == 0) TMC_SET_STEALTH(Y);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Y2)
 | 
			
		||||
            if (index == 1) TMC_SET_STEALTH(Y2);
 | 
			
		||||
          #endif
 | 
			
		||||
          TERN_(Y_HAS_STEALTHCHOP,  if (index == 0) TMC_SET_STEALTH(Y));
 | 
			
		||||
          TERN_(Y2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(Y2));
 | 
			
		||||
          break;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if HAS_Z_AXIS
 | 
			
		||||
        case Z_AXIS:
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z)
 | 
			
		||||
            if (index == 0) TMC_SET_STEALTH(Z);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z2)
 | 
			
		||||
            if (index == 1) TMC_SET_STEALTH(Z2);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z3)
 | 
			
		||||
            if (index == 2) TMC_SET_STEALTH(Z3);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z4)
 | 
			
		||||
            if (index == 3) TMC_SET_STEALTH(Z4);
 | 
			
		||||
          #endif
 | 
			
		||||
          TERN_(Z_HAS_STEALTHCHOP,  if (index == 0) TMC_SET_STEALTH(Z));
 | 
			
		||||
          TERN_(Z2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(Z2));
 | 
			
		||||
          TERN_(Z3_HAS_STEALTHCHOP, if (index == 2) TMC_SET_STEALTH(Z3));
 | 
			
		||||
          TERN_(Z4_HAS_STEALTHCHOP, if (index == 3) TMC_SET_STEALTH(Z4));
 | 
			
		||||
          break;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if I_HAS_STEALTHCHOP
 | 
			
		||||
        case I_AXIS: TMC_SET_STEALTH(I); break;
 | 
			
		||||
      #endif
 | 
			
		||||
      #if J_HAS_STEALTHCHOP
 | 
			
		||||
        case J_AXIS: TMC_SET_STEALTH(J); break;
 | 
			
		||||
      #endif
 | 
			
		||||
      #if K_HAS_STEALTHCHOP
 | 
			
		||||
        case K_AXIS: TMC_SET_STEALTH(K); break;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if HAS_EXTRUDERS
 | 
			
		||||
        case E_AXIS: {
 | 
			
		||||
          if (target_extruder < 0) return;
 | 
			
		||||
          switch (target_extruder) {
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E0)
 | 
			
		||||
              case 0: TMC_SET_STEALTH(E0); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E1)
 | 
			
		||||
              case 1: TMC_SET_STEALTH(E1); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E2)
 | 
			
		||||
              case 2: TMC_SET_STEALTH(E2); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E3)
 | 
			
		||||
              case 3: TMC_SET_STEALTH(E3); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E4)
 | 
			
		||||
              case 4: TMC_SET_STEALTH(E4); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E5)
 | 
			
		||||
              case 5: TMC_SET_STEALTH(E5); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E6)
 | 
			
		||||
              case 6: TMC_SET_STEALTH(E6); break;
 | 
			
		||||
            #endif
 | 
			
		||||
            #if AXIS_HAS_STEALTHCHOP(E7)
 | 
			
		||||
              case 7: TMC_SET_STEALTH(E7); break;
 | 
			
		||||
            #endif
 | 
			
		||||
          }
 | 
			
		||||
          OPTCODE(E0_HAS_STEALTHCHOP, else if (target_extruder == 0) TMC_SET_STEALTH(E0))
 | 
			
		||||
          OPTCODE(E1_HAS_STEALTHCHOP, else if (target_extruder == 1) TMC_SET_STEALTH(E1))
 | 
			
		||||
          OPTCODE(E2_HAS_STEALTHCHOP, else if (target_extruder == 2) TMC_SET_STEALTH(E2))
 | 
			
		||||
          OPTCODE(E3_HAS_STEALTHCHOP, else if (target_extruder == 3) TMC_SET_STEALTH(E3))
 | 
			
		||||
          OPTCODE(E4_HAS_STEALTHCHOP, else if (target_extruder == 4) TMC_SET_STEALTH(E4))
 | 
			
		||||
          OPTCODE(E5_HAS_STEALTHCHOP, else if (target_extruder == 5) TMC_SET_STEALTH(E5))
 | 
			
		||||
          OPTCODE(E6_HAS_STEALTHCHOP, else if (target_extruder == 6) TMC_SET_STEALTH(E6))
 | 
			
		||||
          OPTCODE(E7_HAS_STEALTHCHOP, else if (target_extruder == 7) TMC_SET_STEALTH(E7))
 | 
			
		||||
        } break;
 | 
			
		||||
      #endif
 | 
			
		||||
    }
 | 
			
		||||
@@ -126,55 +101,25 @@ static void set_stealth_status(const bool enable, const int8_t target_extruder)
 | 
			
		||||
 | 
			
		||||
static void say_stealth_status() {
 | 
			
		||||
  #define TMC_SAY_STEALTH_STATUS(Q) tmc_say_stealth_status(stepper##Q)
 | 
			
		||||
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(X)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(X);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(X2)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(X2);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Y)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Y);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Y2)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Y2);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Z)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Z);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Z2)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Z2);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Z3)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Z3);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(Z4)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(Z4);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E0)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E0);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E1)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E1);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E2)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E2);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E3)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E3);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E4)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E4);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E5)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E5);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E6)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E6);
 | 
			
		||||
  #endif
 | 
			
		||||
  #if AXIS_HAS_STEALTHCHOP(E7)
 | 
			
		||||
    TMC_SAY_STEALTH_STATUS(E7);
 | 
			
		||||
  #endif
 | 
			
		||||
  OPTCODE( X_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(X))
 | 
			
		||||
  OPTCODE(X2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(X2))
 | 
			
		||||
  OPTCODE( Y_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Y))
 | 
			
		||||
  OPTCODE(Y2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Y2))
 | 
			
		||||
  OPTCODE( Z_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z))
 | 
			
		||||
  OPTCODE(Z2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z2))
 | 
			
		||||
  OPTCODE(Z3_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z3))
 | 
			
		||||
  OPTCODE(Z4_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z4))
 | 
			
		||||
  OPTCODE( I_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(I))
 | 
			
		||||
  OPTCODE( J_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(J))
 | 
			
		||||
  OPTCODE( K_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(K))
 | 
			
		||||
  OPTCODE(E0_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E0))
 | 
			
		||||
  OPTCODE(E1_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E1))
 | 
			
		||||
  OPTCODE(E2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E2))
 | 
			
		||||
  OPTCODE(E3_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E3))
 | 
			
		||||
  OPTCODE(E4_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E4))
 | 
			
		||||
  OPTCODE(E5_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E5))
 | 
			
		||||
  OPTCODE(E6_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E6))
 | 
			
		||||
  OPTCODE(E7_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E7))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ void GcodeSuite::M906() {
 | 
			
		||||
 | 
			
		||||
  bool report = true;
 | 
			
		||||
 | 
			
		||||
  #if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
 | 
			
		||||
  #if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4) || AXIS_IS_TMC(I) || AXIS_IS_TMC(J) || AXIS_IS_TMC(K)
 | 
			
		||||
    const uint8_t index = parser.byteval('I');
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
@@ -64,7 +64,7 @@ void GcodeSuite::M906() {
 | 
			
		||||
        #endif
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      #if LINEAR_AXES >= XY
 | 
			
		||||
      #if HAS_Y_AXIS
 | 
			
		||||
        case Y_AXIS:
 | 
			
		||||
          #if AXIS_IS_TMC(Y)
 | 
			
		||||
            if (index == 0) TMC_SET_CURRENT(Y);
 | 
			
		||||
@@ -92,6 +92,16 @@ void GcodeSuite::M906() {
 | 
			
		||||
          break;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if AXIS_IS_TMC(I)
 | 
			
		||||
        case I_AXIS: TMC_SET_CURRENT(I); break;
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_TMC(J)
 | 
			
		||||
        case J_AXIS: TMC_SET_CURRENT(J); break;
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_TMC(K)
 | 
			
		||||
        case K_AXIS: TMC_SET_CURRENT(K); break;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      #if HAS_EXTRUDERS
 | 
			
		||||
        case E_AXIS: {
 | 
			
		||||
          const int8_t target_extruder = get_target_extruder_from_command();
 | 
			
		||||
@@ -152,6 +162,15 @@ void GcodeSuite::M906() {
 | 
			
		||||
    #if AXIS_IS_TMC(Z4)
 | 
			
		||||
      TMC_SAY_CURRENT(Z4);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_IS_TMC(I)
 | 
			
		||||
      TMC_SAY_CURRENT(I);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_IS_TMC(J)
 | 
			
		||||
      TMC_SAY_CURRENT(J);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_IS_TMC(K)
 | 
			
		||||
      TMC_SAY_CURRENT(K);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if AXIS_IS_TMC(E0)
 | 
			
		||||
      TMC_SAY_CURRENT(E0);
 | 
			
		||||
    #endif
 | 
			
		||||
 
 | 
			
		||||
@@ -38,18 +38,27 @@
 | 
			
		||||
  #if M91x_USE(X) || M91x_USE(X2)
 | 
			
		||||
    #define M91x_SOME_X 1
 | 
			
		||||
  #endif
 | 
			
		||||
  #if M91x_USE(Y) || M91x_USE(Y2)
 | 
			
		||||
  #if LINEAR_AXES >= 2 && (M91x_USE(Y) || M91x_USE(Y2))
 | 
			
		||||
    #define M91x_SOME_Y 1
 | 
			
		||||
  #endif
 | 
			
		||||
  #if M91x_USE(Z) || M91x_USE(Z2) || M91x_USE(Z3) || M91x_USE(Z4)
 | 
			
		||||
  #if HAS_Z_AXIS && (M91x_USE(Z) || M91x_USE(Z2) || M91x_USE(Z3) || M91x_USE(Z4))
 | 
			
		||||
    #define M91x_SOME_Z 1
 | 
			
		||||
  #endif
 | 
			
		||||
  #if LINEAR_AXES >= 4 && M91x_USE(I)
 | 
			
		||||
    #define M91x_USE_I 1
 | 
			
		||||
  #endif
 | 
			
		||||
  #if LINEAR_AXES >= 5 && M91x_USE(J)
 | 
			
		||||
    #define M91x_USE_J 1
 | 
			
		||||
  #endif
 | 
			
		||||
  #if LINEAR_AXES >= 6 && M91x_USE(K)
 | 
			
		||||
    #define M91x_USE_K 1
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  #if M91x_USE_E(0) || M91x_USE_E(1) || M91x_USE_E(2) || M91x_USE_E(3) || M91x_USE_E(4) || M91x_USE_E(5) || M91x_USE_E(6) || M91x_USE_E(7)
 | 
			
		||||
    #define M91x_SOME_E 1
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  #if !M91x_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_SOME_E
 | 
			
		||||
  #if !M91x_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_USE_I && !M91x_USE_J && !M91x_USE_K && !M91x_SOME_E
 | 
			
		||||
    #error "MONITOR_DRIVER_STATUS requires at least one TMC2130, 2160, 2208, 2209, 2660, 5130, or 5160."
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
@@ -82,6 +91,9 @@
 | 
			
		||||
    #if M91x_USE(Z4)
 | 
			
		||||
      tmc_report_otpw(stepperZ4);
 | 
			
		||||
    #endif
 | 
			
		||||
    TERN_(M91x_USE_I, tmc_report_otpw(stepperI));
 | 
			
		||||
    TERN_(M91x_USE_J, tmc_report_otpw(stepperJ));
 | 
			
		||||
    TERN_(M91x_USE_K, tmc_report_otpw(stepperK));
 | 
			
		||||
    #if M91x_USE_E(0)
 | 
			
		||||
      tmc_report_otpw(stepperE0);
 | 
			
		||||
    #endif
 | 
			
		||||
@@ -124,9 +136,12 @@
 | 
			
		||||
    const bool hasX = TERN0(M91x_SOME_X, parser.seen(axis_codes.x)),
 | 
			
		||||
               hasY = TERN0(M91x_SOME_Y, parser.seen(axis_codes.y)),
 | 
			
		||||
               hasZ = TERN0(M91x_SOME_Z, parser.seen(axis_codes.z)),
 | 
			
		||||
               hasI = TERN0(M91x_USE_I,  parser.seen(axis_codes.i)),
 | 
			
		||||
               hasJ = TERN0(M91x_USE_J,  parser.seen(axis_codes.j)),
 | 
			
		||||
               hasK = TERN0(M91x_USE_K,  parser.seen(axis_codes.k)),
 | 
			
		||||
               hasE = TERN0(M91x_SOME_E, parser.seen(axis_codes.e));
 | 
			
		||||
 | 
			
		||||
    const bool hasNone = !hasE && !hasX && !hasY && !hasZ;
 | 
			
		||||
    const bool hasNone = !hasE && !hasX && !hasY && !hasZ && !hasI && !hasJ && !hasK;
 | 
			
		||||
 | 
			
		||||
    #if M91x_SOME_X
 | 
			
		||||
      const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF));
 | 
			
		||||
@@ -164,6 +179,19 @@
 | 
			
		||||
      #endif
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    #if M91x_USE_I
 | 
			
		||||
      const int8_t ival = int8_t(parser.byteval(axis_codes.i, 0xFF));
 | 
			
		||||
      if (hasNone || ival == 1 || (hasI && ival < 0)) tmc_clear_otpw(stepperI);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if M91x_USE_J
 | 
			
		||||
      const int8_t jval = int8_t(parser.byteval(axis_codes.j, 0xFF));
 | 
			
		||||
      if (hasNone || jval == 1 || (hasJ && jval < 0)) tmc_clear_otpw(stepperJ);
 | 
			
		||||
    #endif
 | 
			
		||||
    #if M91x_USE_K
 | 
			
		||||
      const int8_t kval = int8_t(parser.byteval(axis_codes.k, 0xFF));
 | 
			
		||||
      if (hasNone || kval == 1 || (hasK && kval < 0)) tmc_clear_otpw(stepperK);
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    #if M91x_SOME_E
 | 
			
		||||
      const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF));
 | 
			
		||||
      #if M91x_USE_E(0)
 | 
			
		||||
@@ -206,126 +234,76 @@
 | 
			
		||||
    #define TMC_SET_PWMTHRS_E(E) stepperE##E.set_pwm_thrs(value)
 | 
			
		||||
 | 
			
		||||
    bool report = true;
 | 
			
		||||
    #if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
 | 
			
		||||
    #if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4) || AXIS_IS_TMC(I) || AXIS_IS_TMC(J) || AXIS_IS_TMC(K)
 | 
			
		||||
      const uint8_t index = parser.byteval('I');
 | 
			
		||||
    #endif
 | 
			
		||||
    LOOP_LOGICAL_AXES(i) if (int32_t value = parser.longval(axis_codes[i])) {
 | 
			
		||||
      report = false;
 | 
			
		||||
      switch (i) {
 | 
			
		||||
        case X_AXIS:
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(X)
 | 
			
		||||
            if (index < 2) TMC_SET_PWMTHRS(X,X);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(X2)
 | 
			
		||||
            if (!(index & 1)) TMC_SET_PWMTHRS(X,X2);
 | 
			
		||||
          #endif
 | 
			
		||||
          TERN_(X_HAS_STEALTHCHOP,  if (index < 2) TMC_SET_PWMTHRS(X,X));
 | 
			
		||||
          TERN_(X2_HAS_STEALTHCHOP, if (!(index & 1)) TMC_SET_PWMTHRS(X,X2));
 | 
			
		||||
          break;
 | 
			
		||||
        case Y_AXIS:
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Y)
 | 
			
		||||
            if (index < 2) TMC_SET_PWMTHRS(Y,Y);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Y2)
 | 
			
		||||
            if (!(index & 1)) TMC_SET_PWMTHRS(Y,Y2);
 | 
			
		||||
          #endif
 | 
			
		||||
          TERN_(Y_HAS_STEALTHCHOP,  if (index < 2) TMC_SET_PWMTHRS(Y,Y));
 | 
			
		||||
          TERN_(Y2_HAS_STEALTHCHOP, if (!(index & 1)) TMC_SET_PWMTHRS(Y,Y2));
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        #if I_HAS_STEALTHCHOP
 | 
			
		||||
          case I_AXIS: TMC_SET_PWMTHRS(I,I); break;
 | 
			
		||||
        #endif
 | 
			
		||||
        #if J_HAS_STEALTHCHOP
 | 
			
		||||
          case J_AXIS: TMC_SET_PWMTHRS(J,J); break;
 | 
			
		||||
        #endif
 | 
			
		||||
        #if K_HAS_STEALTHCHOP
 | 
			
		||||
          case K_AXIS: TMC_SET_PWMTHRS(K,K); break;
 | 
			
		||||
        #endif
 | 
			
		||||
 | 
			
		||||
        case Z_AXIS:
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z)
 | 
			
		||||
            if (index < 2) TMC_SET_PWMTHRS(Z,Z);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z2)
 | 
			
		||||
            if (index == 0 || index == 2) TMC_SET_PWMTHRS(Z,Z2);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z3)
 | 
			
		||||
            if (index == 0 || index == 3) TMC_SET_PWMTHRS(Z,Z3);
 | 
			
		||||
          #endif
 | 
			
		||||
          #if AXIS_HAS_STEALTHCHOP(Z4)
 | 
			
		||||
            if (index == 0 || index == 4) TMC_SET_PWMTHRS(Z,Z4);
 | 
			
		||||
          #endif
 | 
			
		||||
          TERN_(Z_HAS_STEALTCHOP, if (index < 2) TMC_SET_PWMTHRS(Z,Z));
 | 
			
		||||
          TERN_(Z2_HAS_STEALTCHOP, if (index == 0 || index == 2) TMC_SET_PWMTHRS(Z,Z2));
 | 
			
		||||
          TERN_(Z3_HAS_STEALTCHOP, if (index == 0 || index == 3) TMC_SET_PWMTHRS(Z,Z3));
 | 
			
		||||
          TERN_(Z4_HAS_STEALTCHOP, if (index == 0 || index == 4) TMC_SET_PWMTHRS(Z,Z4));
 | 
			
		||||
          break;
 | 
			
		||||
        case E_AXIS: {
 | 
			
		||||
          #if E_STEPPERS
 | 
			
		||||
            const int8_t target_extruder = get_target_extruder_from_command();
 | 
			
		||||
            if (target_extruder < 0) return;
 | 
			
		||||
            switch (target_extruder) {
 | 
			
		||||
              #if AXIS_HAS_STEALTHCHOP(E0)
 | 
			
		||||
                case 0: TMC_SET_PWMTHRS_E(0); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
 | 
			
		||||
                case 1: TMC_SET_PWMTHRS_E(1); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
 | 
			
		||||
                case 2: TMC_SET_PWMTHRS_E(2); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
 | 
			
		||||
                case 3: TMC_SET_PWMTHRS_E(3); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
 | 
			
		||||
                case 4: TMC_SET_PWMTHRS_E(4); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
 | 
			
		||||
                case 5: TMC_SET_PWMTHRS_E(5); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
 | 
			
		||||
                case 6: TMC_SET_PWMTHRS_E(6); break;
 | 
			
		||||
              #endif
 | 
			
		||||
              #if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
 | 
			
		||||
                case 7: TMC_SET_PWMTHRS_E(7); break;
 | 
			
		||||
              #endif
 | 
			
		||||
            }
 | 
			
		||||
            TERN_(E0_HAS_STEALTHCHOP, else if (target_extruder == 0) TMC_SET_PWMTHRS_E(0));
 | 
			
		||||
            TERN_(E1_HAS_STEALTHCHOP, else if (target_extruder == 1) TMC_SET_PWMTHRS_E(1));
 | 
			
		||||
            TERN_(E2_HAS_STEALTHCHOP, else if (target_extruder == 2) TMC_SET_PWMTHRS_E(2));
 | 
			
		||||
            TERN_(E3_HAS_STEALTHCHOP, else if (target_extruder == 3) TMC_SET_PWMTHRS_E(3));
 | 
			
		||||
            TERN_(E4_HAS_STEALTHCHOP, else if (target_extruder == 4) TMC_SET_PWMTHRS_E(4));
 | 
			
		||||
            TERN_(E5_HAS_STEALTHCHOP, else if (target_extruder == 5) TMC_SET_PWMTHRS_E(5));
 | 
			
		||||
            TERN_(E6_HAS_STEALTHCHOP, else if (target_extruder == 6) TMC_SET_PWMTHRS_E(6));
 | 
			
		||||
            TERN_(E7_HAS_STEALTHCHOP, else if (target_extruder == 7) TMC_SET_PWMTHRS_E(7));
 | 
			
		||||
          #endif // E_STEPPERS
 | 
			
		||||
        } break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (report) {
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(X)
 | 
			
		||||
        TMC_SAY_PWMTHRS(X,X);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(X2)
 | 
			
		||||
        TMC_SAY_PWMTHRS(X,X2);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Y)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Y,Y);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Y2)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Y,Y2);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Z)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Z,Z);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Z2)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Z,Z2);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Z3)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Z,Z3);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_HAS_STEALTHCHOP(Z4)
 | 
			
		||||
        TMC_SAY_PWMTHRS(Z,Z4);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS && AXIS_HAS_STEALTHCHOP(E0)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(0);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(1);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(2);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(3);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(4);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(5);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(6);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
 | 
			
		||||
        TMC_SAY_PWMTHRS_E(7);
 | 
			
		||||
      #endif
 | 
			
		||||
      TERN_( X_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(X,X));
 | 
			
		||||
      TERN_(X2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(X,X2));
 | 
			
		||||
      TERN_( Y_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Y,Y));
 | 
			
		||||
      TERN_(Y2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Y,Y2));
 | 
			
		||||
      TERN_( Z_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z));
 | 
			
		||||
      TERN_(Z2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z2));
 | 
			
		||||
      TERN_(Z3_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z3));
 | 
			
		||||
      TERN_(Z4_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z4));
 | 
			
		||||
 | 
			
		||||
      TERN_( I_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(I,I));
 | 
			
		||||
      TERN_( J_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(J,J));
 | 
			
		||||
      TERN_( K_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(K,K));
 | 
			
		||||
 | 
			
		||||
      TERN_(E0_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(0));
 | 
			
		||||
      TERN_(E1_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(1));
 | 
			
		||||
      TERN_(E2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(2));
 | 
			
		||||
      TERN_(E3_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(3));
 | 
			
		||||
      TERN_(E4_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(4));
 | 
			
		||||
      TERN_(E5_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(5));
 | 
			
		||||
      TERN_(E6_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(6));
 | 
			
		||||
      TERN_(E7_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(7));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif // HYBRID_THRESHOLD
 | 
			
		||||
@@ -378,6 +356,15 @@
 | 
			
		||||
            #endif
 | 
			
		||||
            break;
 | 
			
		||||
        #endif
 | 
			
		||||
        #if I_SENSORLESS && AXIS_HAS_STALLGUARD(I)
 | 
			
		||||
          case I_AXIS: stepperI.homing_threshold(value); break;
 | 
			
		||||
        #endif
 | 
			
		||||
        #if J_SENSORLESS && AXIS_HAS_STALLGUARD(J)
 | 
			
		||||
          case J_AXIS: stepperJ.homing_threshold(value); break;
 | 
			
		||||
        #endif
 | 
			
		||||
        #if K_SENSORLESS && AXIS_HAS_STALLGUARD(K)
 | 
			
		||||
          case K_AXIS: stepperK.homing_threshold(value); break;
 | 
			
		||||
        #endif
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -412,6 +399,15 @@
 | 
			
		||||
          tmc_print_sgt(stepperZ4);
 | 
			
		||||
        #endif
 | 
			
		||||
      #endif
 | 
			
		||||
      #if I_SENSORLESS && AXIS_HAS_STALLGUARD(I)
 | 
			
		||||
        tmc_print_sgt(stepperI);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if J_SENSORLESS && AXIS_HAS_STALLGUARD(J)
 | 
			
		||||
        tmc_print_sgt(stepperJ);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if K_SENSORLESS && AXIS_HAS_STALLGUARD(K)
 | 
			
		||||
        tmc_print_sgt(stepperK);
 | 
			
		||||
      #endif
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif // USE_SENSORLESS
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,10 @@ uint8_t GcodeSuite::axis_relative = 0 LOGICAL_AXIS_GANG(
 | 
			
		||||
  | (ar_init.e << REL_E),
 | 
			
		||||
  | (ar_init.x << REL_X),
 | 
			
		||||
  | (ar_init.y << REL_Y),
 | 
			
		||||
  | (ar_init.z << REL_Z)
 | 
			
		||||
  | (ar_init.z << REL_Z),
 | 
			
		||||
  | (ar_init.i << REL_I),
 | 
			
		||||
  | (ar_init.j << REL_J),
 | 
			
		||||
  | (ar_init.k << REL_K)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#if EITHER(HAS_AUTO_REPORTING, HOST_KEEPALIVE_FEATURE)
 | 
			
		||||
 
 | 
			
		||||
@@ -315,7 +315,7 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum AxisRelative : uint8_t {
 | 
			
		||||
  LOGICAL_AXIS_LIST(REL_E, REL_X, REL_Y, REL_Z)
 | 
			
		||||
  LOGICAL_AXIS_LIST(REL_E, REL_X, REL_Y, REL_Z, REL_I, REL_J, REL_K)
 | 
			
		||||
  #if HAS_EXTRUDERS
 | 
			
		||||
    , E_MODE_ABS, E_MODE_REL
 | 
			
		||||
  #endif
 | 
			
		||||
@@ -338,7 +338,11 @@ public:
 | 
			
		||||
    return TEST(axis_relative, a);
 | 
			
		||||
  }
 | 
			
		||||
  static inline void set_relative_mode(const bool rel) {
 | 
			
		||||
    axis_relative = rel ? (0 LOGICAL_AXIS_GANG(| _BV(REL_E), | _BV(REL_X), | _BV(REL_Y), | _BV(REL_Z))) : 0;
 | 
			
		||||
    axis_relative = rel ? (0 LOGICAL_AXIS_GANG(
 | 
			
		||||
      | _BV(REL_E),
 | 
			
		||||
      | _BV(REL_X), | _BV(REL_Y), | _BV(REL_Z),
 | 
			
		||||
      | _BV(REL_I), | _BV(REL_J), | _BV(REL_K)
 | 
			
		||||
    )) : 0;
 | 
			
		||||
  }
 | 
			
		||||
  #if HAS_EXTRUDERS
 | 
			
		||||
    static inline void set_e_relative() {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,16 @@
 | 
			
		||||
#include "../../MarlinCore.h"
 | 
			
		||||
 | 
			
		||||
void M206_report() {
 | 
			
		||||
  SERIAL_ECHOLNPAIR_P(PSTR("M206 X"), home_offset.x, SP_Y_STR, home_offset.y, SP_Z_STR, home_offset.z);
 | 
			
		||||
  SERIAL_ECHOLNPAIR_P(
 | 
			
		||||
    LIST_N(DOUBLE(LINEAR_AXES),
 | 
			
		||||
      PSTR("M206 X"), home_offset.x,
 | 
			
		||||
      SP_Y_STR, home_offset.y,
 | 
			
		||||
      SP_Z_STR, home_offset.z,
 | 
			
		||||
      SP_I_STR, home_offset.i,
 | 
			
		||||
      SP_J_STR, home_offset.j,
 | 
			
		||||
      SP_K_STR, home_offset.k,
 | 
			
		||||
    )
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -51,7 +60,7 @@ void GcodeSuite::M206() {
 | 
			
		||||
    if (parser.seen('P')) set_home_offset(B_AXIS, parser.value_float()); // Psi
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  if (!parser.seen("XYZ"))
 | 
			
		||||
  if (!parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z", "I", "J", "K")))
 | 
			
		||||
    M206_report();
 | 
			
		||||
  else
 | 
			
		||||
    report_current_position();
 | 
			
		||||
 
 | 
			
		||||
@@ -125,6 +125,15 @@
 | 
			
		||||
      #if AXIS_IS_L64XX(Z4)
 | 
			
		||||
        REPORT_ABSOLUTE_POS(Z4);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_L64XX(I)
 | 
			
		||||
        REPORT_ABSOLUTE_POS(I);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_L64XX(J)
 | 
			
		||||
        REPORT_ABSOLUTE_POS(J);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_L64XX(K)
 | 
			
		||||
        REPORT_ABSOLUTE_POS(K);
 | 
			
		||||
      #endif
 | 
			
		||||
      #if AXIS_IS_L64XX(E0)
 | 
			
		||||
        REPORT_ABSOLUTE_POS(E0);
 | 
			
		||||
      #endif
 | 
			
		||||
@@ -170,7 +179,13 @@
 | 
			
		||||
 | 
			
		||||
    SERIAL_ECHOPGM("FromStp:");
 | 
			
		||||
    get_cartesian_from_steppers();  // writes 'cartes' (with forward kinematics)
 | 
			
		||||
    xyze_pos_t from_steppers = LOGICAL_AXIS_ARRAY(planner.get_axis_position_mm(E_AXIS), cartes.x, cartes.y, cartes.z);
 | 
			
		||||
    xyze_pos_t from_steppers = LOGICAL_AXIS_ARRAY(
 | 
			
		||||
      planner.get_axis_position_mm(E_AXIS),
 | 
			
		||||
      cartes.x, cartes.y, cartes.z,
 | 
			
		||||
      planner.get_axis_position_mm(I_AXIS),
 | 
			
		||||
      planner.get_axis_position_mm(J_AXIS),
 | 
			
		||||
      planner.get_axis_position_mm(K_AXIS)
 | 
			
		||||
    );
 | 
			
		||||
    report_all_axis_pos(from_steppers);
 | 
			
		||||
 | 
			
		||||
    const xyze_float_t diff = from_steppers - leveled;
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,10 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) {
 | 
			
		||||
        LINEAR_AXIS_GANG(
 | 
			
		||||
            (parser.seen_test('X') ? _BV(X_AXIS) : 0),
 | 
			
		||||
          | (parser.seen_test('Y') ? _BV(Y_AXIS) : 0),
 | 
			
		||||
          | (parser.seen_test('Z') ? _BV(Z_AXIS) : 0))
 | 
			
		||||
          | (parser.seen_test('Z') ? _BV(Z_AXIS) : 0),
 | 
			
		||||
          | (parser.seen_test(AXIS4_NAME) ? _BV(I_AXIS) : 0),
 | 
			
		||||
          | (parser.seen_test(AXIS5_NAME) ? _BV(J_AXIS) : 0),
 | 
			
		||||
          | (parser.seen_test(AXIS6_NAME) ? _BV(K_AXIS) : 0))
 | 
			
		||||
      )
 | 
			
		||||
    #endif
 | 
			
		||||
  ) {
 | 
			
		||||
@@ -85,7 +88,9 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) {
 | 
			
		||||
 | 
			
		||||
      if (MIN_AUTORETRACT <= MAX_AUTORETRACT) {
 | 
			
		||||
        // When M209 Autoretract is enabled, convert E-only moves to firmware retract/recover moves
 | 
			
		||||
        if (fwretract.autoretract_enabled && parser.seen_test('E') && !parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z"))) {
 | 
			
		||||
        if (fwretract.autoretract_enabled && parser.seen_test('E')
 | 
			
		||||
          && !parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR))
 | 
			
		||||
        ) {
 | 
			
		||||
          const float echange = destination.e - current_position.e;
 | 
			
		||||
          // Is this a retract or recover move?
 | 
			
		||||
          if (WITHIN(ABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ void plan_arc(
 | 
			
		||||
      case GcodeSuite::PLANE_ZX: p_axis = Z_AXIS; q_axis = X_AXIS; l_axis = Y_AXIS; break;
 | 
			
		||||
    }
 | 
			
		||||
  #else
 | 
			
		||||
    constexpr AxisEnum p_axis = X_AXIS, q_axis = Y_AXIS, l_axis = Z_AXIS;
 | 
			
		||||
    constexpr AxisEnum p_axis = X_AXIS, q_axis = Y_AXIS OPTARG(HAS_Z_AXIS, l_axis = Z_AXIS);
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  // Radius vector from center to current location
 | 
			
		||||
@@ -73,8 +73,8 @@ void plan_arc(
 | 
			
		||||
              center_P = current_position[p_axis] - rvec.a,
 | 
			
		||||
              center_Q = current_position[q_axis] - rvec.b,
 | 
			
		||||
              rt_X = cart[p_axis] - center_P,
 | 
			
		||||
              rt_Y = cart[q_axis] - center_Q,
 | 
			
		||||
              start_L = current_position[l_axis];
 | 
			
		||||
              rt_Y = cart[q_axis] - center_Q
 | 
			
		||||
              OPTARG(HAS_Z_AXIS, start_L = current_position[l_axis]);
 | 
			
		||||
 | 
			
		||||
  #ifdef MIN_ARC_SEGMENTS
 | 
			
		||||
    uint16_t min_segments = MIN_ARC_SEGMENTS;
 | 
			
		||||
@@ -109,8 +109,9 @@ void plan_arc(
 | 
			
		||||
    #endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float linear_travel = cart[l_axis] - start_L;
 | 
			
		||||
 | 
			
		||||
  #if HAS_Z_AXIS
 | 
			
		||||
    float linear_travel = cart[l_axis] - start_L;
 | 
			
		||||
  #endif
 | 
			
		||||
  #if HAS_EXTRUDERS
 | 
			
		||||
    float extruder_travel = cart.e - current_position.e;
 | 
			
		||||
  #endif
 | 
			
		||||
@@ -118,9 +119,11 @@ void plan_arc(
 | 
			
		||||
  // If circling around...
 | 
			
		||||
  if (ENABLED(ARC_P_CIRCLES) && circles) {
 | 
			
		||||
    const float total_angular = angular_travel + circles * RADIANS(360),  // Total rotation with all circles and remainder
 | 
			
		||||
              part_per_circle = RADIANS(360) / total_angular,             // Each circle's part of the total
 | 
			
		||||
                 l_per_circle = linear_travel * part_per_circle;          // L movement per circle
 | 
			
		||||
              part_per_circle = RADIANS(360) / total_angular;             // Each circle's part of the total
 | 
			
		||||
 | 
			
		||||
    #if HAS_Z_AXIS
 | 
			
		||||
      const float l_per_circle = linear_travel * part_per_circle;         // L movement per circle
 | 
			
		||||
    #endif
 | 
			
		||||
    #if HAS_EXTRUDERS
 | 
			
		||||
      const float e_per_circle = extruder_travel * part_per_circle;       // E movement per circle
 | 
			
		||||
    #endif
 | 
			
		||||
@@ -128,17 +131,15 @@ void plan_arc(
 | 
			
		||||
    xyze_pos_t temp_position = current_position;                          // for plan_arc to compare to current_position
 | 
			
		||||
    for (uint16_t n = circles; n--;) {
 | 
			
		||||
      TERN_(HAS_EXTRUDERS, temp_position.e += e_per_circle);              // Destination E axis
 | 
			
		||||
      temp_position[l_axis] += l_per_circle;                              // Destination L axis
 | 
			
		||||
      TERN_(HAS_Z_AXIS, temp_position[l_axis] += l_per_circle);           // Destination L axis
 | 
			
		||||
      plan_arc(temp_position, offset, clockwise, 0);                      // Plan a single whole circle
 | 
			
		||||
    }
 | 
			
		||||
    linear_travel = cart[l_axis] - current_position[l_axis];
 | 
			
		||||
    #if HAS_EXTRUDERS
 | 
			
		||||
      extruder_travel = cart.e - current_position.e;
 | 
			
		||||
    #endif
 | 
			
		||||
    TERN_(HAS_Z_AXIS, linear_travel = cart[l_axis] - current_position[l_axis]);
 | 
			
		||||
    TERN_(HAS_EXTRUDERS, extruder_travel = cart.e - current_position.e);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const float flat_mm = radius * angular_travel,
 | 
			
		||||
              mm_of_travel = linear_travel ? HYPOT(flat_mm, linear_travel) : ABS(flat_mm);
 | 
			
		||||
              mm_of_travel = TERN_(HAS_Z_AXIS, linear_travel ? HYPOT(flat_mm, linear_travel) :) ABS(flat_mm);
 | 
			
		||||
  if (mm_of_travel < 0.001f) return;
 | 
			
		||||
 | 
			
		||||
  const feedRate_t scaled_fr_mm_s = MMS_SCALED(feedrate_mm_s);
 | 
			
		||||
@@ -187,17 +188,19 @@ void plan_arc(
 | 
			
		||||
  // Vector rotation matrix values
 | 
			
		||||
  xyze_pos_t raw;
 | 
			
		||||
  const float theta_per_segment = angular_travel / segments,
 | 
			
		||||
              linear_per_segment = linear_travel / segments,
 | 
			
		||||
              sq_theta_per_segment = sq(theta_per_segment),
 | 
			
		||||
              sin_T = theta_per_segment - sq_theta_per_segment * theta_per_segment / 6,
 | 
			
		||||
              cos_T = 1 - 0.5f * sq_theta_per_segment; // Small angle approximation
 | 
			
		||||
 | 
			
		||||
  #if HAS_Z_AXIS && DISABLED(AUTO_BED_LEVELING_UBL)
 | 
			
		||||
    const float linear_per_segment = linear_travel / segments;
 | 
			
		||||
  #endif
 | 
			
		||||
  #if HAS_EXTRUDERS
 | 
			
		||||
    const float extruder_per_segment = extruder_travel / segments;
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  // Initialize the linear axis
 | 
			
		||||
  raw[l_axis] = current_position[l_axis];
 | 
			
		||||
  TERN_(HAS_Z_AXIS, raw[l_axis] = current_position[l_axis]);
 | 
			
		||||
 | 
			
		||||
  // Initialize the extruder axis
 | 
			
		||||
  TERN_(HAS_EXTRUDERS, raw.e = current_position.e);
 | 
			
		||||
@@ -246,11 +249,8 @@ void plan_arc(
 | 
			
		||||
    // Update raw location
 | 
			
		||||
    raw[p_axis] = center_P + rvec.a;
 | 
			
		||||
    raw[q_axis] = center_Q + rvec.b;
 | 
			
		||||
    #if ENABLED(AUTO_BED_LEVELING_UBL)
 | 
			
		||||
      raw[l_axis] = start_L;
 | 
			
		||||
      UNUSED(linear_per_segment);
 | 
			
		||||
    #else
 | 
			
		||||
      raw[l_axis] += linear_per_segment;
 | 
			
		||||
    #if HAS_Z_AXIS
 | 
			
		||||
      raw[l_axis] = TERN(AUTO_BED_LEVELING_UBL, start_L, raw[l_axis] + linear_per_segment);
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    TERN_(HAS_EXTRUDERS, raw.e += extruder_per_segment);
 | 
			
		||||
@@ -268,7 +268,7 @@ void plan_arc(
 | 
			
		||||
 | 
			
		||||
  // Ensure last segment arrives at target location.
 | 
			
		||||
  raw = cart;
 | 
			
		||||
  TERN_(AUTO_BED_LEVELING_UBL, raw[l_axis] = start_L);
 | 
			
		||||
  TERN_(AUTO_BED_LEVELING_UBL, TERN_(HAS_Z_AXIS, raw[l_axis] = start_L));
 | 
			
		||||
 | 
			
		||||
  apply_motion_limits(raw);
 | 
			
		||||
 | 
			
		||||
@@ -280,7 +280,7 @@ void plan_arc(
 | 
			
		||||
    OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  TERN_(AUTO_BED_LEVELING_UBL, raw[l_axis] = start_L);
 | 
			
		||||
  TERN_(AUTO_BED_LEVELING_UBL, TERN_(HAS_Z_AXIS, raw[l_axis] = start_L));
 | 
			
		||||
  current_position = raw;
 | 
			
		||||
 | 
			
		||||
} // plan_arc
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,7 @@ void GcodeSuite::M290() {
 | 
			
		||||
    }
 | 
			
		||||
  #endif
 | 
			
		||||
 | 
			
		||||
  if (!parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z")) || parser.seen('R')) {
 | 
			
		||||
  if (!parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR)) || parser.seen('R')) {
 | 
			
		||||
    SERIAL_ECHO_START();
 | 
			
		||||
 | 
			
		||||
    #if ENABLED(BABYSTEP_ZPROBE_OFFSET)
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,7 @@ void GCodeParser::parse(char *p) {
 | 
			
		||||
        case 'R': if (!WITHIN(motion_mode_codenum, 2, 3)) return;
 | 
			
		||||
      #endif
 | 
			
		||||
 | 
			
		||||
      LOGICAL_AXIS_GANG(case 'E':, case 'X':, case 'Y':, case 'Z':)
 | 
			
		||||
      LOGICAL_AXIS_GANG(case 'E':, case 'X':, case 'Y':, case 'Z':, case AXIS4_NAME:, case AXIS5_NAME:, case AXIS6_NAME:)
 | 
			
		||||
      case 'F':
 | 
			
		||||
        if (motion_mode_codenum < 0) return;
 | 
			
		||||
        command_letter = 'G';
 | 
			
		||||
 
 | 
			
		||||
@@ -226,7 +226,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  // Seen any axis parameter
 | 
			
		||||
  static inline bool seen_axis() {
 | 
			
		||||
    return seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z"));
 | 
			
		||||
    return seen(LOGICAL_AXIS_GANG("E", "X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #if ENABLED(GCODE_QUOTED_STRINGS)
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,8 @@ void GcodeSuite::M106() {
 | 
			
		||||
    if (!got_preset && parser.seenval('S'))
 | 
			
		||||
      speed = parser.value_ushort();
 | 
			
		||||
 | 
			
		||||
    TERN_(FOAMCUTTER_XYUV, speed *= 2.55); // Get command in % of max heat
 | 
			
		||||
 | 
			
		||||
    // Set speed, with constraint
 | 
			
		||||
    thermalManager.set_fan_speed(pfan, speed);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user