Browse Source

Merge remote-tracking branch 'upstream/MK3_3.10.0' into MK3_MK404

3d-gussner 3 years ago
parent
commit
722415703f

+ 6 - 5
Firmware/Configuration.h

@@ -16,8 +16,8 @@ extern uint16_t nPrinterType;
 extern PGM_P sPrinterName;
 
 // Firmware version
-#define FW_VERSION "3.9.3"
-#define FW_COMMIT_NR 3556
+#define FW_VERSION "3.10.0-RC2"
+#define FW_COMMIT_NR 4104
 // FW_VERSION_UNKNOWN means this is an unofficial build.
 // The firmware should only be checked into github with this symbol.
 #define FW_DEV_VERSION FW_VERSION_UNKNOWN
@@ -551,9 +551,10 @@ enum CalibrationStatus
 
 // Try to maintain a minimum distance from the bed even when Z is
 // unknown when doing the following operations
-#define MIN_Z_FOR_LOAD    50
-#define MIN_Z_FOR_UNLOAD  50
-#define MIN_Z_FOR_PREHEAT 10
+#define MIN_Z_FOR_LOAD    50 // lcd filament loading or autoload
+#define MIN_Z_FOR_UNLOAD  50 // lcd filament unloading
+#define MIN_Z_FOR_SWAP    27 // filament change (including M600)
+#define MIN_Z_FOR_PREHEAT 10 // lcd preheat
 
 #include "Configuration_adv.h"
 #include "thermistortables.h"

+ 6 - 0
Firmware/Configuration_adv.h

@@ -387,6 +387,12 @@ const unsigned int dropsegments=5; //everything with less than this number of st
  */
 #define EXTENDED_CAPABILITIES_REPORT
 
+/**
+ * Enable M120/M121 G-code commands
+ * 
+ */
+//#define M120_M121_ENABLED  //Be careful enabling and using these G-code commands.
+
 //===========================================================================
 //=============================  Define Defines  ============================
 //===========================================================================

+ 2 - 2
Firmware/Marlin.h

@@ -363,8 +363,8 @@ extern bool mmu_print_saved;
 
 //estimated time to end of the print
 extern uint8_t print_percent_done_normal;
-extern uint16_t print_time_remaining_normal;
 extern uint8_t print_percent_done_silent;
+extern uint16_t print_time_remaining_normal;
 extern uint16_t print_time_remaining_silent;
 extern uint16_t print_time_to_change_normal;
 extern uint16_t print_time_to_change_silent;
@@ -376,7 +376,7 @@ extern uint16_t gcode_in_progress;
 
 extern LongTimer safetyTimer;
 
-#define PRINT_PERCENT_DONE_INIT   0xff
+#define PRINT_PERCENT_DONE_INIT 0xff
 #define PRINTER_ACTIVE (IS_SD_PRINTING || is_usb_printing || isPrintPaused || (custom_message_type == CustomMsg::TempCal) || saved_printing || (lcd_commands_type == LcdCommands::Layer1Cal) || mmu_print_saved || homing_flag || mesh_bed_leveling_flag)
 
 //! Beware - mcode_in_progress is set as soon as the command gets really processed,

+ 560 - 512
Firmware/Marlin_main.cpp

@@ -103,10 +103,10 @@
 #include "tmc2130.h"
 #endif //TMC2130
 
-#ifdef W25X20CL
-#include "w25x20cl.h"
-#include "optiboot_w25x20cl.h"
-#endif //W25X20CL
+#ifdef XFLASH
+#include "xflash.h"
+#include "optiboot_xflash.h"
+#endif //XFLASH
 
 #ifdef BLINKM
 #include "BlinkM.h"
@@ -219,7 +219,7 @@ unsigned int heating_status;
 unsigned int heating_status_counter;
 bool loading_flag = false;
 
-
+#define XY_NO_RESTORE_FLAG (mesh_bed_leveling_flag || homing_flag)
 
 char snmm_filaments_used = 0;
 
@@ -305,7 +305,12 @@ bool no_response = false;
 uint8_t important_status;
 uint8_t saved_filament_type;
 
-#define SAVED_TARGET_UNSET (X_MIN_POS-1)
+// Define some coordinates outside the clamp limits (making them invalid past the parsing stage) so
+// that they can be used later for various logical checks
+#define X_COORD_INVALID (X_MIN_POS-1)
+#define Y_COORD_INVALID (Y_MIN_POS-1)
+
+#define SAVED_TARGET_UNSET X_COORD_INVALID
 float saved_target[NUM_AXIS] = {SAVED_TARGET_UNSET, 0, 0, 0};
 
 // save/restore printing in case that mmu was not responding 
@@ -313,8 +318,8 @@ bool mmu_print_saved = false;
 
 // storing estimated time to end of print counted by slicer
 uint8_t print_percent_done_normal = PRINT_PERCENT_DONE_INIT;
-uint16_t print_time_remaining_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
 uint8_t print_percent_done_silent = PRINT_PERCENT_DONE_INIT;
+uint16_t print_time_remaining_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
 uint16_t print_time_remaining_silent = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
 uint16_t print_time_to_change_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining time to next change in minutes
 uint16_t print_time_to_change_silent = PRINT_TIME_REMAINING_INIT; //estimated remaining time to next change in minutes
@@ -375,7 +380,7 @@ boolean chdkActive = false;
 bool saved_printing = false; //!< Print is paused and saved in RAM
 static uint32_t saved_sdpos = 0; //!< SD card position, or line number in case of USB printing
 uint8_t saved_printing_type = PRINTING_TYPE_SD;
-static float saved_pos[4] = { 0, 0, 0, 0 };
+static float saved_pos[4] = { X_COORD_INVALID, 0, 0, 0 };
 static uint16_t saved_feedrate2 = 0; //!< Default feedrate (truncated from float)
 static int saved_feedmultiply2 = 0;
 static uint8_t saved_active_extruder = 0;
@@ -588,19 +593,6 @@ void crashdet_restore_print_and_continue()
 //	babystep_apply();
 }
 
-
-void crashdet_stop_and_save_print2()
-{
-	cli();
-	planner_abort_hard(); //abort printing
-	cmdqueue_reset(); //empty cmdqueue
-	card.sdprinting = false;
-	card.closefile();
-  // Reset and re-enable the stepper timer just before the global interrupts are enabled.
-  st_reset_timer();
-	sei();
-}
-
 void crashdet_detected(uint8_t mask)
 {
 	st_synchronize();
@@ -910,7 +902,7 @@ uint8_t check_printer_version()
 
 #if (LANG_MODE != 0) //secondary language support
 
-#ifdef W25X20CL
+#ifdef XFLASH
 
 
 // language update from external flash
@@ -936,7 +928,7 @@ void update_sec_lang_from_external_flash()
 				cli();
 				uint16_t size = header.size - state * LANGBOOT_BLOCKSIZE;
 				if (size > LANGBOOT_BLOCKSIZE) size = LANGBOOT_BLOCKSIZE;
-				w25x20cl_rd_data(src_addr + state * LANGBOOT_BLOCKSIZE, (uint8_t*)LANGBOOT_RAMBUFFER, size);
+				xflash_rd_data(src_addr + state * LANGBOOT_BLOCKSIZE, (uint8_t*)LANGBOOT_RAMBUFFER, size);
 				if (state == 0)
 				{
 					//TODO - check header integrity
@@ -954,7 +946,7 @@ void update_sec_lang_from_external_flash()
 }
 
 
-#ifdef DEBUG_W25X20CL
+#ifdef DEBUG_XFLASH
 
 uint8_t lang_xflash_enum_codes(uint16_t* codes)
 {
@@ -964,7 +956,7 @@ uint8_t lang_xflash_enum_codes(uint16_t* codes)
 	while (1)
 	{
 		printf_P(_n("LANGTABLE%d:"), count);
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t));
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t));
 		if (header.magic != LANG_MAGIC)
 		{
 			puts_P(_n("NG!"));
@@ -992,17 +984,17 @@ void list_sec_lang_from_external_flash()
 	printf_P(_n("XFlash lang count = %hhd\n"), count);
 }
 
-#endif //DEBUG_W25X20CL
+#endif //DEBUG_XFLASH
 
-#endif //W25X20CL
+#endif //XFLASH
 
 #endif //(LANG_MODE != 0)
 
 
-static void w25x20cl_err_msg()
+static void xflash_err_msg()
 {
 	lcd_clear();
-	lcd_puts_P(_n("External SPI flash\nW25X20CL is not res-\nponding. Language\nswitch unavailable."));
+	lcd_puts_P(_n("External SPI flash\nXFLASH is not res-\nponding. Language\nswitch unavailable."));
 }
 
 // "Setup" function is called by the Arduino framework on startup.
@@ -1028,23 +1020,23 @@ void setup()
 	fdev_setup_stream(uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); //setup uart out stream
 	stdout = uartout;
 
-#ifdef W25X20CL
-    bool w25x20cl_success = w25x20cl_init();
+#ifdef XFLASH
+    bool xflash_success = xflash_init();
 	uint8_t optiboot_status = 1;
-	if (w25x20cl_success)
+	if (xflash_success)
 	{
-		optiboot_status = optiboot_w25x20cl_enter();
+		optiboot_status = optiboot_xflash_enter();
 #if (LANG_MODE != 0) //secondary language support
         update_sec_lang_from_external_flash();
 #endif //(LANG_MODE != 0)
 	}
 	else
 	{
-	    w25x20cl_err_msg();
+	    xflash_err_msg();
 	}
 #else
-	const bool w25x20cl_success = true;
-#endif //W25X20CL
+	const bool xflash_success = true;
+#endif //XFLASH
 
 
 	setup_killpin();
@@ -1062,11 +1054,6 @@ void setup()
 		selectedSerialPort = 1;
 #endif //HAS_SECOND_SERIAL_PORT
 		MYSERIAL.begin(BAUDRATE);
-#ifdef TMC2130
-		//increased extruder current (PFW363)
-		tmc2130_current_h[E_AXIS] = 36;
-		tmc2130_current_r[E_AXIS] = 36;
-#endif //TMC2130
 #ifdef FILAMENT_SENSOR
 		//disabled filament autoload (PFW360)
 		fsensor_autoload_set(false);
@@ -1076,6 +1063,14 @@ void setup()
                eeprom_update_byte((unsigned char *)EEPROM_FAN_CHECK_ENABLED,true);
 	}
 
+#ifdef TMC2130
+    if( FarmOrUserECool() ){
+		//increased extruder current (PFW363)
+		tmc2130_current_h[E_AXIS] = TMC2130_CURRENTS_FARM;
+		tmc2130_current_r[E_AXIS] = TMC2130_CURRENTS_FARM;
+    }
+#endif //TMC2130
+
     //saved EEPROM SN is not valid. Try to retrieve it.
     //SN is valid only if it is NULL terminated. Any other character means either uninitialized or corrupted
     if (eeprom_read_byte((uint8_t*)EEPROM_PRUSA_SN + 19))
@@ -1091,7 +1086,7 @@ void setup()
     }
 
 
-#ifndef W25X20CL
+#ifndef XFLASH
 	SERIAL_PROTOCOLLNPGM("start");
 #else
 	if ((optiboot_status != 0) || (selectedSerialPort != 0))
@@ -1172,7 +1167,7 @@ void setup()
 #undef LT_PRINT_TEST
 
 #if 0
-		w25x20cl_rd_data(0x25ba, (uint8_t*)&block_buffer, 1024);
+		xflash_rd_data(0x25ba, (uint8_t*)&block_buffer, 1024);
 		for (uint16_t i = 0; i < 1024; i++)
 		{
 			if ((i % 16) == 0) printf_P(_n("%04x:"), 0x25ba+i);
@@ -1269,11 +1264,11 @@ void setup()
 
 	tp_init();    // Initialize temperature loop
 
-	if (w25x20cl_success) lcd_splash(); // we need to do this again, because tp_init() kills lcd
+	if (xflash_success) lcd_splash(); // we need to do this again, because tp_init() kills lcd
 	else
 	{
-	    w25x20cl_err_msg();
-	    puts_P(_n("W25X20CL not responding."));
+	    xflash_err_msg();
+	    puts_P(_n("XFLASH not responding."));
 	}
 #ifdef EXTRUDER_ALTFAN_DETECT
 	SERIAL_ECHORPGM(_n("Extruder fan type: "));
@@ -1345,13 +1340,12 @@ void setup()
 #endif //TMC2130_VARIABLE_RESOLUTION
 
 #endif //TMC2130
-
 	st_init();    // Initialize stepper, this enables interrupts!
   
 #ifdef TMC2130
 	tmc2130_mode = silentMode?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 	update_mode_profile();
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool() ));
 #endif //TMC2130
 #ifdef PSU_Delta
      init_force_z();                              // ! important for correct Z-axis initialization
@@ -1458,16 +1452,16 @@ void setup()
 
 #if (LANG_MODE != 0) //secondary language support
 
-#ifdef DEBUG_W25X20CL
-	W25X20CL_SPI_ENTER();
+#ifdef DEBUG_XFLASH
+	XFLASH_SPI_ENTER();
 	uint8_t uid[8]; // 64bit unique id
-	w25x20cl_rd_uid(uid);
-	puts_P(_n("W25X20CL UID="));
+	xflash_rd_uid(uid);
+	puts_P(_n("XFLASH UID="));
 	for (uint8_t i = 0; i < 8; i ++)
 		printf_P(PSTR("%02hhx"), uid[i]);
 	putchar('\n');
 	list_sec_lang_from_external_flash();
-#endif //DEBUG_W25X20CL
+#endif //DEBUG_XFLASH
 
 //	lang_reset();
 	if (!lang_select(eeprom_read_byte((uint8_t*)EEPROM_LANG)))
@@ -2564,7 +2558,7 @@ void force_high_power_mode(bool start_high_power_section) {
 		cli();
 		tmc2130_mode = (start_high_power_section == true) ? TMC2130_MODE_NORMAL : TMC2130_MODE_SILENT;
 		update_mode_profile();
-		tmc2130_init();
+		tmc2130_init(TMCInitParams(FarmOrUserECool()));
     // We may have missed a stepper timer interrupt due to the time spent in the tmc2130_init() routine.
     // Be safe than sorry, reset the stepper timer before re-enabling interrupts.
     st_reset_timer();
@@ -2668,16 +2662,15 @@ static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, lon
 static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, long home_y_value, bool home_z_axis, long home_z_value, bool without_mbl)
 #endif //TMC2130
 {
+	// Flag for the display update routine and to disable the print cancelation during homing.
 	st_synchronize();
+	homing_flag = true;
 
 #if 0
 	SERIAL_ECHOPGM("G28, initial ");  print_world_coordinates();
 	SERIAL_ECHOPGM("G28, initial ");  print_physical_coordinates();
 #endif
 
-	// Flag for the display update routine and to disable the print cancelation during homing.
-	homing_flag = true;
-
 	// Which axes should be homed?
 	bool home_x = home_x_axis;
 	bool home_y = home_y_axis;
@@ -2920,24 +2913,21 @@ static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, lon
 
 #if (defined(MESH_BED_LEVELING) && !defined(MK1BP))
 	if (home_x_axis || home_y_axis || without_mbl || home_z_axis)
-		{
+    {
       if (! home_z && mbl_was_active) {
         // Re-enable the mesh bed leveling if only the X and Y axes were re-homed.
         mbl.active = true;
         // and re-adjust the current logical Z axis with the bed leveling offset applicable at the current XY position.
         current_position[Z_AXIS] -= mbl.get_z(st_get_position_mm(X_AXIS), st_get_position_mm(Y_AXIS));
       }
-		}
-	else
-		{
-			st_synchronize();
-			homing_flag = false;
-	  }
+    }
 #endif
 
 	  if (farm_mode) { prusa_statistics(20); };
 
+      st_synchronize();
 	  homing_flag = false;
+
 #if 0
       SERIAL_ECHOPGM("G28, final ");  print_world_coordinates();
       SERIAL_ECHOPGM("G28, final ");  print_physical_coordinates();
@@ -2954,6 +2944,422 @@ static void gcode_G28(bool home_x_axis, bool home_y_axis, bool home_z_axis)
 #endif //TMC2130
 }
 
+
+// G80 - Automatic mesh bed leveling
+static void gcode_G80()
+{
+    st_synchronize();
+    if (waiting_inside_plan_buffer_line_print_aborted)
+        return;
+
+    mesh_bed_leveling_flag = true;
+#ifndef PINDA_THERMISTOR
+    static bool run = false; // thermistor-less PINDA temperature compensation is running
+#endif // ndef PINDA_THERMISTOR
+
+#ifdef SUPPORT_VERBOSITY
+    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();
+    }
+#endif //SUPPORT_VERBOSITY
+    // Firstly check if we know where we are
+    if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) {
+        // We don't know where we are! HOME!
+        // Push the commands to the front of the message queue in the reverse order!
+        // There shall be always enough space reserved for these commands.
+        repeatcommand_front(); // repeat G80 with all its parameters
+        enquecommand_front_P(G28W0);
+        return;
+    }
+
+    uint8_t nMeasPoints = MESH_MEAS_NUM_X_POINTS;
+    if (code_seen('N')) {
+        nMeasPoints = code_value_uint8();
+        if (nMeasPoints != 7) {
+            nMeasPoints = 3;
+        }
+    }
+    else {
+        nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
+    }
+
+    uint8_t nProbeRetry = 3;
+    if (code_seen('R')) {
+        nProbeRetry = code_value_uint8();
+        if (nProbeRetry > 10) {
+            nProbeRetry = 10;
+        }
+    }
+    else {
+        nProbeRetry = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
+    }
+    bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
+
+#ifndef PINDA_THERMISTOR
+    if (run == false && temp_cal_active == true && calibration_status_pinda() == true && target_temperature_bed >= 50)
+    {
+        temp_compensation_start();
+        run = true;
+        repeatcommand_front(); // repeat G80 with all its parameters
+        enquecommand_front_P(G28W0);
+        break;
+    }
+    run = false;
+#endif //PINDA_THERMISTOR
+    // Save custom message state, set a new custom message state to display: Calibrating point 9.
+    CustomMsg custom_message_type_old = custom_message_type;
+    unsigned int custom_message_state_old = custom_message_state;
+    custom_message_type = CustomMsg::MeshBedLeveling;
+    custom_message_state = (nMeasPoints * nMeasPoints) + 10;
+    lcd_update(1);
+
+    mbl.reset(); //reset mesh bed leveling
+
+    // Reset baby stepping to zero, if the babystepping has already been loaded before.
+    babystep_undo();
+
+    // Cycle through all points and probe them
+    // First move up. During this first movement, the babystepping will be reverted.
+    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
+    // The move to the first calibration point.
+    current_position[X_AXIS] = BED_X0;
+    current_position[Y_AXIS] = BED_Y0;
+
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1)
+    {
+        bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+        clamped ? SERIAL_PROTOCOLPGM("First calibration point clamped.\n") : SERIAL_PROTOCOLPGM("No clamping for first calibration point.\n");
+    }
+#else //SUPPORT_VERBOSITY
+    world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+#endif //SUPPORT_VERBOSITY
+
+    int XY_AXIS_FEEDRATE = homing_feedrate[X_AXIS] / 20;
+    plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
+    // Wait until the move is finished.
+    st_synchronize();
+    if (waiting_inside_plan_buffer_line_print_aborted)
+    {
+        custom_message_type = custom_message_type_old;
+        custom_message_state = custom_message_state_old;
+        return;
+    }
+
+    uint8_t mesh_point = 0; //index number of calibration point
+    int Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 40;
+    bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point)
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1) {
+        has_z ? SERIAL_PROTOCOLPGM("Z jitter data from Z cal. valid.\n") : SERIAL_PROTOCOLPGM("Z jitter data from Z cal. not valid.\n");
+    }
+#endif // SUPPORT_VERBOSITY
+    int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100
+    while (mesh_point != nMeasPoints * nMeasPoints) {
+        // Get coords of a measuring point.
+        uint8_t ix = mesh_point % nMeasPoints; // from 0 to MESH_NUM_X_POINTS - 1
+        uint8_t iy = mesh_point / nMeasPoints;
+        /*if (!mbl_point_measurement_valid(ix, iy, nMeasPoints, true)) {
+          printf_P(PSTR("Skipping point [%d;%d] \n"), ix, iy);
+          custom_message_state--;
+          mesh_point++;
+          continue; //skip
+          }*/
+        if (iy & 1) ix = (nMeasPoints - 1) - ix; // Zig zag
+        if (nMeasPoints == 7) //if we have 7x7 mesh, compare with Z-calibration for points which are in 3x3 mesh
+        {
+            has_z = ((ix % 3 == 0) && (iy % 3 == 0)) && is_bed_z_jitter_data_valid();
+        }
+        float z0 = 0.f;
+        if (has_z && (mesh_point > 0)) {
+            uint16_t z_offset_u = 0;
+            if (nMeasPoints == 7) {
+                z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((ix/3) + iy - 1)));
+            }
+            else {
+                z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * (ix + iy * 3 - 1)));
+            }
+            z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
+#ifdef SUPPORT_VERBOSITY
+            if (verbosity_level >= 1) {
+                printf_P(PSTR("Bed leveling, point: %d, calibration Z stored in eeprom: %d, calibration z: %f \n"), mesh_point, z_offset_u, z0);
+            }
+#endif // SUPPORT_VERBOSITY
+        }
+
+        // Move Z up to MESH_HOME_Z_SEARCH.
+        if((ix == 0) && (iy == 0)) current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+        else current_position[Z_AXIS] += 2.f / nMeasPoints; //use relative movement from Z coordinate where PINDa triggered on previous point. This makes calibration faster.
+        float init_z_bckp = current_position[Z_AXIS];
+        plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+        st_synchronize();
+
+        // Move to XY position of the sensor point.
+        current_position[X_AXIS] = BED_X(ix, nMeasPoints);
+        current_position[Y_AXIS] = BED_Y(iy, nMeasPoints);
+
+        //printf_P(PSTR("[%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
+
+
+#ifdef SUPPORT_VERBOSITY
+        if (verbosity_level >= 1) {
+            bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+            SERIAL_PROTOCOL(mesh_point);
+            clamped ? SERIAL_PROTOCOLPGM(": xy clamped.\n") : SERIAL_PROTOCOLPGM(": no xy clamping\n");
+        }
+#else //SUPPORT_VERBOSITY
+        world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+#endif // SUPPORT_VERBOSITY
+
+        //printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
+        plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
+        st_synchronize();
+        if (waiting_inside_plan_buffer_line_print_aborted)
+        {
+            custom_message_type = custom_message_type_old;
+            custom_message_state = custom_message_state_old;
+            return;
+        }
+
+        // Go down until endstop is hit
+        const float Z_CALIBRATION_THRESHOLD = 1.f;
+        if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
+            printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+            break;
+        }
+        if (init_z_bckp - current_position[Z_AXIS] < 0.1f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases.
+            //printf_P(PSTR("Another attempt! Current Z position: %f\n"), current_position[Z_AXIS]);
+            current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+            plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+            st_synchronize();
+
+            if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
+                printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+                break;
+            }
+            if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) {
+                puts_P(PSTR("Bed leveling failed. Sensor disconnected or cable broken."));
+                break;
+            }
+        }
+        if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point
+            puts_P(PSTR("Bed leveling failed. Sensor triggered too high."));
+            break;
+        }
+#ifdef SUPPORT_VERBOSITY
+        if (verbosity_level >= 10) {
+            SERIAL_ECHOPGM("X: ");
+            MYSERIAL.print(current_position[X_AXIS], 5);
+            SERIAL_ECHOLNPGM("");
+            SERIAL_ECHOPGM("Y: ");
+            MYSERIAL.print(current_position[Y_AXIS], 5);
+            SERIAL_PROTOCOLPGM("\n");
+        }
+#endif // SUPPORT_VERBOSITY
+        float offset_z = 0;
+
+#ifdef PINDA_THERMISTOR
+        offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda);
+#endif //PINDA_THERMISTOR
+        //			#ifdef SUPPORT_VERBOSITY
+        /*			if (verbosity_level >= 1)
+                    {
+                    SERIAL_ECHOPGM("mesh bed leveling: ");
+                    MYSERIAL.print(current_position[Z_AXIS], 5);
+                    SERIAL_ECHOPGM(" offset: ");
+                    MYSERIAL.print(offset_z, 5);
+                    SERIAL_ECHOLNPGM("");
+                    }*/
+        //			#endif // SUPPORT_VERBOSITY
+        mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z;
+
+        custom_message_state--;
+        mesh_point++;
+        lcd_update(1);
+    }
+    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 20) {
+        SERIAL_ECHOLNPGM("Mesh bed leveling while loop finished.");
+        SERIAL_ECHOLNPGM("MESH_HOME_Z_SEARCH: ");
+        MYSERIAL.print(current_position[Z_AXIS], 5);
+    }
+#endif // SUPPORT_VERBOSITY
+    plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+    st_synchronize();
+    if (mesh_point != nMeasPoints * nMeasPoints) {
+        Sound_MakeSound(e_SOUND_TYPE_StandardAlert);
+        bool bState;
+        do   {                             // repeat until Z-leveling o.k.
+            lcd_display_message_fullscreen_P(_i("Some problem encountered, Z-leveling enforced ..."));
+#ifdef TMC2130
+            lcd_wait_for_click_delay(MSG_BED_LEVELING_FAILED_TIMEOUT);
+            calibrate_z_auto();           // Z-leveling (X-assembly stay up!!!)
+#else // TMC2130
+            lcd_wait_for_click_delay(0);  // ~ no timeout
+            lcd_calibrate_z_end_stop_manual(true); // Z-leveling (X-assembly stay up!!!)
+#endif // TMC2130
+            // ~ Z-homing (can not be used "G28", because X & Y-homing would have been done before (Z-homing))
+            bState=enable_z_endstop(false);
+            current_position[Z_AXIS] -= 1;
+            plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
+            st_synchronize();
+            enable_z_endstop(true);
+#ifdef TMC2130
+            tmc2130_home_enter(Z_AXIS_MASK);
+#endif // TMC2130
+            current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+            plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
+            st_synchronize();
+#ifdef TMC2130
+            tmc2130_home_exit();
+#endif // TMC2130
+            enable_z_endstop(bState);
+        } while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
+        //               plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
+
+        custom_message_type = custom_message_type_old;
+        custom_message_state = custom_message_state_old;
+        lcd_update_enable(true);           // display / status-line recovery
+        gcode_G28(true, true, true);       // X & Y & Z-homing (must be after individual Z-homing (problem with spool-holder)!)
+        repeatcommand_front();             // re-run (i.e. of "G80")
+        return;
+    }
+    clean_up_after_endstop_move(l_feedmultiply);
+    //		SERIAL_ECHOLNPGM("clean up finished ");
+
+#ifndef PINDA_THERMISTOR
+    if(temp_cal_active == true && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation
+#endif
+    babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated.
+    //		SERIAL_ECHOLNPGM("babystep applied");
+    bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1;
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1) {
+        eeprom_bed_correction_valid ? SERIAL_PROTOCOLPGM("Bed correction data valid\n") : SERIAL_PROTOCOLPGM("Bed correction data not valid\n");
+    }
+#endif // SUPPORT_VERBOSITY
+
+    for (uint8_t i = 0; i < 4; ++i) {
+        unsigned char codes[4] = { 'L', 'R', 'F', 'B' };
+        long correction = 0;
+        if (code_seen(codes[i]))
+            correction = code_value_long();
+        else if (eeprom_bed_correction_valid) {
+            unsigned char *addr = (i < 2) ?
+                                  ((i == 0) ? (unsigned char*)EEPROM_BED_CORRECTION_LEFT : (unsigned char*)EEPROM_BED_CORRECTION_RIGHT) :
+                                  ((i == 2) ? (unsigned char*)EEPROM_BED_CORRECTION_FRONT : (unsigned char*)EEPROM_BED_CORRECTION_REAR);
+            correction = eeprom_read_int8(addr);
+        }
+        if (correction == 0)
+            continue;
+
+        if (labs(correction) > BED_ADJUSTMENT_UM_MAX) {
+            SERIAL_ERROR_START;
+            SERIAL_ECHOPGM("Excessive bed leveling correction: ");
+            SERIAL_ECHO(correction);
+            SERIAL_ECHOLNPGM(" microns");
+        }
+        else {
+            float offset = float(correction) * 0.001f;
+            switch (i) {
+            case 0:
+                for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                    for (uint8_t col = 0; col < nMeasPoints - 1; ++col) {
+                        mbl.z_values[row][col] += offset * (nMeasPoints - 1 - col) / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 1:
+                for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                    for (uint8_t col = 1; col < nMeasPoints; ++col) {
+                        mbl.z_values[row][col] += offset * col / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 2:
+                for (uint8_t col = 0; col < nMeasPoints; ++col) {
+                    for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                        mbl.z_values[row][col] += offset * (nMeasPoints - 1 - row) / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 3:
+                for (uint8_t col = 0; col < nMeasPoints; ++col) {
+                    for (uint8_t row = 1; row < nMeasPoints; ++row) {
+                        mbl.z_values[row][col] += offset * row / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            }
+        }
+    }
+    //		SERIAL_ECHOLNPGM("Bed leveling correction finished");
+    if (nMeasPoints == 3) {
+        mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
+    }
+    /*
+      SERIAL_PROTOCOLPGM("Num X,Y: ");
+      SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
+      SERIAL_PROTOCOLPGM(",");
+      SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
+      SERIAL_PROTOCOLPGM("\nZ search height: ");
+      SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
+      SERIAL_PROTOCOLLNPGM("\nMeasured points:");
+      for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
+      for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
+      SERIAL_PROTOCOLPGM("  ");
+      SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
+      }
+      SERIAL_PROTOCOLPGM("\n");
+      }
+    */
+    if (nMeasPoints == 7 && magnet_elimination) {
+        mbl_interpolation(nMeasPoints);
+    }
+    /*
+      SERIAL_PROTOCOLPGM("Num X,Y: ");
+      SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
+      SERIAL_PROTOCOLPGM(",");
+      SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
+      SERIAL_PROTOCOLPGM("\nZ search height: ");
+      SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
+      SERIAL_PROTOCOLLNPGM("\nMeasured points:");
+      for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
+      for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
+      SERIAL_PROTOCOLPGM("  ");
+      SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
+      }
+      SERIAL_PROTOCOLPGM("\n");
+      }
+    */
+    //		SERIAL_ECHOLNPGM("Upsample finished");
+    mbl.active = 1; //activate mesh bed leveling
+    //		SERIAL_ECHOLNPGM("Mesh bed leveling activated");
+    go_home_with_z_lift();
+    //		SERIAL_ECHOLNPGM("Go home finished");
+    //unretract (after PINDA preheat retraction)
+    if ((degHotend(active_extruder) > EXTRUDE_MINTEMP) && eeprom_read_byte((unsigned char *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() && (target_temperature_bed >= 50)) {
+        current_position[E_AXIS] += default_retraction;
+        plan_buffer_line_curposXYZE(400);
+    }
+    KEEPALIVE_STATE(NOT_BUSY);
+    // Restore custom message state
+    lcd_setstatuspgm(_T(WELCOME_MSG));
+    custom_message_type = custom_message_type_old;
+    custom_message_state = custom_message_state_old;
+    mesh_bed_run_from_menu = false;
+    lcd_update(2);
+
+    st_synchronize();
+    mesh_bed_leveling_flag = false;
+}
+
+
 void adjust_bed_reset()
 {
 	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID, 1);
@@ -3218,12 +3624,12 @@ static T gcode_M600_filament_change_z_shift()
 #ifdef FILAMENTCHANGE_ZADD
 	static_assert(Z_MAX_POS < (255 - FILAMENTCHANGE_ZADD), "Z-range too high, change the T type from uint8_t to uint16_t");
 	// avoid floating point arithmetics when not necessary - results in shorter code
+	T z_shift = T(FILAMENTCHANGE_ZADD); // always move above printout
 	T ztmp = T( current_position[Z_AXIS] );
-	T z_shift = 0;
-	if(ztmp < T(25)){
-		z_shift = T(25) - ztmp; // make sure to be at least 25mm above the heat bed
-	} 
-	return z_shift + T(FILAMENTCHANGE_ZADD); // always move above printout
+	if((ztmp + z_shift) < T(MIN_Z_FOR_SWAP)){
+		z_shift = T(MIN_Z_FOR_SWAP) - ztmp; // make sure to be at least 25mm above the heat bed
+	}
+	return z_shift;
 #else
 	return T(0);
 #endif
@@ -3272,7 +3678,7 @@ static void gcode_M600(bool automatic, float x_position, float y_position, float
 
     // Unload filament
     if (mmu_enabled) extr_unload();	//unload just current filament for multimaterial printers (used also in M702)
-    else unload_filament(); //unload filament for single material (used also in M702)
+    else unload_filament(true); //unload filament for single material (used also in M702)
     //finish moves
     st_synchronize();
 
@@ -3559,6 +3965,8 @@ static void extended_capabilities_report()
 #endif //FANCHECK and TACH_0 or TACH_1
     // AUTOREPORT_POSITION (M114)
     cap_line(PSTR("AUTOREPORT_POSITION"), ENABLED(AUTO_REPORT));
+    // EXTENDED_M20 (support for L and T parameters)
+    cap_line(PSTR("EXTENDED_M20"), 1);
     //@todo Update RepRap cap
 }
 #endif //EXTENDED_CAPABILITIES_REPORT
@@ -4013,10 +4421,10 @@ void process_commands()
 		}
 		else if (code_seen_P(PSTR("RESET"))) { // PRUSA RESET
 #ifdef WATCHDOG
-#if defined(W25X20CL) && defined(BOOTAPP)
+#if defined(XFLASH) && defined(BOOTAPP)
             boot_app_magic = BOOT_APP_MAGIC;
             boot_app_flags = BOOT_APP_FLG_RUN;
-#endif //defined(W25X20CL) && defined(BOOTAPP)
+#endif //defined(XFLASH) && defined(BOOTAPP)
             softReset();
 #elif defined(BOOTAPP) //this is a safety precaution. This is because the new bootloader turns off the heaters, but the old one doesn't. The watchdog should be used most of the time.
             asm volatile("jmp 0x3E000");
@@ -4530,9 +4938,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
       gcode_G28(home_x, home_x_value, home_y, home_y_value, home_z, home_z_value, without_mbl);
 #endif //TMC2130
       if ((home_x || home_y || without_mbl || home_z) == false) {
-         // Push the commands to the front of the message queue in the reverse order!
-         // There shall be always enough space reserved for these commands.
-         goto case_G80;
+          gcode_G80();
       }
       break;
     }
@@ -4745,8 +5151,9 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     */
     case 30: 
         {
-            homing_flag = true;
             st_synchronize();
+            homing_flag = true;
+
             // TODO: make sure the bed_level_rotation_matrix is identity or the planner will get set incorectly
             int l_feedmultiply = setup_for_endstop_move();
 
@@ -4849,7 +5256,9 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
             }
         }
 
+        st_synchronize();
         homing_flag = true; // keep homing on to avoid babystepping while the LCD is enabled
+
         lcd_update_enable(true);
         KEEPALIVE_STATE(NOT_BUSY); //no need to print busy messages as we print current temperatures periodicaly
         SERIAL_ECHOLNPGM("PINDA probe calibration start");
@@ -5084,404 +5493,11 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 	*  v Y-axis
 	*/
 
-	case 80:
-
+	case 80: {
 #ifdef MK1BP
 		break;
 #endif //MK1BP
-	case_G80:
-	{
-		mesh_bed_leveling_flag = true;
-#ifndef PINDA_THERMISTOR
-        static bool run = false; // thermistor-less PINDA temperature compensation is running
-#endif // ndef PINDA_THERMISTOR
-
-#ifdef SUPPORT_VERBOSITY
-		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();
-		}
-#endif //SUPPORT_VERBOSITY
-		// Firstly check if we know where we are
-		if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) {
-			// We don't know where we are! HOME!
-			// Push the commands to the front of the message queue in the reverse order!
-			// There shall be always enough space reserved for these commands.
-			repeatcommand_front(); // repeat G80 with all its parameters
-			enquecommand_front_P(G28W0);
-			break;
-		} 
-		
-		uint8_t nMeasPoints = MESH_MEAS_NUM_X_POINTS;
-		if (code_seen('N')) {
-			nMeasPoints = code_value_uint8();
-			if (nMeasPoints != 7) {
-				nMeasPoints = 3;
-			}
-		}
-		else {
-			nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
-		}
-
-		uint8_t nProbeRetry = 3;
-		if (code_seen('R')) {
-			nProbeRetry = code_value_uint8();
-			if (nProbeRetry > 10) {
-				nProbeRetry = 10;
-			}
-		}
-		else {
-			nProbeRetry = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
-		}
-		bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
-		
-#ifndef PINDA_THERMISTOR
-		if (run == false && temp_cal_active == true && calibration_status_pinda() == true && target_temperature_bed >= 50)
-		{
-			temp_compensation_start();
-			run = true;
-			repeatcommand_front(); // repeat G80 with all its parameters
-			enquecommand_front_P(G28W0);
-			break;
-		}
-        run = false;
-#endif //PINDA_THERMISTOR
-		// Save custom message state, set a new custom message state to display: Calibrating point 9.
-		CustomMsg custom_message_type_old = custom_message_type;
-		unsigned int custom_message_state_old = custom_message_state;
-		custom_message_type = CustomMsg::MeshBedLeveling;
-		custom_message_state = (nMeasPoints * nMeasPoints) + 10;
-		lcd_update(1);
-
-		mbl.reset(); //reset mesh bed leveling
-
-		// Reset baby stepping to zero, if the babystepping has already been loaded before.
-		babystep_undo();
-
-		// Cycle through all points and probe them
-		// First move up. During this first movement, the babystepping will be reverted.
-		current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-		plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
-		// The move to the first calibration point.
-		current_position[X_AXIS] = BED_X0;
-		current_position[Y_AXIS] = BED_Y0;
-
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1)
-		{
-		    bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-			clamped ? SERIAL_PROTOCOLPGM("First calibration point clamped.\n") : SERIAL_PROTOCOLPGM("No clamping for first calibration point.\n");
-		}
-		#else //SUPPORT_VERBOSITY
-			world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-		#endif //SUPPORT_VERBOSITY
-
-		plan_buffer_line_curposXYZE(homing_feedrate[X_AXIS] / 30);
-		// Wait until the move is finished.
-		st_synchronize();
-
-		uint8_t mesh_point = 0; //index number of calibration point
-
-		int XY_AXIS_FEEDRATE = homing_feedrate[X_AXIS] / 20;
-		int Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 40;
-		bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point)
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1) {
-			has_z ? SERIAL_PROTOCOLPGM("Z jitter data from Z cal. valid.\n") : SERIAL_PROTOCOLPGM("Z jitter data from Z cal. not valid.\n");
-		}
-		#endif // SUPPORT_VERBOSITY
-		int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100
-		while (mesh_point != nMeasPoints * nMeasPoints) {
-			// Get coords of a measuring point.
-			uint8_t ix = mesh_point % nMeasPoints; // from 0 to MESH_NUM_X_POINTS - 1
-			uint8_t iy = mesh_point / nMeasPoints;
-			/*if (!mbl_point_measurement_valid(ix, iy, nMeasPoints, true)) {
-				printf_P(PSTR("Skipping point [%d;%d] \n"), ix, iy);
-				custom_message_state--;
-				mesh_point++;
-				continue; //skip
-			}*/
-			if (iy & 1) ix = (nMeasPoints - 1) - ix; // Zig zag
-			if (nMeasPoints == 7) //if we have 7x7 mesh, compare with Z-calibration for points which are in 3x3 mesh
-			{
-				has_z = ((ix % 3 == 0) && (iy % 3 == 0)) && is_bed_z_jitter_data_valid(); 
-			}
-			float z0 = 0.f;
-			if (has_z && (mesh_point > 0)) {
-				uint16_t z_offset_u = 0;
-				if (nMeasPoints == 7) {
-					z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((ix/3) + iy - 1)));
-				}
-				else {
-					z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * (ix + iy * 3 - 1)));
-				}
-				z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
-				#ifdef SUPPORT_VERBOSITY
-				if (verbosity_level >= 1) {
-					printf_P(PSTR("Bed leveling, point: %d, calibration Z stored in eeprom: %d, calibration z: %f \n"), mesh_point, z_offset_u, z0);
-				}
-				#endif // SUPPORT_VERBOSITY
-			}
-
-			// Move Z up to MESH_HOME_Z_SEARCH.
-			if((ix == 0) && (iy == 0)) current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-			else current_position[Z_AXIS] += 2.f / nMeasPoints; //use relative movement from Z coordinate where PINDa triggered on previous point. This makes calibration faster.
-			float init_z_bckp = current_position[Z_AXIS];
-			plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-			st_synchronize();
-
-			// Move to XY position of the sensor point.
-			current_position[X_AXIS] = BED_X(ix, nMeasPoints);
-			current_position[Y_AXIS] = BED_Y(iy, nMeasPoints);
-
-			//printf_P(PSTR("[%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
-
-			
-			#ifdef SUPPORT_VERBOSITY
-			if (verbosity_level >= 1) {
-				bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-				SERIAL_PROTOCOL(mesh_point);
-				clamped ? SERIAL_PROTOCOLPGM(": xy clamped.\n") : SERIAL_PROTOCOLPGM(": no xy clamping\n");
-			}
-			#else //SUPPORT_VERBOSITY
-				world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-			#endif // SUPPORT_VERBOSITY
-
-			//printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
-			plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
-			st_synchronize();
-
-			// Go down until endstop is hit
-			const float Z_CALIBRATION_THRESHOLD = 1.f;
-			if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point  
-				printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-				break;
-			}
-			if (init_z_bckp - current_position[Z_AXIS] < 0.1f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases.
-				//printf_P(PSTR("Another attempt! Current Z position: %f\n"), current_position[Z_AXIS]);
-				current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-				plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-				st_synchronize();
-
-				if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point  
-					printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-					break;
-				}
-				if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) {
-					puts_P(PSTR("Bed leveling failed. Sensor disconnected or cable broken."));
-					break;
-				}
-			}
-			if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point
-				puts_P(PSTR("Bed leveling failed. Sensor triggered too high."));
-				break;
-			}
-			#ifdef SUPPORT_VERBOSITY
-			if (verbosity_level >= 10) {
-				SERIAL_ECHOPGM("X: ");
-				MYSERIAL.print(current_position[X_AXIS], 5);
-				SERIAL_ECHOLNPGM("");
-				SERIAL_ECHOPGM("Y: ");
-				MYSERIAL.print(current_position[Y_AXIS], 5);
-				SERIAL_PROTOCOLPGM("\n");
-			}
-			#endif // SUPPORT_VERBOSITY
-			float offset_z = 0;
-
-#ifdef PINDA_THERMISTOR
-			offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda);
-#endif //PINDA_THERMISTOR
-//			#ifdef SUPPORT_VERBOSITY
-/*			if (verbosity_level >= 1)
-			{
-				SERIAL_ECHOPGM("mesh bed leveling: ");
-				MYSERIAL.print(current_position[Z_AXIS], 5);
-				SERIAL_ECHOPGM(" offset: ");
-				MYSERIAL.print(offset_z, 5);
-				SERIAL_ECHOLNPGM("");
-			}*/
-//			#endif // SUPPORT_VERBOSITY
-			mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z;
-
-			custom_message_state--;
-			mesh_point++;
-			lcd_update(1);
-		}
-		current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 20) {
-			SERIAL_ECHOLNPGM("Mesh bed leveling while loop finished.");
-			SERIAL_ECHOLNPGM("MESH_HOME_Z_SEARCH: ");
-			MYSERIAL.print(current_position[Z_AXIS], 5);
-		}
-		#endif // SUPPORT_VERBOSITY
-		plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-		st_synchronize();
-		if (mesh_point != nMeasPoints * nMeasPoints) {
-               Sound_MakeSound(e_SOUND_TYPE_StandardAlert);
-               bool bState;
-               do   {                             // repeat until Z-leveling o.k.
-                    lcd_display_message_fullscreen_P(_i("Some problem encountered, Z-leveling enforced ..."));
-#ifdef TMC2130
-                    lcd_wait_for_click_delay(MSG_BED_LEVELING_FAILED_TIMEOUT);
-                    calibrate_z_auto();           // Z-leveling (X-assembly stay up!!!)
-#else // TMC2130
-                    lcd_wait_for_click_delay(0);  // ~ no timeout
-                    lcd_calibrate_z_end_stop_manual(true); // Z-leveling (X-assembly stay up!!!)
-#endif // TMC2130
-                    // ~ Z-homing (can not be used "G28", because X & Y-homing would have been done before (Z-homing))
-                    bState=enable_z_endstop(false);
-                    current_position[Z_AXIS] -= 1;
-                    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
-                    st_synchronize();
-                    enable_z_endstop(true);
-#ifdef TMC2130
-                    tmc2130_home_enter(Z_AXIS_MASK);
-#endif // TMC2130
-                    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-                    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
-                    st_synchronize();
-#ifdef TMC2130
-                    tmc2130_home_exit();
-#endif // TMC2130
-                    enable_z_endstop(bState);
-                    } while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
-//               plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
-               custom_message_type=CustomMsg::Status; // display / status-line recovery
-               lcd_update_enable(true);           // display / status-line recovery
-               gcode_G28(true, true, true);       // X & Y & Z-homing (must be after individual Z-homing (problem with spool-holder)!)
-               repeatcommand_front();             // re-run (i.e. of "G80")
-               break;
-		}
-		clean_up_after_endstop_move(l_feedmultiply);
-//		SERIAL_ECHOLNPGM("clean up finished ");
-
-#ifndef PINDA_THERMISTOR
-		if(temp_cal_active == true && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation
-#endif
-		babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated.
-//		SERIAL_ECHOLNPGM("babystep applied");
-		bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1;
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1) {
-			eeprom_bed_correction_valid ? SERIAL_PROTOCOLPGM("Bed correction data valid\n") : SERIAL_PROTOCOLPGM("Bed correction data not valid\n");
-		}
-		#endif // SUPPORT_VERBOSITY
-
-		for (uint8_t i = 0; i < 4; ++i) {
-			unsigned char codes[4] = { 'L', 'R', 'F', 'B' };
-			long correction = 0;
-			if (code_seen(codes[i]))
-				correction = code_value_long();
-			else if (eeprom_bed_correction_valid) {
-				unsigned char *addr = (i < 2) ?
-					((i == 0) ? (unsigned char*)EEPROM_BED_CORRECTION_LEFT : (unsigned char*)EEPROM_BED_CORRECTION_RIGHT) :
-					((i == 2) ? (unsigned char*)EEPROM_BED_CORRECTION_FRONT : (unsigned char*)EEPROM_BED_CORRECTION_REAR);
-				correction = eeprom_read_int8(addr);
-			}
-			if (correction == 0)
-				continue;
-			
-			if (labs(correction) > BED_ADJUSTMENT_UM_MAX) {
-				SERIAL_ERROR_START;
-				SERIAL_ECHOPGM("Excessive bed leveling correction: ");
-				SERIAL_ECHO(correction);
-				SERIAL_ECHOLNPGM(" microns");
-			}
-			else {
-				float offset = float(correction) * 0.001f;
-				switch (i) {
-				case 0:
-					for (uint8_t row = 0; row < nMeasPoints; ++row) {						
-						for (uint8_t col = 0; col < nMeasPoints - 1; ++col) {
-							mbl.z_values[row][col] += offset * (nMeasPoints - 1 - col) / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 1:
-					for (uint8_t row = 0; row < nMeasPoints; ++row) {					
-						for (uint8_t col = 1; col < nMeasPoints; ++col) {
-							mbl.z_values[row][col] += offset * col / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 2:
-					for (uint8_t col = 0; col < nMeasPoints; ++col) {						
-						for (uint8_t row = 0; row < nMeasPoints; ++row) {
-							mbl.z_values[row][col] += offset * (nMeasPoints - 1 - row) / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 3:
-					for (uint8_t col = 0; col < nMeasPoints; ++col) {						
-						for (uint8_t row = 1; row < nMeasPoints; ++row) {
-							mbl.z_values[row][col] += offset * row / (nMeasPoints - 1);
-						}
-					}
-					break;
-				}
-			}
-		}
-//		SERIAL_ECHOLNPGM("Bed leveling correction finished");
-		if (nMeasPoints == 3) {
-			mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
-		}
-/*
-		        SERIAL_PROTOCOLPGM("Num X,Y: ");
-                SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
-                SERIAL_PROTOCOLPGM(",");
-                SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
-                SERIAL_PROTOCOLPGM("\nZ search height: ");
-                SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
-                SERIAL_PROTOCOLLNPGM("\nMeasured points:");
-                for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
-                    for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
-                        SERIAL_PROTOCOLPGM("  ");
-                        SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
-                    }
-                    SERIAL_PROTOCOLPGM("\n");
-                }
-*/
-		if (nMeasPoints == 7 && magnet_elimination) {
-			mbl_interpolation(nMeasPoints);
-		}
-/*
-		        SERIAL_PROTOCOLPGM("Num X,Y: ");
-                SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
-                SERIAL_PROTOCOLPGM(",");
-                SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
-                SERIAL_PROTOCOLPGM("\nZ search height: ");
-                SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
-                SERIAL_PROTOCOLLNPGM("\nMeasured points:");
-                for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
-                    for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
-                        SERIAL_PROTOCOLPGM("  ");
-                        SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
-                    }
-                    SERIAL_PROTOCOLPGM("\n");
-                }
-*/
-//		SERIAL_ECHOLNPGM("Upsample finished");
-		mbl.active = 1; //activate mesh bed leveling
-//		SERIAL_ECHOLNPGM("Mesh bed leveling activated");
-		go_home_with_z_lift();
-//		SERIAL_ECHOLNPGM("Go home finished");
-		//unretract (after PINDA preheat retraction)
-		if ((degHotend(active_extruder) > EXTRUDE_MINTEMP) && eeprom_read_byte((unsigned char *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() && (target_temperature_bed >= 50)) {
-			current_position[E_AXIS] += default_retraction;
-			plan_buffer_line_curposXYZE(400);
-		}
-		KEEPALIVE_STATE(NOT_BUSY);
-		// Restore custom message state
-		lcd_setstatuspgm(_T(WELCOME_MSG));
-		custom_message_type = custom_message_type_old;
-		custom_message_state = custom_message_state_old;
-		mesh_bed_leveling_flag = false;
-		mesh_bed_run_from_menu = false;
-		lcd_update(2);
-		
+        gcode_G80();
 	}
 	break;
 
@@ -5727,16 +5743,17 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 	### M20 - SD Card file list <a href="https://reprap.org/wiki/G-code#M20:_List_SD_card">M20: List SD card</a>
     #### Usage
     
-        M20 [ L ]
+        M20 [ L | T ]
     #### Parameters
-    - `L` - Reports ling filenames instead of just short filenames. Requires host software parsing.
+    - `T` - Report timestamps as well. The value is one uint32_t encoded as hex. Requires host software parsing (Cap:EXTENDED_M20).
+    - `L` - Reports long filenames instead of just short filenames. Requires host software parsing (Cap:EXTENDED_M20).
     */
     case 20:
       KEEPALIVE_STATE(NOT_BUSY); // do not send busy messages during listing. Inhibits the output of manage_heater()
       SERIAL_PROTOCOLLNRPGM(_N("Begin file list"));////MSG_BEGIN_FILE_LIST
-      card.ls(code_seen('L'));
+      card.ls(CardReader::ls_param(code_seen('L'), code_seen('T')));
       SERIAL_PROTOCOLLNRPGM(_N("End file list"));////MSG_END_FILE_LIST
-      break;
+    break;
 
     /*!
 	### M21 - Init SD card <a href="https://reprap.org/wiki/G-code#M21:_Initialize_SD_card">M21: Initialize SD card</a>
@@ -6389,14 +6406,19 @@ Sigma_Exit:
         if(code_seen('R')) print_time_remaining_normal = code_value();
         if(code_seen('Q')) print_percent_done_silent = code_value();
         if(code_seen('S')) print_time_remaining_silent = code_value();
-        if(code_seen('C')) print_time_to_change_normal = code_value();
-        if(code_seen('D')) print_time_to_change_silent = code_value();
-
-    {
-        const char* _msg_mode_done_remain = _N("%S MODE: Percent done: %d; print time remaining in mins: %d; Change in mins: %d\n");
-        printf_P(_msg_mode_done_remain, _N("NORMAL"), int(print_percent_done_normal), print_time_remaining_normal, print_time_to_change_normal);
-        printf_P(_msg_mode_done_remain, _N("SILENT"), int(print_percent_done_silent), print_time_remaining_silent, print_time_to_change_silent);
-    }
+        if(code_seen('C')){
+            float print_time_to_change_normal_f = code_value_float();
+            print_time_to_change_normal = ( print_time_to_change_normal_f <= 0 ) ? PRINT_TIME_REMAINING_INIT : print_time_to_change_normal_f;
+        }
+        if(code_seen('D')){
+            float print_time_to_change_silent_f = code_value_float();
+            print_time_to_change_silent = ( print_time_to_change_silent_f <= 0 ) ? PRINT_TIME_REMAINING_INIT : print_time_to_change_silent_f;
+        }
+        {
+            const char* _msg_mode_done_remain = _N("%S MODE: Percent done: %hhd; print time remaining in mins: %d; Change in mins: %d\n");
+            printf_P(_msg_mode_done_remain, _N("NORMAL"), int8_t(print_percent_done_normal), print_time_remaining_normal, print_time_to_change_normal);
+            printf_P(_msg_mode_done_remain, _N("SILENT"), int8_t(print_percent_done_silent), print_time_remaining_silent, print_time_to_change_silent);
+        }
         break;
     }
     /*!
@@ -6967,19 +6989,21 @@ Sigma_Exit:
       lcd_setstatus(strchr_pointer + 5);
       break;*/
 
+#ifdef M120_M121_ENABLED
     /*!
-	### M120 - Enable endstops <a href="https://reprap.org/wiki/G-code#M120:_Enable_endstop_detection">M120: Enable endstop detection</a>
+    ### M120 - Enable endstops <a href="https://reprap.org/wiki/G-code#M120:_Enable_endstop_detection">M120: Enable endstop detection</a>
     */
     case 120:
       enable_endstops(true) ;
       break;
 
     /*!
-	### M121 - Disable endstops <a href="https://reprap.org/wiki/G-code#M121:_Disable_endstop_detection">M121: Disable endstop detection</a>
+    ### M121 - Disable endstops <a href="https://reprap.org/wiki/G-code#M121:_Disable_endstop_detection">M121: Disable endstop detection</a>
     */
     case 121:
       enable_endstops(false) ;
       break;
+#endif //M120_M121_ENABLED
 
     /*!
 	### M119 - Get endstop states <a href="https://reprap.org/wiki/G-code#M119:_Get_Endstop_Status">M119: Get Endstop Status</a>
@@ -8403,6 +8427,7 @@ Sigma_Exit:
     /*!
 	### M907 - Set digital trimpot motor current in mA using axis codes <a href="https://reprap.org/wiki/G-code#M907:_Set_digital_trimpot_motor">M907: Set digital trimpot motor</a>
 	Set digital trimpot motor current using axis codes (X, Y, Z, E, B, S).
+    M907 has no effect when the experimental Extruder motor current scaling mode is active (that applies to farm printing as well)
 	#### Usage
     
         M907 [ X | Y | Z | E | B | S ]
@@ -8419,16 +8444,20 @@ Sigma_Exit:
     {
 #ifdef TMC2130
         // See tmc2130_cur2val() for translation to 0 .. 63 range
-        for (int i = 0; i < NUM_AXIS; i++)
-			if(code_seen(axis_codes[i]))
-			{
-				long cur_mA = code_value_long();
-				uint8_t val = tmc2130_cur2val(cur_mA);
-				tmc2130_set_current_h(i, val);
-				tmc2130_set_current_r(i, val);
-				//if (i == E_AXIS) printf_P(PSTR("E-axis current=%ldmA\n"), cur_mA);
-			}
-
+        for (uint_least8_t i = 0; i < NUM_AXIS; i++){
+            if(code_seen(axis_codes[i])){
+                if( i == E_AXIS && FarmOrUserECool() ){
+                    SERIAL_ECHORPGM(eMotorCurrentScalingEnabled);
+                    SERIAL_ECHOLNPGM(", M907 E ignored");
+                    continue;
+                }
+                long cur_mA = code_value_long();
+                uint8_t val = tmc2130_cur2val(cur_mA);
+                tmc2130_set_current_h(i, val);
+                tmc2130_set_current_r(i, val);
+                //if (i == E_AXIS) printf_P(PSTR("E-axis current=%ldmA\n"), cur_mA);
+            }
+        }
 #else //TMC2130
       #if defined(DIGIPOTSS_PIN) && DIGIPOTSS_PIN > -1
         for(int i=0;i<NUM_AXIS;i++) if(code_seen(axis_codes[i])) st_current_set(i,code_value());
@@ -8653,7 +8682,7 @@ Sigma_Exit:
     case 350: 
     {
 	#ifdef TMC2130
-		for (int i=0; i<NUM_AXIS; i++) 
+		for (uint_least8_t i=0; i<NUM_AXIS; i++) 
 		{
 			if(code_seen(axis_codes[i]))
 			{
@@ -10844,8 +10873,9 @@ void uvlo_()
     }
 
     // save the global state at planning time
+    bool pos_invalid = XY_NO_RESTORE_FLAG;
     uint16_t feedrate_bckp;
-    if (current_block)
+    if (current_block && !pos_invalid)
     {
         memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
         feedrate_bckp = current_block->gcode_feedrate;
@@ -10923,8 +10953,13 @@ void uvlo_()
     eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps);
 
     // Store the current position.
-    eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]);
-    eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]);
+    if (pos_invalid)
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), X_COORD_INVALID);
+    else
+    {
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]);
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]);
+    }
 
     // Store the current feed rate, temperatures, fan speed and extruder multipliers (flow rates)
 	eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDRATE, feedrate_bckp);
@@ -11182,11 +11217,6 @@ bool recover_machine_state_after_power_panic()
   // Recover last E axis position
   current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E));
 
-  memcpy(destination, current_position, sizeof(destination));
-
-  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
-  print_world_coordinates();
-
   // 3) Initialize the logical to physical coordinate system transformation.
   world2machine_initialize();
 //  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
@@ -11198,7 +11228,11 @@ bool recover_machine_state_after_power_panic()
 
   // 5) Set the physical positions from the logical positions using the world2machine transformation
   // This is only done to inizialize Z/E axes with physical locations, since X/Y are unknown.
+  clamp_to_software_endstops(current_position);
+  memcpy(destination, current_position, sizeof(destination));
   plan_set_position_curposXYZE();
+  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
+  print_world_coordinates();
 
   // 6) Power up the Z motors, mark their positions as known.
   axis_known_position[Z_AXIS] = true;
@@ -11235,7 +11269,7 @@ void restore_print_from_eeprom(bool mbl_was_active) {
 	int feedrate_rec;
 	int feedmultiply_rec;
 	uint8_t fan_speed_rec;
-	char cmd[30];
+	char cmd[48];
 	char filename[13];
 	uint8_t depth = 0;
 	char dir_name[9];
@@ -11276,10 +11310,13 @@ void restore_print_from_eeprom(bool mbl_was_active) {
 
     // Move to the XY print position in logical coordinates, where the print has been killed, but
     // without shifting Z along the way. This requires performing the move without mbl.
-	sprintf_P(cmd, PSTR("G1 X%f Y%f F3000"),
-              eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0)),
-              eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4)));
-	enquecommand(cmd);
+    float pos_x = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0));
+    float pos_y = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4));
+    if (pos_x != X_COORD_INVALID)
+    {
+        sprintf_P(cmd, PSTR("G1 X%f Y%f F3000"), pos_x, pos_y);
+        enquecommand(cmd);
+    }
 
     // Enable MBL and switch to logical positioning
     if (mbl_was_active)
@@ -11459,7 +11496,8 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
 #endif
 
   // save the global state at planning time
-  if (current_block)
+  bool pos_invalid = XY_NO_RESTORE_FLAG;
+  if (current_block && !pos_invalid)
   {
       memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
       saved_feedrate2 = current_block->gcode_feedrate;
@@ -11471,7 +11509,10 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
   }
 
 	planner_abort_hard(); //abort printing
+
 	memcpy(saved_pos, current_position, sizeof(saved_pos));
+    if (pos_invalid) saved_pos[X_AXIS] = X_COORD_INVALID;
+
     saved_feedmultiply2 = feedmultiply; //save feedmultiply
 	saved_active_extruder = active_extruder; //save active_extruder
 	saved_extruder_temperature = degTargetHotend(active_extruder);
@@ -11565,6 +11606,13 @@ void restore_print_from_ram_and_continue(float e_move)
     fans_check_enabled = false;
   #endif
 
+    // do not restore XY for commands that do not require that
+    if (saved_pos[X_AXIS] == X_COORD_INVALID)
+    {
+        saved_pos[X_AXIS] = current_position[X_AXIS];
+        saved_pos[Y_AXIS] = current_position[Y_AXIS];
+    }
+
 	//first move print head in XY to the saved position:
 	plan_buffer_line(saved_pos[X_AXIS], saved_pos[Y_AXIS], current_position[Z_AXIS], saved_pos[E_AXIS] - e_move, homing_feedrate[Z_AXIS]/13, active_extruder);
 	//then move Z
@@ -11920,7 +11968,7 @@ void disable_force_z()
 #ifdef TMC2130
     tmc2130_mode=TMC2130_MODE_SILENT;
     update_mode_profile();
-    tmc2130_init(true);
+    tmc2130_init(TMCInitParams(true, FarmOrUserECool()));
 #endif // TMC2130
 }
 
@@ -11934,7 +11982,7 @@ bEnableForce_z=true;
 #ifdef TMC2130
 tmc2130_mode=eeprom_read_byte((uint8_t*)EEPROM_SILENT)?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 update_mode_profile();
-tmc2130_init(true);
+tmc2130_init(TMCInitParams(true, FarmOrUserECool()));
 #endif // TMC2130
 
 WRITE(Z_ENABLE_PIN,Z_ENABLE_ON);                  // slightly redundant ;-p

+ 8 - 5
Firmware/SdFile.cpp

@@ -178,14 +178,17 @@ eof_or_fail:
 }
 
 bool SdFile::gfEnsureBlock(){
-    if ( vol_->cacheRawBlock(gfBlock, SdVolume::CACHE_FOR_READ)){
+    // this comparison is heavy-weight, especially when there is another one inside cacheRawBlock
+    // but it is necessary to avoid computing of terminateOfs if not needed
+    if( gfBlock != vol_->cacheBlockNumber_ ){
+        if ( ! vol_->cacheRawBlock(gfBlock, SdVolume::CACHE_FOR_READ)){
+            return false;
+        }
         // terminate with a '\n'
-        const uint16_t terminateOfs = fileSize_ - gfOffset;
+        const uint32_t terminateOfs = fileSize_ - gfOffset;
         vol_->cache()->data[ terminateOfs < 512 ? terminateOfs : 512 ] = '\n';
-        return true;
-    } else {
-        return false;
     }
+    return true;
 }
 
 bool SdFile::gfComputeNextFileBlock() {

+ 35 - 35
Firmware/cardreader.cpp

@@ -61,10 +61,9 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
 +*   LS_Count           - Add +1 to nrFiles for every file within the parent
 +*   LS_GetFilename     - Get the filename of the file indexed by nrFiles
 +*   LS_SerialPrint     - Print the full path and size of each file to serial output
-+*   LS_SerialPrint_LFN - Print the full path, long filename and size of each file to serial output
 +*/
 
-void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
+void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/, LsAction lsAction, ls_param lsParams) {
 	static uint8_t recursionCnt = 0;
 	// RAII incrementer for the recursionCnt
 	class _incrementer
@@ -80,8 +79,12 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		for (position = parent.curPosition(); parent.readDir(p, longFilename) > 0; position = parent.curPosition()) {
 			if (recursionCnt > MAX_DIR_DEPTH)
 				return;
-			else if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) { // If the entry is a directory and the action is LS_SerialPrint
-				
+			uint8_t pn0 = p.name[0];
+			if (pn0 == DIR_NAME_FREE) break;
+			if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
+			if (longFilename[0] == '.') continue;
+			if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
+			if (DIR_IS_SUBDIR(&p) && lsAction == LS_SerialPrint) { // If the entry is a directory and the action is LS_SerialPrint
 				// Get the short name for the item, which we know is a folder
 				char lfilename[FILENAME_LENGTH];
 				createFilename(lfilename, p);
@@ -99,29 +102,22 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 				// Get a new directory object using the full path
 				// and dive recursively into it.
 				
-				if (lsAction == LS_SerialPrint_LFN)
+				if (lsParams.LFN)
 					printf_P(PSTR("DIR_ENTER: %s \"%s\"\n"), path, longFilename[0] ? longFilename : lfilename);
 				
 				SdFile dir;
 				if (!dir.open(parent, lfilename, O_READ)) {
-					if (lsAction == LS_SerialPrint || lsAction == LS_SerialPrint_LFN) {
-						//SERIAL_ECHO_START();
-						//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
-						//SERIAL_ECHOLN(lfilename);
-					}
+					//SERIAL_ECHO_START();
+					//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
+					//SERIAL_ECHOLN(lfilename);
 				}
-				lsDive(path, dir);
+				lsDive(path, dir, NULL, lsAction, lsParams);
 				// close() is done automatically by destructor of SdFile
 				
-				if (lsAction == LS_SerialPrint_LFN)
+				if (lsParams.LFN)
 					puts_P(PSTR("DIR_EXIT"));
 			}
 			else {
-				uint8_t pn0 = p.name[0];
-				if (pn0 == DIR_NAME_FREE) break;
-				if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
-				if (longFilename[0] == '.') continue;
-				if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
 				filenameIsDir = DIR_IS_SUBDIR(&p);
 				if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
 				switch (lsAction) {
@@ -129,17 +125,29 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						nrFiles++;
 						break;
 					
-					case LS_SerialPrint_LFN:
 					case LS_SerialPrint:
 						createFilename(filename, p);
 						SERIAL_PROTOCOL(prepend);
 						SERIAL_PROTOCOL(filename);
+						
 						MYSERIAL.write(' ');
+						SERIAL_PROTOCOL(p.fileSize);
+						
+						if (lsParams.timestamp)
+						{
+							crmodDate = p.lastWriteDate;
+							crmodTime = p.lastWriteTime;
+							if( crmodDate < p.creationDate || ( crmodDate == p.creationDate && crmodTime < p.creationTime ) ){
+								crmodDate = p.creationDate;
+								crmodTime = p.creationTime;
+							}
+							printf_P(PSTR(" %#lx"), ((uint32_t)crmodDate << 16) | crmodTime);
+						}
 						
-						if (lsAction == LS_SerialPrint_LFN)
-							printf_P(PSTR("\"%s\" "), LONGEST_FILENAME);
+						if (lsParams.LFN)
+							printf_P(PSTR(" \"%s\""), LONGEST_FILENAME);
 						
-						SERIAL_PROTOCOLLN(p.fileSize);
+						SERIAL_PROTOCOLLN();
 						manage_heater();
 						break;
 				
@@ -181,14 +189,10 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		} // while readDir
 }
 
-void CardReader::ls(bool printLFN)
+void CardReader::ls(ls_param params)
 {
-  lsAction = printLFN ? LS_SerialPrint_LFN : LS_SerialPrint;
-  //if(lsAction==LS_Count)
-  //nrFiles=0;
-
   root.rewind();
-  lsDive("",root);
+  lsDive("",root, NULL, LS_SerialPrint, params);
 }
 
 
@@ -695,38 +699,34 @@ void CardReader::closefile(bool store_location)
 void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/)
 {
   curDir=&workDir;
-  lsAction=LS_GetFilename;
   nrFiles=nr;
   curDir->rewind();
-  lsDive("",*curDir,match);
+  lsDive("",*curDir,match, LS_GetFilename);
   
 }
 
 void CardReader::getfilename_simple(uint32_t position, const char * const match/*=NULL*/)
 {
 	curDir = &workDir;
-	lsAction = LS_GetFilename;
 	nrFiles = 0;
 	curDir->seekSet(position);
-	lsDive("", *curDir, match);
+	lsDive("", *curDir, match, LS_GetFilename);
 }
 
 void CardReader::getfilename_next(uint32_t position, const char * const match/*=NULL*/)
 {
 	curDir = &workDir;
-	lsAction = LS_GetFilename;
 	nrFiles = 1;
 	curDir->seekSet(position);
-	lsDive("", *curDir, match);
+	lsDive("", *curDir, match, LS_GetFilename);
 }
 
 uint16_t CardReader::getnrfilenames()
 {
   curDir=&workDir;
-  lsAction=LS_Count;
   nrFiles=0;
   curDir->rewind();
-  lsDive("",*curDir);
+  lsDive("",*curDir, NULL, LS_Count);
   //SERIAL_ECHOLN(nrFiles);
   return nrFiles;
 }

+ 22 - 7
Firmware/cardreader.h

@@ -8,12 +8,25 @@
 #define MAX_DIR_DEPTH 6
 
 #include "SdFile.h"
-enum LsAction {LS_SerialPrint,LS_SerialPrint_LFN,LS_Count,LS_GetFilename};
 class CardReader
 {
 public:
   CardReader();
   
+  enum LsAction : uint8_t
+  {
+    LS_SerialPrint,
+    LS_Count,
+    LS_GetFilename,
+  };
+  struct ls_param
+  {
+    bool LFN : 1;
+    bool timestamp : 1;
+    inline ls_param():LFN(0), timestamp(0) { }
+    inline ls_param(bool LFN, bool timestamp):LFN(LFN), timestamp(timestamp) { }
+  } __attribute__((packed));
+  
   void initsd();
   void write_command(char *buf);
   void write_command_no_newline(char *buf);
@@ -43,7 +56,7 @@ public:
   uint16_t getWorkDirDepth();
   
 
-  void ls(bool printLFN);
+  void ls(ls_param params);
   bool chdir(const char * relpath, bool doPresort);
   void updir();
   void setroot(bool doPresort);
@@ -59,9 +72,12 @@ public:
 
   FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
   bool eof() { return sdpos>=filesize; }
-  // There may be a potential performance problem - when the comment reading fails, sdpos points to the last correctly read character.
-  // However, repeated reading (e.g. after power panic) the comment will be read again - it should survive correctly, it will just take a few moments to skip
-  FORCE_INLINE int16_t getFilteredGcodeChar() {  sdpos = file.curPosition();return (int16_t)file.readFilteredGcode();};
+  FORCE_INLINE int16_t getFilteredGcodeChar()
+  {
+      int16_t c = (int16_t)file.readFilteredGcode();
+      sdpos = file.curPosition();
+      return c;
+  };
   void setIndex(long index) {sdpos = index;file.seekSetFilteredGcode(index);};
   FORCE_INLINE uint8_t percentDone(){if(!isFileOpen()) return 0; if(filesize) return sdpos/((filesize+99)/100); else return 0;};
   FORCE_INLINE char* getWorkDirName(){workDir.getFilename(filename);return filename;};
@@ -119,12 +135,11 @@ private:
 
   bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
   
-  LsAction lsAction; //stored for recursion.
   int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
   char* diveDirName;
 
   bool diveSubfolder (const char *&fileName);
-  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL);
+  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL, LsAction lsAction = LS_GetFilename, ls_param lsParams = ls_param());
 #ifdef SDCARD_SORT_ALPHA
   void flush_presort();
 #endif

+ 2 - 2
Firmware/cmdqueue.cpp

@@ -600,7 +600,7 @@ void get_command()
       }
       // The new command buffer could be updated non-atomically, because it is not yet considered
       // to be inside the active queue.
-      sd_count.value = (card.get_sdpos()+1) - sdpos_atomic;
+      sd_count.value = card.get_sdpos() - sdpos_atomic;
       cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_SDCARD;
       cmdbuffer[bufindw+1] = sd_count.lohi.lo;
       cmdbuffer[bufindw+2] = sd_count.lohi.hi;
@@ -625,7 +625,7 @@ void get_command()
       // or a 115200 Bd serial line receive interrupt, which will not trigger faster than 12kHz.
       ++ buflen;
       bufindw += len;
-      sdpos_atomic = card.get_sdpos()+1;
+      sdpos_atomic = card.get_sdpos();
       if (bufindw == sizeof(cmdbuffer))
           bufindw = 0;
       sei();

+ 0 - 8
Firmware/config.h

@@ -54,14 +54,6 @@
 #define TMC2130_SPCR           SPI_SPCR(TMC2130_SPI_RATE, 1, 1, 1, 0)
 #define TMC2130_SPSR           SPI_SPSR(TMC2130_SPI_RATE)
 
-//W25X20CL configuration
-//pinout:
-#define W25X20CL_PIN_CS        32
-//spi:
-#define W25X20CL_SPI_RATE      0 // fosc/4 = 4MHz
-#define W25X20CL_SPCR          SPI_SPCR(W25X20CL_SPI_RATE, 1, 1, 1, 0)
-#define W25X20CL_SPSR          SPI_SPSR(W25X20CL_SPI_RATE)
-
 //LANG - Multi-language support
 //#define LANG_MODE              0 // primary language only
 #define LANG_MODE              1 // sec. language support

+ 12 - 34
Firmware/eeprom.cpp

@@ -97,6 +97,9 @@ void eeprom_init()
 #ifdef PINDA_TEMP_COMP
 if (eeprom_read_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_PINDA_TEMP_COMPENSATION, 0);
 #endif //PINDA_TEMP_COMP
+
+	if (eeprom_read_dword((uint32_t*)EEPROM_JOB_ID) == EEPROM_EMPTY_VALUE32)
+		eeprom_update_dword((uint32_t*)EEPROM_JOB_ID, 0);
 }
 
 //! @brief Get default sheet name for index
@@ -107,10 +110,10 @@ if (eeprom_read_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION) == 0xff) eeprom_u
 //! | 1     | Smooth2   |
 //! | 2     | Textur1   |
 //! | 3     | Textur2   |
-//! | 4     | Custom1   |
-//! | 5     | Custom2   |
-//! | 6     | Custom3   |
-//! | 7     | Custom4   |
+//! | 4     | Satin 1   |
+//! | 5     | Satin 2   |
+//! | 6     | Custom1   |
+//! | 7     | Custom2   |
 //!
 //! @param[in] index
 //! @param[out] sheetName
@@ -126,41 +129,16 @@ void eeprom_default_sheet_name(uint8_t index, SheetName &sheetName)
     {
         strcpy_P(sheetName.c, PSTR("Textur"));
     }
-    else
+    else if (index < 6)
     {
-        strcpy_P(sheetName.c, PSTR("Custom"));
+        strcpy_P(sheetName.c, PSTR("Satin "));
     }
-
-    switch (index)
+    else
     {
-    case 0:
-        sheetName.c[6] = '1';
-        break;
-    case 1:
-        sheetName.c[6] = '2';
-        break;
-    case 2:
-        sheetName.c[6] = '1';
-        break;
-    case 3:
-        sheetName.c[6] = '2';
-        break;
-    case 4:
-        sheetName.c[6] = '1';
-        break;
-    case 5:
-        sheetName.c[6] = '2';
-        break;
-    case 6:
-        sheetName.c[6] = '3';
-        break;
-    case 7:
-        sheetName.c[6] = '4';
-        break;
-    default:
-        break;
+        strcpy_P(sheetName.c, PSTR("Custom"));
     }
 
+    sheetName.c[6] = '0' + ((index % 2)+1);
     sheetName.c[7] = '\0';
 }
 

+ 14 - 5
Firmware/eeprom.h

@@ -276,19 +276,19 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0D71 3441		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 4th sheet - Z offset 								| ^				| D3 Ax0d71 C2	
 | 0x0D73 3443		| uint8		| ^										| 00h 0			| ffh 255				| 4th sheet - bed temp 								| ^				| D3 Ax0d73 C1	
 | 0x0D74 3444		| uint8		| ^										| 00h 0			| ffh 255				| 4th sheet - PINDA temp 							| ^				| D3 Ax0d74 C1	
-| 0x0D75 3445		| char		| _5th Sheet block_						| 437573746f6d31| ffffffffffffff		| 5th sheet - Name: 	_Custom1_					| ^				| D3 Ax0d75 C7
+| 0x0D75 3445		| char		| _5th Sheet block_						| 536174696e2031| ffffffffffffff		| 5th sheet - Name: 	_Satin 1_					| ^				| D3 Ax0d75 C7
 | 0x0D7C 3452		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 5th sheet - Z offset 								| ^				| D3 Ax0d7c C2	
 | 0x0D7E 3454		| uint8		| ^										| 00h 0			| ffh 255				| 5th sheet - bed temp 								| ^				| D3 Ax0d7e C1	
 | 0x0D7F 3455		| uint8		| ^										| 00h 0			| ffh 255				| 5th sheet - PINDA temp 							| ^				| D3 Ax0d7f C1	
-| 0x0D80 3456		| char		| _6th Sheet block_						| 437573746f6d32| ffffffffffffff		| 6th sheet - Name: 	_Custom2_					| ^				| D3 Ax0d80 C7
+| 0x0D80 3456		| char		| _6th Sheet block_						| 536174696e2032| ffffffffffffff		| 6th sheet - Name: 	_Satin 2_					| ^				| D3 Ax0d80 C7
 | 0x0D87 3463		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 6th sheet - Z offset 								| ^				| D3 Ax0d87 C2	
 | 0x0D89 3465		| uint8		| ^										| 00h 0			| ffh 255				| 6th sheet - bed temp 								| ^				| D3 Ax0d89 C1	
 | 0x0D8A 3466		| uint8		| ^										| 00h 0			| ffh 255				| 6th sheet - PINDA temp 							| ^				| D3 Ax0d8a C1	
-| 0x0D8B 3467		| char		| _7th Sheet block_						| 437573746f6d33| ffffffffffffff		| 7th sheet - Name: 	_Custom3_					| ^				| D3 Ax0d8b C7
+| 0x0D8B 3467		| char		| _7th Sheet block_						| 437573746f6d31| ffffffffffffff		| 7th sheet - Name: 	_Custom1_					| ^				| D3 Ax0d8b C7
 | 0x0D92 3474		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 7th sheet - Z offset 								| ^				| D3 Ax0d92 C2	
 | 0x0D94 3476		| uint8		| ^										| 00h 0			| ffh 255				| 7th sheet - bed temp 								| ^				| D3 Ax0d94 C1	
 | 0x0D95 3477		| uint8		| ^										| 00h 0			| ffh 255				| 7th sheet - PINDA temp 							| ^				| D3 Ax0d95 C1	
-| 0x0D96 3478		| char		| _8th Sheet block_						| 437573746f6d34| ffffffffffffff		| 8th sheet - Name: 	_Custom4_					| ^				| D3 Ax0d96 C7
+| 0x0D96 3478		| char		| _8th Sheet block_						| 437573746f6d32| ffffffffffffff		| 8th sheet - Name: 	_Custom2_					| ^				| D3 Ax0d96 C7
 | 0x0D9D 3485		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 8th sheet - Z offset 								| ^				| D3 Ax0d9d C2	
 | 0x0D9F 3487		| uint8		| ^										| 00h 0			| ffh 255				| 8th sheet - bed temp 								| ^				| D3 Ax0d9f C1	
 | 0x0DA0 3488		| uint8		| ^										| 00h 0			| ffh 255				| 8th sheet - PINDA temp 							| ^				| D3 Ax0da0 C1	
@@ -324,6 +324,9 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0D11 3345		| float		| EEPROM_UVLO_ACCELL                	| ???			| ff ff ff ffh			| Power panic saved normal acceleration     		| ???			| D3 Ax0d11 C4
 | 0x0D0D 3341		| float		| EEPROM_UVLO_RETRACT_ACCELL			| ???			| ff ff ff ffh			| Power panic saved retract acceleration     		| ???			| D3 Ax0d0d C4
 | 0x0D09 3337		| float		| EEPROM_UVLO_TRAVEL_ACCELL				| ???			| ff ff ff ffh			| Power panic saved travel acceleration     		| ???			| D3 Ax0d09 C4
+| 0x0D05 3333		| uint32_t	| EEPROM_JOB_ID							| ???			| 00 00 00 00h			| Job ID used by host software						| D3 only		| D3 Ax0d05 C4
+| 0x0D01 3329		| uint8_t	| EEPROM_ECOOL_ENABLE					| ffh 255		| ^						| Disable extruder motor scaling for non-farm print	| LCD menu		| D3 Ax0d01 FF
+| ^					| ^			| ^										| 2ah 42		| ^						| Enable extruder motor scaling for non-farm print	| ^				| D3 Ax0d01 42
 
 | Address begin		| Bit/Type 	| Name 									| Valid values	| Default/FactoryReset	| Description 										| Gcode/Function| Debug code
 | :--:				| :--: 		| :--: 									| :--:			| :--:					| :--:												| :--:			| :--:
@@ -337,6 +340,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 
 #define EEPROM_EMPTY_VALUE 0xFF
 #define EEPROM_EMPTY_VALUE16 0xFFFF
+#define EEPROM_EMPTY_VALUE32 0xFFFFFFFFl
 // The total size of the EEPROM is
 // 4096 for the Atmega2560
 #define EEPROM_TOP 4096
@@ -534,8 +538,12 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE);
 #define EEPROM_UVLO_RETRACT_ACCELL (EEPROM_UVLO_ACCELL-4) // float
 #define EEPROM_UVLO_TRAVEL_ACCELL (EEPROM_UVLO_RETRACT_ACCELL-4) // float
 
+#define EEPROM_JOB_ID (EEPROM_UVLO_TRAVEL_ACCELL-4) //uint32_t
+
+#define EEPROM_ECOOL_ENABLE (EEPROM_JOB_ID-1) // uint8_t
+
 //This is supposed to point to last item to allow EEPROM overrun check. Please update when adding new items.
-#define EEPROM_LAST_ITEM EEPROM_UVLO_TRAVEL_ACCELL
+#define EEPROM_LAST_ITEM EEPROM_ECOOL_ENABLE
 // !!!!!
 // !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!!
 // !!!!!
@@ -551,6 +559,7 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE);
 #define EEPROM_FIRMWARE_VERSION_MAJOR     FW_PRUSA3D_MAGIC_LEN
 // Magic string, indicating that the current or the previous firmware running was the Prusa3D firmware.
 #define EEPROM_FIRMWARE_PRUSA_MAGIC 0
+#define EEPROM_ECOOL_MAGIC_NUMBER 42
 
 #ifdef __cplusplus
 #include "ConfigurationStore.h"

+ 21 - 21
Firmware/language.c

@@ -7,9 +7,9 @@
 #include "Configuration.h"
 #include "pins.h"
 
-#ifdef W25X20CL
-#include "w25x20cl.h"
-#endif //W25X20CL
+#ifdef XFLASH
+#include "xflash.h"
+#endif //XFLASH
 
 // Currently active language selection.
 uint8_t lang_selected = 0;
@@ -54,7 +54,7 @@ uint8_t lang_select(uint8_t lang)
 		lang_table = 0;
 		lang_selected = lang;
 	}
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang_get_code(lang) == lang_get_code(LANG_ID_SEC)) lang = LANG_ID_SEC;
 	if (lang == LANG_ID_SEC) //current secondary language
 	{
@@ -68,7 +68,7 @@ uint8_t lang_select(uint8_t lang)
 				}
 		}
 	}
-#else //W25X20CL
+#else //XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t table = _SEC_LANG_TABLE;
@@ -82,7 +82,7 @@ uint8_t lang_select(uint8_t lang)
 				}
 		}
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	if (lang_selected == lang)
 	{
 		eeprom_update_byte((unsigned char*)EEPROM_LANG, lang_selected);
@@ -107,19 +107,19 @@ uint8_t lang_get_count()
 {
 	if (pgm_read_dword(((uint32_t*)(_PRI_LANG_SIGNATURE))) == 0xffffffff)
 		return 1; //signature not set - only primary language will be available
-#ifdef W25X20CL
-	W25X20CL_SPI_ENTER();
+#ifdef XFLASH
+	XFLASH_SPI_ENTER();
 	uint8_t count = 2; //count = 1+n (primary + secondary + all in xflash)
 	uint32_t addr = 0x00000; //start of xflash
 	lang_table_header_t header; //table header structure
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
 		if (header.magic != LANG_MAGIC) break; //break if magic not valid
 		addr += header.size; //calc address of next table
 		count++; //inc counter
 	}
-#else //W25X20CL
+#else //XFLASH
 	uint16_t table = _SEC_LANG_TABLE;
 	uint8_t count = 1; //count = 1 (primary)
 	while (pgm_read_dword(((uint32_t*)table)) == LANG_MAGIC) //magic valid
@@ -127,14 +127,14 @@ uint8_t lang_get_count()
 		table += pgm_read_word((uint16_t*)(table + 4));
 		count++;
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return count;
 }
 
 uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* offset)
 {
 	if (lang == LANG_ID_PRI) return 0; //primary lang not supported for this function
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
@@ -142,18 +142,18 @@ uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* off
 		if (offset) *offset = ui;
 		return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
 	}
-	W25X20CL_SPI_ENTER();
+	XFLASH_SPI_ENTER();
 	uint32_t addr = 0x00000; //start of xflash
 	lang--;
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)(header), sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)(header), sizeof(lang_table_header_t)); //read table header from xflash
 		if (header->magic != LANG_MAGIC) break; //break if not valid
 		if (offset) *offset = addr;
 		if (--lang == 0) return 1;
 		addr += header->size; //calc address of next table
 	}
-#else //W25X20CL
+#else //XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
@@ -161,32 +161,32 @@ uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* off
 		if (offset) *offset = ui;
 		return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return 0;
 }
 
 uint16_t lang_get_code(uint8_t lang)
 {
 	if (lang == LANG_ID_PRI) return LANG_CODE_EN; //primary lang = EN
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
 		if (pgm_read_dword(((uint32_t*)(ui + 0))) != LANG_MAGIC) return LANG_CODE_XX; //magic not valid
 		return pgm_read_word(((uint32_t*)(ui + 10))); //return lang code from progmem
 	}
-	W25X20CL_SPI_ENTER();
+	XFLASH_SPI_ENTER();
 	uint32_t addr = 0x00000; //start of xflash
 	lang_table_header_t header; //table header structure
 	lang--;
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
 		if (header.magic != LANG_MAGIC) break; //break if not valid
 		if (--lang == 0) return header.code;
 		addr += header.size; //calc address of next table
 	}
-#else //W25X20CL
+#else //XFLASH
 	uint16_t table = _SEC_LANG_TABLE;
 	uint8_t count = 1; //count = 1 (primary)
 	while (pgm_read_dword((uint32_t*)table) == LANG_MAGIC) //magic valid
@@ -195,7 +195,7 @@ uint16_t lang_get_code(uint8_t lang)
 		table += pgm_read_word((uint16_t*)(table + 4));
 		count++;
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return LANG_CODE_XX;
 }
 

+ 32 - 11
Firmware/mesh_bed_calibration.cpp

@@ -1,4 +1,3 @@
-#include "Marlin.h"
 #include "Configuration.h"
 #include "ConfigurationStore.h"
 #include "language.h"
@@ -1065,7 +1064,7 @@ error:
 }
 
 #ifdef NEW_XYZCAL
-bool xyzcal_find_bed_induction_sensor_point_xy();
+BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy();
 #endif //NEW_XYZCAL
 // Search around the current_position[X,Y],
 // look for the induction sensor response.
@@ -1081,7 +1080,7 @@ bool xyzcal_find_bed_induction_sensor_point_xy();
 #endif //HEATBED_V2
 
 #ifdef HEATBED_V2
-bool find_bed_induction_sensor_point_xy(int
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int
 #if !defined (NEW_XYZCAL) && defined (SUPPORT_VERBOSITY)
         verbosity_level
 #endif
@@ -1137,7 +1136,7 @@ bool find_bed_induction_sensor_point_xy(int
 
 		//        go_xyz(current_position[X_AXIS], current_position[Y_AXIS], MESH_HOME_Z_SEARCH, homing_feedrate[Z_AXIS]/60);
 		go_xyz(x0, y0, current_position[Z_AXIS], feedrate);
-		// Continously lower the Z axis.
+		// Continuously lower the Z axis.
 		endstops_hit_on_purpose();
 		enable_z_endstop(true);
 		bool direction = false;
@@ -1335,7 +1334,7 @@ bool find_bed_induction_sensor_point_xy(int
 #endif //NEW_XYZCAL
 }
 #else //HEATBED_V2
-bool find_bed_induction_sensor_point_xy(int verbosity_level)
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int verbosity_level)
 {
 #ifdef NEW_XYZCAL
 	return xyzcal_find_bed_induction_sensor_point_xy();
@@ -1531,7 +1530,9 @@ bool find_bed_induction_sensor_point_xy(int verbosity_level)
 	}
 
 	enable_z_endstop(false);
-	return found;
+    if (found)
+        return BED_SKEW_OFFSET_DETECTION_POINT_FOUND;
+    return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
 #endif //NEW_XYZCAL
 }
 
@@ -2238,9 +2239,15 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 
     // Collect the rear 2x3 points.
 	current_position[Z_AXIS] = MESH_HOME_Z_SEARCH + FIND_BED_INDUCTION_SENSOR_POINT_Z_STEP * iteration * 0.3;
-	for (int k = 0; k < 4; ++k) {
-		// Don't let the manage_inactivity() function remove power from the motors.
-		refresh_cmd_timeout();
+
+    /// Retry point scanning if a point with bad data appears.
+    /// Bad data could be cause by "cold" sensor.
+    /// This behavior vanishes after few point scans so retry will help.
+    for (int retries = 0; retries <= 1; ++retries) {
+        bool retry = false;
+        for (int k = 0; k < 4; ++k) {
+            // Don't let the manage_inactivity() function remove power from the motors.
+            refresh_cmd_timeout();
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
 		lcd_set_cursor(0, next_line);
 		lcd_print(k + 1);
@@ -2304,8 +2311,19 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 		if (verbosity_level >= 10)
 			delay_keep_alive(3000);
 		#endif // SUPPORT_VERBOSITY
-		if (!find_bed_induction_sensor_point_xy(verbosity_level))
-			return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
+
+        BedSkewOffsetDetectionResultType result;
+        result = find_bed_induction_sensor_point_xy(verbosity_level);
+        switch(result){
+            case BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND:
+                return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
+            case BED_SKEW_OFFSET_DETECTION_POINT_SCAN_FAILED:
+                retry = true;
+                break;
+            default:
+                break;
+        }
+
 #ifndef NEW_XYZCAL
 #ifndef HEATBED_V2
 		
@@ -2380,6 +2398,9 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 			}
 			#endif // SUPPORT_VERBOSITY
         }
+        if (!retry)
+            break;
+    }
         DBG(_n("All 4 calibration points found.\n"));
         delay_keep_alive(0); //manage_heater, reset watchdog, manage inactivity
 		

+ 9 - 8
Firmware/mesh_bed_calibration.h

@@ -1,5 +1,6 @@
-#ifndef MESH_BED_CALIBRATION_H
-#define MESH_BED_CALIBRATION_H
+#pragma once
+
+#include "Marlin.h"
 
 #define BED_ZERO_REF_X (- 22.f + X_PROBE_OFFSET_FROM_EXTRUDER) // -22 + 23 = 1
 #define BED_ZERO_REF_Y (- 0.6f + Y_PROBE_OFFSET_FROM_EXTRUDER + 4.f) // -0.6 + 5 + 4 = 8.4
@@ -145,11 +146,6 @@ inline bool world2machine_clamp(float &x, float &y)
         machine2world(tmpx, tmpy, x, y);
     return clamped;
 }
-
-bool find_bed_induction_sensor_point_z(float minimum_z = -10.f, uint8_t n_iter = 3, int verbosity_level = 0);
-bool find_bed_induction_sensor_point_xy(int verbosity_level = 0);
-void go_home_with_z_lift();
-
 /**
  * @brief Bed skew and offest detection result
  *
@@ -159,8 +155,10 @@ void go_home_with_z_lift();
 
 enum BedSkewOffsetDetectionResultType {
 	// Detection failed, some point was not found.
+	BED_SKEW_OFFSET_DETECTION_POINT_FOUND       =  0, //!< Point found
 	BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND   = -1, //!< Point not found.
 	BED_SKEW_OFFSET_DETECTION_FITTING_FAILED    = -2, //!< Fitting failed
+	BED_SKEW_OFFSET_DETECTION_POINT_SCAN_FAILED = -3, //!< Point scan failed, try again
 	
 	// Detection finished with success.
 	BED_SKEW_OFFSET_DETECTION_PERFECT 			= 0,  //!< Perfect.
@@ -168,6 +166,10 @@ enum BedSkewOffsetDetectionResultType {
 	BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		= 2   //!< Extremely skewed.
 };
 
+bool find_bed_induction_sensor_point_z(float minimum_z = -10.f, uint8_t n_iter = 3, int verbosity_level = 0);
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int verbosity_level = 0);
+void go_home_with_z_lift();
+
 extern BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level, uint8_t &too_far_mask);
 #ifndef NEW_XYZCAL
 extern BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level, uint8_t &too_far_mask);
@@ -213,4 +215,3 @@ extern void mbl_settings_init();
 
 extern bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bool zigzag);
 extern void mbl_interpolation(uint8_t meas_points);
-#endif /* MESH_BED_CALIBRATION_H */

+ 0 - 6
Firmware/optiboot_w25x20cl.h

@@ -1,6 +0,0 @@
-#ifndef OPTIBOOT_W25X20CL_H
-#define OPTIBOOT_W25X20CL_H
-
-extern uint8_t optiboot_w25x20cl_enter();
-
-#endif /* OPTIBOOT_W25X20CL_H */

+ 22 - 22
Firmware/optiboot_w25x20cl.cpp

@@ -4,7 +4,7 @@
 // Licence GLP 2 or later.
 
 #include "Marlin.h"
-#include "w25x20cl.h"
+#include "xflash.h"
 #include "stk500.h"
 #include "bootapp.h"
 #include <avr/wdt.h>
@@ -16,14 +16,14 @@ static unsigned const int __attribute__((section(".version")))
   optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
 
 #if 0
-#define W25X20CL_SIGNATURE_0 9
-#define W25X20CL_SIGNATURE_1 8
-#define W25X20CL_SIGNATURE_2 7
+#define XFLASH_SIGNATURE_0 9
+#define XFLASH_SIGNATURE_1 8
+#define XFLASH_SIGNATURE_2 7
 #else
 //FIXME this is a signature of ATmega2560!
-#define W25X20CL_SIGNATURE_0 0x1E
-#define W25X20CL_SIGNATURE_1 0x98
-#define W25X20CL_SIGNATURE_2 0x01
+#define XFLASH_SIGNATURE_0 0x1E
+#define XFLASH_SIGNATURE_1 0x98
+#define XFLASH_SIGNATURE_2 0x01
 #endif
 
 #define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)
@@ -78,7 +78,7 @@ extern struct block_t *block_buffer;
 //! @brief Enter an STK500 compatible Optiboot boot loader waiting for flashing the languages to an external flash memory.
 //! @return 1 if "start\n" was not sent. Optiboot was skipped
 //! @return 0 if "start\n" was sent. Optiboot ran normally. No need to send "start\n" in setup()
-uint8_t optiboot_w25x20cl_enter()
+uint8_t optiboot_xflash_enter()
 {
 // Make sure to check boot_app_magic as well. Since these bootapp flags are located right in the middle of the stack,
 // they can be unintentionally changed. As a workaround to the language upload problem, do not only check for one bit if it's set,
@@ -154,7 +154,7 @@ uint8_t optiboot_w25x20cl_enter()
   }
 
   spi_init();
-  w25x20cl_init();
+  xflash_init();
   wdt_disable();
 
   /* Forever loop: exits by causing WDT reset */
@@ -254,16 +254,16 @@ uint8_t optiboot_w25x20cl_enter()
         // During a single bootloader run, only erase a 64kB block once.
         // An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.
         if ((address == 0) && (pages_erased & (1 << (addr >> 16))) == 0) {
-          w25x20cl_wait_busy();
-          w25x20cl_enable_wr();
-          w25x20cl_block64_erase(addr);
+          xflash_wait_busy();
+          xflash_enable_wr();
+          xflash_block64_erase(addr);
           pages_erased |= (1 << (addr >> 16));
         }
-        w25x20cl_wait_busy();
-        w25x20cl_enable_wr();
-        w25x20cl_page_program(addr, buff, savelength);
-        w25x20cl_wait_busy();
-        w25x20cl_disable_wr();
+        xflash_wait_busy();
+        xflash_enable_wr();
+        xflash_page_program(addr, buff, savelength);
+        xflash_wait_busy();
+        xflash_disable_wr();
       }
     }
     /* Read memory block mode, length is big endian.  */
@@ -279,8 +279,8 @@ uint8_t optiboot_w25x20cl_enter()
       // Read the destination type. It should always be 'F' as flash. It is not checked.
       (void)getch();
       verifySpace();
-      w25x20cl_wait_busy();
-      w25x20cl_rd_data(addr, buff, length);
+      xflash_wait_busy();
+      xflash_rd_data(addr, buff, length);
       for (i = 0; i < length; ++ i)
         putch(buff[i]);
     }
@@ -288,9 +288,9 @@ uint8_t optiboot_w25x20cl_enter()
     else if(ch == STK_READ_SIGN) {
       // READ SIGN - return what Avrdude wants to hear
       verifySpace();
-      putch(W25X20CL_SIGNATURE_0);
-      putch(W25X20CL_SIGNATURE_1);
-      putch(W25X20CL_SIGNATURE_2);
+      putch(XFLASH_SIGNATURE_0);
+      putch(XFLASH_SIGNATURE_1);
+      putch(XFLASH_SIGNATURE_2);
     }
     else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
       // Adaboot no-wait mod

+ 6 - 0
Firmware/optiboot_xflash.h

@@ -0,0 +1,6 @@
+#ifndef OPTIBOOT_XFLASH_H
+#define OPTIBOOT_XFLASH_H
+
+extern uint8_t optiboot_xflash_enter();
+
+#endif /* OPTIBOOT_XFLASH_H */

+ 3 - 1
Firmware/pins_Einsy_1_0.h

@@ -15,9 +15,11 @@
 #define AMBIENT_THERMISTOR
 #define PINDA_THERMISTOR
 
-#define W25X20CL                 // external 256kB flash
+#define XFLASH                 // external 256kB flash
 #define BOOTAPP                  // bootloader support
 
+#define XFLASH_PIN_CS          32
+
 #define X_TMC2130_CS           41
 #define X_TMC2130_DIAG         64 // !!! changed from 40 (EINY03)
 #define X_STEP_PIN             37

+ 1 - 1
Firmware/stepper.cpp

@@ -1100,7 +1100,7 @@ FORCE_INLINE void advance_isr_scheduler() {
 void st_init()
 {
 #ifdef TMC2130
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
 #endif //TMC2130
 
   st_current_init(); //Initialize Digipot Motor Current

+ 20 - 11
Firmware/tmc2130.cpp

@@ -9,8 +9,6 @@
 #include "language.h"
 #include "spi.h"
 
-
-
 #define TMC2130_GCONF_NORMAL 0x00000000 // spreadCycle
 #define TMC2130_GCONF_SGSENS 0x00003180 // spreadCycle with stallguard (stall activates DIAG0 and DIAG1 [pushpull])
 #define TMC2130_GCONF_SILENT 0x00000004 // stealthChop
@@ -18,7 +16,6 @@
 
 //mode
 uint8_t tmc2130_mode = TMC2130_MODE_NORMAL;
-//holding currents
 uint8_t tmc2130_current_h[4] = TMC2130_CURRENTS_H;
 //running currents
 uint8_t tmc2130_current_r[4] = TMC2130_CURRENTS_R;
@@ -45,6 +42,8 @@ uint8_t tmc2130_sg_thr_home[4] = TMC2130_SG_THRS_HOME;
 
 uint8_t tmc2130_sg_homing_axes_mask = 0x00;
 
+const char eMotorCurrentScalingEnabled[] PROGMEM = "E-motor current scaling enabled";
+
 uint8_t tmc2130_sg_meassure = 0xff;
 uint32_t tmc2130_sg_meassure_cnt = 0;
 uint32_t tmc2130_sg_meassure_val = 0;
@@ -68,8 +67,9 @@ uint8_t tmc2130_sg_diag_mask = 0x00;
 uint8_t tmc2130_sg_crash = 0;
 uint16_t tmc2130_sg_err[4] = {0, 0, 0, 0};
 uint16_t tmc2130_sg_cnt[4] = {0, 0, 0, 0};
+#ifdef DEBUG_CRASHDET_COUNTERS
 bool tmc2130_sg_change = false;
-
+#endif
 
 bool skip_debug_msg = false;
 
@@ -144,11 +144,7 @@ uint16_t __tcoolthrs(uint8_t axis)
 	}
 	return 0;
 }
-#ifdef PSU_Delta
-void tmc2130_init(bool bSupressFlag)
-#else
-void tmc2130_init()
-#endif
+void tmc2130_init(TMCInitParams params)
 {
 //	DBG(_n("tmc2130_init(), mode=%S\n"), tmc2130_mode?_n("STEALTH"):_n("NORMAL"));
 	WRITE(X_TMC2130_CS, HIGH);
@@ -193,7 +189,16 @@ void tmc2130_init()
 		tmc2130_setup_chopper(axis, tmc2130_mres[axis], tmc2130_current_h[axis], tmc2130_current_r[axis]);
 		tmc2130_wr(axis, TMC2130_REG_TPOWERDOWN, 0x00000000);
 #ifndef TMC2130_STEALTH_E
-		tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SGSENS);
+        if( ! params.enableECool ){
+            tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SGSENS);
+        } else {
+            tmc2130_wr(axis, TMC2130_REG_COOLCONF, (((uint32_t)tmc2130_sg_thr[axis]) << 16));
+            tmc2130_wr(axis, TMC2130_REG_TCOOLTHRS, 0);
+            tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SILENT);
+            tmc2130_wr_PWMCONF(axis, TMC2130_PWM_AMPL_Ecool, TMC2130_PWM_GRAD_Ecool, tmc2130_pwm_freq[axis], TMC2130_PWM_AUTO_Ecool, 0, 0);
+            tmc2130_wr_TPWMTHRS(axis, TMC2130_TPWMTHRS_E);
+            SERIAL_ECHOLNRPGM(eMotorCurrentScalingEnabled);
+        }
 #else //TMC2130_STEALTH_E
 		tmc2130_wr(axis, TMC2130_REG_COOLCONF, (((uint32_t)tmc2130_sg_thr[axis]) << 16));
 		tmc2130_wr(axis, TMC2130_REG_TCOOLTHRS, 0);
@@ -222,7 +227,7 @@ void tmc2130_init()
 #endif //TMC2130_LINEARITY_CORRECTION
 
 #ifdef PSU_Delta
-     if(!bSupressFlag)
+     if(!params.bSuppressFlag)
           check_force_z();
 #endif // PSU_Delta
 
@@ -255,7 +260,9 @@ void tmc2130_st_isr()
 		if (tmc2130_sg_cnt[axis] < tmc2130_sg_err[axis])
 		{
 			tmc2130_sg_cnt[axis] = tmc2130_sg_err[axis];
+#ifdef DEBUG_CRASHDET_COUNTERS
 			tmc2130_sg_change = true;
+#endif
 			uint8_t sg_thr = 64;
 //			if (axis == Y_AXIS) sg_thr = 64;
 			if (tmc2130_sg_err[axis] >= sg_thr)
@@ -409,7 +416,9 @@ void tmc2130_check_overtemp()
 
 		}
 		checktime = _millis();
+#ifdef DEBUG_CRASHDET_COUNTERS
 		tmc2130_sg_change = true;
+#endif
 	}
 #ifdef DEBUG_CRASHDET_COUNTERS
 	if (tmc2130_sg_change)

+ 18 - 4
Firmware/tmc2130.h

@@ -1,10 +1,10 @@
 #ifndef TMC2130_H
 #define TMC2130_H
 
+#include <stdint.h>
 
 //mode
 extern uint8_t tmc2130_mode;
-//holding and running currents
 extern uint8_t tmc2130_current_h[4];
 extern uint8_t tmc2130_current_r[4];
 //microstep resolution (0 means 256usteps, 8 means 1ustep
@@ -22,6 +22,8 @@ extern uint32_t tmc2130_sg_meassure_val;
 
 extern uint8_t tmc2130_sg_homing_axes_mask;
 
+extern const char eMotorCurrentScalingEnabled[];
+
 #define TMC2130_MODE_NORMAL 0
 #define TMC2130_MODE_SILENT 1
 
@@ -63,11 +65,23 @@ typedef struct
 extern tmc2130_chopper_config_t tmc2130_chopper_config[4];
 
 //initialize tmc2130
-#ifdef PSU_Delta
-extern void tmc2130_init(bool bSupressFlag=false);
+
+struct TMCInitParams {
+    uint8_t bSuppressFlag : 1; // only relevant on MK3S with PSU_Delta
+    uint8_t enableECool : 1;  // experimental support for E-motor cooler operation
+    inline TMCInitParams():bSuppressFlag(0), enableECool(0) { }
+    inline explicit TMCInitParams(bool bSuppressFlag, bool enableECool):bSuppressFlag(bSuppressFlag), enableECool(enableECool) { }
+    inline explicit TMCInitParams(bool enableECool)
+        : bSuppressFlag(
+#ifdef PSU_delta
+        1
 #else
-extern void tmc2130_init();
+        0
 #endif
+        )
+        , enableECool(enableECool) { }
+};
+extern void tmc2130_init(TMCInitParams params);
 //check diag pins (called from stepper isr)
 extern void tmc2130_st_isr();
 //update stall guard (called from st_synchronize inside the loop)

+ 67 - 29
Firmware/ultralcd.cpp

@@ -510,9 +510,9 @@ void lcdui_print_time(void)
     //if remaining print time estimation is available print it else print elapsed time
     int chars = 0;
     if (PRINTER_ACTIVE) {
-        uint16_t print_t = 0;
-        uint16_t print_tr = 0;
-        uint16_t print_tc = 0;
+        uint16_t print_t = PRINT_TIME_REMAINING_INIT;
+        uint16_t print_tr = PRINT_TIME_REMAINING_INIT;
+        uint16_t print_tc = PRINT_TIME_REMAINING_INIT;
         char suff = ' ';
         char suff_doubt = ' ';
 
@@ -542,12 +542,12 @@ void lcdui_print_time(void)
 
         clock_interval++;
 
-        if (print_tc != 0 && clock_interval > CLOCK_INTERVAL_TIME) {
+        if (print_tc != PRINT_TIME_REMAINING_INIT && clock_interval > CLOCK_INTERVAL_TIME) {
             print_t = print_tc;
             suff = 'C';
         } else
 //#endif //CLOCK_INTERVAL_TIME 
-        if (print_tr != 0) {
+        if (print_tr != PRINT_TIME_REMAINING_INIT) {
             print_t = print_tr;
             suff = 'R';
         } else 
@@ -2201,15 +2201,16 @@ void mFilamentItem(uint16_t nTemp, uint16_t nTempBed)
     }
     else
     {
-        lcd_set_cursor(0, 0);
-        lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0));
-
         if (!bFilamentWaitingFlag)
         {
             // First run after the filament preheat selection:
             // setup the fixed LCD parts and raise Z as we wait
             bFilamentWaitingFlag = true;
 
+            lcd_clear();
+            lcd_draw_update = 1;
+            lcd_puts_at_P(0, 3, _i(">Cancel")); ////MSG_ c=20 r=1
+
             lcd_set_cursor(0, 1);
             switch (eFilamentAction)
             {
@@ -2236,9 +2237,11 @@ void mFilamentItem(uint16_t nTemp, uint16_t nTempBed)
                 // handled earlier
                 break;
             }
-            lcd_puts_at_P(0, 3, _i(">Cancel"));                   ////MSG_ c=20 r=1
         }
 
+        lcd_set_cursor(0, 0);
+        lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0));
+
         if (lcd_clicked())
         {
             bFilamentWaitingFlag = false;
@@ -4292,7 +4295,7 @@ static void lcd_silent_mode_set() {
 	cli();
 	tmc2130_mode = (SilentModeMenu != SILENT_MODE_NORMAL)?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 	update_mode_profile();
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
   // We may have missed a stepper timer interrupt due to the time spent in tmc2130_init.
   // Be safe than sorry, reset the stepper timer before re-enabling interrupts.
   st_reset_timer();
@@ -4361,7 +4364,7 @@ void menu_setlang(unsigned char lang)
 }
 
 #ifdef COMMUNITY_LANG_SUPPORT
-#ifdef W25X20CL
+#ifdef XFLASH
 static void lcd_community_language_menu()
 {
 	MENU_BEGIN();
@@ -4375,7 +4378,7 @@ static void lcd_community_language_menu()
 		}
 	MENU_END();
 }
-#endif //W25X20CL
+#endif //XFLASH
 #endif //COMMUNITY_LANG_SUPPORT && W52X20CL
 
 
@@ -4390,7 +4393,7 @@ static void lcd_language_menu()
 		return;
 	}
 	uint8_t cnt = lang_get_count();
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (cnt == 2) //display secondary language in case of clear xflash 
 	{
 		if (menu_item_text_P(lang_get_name_by_code(lang_get_code(1))))
@@ -4401,9 +4404,9 @@ static void lcd_language_menu()
 	}
 	else
 		for (int i = 2; i < 8; i++) //skip seconday language - solved in lang_select (MK3) 'i < 8'  for 7 official languages
-#else //W25X20CL
+#else //XFLASH
 		for (int i = 1; i < cnt; i++) //all seconday languages (MK2/25)
-#endif //W25X20CL
+#endif //XFLASH
 			if (menu_item_text_P(lang_get_name_by_code(lang_get_code(i))))
 			{
 				menu_setlang(i);
@@ -4411,9 +4414,9 @@ static void lcd_language_menu()
 			}
 
 #ifdef COMMUNITY_LANG_SUPPORT
-#ifdef W25X20CL
+#ifdef XFLASH
 		MENU_ITEM_SUBMENU_P(_T(MSG_COMMUNITY_MADE), lcd_community_language_menu); ////MSG_COMMUNITY_MADE c=18
-#endif //W25X20CL
+#endif //XFLASH
 #endif //COMMUNITY_LANG_SUPPORT && W52X20CL
 
 	MENU_END();
@@ -4885,7 +4888,7 @@ void lcd_wizard(WizState state)
 				lcd_display_message_fullscreen_P(_i("Now I will preheat nozzle for PLA."));
 				wait_preheat();
 				//unload current filament
-				unload_filament();
+				unload_filament(true);
 				//load filament
 				lcd_wizard_load();
 				setTargetHotend(0, 0); //we are finished, cooldown nozzle
@@ -5676,7 +5679,7 @@ static void lcd_settings_linearity_correction_menu_save()
     changed |= (eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_Z_FAC) != tmc2130_wave_fac[Z_AXIS]);
     changed |= (eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_E_FAC) != tmc2130_wave_fac[E_AXIS]);
     lcd_ustep_linearity_menu_save();
-    if (changed) tmc2130_init();
+    if (changed) tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
 }
 #endif //TMC2130
 
@@ -6200,13 +6203,14 @@ static void change_extr_menu(){
 }
 #endif //SNMM
 
-//unload filament for single material printer (used in M702 gcode)
-void unload_filament()
+// unload filament for single material printer (used in M702 gcode)
+// @param automatic: If true, unload_filament is part of a unload+load sequence (M600)
+void unload_filament(bool automatic)
 {
 	custom_message_type = CustomMsg::FilamentLoading;
 	lcd_setstatuspgm(_T(MSG_UNLOADING_FILAMENT));
 
-    raise_z_above(MIN_Z_FOR_UNLOAD);
+    raise_z_above(automatic? MIN_Z_FOR_SWAP: MIN_Z_FOR_UNLOAD);
 
 	//		extr_unload2();
 
@@ -6323,15 +6327,15 @@ unsigned char lcd_choose_color() {
 
 }
 
-#include "w25x20cl.h"
+#include "xflash.h"
 
 #ifdef LCD_TEST
 static void lcd_test_menu()
 {
-	W25X20CL_SPI_ENTER();
-	w25x20cl_enable_wr();
-	w25x20cl_chip_erase();
-	w25x20cl_disable_wr();
+	XFLASH_SPI_ENTER();
+	xflash_enable_wr();
+	xflash_chip_erase();
+	xflash_disable_wr();
 }
 #endif //LCD_TEST
 
@@ -6672,7 +6676,7 @@ static void lcd_main_menu()
     }
     MENU_ITEM_SUBMENU_P(_i("Support"), lcd_support_menu);////MSG_SUPPORT
 #ifdef LCD_TEST
-    MENU_ITEM_SUBMENU_P(_i("W25x20CL init"), lcd_test_menu);////MSG_SUPPORT
+    MENU_ITEM_SUBMENU_P(_i("XFLASH init"), lcd_test_menu);////MSG_SUPPORT
 #endif //LCD_TEST
 
     MENU_END();
@@ -8947,6 +8951,37 @@ void lcd_experimental_toggle()
     eeprom_update_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY, oldVal);
 }
 
+#ifdef TMC2130
+void UserECool_toggle(){
+    // this is only called when the experimental menu is visible, thus the first condition for enabling of the ECool mode is met in this place
+    // The condition is intentionally inverted as we are toggling the state (i.e. if it was enabled, we are disabling the feature and vice versa)
+    bool enable = ! UserECoolEnabled();
+
+    eeprom_update_byte((uint8_t *)EEPROM_ECOOL_ENABLE, enable ? EEPROM_ECOOL_MAGIC_NUMBER : EEPROM_EMPTY_VALUE);
+
+    // @@TODO I don't like this - disabling the experimental menu shall disable ECool mode, but it will not reinit the TMC
+    // and I don't want to add more code for this experimental feature ... ideally do not reinit the TMC here at all and let the user reset the printer.
+    tmc2130_init(TMCInitParams(enable));
+}
+#endif
+
+/// Enable experimental support for cooler operation of the extruder motor
+/// Beware - REQUIRES original Prusa MK3/S/+ extruder motor with adequate maximal current
+/// Therefore we don't want to allow general usage of this feature in public as the community likes to
+/// change motors for various reasons and unless the motor is rotating, we cannot verify its properties
+/// (which would be obviously too late for an improperly sized motor)
+/// For farm printing, the cooler E-motor is enabled by default.
+bool UserECoolEnabled(){
+    // We enable E-cool mode for non-farm prints IFF the experimental menu is visible AND the EEPROM_ECOOL variable has
+    // a value of the universal answer to all problems of the universe
+    return ( eeprom_read_byte((uint8_t *)EEPROM_ECOOL_ENABLE) == EEPROM_ECOOL_MAGIC_NUMBER ) 
+        && ( eeprom_read_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY) == 1 );
+}
+
+bool FarmOrUserECool(){
+    return farm_mode || UserECoolEnabled();
+}
+
 void lcd_experimental_menu()
 {
     MENU_BEGIN();
@@ -8955,7 +8990,10 @@ void lcd_experimental_menu()
 #ifdef EXTRUDER_ALTFAN_DETECT
     MENU_ITEM_TOGGLE_P(_N("ALTFAN det."), altfanOverride_get()?_T(MSG_OFF):_T(MSG_ON), altfanOverride_toggle);////MSG_MENU_ALTFAN c=18
 #endif //EXTRUDER_ALTFAN_DETECT
-
+    
+#ifdef TMC2130
+    MENU_ITEM_TOGGLE_P(_N("E-cool mode"), UserECoolEnabled()?_T(MSG_ON):_T(MSG_OFF), UserECool_toggle);////MSG_MENU_ECOOL c=18
+#endif
     MENU_END();
 }
 

+ 4 - 1
Firmware/ultralcd.h

@@ -133,6 +133,9 @@ extern uint8_t farm_mode;
 extern int farm_timer;
 extern uint8_t farm_status;
 
+extern bool UserECoolEnabled();
+extern bool FarmOrUserECool();
+
 #ifdef TMC2130
 #define SILENT_MODE_NORMAL 0
 #define SILENT_MODE_STEALTH 1
@@ -195,7 +198,7 @@ extern bool bFilamentAction;
 void mFilamentItem(uint16_t nTemp,uint16_t nTempBed);
 void mFilamentItemForce();
 void lcd_generic_preheat_menu();
-void unload_filament();
+void unload_filament(bool automatic = false);
 
 void stack_error();
 void lcd_printer_connected();

+ 9 - 2
Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h

@@ -2,7 +2,7 @@
 #define CONFIGURATION_PRUSA_H
 
 #include <limits.h>
-//-//
+
 #include "printers.h"
 /*------------------------------------
  GENERAL SETTINGS
@@ -159,7 +159,7 @@
 
 //#define DEBUG_BUILD
 //#define DEBUG_SEC_LANG   //secondary language debug output at startup
-//#define DEBUG_W25X20CL   //debug external spi flash
+//#define DEBUG_XFLASH   //debug external spi flash
 #ifdef DEBUG_BUILD
 //#define _NO_ASM
 #define DEBUG_DCODES //D codes
@@ -234,6 +234,11 @@
 #define TMC2130_PWM_AUTO_E  1         // PWMCONF
 #define TMC2130_PWM_FREQ_E  2         // PWMCONF
 
+// experimental setting for E-motor cooler operation
+#define TMC2130_PWM_GRAD_Ecool  84        // PWMCONF 730mA @ 375mm/min  970mA phase peak at feedrate 900mm/min
+#define TMC2130_PWM_AMPL_Ecool  43        // PWMCONF 500mA phase peak at feedrate 10 mm/min
+#define TMC2130_PWM_AUTO_Ecool  0         // PWMCONF
+
 #define TMC2130_TOFF_XYZ    3         // CHOPCONF // fchop = 27.778kHz
 #define TMC2130_TOFF_E      3         // CHOPCONF // fchop = 27.778kHz
 //#define TMC2130_TOFF_E      4         // CHOPCONF // fchop = 21.429kHz
@@ -247,6 +252,7 @@
 #define TMC2130_PWM_CLK   (2 * TMC2130_FCLK / TMC2130_PWM_DIV) // PWM frequency (23.4kHz, 35.1kHz, 46.9kHz, 58.5kHz for 12MHz fclk)
 
 #define TMC2130_TPWMTHRS  0         // TPWMTHRS - Sets the switching speed threshold based on TSTEP from stealthChop to spreadCycle mode
+#define TMC2130_TPWMTHRS_E 403      // Switch extruder from StealthChop to SpreadCycle at around 900mm/min
 #define TMC2130_THIGH     0         // THIGH - unused
 
 //#define TMC2130_TCOOLTHRS_X 450       // TCOOLTHRS - coolstep treshold
@@ -265,6 +271,7 @@
 
 //new settings is possible for vsense = 1, running current value > 31 set vsense to zero and shift both currents by 1 bit right (Z axis only)
 #define TMC2130_CURRENTS_H {16, 20, 35, 30}  // default holding currents for all axes
+#define TMC2130_CURRENTS_FARM 36             // E 805 mA peak for ECool/farm mode
 #define TMC2130_CURRENTS_R {16, 20, 35, 30}  // default running currents for all axes
 #define TMC2130_CURRENTS_R_HOME {8, 10, 20, 18}  // homing running currents for all axes
 

+ 8 - 1
Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h

@@ -161,7 +161,7 @@
 
 //#define DEBUG_BUILD
 //#define DEBUG_SEC_LANG   //secondary language debug output at startup
-//#define DEBUG_W25X20CL   //debug external spi flash
+//#define DEBUG_XFLASH   //debug external spi flash
 #ifdef DEBUG_BUILD
 //#define _NO_ASM
 #define DEBUG_DCODES //D codes
@@ -236,6 +236,11 @@
 #define TMC2130_PWM_AUTO_E  1         // PWMCONF
 #define TMC2130_PWM_FREQ_E  2         // PWMCONF
 
+// experimental setting for E-motor cooler operation
+#define TMC2130_PWM_GRAD_Ecool  84        // PWMCONF 730mA @ 375mm/min  970mA phase peak at feedrate 900mm/min
+#define TMC2130_PWM_AMPL_Ecool  43        // PWMCONF 500mA phase peak at feedrate 10 mm/min
+#define TMC2130_PWM_AUTO_Ecool  0         // PWMCONF
+
 #define TMC2130_TOFF_XYZ    3         // CHOPCONF // fchop = 27.778kHz
 #define TMC2130_TOFF_E      3         // CHOPCONF // fchop = 27.778kHz
 //#define TMC2130_TOFF_E      4         // CHOPCONF // fchop = 21.429kHz
@@ -249,6 +254,7 @@
 #define TMC2130_PWM_CLK   (2 * TMC2130_FCLK / TMC2130_PWM_DIV) // PWM frequency (23.4kHz, 35.1kHz, 46.9kHz, 58.5kHz for 12MHz fclk)
 
 #define TMC2130_TPWMTHRS  0         // TPWMTHRS - Sets the switching speed threshold based on TSTEP from stealthChop to spreadCycle mode
+#define TMC2130_TPWMTHRS_E 403      // Switch extruder from StealthChop to SpreadCycle at around 900mm/min
 #define TMC2130_THIGH     0         // THIGH - unused
 
 //#define TMC2130_TCOOLTHRS_X 450       // TCOOLTHRS - coolstep treshold
@@ -267,6 +273,7 @@
 
 //new settings is possible for vsense = 1, running current value > 31 set vsense to zero and shift both currents by 1 bit right (Z axis only)
 #define TMC2130_CURRENTS_H {16, 20, 35, 30}  // default holding currents for all axes
+#define TMC2130_CURRENTS_FARM 36             // E 805 mA peak for ECool/farm mode
 #define TMC2130_CURRENTS_R {16, 20, 35, 30}  // default running currents for all axes
 #define TMC2130_CURRENTS_R_HOME {8, 10, 20, 18}  // homing running currents for all axes
 

+ 0 - 44
Firmware/w25x20cl.h

@@ -1,44 +0,0 @@
-//w25x20cl.h
-#ifndef _W25X20CL_H
-#define _W25X20CL_H
-
-#include <inttypes.h>
-#include "config.h"
-#include "spi.h"
-
-
-
-#define W25X20CL_STATUS_BUSY   0x01
-#define W25X20CL_STATUS_WEL    0x02
-#define W25X20CL_STATUS_BP0    0x04
-#define W25X20CL_STATUS_BP1    0x08
-#define W25X20CL_STATUS_TB     0x20
-#define W25X20CL_STATUS_SRP    0x80
-
-#define W25X20CL_SPI_ENTER() spi_setup(W25X20CL_SPCR, W25X20CL_SPSR)
-
-#if defined(__cplusplus)
-extern "C" {
-#endif //defined(__cplusplus)
-
-
-extern int8_t w25x20cl_init(void);
-extern void w25x20cl_enable_wr(void);
-extern void w25x20cl_disable_wr(void);
-extern uint8_t w25x20cl_rd_status_reg(void);
-extern void w25x20cl_wr_status_reg(uint8_t val);
-extern void w25x20cl_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_sector_erase(uint32_t addr);
-extern void w25x20cl_block32_erase(uint32_t addr);
-extern void w25x20cl_block64_erase(uint32_t addr);
-extern void w25x20cl_chip_erase(void);
-extern void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_rd_uid(uint8_t* uid);
-extern void w25x20cl_wait_busy(void);
-
-#if defined(__cplusplus)
-}
-#endif //defined(__cplusplus)
-#endif //_W25X20CL_H

+ 45 - 34
Firmware/w25x20cl.c

@@ -1,13 +1,18 @@
-//w25x20cl.c
+//xflash.c
 
-#include "w25x20cl.h"
+#include "xflash.h"
 #include <avr/io.h>
 #include <avr/pgmspace.h>
 #include "spi.h"
 #include "fastio.h"
 
-#define _MFRID             0xEF
-#define _DEVID             0x11
+#ifdef XFLASH
+
+#define _MFRID_W25X20CL    0xEF
+#define _DEVID_W25X20CL    0x11
+
+#define _MFRID_GD25Q20C    0xC8
+#define _DEVID_GD25Q20C    0x11
 
 #define _CMD_ENABLE_WR     0x06
 #define _CMD_ENABLE_WR_VSR 0x50
@@ -31,8 +36,8 @@
 #define _CMD_JEDEC_ID      0x9f
 #define _CMD_RD_UID        0x4b
 
-#define _CS_LOW() WRITE(W25X20CL_PIN_CS, 0)
-#define _CS_HIGH() WRITE(W25X20CL_PIN_CS, 1)
+#define _CS_LOW() WRITE(XFLASH_PIN_CS, 0)
+#define _CS_HIGH() WRITE(XFLASH_PIN_CS, 1)
 
 //#define _SPI_TX swspi_tx
 //#define _SPI_RX swspi_rx
@@ -40,33 +45,33 @@
 #define _SPI_RX()    spi_txrx(0xff)
 
 
-int w25x20cl_mfrid_devid(void);
+int xflash_mfrid_devid(void);
 
 
-int8_t w25x20cl_init(void)
+int8_t xflash_init(void)
 {
 	_CS_HIGH();
-	SET_OUTPUT(W25X20CL_PIN_CS);
-	W25X20CL_SPI_ENTER();
-	if (!w25x20cl_mfrid_devid()) return 0;
+	SET_OUTPUT(XFLASH_PIN_CS);
+	XFLASH_SPI_ENTER();
+	if (!xflash_mfrid_devid()) return 0;
 	return 1;
 }
 
-void w25x20cl_enable_wr(void)
+void xflash_enable_wr(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_ENABLE_WR);             // send command 0x06
 	_CS_HIGH();
 }
 
-void w25x20cl_disable_wr(void)
+void xflash_disable_wr(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_DISABLE_WR);            // send command 0x04
 	_CS_HIGH();
 }
 
-uint8_t w25x20cl_rd_status_reg(void)
+uint8_t xflash_rd_status_reg(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_RD_STATUS_REG);         // send command 0x90
@@ -75,6 +80,7 @@ uint8_t w25x20cl_rd_status_reg(void)
 	return val;
 }
 
+#if 0
 void w25x20cl_wr_status_reg(uint8_t val)
 {
 	_CS_LOW();
@@ -82,8 +88,9 @@ void w25x20cl_wr_status_reg(uint8_t val)
 	_SPI_TX(val);                        // send value
 	_CS_HIGH();
 }
+#endif
 
-void w25x20cl_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt)
+void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_RD_DATA);               // send command 0x03
@@ -95,7 +102,7 @@ void w25x20cl_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt)
 	_CS_HIGH();
 }
 
-void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt)
+void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_PAGE_PROGRAM);          // send command 0x02
@@ -107,7 +114,7 @@ void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt)
 	_CS_HIGH();
 }
 
-void w25x20cl_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt)
+void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_PAGE_PROGRAM);          // send command 0x02
@@ -119,7 +126,7 @@ void w25x20cl_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt)
 	_CS_HIGH();
 }
 
-void w25x20cl_erase(uint8_t cmd, uint32_t addr)
+void xflash_erase(uint8_t cmd, uint32_t addr)
 {
 	_CS_LOW();
 	_SPI_TX(cmd);          			     // send command 0x20
@@ -129,22 +136,22 @@ void w25x20cl_erase(uint8_t cmd, uint32_t addr)
 	_CS_HIGH();
 }
 
-void w25x20cl_sector_erase(uint32_t addr)
+void xflash_sector_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_SECTOR_ERASE, addr);
+	return xflash_erase(_CMD_SECTOR_ERASE, addr);
 }
 
-void w25x20cl_block32_erase(uint32_t addr)
+void xflash_block32_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_BLOCK32_ERASE, addr);
+	return xflash_erase(_CMD_BLOCK32_ERASE, addr);
 }
 
-void w25x20cl_block64_erase(uint32_t addr)
+void xflash_block64_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_BLOCK64_ERASE, addr);
+	return xflash_erase(_CMD_BLOCK64_ERASE, addr);
 }
 
-void w25x20cl_chip_erase(void)
+void xflash_chip_erase(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_CHIP_ERASE);            // send command 0xc7
@@ -152,33 +159,37 @@ void w25x20cl_chip_erase(void)
 }
 
 
-void w25x20cl_rd_uid(uint8_t* uid)
+void xflash_rd_uid(uint8_t* uid)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_RD_UID);                // send command 0x4b
 	uint8_t cnt = 4;                     // 4 dummy bytes
-	while (cnt--)                        // receive dummy bytes
-		_SPI_RX();
+	while (cnt--)                        // transmit dummy bytes
+		_SPI_TX(0x00);
 	cnt = 8;                             // 8 bytes UID
 	while (cnt--)                        // receive UID
 		uid[7 - cnt] = _SPI_RX();
 	_CS_HIGH();
 }
 
-int w25x20cl_mfrid_devid(void)
+int xflash_mfrid_devid(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_MFRID_DEVID);           // send command 0x90
 	uint8_t cnt = 3;                     // 3 address bytes
 	while (cnt--)                        // send address bytes
 		_SPI_TX(0x00);
-	uint8_t w25x20cl_mfrid = _SPI_RX();  // receive mfrid
-	uint8_t w25x20cl_devid = _SPI_RX();  // receive devid
+	uint8_t xflash_mfrid = _SPI_RX();  // receive mfrid
+	uint8_t xflash_devid = _SPI_RX();  // receive devid
 	_CS_HIGH();
-	return ((w25x20cl_mfrid == _MFRID) && (w25x20cl_devid == _DEVID));
+	return
+		((xflash_mfrid == _MFRID_W25X20CL) && (xflash_devid == _DEVID_W25X20CL)) ||
+		((xflash_mfrid == _MFRID_GD25Q20C) && (xflash_devid == _DEVID_GD25Q20C));
 }
 
-void w25x20cl_wait_busy(void)
+void xflash_wait_busy(void)
 {
-	while (w25x20cl_rd_status_reg() & W25X20CL_STATUS_BUSY) ;
+	while (xflash_rd_status_reg() & XFLASH_STATUS_BUSY) ;
 }
+
+#endif //XFLASH

+ 50 - 0
Firmware/xflash.h

@@ -0,0 +1,50 @@
+//xflash.h
+#ifndef _XFLASH_H
+#define _XFLASH_H
+
+#include <inttypes.h>
+#include "config.h"
+#include "spi.h"
+
+
+
+#define XFLASH_STATUS_BUSY   0x01
+#define XFLASH_STATUS_WEL    0x02
+#define XFLASH_STATUS_BP0    0x04
+#define XFLASH_STATUS_BP1    0x08
+#define XFLASH_STATUS_TB     0x20
+#define XFLASH_STATUS_SRP    0x80
+
+#define XFLASH_SPI_RATE      0 // fosc/4 = 4MHz
+#define XFLASH_SPCR          SPI_SPCR(XFLASH_SPI_RATE, 1, 1, 1, 0)
+#define XFLASH_SPSR          SPI_SPSR(XFLASH_SPI_RATE)
+
+#define XFLASH_SPI_ENTER() spi_setup(XFLASH_SPCR, XFLASH_SPSR)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif //defined(__cplusplus)
+
+
+extern int8_t xflash_init(void);
+extern void xflash_enable_wr(void);
+extern void xflash_disable_wr(void);
+extern uint8_t xflash_rd_status_reg(void);
+#if 0
+extern void w25x20cl_wr_status_reg(uint8_t val);
+#endif
+extern void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt);
+extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
+extern void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt);
+extern void xflash_sector_erase(uint32_t addr);
+extern void xflash_block32_erase(uint32_t addr);
+extern void xflash_block64_erase(uint32_t addr);
+extern void xflash_chip_erase(void);
+extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
+extern void xflash_rd_uid(uint8_t* uid);
+extern void xflash_wait_busy(void);
+
+#if defined(__cplusplus)
+}
+#endif //defined(__cplusplus)
+#endif //_XFLASH_H

+ 40 - 8
Firmware/xyzcal.cpp

@@ -837,7 +837,7 @@ void dynamic_circle(uint8_t *matrix_32x32, float &x, float &y, float &r, uint8_t
 	for (int8_t i = iterations; i > 0; --i){
 	
         //@size=128B
-		DBG(_n(" [%f, %f][%f] circle\n"), x, y, r);
+		// DBG(_n(" [%f, %f][%f] circle\n"), x, y, r);
 
 		/// read points on the circle
 		for (uint8_t p = 0; p < num_points; ++p){
@@ -904,12 +904,42 @@ uint8_t find_patterns(uint8_t *matrix32, uint16_t *pattern08, uint16_t *pattern1
 	return match10;
 }
 
+/// Scan should include normal data.
+/// If it's too extreme (00, FF) it could be caused by biased sensor.
+/// \return true if data looks normal
+bool check_scan(uint8_t *matrix32){
+	/// magic constants that define normality
+	const int16_t threshold_total = 900;
+	const int threshold_extreme = 50;
+
+	int16_t mins = 0;
+	int16_t maxs = 0;
+
+	for (int16_t i = 0; i < 32*32;++i){
+		if (matrix32[i] == 0) {
+			++mins;
+		} else if (matrix32[i] == 0xFF){
+			++maxs;
+		}
+	}
+	const int16_t rest = 1024 - mins - maxs;
+
+	if (mins + maxs > threshold_total
+		&& mins > threshold_extreme
+		&& maxs > threshold_extreme
+		&& mins > rest
+		&& maxs > rest)
+		return false;
+
+	return true;
+}
+
 /// scans area around the current head location and
 /// searches for the center of the calibration pin
-bool xyzcal_scan_and_process(void){
+BedSkewOffsetDetectionResultType xyzcal_scan_and_process(){
     //@size=44
-	DBG(_n("sizeof(block_buffer)=%d\n"), sizeof(block_t)*BLOCK_BUFFER_SIZE);
-	bool ret = false;
+	// DBG(_n("sizeof(block_buffer)=%d\n"), sizeof(block_t)*BLOCK_BUFFER_SIZE);
+	BedSkewOffsetDetectionResultType ret = BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
 	int16_t x = _X;
 	int16_t y = _Y;
 	const int16_t z = _Z;
@@ -925,6 +955,8 @@ bool xyzcal_scan_and_process(void){
 
 	xyzcal_scan_pixels_32x32_Zhop(x, y, z, 2400, 200, matrix32);
 	print_image(matrix32);
+	if (!check_scan(matrix32))
+		return BED_SKEW_OFFSET_DETECTION_POINT_SCAN_FAILED;
 
 	/// SEARCH FOR BINARY CIRCLE
 	uint8_t uc = 0;
@@ -955,7 +987,7 @@ bool xyzcal_scan_and_process(void){
 		x = round_to_i16(xf);
 		y = round_to_i16(yf);
 		xyzcal_lineXYZ_to(x, y, z, 200, 0);
-		ret = true;
+		ret = BED_SKEW_OFFSET_DETECTION_POINT_FOUND;
 	}
 
 	/// wipe buffer
@@ -964,11 +996,11 @@ bool xyzcal_scan_and_process(void){
 	return ret;
 }
 
-bool xyzcal_find_bed_induction_sensor_point_xy(void){
-	bool ret = false;
+BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy(void){
+	BedSkewOffsetDetectionResultType ret = BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
 
     //@size=258
-    DBG(_n("xyzcal_find_bed_induction_sensor_point_xy x=%ld y=%ld z=%ld\n"), count_position[X_AXIS], count_position[Y_AXIS], count_position[Z_AXIS]);
+    // DBG(_n("xyzcal_find_bed_induction_sensor_point_xy x=%ld y=%ld z=%ld\n"), count_position[X_AXIS], count_position[Y_AXIS], count_position[Z_AXIS]);
 	st_synchronize();
 
 	xyzcal_meassure_enter();

+ 3 - 10
Firmware/xyzcal.h

@@ -1,9 +1,9 @@
 //xyzcal.h - xyz calibration with image processing
-#ifndef _XYZCAL_H
-#define _XYZCAL_H
+#pragma once
 
 #include <inttypes.h>
 
+#include "mesh_bed_calibration.h"
 
 extern void xyzcal_meassure_enter(void);
 
@@ -17,11 +17,4 @@ extern bool xyzcal_spiral8(int16_t cx, int16_t cy, int16_t z0, int16_t dz, int16
 
 //extern int8_t xyzcal_meassure_pinda_hysterezis(int16_t min_z, int16_t max_z, uint16_t delay_us, uint8_t samples);
 
-extern bool xyzcal_searchZ(void);
-
-extern bool xyzcal_scan_and_process(void);
-
-extern bool xyzcal_find_bed_induction_sensor_point_xy(void);
-
-
-#endif //_XYZCAL_H
+extern BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy();

+ 2 - 1
lang/fw-clean.sh

@@ -45,17 +45,18 @@ rm_if_exists update_lang_de.out
 rm_if_exists update_lang_es.out
 rm_if_exists update_lang_fr.out
 rm_if_exists update_lang_it.out
-rm_if_exists update_lang_nl.out
 rm_if_exists update_lang_pl.out
 rm_if_exists lang.bin
 rm_if_exists lang.hex
 #Community language support
 #Dutch
 rm_if_exists firmware_nl.hex
+rm_if_exists update_lang_nl.out
 
 #Use the 2 lines below as a template and replace 'qr'
 ##New language
 #rm_if_exists firmware_qr.hex
+#rm_if_exists update_lang_qr.out
 
 echo -n "fw-clean.sh finished" >&2
 if [ $result -eq 0 ]; then