diff --git a/Marlin/Conditionals_post.h b/Marlin/Conditionals_post.h index c934d74f4..ad6032250 100644 --- a/Marlin/Conditionals_post.h +++ b/Marlin/Conditionals_post.h @@ -946,6 +946,10 @@ #define PLANNER_LEVELING (OLDSCHOOL_ABL || ENABLED(MESH_BED_LEVELING) || UBL_SEGMENTED || ENABLED(SKEW_CORRECTION)) #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)) + #if ENABLED(AUTO_BED_LEVELING_UBL) + #undef LCD_BED_LEVELING + #endif + /** * Heater & Fan Pausing */ diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 7981b2620..34879ddf8 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -2461,7 +2461,21 @@ void clean_up_after_endstop_or_probe_move() { planner.unapply_leveling(current_position); } #else - planner.leveling_active = enable; // just flip the bit, current_position will be wrong until next move. + // UBL equivalents for apply/unapply_leveling + #if ENABLED(SKEW_CORRECTION) + float pos[XYZ] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] }; + planner.skew(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS]); + #else + const float (&pos)[XYZE] = current_position; + #endif + if (planner.leveling_active) { + current_position[Z_AXIS] += ubl.get_z_correction(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS]); + planner.leveling_active = false; + } + else { + planner.leveling_active = true; + current_position[Z_AXIS] -= ubl.get_z_correction(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS]); + } #endif #else // ABL @@ -9327,6 +9341,14 @@ inline void gcode_M221() { planner.flow_percentage[target_extruder] = parser.value_int(); planner.refresh_e_factor(target_extruder); } + else { + SERIAL_ECHO_START(); + SERIAL_CHAR('E'); + SERIAL_CHAR('0' + target_extruder); + SERIAL_ECHOPAIR(" Flow: ", planner.flow_percentage[target_extruder]); + SERIAL_CHAR('%'); + SERIAL_EOL(); + } } /** @@ -9904,6 +9926,7 @@ void quickstop_stepper() { * With AUTO_BED_LEVELING_UBL only: * * L[index] Load UBL mesh from index (0 is default) + * T[map] 0:Human-readable 1:CSV 2:"LCD" 4:Compact */ inline void gcode_M420() { @@ -9942,7 +9965,7 @@ void quickstop_stepper() { // 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 + ubl.display_map(parser.byteval('T')); // 0= SERIAL_ECHOLNPAIR("ubl.mesh_is_valid = ", ubl.mesh_is_valid()); SERIAL_ECHOLNPAIR("ubl.storage_slot = ", ubl.storage_slot); } @@ -10073,6 +10096,7 @@ void quickstop_stepper() { * Usage: * M421 I J Z * M421 I J Q + * M421 I J N * M421 C Z * M421 C Q */ @@ -10081,6 +10105,7 @@ void quickstop_stepper() { const bool hasI = ix >= 0, hasJ = iy >= 0, hasC = parser.seen('C'), + hasN = parser.seen('N'), hasZ = parser.seen('Z'), hasQ = !hasZ && parser.seen('Q'); @@ -10090,7 +10115,7 @@ void quickstop_stepper() { iy = location.y_index; } - if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ)) { + if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN)) { SERIAL_ERROR_START(); SERIAL_ERRORLNPGM(MSG_ERR_M421_PARAMETERS); } @@ -10099,7 +10124,7 @@ void quickstop_stepper() { SERIAL_ERRORLNPGM(MSG_ERR_MESH_XY); } else - ubl.z_values[ix][iy] = parser.value_linear_units() + (hasQ ? ubl.z_values[ix][iy] : 0); + ubl.z_values[ix][iy] = hasN ? NAN : parser.value_linear_units() + (hasQ ? ubl.z_values[ix][iy] : 0); } #endif // AUTO_BED_LEVELING_UBL diff --git a/Marlin/fwretract.cpp b/Marlin/fwretract.cpp index 6700c3fe8..a913c364c 100644 --- a/Marlin/fwretract.cpp +++ b/Marlin/fwretract.cpp @@ -123,6 +123,7 @@ void FWRetract::retract(const bool retracting #endif } SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]); + SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]); SERIAL_ECHOLNPAIR("hop_amount ", hop_amount); //*/ @@ -139,7 +140,7 @@ void FWRetract::retract(const bool retracting feedrate_mm_s = retract_feedrate_mm_s; current_position[E_AXIS] += (swapping ? swap_retract_length : retract_length) * renormalize; sync_plan_position_e(); - prepare_move_to_destination(); + prepare_move_to_destination(); // set_current_to_destination // Is a Z hop set, and has the hop not yet been done? // No double zlifting @@ -149,7 +150,7 @@ void FWRetract::retract(const bool retracting hop_amount += retract_zlift; // Add to the hop total (again, only once) destination[Z_AXIS] += retract_zlift; // Raise Z by the zlift (M207 Z) amount feedrate_mm_s = planner.max_feedrate_mm_s[Z_AXIS]; // Maximum Z feedrate - prepare_move_to_destination(); // Raise up + prepare_move_to_destination(); // Raise up, set_current_to_destination current_position[Z_AXIS] = old_z; // Spoof the Z position in the planner SYNC_PLAN_POSITION_KINEMATIC(); } @@ -160,17 +161,17 @@ void FWRetract::retract(const bool retracting current_position[Z_AXIS] += hop_amount; // Set actual Z (due to the prior hop) SYNC_PLAN_POSITION_KINEMATIC(); // Spoof the Z position in the planner feedrate_mm_s = planner.max_feedrate_mm_s[Z_AXIS]; // Z feedrate to max - prepare_move_to_destination(); // Lower Z and update current_position + prepare_move_to_destination(); // Lower Z, set_current_to_destination hop_amount = 0.0; // Clear the hop amount } // A retract multiplier has been added here to get faster swap recovery feedrate_mm_s = swapping ? swap_retract_recover_feedrate_mm_s : retract_recover_feedrate_mm_s; - const float move_e = swapping ? swap_retract_length + swap_retract_recover_length : retract_length + retract_recover_length; - current_position[E_AXIS] -= move_e * renormalize; + current_position[E_AXIS] -= (swapping ? swap_retract_length + swap_retract_recover_length + : retract_length + retract_recover_length) * renormalize; sync_plan_position_e(); - prepare_move_to_destination(); // Recover E + prepare_move_to_destination(); // Recover E, set_current_to_destination } feedrate_mm_s = old_feedrate_mm_s; // Restore original feedrate @@ -195,6 +196,7 @@ void FWRetract::retract(const bool retracting #endif } SERIAL_ECHOLNPAIR("current_position[z] ", current_position[Z_AXIS]); + SERIAL_ECHOLNPAIR("current_position[e] ", current_position[E_AXIS]); SERIAL_ECHOLNPAIR("hop_amount ", hop_amount); //*/ diff --git a/Marlin/macros.h b/Marlin/macros.h index 75f32e852..b09812ad2 100644 --- a/Marlin/macros.h +++ b/Marlin/macros.h @@ -217,7 +217,7 @@ #define NEAR(x,y) NEAR_ZERO((x)-(y)) #define RECIPROCAL(x) (NEAR_ZERO(x) ? 0.0 : 1.0 / (x)) -#define FIXFLOAT(f) (f + 0.00001) +#define FIXFLOAT(f) (f + (f < 0.0 ? -0.00001 : 0.00001)) // // Maths macros that can be overridden by HAL diff --git a/Marlin/ubl.cpp b/Marlin/ubl.cpp index bc700e20c..df9c212bb 100644 --- a/Marlin/ubl.cpp +++ b/Marlin/ubl.cpp @@ -40,17 +40,15 @@ void unified_bed_leveling::report_current_mesh() { if (!leveling_is_valid()) return; SERIAL_ECHO_START(); - SERIAL_ECHOLNPGM(" G29 I 999"); + SERIAL_ECHOLNPGM(" G29 I999"); for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) if (!isnan(z_values[x][y])) { SERIAL_ECHO_START(); - SERIAL_ECHOPAIR(" M421 I ", x); - SERIAL_ECHOPAIR(" J ", y); - SERIAL_ECHOPGM(" Z "); - SERIAL_ECHO_F(z_values[x][y], 6); - SERIAL_ECHOPAIR(" ; X ", LOGICAL_X_POSITION(mesh_index_to_xpos(x))); - SERIAL_ECHOPAIR(", Y ", LOGICAL_Y_POSITION(mesh_index_to_ypos(y))); + SERIAL_ECHOPAIR(" M421 I", x); + SERIAL_ECHOPAIR(" J", y); + SERIAL_ECHOPGM(" Z"); + SERIAL_ECHO_F(z_values[x][y], 2); SERIAL_EOL(); } } @@ -63,15 +61,6 @@ safe_delay(50); } - static void serial_echo_xy(const int16_t x, const int16_t y) { - SERIAL_CHAR('('); - SERIAL_ECHO(x); - SERIAL_CHAR(','); - SERIAL_ECHO(y); - SERIAL_CHAR(')'); - safe_delay(10); - } - #if ENABLED(UBL_DEVEL_DEBUGGING) static void debug_echo_axis(const AxisEnum axis) { @@ -169,81 +158,112 @@ } } - // display_map() currently produces three different mesh map types - // 0 : suitable for PronterFace and Repetier's serial console - // 1 : .CSV file suitable for importation into various spread sheets - // 2 : disply of the map data on a RepRap Graphical LCD Panel + static void serial_echo_xy(const uint8_t sp, const int16_t x, const int16_t y) { + SERIAL_ECHO_SP(sp); + SERIAL_CHAR('('); + if (x < 100) { SERIAL_CHAR(' '); if (x < 10) SERIAL_CHAR(' '); } + SERIAL_ECHO(x); + SERIAL_CHAR(','); + if (y < 100) { SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); } + SERIAL_ECHO(y); + SERIAL_CHAR(')'); + safe_delay(5); + } + static void serial_echo_column_labels(const uint8_t sp) { + SERIAL_ECHO_SP(7); + for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { + if (i < 10) SERIAL_CHAR(' '); + SERIAL_ECHO(i); + SERIAL_ECHO_SP(sp); + } + safe_delay(10); + } + + /** + * Produce one of these mesh maps: + * 0: Human-readable + * 1: CSV format for spreadsheet import + * 2: TODO: Display on Graphical LCD + * 4: Compact Human-Readable + */ void unified_bed_leveling::display_map(const int map_type) { #if HAS_AUTO_REPORTING || ENABLED(HOST_KEEPALIVE_FEATURE) suspend_auto_report = true; #endif - constexpr uint8_t spaces = 8 * (GRID_MAX_POINTS_X - 2); + constexpr uint8_t eachsp = 1 + 6 + 1, // [-3.567] + twixt = eachsp * (GRID_MAX_POINTS_X) - 9 * 2; // Leading 4sp, Coordinates 9sp each - SERIAL_PROTOCOLPGM("\nBed Topography Report"); - if (map_type == 0) { - SERIAL_PROTOCOLPGM(":\n\n"); - serial_echo_xy(0, GRID_MAX_POINTS_Y - 1); - SERIAL_ECHO_SP(spaces + 3); - serial_echo_xy(GRID_MAX_POINTS_X - 1, GRID_MAX_POINTS_Y - 1); - SERIAL_EOL(); - serial_echo_xy(MESH_MIN_X, MESH_MAX_Y); - SERIAL_ECHO_SP(spaces); - serial_echo_xy(MESH_MAX_X, MESH_MAX_Y); + const bool human = !(map_type & 0x3), csv = map_type == 1, lcd = map_type == 2, comp = map_type & 0x4; + + SERIAL_ECHOPGM("\nBed Topography Report"); + if (human) { + SERIAL_ECHOPGM(":\n\n"); + serial_echo_xy(4, MESH_MIN_X, MESH_MAX_Y); + serial_echo_xy(twixt, MESH_MAX_X, MESH_MAX_Y); SERIAL_EOL(); + serial_echo_column_labels(eachsp - 2); } else { - SERIAL_PROTOCOLPGM(" for "); - serialprintPGM(map_type == 1 ? PSTR("CSV:\n\n") : PSTR("LCD:\n\n")); + SERIAL_ECHOPGM(" for "); + serialprintPGM(csv ? PSTR("CSV:\n") : PSTR("LCD:\n")); } const float current_xi = get_cell_index_x(current_position[X_AXIS] + (MESH_X_DIST) / 2.0), current_yi = get_cell_index_y(current_position[Y_AXIS] + (MESH_Y_DIST) / 2.0); + if (!lcd) SERIAL_EOL(); for (int8_t j = GRID_MAX_POINTS_Y - 1; j >= 0; j--) { + + // Row Label (J index) + if (human) { + if (j < 10) SERIAL_CHAR(' '); + SERIAL_ECHO(j); + SERIAL_ECHOPGM(" |"); + } + + // Row Values (I indexes) for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) { + + // Opening Brace or Space const bool is_current = i == current_xi && j == current_yi; + if (human) SERIAL_CHAR(is_current ? '[' : ' '); - // is the nozzle here? then mark the number - if (map_type == 0) SERIAL_CHAR(is_current ? '[' : ' '); - + // Z Value at current I, J const float f = z_values[i][j]; - if (isnan(f)) { - serialprintPGM(map_type == 0 ? PSTR(" . ") : PSTR("NAN")); + if (lcd) { + // TODO: Display on Graphical LCD } - else if (map_type <= 1) { - // if we don't do this, the columns won't line up nicely - if (map_type == 0 && f >= 0.0) SERIAL_CHAR(' '); - SERIAL_PROTOCOL_F(f, 3); + else if (isnan(f)) + serialprintPGM(human ? PSTR(" . ") : PSTR("NAN")); + else if (human || csv) { + if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Space for positive ('-' for negative) + SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits } idle(); - if (map_type == 1 && i < GRID_MAX_POINTS_X - 1) SERIAL_CHAR(','); + if (csv && i < GRID_MAX_POINTS_X - 1) SERIAL_CHAR('\t'); + + // Closing Brace or Space + if (human) SERIAL_CHAR(is_current ? ']' : ' '); #if TX_BUFFER_SIZE > 0 SERIAL_FLUSHTX(); #endif - safe_delay(15); - if (map_type == 0) { - SERIAL_CHAR(is_current ? ']' : ' '); - SERIAL_CHAR(' '); - } - } - SERIAL_EOL(); - if (j && map_type == 0) { // we want the (0,0) up tight against the block of numbers - SERIAL_CHAR(' '); - SERIAL_EOL(); + safe_delay(5); } + if (!lcd) SERIAL_EOL(); + + // A blank line between rows (unless compact) + if (j && human && !comp) SERIAL_ECHOLNPGM(" |"); } - if (map_type == 0) { - serial_echo_xy(MESH_MIN_X, MESH_MIN_Y); - SERIAL_ECHO_SP(spaces + 4); - serial_echo_xy(MESH_MAX_X, MESH_MIN_Y); + if (human) { + serial_echo_column_labels(eachsp - 2); + SERIAL_EOL(); + serial_echo_xy(4, MESH_MIN_X, MESH_MIN_Y); + serial_echo_xy(twixt, MESH_MAX_X, MESH_MIN_Y); SERIAL_EOL(); - serial_echo_xy(0, 0); - SERIAL_ECHO_SP(spaces + 5); - serial_echo_xy(GRID_MAX_POINTS_X - 1, 0); SERIAL_EOL(); } diff --git a/Marlin/ubl_G29.cpp b/Marlin/ubl_G29.cpp index 44048408b..06d6d4bbe 100644 --- a/Marlin/ubl_G29.cpp +++ b/Marlin/ubl_G29.cpp @@ -247,9 +247,7 @@ * for subsequent Load and Store operations. Valid storage slot numbers begin at 0 and * extend to a limit related to the available EEPROM storage. * - * S -1 Store Store the current Mesh as a print out that is suitable to be feed back into the system - * at a later date. The GCode output can be saved and later replayed by the host software - * to reconstruct the current mesh on another machine. + * S -1 Store Print the current Mesh as G-code that can be used to restore the mesh anytime. * * T Topology Display the Mesh Map Topology. * 'T' can be used alone (e.g., G29 T) or in combination with most of the other commands. diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 2651d6c6f..8b5204194 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -2921,14 +2921,16 @@ void kill_screen(const char* lcd_msg) { const float diff = float((int32_t)encoderPosition) * move_menu_scale; #if IS_KINEMATIC manual_move_offset += diff; - // Limit only when trying to move towards the limit - if ((int32_t)encoderPosition < 0) NOLESS(manual_move_offset, min - current_position[axis]); - if ((int32_t)encoderPosition > 0) NOMORE(manual_move_offset, max - current_position[axis]); + if ((int32_t)encoderPosition < 0) + NOLESS(manual_move_offset, min - current_position[axis]); + else + NOMORE(manual_move_offset, max - current_position[axis]); #else current_position[axis] += diff; - // Limit only when trying to move towards the limit - if ((int32_t)encoderPosition < 0) NOLESS(current_position[axis], min); - if ((int32_t)encoderPosition > 0) NOMORE(current_position[axis], max); + if ((int32_t)encoderPosition < 0) + NOLESS(current_position[axis], min); + else + NOMORE(current_position[axis], max); #endif manual_move_to_current(axis);