Initial split-up of G-code handlers by category
This commit is contained in:
27
Marlin/src/gcode/calibrate/G26.h
Normal file
27
Marlin/src/gcode/calibrate/G26.h
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
void gcode_G26() {
|
||||
|
||||
ubl.G26();
|
||||
|
||||
}
|
324
Marlin/src/gcode/calibrate/G28.h
Normal file
324
Marlin/src/gcode/calibrate/G28.h
Normal file
@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if HOTENDS > 1
|
||||
#include "../control/tool_change.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(QUICK_HOME)
|
||||
|
||||
static void quick_home_xy() {
|
||||
|
||||
// Pretend the current position is 0,0
|
||||
current_position[X_AXIS] = current_position[Y_AXIS] = 0.0;
|
||||
sync_plan_position();
|
||||
|
||||
const int x_axis_home_dir =
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
x_home_dir(active_extruder)
|
||||
#else
|
||||
home_dir(X_AXIS)
|
||||
#endif
|
||||
;
|
||||
|
||||
const float mlx = max_length(X_AXIS),
|
||||
mly = max_length(Y_AXIS),
|
||||
mlratio = mlx > mly ? mly / mlx : mlx / mly,
|
||||
fr_mm_s = min(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0);
|
||||
|
||||
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s);
|
||||
endstops.hit_on_purpose(); // clear endstop hit flags
|
||||
current_position[X_AXIS] = current_position[Y_AXIS] = 0.0;
|
||||
}
|
||||
|
||||
#endif // QUICK_HOME
|
||||
|
||||
#if ENABLED(Z_SAFE_HOMING)
|
||||
|
||||
inline void home_z_safely() {
|
||||
|
||||
// Disallow Z homing if X or Y are unknown
|
||||
if (!axis_known_position[X_AXIS] || !axis_known_position[Y_AXIS]) {
|
||||
LCD_MESSAGEPGM(MSG_ERR_Z_HOMING);
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPGM(MSG_ERR_Z_HOMING);
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Z_SAFE_HOMING >>>");
|
||||
#endif
|
||||
|
||||
SYNC_PLAN_POSITION_KINEMATIC();
|
||||
|
||||
/**
|
||||
* Move the Z probe (or just the nozzle) to the safe homing point
|
||||
*/
|
||||
destination[X_AXIS] = LOGICAL_X_POSITION(Z_SAFE_HOMING_X_POINT);
|
||||
destination[Y_AXIS] = LOGICAL_Y_POSITION(Z_SAFE_HOMING_Y_POINT);
|
||||
destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height
|
||||
|
||||
#if HOMING_Z_WITH_PROBE
|
||||
destination[X_AXIS] -= X_PROBE_OFFSET_FROM_EXTRUDER;
|
||||
destination[Y_AXIS] -= Y_PROBE_OFFSET_FROM_EXTRUDER;
|
||||
#endif
|
||||
|
||||
if (position_is_reachable_xy(destination[X_AXIS], destination[Y_AXIS])) {
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("Z_SAFE_HOMING", destination);
|
||||
#endif
|
||||
|
||||
// This causes the carriage on Dual X to unpark
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
active_extruder_parked = false;
|
||||
#endif
|
||||
|
||||
do_blocking_move_to_xy(destination[X_AXIS], destination[Y_AXIS]);
|
||||
HOMEAXIS(Z);
|
||||
}
|
||||
else {
|
||||
LCD_MESSAGEPGM(MSG_ZPROBE_OUT);
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPGM(MSG_ZPROBE_OUT);
|
||||
}
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< Z_SAFE_HOMING");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // Z_SAFE_HOMING
|
||||
|
||||
/**
|
||||
* G28: Home all axes according to settings
|
||||
*
|
||||
* Parameters
|
||||
*
|
||||
* None Home to all axes with no parameters.
|
||||
* With QUICK_HOME enabled XY will home together, then Z.
|
||||
*
|
||||
* Cartesian parameters
|
||||
*
|
||||
* X Home to the X endstop
|
||||
* Y Home to the Y endstop
|
||||
* Z Home to the Z endstop
|
||||
*
|
||||
*/
|
||||
void gcode_G28(const bool always_home_all) {
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_ECHOLNPGM(">>> gcode_G28");
|
||||
log_machine_info();
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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 HAS_LEVELING
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
const bool ubl_state_at_entry = leveling_is_active();
|
||||
#endif
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CNC_WORKSPACE_PLANES)
|
||||
workspace_plane = PLANE_XY;
|
||||
#endif
|
||||
|
||||
// Always home with tool 0 active
|
||||
#if HOTENDS > 1
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
tool_change(0, 0, true);
|
||||
#endif
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
|
||||
extruder_duplication_enabled = false;
|
||||
#endif
|
||||
|
||||
setup_for_endstop_or_probe_move();
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("> endstops.enable(true)");
|
||||
#endif
|
||||
endstops.enable(true); // Enable endstops for next homing move
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
home_delta();
|
||||
UNUSED(always_home_all);
|
||||
|
||||
#else // NOT DELTA
|
||||
|
||||
const bool homeX = always_home_all || parser.seen('X'),
|
||||
homeY = always_home_all || parser.seen('Y'),
|
||||
homeZ = always_home_all || parser.seen('Z'),
|
||||
home_all = (!homeX && !homeY && !homeZ) || (homeX && homeY && homeZ);
|
||||
|
||||
set_destination_to_current();
|
||||
|
||||
#if Z_HOME_DIR > 0 // If homing away from BED do Z first
|
||||
|
||||
if (home_all || homeZ) {
|
||||
HOMEAXIS(Z);
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> HOMEAXIS(Z)", current_position);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (home_all || homeX || homeY) {
|
||||
// Raise Z before homing any other axes and z is not already high enough (never lower z)
|
||||
destination[Z_AXIS] = LOGICAL_Z_POSITION(Z_HOMING_HEIGHT);
|
||||
if (destination[Z_AXIS] > current_position[Z_AXIS]) {
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING))
|
||||
SERIAL_ECHOLNPAIR("Raise Z (before homing) to ", destination[Z_AXIS]);
|
||||
#endif
|
||||
|
||||
do_blocking_move_to_z(destination[Z_AXIS]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(QUICK_HOME)
|
||||
|
||||
if (home_all || (homeX && homeY)) quick_home_xy();
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOME_Y_BEFORE_X)
|
||||
|
||||
// Home Y
|
||||
if (home_all || homeY) {
|
||||
HOMEAXIS(Y);
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> homeY", current_position);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Home X
|
||||
if (home_all || homeX) {
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
|
||||
// Always home the 2nd (right) extruder first
|
||||
active_extruder = 1;
|
||||
HOMEAXIS(X);
|
||||
|
||||
// Remember this extruder's position for later tool change
|
||||
inactive_extruder_x_pos = RAW_X_POSITION(current_position[X_AXIS]);
|
||||
|
||||
// Home the 1st (left) extruder
|
||||
active_extruder = 0;
|
||||
HOMEAXIS(X);
|
||||
|
||||
// Consider the active extruder to be parked
|
||||
COPY(raised_parked_position, current_position);
|
||||
delayed_move_time = 0;
|
||||
active_extruder_parked = true;
|
||||
|
||||
#else
|
||||
|
||||
HOMEAXIS(X);
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> homeX", current_position);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DISABLED(HOME_Y_BEFORE_X)
|
||||
// Home Y
|
||||
if (home_all || homeY) {
|
||||
HOMEAXIS(Y);
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> homeY", current_position);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Home Z last if homing towards the bed
|
||||
#if Z_HOME_DIR < 0
|
||||
if (home_all || homeZ) {
|
||||
#if ENABLED(Z_SAFE_HOMING)
|
||||
home_z_safely();
|
||||
#else
|
||||
HOMEAXIS(Z);
|
||||
#endif
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> (home_all || homeZ) > final", current_position);
|
||||
#endif
|
||||
} // home_all || homeZ
|
||||
#endif // Z_HOME_DIR < 0
|
||||
|
||||
SYNC_PLAN_POSITION_KINEMATIC();
|
||||
|
||||
#endif // !DELTA (gcode_G28)
|
||||
|
||||
endstops.not_homing();
|
||||
|
||||
#if ENABLED(DELTA) && ENABLED(DELTA_HOME_TO_SAFE_ZONE)
|
||||
// move to a height where we can use the full xy-area
|
||||
do_blocking_move_to_z(delta_clip_start_height);
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
set_bed_leveling_enabled(ubl_state_at_entry);
|
||||
#endif
|
||||
|
||||
clean_up_after_endstop_or_probe_move();
|
||||
|
||||
// Restore the active tool after homing
|
||||
#if HOTENDS > 1
|
||||
tool_change(old_tool_index, 0,
|
||||
#if ENABLED(PARKING_EXTRUDER)
|
||||
false // fetch the previous toolhead
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
|
||||
lcd_refresh();
|
||||
|
||||
report_current_position();
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< gcode_G28");
|
||||
#endif
|
||||
}
|
946
Marlin/src/gcode/calibrate/G29-abl.h
Normal file
946
Marlin/src/gcode/calibrate/G29-abl.h
Normal file
@ -0,0 +1,946 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
*
|
||||
* 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).
|
||||
* Example: "G29 P4"
|
||||
*
|
||||
* X Set the X size of the grid that will be probed (X x Y points).
|
||||
* Example: "G29 X7 Y5"
|
||||
*
|
||||
* 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 DEBUG_LEVELING_FEATURE only:
|
||||
*
|
||||
* C Make a totally fake grid with no actual probing.
|
||||
* For use in testing when no probing is possible.
|
||||
*
|
||||
* Parameters with BILINEAR leveling only:
|
||||
*
|
||||
* Z Supply an additional Z probe offset
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Extra parameters with BILINEAR only:
|
||||
*
|
||||
* W Write a mesh point. (If G29 is idle.)
|
||||
* I X index for mesh point
|
||||
* J Y index for mesh point
|
||||
* X X for mesh point, overrides I
|
||||
* Y Y for mesh point, overrides J
|
||||
* Z Z for mesh point. Otherwise, raw current Z.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
void gcode_G29() {
|
||||
|
||||
// G29 Q is also available if debugging
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
const bool query = parser.seen('Q');
|
||||
const uint8_t old_debug_flags = marlin_debug_flags;
|
||||
if (query) marlin_debug_flags |= DEBUG_LEVELING;
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_POS(">>> gcode_G29", current_position);
|
||||
log_machine_info();
|
||||
}
|
||||
marlin_debug_flags = old_debug_flags;
|
||||
#if DISABLED(PROBE_MANUALLY)
|
||||
if (query) return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
const bool seenA = parser.seen('A'), seenQ = parser.seen('Q'), no_action = seenA || seenQ;
|
||||
#endif
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE) && DISABLED(PROBE_MANUALLY)
|
||||
const bool faux = parser.boolval('C');
|
||||
#elif ENABLED(PROBE_MANUALLY)
|
||||
const bool faux = no_action;
|
||||
#else
|
||||
bool constexpr faux = false;
|
||||
#endif
|
||||
|
||||
// Don't allow auto-leveling without homing first
|
||||
if (axis_unhomed_error()) return;
|
||||
|
||||
// Define local vars 'static' for manual probing, 'auto' otherwise
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
#define ABL_VAR static
|
||||
#else
|
||||
#define ABL_VAR
|
||||
#endif
|
||||
|
||||
ABL_VAR int verbose_level;
|
||||
ABL_VAR float xProbe, yProbe, measured_z;
|
||||
ABL_VAR bool dryrun, abl_should_enable;
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY) || ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
ABL_VAR int abl_probe_index;
|
||||
#endif
|
||||
|
||||
#if HAS_SOFTWARE_ENDSTOPS && ENABLED(PROBE_MANUALLY)
|
||||
ABL_VAR bool enable_soft_endstops = true;
|
||||
#endif
|
||||
|
||||
#if ABL_GRID
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
ABL_VAR uint8_t PR_OUTER_VAR;
|
||||
ABL_VAR int8_t PR_INNER_VAR;
|
||||
#endif
|
||||
|
||||
ABL_VAR int left_probe_bed_position, right_probe_bed_position, front_probe_bed_position, back_probe_bed_position;
|
||||
ABL_VAR float xGridSpacing = 0, yGridSpacing = 0;
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
ABL_VAR uint8_t abl_grid_points_x = GRID_MAX_POINTS_X,
|
||||
abl_grid_points_y = GRID_MAX_POINTS_Y;
|
||||
ABL_VAR bool do_topography_map;
|
||||
#else // Bilinear
|
||||
uint8_t constexpr abl_grid_points_x = GRID_MAX_POINTS_X,
|
||||
abl_grid_points_y = GRID_MAX_POINTS_Y;
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(PROBE_MANUALLY)
|
||||
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
ABL_VAR int abl2;
|
||||
#else // Bilinear
|
||||
int constexpr abl2 = GRID_MAX_POINTS;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
ABL_VAR float zoffset;
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
|
||||
ABL_VAR int indexIntoAB[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
ABL_VAR float eqnAMatrix[GRID_MAX_POINTS * 3], // "A" matrix of the linear system of equations
|
||||
eqnBVector[GRID_MAX_POINTS], // "B" vector of Z points
|
||||
mean;
|
||||
#endif
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
|
||||
|
||||
int constexpr abl2 = 3;
|
||||
|
||||
// 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
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
struct linear_fit_data lsf_results;
|
||||
incremental_LSF_reset(&lsf_results);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* On the initial G29 fetch command parameters.
|
||||
*/
|
||||
if (!g29_in_progress) {
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY) || ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
abl_probe_index = -1;
|
||||
#endif
|
||||
|
||||
abl_should_enable = leveling_is_active();
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
if (parser.seen('W')) {
|
||||
if (!leveling_is_valid()) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM("No bilinear grid");
|
||||
return;
|
||||
}
|
||||
|
||||
const float z = parser.floatval('Z', RAW_CURRENT_POSITION(Z));
|
||||
if (!WITHIN(z, -10, 10)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM("Bad Z value");
|
||||
return;
|
||||
}
|
||||
|
||||
const float x = parser.floatval('X', NAN),
|
||||
y = parser.floatval('Y', NAN);
|
||||
int8_t i = parser.byteval('I', -1),
|
||||
j = parser.byteval('J', -1);
|
||||
|
||||
if (!isnan(x) && !isnan(y)) {
|
||||
// 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, GRID_MAX_POINTS_X - 1);
|
||||
j = constrain(j, 0, GRID_MAX_POINTS_Y - 1);
|
||||
}
|
||||
if (WITHIN(i, 0, GRID_MAX_POINTS_X - 1) && WITHIN(j, 0, GRID_MAX_POINTS_Y)) {
|
||||
set_bed_leveling_enabled(false);
|
||||
z_values[i][j] = z;
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
bed_level_virt_interpolate();
|
||||
#endif
|
||||
set_bed_leveling_enabled(abl_should_enable);
|
||||
}
|
||||
return;
|
||||
} // parser.seen('W')
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
|
||||
// Jettison bed leveling data
|
||||
if (parser.seen('J')) {
|
||||
reset_bed_level();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
verbose_level = parser.intval('V');
|
||||
if (!WITHIN(verbose_level, 0, 4)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-4).");
|
||||
return;
|
||||
}
|
||||
|
||||
dryrun = parser.boolval('D')
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
|| no_action
|
||||
#endif
|
||||
;
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
|
||||
do_topography_map = verbose_level > 2 || parser.boolval('T');
|
||||
|
||||
// X and Y specify points in each direction, overriding the default
|
||||
// These values may be saved with the completed mesh
|
||||
abl_grid_points_x = parser.intval('X', GRID_MAX_POINTS_X);
|
||||
abl_grid_points_y = parser.intval('Y', GRID_MAX_POINTS_Y);
|
||||
if (parser.seenval('P')) abl_grid_points_x = abl_grid_points_y = parser.value_int();
|
||||
|
||||
if (abl_grid_points_x < 2 || abl_grid_points_y < 2) {
|
||||
SERIAL_PROTOCOLLNPGM("?Number of probe points is implausible (2 minimum).");
|
||||
return;
|
||||
}
|
||||
|
||||
abl2 = abl_grid_points_x * abl_grid_points_y;
|
||||
mean = 0;
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
zoffset = parser.linearval('Z');
|
||||
|
||||
#endif
|
||||
|
||||
#if ABL_GRID
|
||||
|
||||
xy_probe_feedrate_mm_s = MMM_TO_MMS(parser.linearval('S', XY_PROBE_SPEED));
|
||||
|
||||
left_probe_bed_position = (int)parser.linearval('L', LOGICAL_X_POSITION(LEFT_PROBE_BED_POSITION));
|
||||
right_probe_bed_position = (int)parser.linearval('R', LOGICAL_X_POSITION(RIGHT_PROBE_BED_POSITION));
|
||||
front_probe_bed_position = (int)parser.linearval('F', LOGICAL_Y_POSITION(FRONT_PROBE_BED_POSITION));
|
||||
back_probe_bed_position = (int)parser.linearval('B', LOGICAL_Y_POSITION(BACK_PROBE_BED_POSITION));
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
#endif // ABL_GRID
|
||||
|
||||
if (verbose_level > 0) {
|
||||
SERIAL_PROTOCOLLNPGM("G29 Auto Bed Leveling");
|
||||
if (dryrun) SERIAL_PROTOCOLLNPGM("Running in DRY-RUN mode");
|
||||
}
|
||||
|
||||
stepper.synchronize();
|
||||
|
||||
// Disable auto bed leveling during G29
|
||||
planner.abl_enabled = false;
|
||||
|
||||
if (!dryrun) {
|
||||
// Re-orient the current position without leveling
|
||||
// based on where the steppers are positioned.
|
||||
set_current_from_steppers_for_axis(ALL_AXES);
|
||||
|
||||
// Sync the planner to where the steppers stopped
|
||||
SYNC_PLAN_POSITION_KINEMATIC();
|
||||
}
|
||||
|
||||
#if HAS_BED_PROBE
|
||||
// Deploy the probe. Probe will raise if needed.
|
||||
if (DEPLOY_PROBE()) {
|
||||
planner.abl_enabled = abl_should_enable;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!faux) setup_for_endstop_or_probe_move();
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
if (!no_action)
|
||||
#endif
|
||||
if ( xGridSpacing != bilinear_grid_spacing[X_AXIS]
|
||||
|| yGridSpacing != bilinear_grid_spacing[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
|
||||
planner.abl_enabled = abl_should_enable;
|
||||
}
|
||||
// Reset grid to 0.0 or "not probed". (Also disables ABL)
|
||||
reset_bed_level();
|
||||
|
||||
// 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);
|
||||
|
||||
// Can't re-enable (on error) until the new grid is written
|
||||
abl_should_enable = false;
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_BILINEAR
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_3POINT)
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("> 3-point Leveling");
|
||||
#endif
|
||||
|
||||
// Probe at 3 arbitrary points
|
||||
points[0].z = points[1].z = points[2].z = 0;
|
||||
|
||||
#endif // AUTO_BED_LEVELING_3POINT
|
||||
|
||||
} // !g29_in_progress
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
|
||||
// For manual probing, get the next index to probe now.
|
||||
// On the first probe this will be incremented to 0.
|
||||
if (!no_action) {
|
||||
++abl_probe_index;
|
||||
g29_in_progress = true;
|
||||
}
|
||||
|
||||
// Abort current G29 procedure, go back to idle state
|
||||
if (seenA && 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 ENABLED(LCD_BED_LEVELING)
|
||||
lcd_wait_for_move = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Query G29 status
|
||||
if (verbose_level || seenQ) {
|
||||
SERIAL_PROTOCOLPGM("Manual G29 ");
|
||||
if (g29_in_progress) {
|
||||
SERIAL_PROTOCOLPAIR("point ", min(abl_probe_index + 1, abl2));
|
||||
SERIAL_PROTOCOLLNPAIR(" of ", abl2);
|
||||
}
|
||||
else
|
||||
SERIAL_PROTOCOLLNPGM("idle");
|
||||
}
|
||||
|
||||
if (no_action) return;
|
||||
|
||||
if (abl_probe_index == 0) {
|
||||
// For the initial G29 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;
|
||||
|
||||
incremental_LSF(&lsf_results, xProbe, yProbe, measured_z);
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
z_values[xCount][yCount] = measured_z + zoffset;
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_PROTOCOLPAIR("Save X", xCount);
|
||||
SERIAL_PROTOCOLPAIR(" Y", yCount);
|
||||
SERIAL_PROTOCOLLNPAIR(" Z", measured_z + zoffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
|
||||
|
||||
points[abl_probe_index].z = measured_z;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// If there's another point to sample, move there with optional lift.
|
||||
//
|
||||
|
||||
#if ABL_GRID
|
||||
|
||||
// Skip any unreachable points
|
||||
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);
|
||||
|
||||
// Probe in reverse order for every other row/column
|
||||
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 = xCount * xGridSpacing + left_probe_bed_position,
|
||||
yBase = yCount * yGridSpacing + front_probe_bed_position;
|
||||
|
||||
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
|
||||
|
||||
// Keep looping till a reachable point is found
|
||||
if (position_is_reachable_xy(xProbe, yProbe)) 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!
|
||||
#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 {
|
||||
|
||||
// Leveling done! Fall through to G29 finishing code below
|
||||
|
||||
SERIAL_PROTOCOLLNPGM("Grid probing done.");
|
||||
|
||||
// Re-enable software endstops, if needed
|
||||
#if HAS_SOFTWARE_ENDSTOPS
|
||||
soft_endstops_enabled = enable_soft_endstops;
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
|
||||
|
||||
// Probe at 3 arbitrary points
|
||||
if (abl_probe_index < 3) {
|
||||
xProbe = LOGICAL_X_POSITION(points[abl_probe_index].x);
|
||||
yProbe = LOGICAL_Y_POSITION(points[abl_probe_index].y);
|
||||
#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.");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_3POINT
|
||||
|
||||
#else // !PROBE_MANUALLY
|
||||
{
|
||||
const bool stow_probe_after_each = parser.boolval('E');
|
||||
|
||||
measured_z = 0;
|
||||
|
||||
#if ABL_GRID
|
||||
|
||||
bool zig = PR_OUTER_END & 1; // Always end at RIGHT and BACK_PROBE_BED_POSITION
|
||||
|
||||
// Outer loop is Y with PROBE_Y_FIRST disabled
|
||||
for (uint8_t PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) {
|
||||
|
||||
int8_t inStart, inStop, inInc;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
zig ^= true; // zag
|
||||
|
||||
// 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; // 0...
|
||||
#endif
|
||||
|
||||
#if IS_KINEMATIC
|
||||
// Avoid probing outside the round or hexagonal area
|
||||
if (!position_is_reachable_by_probe_xy(xProbe, yProbe)) continue;
|
||||
#endif
|
||||
|
||||
measured_z = faux ? 0.001 * random(-100, 101) : probe_pt(xProbe, yProbe, stow_probe_after_each, verbose_level);
|
||||
|
||||
if (isnan(measured_z)) {
|
||||
planner.abl_enabled = abl_should_enable;
|
||||
break;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
incremental_LSF(&lsf_results, xProbe, yProbe, measured_z);
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
z_values[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 = faux ? 0.001 * random(-100, 101) : probe_pt(xProbe, yProbe, stow_probe_after_each, verbose_level);
|
||||
if (isnan(measured_z)) {
|
||||
planner.abl_enabled = abl_should_enable;
|
||||
break;
|
||||
}
|
||||
points[i].z = measured_z;
|
||||
}
|
||||
|
||||
if (!dryrun && !isnan(measured_z)) {
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_3POINT
|
||||
|
||||
// Raise to _Z_CLEARANCE_DEPLOY_PROBE. Stow the probe.
|
||||
if (STOW_PROBE()) {
|
||||
planner.abl_enabled = abl_should_enable;
|
||||
measured_z = NAN;
|
||||
}
|
||||
}
|
||||
#endif // !PROBE_MANUALLY
|
||||
|
||||
//
|
||||
// G29 Finishing Code
|
||||
//
|
||||
// Unless this is a dry run, auto bed leveling will
|
||||
// definitely be enabled after this point.
|
||||
//
|
||||
// If code above wants to continue leveling, it should
|
||||
// return or loop before this point.
|
||||
//
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("> probing complete", current_position);
|
||||
#endif
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
g29_in_progress = false;
|
||||
#if ENABLED(LCD_BED_LEVELING)
|
||||
lcd_wait_for_move = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Calculate leveling, print reports, correct the position
|
||||
if (!isnan(measured_z)) {
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
if (!dryrun) extrapolate_unprobed_bed_level();
|
||||
print_bilinear_leveling_grid();
|
||||
|
||||
refresh_bed_level();
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
print_bilinear_leveling_grid_virt();
|
||||
#endif
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_LINEAR)
|
||||
|
||||
// For LINEAR leveling calculate matrix, print reports, correct the position
|
||||
|
||||
/**
|
||||
* 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];
|
||||
|
||||
finish_incremental_LSF(&lsf_results);
|
||||
plane_equation_coefficients[0] = -lsf_results.A; // We should be able to eliminate the '-' on these three lines and down below
|
||||
plane_equation_coefficients[1] = -lsf_results.B; // but that is not yet tested.
|
||||
plane_equation_coefficients[2] = -lsf_results.D;
|
||||
|
||||
mean /= abl2;
|
||||
|
||||
if (verbose_level) {
|
||||
SERIAL_PROTOCOLPGM("Eqn coefficients: a: ");
|
||||
SERIAL_PROTOCOL_F(plane_equation_coefficients[0], 8);
|
||||
SERIAL_PROTOCOLPGM(" b: ");
|
||||
SERIAL_PROTOCOL_F(plane_equation_coefficients[1], 8);
|
||||
SERIAL_PROTOCOLPGM(" d: ");
|
||||
SERIAL_PROTOCOL_F(plane_equation_coefficients[2], 8);
|
||||
SERIAL_EOL();
|
||||
if (verbose_level > 2) {
|
||||
SERIAL_PROTOCOLPGM("Mean of sampled points: ");
|
||||
SERIAL_PROTOCOL_F(mean, 8);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
|
||||
// Create the matrix but don't correct the position yet
|
||||
if (!dryrun)
|
||||
planner.bed_level_matrix = matrix_3x3::create_look_at(
|
||||
vector_3(-plane_equation_coefficients[0], -plane_equation_coefficients[1], 1) // We can eliminate the '-' here and up above
|
||||
);
|
||||
|
||||
// Show the Topography map if enabled
|
||||
if (do_topography_map) {
|
||||
|
||||
SERIAL_PROTOCOLLNPGM("\nBed Height Topography:\n"
|
||||
" +--- BACK --+\n"
|
||||
" | |\n"
|
||||
" L | (+) | R\n"
|
||||
" E | | I\n"
|
||||
" F | (-) N (+) | G\n"
|
||||
" T | | H\n"
|
||||
" | (-) | T\n"
|
||||
" | |\n"
|
||||
" O-- FRONT --+\n"
|
||||
" (0,0)");
|
||||
|
||||
float min_diff = 999;
|
||||
|
||||
for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) {
|
||||
for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) {
|
||||
int ind = indexIntoAB[xx][yy];
|
||||
float diff = eqnBVector[ind] - mean,
|
||||
x_tmp = eqnAMatrix[ind + 0 * abl2],
|
||||
y_tmp = eqnAMatrix[ind + 1 * abl2],
|
||||
z_tmp = 0;
|
||||
|
||||
apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp);
|
||||
|
||||
NOMORE(min_diff, eqnBVector[ind] - z_tmp);
|
||||
|
||||
if (diff >= 0.0)
|
||||
SERIAL_PROTOCOLPGM(" +"); // Include + for column alignment
|
||||
else
|
||||
SERIAL_PROTOCOLCHAR(' ');
|
||||
SERIAL_PROTOCOL_F(diff, 5);
|
||||
} // xx
|
||||
SERIAL_EOL();
|
||||
} // yy
|
||||
SERIAL_EOL();
|
||||
|
||||
if (verbose_level > 3) {
|
||||
SERIAL_PROTOCOLLNPGM("\nCorrected Bed Height vs. Bed Topology:");
|
||||
|
||||
for (int8_t yy = abl_grid_points_y - 1; yy >= 0; yy--) {
|
||||
for (uint8_t xx = 0; xx < abl_grid_points_x; xx++) {
|
||||
int ind = indexIntoAB[xx][yy];
|
||||
float x_tmp = eqnAMatrix[ind + 0 * abl2],
|
||||
y_tmp = eqnAMatrix[ind + 1 * abl2],
|
||||
z_tmp = 0;
|
||||
|
||||
apply_rotation_xyz(planner.bed_level_matrix, x_tmp, y_tmp, z_tmp);
|
||||
|
||||
float diff = eqnBVector[ind] - z_tmp - min_diff;
|
||||
if (diff >= 0.0)
|
||||
SERIAL_PROTOCOLPGM(" +");
|
||||
// Include + for column alignment
|
||||
else
|
||||
SERIAL_PROTOCOLCHAR(' ');
|
||||
SERIAL_PROTOCOL_F(diff, 5);
|
||||
} // xx
|
||||
SERIAL_EOL();
|
||||
} // yy
|
||||
SERIAL_EOL();
|
||||
}
|
||||
} //do_topography_map
|
||||
|
||||
#endif // AUTO_BED_LEVELING_LINEAR
|
||||
|
||||
#if ABL_PLANAR
|
||||
|
||||
// For LINEAR and 3POINT leveling correct the current position
|
||||
|
||||
if (verbose_level > 0)
|
||||
planner.bed_level_matrix.debug(PSTR("\n\nBed Level Correction Matrix:"));
|
||||
|
||||
if (!dryrun) {
|
||||
//
|
||||
// Correct the current XYZ position based on the tilted plane.
|
||||
//
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("G29 uncorrected XYZ", current_position);
|
||||
#endif
|
||||
|
||||
float converted[XYZ];
|
||||
COPY(converted, current_position);
|
||||
|
||||
planner.abl_enabled = true;
|
||||
planner.unapply_leveling(converted); // use conversion machinery
|
||||
planner.abl_enabled = false;
|
||||
|
||||
// Use the last measured distance to the bed, if possible
|
||||
if ( NEAR(current_position[X_AXIS], xProbe - (X_PROBE_OFFSET_FROM_EXTRUDER))
|
||||
&& NEAR(current_position[Y_AXIS], yProbe - (Y_PROBE_OFFSET_FROM_EXTRUDER))
|
||||
) {
|
||||
const float simple_z = current_position[Z_AXIS] - measured_z;
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_ECHOPAIR("Z from Probe:", simple_z);
|
||||
SERIAL_ECHOPAIR(" Matrix:", converted[Z_AXIS]);
|
||||
SERIAL_ECHOLNPAIR(" Discrepancy:", simple_z - converted[Z_AXIS]);
|
||||
}
|
||||
#endif
|
||||
converted[Z_AXIS] = simple_z;
|
||||
}
|
||||
|
||||
// The rotated XY and corrected Z are now current_position
|
||||
COPY(current_position, converted);
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("G29 corrected XYZ", current_position);
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
if (!dryrun) {
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR("G29 uncorrected Z:", current_position[Z_AXIS]);
|
||||
#endif
|
||||
|
||||
// Unapply the offset because it is going to be immediately applied
|
||||
// and cause compensation movement in Z
|
||||
current_position[Z_AXIS] -= bilinear_z_offset(current_position);
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR(" corrected Z:", current_position[Z_AXIS]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ABL_PLANAR
|
||||
|
||||
#ifdef Z_PROBE_END_SCRIPT
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR("Z Probe End Script: ", Z_PROBE_END_SCRIPT);
|
||||
#endif
|
||||
enqueue_and_echo_commands_P(PSTR(Z_PROBE_END_SCRIPT));
|
||||
stepper.synchronize();
|
||||
#endif
|
||||
|
||||
// Auto Bed Leveling is complete! Enable if possible.
|
||||
planner.abl_enabled = dryrun ? abl_should_enable : true;
|
||||
} // !isnan(measured_z)
|
||||
|
||||
// Restore state after probing
|
||||
if (!faux) clean_up_after_endstop_or_probe_move();
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< gcode_G29");
|
||||
#endif
|
||||
|
||||
report_current_position();
|
||||
|
||||
KEEPALIVE_STATE(IN_HANDLER);
|
||||
|
||||
if (planner.abl_enabled)
|
||||
SYNC_PLAN_POSITION_KINEMATIC();
|
||||
}
|
200
Marlin/src/gcode/calibrate/G29-mbl.h
Normal file
200
Marlin/src/gcode/calibrate/G29-mbl.h
Normal file
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../libs/buzzer.h"
|
||||
#include "../../lcd/ultralcd.h"
|
||||
|
||||
// Save 130 bytes with non-duplication of PSTR
|
||||
void echo_not_entered() { SERIAL_PROTOCOLLNPGM(" not entered."); }
|
||||
|
||||
void mbl_mesh_report() {
|
||||
SERIAL_PROTOCOLLNPGM("Num X,Y: " STRINGIFY(GRID_MAX_POINTS_X) "," STRINGIFY(GRID_MAX_POINTS_Y));
|
||||
SERIAL_PROTOCOLPGM("Z offset: "); SERIAL_PROTOCOL_F(mbl.z_offset, 5);
|
||||
SERIAL_PROTOCOLLNPGM("\nMeasured points:");
|
||||
print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 5,
|
||||
[](const uint8_t ix, const uint8_t iy) { return mbl.z_values[ix][iy]; }
|
||||
);
|
||||
}
|
||||
|
||||
void mesh_probing_done() {
|
||||
mbl.set_has_mesh(true);
|
||||
home_all_axes();
|
||||
set_bed_leveling_enabled(true);
|
||||
#if ENABLED(MESH_G28_REST_ORIGIN)
|
||||
current_position[Z_AXIS] = LOGICAL_Z_POSITION(Z_MIN_POS);
|
||||
set_destination_to_current();
|
||||
line_to_destination(homing_feedrate(Z_AXIS));
|
||||
stepper.synchronize();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* G29: Mesh-based Z probe, probes a grid and produces a
|
||||
* mesh to compensate for variable bed height
|
||||
*
|
||||
* Parameters With MESH_BED_LEVELING:
|
||||
*
|
||||
* S0 Produce a mesh report
|
||||
* S1 Start probing mesh points
|
||||
* S2 Probe the next mesh point
|
||||
* S3 Xn Yn Zn.nn Manually modify a single point
|
||||
* S4 Zn.nn Set z offset. Positive away from bed, negative closer to bed.
|
||||
* S5 Reset and disable mesh
|
||||
*
|
||||
* The S0 report the points as below
|
||||
*
|
||||
* +----> X-axis 1-n
|
||||
* |
|
||||
* |
|
||||
* v Y-axis 1-n
|
||||
*
|
||||
*/
|
||||
void gcode_G29() {
|
||||
|
||||
static int mbl_probe_index = -1;
|
||||
#if HAS_SOFTWARE_ENDSTOPS
|
||||
static bool enable_soft_endstops;
|
||||
#endif
|
||||
|
||||
const MeshLevelingState state = (MeshLevelingState)parser.byteval('S', (int8_t)MeshReport);
|
||||
if (!WITHIN(state, 0, 5)) {
|
||||
SERIAL_PROTOCOLLNPGM("S out of range (0-5).");
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t px, py;
|
||||
|
||||
switch (state) {
|
||||
case MeshReport:
|
||||
if (leveling_is_valid()) {
|
||||
SERIAL_PROTOCOLLNPAIR("State: ", leveling_is_active() ? MSG_ON : MSG_OFF);
|
||||
mbl_mesh_report();
|
||||
}
|
||||
else
|
||||
SERIAL_PROTOCOLLNPGM("Mesh bed leveling has no data.");
|
||||
break;
|
||||
|
||||
case MeshStart:
|
||||
mbl.reset();
|
||||
mbl_probe_index = 0;
|
||||
enqueue_and_echo_commands_P(PSTR("G28\nG29 S2"));
|
||||
break;
|
||||
|
||||
case MeshNext:
|
||||
if (mbl_probe_index < 0) {
|
||||
SERIAL_PROTOCOLLNPGM("Start mesh probing with \"G29 S1\" first.");
|
||||
return;
|
||||
}
|
||||
// For each G29 S2...
|
||||
if (mbl_probe_index == 0) {
|
||||
#if HAS_SOFTWARE_ENDSTOPS
|
||||
// For the initial G29 S2 save software endstop state
|
||||
enable_soft_endstops = soft_endstops_enabled;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// For G29 S2 after adjusting Z.
|
||||
mbl.set_zigzag_z(mbl_probe_index - 1, current_position[Z_AXIS]);
|
||||
#if HAS_SOFTWARE_ENDSTOPS
|
||||
soft_endstops_enabled = enable_soft_endstops;
|
||||
#endif
|
||||
}
|
||||
// If there's another point to sample, move there with optional lift.
|
||||
if (mbl_probe_index < GRID_MAX_POINTS) {
|
||||
mbl.zigzag(mbl_probe_index, px, py);
|
||||
_manual_goto_xy(mbl.index_to_xpos[px], mbl.index_to_ypos[py]);
|
||||
|
||||
#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
|
||||
|
||||
mbl_probe_index++;
|
||||
}
|
||||
else {
|
||||
// One last "return to the bed" (as originally coded) at completion
|
||||
current_position[Z_AXIS] = LOGICAL_Z_POSITION(Z_MIN_POS) + MANUAL_PROBE_HEIGHT;
|
||||
line_to_current_position();
|
||||
stepper.synchronize();
|
||||
|
||||
// After recording the last point, activate home and activate
|
||||
mbl_probe_index = -1;
|
||||
SERIAL_PROTOCOLLNPGM("Mesh probing done.");
|
||||
BUZZ(100, 659);
|
||||
BUZZ(100, 698);
|
||||
mesh_probing_done();
|
||||
}
|
||||
break;
|
||||
|
||||
case MeshSet:
|
||||
if (parser.seenval('X')) {
|
||||
px = parser.value_int() - 1;
|
||||
if (!WITHIN(px, 0, GRID_MAX_POINTS_X - 1)) {
|
||||
SERIAL_PROTOCOLLNPGM("X out of range (1-" STRINGIFY(GRID_MAX_POINTS_X) ").");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SERIAL_CHAR('X'); echo_not_entered();
|
||||
return;
|
||||
}
|
||||
|
||||
if (parser.seenval('Y')) {
|
||||
py = parser.value_int() - 1;
|
||||
if (!WITHIN(py, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
SERIAL_PROTOCOLLNPGM("Y out of range (1-" STRINGIFY(GRID_MAX_POINTS_Y) ").");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SERIAL_CHAR('Y'); echo_not_entered();
|
||||
return;
|
||||
}
|
||||
|
||||
if (parser.seenval('Z')) {
|
||||
mbl.z_values[px][py] = parser.value_linear_units();
|
||||
}
|
||||
else {
|
||||
SERIAL_CHAR('Z'); echo_not_entered();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case MeshSetZOffset:
|
||||
if (parser.seenval('Z')) {
|
||||
mbl.z_offset = parser.value_linear_units();
|
||||
}
|
||||
else {
|
||||
SERIAL_CHAR('Z'); echo_not_entered();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case MeshReset:
|
||||
reset_bed_level();
|
||||
break;
|
||||
|
||||
} // switch(state)
|
||||
|
||||
report_current_position();
|
||||
}
|
27
Marlin/src/gcode/calibrate/G29-ubl.h
Normal file
27
Marlin/src/gcode/calibrate/G29-ubl.h
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
void gcode_G29() {
|
||||
|
||||
ubl.G29();
|
||||
|
||||
}
|
65
Marlin/src/gcode/calibrate/G29.h
Normal file
65
Marlin/src/gcode/calibrate/G29.h
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if ENABLED(MESH_BED_LEVELING) || ENABLED(PROBE_MANUALLY)
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY) && ENABLED(LCD_BED_LEVELING)
|
||||
extern bool lcd_wait_for_move;
|
||||
#endif
|
||||
|
||||
inline void _manual_goto_xy(const float &x, const float &y) {
|
||||
const float old_feedrate_mm_s = feedrate_mm_s;
|
||||
#if MANUAL_PROBE_HEIGHT > 0
|
||||
const float prev_z = current_position[Z_AXIS];
|
||||
feedrate_mm_s = homing_feedrate(Z_AXIS);
|
||||
current_position[Z_AXIS] = LOGICAL_Z_POSITION(MANUAL_PROBE_HEIGHT);
|
||||
line_to_current_position();
|
||||
#endif
|
||||
|
||||
feedrate_mm_s = MMM_TO_MMS(XY_PROBE_SPEED);
|
||||
current_position[X_AXIS] = LOGICAL_X_POSITION(x);
|
||||
current_position[Y_AXIS] = LOGICAL_Y_POSITION(y);
|
||||
line_to_current_position();
|
||||
|
||||
#if MANUAL_PROBE_HEIGHT > 0
|
||||
feedrate_mm_s = homing_feedrate(Z_AXIS);
|
||||
current_position[Z_AXIS] = prev_z; // move back to the previous Z.
|
||||
line_to_current_position();
|
||||
#endif
|
||||
|
||||
feedrate_mm_s = old_feedrate_mm_s;
|
||||
stepper.synchronize();
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY) && ENABLED(LCD_BED_LEVELING)
|
||||
lcd_wait_for_move = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(MESH_BED_LEVELING)
|
||||
#include "G29-mbl.h"
|
||||
#elif ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
#include "G29-ubl.h"
|
||||
#elif HAS_ABL
|
||||
#include "G29-abl.h"
|
||||
#endif
|
451
Marlin/src/gcode/calibrate/G33.h
Normal file
451
Marlin/src/gcode/calibrate/G33.h
Normal file
@ -0,0 +1,451 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if HOTENDS > 1
|
||||
#include "../control/tool_change.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* G33 - Delta '1-4-7-point' Auto-Calibration
|
||||
* Calibrate height, endstops, delta radius, and tower angles.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* Pn Number of probe points:
|
||||
*
|
||||
* P1 Probe center and set height only.
|
||||
* P2 Probe center and towers. Set height, endstops, and delta radius.
|
||||
* P3 Probe all positions: center, towers and opposite towers. Set all.
|
||||
* P4-P7 Probe all positions at different locations and average them.
|
||||
*
|
||||
* T0 Don't calibrate tower angle corrections
|
||||
*
|
||||
* Cn.nn Calibration precision; when omitted calibrates to maximum precision
|
||||
*
|
||||
* Fn Force to run at least n iterations and takes the best result
|
||||
*
|
||||
* Vn Verbose level:
|
||||
*
|
||||
* V0 Dry-run mode. Report settings and probe results. No calibration.
|
||||
* V1 Report settings
|
||||
* V2 Report settings and probe results
|
||||
*
|
||||
* E Engage the probe for each point
|
||||
*/
|
||||
|
||||
void print_signed_float(const char * const prefix, const float &f) {
|
||||
SERIAL_PROTOCOLPGM(" ");
|
||||
serialprintPGM(prefix);
|
||||
SERIAL_PROTOCOLCHAR(':');
|
||||
if (f >= 0) SERIAL_CHAR('+');
|
||||
SERIAL_PROTOCOL_F(f, 2);
|
||||
}
|
||||
|
||||
inline void print_G33_settings(const bool end_stops, const bool tower_angles){ // TODO echo these to LCD ???
|
||||
SERIAL_PROTOCOLPAIR(".Height:", DELTA_HEIGHT + home_offset[Z_AXIS]);
|
||||
if (end_stops) {
|
||||
print_signed_float(PSTR(" Ex"), endstop_adj[A_AXIS]);
|
||||
print_signed_float(PSTR("Ey"), endstop_adj[B_AXIS]);
|
||||
print_signed_float(PSTR("Ez"), endstop_adj[C_AXIS]);
|
||||
SERIAL_PROTOCOLPAIR(" Radius:", delta_radius);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
if (tower_angles) {
|
||||
SERIAL_PROTOCOLPGM(".Tower angle : ");
|
||||
print_signed_float(PSTR("Tx"), delta_tower_angle_trim[A_AXIS]);
|
||||
print_signed_float(PSTR("Ty"), delta_tower_angle_trim[B_AXIS]);
|
||||
SERIAL_PROTOCOLLNPGM(" Tz:+0.00");
|
||||
}
|
||||
}
|
||||
|
||||
void G33_cleanup(
|
||||
#if HOTENDS > 1
|
||||
const uint8_t old_tool_index
|
||||
#endif
|
||||
) {
|
||||
#if ENABLED(DELTA_HOME_TO_SAFE_ZONE)
|
||||
do_blocking_move_to_z(delta_clip_start_height);
|
||||
#endif
|
||||
STOW_PROBE();
|
||||
clean_up_after_endstop_or_probe_move();
|
||||
#if HOTENDS > 1
|
||||
tool_change(old_tool_index, 0, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gcode_G33() {
|
||||
|
||||
const int8_t probe_points = parser.intval('P', DELTA_CALIBRATION_DEFAULT_POINTS);
|
||||
if (!WITHIN(probe_points, 1, 7)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(P)oints is implausible (1-7).");
|
||||
return;
|
||||
}
|
||||
|
||||
const int8_t verbose_level = parser.byteval('V', 1);
|
||||
if (!WITHIN(verbose_level, 0, 2)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-2).");
|
||||
return;
|
||||
}
|
||||
|
||||
const float calibration_precision = parser.floatval('C');
|
||||
if (calibration_precision < 0) {
|
||||
SERIAL_PROTOCOLLNPGM("?(C)alibration precision is implausible (>0).");
|
||||
return;
|
||||
}
|
||||
|
||||
const int8_t force_iterations = parser.intval('F', 0);
|
||||
if (!WITHIN(force_iterations, 0, 30)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(F)orce iteration is implausible (0-30).");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool towers_set = parser.boolval('T', true),
|
||||
stow_after_each = parser.boolval('E'),
|
||||
_1p_calibration = probe_points == 1,
|
||||
_4p_calibration = probe_points == 2,
|
||||
_4p_towers_points = _4p_calibration && towers_set,
|
||||
_4p_opposite_points = _4p_calibration && !towers_set,
|
||||
_7p_calibration = probe_points >= 3,
|
||||
_7p_half_circle = probe_points == 3,
|
||||
_7p_double_circle = probe_points == 5,
|
||||
_7p_triple_circle = probe_points == 6,
|
||||
_7p_quadruple_circle = probe_points == 7,
|
||||
_7p_multi_circle = _7p_double_circle || _7p_triple_circle || _7p_quadruple_circle,
|
||||
_7p_intermed_points = _7p_calibration && !_7p_half_circle;
|
||||
const static char save_message[] PROGMEM = "Save with M500 and/or copy to Configuration.h";
|
||||
const float dx = (X_PROBE_OFFSET_FROM_EXTRUDER),
|
||||
dy = (Y_PROBE_OFFSET_FROM_EXTRUDER);
|
||||
int8_t iterations = 0;
|
||||
float test_precision,
|
||||
zero_std_dev = (verbose_level ? 999.0 : 0.0), // 0.0 in dry-run mode : forced end
|
||||
zero_std_dev_old = zero_std_dev,
|
||||
zero_std_dev_min = zero_std_dev,
|
||||
e_old[XYZ] = {
|
||||
endstop_adj[A_AXIS],
|
||||
endstop_adj[B_AXIS],
|
||||
endstop_adj[C_AXIS]
|
||||
},
|
||||
dr_old = delta_radius,
|
||||
zh_old = home_offset[Z_AXIS],
|
||||
alpha_old = delta_tower_angle_trim[A_AXIS],
|
||||
beta_old = delta_tower_angle_trim[B_AXIS];
|
||||
|
||||
if (!_1p_calibration) { // test if the outer radius is reachable
|
||||
const float circles = (_7p_quadruple_circle ? 1.5 :
|
||||
_7p_triple_circle ? 1.0 :
|
||||
_7p_double_circle ? 0.5 : 0),
|
||||
r = (1 + circles * 0.1) * delta_calibration_radius;
|
||||
for (uint8_t axis = 1; axis < 13; ++axis) {
|
||||
const float a = RADIANS(180 + 30 * axis);
|
||||
if (!position_is_reachable_xy(cos(a) * r, sin(a) * r)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(M665 B)ed radius is implausible.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
SERIAL_PROTOCOLLNPGM("G33 Auto Calibrate");
|
||||
|
||||
stepper.synchronize();
|
||||
#if HAS_LEVELING
|
||||
reset_bed_level(); // After calibration bed-level data is no longer valid
|
||||
#endif
|
||||
|
||||
#if HOTENDS > 1
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
tool_change(0, 0, true);
|
||||
#define G33_CLEANUP() G33_cleanup(old_tool_index)
|
||||
#else
|
||||
#define G33_CLEANUP() G33_cleanup()
|
||||
#endif
|
||||
|
||||
setup_for_endstop_or_probe_move();
|
||||
endstops.enable(true);
|
||||
if (!home_delta())
|
||||
return;
|
||||
endstops.not_homing();
|
||||
|
||||
// print settings
|
||||
|
||||
const char *checkingac = PSTR("Checking... AC"); // TODO: Make translatable string
|
||||
serialprintPGM(checkingac);
|
||||
if (verbose_level == 0) SERIAL_PROTOCOLPGM(" (DRY-RUN)");
|
||||
SERIAL_EOL();
|
||||
lcd_setstatusPGM(checkingac);
|
||||
|
||||
print_G33_settings(!_1p_calibration, _7p_calibration && towers_set);
|
||||
|
||||
#if DISABLED(PROBE_MANUALLY)
|
||||
const float measured_z = probe_pt(dx, dy, stow_after_each, 1, false); // 1st probe to set height
|
||||
if (isnan(measured_z)) return G33_CLEANUP();
|
||||
home_offset[Z_AXIS] -= measured_z;
|
||||
#endif
|
||||
|
||||
do {
|
||||
|
||||
float z_at_pt[13] = { 0.0 };
|
||||
|
||||
test_precision = zero_std_dev_old != 999.0 ? (zero_std_dev + zero_std_dev_old) / 2 : zero_std_dev;
|
||||
|
||||
iterations++;
|
||||
|
||||
// Probe the points
|
||||
|
||||
if (!_7p_half_circle && !_7p_triple_circle) { // probe the center
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
z_at_pt[0] += lcd_probe_pt(0, 0);
|
||||
#else
|
||||
z_at_pt[0] += probe_pt(dx, dy, stow_after_each, 1, false);
|
||||
if (isnan(z_at_pt[0])) return G33_CLEANUP();
|
||||
#endif
|
||||
}
|
||||
if (_7p_calibration) { // probe extra center points
|
||||
for (int8_t axis = _7p_multi_circle ? 11 : 9; axis > 0; axis -= _7p_multi_circle ? 2 : 4) {
|
||||
const float a = RADIANS(180 + 30 * axis), r = delta_calibration_radius * 0.1;
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
z_at_pt[0] += lcd_probe_pt(cos(a) * r, sin(a) * r);
|
||||
#else
|
||||
z_at_pt[0] += probe_pt(cos(a) * r + dx, sin(a) * r + dy, stow_after_each, 1);
|
||||
if (isnan(z_at_pt[0])) return G33_CLEANUP();
|
||||
#endif
|
||||
}
|
||||
z_at_pt[0] /= float(_7p_double_circle ? 7 : probe_points);
|
||||
}
|
||||
if (!_1p_calibration) { // probe the radius
|
||||
bool zig_zag = true;
|
||||
const uint8_t start = _4p_opposite_points ? 3 : 1,
|
||||
step = _4p_calibration ? 4 : _7p_half_circle ? 2 : 1;
|
||||
for (uint8_t axis = start; axis < 13; axis += step) {
|
||||
const float zigadd = (zig_zag ? 0.5 : 0.0),
|
||||
offset_circles = _7p_quadruple_circle ? zigadd + 1.0 :
|
||||
_7p_triple_circle ? zigadd + 0.5 :
|
||||
_7p_double_circle ? zigadd : 0;
|
||||
for (float circles = -offset_circles ; circles <= offset_circles; circles++) {
|
||||
const float a = RADIANS(180 + 30 * axis),
|
||||
r = delta_calibration_radius * (1 + circles * (zig_zag ? 0.1 : -0.1));
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
z_at_pt[axis] += lcd_probe_pt(cos(a) * r, sin(a) * r);
|
||||
#else
|
||||
z_at_pt[axis] += probe_pt(cos(a) * r + dx, sin(a) * r + dy, stow_after_each, 1);
|
||||
if (isnan(z_at_pt[axis])) return G33_CLEANUP();
|
||||
#endif
|
||||
}
|
||||
zig_zag = !zig_zag;
|
||||
z_at_pt[axis] /= (2 * offset_circles + 1);
|
||||
}
|
||||
}
|
||||
if (_7p_intermed_points) // average intermediates to tower and opposites
|
||||
for (uint8_t axis = 1; axis < 13; axis += 2)
|
||||
z_at_pt[axis] = (z_at_pt[axis] + (z_at_pt[axis + 1] + z_at_pt[(axis + 10) % 12 + 1]) / 2.0) / 2.0;
|
||||
|
||||
float S1 = z_at_pt[0],
|
||||
S2 = sq(z_at_pt[0]);
|
||||
int16_t N = 1;
|
||||
if (!_1p_calibration) // std dev from zero plane
|
||||
for (uint8_t axis = (_4p_opposite_points ? 3 : 1); axis < 13; axis += (_4p_calibration ? 4 : 2)) {
|
||||
S1 += z_at_pt[axis];
|
||||
S2 += sq(z_at_pt[axis]);
|
||||
N++;
|
||||
}
|
||||
zero_std_dev_old = zero_std_dev;
|
||||
zero_std_dev = round(SQRT(S2 / N) * 1000.0) / 1000.0 + 0.00001;
|
||||
|
||||
// Solve matrices
|
||||
|
||||
if ((zero_std_dev < test_precision && zero_std_dev > calibration_precision) || iterations <= force_iterations) {
|
||||
if (zero_std_dev < zero_std_dev_min) {
|
||||
COPY(e_old, endstop_adj);
|
||||
dr_old = delta_radius;
|
||||
zh_old = home_offset[Z_AXIS];
|
||||
alpha_old = delta_tower_angle_trim[A_AXIS];
|
||||
beta_old = delta_tower_angle_trim[B_AXIS];
|
||||
}
|
||||
|
||||
float e_delta[XYZ] = { 0.0 }, r_delta = 0.0, t_alpha = 0.0, t_beta = 0.0;
|
||||
const float r_diff = delta_radius - delta_calibration_radius,
|
||||
h_factor = 1.00 + r_diff * 0.001, //1.02 for r_diff = 20mm
|
||||
r_factor = -(1.75 + 0.005 * r_diff + 0.001 * sq(r_diff)), //2.25 for r_diff = 20mm
|
||||
a_factor = 100.0 / delta_calibration_radius; //1.25 for cal_rd = 80mm
|
||||
|
||||
#define ZP(N,I) ((N) * z_at_pt[I])
|
||||
#define Z1000(I) ZP(1.00, I)
|
||||
#define Z1050(I) ZP(h_factor, I)
|
||||
#define Z0700(I) ZP(h_factor * 2.0 / 3.00, I)
|
||||
#define Z0350(I) ZP(h_factor / 3.00, I)
|
||||
#define Z0175(I) ZP(h_factor / 6.00, I)
|
||||
#define Z2250(I) ZP(r_factor, I)
|
||||
#define Z0750(I) ZP(r_factor / 3.00, I)
|
||||
#define Z0375(I) ZP(r_factor / 6.00, I)
|
||||
#define Z0444(I) ZP(a_factor * 4.0 / 9.0, I)
|
||||
#define Z0888(I) ZP(a_factor * 8.0 / 9.0, I)
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
test_precision = 0.00; // forced end
|
||||
#endif
|
||||
|
||||
switch (probe_points) {
|
||||
case 1:
|
||||
test_precision = 0.00; // forced end
|
||||
LOOP_XYZ(i) e_delta[i] = Z1000(0);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (towers_set) {
|
||||
e_delta[X_AXIS] = Z1050(0) + Z0700(1) - Z0350(5) - Z0350(9);
|
||||
e_delta[Y_AXIS] = Z1050(0) - Z0350(1) + Z0700(5) - Z0350(9);
|
||||
e_delta[Z_AXIS] = Z1050(0) - Z0350(1) - Z0350(5) + Z0700(9);
|
||||
r_delta = Z2250(0) - Z0750(1) - Z0750(5) - Z0750(9);
|
||||
}
|
||||
else {
|
||||
e_delta[X_AXIS] = Z1050(0) - Z0700(7) + Z0350(11) + Z0350(3);
|
||||
e_delta[Y_AXIS] = Z1050(0) + Z0350(7) - Z0700(11) + Z0350(3);
|
||||
e_delta[Z_AXIS] = Z1050(0) + Z0350(7) + Z0350(11) - Z0700(3);
|
||||
r_delta = Z2250(0) - Z0750(7) - Z0750(11) - Z0750(3);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
e_delta[X_AXIS] = Z1050(0) + Z0350(1) - Z0175(5) - Z0175(9) - Z0350(7) + Z0175(11) + Z0175(3);
|
||||
e_delta[Y_AXIS] = Z1050(0) - Z0175(1) + Z0350(5) - Z0175(9) + Z0175(7) - Z0350(11) + Z0175(3);
|
||||
e_delta[Z_AXIS] = Z1050(0) - Z0175(1) - Z0175(5) + Z0350(9) + Z0175(7) + Z0175(11) - Z0350(3);
|
||||
r_delta = Z2250(0) - Z0375(1) - Z0375(5) - Z0375(9) - Z0375(7) - Z0375(11) - Z0375(3);
|
||||
|
||||
if (towers_set) {
|
||||
t_alpha = Z0444(1) - Z0888(5) + Z0444(9) + Z0444(7) - Z0888(11) + Z0444(3);
|
||||
t_beta = Z0888(1) - Z0444(5) - Z0444(9) + Z0888(7) - Z0444(11) - Z0444(3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
LOOP_XYZ(axis) endstop_adj[axis] += e_delta[axis];
|
||||
delta_radius += r_delta;
|
||||
delta_tower_angle_trim[A_AXIS] += t_alpha;
|
||||
delta_tower_angle_trim[B_AXIS] += t_beta;
|
||||
|
||||
// adjust delta_height and endstops by the max amount
|
||||
const float z_temp = MAX3(endstop_adj[A_AXIS], endstop_adj[B_AXIS], endstop_adj[C_AXIS]);
|
||||
home_offset[Z_AXIS] -= z_temp;
|
||||
LOOP_XYZ(i) endstop_adj[i] -= z_temp;
|
||||
|
||||
recalc_delta_settings(delta_radius, delta_diagonal_rod);
|
||||
}
|
||||
else if (zero_std_dev >= test_precision) { // step one back
|
||||
COPY(endstop_adj, e_old);
|
||||
delta_radius = dr_old;
|
||||
home_offset[Z_AXIS] = zh_old;
|
||||
delta_tower_angle_trim[A_AXIS] = alpha_old;
|
||||
delta_tower_angle_trim[B_AXIS] = beta_old;
|
||||
|
||||
recalc_delta_settings(delta_radius, delta_diagonal_rod);
|
||||
}
|
||||
NOMORE(zero_std_dev_min, zero_std_dev);
|
||||
|
||||
// print report
|
||||
|
||||
if (verbose_level != 1) {
|
||||
SERIAL_PROTOCOLPGM(". ");
|
||||
print_signed_float(PSTR("c"), z_at_pt[0]);
|
||||
if (_4p_towers_points || _7p_calibration) {
|
||||
print_signed_float(PSTR(" x"), z_at_pt[1]);
|
||||
print_signed_float(PSTR(" y"), z_at_pt[5]);
|
||||
print_signed_float(PSTR(" z"), z_at_pt[9]);
|
||||
}
|
||||
if (!_4p_opposite_points) SERIAL_EOL();
|
||||
if ((_4p_opposite_points) || _7p_calibration) {
|
||||
if (_7p_calibration) {
|
||||
SERIAL_CHAR('.');
|
||||
SERIAL_PROTOCOL_SP(13);
|
||||
}
|
||||
print_signed_float(PSTR(" yz"), z_at_pt[7]);
|
||||
print_signed_float(PSTR("zx"), z_at_pt[11]);
|
||||
print_signed_float(PSTR("xy"), z_at_pt[3]);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
if (verbose_level != 0) { // !dry run
|
||||
if ((zero_std_dev >= test_precision || zero_std_dev <= calibration_precision) && iterations > force_iterations) { // end iterations
|
||||
SERIAL_PROTOCOLPGM("Calibration OK");
|
||||
SERIAL_PROTOCOL_SP(36);
|
||||
#if DISABLED(PROBE_MANUALLY)
|
||||
if (zero_std_dev >= test_precision && !_1p_calibration)
|
||||
SERIAL_PROTOCOLPGM("rolling back.");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SERIAL_PROTOCOLPGM("std dev:");
|
||||
SERIAL_PROTOCOL_F(zero_std_dev_min, 3);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
char mess[21];
|
||||
sprintf_P(mess, PSTR("Calibration sd:"));
|
||||
if (zero_std_dev_min < 1)
|
||||
sprintf_P(&mess[15], PSTR("0.%03i"), (int)round(zero_std_dev_min * 1000.0));
|
||||
else
|
||||
sprintf_P(&mess[15], PSTR("%03i.x"), (int)round(zero_std_dev_min));
|
||||
lcd_setstatus(mess);
|
||||
print_G33_settings(!_1p_calibration, _7p_calibration && towers_set);
|
||||
serialprintPGM(save_message);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
else { // !end iterations
|
||||
char mess[15];
|
||||
if (iterations < 31)
|
||||
sprintf_P(mess, PSTR("Iteration : %02i"), (int)iterations);
|
||||
else
|
||||
sprintf_P(mess, PSTR("No convergence"));
|
||||
SERIAL_PROTOCOL(mess);
|
||||
SERIAL_PROTOCOL_SP(36);
|
||||
SERIAL_PROTOCOLPGM("std dev:");
|
||||
SERIAL_PROTOCOL_F(zero_std_dev, 3);
|
||||
SERIAL_EOL();
|
||||
lcd_setstatus(mess);
|
||||
print_G33_settings(!_1p_calibration, _7p_calibration && towers_set);
|
||||
}
|
||||
}
|
||||
else { // dry run
|
||||
const char *enddryrun = PSTR("End DRY-RUN");
|
||||
serialprintPGM(enddryrun);
|
||||
SERIAL_PROTOCOL_SP(39);
|
||||
SERIAL_PROTOCOLPGM("std dev:");
|
||||
SERIAL_PROTOCOL_F(zero_std_dev, 3);
|
||||
SERIAL_EOL();
|
||||
|
||||
char mess[21];
|
||||
sprintf_P(mess, enddryrun);
|
||||
sprintf_P(&mess[11], PSTR(" sd:"));
|
||||
if (zero_std_dev < 1)
|
||||
sprintf_P(&mess[15], PSTR("0.%03i"), (int)round(zero_std_dev * 1000.0));
|
||||
else
|
||||
sprintf_P(&mess[15], PSTR("%03i.x"), (int)round(zero_std_dev));
|
||||
lcd_setstatus(mess);
|
||||
}
|
||||
|
||||
endstops.enable(true);
|
||||
home_delta();
|
||||
endstops.not_homing();
|
||||
|
||||
}
|
||||
while ((zero_std_dev < test_precision && zero_std_dev > calibration_precision && iterations < 31) || iterations <= force_iterations);
|
||||
|
||||
G33_CLEANUP();
|
||||
}
|
322
Marlin/src/gcode/calibrate/M100.h
Normal file
322
Marlin/src/gcode/calibrate/M100.h
Normal file
@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../libs/hex_print_routines.h"
|
||||
|
||||
/**
|
||||
* M100 Free Memory Watcher
|
||||
*
|
||||
* This code watches the free memory block between the bottom of the heap and the top of the stack.
|
||||
* This memory block is initialized and watched via the M100 command.
|
||||
*
|
||||
* M100 I Initializes the free memory block and prints vitals statistics about the area
|
||||
*
|
||||
* M100 F Identifies how much of the free memory block remains free and unused. It also
|
||||
* detects and reports any corruption within the free memory block that may have
|
||||
* happened due to errant firmware.
|
||||
*
|
||||
* M100 D Does a hex display of the free memory block along with a flag for any errant
|
||||
* data that does not match the expected value.
|
||||
*
|
||||
* M100 C x Corrupts x locations within the free memory block. This is useful to check the
|
||||
* correctness of the M100 F and M100 D commands.
|
||||
*
|
||||
* Also, there are two support functions that can be called from a developer's C code.
|
||||
*
|
||||
* uint16_t check_for_free_memory_corruption(const char * const ptr);
|
||||
* void M100_dump_routine(const char * const title, const char *start, const char *end);
|
||||
*
|
||||
* Initial version by Roxy-3D
|
||||
*/
|
||||
#define M100_FREE_MEMORY_DUMPER // Enable for the `M110 D` Dump sub-command
|
||||
#define M100_FREE_MEMORY_CORRUPTOR // Enable for the `M100 C` Corrupt sub-command
|
||||
|
||||
#define TEST_BYTE ((char) 0xE5)
|
||||
|
||||
extern char command_queue[BUFSIZE][MAX_CMD_SIZE];
|
||||
|
||||
extern char* __brkval;
|
||||
extern size_t __heap_start, __heap_end, __flp;
|
||||
extern char __bss_end;
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
#define END_OF_HEAP() (__brkval ? __brkval : &__bss_end)
|
||||
|
||||
// Location of a variable on its stack frame. Returns a value above
|
||||
// the stack (once the function returns to the caller).
|
||||
char* top_of_stack() {
|
||||
char x;
|
||||
return &x + 1; // x is pulled on return;
|
||||
}
|
||||
|
||||
// Count the number of test bytes at the specified location.
|
||||
int16_t count_test_bytes(const char * const ptr) {
|
||||
for (uint16_t i = 0; i < 32000; i++)
|
||||
if (((char) ptr[i]) != TEST_BYTE)
|
||||
return i - 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// M100 sub-commands
|
||||
//
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_DUMPER)
|
||||
/**
|
||||
* M100 D
|
||||
* Dump the free memory block from __brkval to the stack pointer.
|
||||
* malloc() eats memory from the start of the block and the stack grows
|
||||
* up from the bottom of the block. Solid test bytes indicate nothing has
|
||||
* used that memory yet. There should not be anything but test bytes within
|
||||
* the block. If so, it may indicate memory corruption due to a bad pointer.
|
||||
* Unexpected bytes are flagged in the right column.
|
||||
*/
|
||||
void dump_free_memory(const char *ptr, const char *sp) {
|
||||
//
|
||||
// Start and end the dump on a nice 16 byte boundary
|
||||
// (even though the values are not 16-byte aligned).
|
||||
//
|
||||
ptr = (char *)((uint16_t)ptr & 0xFFF0); // Align to 16-byte boundary
|
||||
sp = (char *)((uint16_t)sp | 0x000F); // Align sp to the 15th byte (at or above sp)
|
||||
|
||||
// Dump command main loop
|
||||
while (ptr < sp) {
|
||||
print_hex_word((uint16_t)ptr); // Print the address
|
||||
SERIAL_CHAR(':');
|
||||
for (uint8_t i = 0; i < 16; i++) { // and 16 data bytes
|
||||
if (i == 8) SERIAL_CHAR('-');
|
||||
print_hex_byte(ptr[i]);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
safe_delay(25);
|
||||
SERIAL_CHAR('|'); // Point out non test bytes
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
char ccc = (char)ptr[i]; // cast to char before automatically casting to char on assignment, in case the compiler is broken
|
||||
if (&ptr[i] >= (const char*)command_queue && &ptr[i] < (const char*)(command_queue + sizeof(command_queue))) { // Print out ASCII in the command buffer area
|
||||
if (!WITHIN(ccc, ' ', 0x7E)) ccc = ' ';
|
||||
}
|
||||
else { // If not in the command buffer area, flag bytes that don't match the test byte
|
||||
ccc = (ccc == TEST_BYTE) ? ' ' : '?';
|
||||
}
|
||||
SERIAL_CHAR(ccc);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
ptr += 16;
|
||||
safe_delay(25);
|
||||
idle();
|
||||
}
|
||||
}
|
||||
|
||||
void M100_dump_routine(const char * const title, const char *start, const char *end) {
|
||||
SERIAL_ECHOLN(title);
|
||||
//
|
||||
// Round the start and end locations to produce full lines of output
|
||||
//
|
||||
start = (char*)((uint16_t) start & 0xFFF0);
|
||||
end = (char*)((uint16_t) end | 0x000F);
|
||||
dump_free_memory(start, end);
|
||||
}
|
||||
|
||||
#endif // M100_FREE_MEMORY_DUMPER
|
||||
|
||||
int check_for_free_memory_corruption(const char * const title) {
|
||||
SERIAL_ECHO(title);
|
||||
|
||||
char *ptr = END_OF_HEAP(), *sp = top_of_stack();
|
||||
int n = sp - ptr;
|
||||
|
||||
SERIAL_ECHOPAIR("\nfmc() n=", n);
|
||||
SERIAL_ECHOPAIR("\n&__brkval: ", hex_address(&__brkval));
|
||||
SERIAL_ECHOPAIR("=", hex_address(__brkval));
|
||||
SERIAL_ECHOPAIR("\n__bss_end: ", hex_address(&__bss_end));
|
||||
SERIAL_ECHOPAIR(" sp=", hex_address(sp));
|
||||
|
||||
if (sp < ptr) {
|
||||
SERIAL_ECHOPGM(" sp < Heap ");
|
||||
// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
|
||||
// safe_delay(5); // this code can be enabled to pause the display as soon as the
|
||||
// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
|
||||
// idle(); // being on pin-63 which is unassigend and available on most controller
|
||||
// safe_delay(20); // boards.
|
||||
// while ( !READ(63))
|
||||
// idle();
|
||||
safe_delay(20);
|
||||
#if ENABLED(M100_FREE_MEMORY_DUMPER)
|
||||
M100_dump_routine(" Memory corruption detected with sp<Heap\n", (char*)0x1B80, (char*)0x21FF);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Scan through the range looking for the biggest block of 0xE5's we can find
|
||||
int block_cnt = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (ptr[i] == TEST_BYTE) {
|
||||
int16_t j = count_test_bytes(ptr + i);
|
||||
if (j > 8) {
|
||||
// SERIAL_ECHOPAIR("Found ", j);
|
||||
// SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(ptr + i));
|
||||
i += j;
|
||||
block_cnt++;
|
||||
SERIAL_ECHOPAIR(" (", block_cnt);
|
||||
SERIAL_ECHOPAIR(") found=", j);
|
||||
SERIAL_ECHOPGM(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
SERIAL_ECHOPAIR(" block_found=", block_cnt);
|
||||
|
||||
if (block_cnt != 1 || __brkval != 0x0000)
|
||||
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
|
||||
|
||||
if (block_cnt == 0) // Make sure the special case of no free blocks shows up as an
|
||||
block_cnt = -1; // error to the calling code!
|
||||
|
||||
SERIAL_ECHOPGM(" return=");
|
||||
if (block_cnt == 1) {
|
||||
SERIAL_CHAR('0'); // if the block_cnt is 1, nothing has broken up the free memory
|
||||
SERIAL_EOL(); // area and it is appropriate to say 'no corruption'.
|
||||
return 0;
|
||||
}
|
||||
SERIAL_ECHOLNPGM("true");
|
||||
return block_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* M100 F
|
||||
* Return the number of free bytes in the memory pool,
|
||||
* with other vital statistics defining the pool.
|
||||
*/
|
||||
void free_memory_pool_report(char * const ptr, const int16_t size) {
|
||||
int16_t max_cnt = -1, block_cnt = 0;
|
||||
char *max_addr = NULL;
|
||||
// Find the longest block of test bytes in the buffer
|
||||
for (int16_t i = 0; i < size; i++) {
|
||||
char *addr = ptr + i;
|
||||
if (*addr == TEST_BYTE) {
|
||||
const int16_t j = count_test_bytes(addr);
|
||||
if (j > 8) {
|
||||
SERIAL_ECHOPAIR("Found ", j);
|
||||
SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
|
||||
if (j > max_cnt) {
|
||||
max_cnt = j;
|
||||
max_addr = addr;
|
||||
}
|
||||
i += j;
|
||||
block_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block_cnt > 1) {
|
||||
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
|
||||
SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
|
||||
SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
|
||||
}
|
||||
SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption("M100 F "));
|
||||
}
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
|
||||
/**
|
||||
* M100 C<num>
|
||||
* Corrupt <num> locations in the free memory pool and report the corrupt addresses.
|
||||
* This is useful to check the correctness of the M100 D and the M100 F commands.
|
||||
*/
|
||||
void corrupt_free_memory(char *ptr, const uint16_t size) {
|
||||
ptr += 8;
|
||||
const uint16_t near_top = top_of_stack() - ptr - 250, // -250 to avoid interrupt activity that's altered the stack.
|
||||
j = near_top / (size + 1);
|
||||
|
||||
SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
|
||||
for (uint16_t i = 1; i <= size; i++) {
|
||||
char * const addr = ptr + i * j;
|
||||
*addr = i;
|
||||
SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
#endif // M100_FREE_MEMORY_CORRUPTOR
|
||||
|
||||
/**
|
||||
* M100 I
|
||||
* Init memory for the M100 tests. (Automatically applied on the first M100.)
|
||||
*/
|
||||
void init_free_memory(char *ptr, int16_t size) {
|
||||
SERIAL_ECHOLNPGM("Initializing free memory block.\n\n");
|
||||
|
||||
size -= 250; // -250 to avoid interrupt activity that's altered the stack.
|
||||
if (size < 0) {
|
||||
SERIAL_ECHOLNPGM("Unable to initialize.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr += 8; // move a few bytes away from the heap just because we don't want
|
||||
// to be altering memory that close to it.
|
||||
memset(ptr, TEST_BYTE, size);
|
||||
|
||||
SERIAL_ECHO(size);
|
||||
SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
|
||||
|
||||
for (int16_t i = 0; i < size; i++) {
|
||||
if (ptr[i] != TEST_BYTE) {
|
||||
SERIAL_ECHOPAIR("? address : ", hex_address(ptr + i));
|
||||
SERIAL_ECHOLNPAIR("=", hex_byte(ptr[i]));
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* M100: Free Memory Check
|
||||
*/
|
||||
void gcode_M100() {
|
||||
SERIAL_ECHOPAIR("\n__brkval : ", hex_address(__brkval));
|
||||
SERIAL_ECHOPAIR("\n__bss_end : ", hex_address(&__bss_end));
|
||||
|
||||
char *ptr = END_OF_HEAP(), *sp = top_of_stack();
|
||||
|
||||
SERIAL_ECHOPAIR("\nstart of free space : ", hex_address(ptr));
|
||||
SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
|
||||
|
||||
// Always init on the first invocation of M100
|
||||
static bool m100_not_initialized = true;
|
||||
if (m100_not_initialized || parser.seen('I')) {
|
||||
m100_not_initialized = false;
|
||||
init_free_memory(ptr, sp - ptr);
|
||||
}
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_DUMPER)
|
||||
if (parser.seen('D'))
|
||||
return dump_free_memory(ptr, sp);
|
||||
#endif
|
||||
|
||||
if (parser.seen('F'))
|
||||
return free_memory_pool_report(ptr, sp - ptr);
|
||||
|
||||
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
|
||||
|
||||
if (parser.seen('C'))
|
||||
return corrupt_free_memory(ptr, parser.value_int());
|
||||
|
||||
#endif
|
||||
}
|
121
Marlin/src/gcode/calibrate/M420.h
Normal file
121
Marlin/src/gcode/calibrate/M420.h
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* M420: Enable/Disable Bed Leveling and/or set the Z fade height.
|
||||
*
|
||||
* S[bool] Turns leveling on or off
|
||||
* Z[height] Sets the Z fade height (0 or none to disable)
|
||||
* V[bool] Verbose - Print the leveling grid
|
||||
*
|
||||
* With AUTO_BED_LEVELING_UBL only:
|
||||
*
|
||||
* L[index] Load UBL mesh from index (0 is default)
|
||||
*/
|
||||
void gcode_M420() {
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
// L to load a mesh from the EEPROM
|
||||
if (parser.seen('L')) {
|
||||
|
||||
#if ENABLED(EEPROM_SETTINGS)
|
||||
const int8_t storage_slot = parser.has_value() ? parser.value_int() : ubl.state.storage_slot;
|
||||
const int16_t a = settings.calc_num_meshes();
|
||||
|
||||
if (!a) {
|
||||
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WITHIN(storage_slot, 0, a - 1)) {
|
||||
SERIAL_PROTOCOLLNPGM("?Invalid storage slot.");
|
||||
SERIAL_PROTOCOLLNPAIR("?Use 0 to ", a - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.load_mesh(storage_slot);
|
||||
ubl.state.storage_slot = storage_slot;
|
||||
|
||||
#else
|
||||
|
||||
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available.");
|
||||
return;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// L to load a mesh from the EEPROM
|
||||
if (parser.seen('L') || parser.seen('V')) {
|
||||
ubl.display_map(0); // Currently only supports one map type
|
||||
SERIAL_ECHOLNPAIR("UBL_MESH_VALID = ", UBL_MESH_VALID);
|
||||
SERIAL_ECHOLNPAIR("ubl.state.storage_slot = ", ubl.state.storage_slot);
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
||||
|
||||
// V to print the matrix or mesh
|
||||
if (parser.seen('V')) {
|
||||
#if ABL_PLANAR
|
||||
planner.bed_level_matrix.debug(PSTR("Bed Level Correction Matrix:"));
|
||||
#else
|
||||
if (leveling_is_valid()) {
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
print_bilinear_leveling_grid();
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
print_bilinear_leveling_grid_virt();
|
||||
#endif
|
||||
#elif ENABLED(MESH_BED_LEVELING)
|
||||
SERIAL_ECHOLNPGM("Mesh Bed Level data:");
|
||||
mbl_mesh_report();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const bool to_enable = parser.boolval('S');
|
||||
if (parser.seen('S'))
|
||||
set_bed_leveling_enabled(to_enable);
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
if (parser.seen('Z')) set_z_fade_height(parser.value_linear_units());
|
||||
#endif
|
||||
|
||||
const bool new_status = leveling_is_active();
|
||||
|
||||
if (to_enable && !new_status) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_M420_FAILED);
|
||||
}
|
||||
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR("Bed Leveling ", new_status ? MSG_ON : MSG_OFF);
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM("Fade Height ");
|
||||
if (planner.z_fade_height > 0.0)
|
||||
SERIAL_ECHOLN(planner.z_fade_height);
|
||||
else
|
||||
SERIAL_ECHOLNPGM(MSG_OFF);
|
||||
#endif
|
||||
}
|
51
Marlin/src/gcode/calibrate/M421-abl.h
Normal file
51
Marlin/src/gcode/calibrate/M421-abl.h
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* M421: Set a single Mesh Bed Leveling Z coordinate
|
||||
*
|
||||
* Usage:
|
||||
* M421 I<xindex> J<yindex> Z<linear>
|
||||
* M421 I<xindex> J<yindex> Q<offset>
|
||||
*/
|
||||
void gcode_M421() {
|
||||
int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1);
|
||||
const bool hasI = ix >= 0,
|
||||
hasJ = iy >= 0,
|
||||
hasZ = parser.seen('Z'),
|
||||
hasQ = !hasZ && parser.seen('Q');
|
||||
|
||||
if (!hasI || !hasJ || !(hasZ || hasQ)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_M421_PARAMETERS);
|
||||
}
|
||||
else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_MESH_XY);
|
||||
}
|
||||
else {
|
||||
z_values[ix][iy] = parser.value_linear_units() + (hasQ ? z_values[ix][iy] : 0);
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
bed_level_virt_interpolate();
|
||||
#endif
|
||||
}
|
||||
}
|
49
Marlin/src/gcode/calibrate/M421-mbl.h
Normal file
49
Marlin/src/gcode/calibrate/M421-mbl.h
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* M421: Set a single Mesh Bed Leveling Z coordinate
|
||||
*
|
||||
* Usage:
|
||||
* M421 X<linear> Y<linear> Z<linear>
|
||||
* M421 X<linear> Y<linear> Q<offset>
|
||||
* M421 I<xindex> J<yindex> Z<linear>
|
||||
* M421 I<xindex> J<yindex> Q<offset>
|
||||
*/
|
||||
void gcode_M421() {
|
||||
const bool hasX = parser.seen('X'), hasI = parser.seen('I');
|
||||
const int8_t ix = hasI ? parser.value_int() : hasX ? mbl.probe_index_x(RAW_X_POSITION(parser.value_linear_units())) : -1;
|
||||
const bool hasY = parser.seen('Y'), hasJ = parser.seen('J');
|
||||
const int8_t iy = hasJ ? parser.value_int() : hasY ? mbl.probe_index_y(RAW_Y_POSITION(parser.value_linear_units())) : -1;
|
||||
const bool hasZ = parser.seen('Z'), hasQ = !hasZ && parser.seen('Q');
|
||||
|
||||
if (int(hasI && hasJ) + int(hasX && hasY) != 1 || !(hasZ || hasQ)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_M421_PARAMETERS);
|
||||
}
|
||||
else if (ix < 0 || iy < 0) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_MESH_XY);
|
||||
}
|
||||
else
|
||||
mbl.set_z(ix, iy, parser.value_linear_units() + (hasQ ? mbl.z_values[ix][iy] : 0));
|
||||
}
|
56
Marlin/src/gcode/calibrate/M421-ubl.h
Normal file
56
Marlin/src/gcode/calibrate/M421-ubl.h
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* M421: Set a single Mesh Bed Leveling Z coordinate
|
||||
*
|
||||
* Usage:
|
||||
* M421 I<xindex> J<yindex> Z<linear>
|
||||
* M421 I<xindex> J<yindex> Q<offset>
|
||||
* M421 C Z<linear>
|
||||
* M421 C Q<offset>
|
||||
*/
|
||||
void gcode_M421() {
|
||||
int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1);
|
||||
const bool hasI = ix >= 0,
|
||||
hasJ = iy >= 0,
|
||||
hasC = parser.seen('C'),
|
||||
hasZ = parser.seen('Z'),
|
||||
hasQ = !hasZ && parser.seen('Q');
|
||||
|
||||
if (hasC) {
|
||||
const mesh_index_pair location = ubl.find_closest_mesh_point_of_type(REAL, current_position[X_AXIS], current_position[Y_AXIS], USE_NOZZLE_AS_REFERENCE, NULL, false);
|
||||
ix = location.x_index;
|
||||
iy = location.y_index;
|
||||
}
|
||||
|
||||
if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_M421_PARAMETERS);
|
||||
}
|
||||
else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_MESH_XY);
|
||||
}
|
||||
else
|
||||
ubl.z_values[ix][iy] = parser.value_linear_units() + (hasQ ? ubl.z_values[ix][iy] : 0);
|
||||
}
|
273
Marlin/src/gcode/calibrate/M48.h
Normal file
273
Marlin/src/gcode/calibrate/M48.h
Normal file
@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* M48: Z probe repeatability measurement function.
|
||||
*
|
||||
* Usage:
|
||||
* M48 <P#> <X#> <Y#> <V#> <E> <L#>
|
||||
* P = Number of sampled points (4-50, default 10)
|
||||
* X = Sample X position
|
||||
* Y = Sample Y position
|
||||
* V = Verbose level (0-4, default=1)
|
||||
* E = Engage Z probe for each reading
|
||||
* L = Number of legs of movement before probe
|
||||
* S = Schizoid (Or Star if you prefer)
|
||||
*
|
||||
* This function assumes the bed has been homed. Specifically, that a G28 command
|
||||
* as been issued prior to invoking the M48 Z probe repeatability measurement function.
|
||||
* Any information generated by a prior G29 Bed leveling command will be lost and need to be
|
||||
* regenerated.
|
||||
*/
|
||||
void gcode_M48() {
|
||||
|
||||
if (axis_unhomed_error()) return;
|
||||
|
||||
const int8_t verbose_level = parser.byteval('V', 1);
|
||||
if (!WITHIN(verbose_level, 0, 4)) {
|
||||
SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-4).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (verbose_level > 0)
|
||||
SERIAL_PROTOCOLLNPGM("M48 Z-Probe Repeatability Test");
|
||||
|
||||
const int8_t n_samples = parser.byteval('P', 10);
|
||||
if (!WITHIN(n_samples, 4, 50)) {
|
||||
SERIAL_PROTOCOLLNPGM("?Sample size not plausible (4-50).");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool stow_probe_after_each = parser.boolval('E');
|
||||
|
||||
float X_current = current_position[X_AXIS],
|
||||
Y_current = current_position[Y_AXIS];
|
||||
|
||||
const float X_probe_location = parser.linearval('X', X_current + X_PROBE_OFFSET_FROM_EXTRUDER),
|
||||
Y_probe_location = parser.linearval('Y', Y_current + Y_PROBE_OFFSET_FROM_EXTRUDER);
|
||||
|
||||
#if DISABLED(DELTA)
|
||||
if (!WITHIN(X_probe_location, LOGICAL_X_POSITION(MIN_PROBE_X), LOGICAL_X_POSITION(MAX_PROBE_X))) {
|
||||
out_of_range_error(PSTR("X"));
|
||||
return;
|
||||
}
|
||||
if (!WITHIN(Y_probe_location, LOGICAL_Y_POSITION(MIN_PROBE_Y), LOGICAL_Y_POSITION(MAX_PROBE_Y))) {
|
||||
out_of_range_error(PSTR("Y"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!position_is_reachable_by_probe_xy(X_probe_location, Y_probe_location)) {
|
||||
SERIAL_PROTOCOLLNPGM("? (X,Y) location outside of probeable radius.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool seen_L = parser.seen('L');
|
||||
uint8_t n_legs = seen_L ? parser.value_byte() : 0;
|
||||
if (n_legs > 15) {
|
||||
SERIAL_PROTOCOLLNPGM("?Number of legs in movement not plausible (0-15).");
|
||||
return;
|
||||
}
|
||||
if (n_legs == 1) n_legs = 2;
|
||||
|
||||
const bool schizoid_flag = parser.boolval('S');
|
||||
if (schizoid_flag && !seen_L) n_legs = 7;
|
||||
|
||||
/**
|
||||
* Now get everything to the specified probe point So we can safely do a
|
||||
* probe to get us close to the bed. If the Z-Axis is far from the bed,
|
||||
* we don't want to use that as a starting point for each probe.
|
||||
*/
|
||||
if (verbose_level > 2)
|
||||
SERIAL_PROTOCOLLNPGM("Positioning the probe...");
|
||||
|
||||
// Disable bed level correction in M48 because we want the raw data when we probe
|
||||
|
||||
#if HAS_LEVELING
|
||||
const bool was_enabled = leveling_is_active();
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
setup_for_endstop_or_probe_move();
|
||||
|
||||
double mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
|
||||
|
||||
// Move to the first point, deploy, and probe
|
||||
const float t = probe_pt(X_probe_location, Y_probe_location, stow_probe_after_each, verbose_level);
|
||||
bool probing_good = !isnan(t);
|
||||
|
||||
if (probing_good) {
|
||||
randomSeed(millis());
|
||||
|
||||
for (uint8_t n = 0; n < n_samples; n++) {
|
||||
if (n_legs) {
|
||||
const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
|
||||
float angle = random(0.0, 360.0);
|
||||
const float radius = random(
|
||||
#if ENABLED(DELTA)
|
||||
0.1250000000 * (DELTA_PROBEABLE_RADIUS),
|
||||
0.3333333333 * (DELTA_PROBEABLE_RADIUS)
|
||||
#else
|
||||
5.0, 0.125 * min(X_BED_SIZE, Y_BED_SIZE)
|
||||
#endif
|
||||
);
|
||||
|
||||
if (verbose_level > 3) {
|
||||
SERIAL_ECHOPAIR("Starting radius: ", radius);
|
||||
SERIAL_ECHOPAIR(" angle: ", angle);
|
||||
SERIAL_ECHOPGM(" Direction: ");
|
||||
if (dir > 0) SERIAL_ECHOPGM("Counter-");
|
||||
SERIAL_ECHOLNPGM("Clockwise");
|
||||
}
|
||||
|
||||
for (uint8_t l = 0; l < n_legs - 1; l++) {
|
||||
double delta_angle;
|
||||
|
||||
if (schizoid_flag)
|
||||
// The points of a 5 point star are 72 degrees apart. We need to
|
||||
// skip a point and go to the next one on the star.
|
||||
delta_angle = dir * 2.0 * 72.0;
|
||||
|
||||
else
|
||||
// If we do this line, we are just trying to move further
|
||||
// around the circle.
|
||||
delta_angle = dir * (float) random(25, 45);
|
||||
|
||||
angle += delta_angle;
|
||||
|
||||
while (angle > 360.0) // We probably do not need to keep the angle between 0 and 2*PI, but the
|
||||
angle -= 360.0; // Arduino documentation says the trig functions should not be given values
|
||||
while (angle < 0.0) // outside of this range. It looks like they behave correctly with
|
||||
angle += 360.0; // numbers outside of the range, but just to be safe we clamp them.
|
||||
|
||||
X_current = X_probe_location - (X_PROBE_OFFSET_FROM_EXTRUDER) + cos(RADIANS(angle)) * radius;
|
||||
Y_current = Y_probe_location - (Y_PROBE_OFFSET_FROM_EXTRUDER) + sin(RADIANS(angle)) * radius;
|
||||
|
||||
#if DISABLED(DELTA)
|
||||
X_current = constrain(X_current, X_MIN_POS, X_MAX_POS);
|
||||
Y_current = constrain(Y_current, Y_MIN_POS, Y_MAX_POS);
|
||||
#else
|
||||
// If we have gone out too far, we can do a simple fix and scale the numbers
|
||||
// back in closer to the origin.
|
||||
while (!position_is_reachable_by_probe_xy(X_current, Y_current)) {
|
||||
X_current *= 0.8;
|
||||
Y_current *= 0.8;
|
||||
if (verbose_level > 3) {
|
||||
SERIAL_ECHOPAIR("Pulling point towards center:", X_current);
|
||||
SERIAL_ECHOLNPAIR(", ", Y_current);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (verbose_level > 3) {
|
||||
SERIAL_PROTOCOLPGM("Going to:");
|
||||
SERIAL_ECHOPAIR(" X", X_current);
|
||||
SERIAL_ECHOPAIR(" Y", Y_current);
|
||||
SERIAL_ECHOLNPAIR(" Z", current_position[Z_AXIS]);
|
||||
}
|
||||
do_blocking_move_to_xy(X_current, Y_current);
|
||||
} // n_legs loop
|
||||
} // n_legs
|
||||
|
||||
// Probe a single point
|
||||
sample_set[n] = probe_pt(X_probe_location, Y_probe_location, stow_probe_after_each, 0);
|
||||
|
||||
// Break the loop if the probe fails
|
||||
probing_good = !isnan(sample_set[n]);
|
||||
if (!probing_good) break;
|
||||
|
||||
/**
|
||||
* Get the current mean for the data points we have so far
|
||||
*/
|
||||
double sum = 0.0;
|
||||
for (uint8_t j = 0; j <= n; j++) sum += sample_set[j];
|
||||
mean = sum / (n + 1);
|
||||
|
||||
NOMORE(min, sample_set[n]);
|
||||
NOLESS(max, sample_set[n]);
|
||||
|
||||
/**
|
||||
* Now, use that mean to calculate the standard deviation for the
|
||||
* data points we have so far
|
||||
*/
|
||||
sum = 0.0;
|
||||
for (uint8_t j = 0; j <= n; j++)
|
||||
sum += sq(sample_set[j] - mean);
|
||||
|
||||
sigma = SQRT(sum / (n + 1));
|
||||
if (verbose_level > 0) {
|
||||
if (verbose_level > 1) {
|
||||
SERIAL_PROTOCOL(n + 1);
|
||||
SERIAL_PROTOCOLPGM(" of ");
|
||||
SERIAL_PROTOCOL((int)n_samples);
|
||||
SERIAL_PROTOCOLPGM(": z: ");
|
||||
SERIAL_PROTOCOL_F(sample_set[n], 3);
|
||||
if (verbose_level > 2) {
|
||||
SERIAL_PROTOCOLPGM(" mean: ");
|
||||
SERIAL_PROTOCOL_F(mean, 4);
|
||||
SERIAL_PROTOCOLPGM(" sigma: ");
|
||||
SERIAL_PROTOCOL_F(sigma, 6);
|
||||
SERIAL_PROTOCOLPGM(" min: ");
|
||||
SERIAL_PROTOCOL_F(min, 3);
|
||||
SERIAL_PROTOCOLPGM(" max: ");
|
||||
SERIAL_PROTOCOL_F(max, 3);
|
||||
SERIAL_PROTOCOLPGM(" range: ");
|
||||
SERIAL_PROTOCOL_F(max-min, 3);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
|
||||
} // n_samples loop
|
||||
}
|
||||
|
||||
STOW_PROBE();
|
||||
|
||||
if (probing_good) {
|
||||
SERIAL_PROTOCOLLNPGM("Finished!");
|
||||
|
||||
if (verbose_level > 0) {
|
||||
SERIAL_PROTOCOLPGM("Mean: ");
|
||||
SERIAL_PROTOCOL_F(mean, 6);
|
||||
SERIAL_PROTOCOLPGM(" Min: ");
|
||||
SERIAL_PROTOCOL_F(min, 3);
|
||||
SERIAL_PROTOCOLPGM(" Max: ");
|
||||
SERIAL_PROTOCOL_F(max, 3);
|
||||
SERIAL_PROTOCOLPGM(" Range: ");
|
||||
SERIAL_PROTOCOL_F(max-min, 3);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
SERIAL_PROTOCOLPGM("Standard Deviation: ");
|
||||
SERIAL_PROTOCOL_F(sigma, 6);
|
||||
SERIAL_EOL();
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
clean_up_after_endstop_or_probe_move();
|
||||
|
||||
// Re-enable bed level correction if it had been on
|
||||
#if HAS_LEVELING
|
||||
set_bed_leveling_enabled(was_enabled);
|
||||
#endif
|
||||
|
||||
report_current_position();
|
||||
}
|
27
Marlin/src/gcode/calibrate/M49.h
Normal file
27
Marlin/src/gcode/calibrate/M49.h
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
void gcode_M49() {
|
||||
ubl.g26_debug_flag ^= true;
|
||||
SERIAL_PROTOCOLPGM("UBL Debug Flag turned ");
|
||||
serialprintPGM(ubl.g26_debug_flag ? PSTR("on.") : PSTR("off."));
|
||||
}
|
93
Marlin/src/gcode/calibrate/M665.h
Normal file
93
Marlin/src/gcode/calibrate/M665.h
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
/**
|
||||
* M665: Set delta configurations
|
||||
*
|
||||
* H = delta height
|
||||
* L = diagonal rod
|
||||
* R = delta radius
|
||||
* S = segments per second
|
||||
* B = delta calibration radius
|
||||
* X = Alpha (Tower 1) angle trim
|
||||
* Y = Beta (Tower 2) angle trim
|
||||
* Z = Rotate A and B by this angle
|
||||
*/
|
||||
void gcode_M665() {
|
||||
if (parser.seen('H')) {
|
||||
home_offset[Z_AXIS] = parser.value_linear_units() - DELTA_HEIGHT;
|
||||
update_software_endstops(Z_AXIS);
|
||||
}
|
||||
if (parser.seen('L')) delta_diagonal_rod = parser.value_linear_units();
|
||||
if (parser.seen('R')) delta_radius = parser.value_linear_units();
|
||||
if (parser.seen('S')) delta_segments_per_second = parser.value_float();
|
||||
if (parser.seen('B')) delta_calibration_radius = parser.value_float();
|
||||
if (parser.seen('X')) delta_tower_angle_trim[A_AXIS] = parser.value_float();
|
||||
if (parser.seen('Y')) delta_tower_angle_trim[B_AXIS] = parser.value_float();
|
||||
if (parser.seen('Z')) { // rotate all 3 axis for Z = 0
|
||||
delta_tower_angle_trim[A_AXIS] -= parser.value_float();
|
||||
delta_tower_angle_trim[B_AXIS] -= parser.value_float();
|
||||
}
|
||||
recalc_delta_settings(delta_radius, delta_diagonal_rod);
|
||||
}
|
||||
|
||||
#elif IS_SCARA
|
||||
|
||||
/**
|
||||
* M665: Set SCARA settings
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* S[segments-per-second] - Segments-per-second
|
||||
* P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle
|
||||
* T[theta-offset] - Theta offset, added to the elbow (B/Y) angle
|
||||
*
|
||||
* A, P, and X are all aliases for the shoulder angle
|
||||
* B, T, and Y are all aliases for the elbow angle
|
||||
*/
|
||||
void gcode_M665() {
|
||||
if (parser.seen('S')) delta_segments_per_second = parser.value_float();
|
||||
|
||||
const bool hasA = parser.seen('A'), hasP = parser.seen('P'), hasX = parser.seen('X');
|
||||
const uint8_t sumAPX = hasA + hasP + hasX;
|
||||
if (sumAPX == 1)
|
||||
home_offset[A_AXIS] = parser.value_float();
|
||||
else if (sumAPX > 1) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM("Only one of A, P, or X is allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool hasB = parser.seen('B'), hasT = parser.seen('T'), hasY = parser.seen('Y');
|
||||
const uint8_t sumBTY = hasB + hasT + hasY;
|
||||
if (sumBTY == 1)
|
||||
home_offset[B_AXIS] = parser.value_float();
|
||||
else if (sumBTY > 1) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM("Only one of B, T, or Y is allowed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
66
Marlin/src/gcode/calibrate/M666.h
Normal file
66
Marlin/src/gcode/calibrate/M666.h
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
/**
|
||||
* M666: Set delta endstop adjustment
|
||||
*/
|
||||
void gcode_M666() {
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_ECHOLNPGM(">>> gcode_M666");
|
||||
}
|
||||
#endif
|
||||
LOOP_XYZ(i) {
|
||||
if (parser.seen(axis_codes[i])) {
|
||||
endstop_adj[i] = parser.value_linear_units();
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_ECHOPAIR("endstop_adj[", axis_codes[i]);
|
||||
SERIAL_ECHOLNPAIR("] = ", endstop_adj[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
SERIAL_ECHOLNPGM("<<< gcode_M666");
|
||||
}
|
||||
#endif
|
||||
// normalize endstops so all are <=0; set the residue to delta height
|
||||
const float z_temp = MAX3(endstop_adj[A_AXIS], endstop_adj[B_AXIS], endstop_adj[C_AXIS]);
|
||||
home_offset[Z_AXIS] -= z_temp;
|
||||
LOOP_XYZ(i) endstop_adj[i] -= z_temp;
|
||||
}
|
||||
|
||||
#elif ENABLED(Z_DUAL_ENDSTOPS) // !DELTA && ENABLED(Z_DUAL_ENDSTOPS)
|
||||
|
||||
/**
|
||||
* M666: For Z Dual Endstop setup, set z axis offset to the z2 axis.
|
||||
*/
|
||||
void gcode_M666() {
|
||||
if (parser.seen('Z')) z_endstop_adj = parser.value_linear_units();
|
||||
SERIAL_ECHOLNPAIR("Z Endstop Adjustment set to (mm):", z_endstop_adj);
|
||||
}
|
||||
|
||||
#endif
|
81
Marlin/src/gcode/calibrate/common.h
Normal file
81
Marlin/src/gcode/calibrate/common.h
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CALIBRATE_COMMON_H
|
||||
#define CALIBRATE_COMMON_H
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
|
||||
/**
|
||||
* A delta can only safely home all axes at the same time
|
||||
* This is like quick_home_xy() but for 3 towers.
|
||||
*/
|
||||
inline bool home_delta() {
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS(">>> home_delta", current_position);
|
||||
#endif
|
||||
// Init the current position of all carriages to 0,0,0
|
||||
ZERO(current_position);
|
||||
sync_plan_position();
|
||||
|
||||
// Move all carriages together linearly until an endstop is hit.
|
||||
current_position[X_AXIS] = current_position[Y_AXIS] = current_position[Z_AXIS] = (DELTA_HEIGHT + home_offset[Z_AXIS] + 10);
|
||||
feedrate_mm_s = homing_feedrate(X_AXIS);
|
||||
line_to_current_position();
|
||||
stepper.synchronize();
|
||||
|
||||
// If an endstop was not hit, then damage can occur if homing is continued.
|
||||
// This can occur if the delta height (DELTA_HEIGHT + home_offset[Z_AXIS]) is
|
||||
// not set correctly.
|
||||
if (!(Endstops::endstop_hit_bits & (_BV(X_MAX) | _BV(Y_MAX) | _BV(Z_MAX)))) {
|
||||
LCD_MESSAGEPGM(MSG_ERR_HOMING_FAILED);
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ERRORLNPGM(MSG_ERR_HOMING_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
endstops.hit_on_purpose(); // clear endstop hit flags
|
||||
|
||||
// At least one carriage has reached the top.
|
||||
// Now re-home each carriage separately.
|
||||
HOMEAXIS(A);
|
||||
HOMEAXIS(B);
|
||||
HOMEAXIS(C);
|
||||
|
||||
// Set all carriages to their home positions
|
||||
// Do this here all at once for Delta, because
|
||||
// XYZ isn't ABC. Applying this per-tower would
|
||||
// give the impression that they are the same.
|
||||
LOOP_XYZ(i) set_axis_is_at_home((AxisEnum)i);
|
||||
|
||||
SYNC_PLAN_POSITION_KINEMATIC();
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("<<< home_delta", current_position);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // DELTA
|
||||
|
||||
#endif // CALIBRATE_COMMON_H
|
Reference in New Issue
Block a user