@ -353,10 +353,10 @@ static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0;
* the main loop . The process_next_command function parses the next
* command and hands off execution to individual handler functions .
*/
static char command_queue [ BUFSIZE ] [ MAX_CMD_SIZE ] ;
uint8_t commands_in_queue = 0 ; // Count of commands in the queue
static uint8_t cmd_queue_index_r = 0 , // Ring buffer read position
cmd_queue_index_w = 0 , // Ring buffer write position
commands_in_queue = 0 ; // Count of commands in the queue
cmd_queue_index_w = 0 ; // Ring buffer write position
static char command_queue [ BUFSIZE ] [ MAX_CMD_SIZE ] ;
/**
* Current GCode Command
@ -3502,6 +3502,12 @@ inline void gcode_G4() {
# endif // Z_SAFE_HOMING
# if ENABLED(PROBE_MANUALLY)
bool g29_in_progress = false ;
# else
constexpr bool g29_in_progress = false ;
# endif
/**
* G28 : Home all axes according to settings
*
@ -3529,6 +3535,11 @@ inline void gcode_G28() {
// Wait for planner moves to finish!
stepper . synchronize ( ) ;
// Cancel the active G29 session
# if ENABLED(PROBE_MANUALLY)
g29_in_progress = false ;
# endif
// Disable the leveling matrix before homing
# if PLANNER_LEVELING
# if ENABLED(AUTO_BED_LEVELING_UBL)
@ -3719,9 +3730,9 @@ inline void gcode_G28() {
# endif
# if ENABLED(MESH_BED_LEVELING)
# if ENABLED(MESH_BED_LEVELING) || ENABLED(PROBE_MANUALLY)
inline void _m b l_goto_xy( const float & x , const float & y ) {
inline void _m anua l_goto_xy( const float & x , const float & y ) {
const float old_feedrate_mm_s = feedrate_mm_s ;
# if MANUAL_PROBE_HEIGHT > 0
@ -3745,6 +3756,10 @@ inline void gcode_G28() {
stepper . synchronize ( ) ;
}
# endif
# if ENABLED(MESH_BED_LEVELING)
// Save 130 bytes with non-duplication of PSTR
void say_not_entered ( ) { SERIAL_PROTOCOLLNPGM ( " not entered. " ) ; }
@ -3835,7 +3850,7 @@ inline void gcode_G28() {
// If there's another point to sample, move there with optional lift.
if ( mbl_probe_index < ( MESH_NUM_X_POINTS ) * ( MESH_NUM_Y_POINTS ) ) {
mbl . zigzag ( mbl_probe_index , px , py ) ;
_m b l_goto_xy( mbl . index_to_xpos [ px ] , mbl . index_to_ypos [ py ] ) ;
_m anua l_goto_xy( mbl . index_to_xpos [ px ] , mbl . index_to_ypos [ py ] ) ;
# if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
@ -3917,50 +3932,86 @@ inline void gcode_G28() {
# elif HAS_ABL && DISABLED(AUTO_BED_LEVELING_UBL)
# if ABL_GRID
# if ENABLED(PROBE_Y_FIRST)
# define PR_OUTER_VAR xCount
# define PR_OUTER_END abl_grid_points_x
# define PR_INNER_VAR yCount
# define PR_INNER_END abl_grid_points_y
# else
# define PR_OUTER_VAR yCount
# define PR_OUTER_END abl_grid_points_y
# define PR_INNER_VAR xCount
# define PR_INNER_END abl_grid_points_x
# endif
# endif
/**
* G29 : Detailed Z probe , probes the bed at 3 or more points .
* Will fail if the printer has not been homed with G28 .
*
* Enhanced G29 Auto Bed Leveling Probe Routine
*
* Parameters With LINEAR and BILINEAR :
* D Dry - Run mode . Just evaluate the bed Topology - Don ' t apply
* or alter the bed level data . Useful to check the topology
* after a first run of G29 .
*
* J Jettison current bed leveling data
*
* V Set the verbose level ( 0 - 4 ) . Example : " G29 V3 "
*
* Parameters With LINEAR leveling only :
*
* P Set the size of the grid that will be probed ( P x P points ) .
* Not supported by non - linear delta printer bed leveling .
* Example : " G29 P4 "
*
* S Set the XY travel speed between probe points ( in units / min )
* X Set the X size of the grid that will be probed ( X x Y points ) .
* Example : " G29 X7 Y5 "
*
* D Dry - Run mode . Just evaluate the bed Topology - Don ' t apply
* or clean the rotation Matrix . Useful to check the topology
* after a first run of G29 .
*
* V Set the verbose level ( 0 - 4 ) . Example : " G29 V3 "
* Y Set the Y size of the grid that will be probed ( X x Y points ) .
*
* T Generate a Bed Topology Report . Example : " G29 P5 T " for a detailed report .
* This is useful for manual bed leveling and finding flaws in the bed ( to
* assist with part placement ) .
* Not supported by non - linear delta printer bed leveling .
*
* Parameters With LINEAR and BILINEAR leveling only :
*
* S Set the XY travel speed between probe points ( in units / min )
*
* F Set the Front limit of the probing grid
* B Set the Back limit of the probing grid
* L Set the Left limit of the probing grid
* R Set the Right limit of the probing grid
*
* Parameters with BILINEAR only :
* Parameters with BILINEAR leveling only :
*
* Z Supply an additional Z probe offset
*
* Global Parameters :
* Extra parameters with PROBE_MANUALLY :
*
* To do manual probing simply repeat G29 until the procedure is complete .
* The first G29 accepts parameters . ' G29 Q ' for status , ' G29 A ' to abort .
*
* Q Query leveling and G29 state
*
* A Abort current leveling procedure
*
* W Write a mesh point . ( Ignored during leveling . )
* X Required X for mesh point
* Y Required Y for mesh point
* Z Required Z for mesh point
*
* E / e By default G29 will engage the Z probe , test the bed , then disengage .
* Without PROBE_MANUALLY :
*
* E By default G29 will engage the Z probe , test the bed , then disengage .
* Include " E " to engage / disengage the Z probe for each sample .
* There ' s no extra effect if you have a fixed Z probe .
* Usage : " G29 E " or " G29 e "
*
*/
inline void gcode_G29 ( ) {
// G29 Q is also available if debugging
# if ENABLED(DEBUG_LEVELING_FEATURE)
const bool query = code_seen ( ' Q ' ) ;
const uint8_t old_debug_flags = marlin_debug_flags ;
@ -3970,37 +4021,148 @@ inline void gcode_G28() {
log_machine_info ( ) ;
}
marlin_debug_flags = old_debug_flags ;
if ( query ) return ;
# if DISABLED(PROBE_MANUALLY)
if ( query ) return ;
# endif
# endif
// Don't allow auto-leveling without homing first
if ( axis_unhomed_error ( true , true , true ) ) return ;
const int verbose_level = code_seen ( ' V ' ) ? code_value_int ( ) : 1 ;
if ( verbose_level < 0 | | verbose_level > 4 ) {
SERIAL_PROTOCOLLNPGM ( " ?(V)erbose Level is implausible (0-4). " ) ;
return ;
}
// Define local vars 'static' for manual probing, 'auto' otherwise
# if ENABLED(PROBE_MANUALLY)
# define ABL_VAR static
# else
# define ABL_VAR
# endif
bool dryrun = code_seen ( ' D ' ) ,
stow_probe_after_each = code_seen ( ' E ' ) ;
ABL_VAR int verbose_level , abl_probe_index ;
ABL_VAR float xProbe , yProbe , measured_z ;
ABL_VAR bool dryrun , abl_should_enable ;
# if HAS_SOFTWARE_ENDSTOPS
ABL_VAR bool enable_soft_endstops = true ;
# endif
# if ABL_GRID
ABL_VAR uint8_t PR_OUTER_VAR ;
ABL_VAR int8_t PR_INNER_VAR ;
ABL_VAR int left_probe_bed_position , right_probe_bed_position , front_probe_bed_position , back_probe_bed_position ;
ABL_VAR float xGridSpacing , yGridSpacing ;
if ( verbose_level > 0 ) {
SERIAL_PROTOCOLLNPGM ( " G29 Auto Bed Leveling " ) ;
if ( dryrun ) SERIAL_PROTOCOLLNPGM ( " Running in DRY-RUN mode " ) ;
}
# define ABL_GRID_MAX (ABL_GRID_MAX_POINTS_X) * (ABL_GRID_MAX_POINTS_Y)
# if ABL_PLANAR
ABL_VAR uint8_t abl_grid_points_x = ABL_GRID_MAX_POINTS_X ,
abl_grid_points_y = ABL_GRID_MAX_POINTS_Y ;
ABL_VAR int abl2 ;
ABL_VAR bool do_topography_map ;
# else // 3-point
uint8_t constexpr abl_grid_points_x = ABL_GRID_MAX_POINTS_X ,
abl_grid_points_y = ABL_GRID_MAX_POINTS_Y ;
int constexpr abl2 = ABL_GRID_MAX ;
# endif
# if ENABLED(AUTO_BED_LEVELING_BILINEAR)
ABL_VAR float zoffset ;
# elif ENABLED(AUTO_BED_LEVELING_LINEAR)
ABL_VAR int indexIntoAB [ ABL_GRID_MAX_POINTS_X ] [ ABL_GRID_MAX_POINTS_Y ] ;
ABL_VAR float eqnAMatrix [ ABL_GRID_MAX * 3 ] , // "A" matrix of the linear system of equations
eqnBVector [ ABL_GRID_MAX ] , // "B" vector of Z points
mean ;
# endif
# elif ENABLED(AUTO_BED_LEVELING_3POINT)
// Probe at 3 arbitrary points
ABL_VAR vector_3 points [ 3 ] = {
vector_3 ( ABL_PROBE_PT_1_X , ABL_PROBE_PT_1_Y , 0 ) ,
vector_3 ( ABL_PROBE_PT_2_X , ABL_PROBE_PT_2_Y , 0 ) ,
vector_3 ( ABL_PROBE_PT_3_X , ABL_PROBE_PT_3_Y , 0 )
} ;
# endif // AUTO_BED_LEVELING_3POINT
bool do_topography_map = verbose_level > 2 | | code_seen ( ' T ' ) ;
/**
* On the initial G29 fetch command parameters .
*/
if ( ! g29_in_progress ) {
abl_probe_index = 0 ;
abl_should_enable = planner . abl_enabled ;
# if ENABLED(AUTO_BED_LEVELING_BILINEAR)
if ( code_seen ( ' W ' ) ) {
if ( ! bilinear_grid_spacing [ X_AXIS ] ) {
SERIAL_ERROR_START ;
SERIAL_ERRORLNPGM ( " No bilinear grid " ) ;
return ;
}
const float z = code_seen ( ' Z ' ) & & code_has_value ( ) ? code_value_float ( ) : 99999 ;
if ( ! WITHIN ( z , - 10 , 10 ) ) {
SERIAL_ERROR_START ;
SERIAL_ERRORLNPGM ( " Bad Z value " ) ;
return ;
}
const float x = code_seen ( ' X ' ) & & code_has_value ( ) ? code_value_float ( ) : 99999 ,
y = code_seen ( ' Y ' ) & & code_has_value ( ) ? code_value_float ( ) : 99999 ;
int8_t i = code_seen ( ' I ' ) & & code_has_value ( ) ? code_value_byte ( ) : - 1 ,
j = code_seen ( ' J ' ) & & code_has_value ( ) ? code_value_byte ( ) : - 1 ;
if ( x < 99998 & & y < 99998 ) {
// Get nearest i / j from x / y
i = ( x - LOGICAL_X_POSITION ( bilinear_start [ X_AXIS ] ) + 0.5 * xGridSpacing ) / xGridSpacing ;
j = ( y - LOGICAL_Y_POSITION ( bilinear_start [ Y_AXIS ] ) + 0.5 * yGridSpacing ) / yGridSpacing ;
i = constrain ( i , 0 , ABL_GRID_MAX_POINTS_X - 1 ) ;
j = constrain ( j , 0 , ABL_GRID_MAX_POINTS_Y - 1 ) ;
}
if ( WITHIN ( i , 0 , ABL_GRID_MAX_POINTS_X - 1 ) & & WITHIN ( j , 0 , ABL_GRID_MAX_POINTS_Y ) ) {
set_bed_leveling_enabled ( false ) ;
bed_level_grid [ i ] [ j ] = z ;
# if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate ( ) ;
# endif
set_bed_leveling_enabled ( abl_should_enable ) ;
}
return ;
} // code_seen('W')
# endif
# if PLANNER_LEVELING
// Jettison bed leveling data
if ( code_seen ( ' J ' ) ) {
reset_bed_level ( ) ;
return ;
}
# endif
verbose_level = code_seen ( ' V ' ) & & code_has_value ( ) ? code_value_int ( ) : 0 ;
if ( ! WITHIN ( verbose_level , 0 , 4 ) ) {
SERIAL_PROTOCOLLNPGM ( " ?(V)erbose Level is implausible (0-4). " ) ;
return ;
}
dryrun = code_seen ( ' D ' ) ? code_value_bool ( ) : false ;
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
do_topography_map = verbose_level > 2 | | code_seen ( ' T ' ) ;
// X and Y specify points in each direction, overriding the default
// These values may be saved with the completed mesh
int abl_grid_points_x = code_seen ( ' X ' ) ? code_value_int ( ) : ABL_GRID_MAX_POINTS_X ,
abl_grid_points_y = code_seen ( ' Y ' ) ? code_value_int ( ) : ABL_GRID_MAX_POINTS_Y ;
abl_grid_points_x = code_seen ( ' X ' ) ? code_value_int ( ) : ABL_GRID_MAX_POINTS_X ;
abl_grid_points_y = code_seen ( ' Y ' ) ? code_value_int ( ) : ABL_GRID_MAX_POINTS_Y ;
if ( code_seen ( ' P ' ) ) abl_grid_points_x = abl_grid_points_y = code_value_int ( ) ;
if ( abl_grid_points_x < 2 | | abl_grid_points_y < 2 ) {
@ -4008,91 +4170,98 @@ inline void gcode_G28() {
return ;
}
# else
abl2 = abl_grid_points_x * abl_grid_points_y ;
const uint8_t abl_grid_points_x = ABL_GRID_MAX_POINTS_X , abl_grid_points_y = ABL_GRID_MAX_POINTS_Y ;
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
zoffset = code_seen ( ' Z ' ) ? code_value_axis_units ( Z_AXIS ) : 0 ;
# if HAS_BED_PROBE
zoffset + = zprobe_zoffset ;
# endif
# endif
xy_probe_feedrate_mm_s = MMM_TO_MMS ( code_seen ( ' S ' ) ? code_value_linear_units ( ) : XY_PROBE_SPEED ) ;
# if ABL_GRID
int left_probe_bed_position = code_seen ( ' L ' ) ? ( int ) code_value_axis_units ( X_AXIS ) : LOGICAL_X_POSITION ( LEFT_PROBE_BED_POSITION ) ,
right_probe_bed_position = code_seen ( ' R ' ) ? ( int ) code_value_axis_units ( X_AXIS ) : LOGICAL_X_POSITION ( RIGHT_PROBE_BED_POSITION ) ,
front_probe_bed_position = code_seen ( ' F ' ) ? ( int ) code_value_axis_units ( Y_AXIS ) : LOGICAL_Y_POSITION ( FRONT_PROBE_BED_POSITION ) ,
back_probe_bed_position = code_seen ( ' B ' ) ? ( int ) code_value_axis_units ( Y_AXIS ) : LOGICAL_Y_POSITION ( BACK_PROBE_BED_POSITION ) ;
xy_probe_feedrate_mm_s = MMM_TO_MMS ( code_seen ( ' S ' ) ? code_value_linear_units ( ) : XY_PROBE_SPEED ) ;
const bool left_out_l = left_probe_bed_position < LOGICAL_X_POSITION ( MIN_PROBE_X ) ,
left_out = left_out_l | | left_probe_bed_position > right_probe_bed_position - ( MIN_PROBE_EDGE ) ,
right_out_r = right_probe_bed_position > LOGICAL_X_POSITION ( MAX_PROBE_X ) ,
right_out = right_out_r | | right_probe_bed_position < left_probe_bed_position + MIN_PROBE_EDGE ,
front_out_f = front_probe_bed_position < LOGICAL_Y_POSITION ( MIN_PROBE_Y ) ,
front_out = front_out_f | | front_probe_bed_position > back_probe_bed_position - ( MIN_PROBE_EDGE ) ,
back_out_b = back_probe_bed_position > LOGICAL_Y_POSITION ( MAX_PROBE_Y ) ,
back_out = back_out_b | | back_probe_bed_position < front_probe_bed_position + MIN_PROBE_EDGE ;
left_probe_bed_position = code_seen ( ' L ' ) ? ( int ) code_value_axis_units ( X_AXIS ) : LOGICAL_X_POSITION ( LEFT_PROBE_BED_POSITION ) ;
right_probe_bed_position = code_seen ( ' R ' ) ? ( int ) code_value_axis_units ( X_AXIS ) : LOGICAL_X_POSITION ( RIGHT_PROBE_BED_POSITION ) ;
front_probe_bed_position = code_seen ( ' F ' ) ? ( int ) code_value_axis_units ( Y_AXIS ) : LOGICAL_Y_POSITION ( FRONT_PROBE_BED_POSITION ) ;
back_probe_bed_position = code_seen ( ' B ' ) ? ( int ) code_value_axis_units ( Y_AXIS ) : LOGICAL_Y_POSITION ( BACK_PROBE_BED_POSITION ) ;
if ( left_out | | right_out | | front_out | | back_out ) {
if ( left_out ) {
out_of_range_error ( PSTR ( " (L)eft " ) ) ;
left_probe_bed_position = left_out_l ? LOGICAL_X_POSITION ( MIN_PROBE_X ) : right_probe_bed_position - ( MIN_PROBE_EDGE ) ;
}
if ( right_out ) {
out_of_range_error ( PSTR ( " (R)ight " ) ) ;
right_probe_bed_position = right_out_r ? LOGICAL_Y_POSITION ( MAX_PROBE_X ) : left_probe_bed_position + MIN_PROBE_EDGE ;
}
if ( front_out ) {
out_of_range_error ( PSTR ( " (F)ront " ) ) ;
front_probe_bed_position = front_out_f ? LOGICAL_Y_POSITION ( MIN_PROBE_Y ) : back_probe_bed_position - ( MIN_PROBE_EDGE ) ;
}
if ( back_out ) {
out_of_range_error ( PSTR ( " (B)ack " ) ) ;
back_probe_bed_position = back_out_b ? LOGICAL_Y_POSITION ( MAX_PROBE_Y ) : front_probe_bed_position + MIN_PROBE_EDGE ;
}
return ;
}
const bool left_out_l = left_probe_bed_position < LOGICAL_X_POSITION ( MIN_PROBE_X ) ,
left_out = left_out_l | | left_probe_bed_position > right_probe_bed_position - ( MIN_PROBE_EDGE ) ,
right_out_r = right_probe_bed_position > LOGICAL_X_POSITION ( MAX_PROBE_X ) ,
right_out = right_out_r | | right_probe_bed_position < left_probe_bed_position + MIN_PROBE_EDGE ,
front_out_f = front_probe_bed_position < LOGICAL_Y_POSITION ( MIN_PROBE_Y ) ,
front_out = front_out_f | | front_probe_bed_position > back_probe_bed_position - ( MIN_PROBE_EDGE ) ,
back_out_b = back_probe_bed_position > LOGICAL_Y_POSITION ( MAX_PROBE_Y ) ,
back_out = back_out_b | | back_probe_bed_position < front_probe_bed_position + MIN_PROBE_EDGE ;
# endif // ABL_GRID
if ( left_out | | right_out | | front_out | | back_out ) {
if ( left_out ) {
out_of_range_error ( PSTR ( " (L)eft " ) ) ;
left_probe_bed_position = left_out_l ? LOGICAL_X_POSITION ( MIN_PROBE_X ) : right_probe_bed_position - ( MIN_PROBE_EDGE ) ;
}
if ( right_out ) {
out_of_range_error ( PSTR ( " (R)ight " ) ) ;
right_probe_bed_position = right_out_r ? LOGICAL_Y_POSITION ( MAX_PROBE_X ) : left_probe_bed_position + MIN_PROBE_EDGE ;
}
if ( front_out ) {
out_of_range_error ( PSTR ( " (F)ront " ) ) ;
front_probe_bed_position = front_out_f ? LOGICAL_Y_POSITION ( MIN_PROBE_Y ) : back_probe_bed_position - ( MIN_PROBE_EDGE ) ;
}
if ( back_out ) {
out_of_range_error ( PSTR ( " (B)ack " ) ) ;
back_probe_bed_position = back_out_b ? LOGICAL_Y_POSITION ( MAX_PROBE_Y ) : front_probe_bed_position + MIN_PROBE_EDGE ;
}
return ;
}
stepper . synchronize ( ) ;
// probe at the points of a lattice grid
xGridSpacing = ( right_probe_bed_position - left_probe_bed_position ) / ( abl_grid_points_x - 1 ) ;
yGridSpacing = ( back_probe_bed_position - front_probe_bed_position ) / ( abl_grid_points_y - 1 ) ;
// Disable auto bed leveling during G29
bool abl_should_enable = planner . abl_enabled ;
# endif // ABL_GRID
planner . abl_enabled = false ;
if ( verbose_level > 0 ) {
SERIAL_PROTOCOLLNPGM ( " G29 Auto Bed Leveling " ) ;
if ( dryrun ) SERIAL_PROTOCOLLNPGM ( " Running in DRY-RUN mode " ) ;
}
if ( ! dryrun ) {
// Re-orient the current position without leveling
// based on where the steppers are positioned.
set_current_from_steppers_for_axis ( ALL_AXES ) ;
stepper . synchronize ( ) ;
// Sync the planner to where the steppers stopped
SYNC_PLAN_POSITION_KINEMATIC ( ) ;
}
// Disable auto bed leveling during G29
planner . abl_enabled = false ;
setup_for_endstop_or_probe_move ( ) ;
if ( ! dryrun ) {
// Re-orient the current position without leveling
// based on where the steppers are positioned.
set_current_from_steppers_for_axis ( ALL_AXES ) ;
// Deploy the probe. Probe will raise if needed.
if ( DEPLOY_PROBE ( ) ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
// Sync the planner to where the steppers stopped
SYNC_PLAN_POSITION_KINEMATIC ( ) ;
}
float xProbe = 0 , yProbe = 0 , measured_z = 0 ;
setup_for_endstop_or_probe_move ( ) ;
# if ABL_GRID
//xProbe = yProbe = measured_z = 0;
// probe at the points of a lattice grid
const float xGridSpacing = ( right_probe_bed_position - left_probe_bed_position ) / ( abl_grid_points_x - 1 ) ,
yGridSpacing = ( back_probe_bed_position - front_probe_bed_position ) / ( abl_grid_points_y - 1 ) ;
# if HAS_BED_PROBE
// Deploy the probe. Probe will raise if needed.
if ( DEPLOY_PROBE ( ) ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
# endif
# if ENABLED(AUTO_BED_LEVELING_BILINEAR)
float zoffset = zprobe_zoffset ;
if ( code_seen ( ' Z ' ) ) zoffset + = code_value_axis_units ( Z_AXIS ) ;
if ( xGridSpacing ! = bilinear_grid_spacing [ X_AXIS ]
| | yGridSpacing ! = bilinear_grid_spacing [ Y_AXIS ]
| | left_probe_bed_position ! = bilinear_start[ X_AXIS ]
| | front_probe_bed_position ! = bilinear_start[ Y_AXIS ]
| | left_probe_bed_position ! = LOGICAL_X_POSITION ( bilinear_start [ X_AXIS ] )
| | front_probe_bed_position ! = LOGICAL_Y_POSITION( bilinear_start[ Y_AXIS ] )
) {
if ( dryrun ) {
// Before reset bed level, re-enable to correct the position
@ -4101,164 +4270,311 @@ inline void gcode_G28() {
// Reset grid to 0.0 or "not probed". (Also disables ABL)
reset_bed_level ( ) ;
# if ENABLED(ABL_BILINEAR_SUBDIVISION)
bilinear_grid_spacing_virt [ X_AXIS ] = xGridSpacing / ( BILINEAR_SUBDIVISIONS ) ;
bilinear_grid_spacing_virt [ Y_AXIS ] = yGridSpacing / ( BILINEAR_SUBDIVISIONS ) ;
# endif
// Initialize a grid with the given dimensions
bilinear_grid_spacing [ X_AXIS ] = xGridSpacing ;
bilinear_grid_spacing [ Y_AXIS ] = yGridSpacing ;
bilinear_start [ X_AXIS ] = RAW_X_POSITION ( left_probe_bed_position ) ;
bilinear_start [ Y_AXIS ] = RAW_Y_POSITION ( front_probe_bed_position ) ;
# if ENABLED(ABL_BILINEAR_SUBDIVISION)
bilinear_grid_spacing_virt [ X_AXIS ] = xGridSpacing / ( BILINEAR_SUBDIVISIONS ) ;
bilinear_grid_spacing_virt [ Y_AXIS ] = yGridSpacing / ( BILINEAR_SUBDIVISIONS ) ;
# endif
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false ;
}
# elif ENABLED(AUTO_BED_LEVELING_LINEAR)
/**
* solve the plane equation ax + by + d = z
* A is the matrix with rows [ x y 1 ] for all the probed points
* B is the vector of the Z positions
* the normal vector to the plane is formed by the coefficients of the
* plane equation in the standard form , which is Vx * x + Vy * y + Vz * z + d = 0
* so Vx = - a Vy = - b Vz = 1 ( we want the vector facing towards positive Z
*/
mean = 0.0 ;
const int abl2 = abl_grid_points_x * abl_grid_points_y ;
# endif // AUTO_BED_LEVELING_LINEAR
int indexIntoAB [ abl_grid_points_x ] [ abl_grid_points_y ] ,
probe_index = - 1 ;
# if ENABLED(AUTO_BED_LEVELING_3POINT)
float eqnAMatrix [ abl2 * 3 ] , // "A" matrix of the linear system of equations
eqnBVector [ abl2 ] , // "B" vector of Z points
mean = 0.0 ;
# if ENABLED(DEBUG_LEVELING_FEATURE)
if ( DEBUGGING ( LEVELING ) ) SERIAL_ECHOLNPGM ( " > 3-point Leveling " ) ;
# endif
# endif // AUTO_BED_LEVELING_LINEAR
// Probe at 3 arbitrary points
points [ 0 ] . z = points [ 1 ] . z = points [ 2 ] . z = 0 ;
# if ENABLED(PROBE_Y_FIRST)
# define PR_OUTER_VAR xCount
# define PR_OUTER_NUM abl_grid_points_x
# define PR_INNER_VAR yCount
# define PR_INNER_NUM abl_grid_points_y
# else
# define PR_OUTER_VAR yCount
# define PR_OUTER_NUM abl_grid_points_y
# define PR_INNER_VAR xCount
# define PR_INNER_NUM abl_grid_points_x
# endif
# endif // AUTO_BED_LEVELING_3POINT
bool zig = PR_OUTER_NUM & 1 ; // Always end at RIGHT and BACK_PROBE_BED_POSITION
} // !g29_in_progress
// Outer loop is Y with PROBE_Y_FIRST disabled
for ( uint8_t PR_OUTER_VAR = 0 ; PR_OUTER_VAR < PR_OUTER_NUM ; PR_OUTER_VAR + + ) {
# if ENABLED(PROBE_MANUALLY)
int8_t inStart , inStop , inInc ;
// Abort current G29 procedure, go back to ABLStart
if ( code_seen ( ' A ' ) & & g29_in_progress ) {
SERIAL_PROTOCOLLNPGM ( " Manual G29 aborted " ) ;
# if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = enable_soft_endstops ;
# endif
planner . abl_enabled = abl_should_enable ;
g29_in_progress = false ;
}
if ( zig ) { // away from origin
inStart = 0 ;
inStop = PR_INNER_NUM ;
inInc = 1 ;
}
else { // towards origin
inStart = PR_INNER_NUM - 1 ;
inStop = - 1 ;
inInc = - 1 ;
// Query G29 status
if ( code_seen ( ' Q ' ) ) {
if ( ! g29_in_progress )
SERIAL_PROTOCOLLNPGM ( " Manual G29 idle " ) ;
else {
SERIAL_PROTOCOLPAIR ( " Manual G29 point " , abl_probe_index + 1 ) ;
SERIAL_PROTOCOLLNPAIR ( " of " , abl2 ) ;
}
}
if ( code_seen ( ' A ' ) | | code_seen ( ' Q ' ) ) return ;
zig = ! zig ; // zag
// Fall through to probe the first point
g29_in_progress = true ;
if ( abl_probe_index = = 0 ) {
// For the initial G29 S2 save software endstop state
# if HAS_SOFTWARE_ENDSTOPS
enable_soft_endstops = soft_endstops_enabled ;
# endif
}
else {
// For G29 after adjusting Z.
// Save the previous Z before going to the next point
measured_z = current_position [ Z_AXIS ] ;
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
mean + = measured_z ;
eqnBVector [ abl_probe_index ] = measured_z ;
eqnAMatrix [ abl_probe_index + 0 * abl2 ] = xProbe ;
eqnAMatrix [ abl_probe_index + 1 * abl2 ] = yProbe ;
eqnAMatrix [ abl_probe_index + 2 * abl2 ] = 1 ;
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
// Inner loop is Y with PROBE_Y_FIRST enabled
for ( int8_t PR_INNER_VAR = inStart ; PR_INNER_VAR ! = inStop ; PR_INNER_VAR + = inInc ) {
bed_level_grid [ xCount ] [ yCount ] = measured_z + zoffset ;
float xBase = left_probe_bed_position + xGridSpacing * xCount ,
yBase = front_probe_bed_position + yGridSpacing * yCount ;
# elif ENABLED(AUTO_BED_LEVELING_3POINT)
points [ i ] . z = measured_z ;
# endif
}
//
// If there's another point to sample, move there with optional lift.
//
# if ABL_GRID
// Find a next point to probe
// On the first G29 this will be the first probe point
while ( abl_probe_index < abl2 ) {
// Set xCount, yCount based on abl_probe_index, with zig-zag
PR_OUTER_VAR = abl_probe_index / PR_INNER_END ;
PR_INNER_VAR = abl_probe_index - ( PR_OUTER_VAR * PR_INNER_END ) ;
bool zig = ( PR_OUTER_VAR & 1 ) ! = ( ( PR_OUTER_END ) & 1 ) ;
if ( zig ) PR_INNER_VAR = ( PR_INNER_END - 1 ) - PR_INNER_VAR ;
const float xBase = left_probe_bed_position + xGridSpacing * xCount ,
yBase = front_probe_bed_position + yGridSpacing * yCount ;
xProbe = floor ( xBase + ( xBase < 0 ? 0 : 0.5 ) ) ;
yProbe = floor ( yBase + ( yBase < 0 ? 0 : 0.5 ) ) ;
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
indexIntoAB [ xCount ] [ yCount ] = + + probe_index ;
indexIntoAB [ xCount ] [ yCount ] = abl_probe_index ;
# endif
float pos [ XYZ ] = { xProbe , yProbe , 0 } ;
if ( position_is_reachable ( pos ) ) break ;
+ + abl_probe_index ;
}
// Is there a next point to move to?
if ( abl_probe_index < abl2 ) {
_manual_goto_xy ( xProbe , yProbe ) ; // Can be used here too!
+ + abl_probe_index ;
# if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false ;
# endif
return ;
}
else {
// Then leveling is done!
// G29 finishing code goes here
# if IS_KINEMATIC
// Avoid probing outside the round or hexagonal area
float pos [ XYZ ] = { xProbe , yProbe , 0 } ;
if ( ! position_is_reachable ( pos , true ) ) continue ;
// After recording the last point, activate abl
SERIAL_PROTOCOLLNPGM ( " Grid probing done. " ) ;
g29_in_progress = false ;
// Re-enable software endstops, if needed
# if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = enable_soft_endstops ;
# endif
}
measured_z = probe_pt ( xProbe , yProbe , stow_probe_after_each , verbose_level ) ;
# elif ENABLED(AUTO_BED_LEVELING_3POINT)
if ( measured_z = = NAN ) {
planner . abl_enabled = abl_should_enable ;
return ;
// Probe at 3 arbitrary points
if ( abl_probe_index < 3 ) {
xProbe = LOGICAL_X_POSITION ( points [ i ] . x ) ;
yProbe = LOGICAL_Y_POSITION ( points [ i ] . y ) ;
+ + abl_probe_index ;
# if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false ;
# endif
return ;
}
else {
SERIAL_PROTOCOLLNPGM ( " 3-point probing done. " ) ;
g29_in_progress = false ;
// Re-enable software endstops, if needed
# if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = enable_soft_endstops ;
# endif
if ( ! dryrun ) {
vector_3 planeNormal = vector_3 : : cross ( points [ 0 ] - points [ 1 ] , points [ 2 ] - points [ 1 ] ) . get_normal ( ) ;
if ( planeNormal . z < 0 ) {
planeNormal . x * = - 1 ;
planeNormal . y * = - 1 ;
planeNormal . z * = - 1 ;
}
planner . bed_level_matrix = matrix_3x3 : : create_look_at ( planeNormal ) ;
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false ;
}
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
}
mean + = measured_z ;
eqnBVector [ probe_index ] = measured_z ;
eqnAMatrix [ probe_index + 0 * abl2 ] = xProbe ;
eqnAMatrix [ probe_index + 1 * abl2 ] = yProbe ;
eqnAMatrix [ probe_index + 2 * abl2 ] = 1 ;
# endif // AUTO_BED_LEVELING_3POINT
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
bed_level_grid [ xCount ] [ yCount ] = measured_z + zoffset ;
# else // !PROBE_MANUALLY
# endif
idle ( ) ;
bool stow_probe_after_each = code_seen ( ' E ' ) ;
} // inner
} // outer
# if ABL_GRID
# elif ENABLED(AUTO_BED_LEVELING_3POINT)
bool zig = PR_OUTER_END & 1 ; // Always end at RIGHT and BACK_PROBE_BED_POSITION
# if ENABLED(DEBUG_LEVELING_FEATURE)
if ( DEBUGGING ( LEVELING ) ) SERIAL_ECHOLNPGM ( " > 3-point Leveling " ) ;
# endif
// Outer loop is Y with PROBE_Y_FIRST disabled
for ( uint8_t PR_OUTER_VAR = 0 ; PR_OUTER_VAR < PR_OUTER_END ; PR_OUTER_VAR + + ) {
// Probe at 3 arbitrary points
vector_3 points [ 3 ] = {
vector_3 ( ABL_PROBE_PT_1_X , ABL_PROBE_PT_1_Y , 0 ) ,
vector_3 ( ABL_PROBE_PT_2_X , ABL_PROBE_PT_2_Y , 0 ) ,
vector_3 ( ABL_PROBE_PT_3_X , ABL_PROBE_PT_3_Y , 0 )
} ;
int8_t inStart , inStop , inInc ;
for ( uint8_t i = 0 ; i < 3 ; + + i ) {
// Retain the last probe position
xProbe = LOGICAL_X_POSITION ( points [ i ] . x ) ;
yProbe = LOGICAL_Y_POSITION ( points [ i ] . y ) ;
measured_z = points [ i ] . z = probe_pt ( xProbe , yProbe , stow_probe_after_each , verbose_level ) ;
}
if ( zig ) { // away from origin
inStart = 0 ;
inStop = PR_INNER_END ;
inInc = 1 ;
}
else { // towards origin
inStart = PR_INNER_END - 1 ;
inStop = - 1 ;
inInc = - 1 ;
}
if ( measured_z = = NAN ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
zig = ! zig ; // zag
if ( ! dryrun ) {
vector_3 planeNormal = vector_3 : : cross ( points [ 0 ] - points [ 1 ] , points [ 2 ] - points [ 1 ] ) . get_normal ( ) ;
if ( planeNormal . z < 0 ) {
planeNormal . x * = - 1 ;
planeNormal . y * = - 1 ;
planeNormal . z * = - 1 ;
// Inner loop is Y with PROBE_Y_FIRST enabled
for ( int8_t PR_INNER_VAR = inStart ; PR_INNER_VAR ! = inStop ; PR_INNER_VAR + = inInc ) {
float xBase = left_probe_bed_position + xGridSpacing * xCount ,
yBase = front_probe_bed_position + yGridSpacing * yCount ;
xProbe = floor ( xBase + ( xBase < 0 ? 0 : 0.5 ) ) ;
yProbe = floor ( yBase + ( yBase < 0 ? 0 : 0.5 ) ) ;
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
indexIntoAB [ xCount ] [ yCount ] = + + abl_probe_index ;
# endif
# if IS_KINEMATIC
// Avoid probing outside the round or hexagonal area
float pos [ XYZ ] = { xProbe , yProbe , 0 } ;
if ( ! position_is_reachable ( pos , true ) ) continue ;
# endif
measured_z = probe_pt ( xProbe , yProbe , stow_probe_after_each , verbose_level ) ;
if ( measured_z = = NAN ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
# if ENABLED(AUTO_BED_LEVELING_LINEAR)
mean + = measured_z ;
eqnBVector [ abl_probe_index ] = measured_z ;
eqnAMatrix [ abl_probe_index + 0 * abl2 ] = xProbe ;
eqnAMatrix [ abl_probe_index + 1 * abl2 ] = yProbe ;
eqnAMatrix [ abl_probe_index + 2 * abl2 ] = 1 ;
# elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
bed_level_grid [ xCount ] [ yCount ] = measured_z + zoffset ;
# endif
abl_should_enable = false ;
idle ( ) ;
} // inner
} // outer
# elif ENABLED(AUTO_BED_LEVELING_3POINT)
// Probe at 3 arbitrary points
for ( uint8_t i = 0 ; i < 3 ; + + i ) {
// Retain the last probe position
xProbe = LOGICAL_X_POSITION ( points [ i ] . x ) ;
yProbe = LOGICAL_Y_POSITION ( points [ i ] . y ) ;
measured_z = points [ i ] . z = probe_pt ( xProbe , yProbe , stow_probe_after_each , verbose_level ) ;
}
planner . bed_level_matrix = matrix_3x3 : : create_look_at ( planeNormal ) ;
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false ;
}
if ( measured_z = = NAN ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
# endif // AUTO_BED_LEVELING_3POINT
if ( ! dryrun ) {
vector_3 planeNormal = vector_3 : : cross ( points [ 0 ] - points [ 1 ] , points [ 2 ] - points [ 1 ] ) . get_normal ( ) ;
if ( planeNormal . z < 0 ) {
planeNormal . x * = - 1 ;
planeNormal . y * = - 1 ;
planeNormal . z * = - 1 ;
}
planner . bed_level_matrix = matrix_3x3 : : create_look_at ( planeNormal ) ;
// Raise to _Z_CLEARANCE_DEPLOY_PROBE. Stow the probe.
if ( STOW_PROBE ( ) ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false ;
}
# endif // AUTO_BED_LEVELING_3POINT
// Raise to _Z_CLEARANCE_DEPLOY_PROBE. Stow the probe.
if ( STOW_PROBE ( ) ) {
planner . abl_enabled = abl_should_enable ;
return ;
}
# endif // !PROBE_MANUALLY
//
// G29 Finishing Code
//
// Unless this is a dry run, auto bed leveling will
// definitely be enabled after this point
@ -4286,7 +4602,14 @@ inline void gcode_G28() {
// For LINEAR leveling calculate matrix, print reports, correct the position
// solve lsq problem
/**
* solve the plane equation ax + by + d = z
* A is the matrix with rows [ x y 1 ] for all the probed points
* B is the vector of the Z positions
* the normal vector to the plane is formed by the coefficients of the
* plane equation in the standard form , which is Vx * x + Vy * y + Vz * z + d = 0
* so Vx = - a Vy = - b Vz = 1 ( we want the vector facing towards positive Z
*/
float plane_equation_coefficients [ 3 ] ;
qr_solve ( plane_equation_coefficients , abl2 , 3 , eqnAMatrix , eqnBVector ) ;