diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 40b8ba5f2234..95ac6682a325 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -2233,6 +2233,9 @@ #define BILINEAR_SUBDIVISIONS 3 #endif + // Add 'G29 P' to fill the mesh with a single value + //#define ABL_BILINEAR_G29_P_FILL_MESH + #endif #elif ENABLED(AUTO_BED_LEVELING_UBL) diff --git a/Marlin/src/feature/bedlevel/abl/bbl.cpp b/Marlin/src/feature/bedlevel/abl/bbl.cpp index 14c4bd24bcf0..deb3ad5e6343 100644 --- a/Marlin/src/feature/bedlevel/abl/bbl.cpp +++ b/Marlin/src/feature/bedlevel/abl/bbl.cpp @@ -97,15 +97,19 @@ void LevelingBilinear::extrapolate_one_point(const uint8_t x, const uint8_t y, c #endif #endif -void LevelingBilinear::reset() { - grid_start.reset(); - grid_spacing.reset(); +void LevelingBilinear::fill(const float val/*=NAN*/) { GRID_LOOP(x, y) { - z_values[x][y] = NAN; + z_values[x][y] = val; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0)); } } +void LevelingBilinear::reset() { + grid_start.reset(); + grid_spacing.reset(); + fill(); +} + void LevelingBilinear::set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start) { grid_spacing = _grid_spacing; grid_start = _grid_start; diff --git a/Marlin/src/feature/bedlevel/abl/bbl.h b/Marlin/src/feature/bedlevel/abl/bbl.h index ca2e96593fde..f41e5acdf3f5 100644 --- a/Marlin/src/feature/bedlevel/abl/bbl.h +++ b/Marlin/src/feature/bedlevel/abl/bbl.h @@ -50,6 +50,7 @@ class LevelingBilinear { #endif public: + static void fill(const float val=NAN); static void reset(); static void set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start); static void extrapolate_unprobed_bed_level(); diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index e0cf28156bce..04cc8afe243b 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -200,12 +200,15 @@ class G29_State { * * With AUTO_BED_LEVELING_BILINEAR: * Z Supply additional Z offset to all probe points. - * W Write a mesh point. (If G29 is idle.) - * I Index for mesh point - * J Index for mesh point - * X For mesh point, overrides I - * Y For mesh point, overrides J - * Z For mesh point. If omitted, uses current position's raw Z + * W Write a mesh point. (If G29 is idle.) + * I Index for mesh point + * J Index for mesh point + * X For mesh point, overrides I + * Y For mesh point, overrides J + * Z For mesh point. If omitted, uses current position's raw Z + * + * With ABL_BILINEAR_G29_P_FILL_MESH + * P Populate the mesh with a specified Z value * * With DEBUG_LEVELING_FEATURE: * C Make a totally fake grid with no actual probing. @@ -242,14 +245,89 @@ G29_TYPE GcodeSuite::G29() { if (DISABLED(PROBE_MANUALLY) && seenQ) G29_RETURN(false, false); #endif + // W = Write a mesh point (below) + const bool seenW = TERN0(AUTO_BED_LEVELING_BILINEAR, parser.seen_test('W')); + if (seenW && g29_in_progress) { + SERIAL_WARN_MSG("(W) ignored."); + G29_RETURN(false, false); + } + + // J = Jettison bed leveling data + const bool seenJ = !seenW && parser.seen_test('J'); + if (seenJ) { + if (g29_in_progress) { + SERIAL_WARN_MSG("(J) ignored."); + G29_RETURN(false, false); + } + else + reset_bed_level(); + } + + // P = Populate the mesh with a specified value + #if ENABLED(ABL_BILINEAR_G29_P_FILL_MESH) + if (parser.seenval('P')) { + const float init_val = parser.value_linear_units(); + if (!WITHIN(init_val, -10.0f, 10.0f)) { + SERIAL_WARN_MSG("(P) value out of range (-10-10).\n"); + G29_RETURN(false, false); + } + bedlevel.fill(init_val); + } + #endif + + #if ENABLED(AUTO_BED_LEVELING_BILINEAR) + + if (seenW) { + + const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z; + if (!WITHIN(rz, -10, 10)) { + SERIAL_ERROR_MSG("(W) value out of range (-10-10)."); + G29_RETURN(false, false); + } + + const float rx = RAW_X_POSITION(parser.linearval('X', NAN)), + ry = RAW_Y_POSITION(parser.linearval('Y', NAN)); + int8_t i = parser.byteval('I', -1), j = parser.byteval('J', -1); + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + + if (!isnan(rx) && !isnan(ry)) { + // Get nearest i / j from rx / ry + i = (rx - bedlevel.grid_start.x) / bedlevel.grid_spacing.x + 0.5f; + j = (ry - bedlevel.grid_start.y) / bedlevel.grid_spacing.y + 0.5f; + LIMIT(i, 0, (GRID_MAX_POINTS_X) - 1); + LIMIT(j, 0, (GRID_MAX_POINTS_Y) - 1); + } + + #pragma GCC diagnostic pop + + if (WITHIN(i, 0, (GRID_MAX_POINTS_X) - 1) && WITHIN(j, 0, (GRID_MAX_POINTS_Y) - 1)) { + set_bed_leveling_enabled(false); + bedlevel.z_values[i][j] = rz; + bedlevel.refresh_bed_level(); + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(i, j, rz)); + if (!leveling_is_valid()) SERIAL_WARN_MSG("Bilinear grid is invalid."); + if (abl.reenable) { + set_bed_leveling_enabled(true); + report_current_position(); + } + } + + G29_RETURN(false, false); + + } // seenW + + #endif + // A = Abort manual probing // C = Generate fake probe points (DEBUG_LEVELING_FEATURE) const bool seenA = TERN0(PROBE_MANUALLY, parser.seen_test('A')), no_action = seenA || seenQ, - faux = ENABLED(DEBUG_LEVELING_FEATURE) && DISABLED(PROBE_MANUALLY) ? parser.boolval('C') : no_action; + faux = (ENABLED(DEBUG_LEVELING_FEATURE) && DISABLED(PROBE_MANUALLY) ? parser.boolval('C') : no_action); // O = Don't level if leveling is already active - if (!no_action && planner.leveling_active && parser.boolval('O')) { + if (parser.boolval('O') && !no_action && planner.leveling_active) { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Auto-level not needed, skip"); G29_RETURN(false, false); } @@ -259,7 +337,7 @@ G29_TYPE GcodeSuite::G29() { process_subcommands_now(TERN(CAN_SET_LEVELING_AFTER_G28, F("G28L0"), FPSTR(G28_STR))); // Don't allow auto-leveling without homing first - if (homing_needed_error()) G29_RETURN(false, false); + if (!faux && homing_needed_error()) G29_RETURN(false, false); // 3-point leveling gets points from the probe class #if ENABLED(AUTO_BED_LEVELING_3POINT) @@ -297,63 +375,6 @@ G29_TYPE GcodeSuite::G29() { abl.reenable = planner.leveling_active; - #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - - const bool seen_w = parser.seen_test('W'); - if (seen_w) { - if (!leveling_is_valid()) { - SERIAL_ERROR_MSG("No bilinear grid"); - G29_RETURN(false, false); - } - - const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z; - if (!WITHIN(rz, -10, 10)) { - SERIAL_ERROR_MSG("Bad Z value"); - G29_RETURN(false, false); - } - - const float rx = RAW_X_POSITION(parser.linearval('X', NAN)), - ry = RAW_Y_POSITION(parser.linearval('Y', NAN)); - int8_t i = parser.byteval('I', -1), j = parser.byteval('J', -1); - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - - if (!isnan(rx) && !isnan(ry)) { - // Get nearest i / j from rx / ry - i = (rx - bedlevel.grid_start.x) / bedlevel.grid_spacing.x + 0.5f; - j = (ry - bedlevel.grid_start.y) / bedlevel.grid_spacing.y + 0.5f; - LIMIT(i, 0, (GRID_MAX_POINTS_X) - 1); - LIMIT(j, 0, (GRID_MAX_POINTS_Y) - 1); - } - - #pragma GCC diagnostic pop - - if (WITHIN(i, 0, (GRID_MAX_POINTS_X) - 1) && WITHIN(j, 0, (GRID_MAX_POINTS_Y) - 1)) { - set_bed_leveling_enabled(false); - bedlevel.z_values[i][j] = rz; - bedlevel.refresh_bed_level(); - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(i, j, rz)); - if (abl.reenable) { - set_bed_leveling_enabled(true); - report_current_position(); - } - } - G29_RETURN(false, false); - } // parser.seen_test('W') - - #else - - constexpr bool seen_w = false; - - #endif - - // Jettison bed leveling data - if (!seen_w && parser.seen_test('J')) { - reset_bed_level(); - G29_RETURN(false, false); - } - abl.verbose_level = parser.intval('V'); if (!WITHIN(abl.verbose_level, 0, 4)) { SERIAL_ECHOLNPGM(GCODE_ERR_MSG("(V)erbose level implausible (0-4).")); @@ -714,8 +735,9 @@ G29_TYPE GcodeSuite::G29() { if (PR_INNER_VAR == inStart) { char tmp_1[32]; - // move to the start point of new line + // Move to the start point of new line abl.measured_z = faux ? 0.001f * random(-100, 101) : probe.probe_at_point(abl.probePos, raise_after, abl.verbose_level); + // Go to the end of the row/column ... and back up by one // TODO: Why not just use... PR_INNER_VAR = inStop - inInc for (PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; PR_INNER_VAR += inInc); @@ -817,7 +839,7 @@ G29_TYPE GcodeSuite::G29() { // Retain the last probe position abl.probePos = xy_pos_t(points[i]); - abl.measured_z = faux ? 0.001 * random(-100, 101) : probe.probe_at_point(abl.probePos, raise_after, abl.verbose_level); + abl.measured_z = faux ? 0.001f * random(-100, 101) : probe.probe_at_point(abl.probePos, raise_after, abl.verbose_level); if (isnan(abl.measured_z)) { set_bed_leveling_enabled(abl.reenable); break;