diff --git a/Marlin/ubl.h b/Marlin/ubl.h index 21f83c113..2c2ca1ec3 100644 --- a/Marlin/ubl.h +++ b/Marlin/ubl.h @@ -115,7 +115,7 @@ #endif static float measure_point_with_encoder(); - static float measure_business_card_thickness(float&); + static float measure_business_card_thickness(float); static bool g29_parameter_parsing(); static void find_mean_mesh_height(); static void shift_mesh_height(); diff --git a/Marlin/ubl_G29.cpp b/Marlin/ubl_G29.cpp index 3fe2240d7..c70755a33 100644 --- a/Marlin/ubl_G29.cpp +++ b/Marlin/ubl_G29.cpp @@ -84,25 +84,23 @@ * * A Activate Activate the Unified Bed Leveling system. * - * B # Business Use the 'Business Card' mode of the Manual Probe subsystem. This is invoked as - * G29 P2 B. The mode of G29 P2 allows you to use a business card or recipe card - * as a shim that the nozzle will pinch as it is lowered. The idea is that you - * can easily feel the nozzle getting to the same height by the amount of resistance - * the business card exhibits to movement. You should try to achieve the same amount - * of resistance on each probed point to facilitate accurate and repeatable measurements. - * You should be very careful not to drive the nozzle into the business card with a - * lot of force as it is very possible to cause damage to your printer if your are - * careless. If you use the B option with G29 P2 B you can omit the numeric value - * on first use to measure the business card's thickness. Subsequent usage of 'B' - * will apply the previously-measured thickness as the default. - * Note: A non-compressible Spark Gap feeler gauge is recommended over a Business Card. + * B # Business Use the 'Business Card' mode of the Manual Probe subsystem with P2. + * Note: A non-compressible Spark Gap feeler gauge is recommended over a business card. + * In this mode of G29 P2, a business or index card is used as a shim that the nozzle can + * grab onto as it is lowered. In principle, the nozzle-bed distance is the same when the + * same resistance is felt in the shim. You can omit the numerical value on first invocation + * of G29 P2 B to measure shim thickness. Subsequent use of 'B' will apply the previously- + * measured thickness by default. * - * C Continue Continue, Constant, Current Location. This is not a primary command. C is used to - * further refine the behaviour of several other commands. Issuing a G29 P1 C will - * continue the generation of a partially constructed Mesh without invalidating what has - * been done. Issuing a G29 P2 C will tell the Manual Probe subsystem to use the current - * location in its search for the closest unmeasured Mesh Point. When used with a G29 Z C - * it indicates to use the current location instead of defaulting to the center of the print bed. + * C Continue G29 P1 C continues the generation of a partially-constructed Mesh without invalidating + * previous measurements. + * + * C Constant G29 P2 C specifies a Constant and tells the Manual Probe subsystem to use the current + * location in its search for the closest unmeasured Mesh Point. + * + * G29 P3 C specifies the Constant for the fill. Otherwise, uses a "reasonable" value. + * + * C Current G29 Z C uses the Current location (instead of bed center or nearest edge). * * D Disable Disable the Unified Bed Leveling system. * @@ -112,17 +110,18 @@ * specified height, no correction is applied and natural printer kenimatics take over. If no * number is specified for the command, 10mm is assumed to be reasonable. * - * H # Height Specify the Height to raise the nozzle after each manual probe of the bed. The - * default is 5mm. + * H # Height With P2, 'H' specifies the Height to raise the nozzle after each manual probe of the bed. + * If omitted, the nozzle will raise by Z_CLEARANCE_BETWEEN_PROBES. * - * I # Invalidate Invalidate specified number of Mesh Points. The nozzle location is used unless - * the X and Y parameter are used. If no number is specified, only the closest Mesh - * point to the location is invalidated. The 'T' parameter is also available to produce - * a map after the operation. This command is useful to invalidate a portion of the - * Mesh so it can be adjusted using other tools in the Unified Bed Leveling System. When - * attempting to invalidate an isolated bad point in the mesh, the 'T' option will indicate - * where the nozzle is positioned in the Mesh with (#). You can move the nozzle around on - * the bed and use this feature to select the center of the area (or cell) you want to + * H # Offset With P4, 'H' specifies the Offset above the mesh height to place the nozzle. + * If omitted, Z_CLEARANCE_BETWEEN_PROBES will be used. + * + * I # Invalidate Invalidate the specified number of Mesh Points near the given 'X' 'Y'. If X or Y are omitted, + * the nozzle location is used. If no 'I' value is given, only the point nearest to the location + * is invalidated. Use 'T' to produce a map afterward. This command is useful to invalidate a + * portion of the Mesh so it can be adjusted using other UBL tools. When attempting to invalidate + * an isolated bad mesh point, the 'T' option shows the nozzle position in the Mesh with (#). You + * can move the nozzle around and use this feature to select the center of the area (or cell) to * invalidate. * * J # Grid Perform a Grid Based Leveling of the current Mesh using a grid with n points on a side. @@ -151,95 +150,81 @@ * area cannot be automatically probed. For Delta printers the area in which DELTA_PROBEABLE_RADIUS * and DELTA_PRINTABLE_RADIUS do not overlap will not be automatically probed. * - * These points will be handled in Phase 2 and Phase 3. If the Phase 1 command is given the - * C (Continue) parameter it does not invalidate the Mesh prior to automatically - * probing needed locations. This allows you to invalidate portions of the Mesh but still - * use the automatic probing capabilities of the Unified Bed Leveling System. An X and Y - * parameter can be given to prioritize where the command should be trying to measure points. - * If the X and Y parameters are not specified the current probe position is used. - * P1 accepts a 'T' (Topology) parameter so you can observe mesh generation. - * P1 also watches for the LCD Panel Encoder Switch to be held down (assuming you have one), - * and will suspend generation of the Mesh in that case. (Note: This check is only done - * between probe points, so you must press and hold the switch until the Phase 1 command - * detects it.) + * Unreachable points will be handled in Phase 2 and Phase 3. + * + * Use 'C' to leave the previous mesh intact and automatically probe needed points. This allows you + * to invalidate parts of the Mesh but still use Automatic Probing. + * + * The 'X' and 'Y' parameters prioritize where to try and measure points. If omitted, the current + * probe position is used. + * + * Use 'T' (Topology) to generate a report of mesh generation. + * + * P1 will suspend Mesh generation if the controller button is held down. Note that you may need + * to press and hold the switch for several seconds if moves are underway. + * + * P2 Phase 2 Probe unreachable points. * - * P2 Phase 2 Probe areas of the Mesh that can't be automatically handled. Phase 2 respects an H - * parameter to control the height between Mesh points. The default height for movement - * between Mesh points is 5mm. A smaller number can be used to make this part of the - * calibration less time consuming. You will be running the nozzle down until it just barely - * touches the glass. You should have the nozzle clean with no plastic obstructing your view. - * Use caution and move slowly. It is possible to damage your printer if you are careless. - * Note that this command will use the configuration #define SIZE_OF_LITTLE_RAISE if the - * nozzle is moving a distance of less than BIG_RAISE_NOT_NEEDED. + * Use 'H' to set the height between Mesh points. If omitted, Z_CLEARANCE_BETWEEN_PROBES is used. + * Smaller values will be quicker. Move the nozzle down till it barely touches the bed. Make sure the + * nozzle is clean and unobstructed. Use caution and move slowly. This can damage your printer! + * (Uses SIZE_OF_LITTLE_RAISE mm if the nozzle is moving less than BIG_RAISE_NOT_NEEDED mm.) * - * The H parameter can be set negative if your Mesh dips in a large area. You can press - * and hold the LCD Panel's encoder wheel to terminate the current Phase 2 command. You - * can then re-issue the G29 P 2 command with an H parameter that is more suitable for the - * area you are manually probing. Note that the command tries to start you in a corner - * of the bed where movement will be predictable. You can force the location to be used in - * the distance calculations by using the X and Y parameters. You may find it is helpful to - * print out a Mesh Map (G29 T) to understand where the mesh is invalidated and where - * the nozzle will need to move in order to complete the command. The C parameter is - * available on the Phase 2 command also and indicates the search for points to measure should - * be done based on the current location of the nozzle. + * The 'H' value can be negative if the Mesh dips in a large area. Press and hold the + * controller button to terminate the current Phase 2 command. You can then re-issue "G29 P 2" + * with an 'H' parameter more suitable for the area you're manually probing. Note that the command + * tries to start in a corner of the bed where movement will be predictable. Override the distance + * calculation location with the X and Y parameters. You can print a Mesh Map (G29 T) to see where + * the mesh is invalidated and where the nozzle needs to move to complete the command. Use 'C' to + * indicate that the search should be based on the current position. * - * A B parameter is also available for this command and described up above. It places the - * manual probe subsystem into Business Card mode where the thickness of a business card is - * measured and then used to accurately set the nozzle height in all manual probing for the - * duration of the command. (S for Shim mode would be a better parameter name, but S is needed - * for Save or Store of the Mesh to EEPROM) A Business card can be used, but you will have - * better results if you use a flexible Shim that does not compress very much. That makes it - * easier for you to get the nozzle to press with similar amounts of force against the shim so you - * can get accurate measurements. As you are starting to touch the nozzle against the shim try - * to get it to grasp the shim with the same force as when you measured the thickness of the - * shim at the start of the command. + * The 'B' parameter for this command is described above. It places the manual probe subsystem into + * Business Card mode where the thickness of a business card is measured and then used to accurately + * set the nozzle height in all manual probing for the duration of the command. A Business card can + * be used, but you'll get better results with a flexible Shim that doesn't compress. This makes it + * easier to produce similar amounts of force and get more accurate measurements. Google if you're + * not sure how to use a shim. * - * Phase 2 allows the T (Map) parameter to be specified. This helps the user see the progression - * of the Mesh being built. + * The 'T' (Map) parameter helps track Mesh building progress. * - * NOTE: P2 is not available unless you have LCD support enabled! + * NOTE: P2 requires an LCD controller! * - * P3 Phase 3 Fill the unpopulated regions of the Mesh with a fixed value. There are two different paths the - * user can go down. If the user specifies the value using the C parameter, the closest invalid - * mesh points to the nozzle will be filled. The user can specify a repeat count using the R - * parameter with the C version of the command. + * P3 Phase 3 Fill the unpopulated regions of the Mesh with a fixed value. There are two different paths to + * go down: * - * A second version of the fill command is available if no C constant is specified. Not - * specifying a C constant will invoke the 'Smart Fill' algorithm. The G29 P3 command will search - * from the edges of the mesh inward looking for invalid mesh points. It will look at the next - * several mesh points to determine if the print bed is sloped up or down. If the bed is sloped - * upward from the invalid mesh point, it will be replaced with the value of the nearest mesh point. - * If the bed is sloped downward from the invalid mesh point, it will be replaced with a value that - * puts all three points in a line. The second version of the G29 P3 command is a quick, easy and - * usually safe way to populate the unprobed regions of your mesh so you can continue to the G26 - * Mesh Validation Pattern phase. Please note that you are populating your mesh with unverified - * numbers. You should use some scrutiny and caution. + * - If a 'C' constant is specified, the closest invalid mesh points to the nozzle will be filled, + * and a repeat count can then also be specified with 'R'. * - * P4 Phase 4 Fine tune the Mesh. The Delta Mesh Compensation System assume the existence of - * an LCD Panel. It is possible to fine tune the mesh without the use of an LCD Panel using - * G42 and M421; see the UBL documentation for further details. + * - Leaving out 'C' invokes Smart Fill, which scans the mesh from the edges inward looking for + * invalid mesh points. Adjacent points are used to determine the bed slope. If the bed is sloped + * upward from the invalid point, it takes the value of the nearest point. If sloped downward, it's + * replaced by a value that puts all three points in a line. This version of G29 P3 is a quick, easy + * and (usually) safe way to populate unprobed mesh regions before continuing to G26 Mesh Validation + * Pattern. Note that this populates the mesh with unverified values. Pay attention and use caution. * - * The System will search for the closest Mesh Point to the nozzle. It will move the - * nozzle to this location. The user can use the LCD Panel to carefully adjust the nozzle - * so it is just barely touching the bed. When the user clicks the control, the System - * will lock in that height for that point in the Mesh Compensation System. + * P4 Phase 4 Fine tune the Mesh. The Delta Mesh Compensation System assumes the existence of + * an LCD Panel. It is possible to fine tune the mesh without an LCD Panel using + * G42 and M421. See the UBL documentation for further details. * - * Phase 4 has several additional parameters that the user may find helpful. Phase 4 - * can be started at a specific location by specifying an X and Y parameter. Phase 4 - * can be requested to continue the adjustment of Mesh Points by using the R(epeat) - * parameter. If the Repetition count is not specified, it is assumed the user wishes - * to adjust the entire matrix. The nozzle is moved to the Mesh Point being edited. - * The command can be terminated early (or after the area of interest has been edited) by - * pressing and holding the encoder wheel until the system recognizes the exit request. - * Phase 4's general form is G29 P4 [R # of points] [X position] [Y position] + * Phase 4 is meant to be used with G26 Mesh Validation to fine tune the mesh by direct editing + * of Mesh Points. Raise and lower points to fine tune the mesh until it gives consistently reliable + * adhesion. * - * Phase 4 is intended to be used with the G26 Mesh Validation Command. Using the - * information left on the printer's bed from the G26 command it is very straight forward - * and easy to fine tune the Mesh. One concept that is important to remember and that - * will make using the Phase 4 command easy to use is this: You are editing the Mesh Points. - * If you have too little clearance and not much plastic was extruded in an area, you want to - * LOWER the Mesh Point at the location. If you did not get good adheasion, you want to - * RAISE the Mesh Point at that location. + * P4 moves to the closest Mesh Point (and/or the given X Y), raises the nozzle above the mesh height + * by the given 'H' offset (or default Z_CLEARANCE_BETWEEN_PROBES), and waits while the controller is + * used to adjust the nozzle height. On click the displayed height is saved in the mesh. + * + * Start Phase 4 at a specific location with X and Y. Adjust a specific number of Mesh Points with + * the 'R' (Repeat) parameter. (If 'R' is left out, the whole matrix is assumed.) This command can be + * terminated early (e.g., after editing the area of interest) by pressing and holding the encoder button. + * + * The general form is G29 P4 [R points] [X position] [Y position] + * + * The H [offset] parameter is useful if a shim is used to fine-tune the mesh. For a 0.4mm shim the + * command would be G29 P4 H0.4. The nozzle is moved to the shim height, you adjust height to the shim, + * and on click the height minus the shim thickness will be saved in the mesh. + * + * !!Use with caution, as a very poor mesh could cause the nozzle to crash into the bed!! * * NOTE: P4 is not available unless you have LCD support enabled! * @@ -494,28 +479,29 @@ g29_y_pos = current_position[Y_AXIS]; } - float height = Z_CLEARANCE_BETWEEN_PROBES; - if (parser.seen('B')) { - g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness(height); + g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness(Z_CLEARANCE_BETWEEN_PROBES); if (FABS(g29_card_thickness) > 1.5) { SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement."); return; } } - if (parser.seen('H') && parser.has_value()) height = parser.value_float(); - if (!position_is_reachable_xy(g29_x_pos, g29_y_pos)) { SERIAL_PROTOCOLLNPGM("XY outside printable radius."); return; } + const float height = parser.seen('H') && parser.has_value() ? parser.value_float() : Z_CLEARANCE_BETWEEN_PROBES; manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T')); + SERIAL_PROTOCOLLNPGM("G29 P2 finished."); + #else + SERIAL_PROTOCOLLNPGM("?P2 is only available when an LCD is present."); return; + #endif } break; @@ -537,19 +523,17 @@ if (location.x_index < 0) { // No more REACHABLE INVALID mesh points to populate, so we ASSUME // user meant to populate ALL INVALID mesh points to value - 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])) { + 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])) z_values[x][y] = g29_constant; - } - } - } break; // No more invalid Mesh Points to populate } z_values[location.x_index][location.y_index] = g29_constant; } } - } else { + } + else { const float cvf = parser.value_float(); switch((int)truncf(cvf * 10.0) - 30) { // 3.1 -> 1 #if ENABLED(UBL_G29_P31) @@ -967,7 +951,7 @@ static void echo_and_take_a_measurement() { SERIAL_PROTOCOLLNPGM(" and take a measurement."); } - float unified_bed_leveling::measure_business_card_thickness(float &in_height) { + float unified_bed_leveling::measure_business_card_thickness(float in_height) { has_control_of_lcd_panel = true; save_ubl_active_state_and_disable(); // Disable bed level correction for probing @@ -1466,12 +1450,21 @@ } #if ENABLED(NEWPANEL) + void unified_bed_leveling::fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) { if (!parser.seen('R')) // fine_tune_mesh() is special. If no repetition count flag is specified g29_repetition_cnt = 1; // do exactly one mesh location. Otherwise use what the parser decided. - + + #if ENABLED(UBL_MESH_EDIT_MOVES_Z) + const bool is_offset = parser.seen('H'); + const float h_offset = is_offset ? parser.value_linear_units() : Z_CLEARANCE_BETWEEN_PROBES; + if (is_offset && !WITHIN(h_offset, 0, 10)) { + SERIAL_PROTOCOLLNPGM("Offset out of bounds. (0 to 10mm)\n"); + return; + } + #endif + mesh_index_pair location; - uint16_t not_done[16]; if (!position_is_reachable_xy(lx, ly)) { SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius."); @@ -1480,12 +1473,13 @@ save_ubl_active_state_and_disable(); - memset(not_done, 0xFF, sizeof(not_done)); - LCD_MESSAGEPGM(MSG_UBL_FINE_TUNE_MESH); do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); do_blocking_move_to_xy(lx, ly); + + uint16_t not_done[16]; + memset(not_done, 0xFF, sizeof(not_done)); do { location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false); @@ -1521,8 +1515,8 @@ do { new_z = lcd_mesh_edit(); - #ifdef UBL_MESH_EDIT_MOVES_Z - do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES + new_z); // Move the nozzle as the point is edited + #if ENABLED(UBL_MESH_EDIT_MOVES_Z) + do_blocking_move_to_z(h_offset + new_z); // Move the nozzle as the point is edited #endif idle(); } while (!ubl_lcd_clicked()); @@ -1581,7 +1575,8 @@ } else lcd_return_to_status(); } - #endif + + #endif // NEWPANEL /** * 'Smart Fill': Scan from the outward edges of the mesh towards the center. diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index e878e8f20..67015c322 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -4026,44 +4026,34 @@ void kill_screen(const char* lcd_msg) { */ #if ENABLED(ADC_KEYPAD) - inline void handle_adc_keypad() { + inline bool handle_adc_keypad() { static uint8_t adc_steps = 0; if (buttons_reprapworld_keypad) { - adc_steps++; - NOMORE(adc_steps, 20); - + if (adc_steps < 20) ++adc_steps; lcd_quick_feedback(); lcdDrawUpdate = LCDVIEW_REDRAW_NOW; - return_to_status_ms = millis() + LCD_TIMEOUT_TO_STATUS; if (encoderDirection == -1) { // side effect which signals we are inside a menu - if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_DOWN) - encoderPosition -= ENCODER_STEPS_PER_MENU_ITEM; - else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_UP) - encoderPosition += ENCODER_STEPS_PER_MENU_ITEM; - else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_LEFT) - menu_action_back(); - else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_RIGHT) - // enqueue_and_echo_commands_P(PSTR("M0 Pause")); - lcd_return_to_status(); + if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_DOWN) encoderPosition -= ENCODER_STEPS_PER_MENU_ITEM; + else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_UP) encoderPosition += ENCODER_STEPS_PER_MENU_ITEM; + else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_LEFT) menu_action_back(); + else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_RIGHT) lcd_return_to_status(); } else { const int8_t step = adc_steps > 19 ? 100 : adc_steps > 10 ? 10 : 1; - if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_DOWN) - encoderPosition += ENCODER_PULSES_PER_STEP * step; - else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_UP) - encoderPosition -= ENCODER_PULSES_PER_STEP * step; - else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_RIGHT) - encoderPosition = 0; + if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_DOWN) encoderPosition += ENCODER_PULSES_PER_STEP * step; + else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_UP) encoderPosition -= ENCODER_PULSES_PER_STEP * step; + else if (buttons_reprapworld_keypad & EN_REPRAPWORLD_KEYPAD_RIGHT) encoderPosition = 0; } #if ENABLED(ADC_KEYPAD_DEBUG) SERIAL_PROTOCOLLNPAIR("buttons_reprapworld_keypad = ", (uint32_t)buttons_reprapworld_keypad); SERIAL_PROTOCOLLNPAIR("encoderPosition = ", (uint32_t)encoderPosition); #endif + return true; } - else if (!thermalManager.current_ADCKey_raw) { - // reset stepping acceleration - adc_steps = 0; - } + else if (!thermalManager.current_ADCKey_raw) + adc_steps = 0; // reset stepping acceleration + + return false; } #elif ENABLED(REPRAPWORLD_KEYPAD) @@ -4360,7 +4350,8 @@ void lcd_update() { #if ENABLED(ADC_KEYPAD) - handle_adc_keypad(); + if (handle_adc_keypad()) + return_to_status_ms = ms + LCD_TIMEOUT_TO_STATUS; #elif ENABLED(REPRAPWORLD_KEYPAD) @@ -4792,7 +4783,7 @@ void lcd_reset_alert_level() { lcd_status_message_level = 0; } uint8_t ADCKeyNo; } _stADCKeypadTable_; - static const _stADCKeypadTable_ stADCKeyTable[] = PROGMEM { + static const _stADCKeypadTable_ stADCKeyTable[] PROGMEM = { // VALUE_MIN, VALUE_MAX, KEY { 4000, 4096, BLEN_REPRAPWORLD_KEYPAD_F1 + 1 }, // F1 { 4000, 4096, BLEN_REPRAPWORLD_KEYPAD_F2 + 1 }, // F2