Browse Source

Fixed a non-C++ implementation of the PRUSA specific G-Codes.
Yet another improvements of the bed skew calibration.

bubnikv 8 years ago
parent
commit
a88c0a30e7

+ 9 - 62
Firmware/Marlin_main.cpp

@@ -1223,6 +1223,7 @@ void get_command()
 
 // Return True if a character was found
 static inline bool    code_seen(char code) { return (strchr_pointer = strchr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
+static inline bool    code_seen(const char *code) { return (strchr_pointer = strstr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
 static inline float   code_value()         { return strtod(strchr_pointer+1, NULL); }
 static inline long    code_value_long()    { return strtol(strchr_pointer+1, NULL, 10); }
 static inline int16_t code_value_short()   { return int16_t(strtol(strchr_pointer+1, NULL, 10)); };
@@ -1590,19 +1591,18 @@ void process_commands()
 
   // PRUSA GCODES
 
-/*
-  if(code_seen('PRUSA')){
-    if(code_seen('Fir')){
+  if(code_seen("PRUSA")){
+    if(code_seen("Fir")){
 
       SERIAL_PROTOCOLLN(FW_version);
 
-    } else if(code_seen('Rev')){
+    } else if(code_seen("Rev")){
 
       SERIAL_PROTOCOLLN(FILAMENT_SIZE "-" ELECTRONICS "-" NOZZLE_TYPE );
 
-    } else if(code_seen('Lang')) {
+    } else if(code_seen("Lang")) {
       lcd_force_language_selection();
-    } else if(code_seen('Lz')) {
+    } else if(code_seen("Lz")) {
       EEPROM_save_B(EEPROM_BABYSTEP_Z,0);
     } 
     //else if (code_seen('Cal')) {
@@ -1611,7 +1611,6 @@ void process_commands()
 
   }
   else 
-*/
   if(code_seen('G'))
   {
     switch((int)code_value())
@@ -2798,6 +2797,7 @@ void process_commands()
                 verbosity_level = (c == ' ' || c == '\t' || c == 0) ? 1 : code_value_short();
             }
             BedSkewOffsetDetectionResultType result = find_bed_offset_and_skew(verbosity_level);
+            uint8_t point_too_far_mask = 0;
             clean_up_after_endstop_move();
             // Print head up.
             current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
@@ -2811,27 +2811,14 @@ void process_commands()
                 // Home in the XY plane.
                 setup_for_endstop_move();
                 home_xy();
-                result = improve_bed_offset_and_skew(1, verbosity_level);
+                result = improve_bed_offset_and_skew(1, verbosity_level, point_too_far_mask);
                 clean_up_after_endstop_move();
                 // Print head up.
                 current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
                 plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS],current_position[Z_AXIS] , current_position[E_AXIS], homing_feedrate[Z_AXIS]/40, active_extruder);
                 st_synchronize();
             }
-            if (result >= BED_SKEW_OFFSET_DETECTION_FAILED) {
-                lcd_bed_calibration_show_result(result);
-            } else {
-                lcd_bed_calibration_show_result(BED_SKEW_OFFSET_DETECTION_FAILED);
-                lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType(- int8_t(result)));
-            }
-            /*
-            if (result != BED_SKEW_OFFSET_DETECTION_FAILED) {
-                // Mesh bed leveling.
-                // Push the commands to the front of the message queue in the reverse order!
-                // There shall be always enough space reserved for these commands.
-                enquecommand_front_P((PSTR("G80")));
-            }
-            */
+            lcd_bed_calibration_show_result(result, point_too_far_mask);
             lcd_update_enable(true);
             lcd_implementation_clear();
             // lcd_return_to_status();
@@ -2840,46 +2827,6 @@ void process_commands()
         break;
     }
 
-    case 46: // M46: bed skew and offset with manual Z up
-    {
-        // Disable the default update procedure of the display. We will do a modal dialog.
-        lcd_update_enable(false);
-        // Let the planner use the uncorrected coordinates.
-        mbl.reset();
-        world2machine_reset();
-        // Move the print head close to the bed.
-        current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-        plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS],current_position[Z_AXIS] , current_position[E_AXIS], homing_feedrate[Z_AXIS]/40, active_extruder);
-        st_synchronize();
-        // Home in the XY plane.
-        set_destination_to_current();
-        setup_for_endstop_move();
-        home_xy();
-        int8_t verbosity_level = 0;
-        if (code_seen('V')) {
-            // Just 'V' without a number counts as V1.
-            char c = strchr_pointer[1];
-            verbosity_level = (c == ' ' || c == '\t' || c == 0) ? 1 : code_value_short();
-        }
-        bool success = improve_bed_offset_and_skew(1, verbosity_level);
-        clean_up_after_endstop_move();
-        // Print head up.
-        current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-        plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS],current_position[Z_AXIS] , current_position[E_AXIS], homing_feedrate[Z_AXIS]/40, active_extruder);
-        st_synchronize();
-        if (success) {
-            // Mesh bed leveling.
-            // Push the commands to the front of the message queue in the reverse order!
-            // There shall be always enough space reserved for these commands.
-            enquecommand_front_P((PSTR("G80")));
-        }
-        lcd_update_enable(true);
-        lcd_implementation_clear();
-        // lcd_return_to_status();
-        lcd_update();
-        break;
-    }
-
 #if 1
     case 48: // M48: scan the bed induction sensor points, print the sensor trigger coordinates to the serial line for visualization on the PC.
     {

+ 103 - 51
Firmware/language_all.cpp

@@ -278,60 +278,60 @@ const char * const MSG_BED_LEVELING_FAILED_POINT_LOW_LANG_TABLE[LANG_NUM] PROGME
 	MSG_BED_LEVELING_FAILED_POINT_LOW_PL
 };
 
-const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_EN[] PROGMEM = "X/Y calibration failed. Please consult manual.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_CZ[] PROGMEM = "Kalibrace X/Y selhala. Nahlednete do manualu.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_IT[] PROGMEM = "X/Y calibration failed. Please consult manual.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_ES[] PROGMEM = "X/Y calibration failed. Please consult manual.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_PL[] PROGMEM = "X/Y calibration failed. Please consult manual.";
-const char * const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_LANG_TABLE[LANG_NUM] PROGMEM = {
-	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_EN,
-	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_CZ,
-	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_IT,
-	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_ES,
-	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_PL
-};
-
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_EN[] PROGMEM = "X/Y calibration bad. Front calibration points not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_CZ[] PROGMEM = "Kalibrace selhala. Predni kalibracni body moc vpredu. Srovnejte tiskarnu.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_IT[] PROGMEM = "X/Y calibration bad. Front calibration points not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_ES[] PROGMEM = "X/Y calibration bad. Front calibration points not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_PL[] PROGMEM = "X/Y calibration bad. Front calibration points not reachable. Fix the printer.";
-const char * const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_EN,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_CZ,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_IT,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_ES,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_PL
-};
-
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_EN[] PROGMEM = "X/Y calibration bad. Left front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_CZ[] PROGMEM = "Kalibrace selhala. Levy predni bod moc vpredu. Srovnejte tiskarnu.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_IT[] PROGMEM = "X/Y calibration bad. Left front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_ES[] PROGMEM = "X/Y calibration bad. Left front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_PL[] PROGMEM = "X/Y calibration bad. Left front calibration point not reachable. Fix the printer.";
-const char * const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_EN,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_CZ,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_IT,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_ES,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_PL
-};
-
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_EN[] PROGMEM = "X/Y calibration bad. Right front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_CZ[] PROGMEM = "Kalibrace selhala. Pravy predni bod moc vpredu. Srovnejte tiskarnu.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_IT[] PROGMEM = "X/Y calibration bad. Right front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_ES[] PROGMEM = "X/Y calibration bad. Right front calibration point not reachable. Fix the printer.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_PL[] PROGMEM = "X/Y calibration bad. Right front calibration point not reachable. Fix the printer.";
-const char * const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_EN,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_CZ,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_IT,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_ES,
-	MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_PL
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_EN[] PROGMEM = "X/Y calibration failed. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_CZ[] PROGMEM = "Kalibrace X/Y selhala. Predni kalibracni body moc vpredu. Srovnejte tiskarnu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_IT[] PROGMEM = "X/Y calibration failed. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_ES[] PROGMEM = "X/Y calibration failed. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_PL[] PROGMEM = "X/Y calibration failed. Front calibration points not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_PL
+};
+
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_EN[] PROGMEM = "X/Y calibration failed. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_CZ[] PROGMEM = "Kalibrace X/Y selhala. Levy predni bod moc vpredu. Srovnejte tiskarnu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_IT[] PROGMEM = "X/Y calibration failed. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_ES[] PROGMEM = "X/Y calibration failed. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_PL[] PROGMEM = "X/Y calibration failed. Left front calibration point not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_PL
+};
+
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_EN[] PROGMEM = "X/Y calibration failed. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_CZ[] PROGMEM = "Kalibrace X/Y selhala. Pravy predni bod moc vpredu. Srovnejte tiskarnu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_IT[] PROGMEM = "X/Y calibration failed. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_ES[] PROGMEM = "X/Y calibration failed. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_PL[] PROGMEM = "X/Y calibration failed. Right front calibration point not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_PL
+};
+
+const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_EN[] PROGMEM = "X/Y calibration failed. Please consult the manual.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_CZ[] PROGMEM = "Kalibrace X/Y selhala. Nahlednete do manualu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_IT[] PROGMEM = "X/Y calibration failed. Please consult the manual.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_ES[] PROGMEM = "X/Y calibration failed. Please consult the manual.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_PL[] PROGMEM = "X/Y calibration failed. Please consult the manual.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_PL
 };
 
 const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_EN[] PROGMEM = "X/Y calibration ok. X/Y axes are perpendicular.";
-const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_CZ[] PROGMEM = "Kalibrace X/Y perfektni. X/Y osy jsou kolme.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_CZ[] PROGMEM = "X/Y calibration ok. X/Y axes are perpendicular.";
 const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_IT[] PROGMEM = "X/Y calibration ok. X/Y axes are perpendicular.";
 const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_ES[] PROGMEM = "X/Y calibration ok. X/Y axes are perpendicular.";
 const char MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_PL[] PROGMEM = "X/Y calibration ok. X/Y axes are perpendicular.";
@@ -343,6 +343,19 @@ const char * const MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_LANG_TABLE[LANG_NUM] PR
 	MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_PL
 };
 
+const char MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_EN[] PROGMEM = "X/Y calibration failed. Bed calibration point was not found.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_CZ[] PROGMEM = "Kalibrace X/Y selhala. Kalibracni bod podlozky nenalezen.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_IT[] PROGMEM = "X/Y calibration failed. Bed calibration point was not found.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_ES[] PROGMEM = "X/Y calibration failed. Bed calibration point was not found.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_PL[] PROGMEM = "X/Y calibration failed. Bed calibration point was not found.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_PL
+};
+
 const char MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME_EN[] PROGMEM = "X/Y skewed severly. Skew will be corrected automatically.";
 const char MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME_CZ[] PROGMEM = "X/Y osy jsou silne zkosene. Zkoseni bude automaticky vyrovnano pri tisku.";
 const char MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME_IT[] PROGMEM = "X/Y skewed severly. Skew will be corrected automatically.";
@@ -369,6 +382,45 @@ const char * const MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD_LANG_TABLE[LANG_NUM]
 	MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD_PL
 };
 
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_EN[] PROGMEM = "X/Y calibration compromised. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_CZ[] PROGMEM = "Kalibrace X/Y nepresna. Predni kalibracni body moc vpredu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_IT[] PROGMEM = "X/Y calibration compromised. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_ES[] PROGMEM = "X/Y calibration compromised. Front calibration points not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_PL[] PROGMEM = "X/Y calibration compromised. Front calibration points not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_PL
+};
+
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_EN[] PROGMEM = "X/Y calibration compromised. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_CZ[] PROGMEM = "Kalibrace X/Y nepresna. Levy predni bod moc vpredu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_IT[] PROGMEM = "X/Y calibration compromised. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_ES[] PROGMEM = "X/Y calibration compromised. Left front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_PL[] PROGMEM = "X/Y calibration compromised. Left front calibration point not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_PL
+};
+
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_EN[] PROGMEM = "X/Y calibration compromised. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_CZ[] PROGMEM = "Kalibrace X/Y nepresna. Pravy predni bod moc vpredu.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_IT[] PROGMEM = "X/Y calibration compromised. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_ES[] PROGMEM = "X/Y calibration compromised. Right front calibration point not reachable.";
+const char MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_PL[] PROGMEM = "X/Y calibration compromised. Right front calibration point not reachable.";
+const char * const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_EN,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_CZ,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_IT,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_ES,
+	MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_PL
+};
+
 const char MSG_BEGIN_FILE_LIST_EN[] PROGMEM = "Begin file list";
 const char MSG_BEGIN_FILE_LIST_CZ[] PROGMEM = "Begin file list";
 const char MSG_BEGIN_FILE_LIST_IT[] PROGMEM = "Begin file list";

+ 16 - 8
Firmware/language_all.h

@@ -50,20 +50,28 @@ extern const char* const MSG_BED_LEVELING_FAILED_POINT_HIGH_LANG_TABLE[LANG_NUM]
 #define MSG_BED_LEVELING_FAILED_POINT_HIGH LANG_TABLE_SELECT(MSG_BED_LEVELING_FAILED_POINT_HIGH_LANG_TABLE)
 extern const char* const MSG_BED_LEVELING_FAILED_POINT_LOW_LANG_TABLE[LANG_NUM];
 #define MSG_BED_LEVELING_FAILED_POINT_LOW LANG_TABLE_SELECT(MSG_BED_LEVELING_FAILED_POINT_LOW_LANG_TABLE)
-extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_LANG_TABLE[LANG_NUM];
-#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FAILED_LANG_TABLE)
-extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM];
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR_LANG_TABLE)
-extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM];
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR_LANG_TABLE)
-extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM];
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED_LANG_TABLE)
 extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_LANG_TABLE[LANG_NUM];
 #define MSG_BED_SKEW_OFFSET_DETECTION_PERFECT LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_PERFECT_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND_LANG_TABLE)
 extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME_LANG_TABLE[LANG_NUM];
 #define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME_LANG_TABLE)
 extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD_LANG_TABLE[LANG_NUM];
 #define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR_LANG_TABLE)
+extern const char* const MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_LANG_TABLE[LANG_NUM];
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR LANG_TABLE_SELECT(MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR_LANG_TABLE)
 extern const char* const MSG_BEGIN_FILE_LIST_LANG_TABLE[LANG_NUM];
 #define MSG_BEGIN_FILE_LIST LANG_TABLE_SELECT(MSG_BEGIN_FILE_LIST_LANG_TABLE)
 extern const char* const MSG_BROWNOUT_RESET_LANG_TABLE[LANG_NUM];

+ 11 - 7
Firmware/language_cz.h

@@ -297,13 +297,17 @@
 #define MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE2	"kalibracniho bodu"
 #define MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE3	" z 9"
 
-#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED			"Kalibrace X/Y selhala. Nahlednete do manualu."
-#define MSG_BED_SKEW_OFFSET_DETECTION_PERFECT			"Kalibrace X/Y perfektni. X/Y osy jsou kolme."
-#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD			"Kalibrace X/Y v poradku. X/Y osy mirne zkosene."
-#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		"X/Y osy jsou silne zkosene. Zkoseni bude automaticky vyrovnano pri tisku."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR	"Kalibrace selhala. Levy predni bod moc vpredu. Srovnejte tiskarnu."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR	"Kalibrace selhala. Pravy predni bod moc vpredu. Srovnejte tiskarnu."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR	"Kalibrace selhala. Predni kalibracni body moc vpredu. Srovnejte tiskarnu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND			"Kalibrace X/Y selhala. Kalibracni bod podlozky nenalezen."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED			"Kalibrace X/Y selhala. Nahlednete do manualu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_PERFECT					"X/Y calibration ok. X/Y axes are perpendicular."
+#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD					"Kalibrace X/Y v poradku. X/Y osy mirne zkosene."
+#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME				"X/Y osy jsou silne zkosene. Zkoseni bude automaticky vyrovnano pri tisku."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR		"Kalibrace X/Y selhala. Levy predni bod moc vpredu. Srovnejte tiskarnu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR	"Kalibrace X/Y selhala. Pravy predni bod moc vpredu. Srovnejte tiskarnu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR		"Kalibrace X/Y selhala. Predni kalibracni body moc vpredu. Srovnejte tiskarnu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR	"Kalibrace X/Y nepresna. Levy predni bod moc vpredu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR	"Kalibrace X/Y nepresna. Pravy predni bod moc vpredu."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR	"Kalibrace X/Y nepresna. Predni kalibracni body moc vpredu."
 
 #define MSG_BED_LEVELING_FAILED_POINT_LOW				"Kalibrace Z selhala. Sensor nesepnul. Znecistena tryska? Cekam na reset."
 #define MSG_BED_LEVELING_FAILED_POINT_HIGH				"Kalibrace Z selhala. Sensor sepnul prilis vysoko. Cekam na reset."

+ 13 - 9
Firmware/language_en.h

@@ -290,15 +290,19 @@
 #define MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE2	"calibration point"
 #define MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE3	" of 9"
 
-#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED			"X/Y calibration failed. Please consult manual."
-#define MSG_BED_SKEW_OFFSET_DETECTION_PERFECT			"X/Y calibration ok. X/Y axes are perpendicular."
-#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD			"X/Y calibration all right. X/Y axes are slightly skewed."
-#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		"X/Y skewed severly. Skew will be corrected automatically."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR	"X/Y calibration bad. Left front calibration point not reachable. Fix the printer."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR	"X/Y calibration bad. Right front calibration point not reachable. Fix the printer."
-#define MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR	"X/Y calibration bad. Front calibration points not reachable. Fix the printer."
+#define MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND			"X/Y calibration failed. Bed calibration point was not found."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED			"X/Y calibration failed. Please consult the manual."
+#define MSG_BED_SKEW_OFFSET_DETECTION_PERFECT					"X/Y calibration ok. X/Y axes are perpendicular."
+#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD					"X/Y calibration all right. X/Y axes are slightly skewed."
+#define MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME				"X/Y skewed severly. Skew will be corrected automatically."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR		"X/Y calibration failed. Left front calibration point not reachable."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR	"X/Y calibration failed. Right front calibration point not reachable."
+#define MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR		"X/Y calibration failed. Front calibration points not reachable."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR	"X/Y calibration compromised. Left front calibration point not reachable."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR	"X/Y calibration compromised. Right front calibration point not reachable."
+#define MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR	"X/Y calibration compromised. Front calibration points not reachable."
 
-#define MSG_BED_LEVELING_FAILED_POINT_LOW				"Bed leveling failed. Sensor didnt trigger. Debris on nozzle? Waiting for reset."
-#define MSG_BED_LEVELING_FAILED_POINT_HIGH				"Bed leveling failed. Sensor triggered too high. Waiting for reset."
+#define MSG_BED_LEVELING_FAILED_POINT_LOW						"Bed leveling failed. Sensor didnt trigger. Debris on nozzle? Waiting for reset."
+#define MSG_BED_LEVELING_FAILED_POINT_HIGH						"Bed leveling failed. Sensor triggered too high. Waiting for reset."
 
 #endif // LANGUAGE_EN_H

+ 196 - 390
Firmware/mesh_bed_calibration.cpp

@@ -5,7 +5,6 @@
 #include "mesh_bed_leveling.h"
 #include "stepper.h"
 #include "ultralcd.h"
-// #include "qr_solve.h"
 
 uint8_t world2machine_correction_mode;
 float   world2machine_rotation_and_skew[2][2];
@@ -14,7 +13,10 @@ float   world2machine_shift[2];
 
 // Weight of the Y coordinate for the least squares fitting of the bed induction sensor targets.
 // Only used for the first row of the points, which may not befully in reach of the sensor.
-#define WEIGHT_FIRST_ROW (0.2f)
+#define WEIGHT_FIRST_ROW_X_HIGH (1.f)
+#define WEIGHT_FIRST_ROW_X_LOW  (0.35f)
+#define WEIGHT_FIRST_ROW_Y_HIGH (0.3f)
+#define WEIGHT_FIRST_ROW_Y_LOW  (0.0f)
 
 #define BED_ZERO_REF_X (- 22.f + X_PROBE_OFFSET_FROM_EXTRUDER)
 #define BED_ZERO_REF_Y (- 0.6f + Y_PROBE_OFFSET_FROM_EXTRUDER)
@@ -37,6 +39,14 @@ float   world2machine_shift[2];
 
 #define MIN_BED_SENSOR_POINT_RESPONSE_DMR           (2.0f)
 
+//#define Y_MIN_POS_FOR_BED_CALIBRATION (MANUAL_Y_HOME_POS-0.2f)
+#define Y_MIN_POS_FOR_BED_CALIBRATION (Y_MIN_POS)
+// Distances toward the print bed edge may not be accurate.
+#define Y_MIN_POS_CALIBRATION_POINT_ACCURATE (Y_MIN_POS + 3.f)
+// When the measured point center is out of reach of the sensor, Y coordinate will be ignored
+// by the Least Squares fitting and the X coordinate will be weighted low.
+#define Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH (Y_MIN_POS - 0.5f)
+
 // Positions of the bed reference points in the machine coordinates, referenced to the P.I.N.D.A sensor.
 // The points are ordered in a zig-zag fashion to speed up the calibration.
 const float bed_ref_points[] PROGMEM = {
@@ -62,314 +72,48 @@ const float bed_ref_points_4[] PROGMEM = {
     13.f  - BED_ZERO_REF_X, 104.4f - BED_ZERO_REF_Y
 };
 
-//#define Y_MIN_POS_FOR_BED_CALIBRATION (MANUAL_Y_HOME_POS-0.2f)
-#define Y_MIN_POS_FOR_BED_CALIBRATION (Y_MIN_POS)
-
 static inline float sqr(float x) { return x * x; }
 
-
-#if 0
-// Linear Least Squares fitting of the bed to the measured induction points.
-// This method will not maintain a unity length of the machine axes.
-// This may be all right if the sensor points are measured precisely,
-// but it will stretch or shorten the machine axes if the measured data is not precise enough.
-bool calculate_machine_skew_and_offset_LS(
-    // Matrix of maximum 9 2D points (18 floats)
-    const float  *measured_pts,
-    uint8_t       npts,
-    const float  *true_pts,
-    // Resulting correction matrix.
-    float        *vec_x,
-    float        *vec_y,
-    float        *cntr,
-    // Temporary values, 49-18-(2*3)=25 floats
-//    , float *temp
-    int8_t        verbosity_level
-    )
+// Weight of a point coordinate in a least squares optimization.
+// The first row of points may not be fully reachable
+// and the y values may be shortened a bit by the bed carriage
+// pulling the belt up.
+static inline float point_weight_x(const uint8_t i, const float &y)
 {
-    if (verbosity_level >= 10) {
-        // Show the initial state, before the fitting.
-        SERIAL_ECHOPGM("X vector, initial: ");
-        MYSERIAL.print(vec_x[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_x[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("Y vector, initial: ");
-        MYSERIAL.print(vec_y[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_y[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("center, initial: ");
-        MYSERIAL.print(cntr[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(cntr[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        for (uint8_t i = 0; i < npts; ++ i) {
-            SERIAL_ECHOPGM("point #");
-            MYSERIAL.print(int(i));
-            SERIAL_ECHOPGM(" measured: (");
-            MYSERIAL.print(measured_pts[i*2], 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(measured_pts[i*2+1], 5);
-            SERIAL_ECHOPGM("); target: (");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2  ), 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2+1), 5);
-            SERIAL_ECHOPGM("), error: ");
-            MYSERIAL.print(sqrt(
-                sqr(pgm_read_float(true_pts+i*2  ) - measured_pts[i*2  ]) +
-                sqr(pgm_read_float(true_pts+i*2+1) - measured_pts[i*2+1])), 5);
-            SERIAL_ECHOLNPGM("");
-        }
-        delay_keep_alive(100);
-    }
-
-    {
-        // Create covariance matrix for A, collect the right hand side b.
-        float A[3][3] = { 0.f };
-        float b[3] = { 0.f };
-        float acc;
-        for (uint8_t r = 0; r < 3; ++ r) {
-            for (uint8_t c = 0; c < 3; ++ c) {
-                acc = 0;
-                for (uint8_t i = 0; i < npts; ++ i) {
-                    float a = (r == 2) ? 1.f : measured_pts[2 * i + r];
-                    float b = (c == 2) ? 1.f : measured_pts[2 * i + c];
-                    acc += a * b;
-                }
-                A[r][c] = acc;
-            }
-            acc = 0.f;
-            for (uint8_t i = 0; i < npts; ++ i) {
-                float a = (r == 2) ? 1.f : measured_pts[2 * i + r];
-                float b = pgm_read_float(true_pts+i*2);
-                acc += a * b;
-            }
-            b[r] = acc;
-        }
-        // Solve the linear equation for ax, bx, cx.
-        float x[3] = { 0.f };
-        for (uint8_t iter = 0; iter < 100; ++ iter) {
-            x[0] = (b[0] - A[0][1] * x[1] - A[0][2] * x[2]) / A[0][0];
-            x[1] = (b[1] - A[1][0] * x[0] - A[1][2] * x[2]) / A[1][1];
-            x[2] = (b[2] - A[2][0] * x[0] - A[2][1] * x[1]) / A[2][2];
-        }
-        // Store the result to the output variables.
-        vec_x[0] = x[0];
-        vec_y[0] = x[1];
-        cntr[0] = x[2];
-
-        // Recalculate A and b for the y values.
-        // Note the weighting of the first row of values.
-        for (uint8_t r = 0; r < 3; ++ r) {
-            for (uint8_t c = 0; c < 3; ++ c) {
-                acc = 0;
-                for (uint8_t i = 0; i < npts; ++ i) {
-                    float w = (i < 3) ? WEIGHT_FIRST_ROW : 1.f;
-                    float a = (r == 2) ? 1.f : measured_pts[2 * i + r];
-                    float b = (c == 2) ? 1.f : measured_pts[2 * i + c];
-                    acc += a * b * w;
-                }
-                A[r][c] = acc;
-            }
-            acc = 0.f;
-            for (uint8_t i = 0; i < npts; ++ i) {
-                float w = (i < 3) ? WEIGHT_FIRST_ROW : 1.f;
-                float a = (r == 2) ? 1.f : measured_pts[2 * i + r];
-                float b = pgm_read_float(true_pts+i*2+1);
-                acc += w * a * b;
-            }
-            b[r] = acc;
-        }
-        // Solve the linear equation for ay, by, cy.
-        x[0] = 0.f, x[1] = 0.f; x[2] = 0.f;
-        for (uint8_t iter = 0; iter < 100; ++ iter) {
-            x[0] = (b[0] - A[0][1] * x[1] - A[0][2] * x[2]) / A[0][0];
-            x[1] = (b[1] - A[1][0] * x[0] - A[1][2] * x[2]) / A[1][1];
-            x[2] = (b[2] - A[2][0] * x[0] - A[2][1] * x[1]) / A[2][2];
-        }
-        // Store the result to the output variables.
-        vec_x[1] = x[0];
-        vec_y[1] = x[1];
-        cntr[1] = x[2];
-    }
-
-    if (verbosity_level >= 10) {
-        // Show the adjusted state, before the fitting.
-        SERIAL_ECHOPGM("X vector new, inverted: ");
-        MYSERIAL.print(vec_x[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_x[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("Y vector new, inverted: ");
-        MYSERIAL.print(vec_y[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_y[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("center new, inverted: ");
-        MYSERIAL.print(cntr[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(cntr[1], 5);
-        SERIAL_ECHOLNPGM("");
-        delay_keep_alive(100);
-
-        SERIAL_ECHOLNPGM("Error after correction: ");
-        for (uint8_t i = 0; i < npts; ++ i) {
-            float x = vec_x[0] * measured_pts[i*2] + vec_y[0] * measured_pts[i*2+1] + cntr[0];
-            float y = vec_x[1] * measured_pts[i*2] + vec_y[1] * measured_pts[i*2+1] + cntr[1];
-            SERIAL_ECHOPGM("point #");
-            MYSERIAL.print(int(i));
-            SERIAL_ECHOPGM(" measured: (");
-            MYSERIAL.print(measured_pts[i*2], 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(measured_pts[i*2+1], 5);
-            SERIAL_ECHOPGM("); corrected: (");
-            MYSERIAL.print(x, 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(y, 5);
-            SERIAL_ECHOPGM("); target: (");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2  ), 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2+1), 5);
-            SERIAL_ECHOPGM("), error: ");
-            MYSERIAL.print(sqrt(sqr(pgm_read_float(true_pts+i*2)-x)+sqr(pgm_read_float(true_pts+i*2+1)-y)));
-            SERIAL_ECHOLNPGM("");
-        }
-    }
-
-#if 0
-    // Normalize the vectors. We expect, that the machine axes may be skewed a bit, but the distances are correct.
-    // l shall be very close to 1 already.
-    float l = sqrt(vec_x[0]*vec_x[0] + vec_x[1] * vec_x[1]);
-    vec_x[0] /= l;
-    vec_x[1] /= l;
-    SERIAL_ECHOPGM("Length of the X vector: ");
-    MYSERIAL.print(l, 5);
-    SERIAL_ECHOLNPGM("");
-    l = sqrt(vec_y[0]*vec_y[0] + vec_y[1] * vec_y[1]);
-    vec_y[0] /= l;
-    vec_y[1] /= l;
-    SERIAL_ECHOPGM("Length of the Y vector: ");
-    MYSERIAL.print(l, 5);
-    SERIAL_ECHOLNPGM("");
-
-    // Recalculate the center using the adjusted vec_x/vec_y
-    {
-        cntr[0] = 0.f;
-        cntr[1] = 0.f;
-        for (uint8_t i = 0; i < npts; ++ i) {
-            cntr[0] += measured_pts[2 * i    ] - pgm_read_float(true_pts+i*2) * vec_x[0] - pgm_read_float(true_pts+i*2+1) * vec_y[0];
-            cntr[1] += measured_pts[2 * i + 1] - pgm_read_float(true_pts+i*2) * vec_x[1] - pgm_read_float(true_pts+i*2+1) * vec_y[1];
-        }
-        cntr[0] /= float(npts);
-        cntr[1] /= float(npts);
-    }
-
-    SERIAL_ECHOPGM("X vector new, inverted, normalized: ");
-    MYSERIAL.print(vec_x[0], 5);
-    SERIAL_ECHOPGM(", ");
-    MYSERIAL.print(vec_x[1], 5);
-    SERIAL_ECHOLNPGM("");
-
-    SERIAL_ECHOPGM("Y vector new, inverted, normalized: ");
-    MYSERIAL.print(vec_y[0], 5);
-    SERIAL_ECHOPGM(", ");
-    MYSERIAL.print(vec_y[1], 5);
-    SERIAL_ECHOLNPGM("");
-
-    SERIAL_ECHOPGM("center new, inverted, normalized: ");
-    MYSERIAL.print(cntr[0], 5);
-    SERIAL_ECHOPGM(", ");
-    MYSERIAL.print(cntr[1], 5);
-    SERIAL_ECHOLNPGM("");
-#endif
-
-    // Invert the transformation matrix made of vec_x, vec_y and cntr.
-    {
-        float d = vec_x[0] * vec_y[1] - vec_x[1] * vec_y[0];
-        float Ainv[2][2] = { 
-            {   vec_y[1] / d, - vec_y[0] / d },
-            { - vec_x[1] / d,   vec_x[0] / d }
-        };
-        float cntrInv[2] = {
-            - Ainv[0][0] * cntr[0] - Ainv[0][1] * cntr[1],
-            - Ainv[1][0] * cntr[0] - Ainv[1][1] * cntr[1]
-        };
-        vec_x[0] = Ainv[0][0];
-        vec_x[1] = Ainv[1][0];
-        vec_y[0] = Ainv[0][1];
-        vec_y[1] = Ainv[1][1];
-        cntr[0] = cntrInv[0];
-        cntr[1] = cntrInv[1];
-    }
-
-    if (verbosity_level >= 1) {
-        // Show the adjusted state, before the fitting.
-        SERIAL_ECHOPGM("X vector, adjusted: ");
-        MYSERIAL.print(vec_x[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_x[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("Y vector, adjusted: ");
-        MYSERIAL.print(vec_y[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(vec_y[1], 5);
-        SERIAL_ECHOLNPGM("");
-
-        SERIAL_ECHOPGM("center, adjusted: ");
-        MYSERIAL.print(cntr[0], 5);
-        SERIAL_ECHOPGM(", ");
-        MYSERIAL.print(cntr[1], 5);
-        SERIAL_ECHOLNPGM("");
-        delay_keep_alive(100);
-    }
-
-    if (verbosity_level >= 2) {
-        SERIAL_ECHOLNPGM("Difference after correction: ");
-        for (uint8_t i = 0; i < npts; ++ i) {
-            float x = vec_x[0] * pgm_read_float(true_pts+i*2) + vec_y[0] * pgm_read_float(true_pts+i*2+1) + cntr[0];
-            float y = vec_x[1] * pgm_read_float(true_pts+i*2) + vec_y[1] * pgm_read_float(true_pts+i*2+1) + cntr[1];
-            SERIAL_ECHOPGM("point #");
-            MYSERIAL.print(int(i));
-            SERIAL_ECHOPGM("measured: (");
-            MYSERIAL.print(measured_pts[i*2], 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(measured_pts[i*2+1], 5);
-            SERIAL_ECHOPGM("); measured-corrected: (");
-            MYSERIAL.print(x, 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(y, 5);
-            SERIAL_ECHOPGM("); target: (");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2  ), 5);
-            SERIAL_ECHOPGM(", ");
-            MYSERIAL.print(pgm_read_float(true_pts+i*2+1), 5);
-            SERIAL_ECHOPGM("), error: ");
-            MYSERIAL.print(sqrt(sqr(measured_pts[i*2]-x)+sqr(measured_pts[i*2+1]-y)));
-            SERIAL_ECHOLNPGM("");
+    float w = 1.f;
+    if (i < 3) {
+        if (y >= Y_MIN_POS_CALIBRATION_POINT_ACCURATE) {
+            w = WEIGHT_FIRST_ROW_X_HIGH;
+        } else if (y < Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH) {
+            // If the point is fully outside, give it some weight.
+            w = WEIGHT_FIRST_ROW_X_LOW;
+        } else {
+            // Linearly interpolate the weight from 1 to WEIGHT_FIRST_ROW_X.
+            float t = (y - Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH) / (Y_MIN_POS_CALIBRATION_POINT_ACCURATE - Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH);
+            w = (1.f - t) * WEIGHT_FIRST_ROW_X_LOW + t * WEIGHT_FIRST_ROW_X_HIGH;
         }
-        delay_keep_alive(100);
     }
-
-    return true;
+    return w;
 }
 
-#else
-
-static inline float point_weight_y(uint8_t i, BedSkewOffsetDetectionResultType measured_points_status)
+// Weight of a point coordinate in a least squares optimization.
+// The first row of points may not be fully reachable
+// and the y values may be shortened a bit by the bed carriage
+// pulling the belt up.
+static inline float point_weight_y(const uint8_t i, const float &y)
 {
     float w = 1.f;
     if (i < 3) {
-        w = WEIGHT_FIRST_ROW;
-        if ((i == 0 && (measured_points_status & BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR)) ||
-            (i == 1 && (measured_points_status & BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR)) ||
-            (i == 2 && (measured_points_status & BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR)))
-            w = 0.f;
+        if (y >= Y_MIN_POS_CALIBRATION_POINT_ACCURATE) {
+            w = WEIGHT_FIRST_ROW_Y_HIGH;
+        } else if (y < Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH) {
+            // If the point is fully outside, give it some weight.
+            w = WEIGHT_FIRST_ROW_Y_LOW;
+        } else {
+            // Linearly interpolate the weight from 1 to WEIGHT_FIRST_ROW_X.
+            float t = (y - Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH) / (Y_MIN_POS_CALIBRATION_POINT_ACCURATE - Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH);
+            w = (1.f - t) * WEIGHT_FIRST_ROW_Y_LOW + t * WEIGHT_FIRST_ROW_Y_HIGH;
+        }
     }
     return w;
 }
@@ -382,7 +126,6 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
     // Matrix of maximum 9 2D points (18 floats)
     const float  *measured_pts,
     uint8_t       npts,
-    BedSkewOffsetDetectionResultType measured_points_status,
     const float  *true_pts,
     // Resulting correction matrix.
     float        *vec_x,
@@ -466,7 +209,8 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
                              (c == 0) ? 1.f :
                             ((c == 2) ? (-s1 * measured_pts[2 * i]) :
                                         (-c2 * measured_pts[2 * i + 1]));
-                        acc += a * b;
+                        float w = point_weight_x(i, measured_pts[2 * i + 1]);
+                        acc += a * b * w;
                     }
                     // Second for the residuum in the y axis. 
                     // The first row of the points have a low weight, because their position may not be known
@@ -480,7 +224,7 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
                              (c == 1) ? 1.f :
                             ((c == 2) ? ( c1 * measured_pts[2 * i]) :
                                         (-s2 * measured_pts[2 * i + 1]));
-                        float w = point_weight_y(i, measured_points_status);
+                        float w = point_weight_y(i, measured_pts[2 * i + 1]);
                         acc += a * b * w;
                     }
                 }
@@ -496,7 +240,8 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
                         ((r == 2) ? (-s1 * measured_pts[2 * i]) :
                                     (-c2 * measured_pts[2 * i + 1])));
                     float fx = c1 * measured_pts[2 * i] - s2 * measured_pts[2 * i + 1] + cntr[0] - pgm_read_float(true_pts + i * 2);
-                    acc += j * fx;
+                    float w = point_weight_x(i, measured_pts[2 * i + 1]);
+                    acc += j * fx * w;
                 }
                 {
                     float j = 
@@ -505,7 +250,7 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
                         ((r == 2) ? ( c1 * measured_pts[2 * i]) :
                                     (-s2 * measured_pts[2 * i + 1])));
                     float fy = s1 * measured_pts[2 * i] + c2 * measured_pts[2 * i + 1] + cntr[1] - pgm_read_float(true_pts + i * 2 + 1);
-                    float w = point_weight_y(i, measured_points_status);
+                    float w = point_weight_y(i, measured_pts[2 * i + 1]);
                     acc += j * fy * w;
                 }
             }
@@ -612,13 +357,13 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
         float errY = sqr(pgm_read_float(true_pts + i * 2 + 1) - y);
         float err = sqrt(errX + errY);
         if (i < 3) {
-            float w = point_weight_y(i, measured_points_status);
+            float w = point_weight_y(i, measured_pts[2 * i + 1]);
             if (sqrt(errX) > BED_CALIBRATION_POINT_OFFSET_MAX_1ST_ROW_X ||
                 (w != 0.f && sqrt(errY) > BED_CALIBRATION_POINT_OFFSET_MAX_1ST_ROW_Y))
-                result = (measured_points_status == BED_SKEW_OFFSET_DETECTION_PERFECT) ? BED_SKEW_OFFSET_DETECTION_FAILED : BedSkewOffsetDetectionResultType(- int8_t(measured_points_status));
+                result = BED_SKEW_OFFSET_DETECTION_FITTING_FAILED;
         } else {
             if (err > BED_CALIBRATION_POINT_OFFSET_MAX_EUCLIDIAN)
-                result = (measured_points_status == BED_SKEW_OFFSET_DETECTION_PERFECT) ? BED_SKEW_OFFSET_DETECTION_FAILED : BedSkewOffsetDetectionResultType(- int8_t(measured_points_status));
+                result = BED_SKEW_OFFSET_DETECTION_FITTING_FAILED;
         }
         if (verbosity_level >= 10) {
             SERIAL_ECHOPGM("point #");
@@ -664,16 +409,19 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
         // Refresh the offset.
         cntr[0] = 0.f;
         cntr[1] = 0.f;
+        float wx = 0.f;
         float wy = 0.f;
         for (int8_t i = 0; i < 9; ++ i) {
             float x = vec_x[0] * measured_pts[i * 2] + vec_y[0] * measured_pts[i * 2 + 1];
             float y = vec_x[1] * measured_pts[i * 2] + vec_y[1] * measured_pts[i * 2 + 1];
-            float w = (i < 3) ? WEIGHT_FIRST_ROW : 1.f;
-            cntr[0] += pgm_read_float(true_pts + i * 2) - x;
+            float w = point_weight_x(i, y);
+            cntr[0] += w * (pgm_read_float(true_pts + i * 2) - x);
+            wx += w;
+            w = point_weight_y(i, y);
             cntr[1] += w * (pgm_read_float(true_pts + i * 2 + 1) - y);
             wy += w;
         }
-        cntr[0] /= 9.f;
+        cntr[0] /= wx;
         cntr[1] /= wy;
     }
     #endif
@@ -745,14 +493,9 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
         delay_keep_alive(100);
     }
 
-    if (result >= 0 && measured_points_status > 0)
-        // Maintain the "left / right / both points out of reach" status.
-        result = measured_points_status;
     return result;
 }
 
-#endif
-
 void reset_bed_offset_and_skew()
 {
     eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_CENTER+0), 0x0FFFFFFFF);
@@ -1312,6 +1055,11 @@ inline bool improve_bed_induction_sensor_point2(bool lift_z_on_min_y, int8_t ver
         }
         b = current_position[X_AXIS];
         if (b - a < MIN_BED_SENSOR_POINT_RESPONSE_DMR) {
+            if (verbosity_level >= 5) {
+                SERIAL_ECHOPGM("Point width too small: ");
+                SERIAL_ECHO(b - a);
+                SERIAL_ECHOLNPGM("");
+            }
             // We force the calibration routine to move the Z axis slightly down to make the response more pronounced.
             current_position[X_AXIS] = center_old_x;
             goto canceled;            
@@ -1372,8 +1120,13 @@ inline bool improve_bed_induction_sensor_point2(bool lift_z_on_min_y, int8_t ver
         b = current_position[Y_AXIS];
         if (b - a < MIN_BED_SENSOR_POINT_RESPONSE_DMR) {
             // We force the calibration routine to move the Z axis slightly down to make the response more pronounced.
+            if (verbosity_level >= 5) {
+                SERIAL_ECHOPGM("Point height too small: ");
+                SERIAL_ECHO(b - a);
+                SERIAL_ECHOLNPGM("");
+            }
             current_position[Y_AXIS] = center_old_y;
-            goto canceled;            
+            goto canceled;
         }
         if (verbosity_level >= 5) {
             debug_output_point(PSTR("top" ), current_position[X_AXIS], a, current_position[Z_AXIS]);
@@ -1399,20 +1152,18 @@ canceled:
 // Searching in a zig-zag movement in a plane for the maximum width of the response.
 #define IMPROVE_BED_INDUCTION_SENSOR_POINT3_SEARCH_RADIUS (4.f)
 #define IMPROVE_BED_INDUCTION_SENSOR_POINT3_SEARCH_STEP_FINE_Y (0.1f)
-enum InductionSensorPointStatusType
-{
-    INDUCTION_SENSOR_POINT_FAILED = -1,
-    INDUCTION_SENSOR_POINT_OK = 0,
-    INDUCTION_SENSOR_POINT_FAR = 1,
-};
-inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int verbosity_level)
+inline bool improve_bed_induction_sensor_point3(int verbosity_level)
 {
+    if (current_position[Y_AXIS] < Y_MIN_POS_FOR_BED_CALIBRATION)
+        current_position[Y_AXIS] = Y_MIN_POS_FOR_BED_CALIBRATION;
+
     float center_old_x = current_position[X_AXIS];
     float center_old_y = current_position[Y_AXIS];
     float a, b;
+    bool  result = true;
+
     // Was the sensor point detected too far in the minus Y axis?
     // If yes, the center of the induction point cannot be reached by the machine.
-    bool y_too_far = false;
     {
         float x0 = center_old_x - IMPROVE_BED_INDUCTION_SENSOR_POINT3_SEARCH_RADIUS;
         float x1 = center_old_x + IMPROVE_BED_INDUCTION_SENSOR_POINT3_SEARCH_RADIUS;
@@ -1438,6 +1189,7 @@ inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int ve
         }
 
         // Search in the positive Y direction, until a maximum diameter is found.
+        // (the next diameter is smaller than the current one.)
         float dmax = 0.f;
         float xmax1 = 0.f;
         float xmax2 = 0.f;
@@ -1482,14 +1234,37 @@ inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int ve
         if (dmax == 0.) {
             if (verbosity_level > 0)
                 SERIAL_PROTOCOLPGM("failed - not found\n");
+            current_position[X_AXIS] = center_old_x;
+            current_position[Y_AXIS] = center_old_y;
+            goto canceled;
+        }
+
+        {
+            // Find the positive Y hit. This gives the extreme Y value for the search of the maximum diameter in the -Y direction.
+            enable_z_endstop(false);
+            go_xy(xmax1, y0 + IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS, homing_feedrate[X_AXIS] / 60.f);
+            enable_z_endstop(true);
+            go_xy(xmax1, max(y0 - IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS, Y_MIN_POS_FOR_BED_CALIBRATION), homing_feedrate[X_AXIS] / 60.f);
+            update_current_position_xyz();
+            if (! endstop_z_hit_on_purpose()) {
+                current_position[Y_AXIS] = center_old_y;
+                goto canceled;
+            }
+            if (verbosity_level >= 5)
+                debug_output_point(PSTR("top" ), current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]);
+            y1 = current_position[Y_AXIS];
+        }
+
+        if (y1 <= y0) {
+            // Either the induction sensor is too high, or the induction sensor target is out of reach.
+            current_position[Y_AXIS] = center_old_y;
             goto canceled;
         }
 
-        // SERIAL_PROTOCOLPGM("ok 1\n");
         // Search in the negative Y direction, until a maximum diameter is found.
         dmax = 0.f;
-        if (y0 + 1.f < y1)
-            y1 = y0 + 1.f;
+        // if (y0 + 1.f < y1)
+        //    y1 = y0 + 1.f;
         for (y = y1; y >= y0; y -= IMPROVE_BED_INDUCTION_SENSOR_POINT3_SEARCH_STEP_FINE_Y) {
             enable_z_endstop(false);
             go_xy(x0, y, homing_feedrate[X_AXIS] / 60.f);
@@ -1585,7 +1360,7 @@ inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int ve
 
         {
             // Compare the distance in the Y+ direction with the diameter in the X direction.
-            // Find the positive Y hit.
+            // Find the positive Y hit once again, this time along the Y axis going through the X point with the highest diameter.
             enable_z_endstop(false);
             go_xy(xmax, ymax + IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS, homing_feedrate[X_AXIS] / 60.f);
             enable_z_endstop(true);
@@ -1597,19 +1372,45 @@ inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int ve
             }
             if (verbosity_level >= 5)
                 debug_output_point(PSTR("top" ), current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]);
-            if (current_position[Y_AXIS] - y0 < 0.5f * dmax) {
-                // Probably not even a half circle was detected. The induction point is too far in the minus Y direction.
-                if (current_position[Y_AXIS] - 0.5f * dmax < Y_MIN_POS_FOR_BED_CALIBRATION - 0.6f) {
-                    ymax = current_position[Y_AXIS] - 0.5f * dmax;
-                    // ymax = Y_MIN_POS_FOR_BED_CALIBRATION;
-                    y_too_far = true;
+            if (current_position[Y_AXIS] - Y_MIN_POS_FOR_BED_CALIBRATION < 0.5f * dmax) {
+                // Probably not even a half circle was detected. The induction point is likely too far in the minus Y direction.
+                // First verify, if the measurement has been done at a sufficient height. If no, lower the Z axis a bit.
+                if (current_position[Y_AXIS] < ymax || dmax < 0.5f * MIN_BED_SENSOR_POINT_RESPONSE_DMR) {
+                    if (verbosity_level >= 5) {
+                        SERIAL_ECHOPGM("Partial point diameter too small: ");
+                        SERIAL_ECHO(dmax);
+                        SERIAL_ECHOLNPGM("");
+                    }
+                    result = false;
                 } else {
-                    ymax = current_position[Y_AXIS] - 0.5f * dmax;
+                    // Estimate the circle radius from the maximum diameter and height:
+                    float h = current_position[Y_AXIS] - ymax;
+                    float r = dmax * dmax / (8.f * h) + 0.5f * h;
+                    if (r < 0.8f * MIN_BED_SENSOR_POINT_RESPONSE_DMR) {
+                        if (verbosity_level >= 5) {
+                            SERIAL_ECHOPGM("Partial point estimated radius too small: ");
+                            SERIAL_ECHO(r);
+                            SERIAL_ECHOPGM(", dmax:");
+                            SERIAL_ECHO(dmax);
+                            SERIAL_ECHOPGM(", h:");
+                            SERIAL_ECHO(h);
+                            SERIAL_ECHOLNPGM("");
+                        }
+                        result = false;
+                    } else {
+                        // The point may end up outside of the machine working space.
+                        // That is all right as it helps to improve the accuracy of the measurement point
+                        // due to averaging.
+                        // For the y correction, use an average of dmax/2 and the estimated radius.
+                        r = 0.5f * (0.5f * dmax + r);
+                        ymax = current_position[Y_AXIS] - r;
+                    }
                 }
-                //FIXME
-                if (ymax < Y_MIN_POS_FOR_BED_CALIBRATION)
-                    ymax = Y_MIN_POS_FOR_BED_CALIBRATION;
             } else {
+                // If the diameter of the detected spot was smaller than a minimum allowed,
+                // the induction sensor is probably too high. Returning false will force
+                // the sensor to be lowered a tiny bit.
+                result = xmax >= MIN_BED_SENSOR_POINT_RESPONSE_DMR;
                 ymax = 0.5f * (y0 + y1);
             }
         }
@@ -1629,13 +1430,13 @@ inline InductionSensorPointStatusType improve_bed_induction_sensor_point3(int ve
         // delay_keep_alive(3000);
     }
 
-    return y_too_far ? INDUCTION_SENSOR_POINT_FAR : INDUCTION_SENSOR_POINT_OK;
+    return result;
 
 canceled:
     // Go back to the center.
     enable_z_endstop(false);
     go_xy(current_position[X_AXIS], current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
-    return INDUCTION_SENSOR_POINT_FAILED;
+    return false;
 }
 
 // Scan the mesh bed induction points one by one by a left-right zig-zag movement,
@@ -1738,17 +1539,17 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
         if (verbosity_level >= 10)
             delay_keep_alive(3000);
         if (! find_bed_induction_sensor_point_xy())
-            return BED_SKEW_OFFSET_DETECTION_FAILED;
-        find_bed_induction_sensor_point_z();
+            return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
 #if 1
         if (k == 0) {
             // Improve the position of the 1st row sensor points by a zig-zag movement.
+            find_bed_induction_sensor_point_z();
             int8_t i = 4;
             for (;;) {
-                if (improve_bed_induction_sensor_point3(verbosity_level) != INDUCTION_SENSOR_POINT_FAILED)
+                if (improve_bed_induction_sensor_point3(verbosity_level))
                     break;
                 if (-- i == 0)
-                    return BED_SKEW_OFFSET_DETECTION_FAILED;
+                    return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
                 // Try to move the Z axis down a bit to increase a chance of the sensor to trigger.
                 current_position[Z_AXIS] -= 0.025f;
                 enable_endstops(false);
@@ -1757,7 +1558,7 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
             }
             if (i == 0)
                 // not found
-                return BED_SKEW_OFFSET_DETECTION_FAILED;
+                return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
         }
 #endif
         if (verbosity_level >= 10)
@@ -1791,44 +1592,49 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
         }
     }
 
-    calculate_machine_skew_and_offset_LS(pts, 4, BED_SKEW_OFFSET_DETECTION_PERFECT, bed_ref_points_4, vec_x, vec_y, cntr, verbosity_level);
-    world2machine_update(vec_x, vec_y, cntr);
-#if 1
-    // Fearlessly store the calibration values into the eeprom.
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+0), cntr [0]);
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+4), cntr [1]);
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +0), vec_x[0]);
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +4), vec_x[1]);
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +0), vec_y[0]);
-    eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +4), vec_y[1]);
-#endif
+    BedSkewOffsetDetectionResultType result = calculate_machine_skew_and_offset_LS(pts, 4, bed_ref_points_4, vec_x, vec_y, cntr, verbosity_level);
+    if (result >= 0) {
+        world2machine_update(vec_x, vec_y, cntr);
+    #if 1
+        // Fearlessly store the calibration values into the eeprom.
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+0), cntr [0]);
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+4), cntr [1]);
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +0), vec_x[0]);
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +4), vec_x[1]);
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +0), vec_y[0]);
+        eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +4), vec_y[1]);
+    #endif
 
-    // Correct the current_position to match the transformed coordinate system after world2machine_rotation_and_skew and world2machine_shift were set.
-    world2machine_update_current();
+        // Correct the current_position to match the transformed coordinate system after world2machine_rotation_and_skew and world2machine_shift were set.
+        world2machine_update_current();
 
-    if (verbosity_level >= 20) {
-        // Test the positions. Are the positions reproducible? Now the calibration is active in the planner.
-        delay_keep_alive(3000);
-        for (int8_t mesh_point = 0; mesh_point < 9; ++ mesh_point) {
-            // Don't let the manage_inactivity() function remove power from the motors.
-            refresh_cmd_timeout();
-            // Go to the measurement point.
-            // Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
-            current_position[X_AXIS] = pgm_read_float(bed_ref_points+mesh_point*2);
-            current_position[Y_AXIS] = pgm_read_float(bed_ref_points+mesh_point*2+1);
-            go_to_current(homing_feedrate[X_AXIS]/60);
+        if (verbosity_level >= 20) {
+            // Test the positions. Are the positions reproducible? Now the calibration is active in the planner.
             delay_keep_alive(3000);
+            for (int8_t mesh_point = 0; mesh_point < 9; ++ mesh_point) {
+                // Don't let the manage_inactivity() function remove power from the motors.
+                refresh_cmd_timeout();
+                // Go to the measurement point.
+                // Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
+                current_position[X_AXIS] = pgm_read_float(bed_ref_points+mesh_point*2);
+                current_position[Y_AXIS] = pgm_read_float(bed_ref_points+mesh_point*2+1);
+                go_to_current(homing_feedrate[X_AXIS]/60);
+                delay_keep_alive(3000);
+            }
         }
     }
 
-    return BED_SKEW_OFFSET_DETECTION_PERFECT;
+    return result;
 }
 
-BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level)
+BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level, uint8_t &too_far_mask)
 {
     // Don't let the manage_inactivity() function remove power from the motors.
     refresh_cmd_timeout();
 
+    // Mask of the first three points. Are they too far?
+    too_far_mask = 0;
+
     // Reusing the z_values memory for the measurement cache.
     // 7x7=49 floats, good for 16 (x,y,z) vectors.
     float *pts = &mbl.z_values[0][0];
@@ -1857,9 +1663,7 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 
     // Collect a matrix of 9x9 points.
-    bool leftFrontTooFar = false;
-    bool rightFrontTooFar = false;
-    int8_t result = BED_SKEW_OFFSET_DETECTION_PERFECT;
+    BedSkewOffsetDetectionResultType result = BED_SKEW_OFFSET_DETECTION_PERFECT;
     for (int8_t mesh_point = 0; mesh_point < 9; ++ mesh_point) {
         // Don't let the manage_inactivity() function remove power from the motors.
         refresh_cmd_timeout();
@@ -1900,6 +1704,8 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
         find_bed_induction_sensor_point_z();
         if (verbosity_level >= 10)
             delay_keep_alive(3000);
+        // Try to move the Z axis down a bit to increase a chance of the sensor to trigger.
+        current_position[Z_AXIS] -= 0.025f;
         // Improve the point position by searching its center in a current plane.
         int8_t n_errors = 3;
         for (int8_t iter = 0; iter < 8; ) {
@@ -1918,18 +1724,7 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
                 // of the sensor points, the y position cannot be measured
                 // by a cross center method.
                 // Use a zig-zag search for the first row of the points.
-                InductionSensorPointStatusType status = improve_bed_induction_sensor_point3(verbosity_level);
-                if (status == INDUCTION_SENSOR_POINT_FAILED) {
-                    found = false;
-                } else {
-                    found = true;
-                    if (iter == 7 && INDUCTION_SENSOR_POINT_FAR)
-                        // Remember, which side of the bed is shifted too far in the minus y direction.
-                        result |= 
-                             (mesh_point == 1) ? BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR : 
-                            ((mesh_point == 0) ? BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR : 
-                                                 BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR);
-                }
+                found = improve_bed_induction_sensor_point3(verbosity_level);
             } else {
                 switch (method) {
                     case 0: found = improve_bed_induction_sensor_point(); break;
@@ -1946,6 +1741,7 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
                 ++ iter;
             } else if (n_errors -- == 0) {
                 // Give up.
+                result = BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
                 goto canceled;
             } else {
                 // Try to move the Z axis down a bit to increase a chance of the sensor to trigger.
@@ -2001,11 +1797,21 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
     }
 
     {
-        result = calculate_machine_skew_and_offset_LS(pts, 9, BedSkewOffsetDetectionResultType(result), bed_ref_points, vec_x, vec_y, cntr, verbosity_level);
+        // First fill in the too_far_mask from the measured points.
+        for (uint8_t mesh_point = 0; mesh_point < 3; ++ mesh_point)
+            if (pts[mesh_point * 2 + 1] < Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH)
+                too_far_mask |= 1 << mesh_point;
+        result = calculate_machine_skew_and_offset_LS(pts, 9, bed_ref_points, vec_x, vec_y, cntr, verbosity_level);
         if (result < 0) {
             SERIAL_ECHOLNPGM("Calculation of the machine skew and offset failed.");
             goto canceled;
         }
+        // In case of success, update the too_far_mask from the calculated points.
+        for (uint8_t mesh_point = 0; mesh_point < 3; ++ mesh_point) {
+            float y = vec_x[1] * pgm_read_float(bed_ref_points+mesh_point*2) + vec_y[1] * pgm_read_float(bed_ref_points+mesh_point*2+1) + cntr[1];
+            if (y < Y_MIN_POS_CALIBRATION_POINT_OUT_OF_REACH)
+                too_far_mask |= 1 << mesh_point;
+        }
     }
 
     world2machine_update(vec_x, vec_y, cntr);
@@ -2150,7 +1956,7 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
     enable_z_endstop(endstop_z_enabled);
     // Don't let the manage_inactivity() function remove power from the motors.
     refresh_cmd_timeout();
-    return BedSkewOffsetDetectionResultType(result);
+    return result;
 
 canceled:
     // Don't let the manage_inactivity() function remove power from the motors.
@@ -2162,7 +1968,7 @@ canceled:
     reset_bed_offset_and_skew();
     enable_endstops(endstops_enabled);
     enable_z_endstop(endstop_z_enabled);
-    return (result == BED_SKEW_OFFSET_DETECTION_PERFECT) ? BED_SKEW_OFFSET_DETECTION_FAILED : BedSkewOffsetDetectionResultType(- int8_t(result));
+    return result;
 }
 
 bool scan_bed_induction_points(int8_t verbosity_level)

+ 4 - 8
Firmware/mesh_bed_calibration.h

@@ -145,21 +145,17 @@ extern bool find_bed_induction_sensor_point_xy();
 // Negative: failed
 enum BedSkewOffsetDetectionResultType {
 	// Detection failed, some point was not found.
-	BED_SKEW_OFFSET_DETECTION_FAILED = -1,
+	BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND   = -1,
+	BED_SKEW_OFFSET_DETECTION_FITTING_FAILED    = -2,
 
 	// Detection finished with success.
 	BED_SKEW_OFFSET_DETECTION_PERFECT 			= 0,
 	BED_SKEW_OFFSET_DETECTION_SKEW_MILD			= 1,
-	BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		= 2,
-	// Detection finished with success, but it is recommended to fix the printer mechanically.
-	BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR	= 4,
-	BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR	= 8,
-	BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR	= BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR | BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR,
+	BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		= 2
 };
 
 extern BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level);
-extern BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level);
-
+extern BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level, uint8_t &too_far_mask);
 
 extern void reset_bed_offset_and_skew();
 extern bool is_bed_z_jitter_data_valid();

+ 53 - 27
Firmware/ultralcd.cpp

@@ -1353,34 +1353,8 @@ void lcd_display_message_fullscreen_P(const char *msg)
     }
 }
 
-void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result)
+static void lcd_show_fullscreen_message_and_wait_P(const char *msg)
 {
-    const char *msg = NULL;
-    switch (result) {
-        case BED_SKEW_OFFSET_DETECTION_FAILED:
-        default:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_FAILED;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_PERFECT:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_PERFECT;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_SKEW_MILD:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_FRONT_LEFT_FAR;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_FRONT_RIGHT_FAR;
-            break;
-        case BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR:
-            msg = MSG_BED_SKEW_OFFSET_DETECTION_FRONT_BOTH_FAR;
-            break;
-    }
-
     lcd_display_message_fullscreen_P(msg);
 
     // Until confirmed by a button click.
@@ -1395,6 +1369,58 @@ void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result)
     }
 }
 
+void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result, uint8_t point_too_far_mask)
+{
+    const char *msg = NULL;
+    if (result == BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND) {
+        lcd_show_fullscreen_message_and_wait_P(MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND);
+    } else if (result == BED_SKEW_OFFSET_DETECTION_FITTING_FAILED) {
+        if (point_too_far_mask == 0)
+            msg = MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED;
+        else if (point_too_far_mask == 2 || point_too_far_mask == 7)
+            // Only the center point or all the three front points.
+            msg = MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR;
+        else if (point_too_far_mask & 1 == 0)
+            // The right and maybe the center point out of reach.
+            msg = MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR;
+        else
+            // The left and maybe the center point out of reach.
+            msg = MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR;
+        lcd_show_fullscreen_message_and_wait_P(msg);
+    } else {
+        if (point_too_far_mask != 0) {
+            if (point_too_far_mask == 2 || point_too_far_mask == 7)
+                // Only the center point or all the three front points.
+                msg = MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_BOTH_FAR;
+            else if (point_too_far_mask & 1 == 0)
+                // The right and maybe the center point out of reach.
+                msg = MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_RIGHT_FAR;
+            else
+                // The left and maybe the center point out of reach.
+                msg = MSG_BED_SKEW_OFFSET_DETECTION_WARNING_FRONT_LEFT_FAR;
+            lcd_show_fullscreen_message_and_wait_P(msg);
+        }
+        if (point_too_far_mask == 0 || result > 0) {
+            switch (result) {
+                default:
+                    // should not happen
+                    msg = MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED;
+                    break;
+                case BED_SKEW_OFFSET_DETECTION_PERFECT:
+                    msg = MSG_BED_SKEW_OFFSET_DETECTION_PERFECT;
+                    break;
+                case BED_SKEW_OFFSET_DETECTION_SKEW_MILD:
+                    msg = MSG_BED_SKEW_OFFSET_DETECTION_SKEW_MILD;
+                    break;
+                case BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME:
+                    msg = MSG_BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME;
+                    break;
+            }
+            lcd_show_fullscreen_message_and_wait_P(msg);
+        }
+    }
+}
+
 static void lcd_show_end_stops() {
     lcd.setCursor(0, 0);
     lcd_printPGM((PSTR("End stops diag")));

+ 1 - 1
Firmware/ultralcd.h

@@ -44,7 +44,7 @@
   // the user confirm that it has been done.
   extern bool lcd_calibrate_z_end_stop_manual();
   // Show the result of the calibration process on the LCD screen.
-  extern void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result);
+  extern void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result, uint8_t point_too_far_mask);
 
   extern void lcd_diag_show_end_stops();