Browse Source

Merge branch 'MK3_3.9.0' into flashair_display_ip

odaki 4 years ago
parent
commit
c34c622b3c

+ 3 - 3
Firmware/Configuration.h

@@ -16,8 +16,8 @@ extern uint16_t nPrinterType;
 extern PGM_P sPrinterName;
 
 // Firmware version
-#define FW_VERSION "3.9.0"
-#define FW_COMMIT_NR   3175
+#define FW_VERSION "3.9.0-RC2"
+#define FW_COMMIT_NR   3398
 // 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
@@ -424,7 +424,7 @@ your extruder heater takes 2 minutes to hit the target on heating.
 #define DEFAULT_XJERK                10       // (mm/sec)
 #define DEFAULT_YJERK                10       // (mm/sec)
 #define DEFAULT_ZJERK                 0.4     // (mm/sec)
-#define DEFAULT_EJERK                 2.5     // (mm/sec)
+#define DEFAULT_EJERK                 4.5     // (mm/sec)
 
 //===========================================================================
 //=============================Additional Features===========================

+ 0 - 1
Firmware/Configuration_adv.h

@@ -152,7 +152,6 @@
 #define Z_HOME_RETRACT_MM 2
 //#define QUICK_HOME  //if this is defined, if both x and y are to be homed, a diagonal move will be performed initially.
 
-#define AXIS_RELATIVE_MODES {0, 0, 0, 0}
 #define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step). Toshiba steppers are 4x slower, but Prusa3D does not use those.
 //By default pololu step drivers require an active high signal. However, some high power drivers require an active low signal as step.
 #define INVERT_X_STEP_PIN 0

+ 251 - 126
Firmware/Dcodes.cpp

@@ -1,5 +1,6 @@
 #include "Dcodes.h"
 //#include "Marlin.h"
+#include "Configuration.h"
 #include "language.h"
 #include "cmdqueue.h"
 #include <stdio.h>
@@ -97,19 +98,25 @@ void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperl
 	}
 }
 
-#ifdef DEBUG_DCODE3
+#if defined DEBUG_DCODE3 || defined DEBUG_DCODES
 #define EEPROM_SIZE 0x1000
     /*!
-    *
     ### D3 - Read/Write EEPROM <a href="https://reprap.org/wiki/G-code#D3:_Read.2FWrite_EEPROM">D3: Read/Write EEPROM</a>
     This command can be used without any additional parameters. It will read the entire eeprom.
-      
-          D3 [ A | C | X ]
-      
-      - `A` - Address (0x0000-0x0fff)
-      - `C` - Count (0x0001-0x1000)
-      - `X` - Data
-    *
+    #### Usage
+    
+        D3 [ A | C | X ]
+    
+    #### Parameters
+    - `A` - Address (x0000-x0fff)
+    - `C` - Count (1-4096)
+    - `X` - Data (hex)
+	
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
     */
 void dcode_3()
 {
@@ -179,7 +186,6 @@ void dcode_3()
 #define BOOT_APP_FLG_COPY  0x02
 #define BOOT_APP_FLG_FLASH 0x04
 
-extern uint8_t fsensor_log;
 extern float current_temperature_pinda;
 extern float axis_steps_per_unit[NUM_AXIS];
 
@@ -206,13 +212,13 @@ void dcode__1()
 #ifdef DEBUG_DCODES
 
     /*!
-    *
     ### D0 - Reset <a href="https://reprap.org/wiki/G-code#D0:_Reset">D0: Reset</a>
-      
-          D0 [ B ]
-      
-      - `B` - Bootloader
-    *
+    #### Usage
+    
+        D0 [ B ]
+    
+    #### Parameters
+    - `B` - Bootloader
     */
 void dcode_0()
 {
@@ -251,16 +257,22 @@ void dcode_1()
 }
 
     /*!
-    *
     ### D2 - Read/Write RAM <a href="https://reprap.org/wiki/G-code#D2:_Read.2FWrite_RAM">D3: Read/Write RAM</a>
     This command can be used without any additional parameters. It will read the entire RAM.
-      
-          D3 [ A | C | X ]
-      
-      - `A` - Address (0x0000-0x1fff)
-      - `C` - Count (0x0001-0x2000)
-      - `X` - Data
-    *
+    #### Usage
+    
+        D2 [ A | C | X ]
+    
+    #### Parameters
+    - `A` - Address (x0000-x1fff)
+    - `C` - Count (1-8192)
+    - `X` - Data
+
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
     */
 void dcode_2()
 {
@@ -306,17 +318,17 @@ void dcode_2()
 }
 
     /*!
-    *
-    ### D4 - Read/Write PIN <a href="https://reprap.org/wiki/G-code#D4:_Read.2FWrite_PIN">D4: Read/Write PIN</a>
     
+    ### D4 - Read/Write PIN <a href="https://reprap.org/wiki/G-code#D4:_Read.2FWrite_PIN">D4: Read/Write PIN</a>
     To read the digital value of a pin you need only to define the pin number.
-      
-          D4 [ P | F | V ]
-      
-      - `P` - Pin (0-255)
-      - `F` - Function in/out (0/1)
-      - `V` - Value (0/1)
-    *
+    #### Usage
+    
+        D4 [ P | F | V ]
+    
+    #### Parameters
+    - `P` - Pin (0-255)
+    - `F` - Function in/out (0/1)
+    - `V` - Value (0/1)
     */
 void dcode_4()
 {
@@ -348,21 +360,27 @@ void dcode_4()
 }
 #endif //DEBUG_DCODES
 
-#ifdef DEBUG_DCODE5
+#if defined DEBUG_DCODE5 || defined DEBUG_DCODES
 
     /*!
-    *
     ### D5 - Read/Write FLASH <a href="https://reprap.org/wiki/G-code#D5:_Read.2FWrite_FLASH">D5: Read/Write Flash</a>
     This command can be used without any additional parameters. It will read the 1kb FLASH.
-      
-          D3 [ A | C | X | E ]
-      
-      - `A` - Address (0x00000-0x3ffff)
-      - `C` - Count (0x0001-0x2000)
-      - `X` - Data
-      - `E` - Erase
-    *
-    */
+    #### Usage
+    
+        D5 [ A | C | X | E ]
+    
+    #### Parameters
+    - `A` - Address (x00000-x3ffff)
+    - `C` - Count (1-8192)
+    - `X` - Data (hex)
+    - `E` - Erase
+ 	
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
+   */
 void dcode_5()
 {
 	printf_P(PSTR("D5 - Read/Write FLASH\n"));
@@ -427,24 +445,18 @@ void dcode_5()
 #ifdef DEBUG_DCODES
 
     /*!
-    *
     ### D6 - Read/Write external FLASH <a href="https://reprap.org/wiki/G-code#D6:_Read.2FWrite_external_FLASH">D6: Read/Write external Flash</a>
-    
     Reserved
-   *
-   */
+    */
 void dcode_6()
 {
 	LOG("D6 - Read/Write external FLASH\n");
 }
 
     /*!
-    *
     ### D7 - Read/Write Bootloader <a href="https://reprap.org/wiki/G-code#D7:_Read.2FWrite_Bootloader">D7: Read/Write Bootloader</a>
-    
     Reserved
-   *
-   */
+    */
 void dcode_7()
 {
 	LOG("D7 - Read/Write Bootloader\n");
@@ -461,16 +473,16 @@ void dcode_7()
 }
 
     /*!
-    *
     ### D8 - Read/Write PINDA <a href="https://reprap.org/wiki/G-code#D8:_Read.2FWrite_PINDA">D8: Read/Write PINDA</a>
-      
-          D8 [ ? | ! | P | Z ]
-      
-      - `?` - Read PINDA temperature shift values
-      - `!` - Reset PINDA temperature shift values to default
-      - `P` - Pinda temperature [C]
-      - `Z` - Z Offset [mm]
-    *
+    #### Usage
+    
+        D8 [ ? | ! | P | Z ]
+    
+    #### Parameters
+    - `?` - Read PINDA temperature shift values
+    - `!` - Reset PINDA temperature shift values to default
+    - `P` - Pinda temperature [C]
+    - `Z` - Z Offset [mm]
     */
 void dcode_8()
 {
@@ -514,21 +526,21 @@ void dcode_8()
 }
 
     /*!
-    *
     ### D9 - Read ADC <a href="https://reprap.org/wiki/G-code#D9:_Read.2FWrite_ADC">D9: Read ADC</a>
-      
-          D9 [ I | V ]
-      
-      - `I` - ADC channel index 
-         - `0` - Heater 0 temperature
-         - `1` - Heater 1 temperature
-         - `2` - Bed temperature
-         - `3` - PINDA temperature
-         - `4` - PWR voltage
-         - `5` - Ambient temperature
-         - `6` - BED voltage
-      - `V` Value to be written as simulated
-    *
+    #### Usage
+    
+        D9 [ I | V ]
+    
+    #### Parameters
+    - `I` - ADC channel index 
+        - `0` - Heater 0 temperature
+        - `1` - Heater 1 temperature
+        - `2` - Bed temperature
+        - `3` - PINDA temperature
+        - `4` - PWR voltage
+        - `5` - Ambient temperature
+        - `6` - BED voltage
+    - `V` Value to be written as simulated
     */
 const char* dcode_9_ADC_name(uint8_t i)
 {
@@ -604,11 +616,8 @@ void dcode_9()
 }
 
     /*!
-    *
     ### D10 - Set XYZ calibration = OK <a href="https://reprap.org/wiki/G-code#D10:_Set_XYZ_calibration_.3D_OK">D10: Set XYZ calibration = OK</a>
-    
-   *
-   */
+    */
 void dcode_10()
 {//Tell the printer that XYZ calibration went OK
 	LOG("D10 - XYZ calibration = OK\n");
@@ -616,54 +625,168 @@ void dcode_10()
 }
 
     /*!
-    *
     ### D12 - Time <a href="https://reprap.org/wiki/G-code#D12:_Time">D12: Time</a>
-    
-   *
-   */
+    Writes the current time in the log file.
+    */
+
 void dcode_12()
 {//Time
 	LOG("D12 - Time\n");
 
 }
 
+#ifdef HEATBED_ANALYSIS
+    /*!
+    ### D80 - Bed check <a href="https://reprap.org/wiki/G-code#D80:_Bed_check">D80: Bed check</a>
+    This command will log data to SD card file "mesh.txt".
+    #### Usage
+    
+        D80 [ E | F | G | H | I | J ]
+    
+    #### Parameters
+    - `E` - Dimension X (default 40)
+    - `F` - Dimention Y (default 40)
+    - `G` - Points X (default 40)
+    - `H` - Points Y (default 40)
+    - `I` - Offset X (default 74)
+    - `J` - Offset Y (default 34)
+  */
+void dcode_80()
+{
+	float dimension_x = 40;
+	float dimension_y = 40;
+	int points_x = 40;
+	int points_y = 40;
+	float offset_x = 74;
+	float offset_y = 33;
+
+	if (code_seen('E')) dimension_x = code_value();
+	if (code_seen('F')) dimension_y = code_value();
+	if (code_seen('G')) {points_x = code_value(); }
+	if (code_seen('H')) {points_y = code_value(); }
+	if (code_seen('I')) {offset_x = code_value(); }
+	if (code_seen('J')) {offset_y = code_value(); }
+	printf_P(PSTR("DIM X: %f\n"), dimension_x);
+	printf_P(PSTR("DIM Y: %f\n"), dimension_y);
+	printf_P(PSTR("POINTS X: %d\n"), points_x);
+	printf_P(PSTR("POINTS Y: %d\n"), points_y);
+	printf_P(PSTR("OFFSET X: %f\n"), offset_x);
+	printf_P(PSTR("OFFSET Y: %f\n"), offset_y);
+		bed_check(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
+}
+
+
+    /*!
+    ### D81 - Bed analysis <a href="https://reprap.org/wiki/G-code#D81:_Bed_analysis">D80: Bed analysis</a>
+    This command will log data to SD card file "wldsd.txt".
+    #### Usage
+    
+        D81 [ E | F | G | H | I | J ]
+    
+    #### Parameters
+    - `E` - Dimension X (default 40)
+    - `F` - Dimention Y (default 40)
+    - `G` - Points X (default 40)
+    - `H` - Points Y (default 40)
+    - `I` - Offset X (default 74)
+    - `J` - Offset Y (default 34)
+  */
+void dcode_81()
+{
+	float dimension_x = 40;
+	float dimension_y = 40;
+	int points_x = 40;
+	int points_y = 40;
+	float offset_x = 74;
+	float offset_y = 33;
+
+	if (code_seen('E')) dimension_x = code_value();
+	if (code_seen('F')) dimension_y = code_value();
+	if (code_seen("G")) { strchr_pointer+=1; points_x = code_value(); }
+	if (code_seen("H")) { strchr_pointer+=1; points_y = code_value(); }
+	if (code_seen("I")) { strchr_pointer+=1; offset_x = code_value(); }
+	if (code_seen("J")) { strchr_pointer+=1; offset_y = code_value(); }
+	
+	bed_analysis(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
+	
+}
+
+#endif //HEATBED_ANALYSIS
+
+    /*!
+    ### D106 - Print measured fan speed for different pwm values <a href="https://reprap.org/wiki/G-code#D106:_Print_measured_fan_speed_for_different_pwm_values">D106: Print measured fan speed for different pwm values</a>
+    */
+void dcode_106()
+{
+	for (int i = 255; i > 0; i = i - 5) {
+		fanSpeed = i;
+		//delay_keep_alive(2000);
+		for (int j = 0; j < 100; j++) {
+			delay_keep_alive(100);
+			}
+			printf_P(_N("%d: %d\n"), i, fan_speed[1]);
+	}
+}
 
 #ifdef TMC2130
 #include "planner.h"
 #include "tmc2130.h"
 extern void st_synchronize();
-/**
- * @brief D2130 Trinamic stepper controller
- * D2130<axis><command>[subcommand][value]
- *  * Axis
- *  * * 'X'
- *  * * 'Y'
- *  * * 'Z'
- *  * * 'E'
- *  * command
- *  * * '0' current off
- *  * * '1' current on
- *  * * '+' single step
- *  * * * value sereval steps
- *  * * '-' dtto oposite direction
- *  * * '?' read register
- *  * * * "mres"
- *  * * * "step"
- *  * * * "mscnt"
- *  * * * "mscuract"
- *  * * * "wave"
- *  * * '!' set register
- *  * * * "mres"
- *  * * * "step"
- *  * * * "wave"
- *  * * * *0, 180..250 meaning: off, 0.9..1.25, recommended value is 1.1
- *  * * '@' home calibrate axis
- *
- *  Example:
- *  D2130E?wave //print extruder microstep linearity compensation curve
- *  D2130E!wave0 //disable extruder linearity compensation curve, (sine curve is used)
- *  D2130E!wave220 // (sin(x))^1.1 extruder microstep compensation curve used
- */
+    /*!
+    ### D2130 - Trinamic stepper controller <a href="https://reprap.org/wiki/G-code#D2130:_Trinamic_stepper_controller">D2130: Trinamic stepper controller</a>
+    @todo Please review by owner of the code. RepRap Wiki Gcode needs to be updated after review of owner as well.
+    
+    #### Usage
+    
+        D2130 [ Axis | Command | Subcommand | Value ]
+    
+    #### Parameters
+    - Axis
+      - `X` - X stepper driver
+      - `Y` - Y stepper driver
+      - `Z` - Z stepper driver
+      - `E` - Extruder stepper driver
+    - Commands
+      - `0`   - Current off
+      - `1`   - Current on
+      - `+`   - Single step
+      - `-`   - Single step oposite direction
+      - `NNN` - Value sereval steps
+      - `?`   - Read register
+      - Subcommands for read register
+        - `mres`     - Micro step resolution. More information in datasheet '5.5.2 CHOPCONF – Chopper Configuration'
+        - `step`     - Step
+        - `mscnt`    - Microstep counter. More information in datasheet '5.5 Motor Driver Registers'
+        - `mscuract` - Actual microstep current for motor. More information in datasheet '5.5 Motor Driver Registers'
+        - `wave`     - Microstep linearity compensation curve
+      - `!`   - Set register
+      - Subcommands for set register
+        - `mres`     - Micro step resolution
+        - `step`     - Step
+        - `wave`     - Microstep linearity compensation curve
+        - Values for set register
+          - `0, 180 --> 250` - Off
+          - `0.9 --> 1.25`   - Valid values (recommended is 1.1)
+      - `@`   - Home calibrate axis
+    
+    Examples:
+      
+          D2130E?wave
+      
+      Print extruder microstep linearity compensation curve
+      
+          D2130E!wave0
+      
+      Disable extruder linearity compensation curve, (sine curve is used)
+      
+          D2130E!wave220
+      
+      (sin(x))^1.1 extruder microstep compensation curve used
+    
+    Notes:
+      For more information see https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
+    *
+	*/
 void dcode_2130()
 {
 	printf_P(PSTR("D2130 - TMC2130\n"));
@@ -767,18 +890,18 @@ void dcode_2130()
 
 #ifdef PAT9125
     /*!
-    *
     ### D9125 - PAT9125 filament sensor <a href="https://reprap.org/wiki/G-code#D9:_Read.2FWrite_ADC">D9125: PAT9125 filament sensor</a>
-      
-          D9125 [ ? | ! | R | X | Y | L ]
-      
-      - `?` - Print values
-      - `!` - Print values
-      - `R` - Resolution. Not active in code
-      - `X` - X values
-      - `Y` - Y values
-      - `L` - Activate filament sensor log
-    *
+    #### Usage
+    
+        D9125 [ ? | ! | R | X | Y | L ]
+    
+    #### Parameters
+    - `?` - Print values
+    - `!` - Print values
+    - `R` - Resolution. Not active in code
+    - `X` - X values
+    - `Y` - Y values
+    - `L` - Activate filament sensor log
     */
 void dcode_9125()
 {
@@ -812,11 +935,13 @@ void dcode_9125()
 		pat9125_y = (int)code_value();
 		LOG("pat9125_y=%d\n", pat9125_y);
 	}
+#ifdef DEBUG_FSENSOR_LOG
 	if (code_seen('L'))
 	{
 		fsensor_log = (int)code_value();
 		LOG("fsensor_log=%d\n", fsensor_log);
 	}
+#endif //DEBUG_FSENSOR_LOG
 }
 #endif //PAT9125
 

+ 18 - 4
Firmware/Dcodes.h

@@ -2,26 +2,40 @@
 #define DCODES_H
 
 extern void dcode__1(); //D-1 - Endless loop (to simulate deadlock)
-
 extern void dcode_0(); //D0 - Reset
 extern void dcode_1(); //D1 - Clear EEPROM
 extern void dcode_2(); //D2 - Read/Write RAM
+
+#if defined DEBUG_DCODE3 || defined DEBUG_DCODES
 extern void dcode_3(); //D3 - Read/Write EEPROM
+#endif //DEBUG_DCODE3
+
 extern void dcode_4(); //D4 - Read/Write PIN
+
+#if defined DEBUG_DCODE5 || defined DEBUG_DCODES
 extern void dcode_5(); //D5 - Read/Write FLASH
+#endif //DEBUG_DCODE5
+
 extern void dcode_6(); //D6 - Read/Write external FLASH
 extern void dcode_7(); //D7 - Read/Write Bootloader
 extern void dcode_8(); //D8 - Read/Write PINDA
 extern void dcode_9(); //D9 - Read/Write ADC (Write=enable simulated, Read=disable simulated)
-
 extern void dcode_10(); //D10 - XYZ calibration = OK
+extern void dcode_12(); //D12 - Log time. Writes the current time in the log file.
+
+#ifdef HEATBED_ANALYSIS
+extern void dcode_80(); //D80 - Bed check. This command will log data to SD card file "mesh.txt".
+extern void dcode_81(); //D81 - Bed analysis. This command will log data to SD card file "wldsd.txt".
+#endif //HEATBED_ANALYSIS
+
+	extern void dcode_106(); //D106 - Print measured fan speed for different pwm values
 
 #ifdef TMC2130
-extern void dcode_2130(); //D2130 - TMC2130
+	extern void dcode_2130(); //D2130 - TMC2130
 #endif //TMC2130
 
 #ifdef PAT9125
-extern void dcode_9125(); //D9125 - PAT9125
+	extern void dcode_9125(); //D9125 - PAT9125
 #endif //PAT9125
 
 

+ 20 - 22
Firmware/Marlin.h

@@ -146,40 +146,39 @@ void manage_inactivity(bool ignore_stepper_queue=false);
 #if defined(Z_ENABLE_PIN) && Z_ENABLE_PIN > -1 
 	#if defined(Z_AXIS_ALWAYS_ON)
 		  #ifdef Z_DUAL_STEPPER_DRIVERS
-			#define  enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
-			#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
+			#define  poweron_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
+			#define poweroff_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
 		  #else
-			#define  enable_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
-			#define  disable_z() {}
+			#define  poweron_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
+			#define poweroff_z() {}
 		  #endif
 	#else
 		#ifdef Z_DUAL_STEPPER_DRIVERS
-			#define  enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
-			#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
+			#define  poweron_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
+			#define poweroff_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
 		#else
-			#define  enable_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
-			#define disable_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
+			#define  poweron_z() WRITE(Z_ENABLE_PIN, Z_ENABLE_ON)
+			#define poweroff_z() { WRITE(Z_ENABLE_PIN,!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
 		#endif
 	#endif
 #else
-  #define enable_z() {}
-  #define disable_z() {}
+    #define  poweron_z() {}
+    #define poweroff_z() {}
 #endif
 
-#ifdef PSU_Delta
+#ifndef PSU_Delta
+    #define  enable_z()  poweron_z()
+    #define disable_z() poweroff_z()
+#else
     void init_force_z();
     void check_force_z();
-    #undef disable_z
-    #define disable_z() disable_force_z()
-    void disable_force_z();
-    #undef enable_z
-    #define enable_z() enable_force_z()
     void enable_force_z();
+    void disable_force_z();
+    #define  enable_z()  enable_force_z()
+    #define disable_z() disable_force_z()
 #endif // PSU_Delta
 
 
-
-
 //#if defined(Z_ENABLE_PIN) && Z_ENABLE_PIN > -1
 //#ifdef Z_DUAL_STEPPER_DRIVERS
 //#define  enable_z() { WRITE(Z_ENABLE_PIN, Z_ENABLE_ON); WRITE(Z2_ENABLE_PIN, Z_ENABLE_ON); }
@@ -295,7 +294,7 @@ void setPwmFrequency(uint8_t pin, int val);
 
 extern bool fans_check_enabled;
 extern float homing_feedrate[];
-extern bool axis_relative_modes[];
+extern uint8_t axis_relative_modes;
 extern float feedrate;
 extern int feedmultiply;
 extern int extrudemultiply; // Sets extrude multiply factor (in percent) for all extruders
@@ -445,9 +444,8 @@ void setup_uvlo_interrupt();
 void setup_fan_interrupt();
 #endif
 
-//extern void recover_machine_state_after_power_panic();
-extern void recover_machine_state_after_power_panic(bool bTiny);
-extern void restore_print_from_eeprom();
+extern bool recover_machine_state_after_power_panic();
+extern void restore_print_from_eeprom(bool mbl_was_active);
 extern void position_menu();
 
 extern void print_world_coordinates();

+ 388 - 318
Firmware/Marlin_main.cpp

@@ -46,6 +46,7 @@
 //-//
 #include "Configuration.h"
 #include "Marlin.h"
+#include "config.h"
   
 #ifdef ENABLE_AUTO_BED_LEVELING
 #include "vector_3.h"
@@ -178,9 +179,13 @@ float default_retraction = DEFAULT_RETRACTION;
 
 
 float homing_feedrate[] = HOMING_FEEDRATE;
-// Currently only the extruder axis may be switched to a relative mode.
-// Other axes are always absolute or relative based on the common relative_mode flag.
-bool axis_relative_modes[] = AXIS_RELATIVE_MODES;
+
+//Although this flag and many others like this could be represented with a struct/bitfield for each axis (more readable and efficient code), the implementation
+//would not be standard across all platforms. That being said, the code will continue to use bitmasks for independent axis.
+//Moreover, according to C/C++ standard, the ordering of bits is platform/compiler dependent and the compiler is allowed to align the bits arbitrarily,
+//thus bit operations like shifting and masking may stop working and will be very hard to fix.
+uint8_t axis_relative_modes = 0;
+
 int feedmultiply=100; //100->1 200->2
 int extrudemultiply=100; //100->1 200->2
 int extruder_multiply[EXTRUDERS] = {100
@@ -640,6 +645,9 @@ void failstats_reset_print()
 	eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
 	eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
 	eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+    fsensor_softfail = 0;
+#endif
 }
 
 
@@ -707,6 +715,12 @@ static void factory_reset(char level)
 
             eeprom_update_dword((uint32_t *)EEPROM_TOTALTIME, 0);
             eeprom_update_dword((uint32_t *)EEPROM_FILAMENTUSED, 0);
+
+			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_X, 0);
+			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0);
+			eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0);
+			eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
+
             eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_X_TOT, 0);
             eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_Y_TOT, 0);
             eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
@@ -1298,10 +1312,6 @@ void setup()
 
 	st_init();    // Initialize stepper, this enables interrupts!
   
-#ifdef UVLO_SUPPORT
-    setup_uvlo_interrupt();
-#endif //UVLO_SUPPORT
-
 #ifdef TMC2130
 	tmc2130_mode = silentMode?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 	update_mode_profile();
@@ -1314,10 +1324,17 @@ void setup()
 	setup_photpin();
 
 	servo_init();
+
 	// Reset the machine correction matrix.
 	// It does not make sense to load the correction matrix until the machine is homed.
 	world2machine_reset();
-    
+
+    // Initialize current_position accounting for software endstops to
+    // avoid unexpected initial shifts on the first move
+    clamp_to_software_endstops(current_position);
+    plan_set_position(current_position[X_AXIS], current_position[Y_AXIS],
+                      current_position[Z_AXIS], current_position[E_AXIS]);
+
 #ifdef FILAMENT_SENSOR
 	fsensor_init();
 #endif //FILAMENT_SENSOR
@@ -1327,29 +1344,12 @@ void setup()
 	SET_OUTPUT(CONTROLLERFAN_PIN); //Set pin used for driver cooling fan
 #endif
 
-
 	setup_homepin();
 
-#ifdef TMC2130
-
-  if (1) {
-    // try to run to zero phase before powering the Z motor.    
-    // Move in negative direction
-    WRITE(Z_DIR_PIN,INVERT_Z_DIR);
-    // Round the current micro-micro steps to micro steps.
-    for (uint16_t phase = (tmc2130_rd_MSCNT(Z_AXIS) + 8) >> 4; phase > 0; -- phase) {
-      // Until the phase counter is reset to zero.
-      WRITE(Z_STEP_PIN, !INVERT_Z_STEP_PIN);
-      _delay(2);
-      WRITE(Z_STEP_PIN, INVERT_Z_STEP_PIN);
-      _delay(2);
-    }
-  }
-#endif //TMC2130
-
-#if defined(Z_AXIS_ALWAYS_ON) && !defined(PSU_Delta)
-	enable_z();
+#if defined(Z_AXIS_ALWAYS_ON)
+    enable_z();
 #endif
+
 	farm_mode = eeprom_read_byte((uint8_t*)EEPROM_FARM_MODE);
 	EEPROM_read_B(EEPROM_FARM_NUMBER, &farm_no);
 	if ((farm_mode == 0xFF && farm_no == 0) || (farm_no == static_cast<int>(0xFFFF))) farm_mode = false; //if farm_mode has not been stored to eeprom yet and farm number is set to zero or EEPROM is fresh, deactivate farm mode
@@ -1613,12 +1613,14 @@ void setup()
               lcd_update(2); 
               lcd_setstatuspgm(_T(WELCOME_MSG)); 
           } 
-           
       }
-
-	   
   }
+
+  // Only arm the uvlo interrupt _after_ a recovering print has been initialized and
+  // the entire state machine initialized.
+  setup_uvlo_interrupt();
 #endif //UVLO_SUPPORT
+
   fCheckModeInit();
   fSetMmuMode(mmu_enabled);
   KEEPALIVE_STATE(NOT_BUSY);
@@ -1900,10 +1902,6 @@ static void axis_is_at_home(int axis) {
   max_pos[axis] =          base_max_pos(axis) + cs.add_homing[axis];
 }
 
-
-inline void set_current_to_destination() { memcpy(current_position, destination, sizeof(current_position)); }
-inline void set_destination_to_current() { memcpy(destination, current_position, sizeof(destination)); }
-
 //! @return original feedmultiply
 static int setup_for_endstop_move(bool enable_endstops_now = true) {
     saved_feedrate = feedrate;
@@ -2198,6 +2196,21 @@ bool calibrate_z_auto()
 }
 #endif //TMC2130
 
+#ifdef TMC2130
+static void check_Z_crash(void)
+{
+	if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
+		FORCE_HIGH_POWER_END;
+		current_position[Z_AXIS] = 0;
+		plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
+		current_position[Z_AXIS] += MESH_HOME_Z_SEARCH;
+		plan_buffer_line_curposXYZE(max_feedrate[Z_AXIS], active_extruder);
+		st_synchronize();
+		kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+	}
+}
+#endif //TMC2130
+
 #ifdef TMC2130
 void homeaxis(int axis, uint8_t cnt, uint8_t* pstep)
 #else
@@ -2314,11 +2327,7 @@ void homeaxis(int axis, uint8_t cnt)
         plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
         st_synchronize();
 #ifdef TMC2130
-		if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
-			FORCE_HIGH_POWER_END;
-			kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-			return; 
-		}
+        check_Z_crash();
 #endif //TMC2130
         current_position[axis] = 0;
         plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
@@ -2330,11 +2339,7 @@ void homeaxis(int axis, uint8_t cnt)
         plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
         st_synchronize();
 #ifdef TMC2130
-		if (READ(Z_TMC2130_DIAG) != 0) { //Z crash
-			FORCE_HIGH_POWER_END;
-			kill(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-			return; 
-		}
+        check_Z_crash();
 #endif //TMC2130
         axis_is_at_home(axis);
         destination[axis] = current_position[axis];
@@ -3855,6 +3860,17 @@ void process_commands()
     } else if(code_seen("FR")) { // PRUSA FR
         // Factory full reset
         factory_reset(0);
+    } else if(code_seen("MBL")) { // PRUSA MBL
+        // Change the MBL status without changing the logical Z position.
+        if(code_seen("V")) {
+            bool value = code_value_short();
+            st_synchronize();
+            if(value != mbl.active) {
+                mbl.active = value;
+                // Use plan_set_z_position to reset the physical values
+                plan_set_z_position(current_position[Z_AXIS]);
+            }
+        }
 
 //-//
 /*
@@ -5004,7 +5020,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 			
 			#ifdef SUPPORT_VERBOSITY
 			if (verbosity_level >= 1) {
-				clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+				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");
 			}
@@ -5372,21 +5388,19 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 
     /*!
 	### G90 - Switch off relative mode <a href="https://reprap.org/wiki/G-code#G90:_Set_to_Absolute_Positioning">G90: Set to Absolute Positioning</a>
-	All coordinates from now on are absolute relative to the origin of the machine. E axis is also switched to absolute mode.
+	All coordinates from now on are absolute relative to the origin of the machine. E axis is left intact.
     */
     case 90: {
-        for(uint8_t i = 0; i != NUM_AXIS; ++i)
-            axis_relative_modes[i] = false;
+		axis_relative_modes &= ~(X_AXIS_MASK | Y_AXIS_MASK | Z_AXIS_MASK);
     }
     break;
 
     /*!
 	### G91 - Switch on relative mode <a href="https://reprap.org/wiki/G-code#G91:_Set_to_Relative_Positioning">G91: Set to Relative Positioning</a>
-    All coordinates from now on are relative to the last position. E axis is also switched to relative mode.
+    All coordinates from now on are relative to the last position. E axis is left intact.
 	*/
     case 91: {
-        for(uint8_t i = 0; i != NUM_AXIS; ++i)
-            axis_relative_modes[i] = true;
+		axis_relative_modes |= X_AXIS_MASK | Y_AXIS_MASK | Z_AXIS_MASK;
     }
     break;
 
@@ -5584,10 +5598,15 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
           lcd_resume_print();
       else
       {
-          failstats_reset_print();
+          if (!card.get_sdpos())
+          {
+              // A new print has started from scratch, reset stats
+              failstats_reset_print();
 #ifndef LA_NOCOMPAT
-          la10c_reset();
+              la10c_reset();
 #endif
+          }
+
           card.startFileprint();
           starttime=_millis();
       }
@@ -5697,12 +5716,19 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         if(code_seen('S'))
           if(strchr_pointer<namestartpos) //only if "S" is occuring _before_ the filename
             card.setIndex(code_value_long());
-#ifndef LA_NOCOMPAT
-        la10c_reset();
-#endif
         card.startFileprint();
         if(!call_procedure)
-          starttime=_millis(); //procedure calls count as normal print time.
+        {
+            if(!card.get_sdpos())
+            {
+                // A new print has started from scratch, reset stats
+                failstats_reset_print();
+#ifndef LA_NOCOMPAT
+                la10c_reset();
+#endif
+            }
+            starttime=_millis(); // procedure calls count as normal print time.
+        }
       }
     } break;
 
@@ -6553,7 +6579,7 @@ Sigma_Exit:
 	Makes the extruder interpret extrusion as absolute positions.
     */
     case 82:
-      axis_relative_modes[E_AXIS] = false;
+      axis_relative_modes &= ~E_AXIS_MASK;
       break;
 
     /*!
@@ -6561,7 +6587,7 @@ Sigma_Exit:
 	Makes the extruder interpret extrusion values as relative positions.
     */
     case 83:
-      axis_relative_modes[E_AXIS] = true;
+      axis_relative_modes |= E_AXIS_MASK;
       break;
 
     /*!
@@ -6670,7 +6696,7 @@ Sigma_Exit:
       {
         if(code_seen(axis_codes[i]))
         {
-          if(i == 3) { // E
+          if(i == E_AXIS) { // E
             float value = code_value();
             if(value < 20.0) {
               float factor = cs.axis_steps_per_unit[i] / value; // increase e constants if M92 E14 is given for netfab.
@@ -6679,6 +6705,9 @@ Sigma_Exit:
               axis_steps_per_sqr_second[i] *= factor;
             }
             cs.axis_steps_per_unit[i] = value;
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+            fsensor_set_axis_steps_per_unit(value);
+#endif
           }
           else {
             cs.axis_steps_per_unit[i] = code_value();
@@ -7085,9 +7114,16 @@ Sigma_Exit:
       if(code_seen('X')) cs.max_jerk[X_AXIS] = cs.max_jerk[Y_AXIS] = code_value();
       if(code_seen('Y')) cs.max_jerk[Y_AXIS] = code_value();
       if(code_seen('Z')) cs.max_jerk[Z_AXIS] = code_value();
-      if(code_seen('E')) cs.max_jerk[E_AXIS] = code_value();
-		if (cs.max_jerk[X_AXIS] > DEFAULT_XJERK) cs.max_jerk[X_AXIS] = DEFAULT_XJERK;
-		if (cs.max_jerk[Y_AXIS] > DEFAULT_YJERK) cs.max_jerk[Y_AXIS] = DEFAULT_YJERK;
+      if(code_seen('E'))
+      {
+          float e = code_value();
+#ifndef LA_NOCOMPAT
+          e = la10c_jerk(e);
+#endif
+          cs.max_jerk[E_AXIS] = e;
+      }
+      if (cs.max_jerk[X_AXIS] > DEFAULT_XJERK) cs.max_jerk[X_AXIS] = DEFAULT_XJERK;
+      if (cs.max_jerk[Y_AXIS] > DEFAULT_YJERK) cs.max_jerk[Y_AXIS] = DEFAULT_YJERK;
     }
     break;
 
@@ -8418,7 +8454,6 @@ Sigma_Exit:
 				res_valid |= (i == E_AXIS) && ((res_new == 64) || (res_new == 128)); // resolutions valid for E only
 				if (res_valid)
 				{
-					
 					st_synchronize();
 					uint16_t res = tmc2130_get_res(i);
 					tmc2130_set_res(i, res_new);
@@ -8435,6 +8470,10 @@ Sigma_Exit:
 						cs.axis_steps_per_unit[i] /= fac;
 						position[i] /= fac;
 					}
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+                    if (i == E_AXIS)
+                        fsensor_set_axis_steps_per_unit(cs.axis_steps_per_unit[i]);
+#endif
 				}
 			}
 		}
@@ -8783,17 +8822,23 @@ Sigma_Exit:
     This command can be used without any additional parameters. It will read the entire RAM.
     #### Usage
     
-        D3 [ A | C | X ]
+        D2 [ A | C | X ]
     
     #### Parameters
-    - `A` - Address (0x0000-0x1fff)
-    - `C` - Count (0x0001-0x2000)
+    - `A` - Address (x0000-x1fff)
+    - `C` - Count (1-8192)
     - `X` - Data
+
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
     */
 	case 2:
 		dcode_2(); break;
 #endif //DEBUG_DCODES
-#ifdef DEBUG_DCODE3
+#if defined DEBUG_DCODE3 || defined DEBUG_DCODES
 
     /*!
     ### D3 - Read/Write EEPROM <a href="https://reprap.org/wiki/G-code#D3:_Read.2FWrite_EEPROM">D3: Read/Write EEPROM</a>
@@ -8803,9 +8848,15 @@ Sigma_Exit:
         D3 [ A | C | X ]
     
     #### Parameters
-    - `A` - Address (0x0000-0x0fff)
-    - `C` - Count (0x0001-0x1000)
-    - `X` - Data
+    - `A` - Address (x0000-x0fff)
+    - `C` - Count (1-4096)
+    - `X` - Data (hex)
+	
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
     */
 	case 3:
 		dcode_3(); break;
@@ -8828,24 +8879,29 @@ Sigma_Exit:
 	case 4:
 		dcode_4(); break;
 #endif //DEBUG_DCODES
-#ifdef DEBUG_DCODE5
+#if defined DEBUG_DCODE5 || defined DEBUG_DCODES
 
     /*!
     ### D5 - Read/Write FLASH <a href="https://reprap.org/wiki/G-code#D5:_Read.2FWrite_FLASH">D5: Read/Write Flash</a>
     This command can be used without any additional parameters. It will read the 1kb FLASH.
     #### Usage
     
-        D3 [ A | C | X | E ]
+        D5 [ A | C | X | E ]
     
     #### Parameters
-    - `A` - Address (0x00000-0x3ffff)
-    - `C` - Count (0x0001-0x2000)
-    - `X` - Data
+    - `A` - Address (x00000-x3ffff)
+    - `C` - Count (1-8192)
+    - `X` - Data (hex)
     - `E` - Erase
-    */
+ 	
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal 
+	- The hex data needs to be lowercase
+	
+   */
 	case 5:
 		dcode_5(); break;
-		break;
 #endif //DEBUG_DCODE5
 #ifdef DEBUG_DCODES
 
@@ -8906,7 +8962,7 @@ Sigma_Exit:
 
     /*!
     ### D12 - Time <a href="https://reprap.org/wiki/G-code#D12:_Time">D12: Time</a>
-    Writes the actual time in the log file.
+    Writes the current time in the log file.
     */
 
 #endif //DEBUG_DCODES
@@ -8928,28 +8984,7 @@ Sigma_Exit:
     - `J` - Offset Y (default 34)
   */
 	case 80:
-	{
-		float dimension_x = 40;
-		float dimension_y = 40;
-		int points_x = 40;
-		int points_y = 40;
-		float offset_x = 74;
-		float offset_y = 33;
-
-		if (code_seen('E')) dimension_x = code_value();
-		if (code_seen('F')) dimension_y = code_value();
-		if (code_seen('G')) {points_x = code_value(); }
-		if (code_seen('H')) {points_y = code_value(); }
-		if (code_seen('I')) {offset_x = code_value(); }
-		if (code_seen('J')) {offset_y = code_value(); }
-		printf_P(PSTR("DIM X: %f\n"), dimension_x);
-		printf_P(PSTR("DIM Y: %f\n"), dimension_y);
-		printf_P(PSTR("POINTS X: %d\n"), points_x);
-		printf_P(PSTR("POINTS Y: %d\n"), points_y);
-		printf_P(PSTR("OFFSET X: %f\n"), offset_x);
-		printf_P(PSTR("OFFSET Y: %f\n"), offset_y);
- 		bed_check(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
-	}break;
+		dcode_80(); break;
 
     /*!
     ### D81 - Bed analysis <a href="https://reprap.org/wiki/G-code#D81:_Bed_analysis">D80: Bed analysis</a>
@@ -8967,24 +9002,7 @@ Sigma_Exit:
     - `J` - Offset Y (default 34)
   */
 	case 81:
-	{
-		float dimension_x = 40;
-		float dimension_y = 40;
-		int points_x = 40;
-		int points_y = 40;
-		float offset_x = 74;
-		float offset_y = 33;
-
-		if (code_seen('E')) dimension_x = code_value();
-		if (code_seen('F')) dimension_y = code_value();
-		if (code_seen("G")) { strchr_pointer+=1; points_x = code_value(); }
-		if (code_seen("H")) { strchr_pointer+=1; points_y = code_value(); }
-		if (code_seen("I")) { strchr_pointer+=1; offset_x = code_value(); }
-		if (code_seen("J")) { strchr_pointer+=1; offset_y = code_value(); }
-		
-		bed_analysis(dimension_x,dimension_y,points_x,points_y,offset_x,offset_y);
-		
-	} break;
+		dcode_81(); break;
 	
 #endif //HEATBED_ANALYSIS
 #ifdef DEBUG_DCODES
@@ -8993,17 +9011,7 @@ Sigma_Exit:
     ### D106 - Print measured fan speed for different pwm values <a href="https://reprap.org/wiki/G-code#D106:_Print_measured_fan_speed_for_different_pwm_values">D106: Print measured fan speed for different pwm values</a>
     */
 	case 106:
-	{
-		for (int i = 255; i > 0; i = i - 5) {
-			fanSpeed = i;
-			//delay_keep_alive(2000);
-			for (int j = 0; j < 100; j++) {
-				delay_keep_alive(100);
-
-			}
-			printf_P(_N("%d: %d\n"), i, fan_speed[1]);
-		}
-	}break;
+		dcode_106(); break;
 
 #ifdef TMC2130
     /*!
@@ -9061,7 +9069,6 @@ Sigma_Exit:
       For more information see https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2130_datasheet.pdf
     *
 	*/
-
 	case 2130:
 		dcode_2130(); break;
 #endif //TMC2130
@@ -9105,8 +9112,8 @@ Sigma_Exit:
 #### End of D-Codes
 */
 
-  /** @defgroup GCodes G-Code List 
-  */
+/** @defgroup GCodes G-Code List 
+*/
 
 // ---------------------------------------------------
 
@@ -9171,7 +9178,7 @@ void get_coordinates()
   for(int8_t i=0; i < NUM_AXIS; i++) {
     if(code_seen(axis_codes[i]))
     {
-      bool relative = axis_relative_modes[i];
+      bool relative = axis_relative_modes & (1 << i);
       destination[i] = (float)code_value();
       if (i == E_AXIS) {
         float emult = extruder_multiplier[active_extruder];
@@ -9441,10 +9448,15 @@ static void handleSafetyTimer()
 }
 #endif //SAFETYTIMER
 
+#define FS_CHECK_COUNT 15
 void manage_inactivity(bool ignore_stepper_queue/*=false*/) //default argument set in Marlin.h
 {
-bool bInhibitFlag;
 #ifdef FILAMENT_SENSOR
+bool bInhibitFlag;
+#ifdef IR_SENSOR_ANALOG
+static uint8_t nFSCheckCount=0;
+#endif // IR_SENSOR_ANALOG
+
 	if (mmu_enabled == false)
 	{
 //-//		if (mcode_in_progress != 600) //M600 not in progress
@@ -9453,11 +9465,35 @@ bool bInhibitFlag;
 #endif // PAT9125
 #ifdef IR_SENSOR
           bInhibitFlag=(menu_menu==lcd_menu_show_sensors_state); // Support::SensorInfo menu active
+#ifdef IR_SENSOR_ANALOG
+          bInhibitFlag=bInhibitFlag||bMenuFSDetect; // Settings::HWsetup::FSdetect menu active
+#endif // IR_SENSOR_ANALOG
 #endif // IR_SENSOR
           if ((mcode_in_progress != 600) && (eFilamentAction != FilamentAction::AutoLoad) && (!bInhibitFlag)) //M600 not in progress, preHeat @ autoLoad menu not active, Support::ExtruderInfo/SensorInfo menu not active
 		{
 			if (!moves_planned() && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal) && ! eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE))
 			{
+#ifdef IR_SENSOR_ANALOG
+                    bool bTemp=current_voltage_raw_IR>IRsensor_Hmin_TRESHOLD;
+                    bTemp=bTemp&&current_voltage_raw_IR<IRsensor_Hopen_TRESHOLD;
+                    bTemp=bTemp&&(!CHECK_ALL_HEATERS);
+                    bTemp=bTemp&&(menu_menu==lcd_status_screen);
+                    bTemp=bTemp&&((oFsensorPCB==ClFsensorPCB::_Old)||(oFsensorPCB==ClFsensorPCB::_Undef));
+                    bTemp=bTemp&&fsensor_enabled;
+                    if(bTemp)
+                    {
+                         nFSCheckCount++;
+                         if(nFSCheckCount>FS_CHECK_COUNT)
+                         {
+                              nFSCheckCount=0;    // not necessary
+                              oFsensorPCB=ClFsensorPCB::_Rev04;
+                              eeprom_update_byte((uint8_t*)EEPROM_FSENSOR_PCB,(uint8_t)oFsensorPCB);
+                              printf_IRSensorAnalogBoardChange(true);
+                              lcd_setstatuspgm(_i("FS v0.4 or newer"));
+                         }
+                    }
+                    else nFSCheckCount=0;
+#endif // IR_SENSOR_ANALOG
 				if (fsensor_check_autoload())
 				{
 #ifdef PAT9125
@@ -9498,7 +9534,8 @@ if(0)
 #ifdef PAT9125
 				fsensor_autoload_check_stop();
 #endif //PAT9125
-				fsensor_update();
+                if (fsensor_enabled && !saved_printing)
+                    fsensor_update();
 			}
 		}
 	}
@@ -9603,7 +9640,7 @@ void kill(const char *full_screen_message, unsigned char id)
   disable_x();
 //  SERIAL_ECHOLNPGM("kill - disable Y");
   disable_y();
-  disable_z();
+  poweroff_z();
   disable_e0();
   disable_e1();
   disable_e2();
@@ -10490,6 +10527,16 @@ void serialecho_temperatures() {
 }
 
 #ifdef UVLO_SUPPORT
+void uvlo_drain_reset()
+{
+    // burn all that residual power
+    wdt_enable(WDTO_1S);
+    WRITE(BEEPER,HIGH);
+    lcd_clear();
+    lcd_puts_at_P(0, 1, MSG_POWERPANIC_DETECTED);
+    while(1);
+}
+
 
 void uvlo_()
 {
@@ -10511,16 +10558,11 @@ void uvlo_()
 	tmc2130_set_current_r(E_AXIS, 20);
 #endif //TMC2130
 
-
-    // Indicate that the interrupt has been triggered.
-	//	SERIAL_ECHOLNPGM("UVLO");
-
-    // Read out the current Z motor microstep counter. This will be later used
-    // for reaching the zero full step before powering off.
-    uint16_t z_microsteps = 0;
-#ifdef TMC2130
-	z_microsteps = tmc2130_rd_MSCNT(Z_TMC2130_CS);
-#endif //TMC2130
+    // Stop all heaters
+    uint8_t saved_target_temperature_bed = target_temperature_bed;
+    uint16_t saved_target_temperature_ext = target_temperature[active_extruder];
+    setAllTargetHotends(0);
+    setTargetBed(0);
 
     // Calculate the file position, from which to resume this print.
     long sd_position = sdpos_atomic; //atomic sd position of last command added in queue
@@ -10534,7 +10576,7 @@ void uvlo_()
 
     // save the global state at planning time
     uint16_t feedrate_bckp;
-    if (blocks_queued())
+    if (current_block)
     {
         memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
         feedrate_bckp = current_block->gcode_feedrate;
@@ -10545,75 +10587,81 @@ void uvlo_()
         feedrate_bckp = feedrate;
     }
 
+    // From this point on and up to the print recovery, Z should not move during X/Y travels and
+    // should be controlled precisely. Reset the MBL status before planner_abort_hard in order to
+    // get the physical Z for further manipulation.
+    bool mbl_was_active = mbl.active;
+    mbl.active = false;
+
     // After this call, the planner queue is emptied and the current_position is set to a current logical coordinate.
     // The logical coordinate will likely differ from the machine coordinate if the skew calibration and mesh bed leveling
     // are in action.
     planner_abort_hard();
 
-	// Store the current extruder position.
-	eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E), st_get_position_mm(E_AXIS));
-	eeprom_update_byte((uint8_t*)EEPROM_UVLO_E_ABS, axis_relative_modes[3]?0:1);
-    // Clean the input command queue.
+    // Store the print logical Z position, which we need to recover (a slight error here would be
+    // recovered on the next Gcode instruction, while a physical location error would not)
+    float logical_z = current_position[Z_AXIS];
+    if(mbl_was_active) logical_z -= mbl.get_z(st_get_position_mm(X_AXIS), st_get_position_mm(Y_AXIS));
+    eeprom_update_float((float*)EEPROM_UVLO_CURRENT_POSITION_Z, logical_z);
+
+    // Store the print E position before we lose track
+	eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E), current_position[E_AXIS]);
+	eeprom_update_byte((uint8_t*)EEPROM_UVLO_E_ABS, (axis_relative_modes & E_AXIS_MASK)?0:1);
+
+    // Clean the input command queue, inhibit serial processing using saved_printing
     cmdqueue_reset();
     card.sdprinting = false;
-//    card.closefile();    
-    // Enable stepper driver interrupt to move Z axis.
-    // This should be fine as the planner and command queues are empty and the SD card printing is disabled.
-    //FIXME one may want to disable serial lines at this point of time to avoid interfering with the command queue,
-    // though it should not happen that the command queue is touched as the plan_buffer_line always succeed without blocking.
-		sei();
-		plan_buffer_line(
-      current_position[X_AXIS], 
-      current_position[Y_AXIS], 
-      current_position[Z_AXIS], 
-      current_position[E_AXIS] - default_retraction,
-      95, active_extruder);
-    
-        st_synchronize();
-        disable_e0();
-    
-    plan_buffer_line(
-      current_position[X_AXIS],
-      current_position[Y_AXIS],
-      current_position[Z_AXIS] + UVLO_Z_AXIS_SHIFT + float((1024 - z_microsteps + 7) >> 4) / cs.axis_steps_per_unit[Z_AXIS],
-      current_position[E_AXIS] - default_retraction,
-      40, active_extruder);
+    saved_printing = true;
+
+    // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and
+    // command queues are empty, SD card printing is disabled, usb is inhibited.
+    sei();
+
+    // Retract
+    current_position[E_AXIS] -= default_retraction;
+    plan_buffer_line_curposXYZE(95, active_extruder);
     st_synchronize();
     disable_e0();
 
-    plan_buffer_line(
-      current_position[X_AXIS],
-      current_position[Y_AXIS],
-      current_position[Z_AXIS] + UVLO_Z_AXIS_SHIFT + float((1024 - z_microsteps + 7) >> 4) / cs.axis_steps_per_unit[Z_AXIS],
-      current_position[E_AXIS] - default_retraction,
-      40, active_extruder);
+    // Read out the current Z motor microstep counter to move the axis up towards
+    // a full step before powering off. NOTE: we need to ensure to schedule more
+    // than "dropsegments" steps in order to move (this is always the case here
+    // due to UVLO_Z_AXIS_SHIFT being used)
+    uint16_t z_res = tmc2130_get_res(Z_AXIS);
+    uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS);
+    current_position[Z_AXIS] += float(1024 - z_microsteps)
+                                / (z_res * cs.axis_steps_per_unit[Z_AXIS])
+                                + UVLO_Z_AXIS_SHIFT;
+    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60, active_extruder);
     st_synchronize();
+    poweroff_z();
 
-    disable_e0();
-    // Move Z up to the next 0th full step.
     // Write the file position.
     eeprom_update_dword((uint32_t*)(EEPROM_FILE_POSITION), sd_position);
+
     // Store the mesh bed leveling offsets. This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case.
     for (int8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) {
       uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
       uint8_t iy = mesh_point / MESH_NUM_X_POINTS;
       // Scale the z value to 1u resolution.
-      int16_t v = mbl.active ? int16_t(floor(mbl.z_values[iy][ix] * 1000.f + 0.5f)) : 0;
+      int16_t v = mbl_was_active ? int16_t(floor(mbl.z_values[iy][ix] * 1000.f + 0.5f)) : 0;
       eeprom_update_word((uint16_t*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL +2*mesh_point), *reinterpret_cast<uint16_t*>(&v));
     }
-    // Read out the current Z motor microstep counter. This will be later used
-    // for reaching the zero full step before powering off.
+
+    // Write the _final_ Z position and motor microstep counter (unused).
+    eeprom_update_float((float*)EEPROM_UVLO_TINY_CURRENT_POSITION_Z, current_position[Z_AXIS]);
+    z_microsteps = tmc2130_rd_MSCNT(Z_AXIS);
     eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps);
-    // Store the current position.
 
+    // 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]);
-    eeprom_update_float((float*)EEPROM_UVLO_CURRENT_POSITION_Z , current_position[Z_AXIS]);
+
     // Store the current feed rate, temperatures, fan speed and extruder multipliers (flow rates)
 	eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDRATE, feedrate_bckp);
-    EEPROM_save_B(EEPROM_UVLO_FEEDMULTIPLY, &feedmultiply);
-    eeprom_update_byte((uint8_t*)EEPROM_UVLO_TARGET_HOTEND, target_temperature[active_extruder]);
-    eeprom_update_byte((uint8_t*)EEPROM_UVLO_TARGET_BED, target_temperature_bed);
+    eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY, feedmultiply);
+    eeprom_update_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND, saved_target_temperature_ext);
+    eeprom_update_byte((uint8_t*)EEPROM_UVLO_TARGET_BED, saved_target_temperature_bed);
     eeprom_update_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED, fanSpeed);
 	eeprom_update_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_0), extruder_multiplier[0]);
 #if EXTRUDERS > 1
@@ -10637,71 +10685,88 @@ void uvlo_()
     // Finaly store the "power outage" flag.
 	if(sd_print) eeprom_update_byte((uint8_t*)EEPROM_UVLO, 1);
 
-    st_synchronize();
-    printf_P(_N("stps%d\n"), tmc2130_rd_MSCNT(Z_AXIS));
-    
     // Increment power failure counter
 	eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) + 1);
 	eeprom_update_word((uint16_t*)EEPROM_POWER_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) + 1);
-      printf_P(_N("UVLO - end %d\n"), _millis() - time_start);
 
-#if 0
-    // Move the print head to the side of the print until all the power stored in the power supply capacitors is depleted.
+    printf_P(_N("UVLO - end %d\n"), _millis() - time_start);
+    WRITE(BEEPER,HIGH);
+
+    // All is set: with all the juice left, try to move extruder away to detach the nozzle completely from the print
+    poweron_z();
     current_position[X_AXIS] = (current_position[X_AXIS] < 0.5f * (X_MIN_POS + X_MAX_POS)) ? X_MIN_POS : X_MAX_POS;
     plan_buffer_line_curposXYZE(500, active_extruder);
     st_synchronize();
-#endif
-wdt_enable(WDTO_500MS);
-WRITE(BEEPER,HIGH);
-while(1)
-     ;
+
+    wdt_enable(WDTO_1S);
+    while(1);
 }
 
 
 void uvlo_tiny()
 {
-uint16_t z_microsteps=0;
+    unsigned long time_start = _millis();
 
-// Conserve power as soon as possible.
-disable_x();
-disable_y();
-disable_e0();
-    
-#ifdef TMC2130
-tmc2130_set_current_h(Z_AXIS, 20);
-tmc2130_set_current_r(Z_AXIS, 20);
-#endif //TMC2130
+    // Conserve power as soon as possible.
+    disable_x();
+    disable_y();
+    disable_e0();
 
-// Read out the current Z motor microstep counter
 #ifdef TMC2130
-z_microsteps=tmc2130_rd_MSCNT(Z_TMC2130_CS);
+    tmc2130_set_current_h(Z_AXIS, 20);
+    tmc2130_set_current_r(Z_AXIS, 20);
 #endif //TMC2130
-planner_abort_hard();
 
-//save current position only in case, where the printer is moving on Z axis, which is only when EEPROM_UVLO is 1
-//EEPROM_UVLO is 1 after normal uvlo or after recover_print(), when the extruder is moving on Z axis after rehome
-if(eeprom_read_byte((uint8_t*)EEPROM_UVLO)!=2){
-  eeprom_update_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z), current_position[Z_AXIS]);
-  eeprom_update_word((uint16_t*)(EEPROM_UVLO_TINY_Z_MICROSTEPS),z_microsteps);
-}
+    // Stop all heaters
+    setAllTargetHotends(0);
+    setTargetBed(0);
 
-//after multiple power panics current Z axis is unknow
-//in this case we set EEPROM_UVLO_TINY_CURRENT_POSITION_Z to last know position which is EEPROM_UVLO_CURRENT_POSITION_Z 
-if(eeprom_read_float((float*)EEPROM_UVLO_TINY_CURRENT_POSITION_Z) < 0.001f){
-  eeprom_update_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z), eeprom_read_float((float*)EEPROM_UVLO_CURRENT_POSITION_Z));
-  eeprom_update_word((uint16_t*)(EEPROM_UVLO_TINY_Z_MICROSTEPS), eeprom_read_word((uint16_t*)EEPROM_UVLO_Z_MICROSTEPS));
-}
+    // When power is interrupted on the _first_ recovery an attempt can be made to raise the
+    // extruder, causing the Z position to change. Similarly, when recovering, the Z position is
+    // lowered. In such cases we cannot just save Z, we need to re-align the steppers to a fullstep.
+    // Disable MBL (if not already) to work with physical coordinates.
+    mbl.active = false;
+    planner_abort_hard();
+
+    // Allow for small roundoffs to be ignored
+    if(abs(current_position[Z_AXIS] - eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))) >= 1.f/cs.axis_steps_per_unit[Z_AXIS])
+    {
+        // Clean the input command queue, inhibit serial processing using saved_printing
+        cmdqueue_reset();
+        card.sdprinting = false;
+        saved_printing = true;
+
+        // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and
+        // command queues are empty, SD card printing is disabled, usb is inhibited.
+        sei();
+
+        // The axis was moved: adjust Z as done on a regular UVLO.
+        uint16_t z_res = tmc2130_get_res(Z_AXIS);
+        uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS);
+        current_position[Z_AXIS] += float(1024 - z_microsteps)
+                                    / (z_res * cs.axis_steps_per_unit[Z_AXIS])
+                                    + UVLO_TINY_Z_AXIS_SHIFT;
+        plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60, active_extruder);
+        st_synchronize();
+        poweroff_z();
+
+        // Update Z position
+        eeprom_update_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z), current_position[Z_AXIS]);
+
+        // Update the _final_ Z motor microstep counter (unused).
+        z_microsteps = tmc2130_rd_MSCNT(Z_AXIS);
+        eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps);
+    }
+
+    // Update the the "power outage" flag.
+    eeprom_update_byte((uint8_t*)EEPROM_UVLO,2);
 
-// Finaly store the "power outage" flag.
-eeprom_update_byte((uint8_t*)EEPROM_UVLO,2);
+    // Increment power failure counter
+    eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) + 1);
+    eeprom_update_word((uint16_t*)EEPROM_POWER_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) + 1);
 
-// Increment power failure counter
-eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) + 1);
-eeprom_update_word((uint16_t*)EEPROM_POWER_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) + 1);
-wdt_enable(WDTO_500MS);
-WRITE(BEEPER,HIGH);
-while(1)
-     ;
+    printf_P(_N("UVLO_TINY - end %d\n"), _millis() - time_start);
+    uvlo_drain_reset();
 }
 #endif //UVLO_SUPPORT
 
@@ -10748,12 +10813,19 @@ void setup_uvlo_interrupt() {
 	DDRE &= ~(1 << 4); //input pin
 	PORTE &= ~(1 << 4); //no internal pull-up
 
-						//sensing falling edge
+    // sensing falling edge
 	EICRB |= (1 << 0);
 	EICRB &= ~(1 << 1);
 
-	//enable INT4 interrupt
+	// enable INT4 interrupt
 	EIMSK |= (1 << 4);
+
+    // check if power was lost before we armed the interrupt
+    if(!(PINE & (1 << 4)) && eeprom_read_byte((uint8_t*)EEPROM_UVLO))
+    {
+        SERIAL_ECHOLNPGM("INT4");
+        uvlo_drain_reset();
+    }
 }
 
 ISR(INT4_vect) {
@@ -10770,44 +10842,55 @@ void recover_print(uint8_t automatic) {
 	lcd_update(2);
   lcd_setstatuspgm(_i("Recovering print    "));////MSG_RECOVERING_PRINT c=20 r=1
 
-      bool bTiny=(eeprom_read_byte((uint8_t*)EEPROM_UVLO)==2);
-      recover_machine_state_after_power_panic(bTiny); //recover position, temperatures and extrude_multipliers
-  // Lift the print head, so one may remove the excess priming material.
-      if(!bTiny&&(current_position[Z_AXIS]<25))
-          enquecommand_P(PSTR("G1 Z25 F800"));
+  // Recover position, temperatures and extrude_multipliers
+  bool mbl_was_active = recover_machine_state_after_power_panic();
 
-  // Home X and Y axes. Homing just X and Y shall not touch the babystep and the world2machine transformation status.
+  // Lift the print head 25mm, first to avoid collisions with oozed material with the print,
+  // and second also so one may remove the excess priming material.
+  if(eeprom_read_byte((uint8_t*)EEPROM_UVLO) == 1)
+  {
+      sprintf_P(cmd, PSTR("G1 Z%.3f F800"), current_position[Z_AXIS] + 25);
+      enquecommand(cmd);
+  }
+
+  // Home X and Y axes. Homing just X and Y shall not touch the babystep and the world2machine
+  // transformation status. G28 will not touch Z when MBL is off.
 	enquecommand_P(PSTR("G28 X Y"));
   // Set the target bed and nozzle temperatures and wait.
-	sprintf_P(cmd, PSTR("M109 S%d"), target_temperature[active_extruder]);
+	sprintf_P(cmd, PSTR("M104 S%d"), target_temperature[active_extruder]);
 	enquecommand(cmd);
 	sprintf_P(cmd, PSTR("M190 S%d"), target_temperature_bed);
 	enquecommand(cmd);
+	sprintf_P(cmd, PSTR("M109 S%d"), target_temperature[active_extruder]);
+	enquecommand(cmd);
+
 	enquecommand_P(PSTR("M83")); //E axis relative mode
-	//enquecommand_P(PSTR("G1 E5 F120")); //Extrude some filament to stabilize pessure
-    // If not automatically recoreverd (long power loss), extrude extra filament to stabilize 
-    if(automatic == 0){ 
-        enquecommand_P(PSTR("G1 E5 F120")); //Extrude some filament to stabilize pessure 
-    } 
-	enquecommand_P(PSTR("G1 E"  STRINGIFY(-default_retraction)" F480"));
+
+    // If not automatically recoreverd (long power loss)
+    if(automatic == 0){
+        //Extrude some filament to stabilize the pressure
+        enquecommand_P(PSTR("G1 E5 F120"));
+        // Retract to be consistent with a short pause
+        sprintf_P(cmd, PSTR("G1 E%-0.3f F2700"), default_retraction);
+        enquecommand(cmd);
+    }
 
 	printf_P(_N("After waiting for temp:\nCurrent pos X_AXIS:%.3f\nCurrent pos Y_AXIS:%.3f\n"), current_position[X_AXIS], current_position[Y_AXIS]);
 
   // Restart the print.
-	restore_print_from_eeprom();
+  restore_print_from_eeprom(mbl_was_active);
   printf_P(_N("Current pos Z_AXIS:%.3f\nCurrent pos E_AXIS:%.3f\n"), current_position[Z_AXIS], current_position[E_AXIS]);
 }
 
-void recover_machine_state_after_power_panic(bool bTiny)
+bool recover_machine_state_after_power_panic()
 {
-  char cmd[30];
-  // 1) Recover the logical cordinates at the time of the power panic.
-  // The logical XY coordinates are needed to recover the machine Z coordinate corrected by the mesh bed leveling.
-  current_position[X_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0));
-  current_position[Y_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4));
-
-  // 2) Restore the mesh bed leveling offsets. This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case.
-  mbl.active = false;
+  // 1) Preset some dummy values for the XY axes
+  current_position[X_AXIS] = 0;
+  current_position[Y_AXIS] = 0;
+
+  // 2) Restore the mesh bed leveling offsets, but not the MBL status.
+  // This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case.
+  bool mbl_was_active = false;
   for (int8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) {
     uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
     uint8_t iy = mesh_point / MESH_NUM_X_POINTS;
@@ -10815,32 +10898,16 @@ void recover_machine_state_after_power_panic(bool bTiny)
     int16_t v;
     eeprom_read_block(&v, (void*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL+2*mesh_point), 2);
     if (v != 0)
-      mbl.active = true;
+      mbl_was_active = true;
     mbl.z_values[iy][ix] = float(v) * 0.001f;
   }
 
-  // Recover the logical coordinate of the Z axis at the time of the power panic.
+  // Recover the physical coordinate of the Z axis at the time of the power panic.
   // The current position after power panic is moved to the next closest 0th full step.
-  if(bTiny){    
-    current_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))
-     + float((1024 - eeprom_read_word((uint16_t*)(EEPROM_UVLO_TINY_Z_MICROSTEPS)) 
-    + 7) >> 4) / cs.axis_steps_per_unit[Z_AXIS];
-
-    //after multiple power panics the print is slightly in the air so get it little bit down. 
-    //Not exactly sure why is this happening, but it has something to do with bed leveling and world2machine coordinates 
-    current_position[Z_AXIS] -= 0.4*mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS]); 
-  }
-  else{
-    current_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z)) + 
-    UVLO_Z_AXIS_SHIFT + float((1024 - eeprom_read_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS)) 
-    + 7) >> 4) / cs.axis_steps_per_unit[Z_AXIS];
-  }
-  if (eeprom_read_byte((uint8_t*)EEPROM_UVLO_E_ABS)) {
-	  current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E));
-	  sprintf_P(cmd, PSTR("G92 E"));
-	  dtostrf(current_position[E_AXIS], 6, 3, cmd + strlen(cmd));
-	  enquecommand(cmd);
-  }
+  current_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z));
+
+  // Recover last E axis position
+  current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E));
 
   memcpy(destination, current_position, sizeof(destination));
 
@@ -10856,20 +10923,16 @@ void recover_machine_state_after_power_panic(bool bTiny)
   // The baby stepping value is used to reset the physical Z axis when rehoming the Z axis.
   babystep_load();
 
-  // 5) Set the physical positions from the logical positions using the world2machine transformation and the active bed leveling.
+  // 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.
   plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
 
-  // 6) Power up the motors, mark their positions as known.
-  //FIXME Verfiy, whether the X and Y axes should be powered up here, as they will later be re-homed anyway.
-  axis_known_position[X_AXIS] = true; enable_x();
-  axis_known_position[Y_AXIS] = true; enable_y();
-  axis_known_position[Z_AXIS] = true; enable_z();
-  
-  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
-  print_physical_coordinates();
+  // 6) Power up the Z motors, mark their positions as known.
+  axis_known_position[Z_AXIS] = true;
+  enable_z();
 
   // 7) Recover the target temperatures.
-  target_temperature[active_extruder] = eeprom_read_byte((uint8_t*)EEPROM_UVLO_TARGET_HOTEND);
+  target_temperature[active_extruder] = eeprom_read_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND);
   target_temperature_bed = eeprom_read_byte((uint8_t*)EEPROM_UVLO_TARGET_BED);
 
   // 8) Recover extruder multipilers
@@ -10891,9 +10954,11 @@ void recover_machine_state_after_power_panic(bool bTiny)
 #ifdef LIN_ADVANCE
   extruder_advance_K = eeprom_read_float((float*)EEPROM_UVLO_LA_K);
 #endif
+
+  return mbl_was_active;
 }
 
-void restore_print_from_eeprom() {
+void restore_print_from_eeprom(bool mbl_was_active) {
 	int feedrate_rec;
 	int feedmultiply_rec;
 	uint8_t fan_speed_rec;
@@ -10904,7 +10969,7 @@ void restore_print_from_eeprom() {
 
 	fan_speed_rec = eeprom_read_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED);
     feedrate_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDRATE);
-	EEPROM_read_B(EEPROM_UVLO_FEEDMULTIPLY, &feedmultiply_rec);
+    feedmultiply_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY);
 	SERIAL_ECHOPGM("Feedrate:");
 	MYSERIAL.print(feedrate_rec);
 	SERIAL_ECHOPGM(", feedmultiply:");
@@ -10934,30 +10999,38 @@ void restore_print_from_eeprom() {
 	enquecommand(cmd);
 	uint32_t position = eeprom_read_dword((uint32_t*)(EEPROM_FILE_POSITION));
 	SERIAL_ECHOPGM("Position read from eeprom:");
-	MYSERIAL.println(position);	
-  // E axis relative mode.
-	enquecommand_P(PSTR("M83"));
-  // Move to the XY print position in logical coordinates, where the print has been killed.
-	strcpy_P(cmd, PSTR("G1 X")); strcat(cmd, ftostr32(eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0))));
-	strcat_P(cmd, PSTR(" Y"));   strcat(cmd, ftostr32(eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4))));
-	strcat_P(cmd, PSTR(" F2000"));
+	MYSERIAL.println(position);
+
+    // 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);
-  //moving on Z axis ahead, set EEPROM_UVLO to 1, so normal uvlo can fire
-  eeprom_update_byte((uint8_t*)EEPROM_UVLO,1);
-  // Move the Z axis down to the print, in logical coordinates.
-	strcpy_P(cmd, PSTR("G1 Z")); strcat(cmd, ftostr32(eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z))));
+
+    // Enable MBL and switch to logical positioning
+    if (mbl_was_active)
+        enquecommand_P(PSTR("PRUSA MBL V1"));
+
+    // Move the Z axis down to the print, in logical coordinates.
+    sprintf_P(cmd, PSTR("G1 Z%f"), eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z)));
 	enquecommand(cmd);
+
   // Unretract.
-	enquecommand_P(PSTR("G1 E"  STRINGIFY(2*default_retraction)" F480"));
+    sprintf_P(cmd, PSTR("G1 E%0.3f F2700"), default_retraction);
+    enquecommand(cmd);
+  // Recover final E axis position and mode
+    float pos_e = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E));
+    sprintf_P(cmd, PSTR("G92 E"));
+    dtostrf(pos_e, 6, 3, cmd + strlen(cmd));
+    enquecommand(cmd);
+    if (eeprom_read_byte((uint8_t*)EEPROM_UVLO_E_ABS))
+        enquecommand_P(PSTR("M82")); //E axis abslute mode
   // Set the feedrates saved at the power panic.
 	sprintf_P(cmd, PSTR("G1 F%d"), feedrate_rec);
 	enquecommand(cmd);
 	sprintf_P(cmd, PSTR("M220 S%d"), feedmultiply_rec);
 	enquecommand(cmd);
-	if (eeprom_read_byte((uint8_t*)EEPROM_UVLO_E_ABS))
-	{
-	  enquecommand_P(PSTR("M82")); //E axis abslute mode
-	}
   // Set the fan speed saved at the power panic.
 	strcpy_P(cmd, PSTR("M106 S"));
 	strcat(cmd, itostr3(int(fan_speed_rec)));
@@ -11106,7 +11179,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
 #endif
 
   // save the global state at planning time
-  if (blocks_queued())
+  if (current_block)
   {
       memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
       saved_feedrate2 = current_block->gcode_feedrate;
@@ -11122,7 +11195,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
     saved_feedmultiply2 = feedmultiply; //save feedmultiply
 	saved_active_extruder = active_extruder; //save active_extruder
 	saved_extruder_temperature = degTargetHotend(active_extruder);
-	saved_extruder_relative_mode = axis_relative_modes[E_AXIS];
+	saved_extruder_relative_mode = axis_relative_modes & E_AXIS_MASK;
 	saved_fanSpeed = fanSpeed;
 	cmdqueue_reset(); //empty cmdqueue
 	card.sdprinting = false;
@@ -11204,7 +11277,7 @@ void restore_print_from_ram_and_continue(float e_move)
 		wait_for_heater(_millis(), saved_active_extruder);
 		heating_status = 2;
 	}
-	axis_relative_modes[E_AXIS] = saved_extruder_relative_mode;
+	axis_relative_modes ^= (-saved_extruder_relative_mode ^ axis_relative_modes) & E_AXIS_MASK;
 	float e = saved_pos[E_AXIS] - e_move;
 	plan_set_e_position(e);
   
@@ -11254,6 +11327,7 @@ void restore_print_from_ram_and_continue(float e_move)
 // Cancel the state related to a currently saved print
 void cancel_saved_printing()
 {
+    eeprom_update_byte((uint8_t*)EEPROM_UVLO, 0);
     saved_target[0] = SAVED_TARGET_UNSET;
     saved_printing_type = PRINTING_TYPE_NONE;
     saved_printing = false;
@@ -11565,8 +11639,6 @@ if(!(bEnableForce_z||eeprom_read_byte((uint8_t*)EEPROM_SILENT)))
 
 void disable_force_z()
 {
-    uint16_t z_microsteps=0;
-
     if(!bEnableForce_z) return;   // motor already disabled (may be ;-p )
 
     bEnableForce_z=false;
@@ -11577,8 +11649,6 @@ void disable_force_z()
     update_mode_profile();
     tmc2130_init(true);
 #endif // TMC2130
-
-    axis_known_position[Z_AXIS]=false;
 }
 
 

+ 19 - 10
Firmware/cardreader.cpp

@@ -136,8 +136,17 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						SERIAL_ECHOPGM("Access date: ");
 						MYSERIAL.println(p.lastAccessDate);
 						SERIAL_ECHOLNPGM("");*/
-						modificationDate = p.lastWriteDate;
-						modificationTime = p.lastWriteTime;
+						crmodDate = p.lastWriteDate;
+						crmodTime = p.lastWriteTime;
+						// There are scenarios when simple modification time is not enough (on MS Windows)
+						// For example - extract an old g-code from an archive onto the SD card.
+						// In such case the creation time is current time (which is correct), but the modification time
+						// stays the same - i.e. old.
+						// Therefore let's pick the most recent timestamp from both creation and modification timestamps
+						if( crmodDate < p.creationDate || ( crmodDate == p.creationDate && crmodTime < p.creationTime ) ){
+							crmodDate = p.creationDate;
+							crmodTime = p.creationTime;
+						}
 						//writeDate = p.lastAccessDate;
 						if (match != NULL) {
 							if (strcasecmp(match, filename) == 0) return;
@@ -773,8 +782,8 @@ void CardReader::presort() {
 		// retaining only two filenames at a time. This is very
 		// slow but is safest and uses minimal RAM.
 		char name1[LONG_FILENAME_LENGTH + 1];
-		uint16_t modification_time_bckp;
-		uint16_t modification_date_bckp;
+		uint16_t crmod_time_bckp;
+		uint16_t crmod_date_bckp;
 
 		#endif
 		position = 0;
@@ -800,8 +809,8 @@ void CardReader::presort() {
 				#else
 				// Copy filenames into the static array
 				strcpy(sortnames[i], LONGEST_FILENAME);
-				modification_time[i] = modificationTime;
-				modification_date[i] = modificationDate;
+				modification_time[i] = crmodTime;
+				modification_date[i] = crmodDate;
 				#if SDSORT_CACHE_NAMES
 				strcpy(sortshort[i], filename);
 				#endif
@@ -830,8 +839,8 @@ void CardReader::presort() {
 																	(modification_date[o1] < modification_date [o2]))
 			#else
 			#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0) //true if lowercase(name1) > lowercase(name2)
-			#define _SORT_CMP_TIME_NODIR() (((modification_date_bckp == modificationDate) && (modification_time_bckp > modificationTime)) || \
-																	(modification_date_bckp > modificationDate))
+			#define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp > crmodTime)) || \
+																	(crmod_date_bckp > crmodDate))
 
 			#endif
 
@@ -882,8 +891,8 @@ void CardReader::presort() {
 					counter++;
 					getfilename_simple(positions[o1]);
 					strcpy(name1, LONGEST_FILENAME); // save (or getfilename below will trounce it)
-					modification_date_bckp = modificationDate;
-					modification_time_bckp = modificationTime;
+					crmod_date_bckp = crmodDate;
+					crmod_time_bckp = crmodTime;
 					#if HAS_FOLDER_SORTING
 					bool dir1 = filenameIsDir;
 					#endif

+ 3 - 1
Firmware/cardreader.h

@@ -75,7 +75,9 @@ public:
   bool sdprinting ;  
   bool cardOK ;
   char filename[13];
-  uint16_t modificationTime, modificationDate;
+  // There are scenarios when simple modification time is not enough (on MS Windows)
+  // Therefore these timestamps hold the most recent one of creation/modification date/times
+  uint16_t crmodTime, crmodDate;
   uint32_t cluster, position;
   char longFilename[LONG_FILENAME_LENGTH];
   bool filenameIsDir;

+ 7 - 4
Firmware/config.h

@@ -5,10 +5,12 @@
 #include "Configuration_prusa.h"
 #include "pins.h"
 
-#define IR_SENSOR_ANALOG (defined(VOLT_IR_PIN) && defined(IR_SENSOR))
+#if (defined(VOLT_IR_PIN) && defined(IR_SENSOR))
+# define IR_SENSOR_ANALOG
+#endif
 
 //ADC configuration
-#if !IR_SENSOR_ANALOG
+#ifndef IR_SENSOR_ANALOG
 #define ADC_CHAN_MSK      0b0000001001011111 //used AD channels bit mask (0,1,2,3,4,6,9)
 #define ADC_DIDR_MSK      0b0000001001011111 //AD channels DIDR mask (1 ~ disabled digital input)
 #define ADC_CHAN_CNT      7         //number of used channels)
@@ -34,7 +36,8 @@
 //#define PAT9125_I2C_ADDR  0x79  //ID=HI
 //#define PAT9125_I2C_ADDR  0x73  //ID=NC
 #define PAT9125_XRES      0
-#define PAT9125_YRES      240
+#define PAT9125_YRES      240                   // maximum resolution (5*X cpi)
+#define PAT9124_YRES_MM   (5*PAT9125_YRES/25.4) // counts per mm
 
 //SM4 configuration
 #define SM4_DEFDELAY      500       //default step delay [us]
@@ -55,7 +58,7 @@
 #define W25X20CL_SPSR          SPI_SPSR(W25X20CL_SPI_RATE)
 
 //LANG - Multi-language support
-//define LANG_MODE              0 // primary language only
+//#define LANG_MODE              0 // primary language only
 #define LANG_MODE              1 // sec. language support
 
 #define LANG_SIZE_RESERVED     0x3000 // reserved space for secondary language (12288 bytes)

+ 354 - 9
Firmware/eeprom.h

@@ -1,3 +1,13 @@
+
+/**
+ * @file
+ * @author 3d-gussner
+ */
+ /** \ingroup eeprom_table */
+ 
+ //! _This is a EEPROM table of currently implemented in Prusa firmware (dynamically generated from doxygen)._
+
+
 #ifndef EEPROM_H
 #define EEPROM_H
 
@@ -26,6 +36,338 @@ typedef struct
 #ifdef __cplusplus
 static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEPROM_SHEETS_SIZEOF.");
 #endif
+/** @defgroup eeprom_table EEPROM Table
+ *  
+ 
+  ---------------------------------------------------------------------------------
+  EEPROM 8-bit Empty value = 0xFFh 255
+  
+  EEPROM 16-bit Empty value = 0xFFFFh 65535
+  
+  _Italic = unused or default_
+  
+  __Bold = Status__
+  
+  In Default/FactoryReset column the 
+  
+   - __L__		Language
+   - __S__ 		Statistics
+   - __P__ 		Shipping prep
+   - __S/P__	Statistics and Shipping prep
+   
+  will overwrite existing values to 0 or default.
+  A FactoryReset All Data will overwrite the whole EEPROM with ffh and some values will be initialized automatically,
+  others need a reset / reboot.
+  
+  ---------------------------------------------------------------------------------
+  How can you use the debug codes?
+  - Serial terminal like Putty.
+  - Octoprint does support D-codes
+  - _Pronterface_ does __not__ support D-codes
+
+  ### !!! D-codes are case sensitive so please don't use upper case A,C or X in the address you want to read !!!
+   
+  #### Useful tools/links:
+  To convert hex to ascii 		https://www.rapidtables.com/convert/number/hex-to-ascii.html
+  
+  To convert hex to dec 		https://www.rapidtables.com/convert/number/hex-to-decimal.html
+  
+  Version: 1.0.1
+  
+  ---------------------------------------------------------------------------------
+  
+  
+| Address begin		| Bit/Type 	| Name 									| Valid values	| Default/FactoryReset	| Description 										| Gcode/Function| Debug code
+| :--				| :-- 		| :-- 									| :--:			| :--:					| :--												| :--:			| :--:
+| 0x0FFFh 4095		| uchar    	| EEPROM_SILENT 						| 00h 0			| ffh 255				| TMC Stealth mode: __off__ / miniRambo Power mode	| LCD menu		| D3 Ax0fff C1
+| ^ 				| ^ 		| ^										| 01h 1			| ^						| TMC Stealth mode: __on__ / miniRambo Silent mode	| ^				| ^ 
+| 0x0FFEh 4094		| uchar    	| EEPROM_LANG 							| 00h 0			| ffh 255		__L__	| English / LANG_ID_PRI								| LCD menu		| D3 Ax0ffe C1 
+| ^ 				| ^ 		| ^										| 01h 1			| ^						| Other language LANG_ID_SEC						| ^ 			| ^
+| 0x0FFCh 4092		| uint16	| EEPROM_BABYSTEP_X						| ???			| ff ffh 65535			| Babystep for X axis _unsued_						| ??? 			| D3 Ax0ffc C2
+| 0x0FFAh 4090		| uint16	| EEPROM_BABYSTEP_Y						| ???			| ff ffh 65535			| Babystep for Y axis _unsued_						| ^ 			| D3 Ax0ffa C2
+| 0x0FF8h 4088		| uint16	| EEPROM_BABYSTEP_Z						| ???			| ff ffh 65535			| Babystep for Z axis _lagacy_						| ^ 			| D3 Ax0ff8 C2
+| ^ 				| ^ 		| ^										| ^				| ^						| multiple values stored now in EEPROM_Sheets_base	| ^ 			| ^
+| 0x0FF7h 4087		| uint8		| EEPROM_CALIBRATION_STATUS				| ffh 255		| ffh 255				| Assembled _default_								| ??? 			| D3 Ax0ff7 C1
+| ^ 				| ^ 		| ^										| 01h 1			| ^						| Calibrated										| ^ 			| ^
+| ^ 				| ^ 		| ^										| e6h 230		| ^						| needs Live Z adjustment							| ^ 			| ^
+| ^ 				| ^ 		| ^										| f0h 240		| ^				__P__	| needs Z calibration								| ^ 			| ^
+| ^ 				| ^ 		| ^										| fah 250		| ^						| needs XYZ calibration								| ^ 			| ^ 
+| ^ 				| ^ 		| ^										| 00h 0			| ^						| Unknown											| ^ 			| ^
+| 0x0FF5h 4085		| uint16	| EEPROM_BABYSTEP_Z0					| ???			| ff ffh 65535			| Babystep for Z ???								| ??? 			| D3 Ax0ff5 C2
+| 0x0FF1h 4081		| uint32	| EEPROM_FILAMENTUSED					| ???			| 00 00 00 00h 0 __S/P__| Filament used in meters							| ??? 			| D3 Ax0ff1 C4
+| 0x0FEDh 4077		| uint32	| EEPROM_TOTALTIME						| ???			| 00 00 00 00h 0 __S/P__| Total print time									| ??? 			| D3 Ax0fed C4
+| 0x0FE5h 4069		| float		| EEPROM_BED_CALIBRATION_CENTER			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fe5 C8
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0FDDh 4061		| float		| EEPROM_BED_CALIBRATION_VEC_X			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fdd C8
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^	
+| 0x0FD5h 4053		| float		| EEPROM_BED_CALIBRATION_VEC_Y			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fd5 C8
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0FC5h 4037		| int16		| EEPROM_BED_CALIBRATION_Z_JITTER		| ???			| ff ffh 65535			| ???											 	| ??? 			| D3 Ax0fc5 C16
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^	
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0FC4h 4036		| bool		| EEPROM_FARM_MODE						| 00h 0			| ffh 255 		__P__	| Prusa farm mode: __off__							| G99 			| D3 Ax0fc4 C1
+| ^					| ^			| ^										| 01h 1			| ^						| Prusa farm mode: __on__							| G98			| ^
+| 0x0FC3h 4035		| free		| _EEPROM_FREE_NR1_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0fc3 C1
+| 0x0FC1h 4033		| ???		| EEPROM_FARM_NUMBER					| 000-999		| ff ffh / 000	__P__	| Prusa farm number	_only 0-9 are allowed: 000-999_	| LCD menu		| D3 Ax0fc1 C2
+| 0x0FC0h 4032		| bool		| EEPROM_BED_CORRECTION_VALID			| 00h 0			| 00h 0					| Bed correction invalid							| ??? 			| D3 Ax0fc0 C1
+| ^					| ^			| ^										| ffh 255		| 						| Bed correction valid								| ??? 			| ^
+| 0x0FBFh 4031		| char		| EEPROM_BED_CORRECTION_LEFT			| 00h ffh		| 00h 0					| Bed manual correction left						| LCD menu 		| D3 Ax0fbf C1
+| ^					| ^			| ^										| ^				| ^						| At this moment limited to +-100um					| G80 Lxxx 			| ^
+| 0x0FBEh 4030		| char		| EEPROM_BED_CORRECTION_RIGHT			| 00h ffh		| 00h 0					| Bed manual correction right						| LCD menu 		| D3 Ax0fbe C1
+| ^					| ^			| ^										| ^				| ^						| At this moment limited to +-100um					| G80 Rxxx 			| ^
+| 0x0FBDh 4029		| char		| EEPROM_BED_CORRECTION_FRONT			| 00h ffh		| 00h 0					| Bed manual correction front						| LCD menu 		| D3 Ax0fbd C1
+| ^					| ^			| ^										| ^				| ^						| At this moment limited to +-100um					| G80 Fxxx 			| ^
+| 0x0FBCh 4028		| char		| EEPROM_BED_CORRECTION_BACK			| 00h ffh		| 00h 0					| Bed manual correction back						| LCD menu 		| D3 Ax0fbc C1
+| ^					| ^			| ^										| ^				| ^						| At this moment limited to +-100um					| G80 Bxxx 			| ^
+| 0x0FBBh 4027		| bool		| EEPROM_TOSHIBA_FLASH_AIR_COMPATIBLITY	| 00h 0			| ffh 255				| Toshiba Air: __off__								| LCD menu 		| D3 Ax0fbb C1
+| ^					| ^			| ^										| 01h 1			| ^						| Toshiba Air: __on__								| ^ 			| ^	
+| 0x0FBAh 4026		| uchar		| EEPROM_PRINT_FLAG						| ???			| ???					| _unsued_											| ??? 			| D3 Ax0fba C1
+| 0x0FB0h 4016		| int16		| EEPROM_PROBE_TEMP_SHIFT				| ???			| ???					| ???												| ??? 			| D3 Ax0fb0 C10
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0FAFh 4015		| bool		| EEPROM_TEMP_CAL_ACTIVE				| 00h 0			| 00h 0					| PINDA Temp cal.: __inactive__						| LCD menu		| D3 Ax0faf C1
+| ^					| ^			| ^										| ffh 255		| ^						| PINDA Temp cal.: __active__						| ^ 			| ^
+| 0x0FA7h 4007		| uint32	| EEPROM_BOWDEN_LENGTH					| ???			| ff 00 00 00h			| Bowden length										| ??? 			| D3 Ax0fae C8
+| ^					| ^			| ^										| ^				| 00 00 00 00h			| ^													| ^ 			| ^
+| 0x0FA6h 4006		| uint8		| EEPROM_CALIBRATION_STATUS_PINDA		| 00h 0			| ffh 255				| PINDA Temp: __not calibrated__					| ??? 			| D3 Ax0fa6 C1
+| ^					| ^			| ^										| 01h 1			| ^						| PINDA Temp: __calibrated__						| ^ 			| ^
+| 0x0FA5h 4005		| uint8		| EEPROM_UVLO							| 00h 0			| ffh 255				| Power Panic flag: __inactive__					| ??? 			| D3 Ax0fa5 C1
+| ^					| ^			| ^										| 01h 1			| ^						| Power Panic flag: __active__						| ^ 			| ^
+| ^					| ^			| ^										| 02h 2			| ^						| Power Panic flag: __???__							| ^ 			| ^
+| 0x0F9Dh 3997		| float		| EEPROM_UVLO_CURRENT_POSITION			| ???			| ffh 255				| Power Panic position 								| ??? 			| D3 Ax0f9d C8
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0F95h 3989		| char		| EEPROM_FILENAME						| ???			| ffh 255				| Power Panic Filename 								| ??? 			| D3 Ax0f95 C8
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0F91h 39851		| uint32	| EEPROM_FILE_POSITION					| ???			| ff ff ff ffh			| Power Panic File Position							| ??? 			| D3 Ax0f91 C4
+| 0x0F8Dh 3981		| float		| EEPROM_UVLO_CURRENT_POSITION_Z		| ???			| ff ff ff ffh			| Power Panic Z Position	 						| ^ 			| D3 Ax0f8d C4
+| 0x0F8Ch 3980		| ???		| EEPROM_UVLO_UNUSED_001				| ??? 			| ffh 255				| Power Panic _unused_								| ^ 			| D3 Ax0f8c C1
+| 0x0F8Bh 3979		| uint8		| EEPROM_UVLO_TARGET_BED				| ???			| ffh 255				| Power Panic Bed temperature						| ^ 			| D3 Ax0f8b C1
+| 0x0F89h 3977		| uint16	| EEPROM_UVLO_FEEDRATE					| ???			| ff ffh 65535			| Power Panic Feedrate								| ^ 			| D3 Ax0f89 C2
+| 0x0F88h 3976		| uint8		| EEPROM_UVLO_FAN_SPEED					| ???			| ffh 255				| Power Panic Fan speed								| ^ 			| D3 Ax0f88 C1
+| 0x0F87h 3975		| uint8		| EEPROM_FAN_CHECK_ENABLED				| 00h 0			| ???					| Fan Check __disabled__							| LCD menu		| D3 Ax0f87 C1
+| ^					| ^			| ^										| 01h 1			| ffh 255				| Fan Check __enabled__ 							| ^ 			| ^
+| 0x0F75h 3957		| uint16	| EEPROM_UVLO_MESH_BED_LEVELING			| ???			| ff ffh 65535			| Power Panic Mesh Bed Leveling						| ???			| D3 Ax0f75 C18 
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
+| 0x0F73h 3955		| uint16	| EEPROM_UVLO_Z_MICROSTEPS				| ???			| ff ffh 65535			| Power Panic Z microsteps							| ???			| D3 Ax0f73 C2 
+| 0x0F72h 3954		| uint8		| EEPROM_UVLO_E_ABS						| ???			| ffh 255				| Power Panic ??? position							| ???			| D3 Ax0f72 C1
+| 0x0F6Eh 3950		| foat		| EEPROM_UVLO_CURRENT_POSITION_E		| ???			| ff ff ff ffh			| Power Panic E position							| ???			| D3 Ax0f6e C4
+| 0x0F6Dh 3949		| ???		| _EEPROM_FREE_NR2_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0f6d C1
+| 0x0F6Ch 3948		| ???		| _EEPROM_FREE_NR3_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0f6c C1
+| 0x0F6Bh 3947		| ???		| _EEPROM_FREE_NR4_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0f6b C1
+| 0x0F6Ah 3946		| ???		| _EEPROM_FREE_NR5_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0f6a C1
+| 0x0F69h 3945		| uint8		| EEPROM_CRASH_DET						| ffh 255		| ffh 255				| Crash detection: __enabled__						| LCD menu		| D3 Ax0f69 C1
+| ^					| ^			| ^										| 00h 0			| ^						| Crash detection: __disabled__						| LCD menu		| ^
+| 0x0F68h 3944		| uint8		| EEPROM_CRASH_COUNT_Y					| 00h-ffh 0-255	| ffh 255		__S/P__	| Crashes detected on y axis						| ???			| D3 Ax0f68 C1
+| 0x0F67h 3943		| uint8		| EEPROM_FSENSOR						| 01h 1			| ffh 255		__P__	| Filament sensor: __enabled__						| LCD menu		| D3 Ax0f67 C1
+| ^					| ^			| ^										| 00h 0			| ^						| Filament sensor: __disabled__						| LCD menu		| ^
+| 0x0F65h 3942		| uint8		| EEPROM_CRASH_COUNT_X					| 00h-ffh 0-255	| ffh 255		__S/P__	| Crashes detected on x axis						| ???			| D3 Ax0f66 C1
+| 0x0F65h 3941		| uint8		| EEPROM_FERROR_COUNT					| 00h-ffh 0-255	| ffh 255		__S/P__	| Filament sensor error counter						| ???			| D3 Ax0f65 C1
+| 0x0F64h 3940		| uint8		| EEPROM_POWER_COUNT					| 00h-ffh 0-255	| ffh 255		__S/P__	| Power failure counter								| ???			| D3 Ax0f64 C1
+| 0x0F60h 3936		| float		| EEPROM_XYZ_CAL_SKEW					| ???			| ff ff ff ffh			| XYZ skew value									| ???			| D3 Ax0f60 C4
+| 0x0F5Fh 3935		| uint8		| EEPROM_WIZARD_ACTIVE					| 01h 1			| 01h 1			__P__	| Wizard __active__									| ???			| D3 Ax0f5f C1
+| ^					| ^			| ^										| 00h 0			| ^						| Wizard __inactive__								| ^ 			| ^
+| 0x0F5Dh 3933		| uint16	| EEPROM_BELTSTATUS_X					| ???			| ff ffh				| X Beltstatus 										| ???			| D3 Ax0f5d C2
+| 0x0F5Bh 3931		| uint16	| EEPROM_BELTSTATUS_Y					| ???			| ff ffh				| Y Beltstatus 										| ???			| D3 Ax0f5b C2
+| 0x0F5Ah 3930		| uint8		| EEPROM_DIR_DEPTH						| 00h-ffh 0-255	| ffh 255				| Directory depth									| ???			| D3 Ax0f5a C1
+| 0x0F0Ah 3850		| uint8		| EEPROM_DIRS							| ???			| ffh 255				| Directories ???									| ???			| D3 Ax0f0a C80
+| 0x0F09h 3849		| uint8		| EEPROM_SD_SORT						| 00h 0			| ffh 255				| SD card sort by: 	__time__						| LCD menu		| D3 Ax0f09 C1
+| ^					| ^			| ^										| 01h 1			| ^						| SD card sort by: 	__alphabet__					| LCD menu		| ^
+| ^					| ^			| ^										| 02h 1			| ^						| SD card:			__not sorted__					| LCD menu		| ^
+| 0x0F08h 3848		| uint8		| EEPROM_SECOND_SERIAL_ACTIVE			| 00h 0			| ffh 255				| RPi Port: __disabled__							| LCD menu		| D3 Ax0f08 C1
+| ^					| ^			| ^										| 01h 1			| ^						| RPi Port: __enabled__								| LCD menu		| ^
+| 0x0F07h 3847		| uint8		| EEPROM_FSENS_AUTOLOAD_ENABLED			| 01h 1			| ffh 255		__P__	| Filament autoload: __enabled__					| LCD menu		| D3 Ax0f07 C1
+| ^					| ^			| ^										| 00h 0			| ^						| Filament autoload: __disabled__					| LCD menu		| ^
+| 0x0F05h 3845		| uint16	| EEPROM_CRASH_COUNT_X_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total crashes on x axis	  						| ???			| D3 Ax0f05 C2
+| 0x0F03h 3843		| uint16	| EEPROM_CRASH_COUNT_Y_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total crashes on y axis  							| ???			| D3 Ax0f03 C2
+| 0x0F01h 3841		| uint16	| EEPROM_FERROR_COUNT_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total filament sensor errors 						| ???			| D3 Ax0f01 C2
+| 0x0EFFh 3839		| uint16	| EEPROM_POWER_COUNT_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total power failures		  						| ???			| D3 Ax0eff C2
+| 0x0EFEh 3838		| uint8		| EEPROM_TMC2130_HOME_X_ORIGIN			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efe C1
+| 0x0EFDh 3837		| uint8		| EEPROM	MC2130_HOME_X_BSTEPS			| ???			| ffh 255			| ???						  						| ???			| D3 Ax0efd C1
+| 0x0EFCh 3836		| uint8		| EEPROM_TMC2130_HOME_X_FSTEPS			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efc C1
+| 0x0EFBh 3835		| uint8		| EEPROM_TMC2130_HOME_Y_ORIGIN			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efb C1
+| 0x0EFAh 3834		| uint8		| EEPROM_TMC2130_HOME_Y_BSTEPS			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efa C1
+| 0x0EF9h 3833		| uint8		| EEPROM_TMC2130_HOME_Y_FSTEPS			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef9 C1
+| 0x0EF8h 3832		| uint8		| EEPROM_TMC2130_HOME_ENABLED			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef8 C1
+| 0x0EF7h 3831		| uint8		| EEPROM_TMC2130_WAVE_X_FAC				| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef7 C1
+| 0x0EF6h 3830		| uint8		| EEPROM_TMC2130_WAVE_Y_FAC				| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef6 C1
+| 0x0EF5h 3829		| uint8		| EEPROM_TMC2130_WAVE_Z_FAC				| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef5 C1
+| 0x0EF4h 3828		| uint8		| EEPROM_TMC2130_WAVE_E_FAC				| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef4 C1
+| 0x0EF3h 3827		| uint8		| EEPROM_TMC2130_X_MRES					| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef3 C1
+| 0x0EF2h 3826		| uint8		| EEPROM_TMC2130_Y_MRES					| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef2 C1
+| 0x0EF1h 3825		| uint8		| EEPROM_TMC2130_Z_MRES					| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef1 C1
+| 0x0EF0h 3824		| uint8		| EEPROM_TMC2130_E_MRES					| ???			| ffh 255				| ???						  						| ???			| D3 Ax0ef0 C1
+| 0x0EEE 3822		| uint16 	| EEPROM_PRINTER_TYPE					| ???			| ff ffh 65535			| Printer Type										| ???			| D3 Ax0eee C2
+| ^					| ^			| ^										| 64 00h 100	| ^						| PRINTER_MK1										| ???			| ^
+| ^					| ^			| ^										| c8 00h 200	| ^						| PRINTER_MK2										| ???			| ^
+| ^					| ^			| ^										| c9 00h 201	| ^						| PRINTER_MK2 with MMU1								| ???			| ^
+| ^					| ^			| ^										| ca 00h 202	| ^						| PRINTER_MK2S										| ???			| ^
+| ^					| ^			| ^										| cb 00h 203	| ^						| PRINTER_MK2S with MMU1							| ???			| ^
+| ^					| ^			| ^										| fa 00h 250	| ^						| PRINTER_MK2.5										| ???			| ^
+| ^					| ^			| ^										| 1a 4fh 20250	| ^						| PRINTER_MK2.5 with MMU2							| ???			| ^
+| ^					| ^			| ^										| fc 00h 252	| ^						| PRINTER_MK2.5S									| ???			| ^
+| ^					| ^			| ^										| 1c 4fh 20252	| ^						| PRINTER_MK2.5S with MMU2S							| ???			| ^
+| ^					| ^			| ^										| 2c 01h 300	| ^						| PRINTER_MK3										| ???			| ^
+| ^					| ^			| ^										| 4c 4fh 20300	| ^						| PRINTER_MK3 with MMU2								| ???			| ^
+| ^					| ^			| ^										| 2e 01h 302	| ^						| PRINTER_MK3S										| ???			| ^
+| ^					| ^			| ^										| 4e 4fh 20302	| ^						| PRINTER_MK3S with MMU2S							| ???			| ^
+| 0x0EEC 3820		| uint16	| EEPROM_BOARD_TYPE						| ???			| ff ffh 65535			| Board Type										| ???			| D3 Ax0eec C2
+| ^					| ^			| ^										| c8 00h 200	| ^						| BOARD_RAMBO_MINI_1_0								| ???			| ^
+| ^					| ^			| ^										| cb 00h 203	| ^						| BOARD_RAMBO_MINI_1_3								| ???			| ^
+| ^					| ^			| ^										| 36 01h 310	| ^						| BOARD_EINSY_1_0a									| ???			| ^
+| 0x0EE8 3816		| float		| EEPROM_EXTRUDER_MULTIPLIER_0			| ???			| ff ff ff ffh			| Power panic Extruder 0 multiplier					| ???			| D3 Ax0ee8 C4
+| 0x0EE4 3812		| float		| EEPROM_EXTRUDER_MULTIPLIER_1			| ???			| ff ff ff ffh			| Power panic Extruder 1 multiplier					| ???			| D3 Ax0ee4 C4
+| 0x0EE0 3808		| float		| EEPROM_EXTRUDER_MULTIPLIER_2			| ???			| ff ff ff ffh			| Power panic Extruder 2 multiplier					| ???			| D3 Ax0ee0 C4
+| 0x0EDE 3806		| uint16	| EEPROM_EXTRUDEMULTIPLY				| ???			| ff ffh 65535			| Power panic Extruder multiplier					| ???			| D3 Ax0ede C2
+| 0x0EDA 3802		| float		| EEPROM_UVLO_TINY_CURRENT_POSITION_Z	| ???			| ff ff ff ffh			| Power panic Z position							| ???			| D3 Ax0eda C4
+| 0x0ED8 3800		| uint16	| EEPROM_UVLO_TARGET_HOTEND				| ???			| ff ffh 65535			| Power panic target Hotend temperature				| ???			| D3 Ax0ed8 C2
+| 0x0ED7 3799		| uint8		| EEPROM_SOUND_MODE						| 00h 0			| ffh 255				| Sound mode: __loud__								| ???			| D3 Ax0ed7 C1
+| ^					| ^			| ^										| 01h 1			| ^						| Sound mode: __once__								| ^				| ^
+| ^					| ^			| ^										| 02h 1			| ^						| Sound mode: __silent__							| ^				| ^
+| ^					| ^			| ^										| 03h 1			| ^						| Sound mode: __assist__							| ^				| ^
+| 0x0ED6 3798		| bool		| EEPROM_AUTO_DEPLETE					| 01h 1			| ffh 255				| MMU2/s autodeplete: __on__						| ???			| D3 Ax0ed6 C1
+| ^					| ^			| ^										| 00h 0			| ^						| MMU2/s autodeplete: __off__						| ^				| ^
+| 0x0ED5 3797		| bool		| EEPROM_FSENS_OQ_MEASS_ENABLED			| ???			| ffh 255				| PAT1925 ???										| ???			| D3 Ax0ed5 C1
+| ^					| ^			| ^										| ???			| ^						| PAT1925 ???										| ^				| ^
+| 0x0ED3 3795		| uint16	| EEPROM_MMU_FAIL_TOT					| ???			| ff ffh 65535	__S/P__	| MMU2/s total failures								| ???			| D3 Ax0ed3 C2
+| 0x0ED2 3794		| uint8		| EEPROM_MMU_FAIL						| ???			| ffh 255		__S/P__	| MMU2/s fails during print							| ???			| D3 Ax0ed2 C1
+| 0x0ED0 3792		| uint16	| EEPROM_MMU_LOAD_FAIL_TOT				| ???			| ff ffh 65535	__S/P__	| MMU2/s total load failures						| ???			| D3 Ax0ed0 C2
+| 0x0ECF 3791		| uint8		| EEPROM_MMU_LOAD_FAIL					| ???			| ffh 255		__S/P__	| MMU2/s load failures during print					| ???			| D3 Ax0ecf C1
+| 0x0ECE 3790		| uint8		| EEPROM_MMU_CUTTER_ENABLED				| 00h 0			| ffh 255				| MMU2/s cutter: __disabled__						| LCD menu		| D3 Ax0ece C1
+| ^					| ^			| ^										| 01h 1			| ^						| MMU2/s cutter: __enabled__						| ^				| ^
+| ^					| ^			| ^										| 02h 2			| ^						| MMU2/s cutter: __always__							| ^				| ^
+| 0x0DAE 3502		| uint16	| EEPROM_UVLO_MESH_BED_LEVELING_FULL	| ???			| ff ffh 65535			| Power panic Mesh bed leveling points 				| ???			| D3 Ax0dae C288
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
+| 0x0DAD 3501		| uint8		| EEPROM_MBL_TYPE						| ???			| ffh 255				| Mesh bed leveling precision 		_unused atm_	| ???			| D3 Ax0dad C1
+| 0x0DAC 3500		| bool		| EEPROM_MBL_MAGNET_ELIMINATION			| 01h 1			| ffh 255				| Mesh bed leveling does: __ignores__ magnets		| LCD menu		| D3 Ax0dac C1
+| ^					| ^			| ^										| 00h 0			| ^						| Mesh bed leveling does: __NOT ignores__ magnets	| ^				| ^
+| 0x0DAB 3499		| uint8		| EEPROM_MBL_POINTS_NR					| 03h 3			| ffh 255				| Mesh bed leveling points: __3x3__					| LCD menu		| D3 Ax0dab C1
+| ^					| ^			| ^										| 07h 7			| ^						| Mesh bed leveling points: __7x7__					| ^				| ^
+| 0x0DAA 3498		| uint8		| EEPROM_MBL_PROBE_NR					| 03h 3			| ffh 255				| MBL times measurements for each point: __3__ 		| LCD menu		| D3 Ax0daa C1
+| ^					| ^			| ^										| 05h 5			| ^						| MBL times measurements for each point: __5__		| ^				| ^
+| ^					| ^			| ^										| 01h 1			| ^						| MBL times measurements for each point: __1__		| ^				| ^
+| 0x0DA9 3497		| uint8		| EEPROM_MMU_STEALTH					| 01h 1			| ffh 255				| MMU2/s Silent mode: __on__						| ???			| D3 Ax0da9 C1
+| ^					| ^			| ^										| 00h 0			| ^						| MMU2/s Silent mode: __off__						| ^				| ^
+| 0x0DA8 3496		| uint8		| EEPROM_CHECK_MODE						| 01h 1			| ffh 255				| Check mode for nozzle is: __warn__				| LCD menu		| D3 Ax0da8 C1
+| ^					| ^			| ^										| 02h 0			| ^						| Check mode for nozzle is: __strict__				| ^				| ^
+| ^					| ^			| ^										| 00h 0			| ^						| Check mode for nozzle is: __none__				| ^				| ^
+| 0x0DA7 3495		| uint8		| EEPROM_NOZZLE_DIAMETER				| 28h 40		| ffh 255				| Nozzle diameter is: __40 or 0.40mm__				| LCD menu		| D3 Ax0da7 C1
+| ^					| ^			| ^										| 3ch 60		| ^						| Nozzle diameter is: __60 or 0.60mm__				| ^				| ^
+| ^					| ^			| ^										| 19h 25		| ^						| Nozzle diameter is: __25 or 0.25mm__				| ^				| ^
+| 0x0DA5 3493		| uint16	| EEPROM_NOZZLE_DIAMETER_uM				| 9001h			| ff ffh 65535			| Nozzle diameter is: __400um__						| LCD menu		| D3 Ax0da5 C2
+| ^					| ^			| ^										| 5802h			| ^						| Nozzle diameter is: __600um__						| ^				| ^
+| ^					| ^			| ^										| fa00h			| ^						| Nozzle diameter is: __250um__						| ^				| ^
+| 0x0DA4 3492		| uint8		| EEPROM_CHECK_MODEL					| 01h 1			| ffh 255				| Check mode for printer model is: __warn__			| LCD menu		| D3 Ax0da4 C1
+| ^					| ^			| ^										| 02h 0			| ^						| Check mode for printer model is: __strict__		| ^				| ^
+| ^					| ^			| ^										| 00h 0			| ^						| Check mode for printer model is: __none__			| ^				| ^
+| 0x0DA3 3491		| uint8		| EEPROM_CHECK_VERSION					| 01h 1			| ffh 255				| Check mode for firmware is: __warn__				| LCD menu		| D3 Ax0da3 C1
+| ^					| ^			| ^										| 02h 0			| ^						| Check mode for firmware is: __strict__			| ^				| ^
+| ^					| ^			| ^										| 00h 0			| ^						| Check mode for firmware is: __none__				| ^				| ^
+| 0x0DA2 3490		| uint8		| EEPROM_CHECK_GCODE					| 01h 1			| ffh 255				| Check mode for gcode is: __warn__ _unused atm_	| LCD menu		| D3 Ax0da2 C1
+| ^					| ^			| ^										| 02h 0			| ^						| Check mode for gcode is: __strict__ _unused atm_	| ^				| ^
+| ^					| ^			| ^										| 00h 0			| ^						| Check mode for gcode is: __none__ _unused atm_	| ^				| ^
+| 0x0D49 3401		| uint16	| EEPROM_SHEETS_BASE					| ???			| ffh 255				| ???												| LCD menu		| D3 Ax0d49 C89
+| 0x0D49 3401		| char		| _1st Sheet block_						| 536d6f6f746831| ffffffffffffff		| 1st sheet - Name: 	_Smooth1_					| ^				| D3 Ax0d49 C7
+| 0x0D50 3408		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 1st sheet - Z offset 								| ^				| D3 Ax0d50 C2	
+| 0x0D52 3410		| uint8		| ^										| 00h 0			| ffh 255				| 1st sheet - bed temp 								| ^				| D3 Ax0d52 C1	
+| 0x0D53 3411		| uint8		| ^										| 00h 0			| ffh 255				| 1st sheet - PINDA temp 							| ^				| D3 Ax0d53 C1	
+| 0x0D54 3412		| char		| _2nd Sheet block_						| 536d6f6f746832| ffffffffffffff		| 2nd sheet - Name: 	_Smooth2_					| ^				| D3 Ax0d54 C7
+| 0x0D5B 3419		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 2nd sheet - Z offset 								| ^				| D3 Ax0d5b C2	
+| 0x0D5D 3421		| uint8		| ^										| 00h 0			| ffh 255				| 2nd sheet - bed temp 								| ^				| D3 Ax0d5d C1	
+| 0x0D5E 3422		| uint8		| ^										| 00h 0			| ffh 255				| 2nd sheet - PINDA temp 							| ^				| D3 Ax0d5e C1	
+| 0x0D5F 3423		| char		| _3rd Sheet block_						| 54657874757231| ffffffffffffff		| 3rd sheet - Name: 	_Textur1_					| ^				| D3 Ax0d5f C7
+| 0x0D66 3430		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 3rd sheet - Z offset 								| ^				| D3 Ax0d66 C2	
+| 0x0D68 3432		| uint8		| ^										| 00h 0			| ffh 255				| 3rd sheet - bed temp 								| ^				| D3 Ax0d68 C1	
+| 0x0D69 3433		| uint8		| ^										| 00h 0			| ffh 255				| 3rd sheet - PINDA temp 							| ^				| D3 Ax0d69 C1	
+| 0x0D6A 3434		| char		| _4th Sheet block_						| 54657874757232| ffffffffffffff		| 4th sheet - Name: 	_Textur2_					| ^				| D3 Ax0d6a C7
+| 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
+| 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
+| 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
+| 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
+| 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	
+| 0x0DA1 3489		| uint8		| ???									| 00h 0			| ffh 255				| ???												| ???			| D3 Ax0da1 C1
+| 0x0D48 3400		| uint8		| EEPROM_FSENSOR_PCB					| ???			| ffh 255				| Filament Sensor type old vs new					| ???			| D3 Ax0d48 C1
+| ^					| ^			| ^										| ???			| ^						| Filament Sensor type ???							| ^				| ^
+| 0x0D47 3399		| uint8		| EEPROM_FSENSOR_ACTION_NA				| 00h 0			| ffh 255				| Filament Sensor action: __Continue__				| LCD menu		| D3 Ax0d47 C1
+| ^					| ^			| ^										| 01h 1			| ^						| Filament Sensor action: __Pause__					| ^				| ^
+| 0x0D37 3383		| float		| EEPROM_UVLO_SAVED_TARGET				| ???			| ff ff ff ffh			| Power panic saved target all-axis					| ???			| D3 Ax0d37 C16
+| ^					| ^			| ^										| ???			| ^						| Power panic saved target e-axis					| ^				| D3 Ax0d43 C4
+| ^					| ^			| ^										| ???			| ^						| Power panic saved target z-axis					| ^				| D3 Ax0d3f C4
+| ^					| ^			| ^										| ???			| ^						| Power panic saved target y-axis					| ^				| D3 Ax0d3b C4
+| ^					| ^			| ^										| ???			| ^						| Power panic saved target x-axis					| ^				| D3 Ax0d37 C4
+| 0x0D35 3381		| uint16	| EEPROM_UVLO_FEEDMULTIPLY				| ???			| ff ffh 65355			| Power panic saved feed multiplier					| ???			| D3 Ax0d35 C2
+| 0x0D34 3380		| uint8		| EEPROM_BACKLIGHT_LEVEL_HIGH			| 00h - ffh 	| 82h 130				| LCD backlight bright:	__128__	Dim value to 255	| LCD menu		| D3 Ax0d34 C1
+| 0x0D33 3379		| uint8		| EEPROM_BACKLIGHT_LEVEL_LOW			| 00h - ffh		| 32h 50				| LCD backlight dim:	__50__ 	0 to Bright value	| LCD menu		| D3 Ax0d33 C1
+| 0x0D32 3378		| uint8		| EEPROM_BACKLIGHT_MODE					| 02h 2			| ffh 255				| LCD backlight mode: __Auto__						| LCD menu		| D3 Ax0d32 C1
+| ^					| ^			| ^										| 01h 1			| ^						| LCD backlight mode: __Bright__					| ^				| ^
+| ^					| ^			| ^										| 00h 0			| ^						| LCD backlight mode: __Dim__						| ^				| ^
+| 0x0D30 3376		| uint16	| EEPROM_BACKLIGHT_TIMEOUT				| 01 00 - ff ff | 0a 00h 65535			| LCD backlight timeout: __10__ seconds				| LCD menu		| D3 Ax0d30 C2
+| 0x0D2C 3372		| float		| EEPROM_UVLO_LA_K						| ???			| ff ff ff ffh			| Power panic saved Linear Advanced K value			| ???			| D3 Ax0d2c C4
+
+  
+| Address begin		| Bit/Type 	| Name 									| Valid values	| Default/FactoryReset	| Description 										| Gcode/Function| Debug code
+| :--:				| :--: 		| :--: 									| :--:			| :--:					| :--:												| :--:			| :--:
+| 0x0012 18			| uint16	| EEPROM_FIRMWARE_VERSION_END			| ???			| ff ffh 65535			| ???												| ???			| D3 Ax0012 C2
+| 0x0010 16			| uint16	| EEPROM_FIRMWARE_VERSION_FLAVOR		| ???			| ff ffh 65535			| ???												| ???			| D3 Ax0010 C2
+| 0x000E 14			| uint16	| EEPROM_FIRMWARE_VERSION_REVISION		| ???			| ff ffh 65535			| Firmware version revision number DEV/ALPHA/BETA/RC| ???			| D3 Ax000e C2
+| 0x000C 12			| uint16	| EEPROM_FIRMWARE_VERSION_MINOR			| ???			| ff ffh 65535			| Firmware version minor number						| ???			| D3 Ax000c C2
+| 0x000A 10			| uint16	| EEPROM_FIRMWARE_VERSION_MAJOR			| ???			| ff ffh 65535			| Firmware version major number 					| ???			| D3 Ax000a C2
+| 0x0000 0			| char		| FW_PRUSA3D_MAGIC						| ???			| ffffffffffffffffffff	| __`PRUSA3DFW`__				 					| ???			| D3 Ax0000 C10
+*/
 
 #define EEPROM_EMPTY_VALUE 0xFF
 #define EEPROM_EMPTY_VALUE16 0xFFFF
@@ -51,7 +393,8 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 // The offsets are saved as 16bit signed int, scaled to tenths of microns.
 #define EEPROM_BED_CALIBRATION_Z_JITTER   (EEPROM_BED_CALIBRATION_VEC_Y-2*8)
 #define EEPROM_FARM_MODE (EEPROM_BED_CALIBRATION_Z_JITTER-1)
-#define EEPROM_FARM_NUMBER (EEPROM_FARM_MODE-3)
+#define EEPROM_FREE_NR1 (EEPROM_FARM_MODE-1)
+#define EEPROM_FARM_NUMBER (EEPROM_FREE_NR1-2)
 
 // Correction of the bed leveling, in micrometers.
 // Maximum 50 micrometers allowed.
@@ -72,19 +415,23 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 #define EEPROM_FILENAME (EEPROM_UVLO_CURRENT_POSITION - 8) //8chars to store filename without extension
 #define EEPROM_FILE_POSITION (EEPROM_FILENAME - 4) //32 bit for uint32_t file position 
 #define EEPROM_UVLO_CURRENT_POSITION_Z	(EEPROM_FILE_POSITION - 4) //float for current position in Z
-#define EEPROM_UVLO_TARGET_HOTEND		(EEPROM_UVLO_CURRENT_POSITION_Z - 1)
-#define EEPROM_UVLO_TARGET_BED			(EEPROM_UVLO_TARGET_HOTEND - 1)
+#define EEPROM_UVLO_UNUSED_001		(EEPROM_UVLO_CURRENT_POSITION_Z - 1) // uint8_t (unused)
+#define EEPROM_UVLO_TARGET_BED			(EEPROM_UVLO_UNUSED_001 - 1)
 #define EEPROM_UVLO_FEEDRATE			(EEPROM_UVLO_TARGET_BED - 2) //uint16_t
 #define EEPROM_UVLO_FAN_SPEED			(EEPROM_UVLO_FEEDRATE - 1) 
 #define EEPROM_FAN_CHECK_ENABLED		(EEPROM_UVLO_FAN_SPEED - 1)
 #define EEPROM_UVLO_MESH_BED_LEVELING     (EEPROM_FAN_CHECK_ENABLED - 9*2)
 
-#define EEPROM_UVLO_Z_MICROSTEPS     (EEPROM_UVLO_MESH_BED_LEVELING - 2)
+#define EEPROM_UVLO_Z_MICROSTEPS     (EEPROM_UVLO_MESH_BED_LEVELING - 2) // uint16_t (could be removed)
 #define EEPROM_UVLO_E_ABS            (EEPROM_UVLO_Z_MICROSTEPS - 1)
 #define EEPROM_UVLO_CURRENT_POSITION_E	(EEPROM_UVLO_E_ABS - 4)                 //float for current position in E
 
+#define EEPROM_FREE_NR2         (EEPROM_UVLO_CURRENT_POSITION_E - 1)			// FREE EEPROM SPACE
+#define EEPROM_FREE_NR3         (EEPROM_FREE_NR2 - 1)							// FREE EEPROM SPACE
+#define EEPROM_FREE_NR4         (EEPROM_FREE_NR3 - 1)							// FREE EEPROM SPACE
+#define EEPROM_FREE_NR5         (EEPROM_FREE_NR4 - 1)							// FREE EEPROM SPACE
 // Crash detection mode EEPROM setting 
-#define EEPROM_CRASH_DET         (EEPROM_UVLO_CURRENT_POSITION_E - 5)           // float (orig EEPROM_UVLO_MESH_BED_LEVELING-12) 
+#define EEPROM_CRASH_DET         (EEPROM_FREE_NR5 - 1)       				    // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-12) 
 // Crash detection counter Y (last print)
 #define EEPROM_CRASH_COUNT_Y       (EEPROM_CRASH_DET - 1)                       // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-15)
 // Filament sensor on/off EEPROM setting 
@@ -165,13 +512,11 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 #define EEPROM_EXTRUDER_MULTIPLIER_2 (EEPROM_EXTRUDER_MULTIPLIER_1 - 4)                        //float
 #define EEPROM_EXTRUDEMULTIPLY (EEPROM_EXTRUDER_MULTIPLIER_2 - 2)                              // uint16
 
-//
 #define EEPROM_UVLO_TINY_CURRENT_POSITION_Z (EEPROM_EXTRUDEMULTIPLY-4) // float
-#define EEPROM_UVLO_TINY_Z_MICROSTEPS (EEPROM_UVLO_TINY_CURRENT_POSITION_Z-2) // uint16
+#define EEPROM_UVLO_TARGET_HOTEND (EEPROM_UVLO_TINY_CURRENT_POSITION_Z-2) // uint16
 
 // Sound Mode
-//#define EEPROM_SOUND_MODE (EEPROM_EXTRUDEMULTIPLY-1) // uint8
-#define EEPROM_SOUND_MODE (EEPROM_UVLO_TINY_Z_MICROSTEPS-1) // uint8
+#define EEPROM_SOUND_MODE (EEPROM_UVLO_TARGET_HOTEND-1) // uint8
 #define EEPROM_AUTO_DEPLETE (EEPROM_SOUND_MODE-1) //bool
 
 #define EEPROM_FSENS_OQ_MEASS_ENABLED (EEPROM_AUTO_DEPLETE - 1) //bool

+ 183 - 129
Firmware/fsensor.cpp

@@ -18,19 +18,19 @@
 
 //! @name Basic parameters
 //! @{
-#define FSENSOR_CHUNK_LEN    0.64F  //!< filament sensor chunk length 0.64mm
-#define FSENSOR_ERR_MAX         17  //!< filament sensor maximum error count for runout detection
+#define FSENSOR_CHUNK_LEN      1.25 //!< filament sensor chunk length (mm)
+#define FSENSOR_ERR_MAX           4 //!< filament sensor maximum error/chunk count for runout detection
+
+#define FSENSOR_SOFTERR_CMAX      3 //!< number of contiguous soft failures before a triggering a runout
+#define FSENSOR_SOFTERR_DELTA 30000 //!< maximum interval (ms) to consider soft failures contiguous
 //! @}
 
 //! @name Optical quality measurement parameters
 //! @{
-#define FSENSOR_OQ_MAX_ES      6    //!< maximum error sum while loading (length ~64mm = 100chunks)
-#define FSENSOR_OQ_MAX_EM      2    //!< maximum error counter value while loading
-#define FSENSOR_OQ_MIN_YD      2    //!< minimum yd per chunk (applied to avg value)
-#define FSENSOR_OQ_MAX_YD      200  //!< maximum yd per chunk (applied to avg value)
-#define FSENSOR_OQ_MAX_PD      4    //!< maximum positive deviation (= yd_max/yd_avg)
-#define FSENSOR_OQ_MAX_ND      5    //!< maximum negative deviation (= yd_avg/yd_min)
-#define FSENSOR_OQ_MAX_SH      13   //!< maximum shutter value
+#define FSENSOR_OQ_MAX_ES      2    //!< maximum sum of error blocks during filament recheck
+#define FSENSOR_OQ_MIN_YD      2    //!< minimum yd sum during filament check (counts per inch)
+#define FSENSOR_OQ_MIN_BR      80   //!< minimum brightness value
+#define FSENSOR_OQ_MAX_SH      10   //!< maximum shutter value
 //! @}
 
 const char ERRMSG_PAT9125_NOT_RESP[] PROGMEM = "PAT9125 not responding (%d)!\n";
@@ -44,28 +44,35 @@ const char ERRMSG_PAT9125_NOT_RESP[] PROGMEM = "PAT9125 not responding (%d)!\n";
 #define FSENSOR_INT_PIN_PCMSK_BIT PCINT13         // PinChange Interrupt / PinChange Enable Mask @ PJ4
 #define FSENSOR_INT_PIN_PCICR_BIT PCIE1           // PinChange Interrupt Enable / Flag @ PJ4
 
-//uint8_t fsensor_int_pin = FSENSOR_INT_PIN;
-uint8_t fsensor_int_pin_old = 0;
-int16_t fsensor_chunk_len = 0;
-
 //! enabled = initialized and sampled every chunk event
 bool fsensor_enabled = true;
 //! runout watching is done in fsensor_update (called from main loop)
 bool fsensor_watch_runout = true;
 //! not responding - is set if any communication error occurred during initialization or readout
 bool fsensor_not_responding = false;
+
+#ifdef PAT9125
+uint8_t fsensor_int_pin_old = 0;
+//! optical checking "chunk lenght" (already in steps)
+int16_t fsensor_chunk_len = 0;
 //! enable/disable quality meassurement
 bool fsensor_oq_meassure_enabled = false;
-
 //! number of errors, updated in ISR
 uint8_t fsensor_err_cnt = 0;
 //! variable for accumulating step count (updated callbacks from stepper and ISR)
 int16_t fsensor_st_cnt = 0;
-//! last dy value from pat9125 sensor (used in ISR)
-int16_t fsensor_dy_old = 0;
+//! count of total sensor "soft" failures (filament status checks)
+uint8_t fsensor_softfail = 0;
+//! timestamp of last soft failure
+unsigned long fsensor_softfail_last = 0;
+//! count of soft failures within the configured time
+uint8_t fsensor_softfail_ccnt = 0;
+#endif
 
+#ifdef DEBUG_FSENSOR_LOG
 //! log flag: 0=log disabled, 1=log enabled
 uint8_t fsensor_log = 1;
+#endif //DEBUG_FSENSOR_LOG
 
 
 //! @name filament autoload variables
@@ -75,6 +82,8 @@ uint8_t fsensor_log = 1;
 bool fsensor_autoload_enabled = true;
 //! autoload watching enable/disable flag
 bool fsensor_watch_autoload = false;
+
+#ifdef PAT9125
 //
 uint16_t fsensor_autoload_y;
 //
@@ -84,6 +93,7 @@ uint32_t fsensor_autoload_last_millis;
 //
 uint8_t fsensor_autoload_sum;
 //! @}
+#endif
 
 
 //! @name filament optical quality measurement variables
@@ -111,7 +121,7 @@ int16_t fsensor_oq_yd_max;
 uint16_t fsensor_oq_sh_sum;
 //! @}
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 ClFsensorPCB oFsensorPCB;
 ClFsensorActionNA oFsensorActionNA;
 bool bIRsensorStateFlag=false;
@@ -121,14 +131,34 @@ unsigned long nIRsensorLastTime;
 void fsensor_stop_and_save_print(void)
 {
     printf_P(PSTR("fsensor_stop_and_save_print\n"));
-    stop_and_save_print_to_ram(0, 0); //XYZE - no change
+    stop_and_save_print_to_ram(0, 0);
+    fsensor_watch_runout = false;
+}
+
+#ifdef PAT9125
+// Reset all internal counters to zero, including stepper callbacks
+void fsensor_reset_err_cnt()
+{
+    fsensor_err_cnt = 0;
+    pat9125_y = 0;
+    st_reset_fsensor();
 }
 
+void fsensor_set_axis_steps_per_unit(float u)
+{
+    fsensor_chunk_len = (int16_t)(FSENSOR_CHUNK_LEN * u);
+}
+#endif
+
+
 void fsensor_restore_print_and_continue(void)
 {
     printf_P(PSTR("fsensor_restore_print_and_continue\n"));
-	fsensor_err_cnt = 0;
-    restore_print_from_ram_and_continue(0); //XYZ = orig, E - no change
+    fsensor_watch_runout = true;
+#ifdef PAT9125
+    fsensor_reset_err_cnt();
+#endif
+    restore_print_from_ram_and_continue(0);
 }
 
 // fsensor_checkpoint_print cuts the current print job at the current position,
@@ -152,7 +182,7 @@ void fsensor_init(void)
 #ifdef PAT9125
 	uint8_t oq_meassure_enabled = eeprom_read_byte((uint8_t*)EEPROM_FSENS_OQ_MEASS_ENABLED);
 	fsensor_oq_meassure_enabled = (oq_meassure_enabled == 1)?true:false;
-	fsensor_chunk_len = (int16_t)(FSENSOR_CHUNK_LEN * cs.axis_steps_per_unit[E_AXIS]);
+    fsensor_set_axis_steps_per_unit(cs.axis_steps_per_unit[E_AXIS]);
 
 	if (!pat9125)
 	{
@@ -160,7 +190,7 @@ void fsensor_init(void)
 		fsensor_not_responding = true;
 	}
 #endif //PAT9125
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
      bIRsensorStateFlag=false;
      oFsensorPCB=(ClFsensorPCB)eeprom_read_byte((uint8_t*)EEPROM_FSENSOR_PCB);
      oFsensorActionNA=(ClFsensorActionNA)eeprom_read_byte((uint8_t*)EEPROM_FSENSOR_ACTION_NA);
@@ -170,8 +200,8 @@ void fsensor_init(void)
 	else
 		fsensor_disable(false);                 // (in this case) EEPROM update is not necessary
 	printf_P(PSTR("FSensor %S"), (fsensor_enabled?PSTR("ENABLED"):PSTR("DISABLED")));
-#if IR_SENSOR_ANALOG
-     printf_P(PSTR(" (sensor board revision: %S)\n"),(oFsensorPCB==ClFsensorPCB::_Rev03b)?PSTR("03b or newer"):PSTR("03 or older"));
+#ifdef IR_SENSOR_ANALOG
+     printf_P(PSTR(" (sensor board revision: %S)\n"),(oFsensorPCB==ClFsensorPCB::_Rev04) ? MSG_04_OR_NEWER : MSG_03_OR_OLDER);
 #else //IR_SENSOR_ANALOG
      printf_P(PSTR("\n"));
 #endif //IR_SENSOR_ANALOG
@@ -192,8 +222,7 @@ bool fsensor_enable(bool bUpdateEEPROM)
 		fsensor_enabled = pat9125 ? true : false;
 		fsensor_watch_runout = true;
 		fsensor_oq_meassure = false;
-		fsensor_err_cnt = 0;
-		fsensor_dy_old = 0;
+        fsensor_reset_err_cnt();
 		eeprom_update_byte((uint8_t*)EEPROM_FSENSOR, fsensor_enabled ? 0x01 : 0x00);
 		FSensorStateMenu = fsensor_enabled ? 1 : 0;
 	}
@@ -204,7 +233,7 @@ bool fsensor_enable(bool bUpdateEEPROM)
 		FSensorStateMenu = 1;
 	}
 #else // PAT9125
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
      if(!fsensor_IR_check())
           {
           bUpdateEEPROM=true;
@@ -217,7 +246,7 @@ bool fsensor_enable(bool bUpdateEEPROM)
      fsensor_enabled=true;
      fsensor_not_responding=false;
      FSensorStateMenu=1;
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
           }
 #endif //IR_SENSOR_ANALOG
      if(bUpdateEEPROM)
@@ -273,12 +302,11 @@ void fsensor_autoload_check_start(void)
 	fsensor_autoload_last_millis = _millis();
 	fsensor_watch_runout = false;
 	fsensor_watch_autoload = true;
-	fsensor_err_cnt = 0;
 }
 
+
 void fsensor_autoload_check_stop(void)
 {
-
 //	puts_P(_N("fsensor_autoload_check_stop\n"));
 	if (!fsensor_enabled) return;
 //	puts_P(_N("fsensor_autoload_check_stop 1\n"));
@@ -289,7 +317,7 @@ void fsensor_autoload_check_stop(void)
 	fsensor_autoload_sum = 0;
 	fsensor_watch_autoload = false;
 	fsensor_watch_runout = true;
-	fsensor_err_cnt = 0;
+    fsensor_reset_err_cnt();
 }
 #endif //PAT9125
 
@@ -354,6 +382,7 @@ bool fsensor_check_autoload(void)
 	return false;
 }
 
+#ifdef PAT9125
 void fsensor_oq_meassure_set(bool State)
 {
 	fsensor_oq_meassure_enabled = State;
@@ -371,12 +400,11 @@ void fsensor_oq_meassure_start(uint8_t skip)
 	fsensor_oq_yd_sum = 0;
 	fsensor_oq_er_sum = 0;
 	fsensor_oq_er_max = 0;
-	fsensor_oq_yd_min = FSENSOR_OQ_MAX_YD;
+	fsensor_oq_yd_min = INT16_MAX;
 	fsensor_oq_yd_max = 0;
 	fsensor_oq_sh_sum = 0;
 	pat9125_update();
 	pat9125_y = 0;
-	fsensor_watch_runout = false;
 	fsensor_oq_meassure = true;
 }
 
@@ -388,10 +416,9 @@ void fsensor_oq_meassure_stop(void)
 	printf_P(_N(" st_sum=%u yd_sum=%u er_sum=%u er_max=%hhu\n"), fsensor_oq_st_sum, fsensor_oq_yd_sum, fsensor_oq_er_sum, fsensor_oq_er_max);
 	printf_P(_N(" yd_min=%u yd_max=%u yd_avg=%u sh_avg=%u\n"), fsensor_oq_yd_min, fsensor_oq_yd_max, (uint16_t)((uint32_t)fsensor_oq_yd_sum * fsensor_chunk_len / fsensor_oq_st_sum), (uint16_t)(fsensor_oq_sh_sum / fsensor_oq_samples));
 	fsensor_oq_meassure = false;
-	fsensor_watch_runout = true;
-	fsensor_err_cnt = 0;
 }
 
+#ifdef FSENSOR_QUALITY
 const char _OK[] PROGMEM = "OK";
 const char _NG[] PROGMEM = "NG!";
 
@@ -427,18 +454,24 @@ bool fsensor_oq_result(void)
 	printf_P(_N("fsensor_oq_result %S\n"), (res?_OK:_NG));
 	return res;
 }
-#ifdef PAT9125
+#endif //FSENSOR_QUALITY
+
 ISR(FSENSOR_INT_PIN_VECT)
 {
 	if (mmu_enabled || ir_sensor_detected) return;
 	if (!((fsensor_int_pin_old ^ FSENSOR_INT_PIN_PIN_REG) & FSENSOR_INT_PIN_MASK)) return;
 	fsensor_int_pin_old = FSENSOR_INT_PIN_PIN_REG;
+
+    // prevent isr re-entry
 	static bool _lock = false;
 	if (_lock) return;
 	_lock = true;
+
+    // fetch fsensor_st_cnt atomically
 	int st_cnt = fsensor_st_cnt;
 	fsensor_st_cnt = 0;
 	sei();
+
 	uint8_t old_err_cnt = fsensor_err_cnt;
 	uint8_t pat9125_res = fsensor_oq_meassure?pat9125_update():pat9125_update_y();
 	if (!pat9125_res)
@@ -447,56 +480,71 @@ ISR(FSENSOR_INT_PIN_VECT)
 		fsensor_not_responding = true;
 		printf_P(ERRMSG_PAT9125_NOT_RESP, 1);
 	}
+
 	if (st_cnt != 0)
-	{ //movement
-		if (st_cnt > 0) //positive movement
-		{
-			if (pat9125_y < 0)
-			{
-				if (fsensor_err_cnt)
-					fsensor_err_cnt += 2;
-				else
-					fsensor_err_cnt++;
-			}
-			else if (pat9125_y > 0)
-			{
-				if (fsensor_err_cnt)
-					fsensor_err_cnt--;
-			}
-			else //(pat9125_y == 0)
-				if (((fsensor_dy_old <= 0) || (fsensor_err_cnt)) && (st_cnt > (fsensor_chunk_len >> 1)))
-					fsensor_err_cnt++;
-			if (fsensor_oq_meassure)
-			{
-				if (fsensor_oq_skipchunk)
-				{
-					fsensor_oq_skipchunk--;
-					fsensor_err_cnt = 0;
-				}
-				else
-				{
-					if (st_cnt == fsensor_chunk_len)
-					{
-						if (pat9125_y > 0) if (fsensor_oq_yd_min > pat9125_y) fsensor_oq_yd_min = (fsensor_oq_yd_min + pat9125_y) / 2;
-						if (pat9125_y >= 0) if (fsensor_oq_yd_max < pat9125_y) fsensor_oq_yd_max = (fsensor_oq_yd_max + pat9125_y) / 2;
-					}
-					fsensor_oq_samples++;
-					fsensor_oq_st_sum += st_cnt;
-					if (pat9125_y > 0) fsensor_oq_yd_sum += pat9125_y;
-					if (fsensor_err_cnt > old_err_cnt)
-						fsensor_oq_er_sum += (fsensor_err_cnt - old_err_cnt);
-					if (fsensor_oq_er_max < fsensor_err_cnt)
-						fsensor_oq_er_max = fsensor_err_cnt;
-					fsensor_oq_sh_sum += pat9125_s;
-				}
-			}
-		}
-		else //negative movement
-		{
-		}
-	}
-	else
-	{ //no movement
+	{
+        // movement was planned, check for sensor movement
+        int8_t st_dir = st_cnt >= 0;
+        int8_t pat9125_dir = pat9125_y >= 0;
+
+        if (pat9125_y == 0)
+        {
+            if (st_dir)
+            {
+                // no movement detected: we might be within a blind sensor range,
+                // update the frame and shutter parameters we didn't earlier
+                if (!fsensor_oq_meassure)
+                    pat9125_update_bs();
+
+                // increment the error count only if underexposed: filament likely missing
+                if ((pat9125_b < FSENSOR_OQ_MIN_BR) && (pat9125_s > FSENSOR_OQ_MAX_SH))
+                {
+                    // check for a dark frame (<30% avg brightness) with long exposure
+                    ++fsensor_err_cnt;
+                }
+                else
+                {
+                    // good frame, filament likely present
+                    if(fsensor_err_cnt) --fsensor_err_cnt;
+                }
+            }
+        }
+        else if (pat9125_dir != st_dir)
+        {
+            // detected direction opposite of motor movement
+            if (st_dir) ++fsensor_err_cnt;
+        }
+        else if (pat9125_dir == st_dir)
+        {
+            // direction agreeing with planned movement
+            if (fsensor_err_cnt) --fsensor_err_cnt;
+        }
+
+        if (st_dir && fsensor_oq_meassure)
+        {
+            // extruding with quality assessment
+            if (fsensor_oq_skipchunk)
+            {
+                fsensor_oq_skipchunk--;
+                fsensor_err_cnt = 0;
+            }
+            else
+            {
+                if (st_cnt == fsensor_chunk_len)
+                {
+                    if (pat9125_y > 0) if (fsensor_oq_yd_min > pat9125_y) fsensor_oq_yd_min = (fsensor_oq_yd_min + pat9125_y) / 2;
+                    if (pat9125_y >= 0) if (fsensor_oq_yd_max < pat9125_y) fsensor_oq_yd_max = (fsensor_oq_yd_max + pat9125_y) / 2;
+                }
+                fsensor_oq_samples++;
+                fsensor_oq_st_sum += st_cnt;
+                if (pat9125_y > 0) fsensor_oq_yd_sum += pat9125_y;
+                if (fsensor_err_cnt > old_err_cnt)
+                    fsensor_oq_er_sum += (fsensor_err_cnt - old_err_cnt);
+                if (fsensor_oq_er_max < fsensor_err_cnt)
+                    fsensor_oq_er_max = fsensor_err_cnt;
+                fsensor_oq_sh_sum += pat9125_s;
+            }
+        }
 	}
 
 #ifdef DEBUG_FSENSOR_LOG
@@ -507,9 +555,7 @@ ISR(FSENSOR_INT_PIN_VECT)
 	}
 #endif //DEBUG_FSENSOR_LOG
 
-	fsensor_dy_old = pat9125_y;
 	pat9125_y = 0;
-
 	_lock = false;
 	return;
 }
@@ -529,19 +575,16 @@ void fsensor_setup_interrupt(void)
      PCICR |= bit(FSENSOR_INT_PIN_PCICR_BIT);     // enable corresponding PinChangeInterrupt (set of pins)
 }
 
-#endif //PAT9125
-
 void fsensor_st_block_chunk(int cnt)
 {
 	if (!fsensor_enabled) return;
 	fsensor_st_cnt += cnt;
-	if (abs(fsensor_st_cnt) >= fsensor_chunk_len)
-	{
-// !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
-		if (PIN_GET(FSENSOR_INT_PIN)) {PIN_VAL(FSENSOR_INT_PIN, LOW);}
-		else {PIN_VAL(FSENSOR_INT_PIN, HIGH);}
-	}
+
+    // !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
+    if (PIN_GET(FSENSOR_INT_PIN)) {PIN_VAL(FSENSOR_INT_PIN, LOW);}
+    else {PIN_VAL(FSENSOR_INT_PIN, HIGH);}
 }
+#endif //PAT9125
 
 
 //! Common code for enqueing M600 and supplemental codes into the command queue.
@@ -561,54 +604,65 @@ void fsensor_enque_M600(){
 void fsensor_update(void)
 {
 #ifdef PAT9125
-		if (fsensor_enabled && fsensor_watch_runout && (fsensor_err_cnt > FSENSOR_ERR_MAX))
+		if (fsensor_watch_runout && (fsensor_err_cnt > FSENSOR_ERR_MAX))
 		{
+			fsensor_stop_and_save_print();
+            KEEPALIVE_STATE(IN_HANDLER);
+
 			bool autoload_enabled_tmp = fsensor_autoload_enabled;
 			fsensor_autoload_enabled = false;
 			bool oq_meassure_enabled_tmp = fsensor_oq_meassure_enabled;
 			fsensor_oq_meassure_enabled = true;
 
-			fsensor_stop_and_save_print();
+            // move the nozzle away while checking the filament
+            current_position[Z_AXIS] += 0.8;
+            if(current_position[Z_AXIS] > Z_MAX_POS) current_position[Z_AXIS] = Z_MAX_POS;
+            plan_buffer_line_curposXYZE(max_feedrate[Z_AXIS], active_extruder);
+            st_synchronize();
 
-			fsensor_err_cnt = 0;
+            // check the filament in isolation
+            fsensor_reset_err_cnt();
 			fsensor_oq_meassure_start(0);
-
-			enquecommand_front_P((PSTR("G1 E-3 F200")));
-			process_commands();
-			KEEPALIVE_STATE(IN_HANDLER);
-			cmdqueue_pop_front();
-			st_synchronize();
-
-			enquecommand_front_P((PSTR("G1 E3 F200")));
-			process_commands();
-			KEEPALIVE_STATE(IN_HANDLER);
-			cmdqueue_pop_front();
-			st_synchronize();
-
-			uint8_t err_cnt = fsensor_err_cnt;
+            float e_tmp = current_position[E_AXIS];
+            current_position[E_AXIS] -= 3;
+            plan_buffer_line_curposXYZE(250/60, active_extruder);
+            current_position[E_AXIS] = e_tmp;
+            plan_buffer_line_curposXYZE(200/60, active_extruder);
+            st_synchronize();
 			fsensor_oq_meassure_stop();
 
 			bool err = false;
-			err |= (err_cnt > 1);
-
-			err |= (fsensor_oq_er_sum > 2);
-			err |= (fsensor_oq_yd_sum < (4 * FSENSOR_OQ_MIN_YD));
+			err |= (fsensor_err_cnt > 0);                   // final error count is non-zero
+			err |= (fsensor_oq_er_sum > FSENSOR_OQ_MAX_ES); // total error count is above limit
+			err |= (fsensor_oq_yd_sum < FSENSOR_OQ_MIN_YD); // total measured distance is below limit
 
             fsensor_restore_print_and_continue();
-			fsensor_autoload_enabled = autoload_enabled_tmp;
-			fsensor_oq_meassure_enabled = oq_meassure_enabled_tmp;
-
-			if (!err)
-				printf_P(PSTR("fsensor_err_cnt = 0\n"));
-			else
-				fsensor_enque_M600();
+            fsensor_autoload_enabled = autoload_enabled_tmp;
+            fsensor_oq_meassure_enabled = oq_meassure_enabled_tmp;
+
+            unsigned long now = _millis();
+            if (!err && (now - fsensor_softfail_last) > FSENSOR_SOFTERR_DELTA)
+                fsensor_softfail_ccnt = 0;
+            if (!err && fsensor_softfail_ccnt <= FSENSOR_SOFTERR_CMAX)
+            {
+                printf_P(PSTR("fsensor_err_cnt = 0\n"));
+                ++fsensor_softfail;
+                ++fsensor_softfail_ccnt;
+                fsensor_softfail_last = now;
+            }
+            else
+            {
+                fsensor_softfail_ccnt = 0;
+                fsensor_softfail_last = 0;
+                fsensor_enque_M600();
+            }
 		}
 #else //PAT9125
-		if (CHECK_FSENSOR && fsensor_enabled && ir_sensor_detected)
+		if (CHECK_FSENSOR && ir_sensor_detected)
         {
                if(digitalRead(IR_SENSOR_PIN))
                {                                  // IR_SENSOR_PIN ~ H
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
                     if(!bIRsensorStateFlag)
                     {
                          bIRsensorStateFlag=true;
@@ -637,7 +691,7 @@ void fsensor_update(void)
                               ADCSRB=nMUX2;
                               ENABLE_TEMPERATURE_INTERRUPT();
                               // end of sequence for ...
-                              if((oFsensorPCB==ClFsensorPCB::_Rev03b)&&((nADC*OVERSAMPLENR)>((int)IRsensor_Hopen_TRESHOLD)))
+                              if((oFsensorPCB==ClFsensorPCB::_Rev04)&&((nADC*OVERSAMPLENR)>((int)IRsensor_Hopen_TRESHOLD)))
                               {
                                    fsensor_disable();
                                    fsensor_not_responding = true;
@@ -651,7 +705,7 @@ void fsensor_update(void)
 #endif //IR_SENSOR_ANALOG
                                   fsensor_checkpoint_print();
                                   fsensor_enque_M600();
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
                               }
                          }
                     }
@@ -665,7 +719,7 @@ void fsensor_update(void)
 #endif //PAT9125
 }
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 bool fsensor_IR_check()
 {
 uint16_t volt_IR_int;
@@ -673,7 +727,7 @@ bool bCheckResult;
 
 volt_IR_int=current_voltage_raw_IR;
 bCheckResult=(volt_IR_int<((int)IRsensor_Lmax_TRESHOLD))||(volt_IR_int>((int)IRsensor_Hmin_TRESHOLD));
-bCheckResult=bCheckResult&&(!((oFsensorPCB==ClFsensorPCB::_Rev03b)&&(volt_IR_int>((int)IRsensor_Hopen_TRESHOLD))));
+bCheckResult=bCheckResult&&(!((oFsensorPCB==ClFsensorPCB::_Rev04)&&(volt_IR_int>((int)IRsensor_Hopen_TRESHOLD))));
 return(bCheckResult);
 }
 #endif //IR_SENSOR_ANALOG

+ 19 - 7
Firmware/fsensor.h

@@ -6,15 +6,16 @@
 #include "config.h"
 
 
-//! minimum meassured chunk length in steps
-extern int16_t fsensor_chunk_len;
 // enable/disable flag
 extern bool fsensor_enabled;
 // not responding flag
 extern bool fsensor_not_responding;
-//enable/disable quality meassurement
-extern bool fsensor_oq_meassure_enabled;
-
+#ifdef PAT9125
+// optical checking "chunk lenght" (already in steps)
+extern int16_t fsensor_chunk_len;
+// count of soft failures
+extern uint8_t fsensor_softfail;
+#endif
 
 //! @name save restore printing
 //! @{
@@ -28,6 +29,11 @@ extern void fsensor_checkpoint_print(void);
 //! initialize
 extern void fsensor_init(void);
 
+#ifdef PAT9125
+//! update axis resolution
+extern void fsensor_set_axis_steps_per_unit(float u);
+#endif
+
 //! @name enable/disable
 //! @{
 extern bool fsensor_enable(bool bUpdateEEPROM=true);
@@ -52,8 +58,10 @@ extern void fsensor_autoload_check_stop(void);
 extern bool fsensor_check_autoload(void);
 //! @}
 
+#ifdef PAT9125
 //! @name optical quality measurement support
 //! @{
+extern bool fsensor_oq_meassure_enabled;
 extern void fsensor_oq_meassure_set(bool State);
 extern void fsensor_oq_meassure_start(uint8_t skip);
 extern void fsensor_oq_meassure_stop(void);
@@ -64,21 +72,25 @@ extern bool fsensor_oq_result(void);
 //! @{
 extern void fsensor_st_block_chunk(int cnt);
 
+// debugging
+extern uint8_t fsensor_log;
+
 // There's really nothing to do in block_begin: the stepper ISR likely has
 // called us already at the end of the last block, making this integration
 // redundant. LA1.5 might not always do that during a coasting move, so attempt
 // to drain fsensor_st_cnt anyway at the beginning of the new block.
 #define fsensor_st_block_begin(rev) fsensor_st_block_chunk(0)
 //! @}
+#endif //PAT9125
 
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 #define IR_SENSOR_STEADY 10                       // [ms]
 
 enum class ClFsensorPCB:uint_least8_t
 {
     _Old=0,
-    _Rev03b=1,
+    _Rev04=1,
     _Undef=EEPROM_EMPTY_VALUE
 };
 

+ 190 - 180
Firmware/heatbed_pwm.cpp

@@ -1,180 +1,190 @@
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include "io_atmega2560.h"
-
-// All this is about silencing the heat bed, as it behaves like a loudspeaker.
-// Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
-// frequency for both power supply units (i.e. both PSUs are reasonably silent).
-// The only trouble is the rising or falling edge of bed heating - that creates an audible click.
-// This audible click may be suppressed by making the rising or falling edge NOT sharp.
-// Of course, making non-sharp edges in digital technology is not easy, but there is a solution.
-// It is possible to do a fast PWM sequence with duty starting from 0 to 255.
-// Doing this at higher frequency than the bed "loudspeaker" can handle makes the click barely audible.
-// Technically:
-// timer0 is set to fast PWM mode at 62.5kHz (timer0 is linked to the bed heating pin) (zero prescaler)
-// To keep the bed switching at 30Hz - we don't want the PWM running at 62kHz all the time 
-// since it would burn the heatbed's MOSFET:
-// 16MHz/256 levels of PWM duty gives us 62.5kHz
-// 62.5kHz/256 gives ~244Hz, that is still too fast - 244/8 gives ~30Hz, that's what we need
-// So the automaton runs atop of inner 8 (or 16) cycles.
-// The finite automaton is running in the ISR(TIMER0_OVF_vect)
-
-// 2019-08-14 update: the original algorithm worked very well, however there were 2 regressions:
-// 1. 62kHz ISR requires considerable amount of processing power, 
-//    USB transfer speed dropped by 20%, which was most notable when doing short G-code segments.
-// 2. Some users reported TLed PSU started clicking when running at 120V/60Hz. 
-//    This looks like the original algorithm didn't maintain base PWM 30Hz, but only 15Hz
-// To address both issues, there is an improved approach based on the idea of leveraging
-// different CLK prescalers in some automaton states - i.e. when holding LOW or HIGH on the output pin,
-// we don't have to clock 62kHz, but we can increase the CLK prescaler for these states to 8 (or even 64).
-// That shall result in the ISR not being called that much resulting in regained performance
-// Theoretically this is relatively easy, however one must be very carefull handling the AVR's timer
-// control registers correctly, especially setting them in a correct order.
-// Some registers are double buffered, some changes are applied in next cycles etc.
-// The biggest problem was with the CLK prescaler itself - this circuit is shared among almost all timers,
-// we don't want to reset the prescaler counted value when transiting among automaton states.
-// Resetting the prescaler would make the PWM more precise, right now there are temporal segments
-// of variable period ranging from 0 to 7 62kHz ticks - that's logical, the timer must "sync"
-// to the new slower CLK after setting the slower prescaler value.
-// In our application, this isn't any significant problem and may be ignored.
-// Doing changes in timer's registers non-correctly results in artefacts on the output pin
-// - it can toggle unnoticed, which will result in bed clicking again.
-// That's why there are special transition states ZERO_TO_RISE and ONE_TO_FALL, which enable the
-// counter change its operation atomically and without artefacts on the output pin.
-// The resulting signal on the output pin was checked with an osciloscope. 
-// If there are any change requirements in the future, the signal must be checked with an osciloscope again,
-// ad-hoc changes may completely screw things up!
-
-///! Definition off finite automaton states
-enum class States : uint8_t {
-	ZERO_START = 0,///< entry point of the automaton - reads the soft_pwm_bed value for the next whole PWM cycle
-	ZERO,          ///< steady 0 (OFF), no change for the whole period
-	ZERO_TO_RISE,  ///< metastate allowing the timer change its state atomically without artefacts on the output pin
-	RISE,          ///< 16 fast PWM cycles with increasing duty up to steady ON
-	RISE_TO_ONE,   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
-	ONE,           ///< steady 1 (ON), no change for the whole period 
-	ONE_TO_FALL,   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
-	FALL,          ///< 16 fast PWM cycles with decreasing duty down to steady OFF
-	FALL_TO_ZERO   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
-};
-
-///! Inner states of the finite automaton
-static States state = States::ZERO_START;
-
-///! Fast PWM counter is used in the RISE and FALL states (62.5kHz)
-static uint8_t slowCounter = 0;
-///! Slow PWM counter is used in the ZERO and ONE states (62.5kHz/8 or 64)
-static uint8_t fastCounter = 0;
-///! PWM counter for the whole cycle - a cache for soft_pwm_bed
-static uint8_t pwm = 0;
-
-///! The slow PWM duty for the next 30Hz cycle
-///! Set in the whole firmware at various places
-extern unsigned char soft_pwm_bed;
-
-/// fastMax - how many fast PWM steps to do in RISE and FALL states
-/// 16 is a good compromise between silenced bed ("smooth" edges)
-/// and not burning the switching MOSFET
-static const uint8_t fastMax = 16;
-
-/// Scaler 16->256 for fast PWM
-static const uint8_t fastShift = 4;
-
-/// Increment slow PWM counter by slowInc every ZERO or ONE state
-/// This allows for fine-tuning the basic PWM switching frequency
-/// A possible further optimization - use a 64 prescaler (instead of 8)
-/// increment slowCounter by 1
-/// but use less bits of soft PWM - something like soft_pwm_bed >> 2
-/// that may further reduce the CPU cycles required by the bed heating automaton
-/// Due to the nature of bed heating the reduced PID precision may not be a major issue, however doing 8x less ISR(timer0_ovf) may significantly improve the performance 
-static const uint8_t slowInc = 1;
-
-ISR(TIMER0_OVF_vect)          // timer compare interrupt service routine
-{
-	switch(state){
-	case States::ZERO_START:
-		pwm = soft_pwm_bed << 1;// expecting soft_pwm_bed to be 7bit!
-		if( pwm != 0 ){
-			state = States::ZERO;     // do nothing, let it tick once again after the 30Hz period
-		}
-		break;
-	case States::ZERO: // end of state ZERO - we'll either stay in ZERO or change to RISE
-		// In any case update our cache of pwm value for the next whole cycle from soft_pwm_bed
-		slowCounter += slowInc; // this does software timer_clk/256 or less (depends on slowInc)
-		if( slowCounter > pwm ){
-			return;
-		} // otherwise moving towards RISE
-		state = States::ZERO_TO_RISE; // and finalize the change in a transitional state RISE0
-		break;
-	// even though it may look like the ZERO state may be glued together with the ZERO_TO_RISE, don't do it
-	// the timer must tick once more in order to get rid of occasional output pin toggles.
-	case States::ZERO_TO_RISE:  // special state for handling transition between prescalers and switching inverted->non-inverted fast-PWM without toggling the output pin.
-		// It must be done in consequent steps, otherwise the pin will get flipped up and down during one PWM cycle.
-		// Also beware of the correct sequence of the following timer control registers initialization - it really matters!
-		state = States::RISE;     // prepare for standard RISE cycles
-		fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
-		TCNT0 = 255;              // force overflow on the next clock cycle
-		TCCR0B = (1 << CS00);     // change prescaler to 1, i.e. 62.5kHz
-		TCCR0A &= ~(1 << COM0B0); // Clear OC0B on Compare Match, set OC0B at BOTTOM (non-inverting mode)
-		break;
-	case States::RISE:
-		OCR0B = (fastMax - fastCounter) << fastShift;
-		if( fastCounter ){
-			--fastCounter;
-		} else { // end of RISE cycles, changing into state ONE
-			state = States::RISE_TO_ONE;
-			OCR0B = 255;          // full duty
-			TCNT0 = 254;          // make the timer overflow in the next cycle
-			// @@TODO these constants are still subject to investigation
-		}
-		break;
-	case States::RISE_TO_ONE:
-		state = States::ONE;
-		OCR0B = 255;              // full duty
-		TCNT0 = 255;              // make the timer overflow in the next cycle
-		TCCR0B = (1 << CS01);     // change prescaler to 8, i.e. 7.8kHz
-		break;
-	case States::ONE:             // state ONE - we'll either stay in ONE or change to FALL
-		OCR0B = 255;
-		slowCounter += slowInc;   // this does software timer_clk/256 or less
-		if( slowCounter < pwm ){
-			return;
-		}
-		if( (soft_pwm_bed << 1) >= (255 - slowInc - 1) ){  //@@TODO simplify & explain
-			// if slowInc==2, soft_pwm == 251 will be the first to do short drops to zero. 252 will keep full heating
-			return;           // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
-		}
-		// otherwise moving towards FALL
-		// @@TODO it looks like ONE_TO_FALL isn't necessary, there are no artefacts at all
-		state = States::ONE;//_TO_FALL;
-//		TCCR0B = (1 << CS00);      // change prescaler to 1, i.e. 62.5kHz
-//		break;
-//	case States::ONE_TO_FALL:
-//		OCR0B = 255;              // zero duty
-		state=States::FALL;
-		fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
-		TCNT0 = 255;              // force overflow on the next clock cycle
-		TCCR0B = (1 << CS00);     // change prescaler to 1, i.e. 62.5kHz
-		// must switch to inverting mode already here, because it takes a whole PWM cycle and it would make a "1" at the end of this pwm cycle
-		// COM0B1 remains set both in inverting and non-inverting mode
-		TCCR0A |= (1 << COM0B0);  // inverting mode
-		break;
-	case States::FALL:
-		OCR0B = (fastMax - fastCounter) << fastShift; // this is the same as in RISE, because now we are setting the zero part of duty due to inverting mode
-		//TCCR0A |= (1 << COM0B0); // already set in ONE_TO_FALL
-		if( fastCounter ){
-			--fastCounter;
-		} else {   // end of FALL cycles, changing into state ZERO
-			state = States::FALL_TO_ZERO;
-			TCNT0 = 128; //@@TODO again - need to wait long enough to propagate the timer state changes
-			OCR0B = 255;
-		}
-		break;
-	case States::FALL_TO_ZERO:
-		state = States::ZERO_START; // go to read new soft_pwm_bed value for the next cycle
-		TCNT0 = 128;
-		OCR0B = 255;
-		TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
-		break;		
-    }
-}
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "io_atmega2560.h"
+
+// All this is about silencing the heat bed, as it behaves like a loudspeaker.
+// Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
+// frequency for both power supply units (i.e. both PSUs are reasonably silent).
+// The only trouble is the rising or falling edge of bed heating - that creates an audible click.
+// This audible click may be suppressed by making the rising or falling edge NOT sharp.
+// Of course, making non-sharp edges in digital technology is not easy, but there is a solution.
+// It is possible to do a fast PWM sequence with duty starting from 0 to 255.
+// Doing this at higher frequency than the bed "loudspeaker" can handle makes the click barely audible.
+// Technically:
+// timer0 is set to fast PWM mode at 62.5kHz (timer0 is linked to the bed heating pin) (zero prescaler)
+// To keep the bed switching at 30Hz - we don't want the PWM running at 62kHz all the time 
+// since it would burn the heatbed's MOSFET:
+// 16MHz/256 levels of PWM duty gives us 62.5kHz
+// 62.5kHz/256 gives ~244Hz, that is still too fast - 244/8 gives ~30Hz, that's what we need
+// So the automaton runs atop of inner 8 (or 16) cycles.
+// The finite automaton is running in the ISR(TIMER0_OVF_vect)
+
+// 2019-08-14 update: the original algorithm worked very well, however there were 2 regressions:
+// 1. 62kHz ISR requires considerable amount of processing power, 
+//    USB transfer speed dropped by 20%, which was most notable when doing short G-code segments.
+// 2. Some users reported TLed PSU started clicking when running at 120V/60Hz. 
+//    This looks like the original algorithm didn't maintain base PWM 30Hz, but only 15Hz
+// To address both issues, there is an improved approach based on the idea of leveraging
+// different CLK prescalers in some automaton states - i.e. when holding LOW or HIGH on the output pin,
+// we don't have to clock 62kHz, but we can increase the CLK prescaler for these states to 8 (or even 64).
+// That shall result in the ISR not being called that much resulting in regained performance
+// Theoretically this is relatively easy, however one must be very carefull handling the AVR's timer
+// control registers correctly, especially setting them in a correct order.
+// Some registers are double buffered, some changes are applied in next cycles etc.
+// The biggest problem was with the CLK prescaler itself - this circuit is shared among almost all timers,
+// we don't want to reset the prescaler counted value when transiting among automaton states.
+// Resetting the prescaler would make the PWM more precise, right now there are temporal segments
+// of variable period ranging from 0 to 7 62kHz ticks - that's logical, the timer must "sync"
+// to the new slower CLK after setting the slower prescaler value.
+// In our application, this isn't any significant problem and may be ignored.
+// Doing changes in timer's registers non-correctly results in artefacts on the output pin
+// - it can toggle unnoticed, which will result in bed clicking again.
+// That's why there are special transition states ZERO_TO_RISE and ONE_TO_FALL, which enable the
+// counter change its operation atomically and without artefacts on the output pin.
+// The resulting signal on the output pin was checked with an osciloscope. 
+// If there are any change requirements in the future, the signal must be checked with an osciloscope again,
+// ad-hoc changes may completely screw things up!
+
+// 2020-01-29 update: we are introducing a new option to the automaton that will allow us to force the output state
+// to either full ON or OFF. This is so that interference during the MBL probing is minimal.
+// To accomplish this goal we use bedPWMDisabled. It is only supposed to be used for brief periods of time as to
+// not make the bed temperature too unstable. Also, careful consideration should be used when using this
+// option as leaving this enabled will also keep the bed output in the state it stopped in.
+
+///! Definition off finite automaton states
+enum class States : uint8_t {
+	ZERO_START = 0,///< entry point of the automaton - reads the soft_pwm_bed value for the next whole PWM cycle
+	ZERO,          ///< steady 0 (OFF), no change for the whole period
+	ZERO_TO_RISE,  ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+	RISE,          ///< 16 fast PWM cycles with increasing duty up to steady ON
+	RISE_TO_ONE,   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+	ONE,           ///< steady 1 (ON), no change for the whole period 
+	ONE_TO_FALL,   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+	FALL,          ///< 16 fast PWM cycles with decreasing duty down to steady OFF
+	FALL_TO_ZERO   ///< metastate allowing the timer change its state atomically without artefacts on the output pin
+};
+
+///! Inner states of the finite automaton
+static States state = States::ZERO_START;
+
+bool bedPWMDisabled = 0;
+
+///! Fast PWM counter is used in the RISE and FALL states (62.5kHz)
+static uint8_t slowCounter = 0;
+///! Slow PWM counter is used in the ZERO and ONE states (62.5kHz/8 or 64)
+static uint8_t fastCounter = 0;
+///! PWM counter for the whole cycle - a cache for soft_pwm_bed
+static uint8_t pwm = 0;
+
+///! The slow PWM duty for the next 30Hz cycle
+///! Set in the whole firmware at various places
+extern unsigned char soft_pwm_bed;
+
+/// fastMax - how many fast PWM steps to do in RISE and FALL states
+/// 16 is a good compromise between silenced bed ("smooth" edges)
+/// and not burning the switching MOSFET
+static const uint8_t fastMax = 16;
+
+/// Scaler 16->256 for fast PWM
+static const uint8_t fastShift = 4;
+
+/// Increment slow PWM counter by slowInc every ZERO or ONE state
+/// This allows for fine-tuning the basic PWM switching frequency
+/// A possible further optimization - use a 64 prescaler (instead of 8)
+/// increment slowCounter by 1
+/// but use less bits of soft PWM - something like soft_pwm_bed >> 2
+/// that may further reduce the CPU cycles required by the bed heating automaton
+/// Due to the nature of bed heating the reduced PID precision may not be a major issue, however doing 8x less ISR(timer0_ovf) may significantly improve the performance 
+static const uint8_t slowInc = 1;
+
+ISR(TIMER0_OVF_vect)          // timer compare interrupt service routine
+{
+	switch(state){
+	case States::ZERO_START:
+		if (bedPWMDisabled) return; // stay in the OFF state and do not change the output pin
+		pwm = soft_pwm_bed << 1;// expecting soft_pwm_bed to be 7bit!
+		if( pwm != 0 ){
+			state = States::ZERO;     // do nothing, let it tick once again after the 30Hz period
+		}
+		break;
+	case States::ZERO: // end of state ZERO - we'll either stay in ZERO or change to RISE
+		// In any case update our cache of pwm value for the next whole cycle from soft_pwm_bed
+		slowCounter += slowInc; // this does software timer_clk/256 or less (depends on slowInc)
+		if( slowCounter > pwm ){
+			return;
+		} // otherwise moving towards RISE
+		state = States::ZERO_TO_RISE; // and finalize the change in a transitional state RISE0
+		break;
+	// even though it may look like the ZERO state may be glued together with the ZERO_TO_RISE, don't do it
+	// the timer must tick once more in order to get rid of occasional output pin toggles.
+	case States::ZERO_TO_RISE:  // special state for handling transition between prescalers and switching inverted->non-inverted fast-PWM without toggling the output pin.
+		// It must be done in consequent steps, otherwise the pin will get flipped up and down during one PWM cycle.
+		// Also beware of the correct sequence of the following timer control registers initialization - it really matters!
+		state = States::RISE;     // prepare for standard RISE cycles
+		fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
+		TCNT0 = 255;              // force overflow on the next clock cycle
+		TCCR0B = (1 << CS00);     // change prescaler to 1, i.e. 62.5kHz
+		TCCR0A &= ~(1 << COM0B0); // Clear OC0B on Compare Match, set OC0B at BOTTOM (non-inverting mode)
+		break;
+	case States::RISE:
+		OCR0B = (fastMax - fastCounter) << fastShift;
+		if( fastCounter ){
+			--fastCounter;
+		} else { // end of RISE cycles, changing into state ONE
+			state = States::RISE_TO_ONE;
+			OCR0B = 255;          // full duty
+			TCNT0 = 254;          // make the timer overflow in the next cycle
+			// @@TODO these constants are still subject to investigation
+		}
+		break;
+	case States::RISE_TO_ONE:
+		state = States::ONE;
+		OCR0B = 255;              // full duty
+		TCNT0 = 255;              // make the timer overflow in the next cycle
+		TCCR0B = (1 << CS01);     // change prescaler to 8, i.e. 7.8kHz
+		break;
+	case States::ONE:             // state ONE - we'll either stay in ONE or change to FALL
+		OCR0B = 255;
+		if (bedPWMDisabled) return; // stay in the ON state and do not change the output pin
+		slowCounter += slowInc;   // this does software timer_clk/256 or less
+		if( slowCounter < pwm ){
+			return;
+		}
+		if( (soft_pwm_bed << 1) >= (255 - slowInc - 1) ){  //@@TODO simplify & explain
+			// if slowInc==2, soft_pwm == 251 will be the first to do short drops to zero. 252 will keep full heating
+			return;           // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
+		}
+		// otherwise moving towards FALL
+		// @@TODO it looks like ONE_TO_FALL isn't necessary, there are no artefacts at all
+		state = States::ONE;//_TO_FALL;
+//		TCCR0B = (1 << CS00);      // change prescaler to 1, i.e. 62.5kHz
+//		break;
+//	case States::ONE_TO_FALL:
+//		OCR0B = 255;              // zero duty
+		state=States::FALL;
+		fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
+		TCNT0 = 255;              // force overflow on the next clock cycle
+		TCCR0B = (1 << CS00);     // change prescaler to 1, i.e. 62.5kHz
+		// must switch to inverting mode already here, because it takes a whole PWM cycle and it would make a "1" at the end of this pwm cycle
+		// COM0B1 remains set both in inverting and non-inverting mode
+		TCCR0A |= (1 << COM0B0);  // inverting mode
+		break;
+	case States::FALL:
+		OCR0B = (fastMax - fastCounter) << fastShift; // this is the same as in RISE, because now we are setting the zero part of duty due to inverting mode
+		//TCCR0A |= (1 << COM0B0); // already set in ONE_TO_FALL
+		if( fastCounter ){
+			--fastCounter;
+		} else {   // end of FALL cycles, changing into state ZERO
+			state = States::FALL_TO_ZERO;
+			TCNT0 = 128; //@@TODO again - need to wait long enough to propagate the timer state changes
+			OCR0B = 255;
+		}
+		break;
+	case States::FALL_TO_ZERO:
+		state = States::ZERO_START; // go to read new soft_pwm_bed value for the next cycle
+		TCNT0 = 128;
+		OCR0B = 255;
+		TCCR0B = (1 << CS01); // change prescaler to 8, i.e. 7.8kHz
+		break;		
+    }
+}

+ 42 - 2
Firmware/la10compat.cpp

@@ -2,13 +2,24 @@
 #include "Marlin.h"
 
 
-static LA10C_MODE la10c_mode = LA10C_UNKNOWN;
+static LA10C_MODE la10c_mode = LA10C_UNKNOWN; // Current LA compatibility mode
+static float la10c_orig_jerk = 0;             // Unadjusted/saved e-jerk
+
+
+LA10C_MODE la10c_mode_get()
+{
+    return la10c_mode;
+}
 
 
 void la10c_mode_change(LA10C_MODE mode)
 {
     if(mode == la10c_mode) return;
 
+    // always restore to the last unadjusted E-jerk value
+    if(la10c_orig_jerk)
+        cs.max_jerk[E_AXIS] = la10c_orig_jerk;
+
     SERIAL_ECHOPGM("LA10C: Linear Advance mode: ");
     switch(mode)
     {
@@ -17,13 +28,16 @@ void la10c_mode_change(LA10C_MODE mode)
     case LA10C_LA10:    SERIAL_ECHOLNPGM("1.0"); break;
     }
     la10c_mode = mode;
+
+    // adjust the E-jerk if needed
+    cs.max_jerk[E_AXIS] = la10c_jerk(cs.max_jerk[E_AXIS]);
 }
 
 
 // Approximate a LA10 value to a LA15 equivalent.
 static float la10c_convert(float k)
 {
-    float new_K = k * 0.004 - 0.06;
+    float new_K = k * 0.004 - 0.05;
     return (new_K < 0? 0: new_K);
 }
 
@@ -46,3 +60,29 @@ float la10c_value(float k)
     else
         return (k >= 0? la10c_convert(k): -1);
 }
+
+
+float la10c_jerk(float j)
+{
+    la10c_orig_jerk = j;
+
+    if(la10c_mode != LA10C_LA10)
+        return j;
+
+    // check for a compatible range of values prior to convert (be sure that
+    // a higher E-jerk would still be compatible wrt the E accell range)
+    if(j < 4.5 && cs.max_acceleration_units_per_sq_second_normal[E_AXIS] < 2000)
+        return j;
+
+    // bring low E-jerk values into equivalent LA 1.5 values by
+    // flattening the response in the (1-4.5) range using a piecewise
+    // function. Is it truly worth to preserve the difference between
+    // 1.5/2.5 E-jerk for LA1.0? Probably not, but we try nonetheless.
+    j = j < 1.0? j * 3.625:
+        j < 4.5? j * 0.25 + 3.375:
+        j;
+
+    SERIAL_ECHOPGM("LA10C: Adjusted E-Jerk: ");
+    SERIAL_ECHOLN(j);
+    return j;
+}

+ 8 - 1
Firmware/la10compat.h

@@ -5,6 +5,9 @@
 // compatbility mode is active the K factor is converted to a LA15
 // equivalent (that is, the return value is always a LA15 value).
 //
+// E-jerk<2 is also bumped in LA10 mode to restore the the printing speed
+// to values comparable to existing settings.
+//
 // Once the interpretation mode has been set it is kept until the mode
 // is explicitly reset. This is done to handle transparent fallback for
 // old firmware revisions in combination with the following gcode
@@ -31,9 +34,13 @@ enum __attribute__((packed)) LA10C_MODE
     LA10C_LA10    = 2
 };
 
-// Explicitly set/reset the interpretation mode for la10c_value()
+// Explicitly set/get/reset the interpretation mode for la10c_value()
 void la10c_mode_change(LA10C_MODE mode);
+LA10C_MODE la10c_mode_get();
 static inline void la10c_reset() { la10c_mode_change(LA10C_UNKNOWN); }
 
 // Return a LA15 K value according to the supplied value and mode
 float la10c_value(float k);
+
+// Return an updated LA15 E-jerk value according to the current mode
+float la10c_jerk(float j);

+ 3 - 1
Firmware/language.h

@@ -6,7 +6,9 @@
 
 #include "config.h"
 #include <inttypes.h>
-//#include <stdio.h>
+#ifdef DEBUG_SEC_LANG
+    #include <stdio.h>
+#endif //DEBUG_SEC_LANG
 
 #define PROTOCOL_VERSION "1.0"
 

+ 4 - 0
Firmware/mesh_bed_calibration.cpp

@@ -6,6 +6,7 @@
 #include "mesh_bed_leveling.h"
 #include "stepper.h"
 #include "ultralcd.h"
+#include "temperature.h"
 
 #ifdef TMC2130
 #include "tmc2130.h"
@@ -946,6 +947,7 @@ inline bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, i
         )
 {
 	bool high_deviation_occured = false; 
+    bedPWMDisabled = 1;
 #ifdef TMC2130
 	FORCE_HIGH_POWER_START;
 #endif
@@ -1044,6 +1046,7 @@ inline bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, i
 #ifdef TMC2130
 	FORCE_HIGH_POWER_END;
 #endif
+    bedPWMDisabled = 0;
 	return true;
 
 error:
@@ -1053,6 +1056,7 @@ error:
 #ifdef TMC2130
 	FORCE_HIGH_POWER_END;
 #endif
+    bedPWMDisabled = 0;
 	return false;
 }
 

+ 7 - 2
Firmware/messages.c

@@ -138,6 +138,11 @@ const char MSG_TIMEOUT[] PROGMEM_I1 = ISTR("Timeout"); ////
 const char MSG_BRIGHT[] PROGMEM_I1 = ISTR("Bright"); ////
 const char MSG_DIM[] PROGMEM_I1 = ISTR("Dim"); ////
 const char MSG_AUTO[] PROGMEM_I1 = ISTR("Auto"); ////
+#ifdef IR_SENSOR_ANALOG
+// Beware - the space at the beginning is necessary since it is reused in LCD menu items which are to be with a space
+const char MSG_04_OR_NEWER[] PROGMEM_I1 = ISTR(" 0.4 or newer");
+const char MSG_03_OR_OLDER[] PROGMEM_I1 = ISTR(" 0.3 or older");
+#endif
 
 //not internationalized messages
 const char MSG_SD_WORKDIR_FAIL[] PROGMEM_N1 = "workDir open failed"; ////
@@ -168,6 +173,6 @@ const char MSG_OCTOPRINT_CANCEL[] PROGMEM_N1 = "// action:cancel"; ////
 const char MSG_FANCHECK_EXTRUDER[] PROGMEM_N1 = "Err: EXTR. FAN ERROR"; ////c=20
 const char MSG_FANCHECK_PRINT[] PROGMEM_N1 = "Err: PRINT FAN ERROR"; ////c=20
 const char MSG_M112_KILL[] PROGMEM_N1 = "M112 called. Emergency Stop."; ////c=20
-#ifdef LA_LIVE_K
 const char MSG_ADVANCE_K[] PROGMEM_N1 = "Advance K:"; ////c=13
-#endif
+const char MSG_POWERPANIC_DETECTED[] PROGMEM_N1 = "POWER PANIC DETECTED"; ////c=20
+

+ 5 - 0
Firmware/messages.h

@@ -138,6 +138,10 @@ extern const char MSG_TIMEOUT[];
 extern const char MSG_BRIGHT[];
 extern const char MSG_DIM[];
 extern const char MSG_AUTO[];
+#ifdef IR_SENSOR_ANALOG
+extern const char MSG_04_OR_NEWER[];
+extern const char MSG_03_OR_OLDER[];
+#endif
 
 //not internationalized messages
 extern const char MSG_BROWNOUT_RESET[];
@@ -170,6 +174,7 @@ extern const char MSG_FANCHECK_EXTRUDER[];
 extern const char MSG_FANCHECK_PRINT[];
 extern const char MSG_M112_KILL[];
 extern const char MSG_ADVANCE_K[];
+extern const char MSG_POWERPANIC_DETECTED[];
 
 #if defined(__cplusplus)
 }

+ 6 - 5
Firmware/mmu.cpp

@@ -383,8 +383,9 @@ void mmu_loop(void)
 			//printf_P(PSTR("Eact: %d\n"), int(e_active()));
 			if (!mmu_finda && CHECK_FSENSOR && fsensor_enabled) {
 				fsensor_checkpoint_print();
-				ad_markDepleted(mmu_extruder);
-				if (lcd_autoDepleteEnabled() && !ad_allDepleted())
+				if (mmu_extruder != MMU_FILAMENT_UNKNOWN) // Can't deplete unknown extruder.
+                    ad_markDepleted(mmu_extruder);
+				if (lcd_autoDepleteEnabled() && !ad_allDepleted() && mmu_extruder != MMU_FILAMENT_UNKNOWN) // Can't auto if F=?
 				{
 				    enquecommand_front_P(PSTR("M600 AUTO")); //save print and run M600 command
 				}
@@ -795,8 +796,8 @@ void mmu_load_to_nozzle()
 {
 	st_synchronize();
 	
-	bool saved_e_relative_mode = axis_relative_modes[E_AXIS];
-	if (!saved_e_relative_mode) axis_relative_modes[E_AXIS] = true;
+	const bool saved_e_relative_mode = axis_relative_modes & E_AXIS_MASK;
+	if (!saved_e_relative_mode) axis_relative_modes |= E_AXIS_MASK;
 	if (ir_sensor_detected)
 	{
 		current_position[E_AXIS] += 3.0f;
@@ -820,7 +821,7 @@ void mmu_load_to_nozzle()
 	feedrate = 871;
 	plan_buffer_line_curposXYZE(feedrate / 60, active_extruder);
     st_synchronize();
-	if (!saved_e_relative_mode) axis_relative_modes[E_AXIS] = false;
+	if (!saved_e_relative_mode) axis_relative_modes &= ~E_AXIS_MASK;
 }
 
 void mmu_M600_wait_and_beep() {

+ 9 - 14
Firmware/pat9125.c

@@ -183,9 +183,9 @@ uint8_t pat9125_update(void)
 		if (pat9125_PID1 == 0xff) return 0;
 		if (ucMotion & 0x80)
 		{
-			uint8_t ucXL = pat9125_rd_reg(PAT9125_DELTA_XL);
-			uint8_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
-			uint8_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
+			uint16_t ucXL = pat9125_rd_reg(PAT9125_DELTA_XL);
+			uint16_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
+			uint16_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
 			if (pat9125_PID1 == 0xff) return 0;
 			int16_t iDX = ucXL | ((ucXYH << 4) & 0xf00);
 			int16_t iDY = ucYL | ((ucXYH << 8) & 0xf00);
@@ -207,8 +207,8 @@ uint8_t pat9125_update_y(void)
 		if (pat9125_PID1 == 0xff) return 0;
 		if (ucMotion & 0x80)
 		{
-			uint8_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
-			uint8_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
+			uint16_t ucYL = pat9125_rd_reg(PAT9125_DELTA_YL);
+			uint16_t ucXYH = pat9125_rd_reg(PAT9125_DELTA_XYH);
 			if (pat9125_PID1 == 0xff) return 0;
 			int16_t iDY = ucYL | ((ucXYH << 8) & 0xf00);
 			if (iDY & 0x800) iDY -= 4096;
@@ -219,18 +219,13 @@ uint8_t pat9125_update_y(void)
 	return 0;
 }
 
-uint8_t pat9125_update_y2(void)
+uint8_t pat9125_update_bs(void)
 {
 	if ((pat9125_PID1 == 0x31) && (pat9125_PID2 == 0x91))
 	{
-		uint8_t ucMotion = pat9125_rd_reg(PAT9125_MOTION);
-		if (pat9125_PID1 == 0xff) return 0; //NOACK error
-		if (ucMotion & 0x80)
-		{
-			int8_t dy = pat9125_rd_reg(PAT9125_DELTA_YL);
-			if (pat9125_PID1 == 0xff) return 0; //NOACK error
-			pat9125_y -= dy; //negative number, because direction switching does not work
-		}
+		pat9125_b = pat9125_rd_reg(PAT9125_FRAME);
+		pat9125_s = pat9125_rd_reg(PAT9125_SHUTTER);
+		if (pat9125_PID1 == 0xff) return 0;
 		return 1;
 	}
 	return 0;

+ 3 - 3
Firmware/pat9125.h

@@ -19,9 +19,9 @@ extern uint8_t pat9125_b;
 extern uint8_t pat9125_s;
 
 extern uint8_t pat9125_init(void);
-extern uint8_t pat9125_update(void);
-extern uint8_t pat9125_update_y(void);
-extern uint8_t pat9125_update_y2(void);
+extern uint8_t pat9125_update(void);    // update all sensor data
+extern uint8_t pat9125_update_y(void);  // update _y only
+extern uint8_t pat9125_update_bs(void); // update _b/_s only
 
 
 #if defined(__cplusplus)

+ 28 - 23
Firmware/planner.cpp

@@ -1061,16 +1061,16 @@ Having the real displacement of the head, we can calculate the total movement le
     /**
      * Use LIN_ADVANCE within this block if all these are true:
      *
-     * block->steps_e           : This is a print move, because we checked for X, Y, Z steps before.
      * extruder_advance_K       : There is an advance factor set.
-     * delta_mm[E_AXIS] > 0     : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
+     * delta_mm[E_AXIS] >= 0    : Extruding or traveling, but _not_ retracting.
      * |delta_mm[Z_AXIS]| < 0.5 : Z is only moved for leveling (_not_ for priming)
      */
-    block->use_advance_lead = block->steps_e.wide
-                              && extruder_advance_K
-                              && delta_mm[E_AXIS] > 0
+    block->use_advance_lead = extruder_advance_K > 0
+                              && delta_mm[E_AXIS] >= 0
                               && abs(delta_mm[Z_AXIS]) < 0.5;
     if (block->use_advance_lead) {
+        // all extrusion moves with LA require a compression which is proportional to the
+        // extrusion_length to distance ratio (e/D)
         e_D_ratio = (e - position_float[E_AXIS]) /
                     sqrt(sq(x - position_float[X_AXIS])
                          + sq(y - position_float[Y_AXIS])
@@ -1082,10 +1082,10 @@ Having the real displacement of the head, we can calculate the total movement le
         // 100mm wide lines using 3mm filament or 35mm wide lines using 1.75mm filament.
         if (e_D_ratio > 3.0)
             block->use_advance_lead = false;
-        else {
-            const uint32_t max_accel_steps_per_s2 = cs.max_jerk[E_AXIS] / (extruder_advance_K * e_D_ratio) * steps_per_mm;
-            if (block->acceleration_st > max_accel_steps_per_s2) {
-                block->acceleration_st = max_accel_steps_per_s2;
+        else if (e_D_ratio > 0) {
+            const float max_accel_per_s2 = cs.max_jerk[E_AXIS] / (extruder_advance_K * e_D_ratio);
+            if (cs.acceleration > max_accel_per_s2) {
+                block->acceleration_st = ceil(max_accel_per_s2 * steps_per_mm);
                 #ifdef LA_DEBUG
                 SERIAL_ECHOLNPGM("LA: Block acceleration limited due to max E-jerk");
                 #endif
@@ -1133,21 +1133,33 @@ Having the real displacement of the head, we can calculate the total movement le
       block->adv_comp = extruder_advance_K * e_D_ratio * cs.axis_steps_per_unit[E_AXIS];
       block->max_adv_steps = block->nominal_speed * block->adv_comp;
 
+      float advance_speed;
+      if (e_D_ratio > 0)
+          advance_speed = (extruder_advance_K * e_D_ratio * block->acceleration * cs.axis_steps_per_unit[E_AXIS]);
+      else
+          advance_speed = cs.max_jerk[E_AXIS] * cs.axis_steps_per_unit[E_AXIS];
+
       // to save more space we avoid another copy of calc_timer and go through slow division, but we
       // still need to replicate the *exact* same step grouping policy (see below)
-      float advance_speed = (extruder_advance_K * e_D_ratio * block->acceleration * cs.axis_steps_per_unit[E_AXIS]);
       if (advance_speed > MAX_STEP_FREQUENCY) advance_speed = MAX_STEP_FREQUENCY;
-      block->advance_rate = (F_CPU / 8.0) / advance_speed;
-      if (block->advance_rate > 20000) {
-          block->advance_rate = (block->advance_rate >> 2)&0x3fff;
+      float advance_rate = (F_CPU / 8.0) / advance_speed;
+      if (advance_speed > 20000) {
+          block->advance_rate = advance_rate * 4;
           block->advance_step_loops = 4;
       }
-      else if (block->advance_rate > 10000) {
-          block->advance_rate = (block->advance_rate >> 1)&0x7fff;
+      else if (advance_speed > 10000) {
+          block->advance_rate = advance_rate * 2;
           block->advance_step_loops = 2;
       }
       else
+      {
+          // never overflow the internal accumulator with very low rates
+          if (advance_rate < UINT16_MAX)
+              block->advance_rate = advance_rate;
+          else
+              block->advance_rate = UINT16_MAX;
           block->advance_step_loops = 1;
+      }
 
       #ifdef LA_DEBUG
       if (block->advance_step_loops > 2)
@@ -1345,14 +1357,7 @@ void plan_set_position(float x, float y, float z, const float &e)
     apply_rotation_xyz(plan_bed_level_matrix, x, y, z);
 #endif // ENABLE_AUTO_BED_LEVELING
 
-    // Apply the machine correction matrix.
-    if (world2machine_correction_mode != WORLD2MACHINE_CORRECTION_NONE)
-    {
-        float tmpx = x;
-        float tmpy = y;
-        x = world2machine_rotation_and_skew[0][0] * tmpx + world2machine_rotation_and_skew[0][1] * tmpy + world2machine_shift[0];
-        y = world2machine_rotation_and_skew[1][0] * tmpx + world2machine_rotation_and_skew[1][1] * tmpy + world2machine_shift[1];
-    }
+    world2machine(x, y);
 
   position[X_AXIS] = lround(x*cs.axis_steps_per_unit[X_AXIS]);
   position[Y_AXIS] = lround(y*cs.axis_steps_per_unit[Y_AXIS]);

+ 3 - 0
Firmware/planner.h

@@ -173,6 +173,9 @@ void plan_set_e_position(const float &e);
 // Reset the E position to zero at the start of the next segment
 void plan_reset_next_e();
 
+inline void set_current_to_destination() { memcpy(current_position, destination, sizeof(current_position)); }
+inline void set_destination_to_current() { memcpy(destination, current_position, sizeof(destination)); }
+
 extern bool e_active();
 
 void check_axes_activity();

+ 65 - 33
Firmware/stepper.cpp

@@ -36,9 +36,9 @@
 #include "tmc2130.h"
 #endif //TMC2130
 
-#ifdef FILAMENT_SENSOR
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
 #include "fsensor.h"
-int fsensor_counter = 0; //counter for e-steps
+int fsensor_counter; //counter for e-steps
 #endif //FILAMENT_SENSOR
 
 #include "mmu.h"
@@ -117,8 +117,8 @@ volatile signed char count_direction[NUM_AXIS] = { 1, 1, 1, 1};
   void advance_isr();
 
   static const uint16_t ADV_NEVER      = 0xFFFF;
-  static const uint8_t  ADV_INIT       = 0b01;
-  static const uint8_t  ADV_DECELERATE = 0b10;
+  static const uint8_t  ADV_INIT       = 0b01; // initialize LA
+  static const uint8_t  ADV_ACC_VARY   = 0b10; // varying acceleration phase
 
   static uint16_t nextMainISR;
   static uint16_t nextAdvanceISR;
@@ -128,13 +128,12 @@ volatile signed char count_direction[NUM_AXIS] = { 1, 1, 1, 1};
   static uint16_t eISR_Err;
 
   static uint16_t current_adv_steps;
-  static uint16_t final_adv_steps;
-  static uint16_t max_adv_steps;
-  static uint32_t LA_decelerate_after;
+  static uint16_t target_adv_steps;
 
-  static int8_t e_steps;
-  static uint8_t e_step_loops;
-  static int8_t LA_phase;
+  static int8_t e_steps;        // scheduled e-steps during each isr loop
+  static uint8_t e_step_loops;  // e-steps to execute at most in each isr loop
+  static uint8_t e_extruding;   // current move is an extrusion move
+  static int8_t LA_phase;       // LA compensation phase
 
   #define _NEXT_ISR(T)    main_Rate = nextMainISR = T
 #else
@@ -349,15 +348,12 @@ FORCE_INLINE void stepper_next_block()
 
 #ifdef LIN_ADVANCE
     if (current_block->use_advance_lead) {
-        LA_decelerate_after = current_block->decelerate_after;
-        final_adv_steps = current_block->final_adv_steps;
-        max_adv_steps = current_block->max_adv_steps;
         e_step_loops = current_block->advance_step_loops;
+        target_adv_steps = current_block->max_adv_steps;
     } else {
-        e_steps = 0;
         e_step_loops = 1;
-        current_adv_steps = 0;
     }
+    e_steps = 0;
     nextAdvanceISR = ADV_NEVER;
     LA_phase = -1;
 #endif
@@ -371,11 +367,17 @@ FORCE_INLINE void stepper_next_block()
       counter_y.lo = counter_x.lo;
       counter_z.lo = counter_x.lo;
       counter_e.lo = counter_x.lo;
+#ifdef LIN_ADVANCE
+      e_extruding = current_block->steps_e.lo != 0;
+#endif
     } else {
       counter_x.wide = -(current_block->step_event_count.wide >> 1);
       counter_y.wide = counter_x.wide;
       counter_z.wide = counter_x.wide;
       counter_e.wide = counter_x.wide;
+#ifdef LIN_ADVANCE
+      e_extruding = current_block->steps_e.wide != 0;
+#endif
     }
     step_events_completed.wide = 0;
     // Set directions.
@@ -421,9 +423,8 @@ FORCE_INLINE void stepper_next_block()
 #endif /* LIN_ADVANCE */
       count_direction[E_AXIS] = 1;
     }
-#ifdef FILAMENT_SENSOR
-	fsensor_counter = 0;
-	fsensor_st_block_begin(count_direction[E_AXIS] < 0);
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+    fsensor_st_block_begin(count_direction[E_AXIS] < 0);
 #endif //FILAMENT_SENSOR
   }
   else {
@@ -812,7 +813,7 @@ FORCE_INLINE void isr() {
 #ifdef LIN_ADVANCE
         if (current_block->use_advance_lead) {
             if (step_events_completed.wide <= (unsigned long int)step_loops)
-                la_state = ADV_INIT;
+                la_state = ADV_INIT | ADV_ACC_VARY;
         }
 #endif
       }
@@ -828,11 +829,13 @@ FORCE_INLINE void isr() {
         uint16_t timer = calc_timer(step_rate, step_loops);
         _NEXT_ISR(timer);
         deceleration_time += timer;
+
 #ifdef LIN_ADVANCE
         if (current_block->use_advance_lead) {
-            la_state = ADV_DECELERATE;
-            if (step_events_completed.wide <= (unsigned long int)current_block->decelerate_after + step_loops)
-                la_state |= ADV_INIT;
+            if (step_events_completed.wide <= (unsigned long int)current_block->decelerate_after + step_loops) {
+                target_adv_steps = current_block->final_adv_steps;
+                la_state = ADV_INIT | ADV_ACC_VARY;
+            }
         }
 #endif
       }
@@ -842,6 +845,17 @@ FORCE_INLINE void isr() {
           // the initial interrupt blocking.
           OCR1A_nominal = calc_timer(uint16_t(current_block->nominal_rate), step_loops);
           step_loops_nominal = step_loops;
+
+#ifdef LIN_ADVANCE
+          if(current_block->use_advance_lead) {
+              if (!nextAdvanceISR) {
+                  // Due to E-jerk, there can be discontinuities in pressure state where an
+                  // acceleration or deceleration can be skipped or joined with the previous block.
+                  // If LA was not previously active, re-check the pressure level
+                  la_state = ADV_INIT;
+              }
+          }
+#endif
         }
         _NEXT_ISR(OCR1A_nominal);
       }
@@ -850,10 +864,23 @@ FORCE_INLINE void isr() {
 
 #ifdef LIN_ADVANCE
     // avoid multiple instances or function calls to advance_spread
-    if (la_state & ADV_INIT) eISR_Err = current_block->advance_rate / 4;
+    if (la_state & ADV_INIT) {
+        if (current_adv_steps == target_adv_steps) {
+            // nothing to be done in this phase
+            la_state = 0;
+        }
+        else {
+            eISR_Err = current_block->advance_rate / 4;
+            if ((la_state & ADV_ACC_VARY) && e_extruding && (current_adv_steps > target_adv_steps)) {
+                // LA could reverse the direction of extrusion in this phase
+                LA_phase = 0;
+            }
+        }
+    }
     if (la_state & ADV_INIT || nextAdvanceISR != ADV_NEVER) {
+        // update timers & phase for the next iteration
         advance_spread(main_Rate);
-        if (la_state & ADV_DECELERATE) {
+        if (LA_phase >= 0) {
             if (step_loops == e_step_loops)
                 LA_phase = (eISR_Rate > main_Rate);
             else {
@@ -899,7 +926,7 @@ FORCE_INLINE void isr() {
 // Timer interrupt for E. e_steps is set in the main routine.
 
 FORCE_INLINE void advance_isr() {
-    if (step_events_completed.wide > LA_decelerate_after && current_adv_steps > final_adv_steps) {
+    if (current_adv_steps > target_adv_steps) {
         // decompression
         e_steps -= e_step_loops;
         if (e_steps) WRITE_NC(E0_DIR_PIN, e_steps < 0? INVERT_E0_DIR: !INVERT_E0_DIR);
@@ -909,7 +936,7 @@ FORCE_INLINE void advance_isr() {
             current_adv_steps = 0;
         nextAdvanceISR = eISR_Rate;
     }
-    else if (step_events_completed.wide < LA_decelerate_after && current_adv_steps < max_adv_steps) {
+    else if (current_adv_steps < target_adv_steps) {
         // compression
         e_steps += e_step_loops;
         if (e_steps) WRITE_NC(E0_DIR_PIN, e_steps < 0? INVERT_E0_DIR: !INVERT_E0_DIR);
@@ -973,13 +1000,13 @@ FORCE_INLINE void advance_isr_scheduler() {
             WRITE_NC(E0_STEP_PIN, !INVERT_E_STEP_PIN);
             e_steps += (rev? 1: -1);
             WRITE_NC(E0_STEP_PIN, INVERT_E_STEP_PIN);
-#ifdef FILAMENT_SENSOR
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
             fsensor_counter += (rev? -1: 1);
 #endif
         }
         while(--max_ticks);
 
-#ifdef FILAMENT_SENSOR
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
         if (abs(fsensor_counter) >= fsensor_chunk_len)
         {
             fsensor_st_block_chunk(fsensor_counter);
@@ -1234,9 +1261,6 @@ void st_init()
   nextMainISR = 0;
   nextAdvanceISR = ADV_NEVER;
   main_Rate = ADV_NEVER;
-  e_steps = 0;
-  e_step_loops = 1;
-  LA_phase = -1;
   current_adv_steps = 0;
 #endif
 
@@ -1357,8 +1381,6 @@ void quickStop()
 }
 
 #ifdef BABYSTEPPING
-
-
 void babystep(const uint8_t axis,const bool direction)
 {
   //MUST ONLY BE CALLED BY A ISR, it depends on that no other ISR interrupts this
@@ -1594,3 +1616,13 @@ void microstep_readings()
       #endif
 }
 #endif //TMC2130
+
+
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+void st_reset_fsensor()
+{
+    CRITICAL_SECTION_START;
+    fsensor_counter = 0;
+    CRITICAL_SECTION_END;
+}
+#endif //FILAMENT_SENSOR

+ 4 - 1
Firmware/stepper.h

@@ -92,7 +92,10 @@ void microstep_readings();
 #ifdef BABYSTEPPING
   void babystep(const uint8_t axis,const bool direction); // perform a short step with a single stepper motor, outside of any convention
 #endif
-     
 
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
+// reset the internal filament sensor state
+void st_reset_fsensor();
+#endif
 
 #endif

+ 4 - 4
Firmware/system_timer.h

@@ -11,8 +11,8 @@
 #define _millis millis2
 #define _micros micros2
 #define _delay delay2
-#define _tone tone2
-#define _noTone noTone2
+#define _tone tone
+#define _noTone noTone
 
 #define timer02_set_pwm0(pwm0)
 
@@ -20,8 +20,8 @@
 #define _millis millis
 #define _micros micros
 #define _delay delay
-#define _tone(x, y) /*tone*/
-#define _noTone(x) /*noTone*/
+#define _tone tone
+#define _noTone noTone
 #define timer02_set_pwm0(pwm0)
 #endif //SYSTEM_TIMER_2
 

+ 16 - 5
Firmware/temperature.cpp

@@ -73,7 +73,7 @@ int current_voltage_raw_pwr = 0;
 int current_voltage_raw_bed = 0;
 #endif
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 int current_voltage_raw_IR = 0;
 #endif //IR_SENSOR_ANALOG
 
@@ -210,6 +210,14 @@ static void temp_runaway_check(int _heater_id, float _target_temperature, float
 static void temp_runaway_stop(bool isPreheat, bool isBed);
 #endif
 
+// return "false", if all extruder-heaters are 'off' (ie. "true", if any heater is 'on')
+bool checkAllHotends(void)
+{
+    bool result=false;
+    for(int i=0;i<EXTRUDERS;i++) result=(result||(target_temperature[i]!=0));
+    return(result);
+}
+
   void PID_autotune(float temp, int extruder, int ncycles)
   {
   pid_number_of_cycles = ncycles;
@@ -1403,6 +1411,7 @@ void disable_heater()
     target_temperature_bed=0;
     soft_pwm_bed=0;
 	timer02_set_pwm0(soft_pwm_bed << 1);
+	bedPWMDisabled = 0;
     #if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
       //WRITE(HEATER_BED_PIN,LOW);
     #endif
@@ -1587,7 +1596,7 @@ void adc_ready(void) //callback from adc when sampling finished
 #ifdef VOLT_BED_PIN
 	current_voltage_raw_bed = adc_values[ADC_PIN_IDX(VOLT_BED_PIN)]; // 6->9
 #endif
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
      current_voltage_raw_IR = adc_values[ADC_PIN_IDX(VOLT_IR_PIN)];
 #endif //IR_SENSOR_ANALOG
 	temp_meas_ready = true;
@@ -2002,6 +2011,8 @@ void check_max_temp()
 //! number of repeating the same state with consecutive step() calls
 //! used to slow down text switching
 struct alert_automaton_mintemp {
+	const char *m2;
+	alert_automaton_mintemp(const char *m2):m2(m2){}
 private:
 	enum { ALERT_AUTOMATON_SPEED_DIV = 5 };
 	enum class States : uint8_t { Init = 0, TempAboveMintemp, ShowPleaseRestart, ShowMintemp };
@@ -2021,7 +2032,6 @@ public:
 	//! @param current_temp current hotend/bed temperature (for computing simple hysteresis)
 	//! @param mintemp minimal temperature including hysteresis to check current_temp against
 	void step(float current_temp, float mintemp){
-		static const char m2[] PROGMEM = "MINTEMP fixed";
 		static const char m1[] PROGMEM = "Please restart";
 		switch(state){
 		case States::Init: // initial state - check hysteresis
@@ -2049,8 +2059,9 @@ public:
 		}
 	}
 };
-
-static alert_automaton_mintemp alert_automaton_hotend, alert_automaton_bed;
+static const char m2hotend[] PROGMEM = "MINTEMP HOTEND fixed";
+static const char m2bed[] PROGMEM = "MINTEMP BED fixed";
+static alert_automaton_mintemp alert_automaton_hotend(m2hotend), alert_automaton_bed(m2bed);
 
 void check_min_temp_heater0()
 {

+ 8 - 1
Firmware/temperature.h

@@ -47,6 +47,8 @@
 void tp_init();  //initialize the heating
 void manage_heater(); //it is critical that this is called periodically.
 
+extern bool checkAllHotends(void);
+
 // low level conversion routines
 // do not use these routines and variables outside of temperature.cpp
 extern int target_temperature[EXTRUDERS];  
@@ -76,7 +78,7 @@ extern int current_voltage_raw_pwr;
 extern int current_voltage_raw_bed;
 #endif
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 extern int current_voltage_raw_IR;
 #endif //IR_SENSOR_ANALOG
 
@@ -84,6 +86,8 @@ extern int current_voltage_raw_IR;
   extern unsigned char soft_pwm_bed;
 #endif
 
+extern bool bedPWMDisabled;
+
 #ifdef PIDTEMP
   extern int pid_cycle, pid_number_of_cycles;
   extern float Kc,_Kp,_Ki,_Kd;
@@ -220,6 +224,9 @@ FORCE_INLINE bool isCoolingBed() {
 #error Invalid number of extruders
 #endif
 
+// return "false", if all heaters are 'off' (ie. "true", if any heater is 'on')
+#define CHECK_ALL_HEATERS (checkAllHotends()||(target_temperature_bed!=0))
+
 int getHeaterPower(int heater);
 void disable_heater();
 void updatePID();

+ 0 - 10
Firmware/timer02.c

@@ -136,14 +136,4 @@ void delay2(unsigned long ms)
 	}
 }
 
-void tone2(__attribute__((unused)) uint8_t _pin, __attribute__((unused)) unsigned int frequency/*, unsigned long duration*/)
-{
-	PIN_SET(BEEPER);
-}
-
-void noTone2(__attribute__((unused)) uint8_t _pin)
-{
-	PIN_CLR(BEEPER);
-}
-
 #endif //SYSTEM_TIMER_2

+ 0 - 7
Firmware/timer02.h

@@ -23,13 +23,6 @@ extern unsigned long micros2(void);
 ///! Reimplemented original delay() using timer2
 extern void delay2(unsigned long ms);
 
-///! Reimplemented original tone() using timer2
-///! Does not perform any PWM tone generation, it just sets the beeper pin to 1
-extern void tone2(uint8_t _pin, unsigned int frequency/*, unsigned long duration*/);
-
-///! Turn off beeping - set beeper pin to 0
-extern void noTone2(uint8_t _pin);
-
 #if defined(__cplusplus)
 }
 #endif //defined(__cplusplus)

+ 214 - 172
Firmware/ultralcd.cpp

@@ -68,6 +68,10 @@ uint8_t SilentModeMenu_MMU = 1; //activate mmu unit stealth mode
 
 int8_t FSensorStateMenu = 1;
 
+#ifdef IR_SENSOR_ANALOG
+bool bMenuFSDetect=false;
+#endif //IR_SENSOR_ANALOG
+
 
 #ifdef SDCARD_SORT_ALPHA
 bool presort_flag = false;
@@ -114,7 +118,7 @@ static const char* lcd_display_message_fullscreen_nonBlocking_P(const char *msg,
 // void copy_and_scalePID_d();
 
 /* Different menus */
-static void lcd_status_screen();
+//static void lcd_status_screen();                // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
 #if (LANG_MODE != 0)
 static void lcd_language_menu();
 #endif
@@ -164,10 +168,10 @@ static void reset_crash_det(unsigned char axis);
 static bool lcd_selfcheck_axis_sg(unsigned char axis);
 static bool lcd_selfcheck_axis(int _axis, int _travel);
 #else
-static bool lcd_selfcheck_endstops();
 static bool lcd_selfcheck_axis(int _axis, int _travel);
 static bool lcd_selfcheck_pulleys(int axis);
 #endif //TMC2130
+static bool lcd_selfcheck_endstops();
 
 static bool lcd_selfcheck_check_heater(bool _isbed);
 enum class TestScreen : uint_least8_t
@@ -235,8 +239,9 @@ static FanCheck lcd_selftest_fan_auto(int _fan);
 static bool lcd_selftest_fsensor();
 #endif //PAT9125
 static bool selftest_irsensor();
-#if IR_SENSOR_ANALOG
-static bool lcd_selftest_IRsensor();
+#ifdef IR_SENSOR_ANALOG
+static bool lcd_selftest_IRsensor(bool bStandalone=false);
+static void lcd_detect_IRsensor();
 #endif //IR_SENSOR_ANALOG
 static void lcd_selftest_error(TestError error, const char *_error_1, const char *_error_2);
 static void lcd_colorprint_change();
@@ -975,7 +980,7 @@ void lcdui_print_status_screen(void)
 }
 
 // Main status screen. It's up to the implementation specific part to show what is needed. As this is very display dependent
-static void lcd_status_screen()
+void lcd_status_screen()                          // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
 {
 	if (firstrun == 1) 
 	{
@@ -1796,11 +1801,23 @@ static void lcd_menu_fails_stats_print()
     uint8_t crashX = eeprom_read_byte((uint8_t*)EEPROM_CRASH_COUNT_X);
     uint8_t crashY = eeprom_read_byte((uint8_t*)EEPROM_CRASH_COUNT_Y);
     lcd_home();
+#ifndef PAT9125
     lcd_printf_P(failStatsFmt,
         _i("Last print failures"),  ////c=20 r=1
         _i("Power failures"), power,  ////c=14 r=1
         _i("Filam. runouts"), filam,  ////c=14 r=1
         _i("Crash"), crashX, crashY);  ////c=7 r=1
+#else
+    // On the MK3 include detailed PAT9125 statistics about soft failures
+    lcd_printf_P(PSTR("%S\n"
+                      " %-16.16S%-3d\n"
+                      " %-7.7S H %-3d S %-3d\n"
+                      " %-7.7S X %-3d Y %-3d"),
+                 _i("Last print failures"), ////c=20 r=1
+                 _i("Power failures"), power, ////c=14 r=1
+                 _i("Runouts"), filam, fsensor_softfail, //c=7 r=1
+                 _i("Crash"), crashX, crashY);  ////c=7 r=1
+#endif
     menu_back_if_clicked_fb();
 }
 
@@ -1940,7 +1957,7 @@ static void lcd_menu_temperatures()
     menu_back_if_clicked();
 }
 
-#if defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN) || IR_SENSOR_ANALOG
+#if defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN) || defined(IR_SENSOR_ANALOG)
 #define VOLT_DIV_R1 10000
 #define VOLT_DIV_R2 2370
 #define VOLT_DIV_FAC ((float)VOLT_DIV_R2 / (VOLT_DIV_R2 + VOLT_DIV_R1))
@@ -1952,27 +1969,24 @@ static void lcd_menu_temperatures()
 //! |                    |
 //! | PWR:         00.0V |	c=12 r=1
 //! | Bed:         00.0V |	c=12 r=1
-//! |                    |
+//! | IR :         00.0V |  c=12 r=1 optional
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
 static void lcd_menu_voltages()
 {
-	lcd_timeoutToStatus.stop(); //infinite timeout
-	float volt_pwr = VOLT_DIV_REF * ((float)current_voltage_raw_pwr / (1023 * OVERSAMPLENR)) / VOLT_DIV_FAC;
-	float volt_bed = VOLT_DIV_REF * ((float)current_voltage_raw_bed / (1023 * OVERSAMPLENR)) / VOLT_DIV_FAC;
-	lcd_home();
-#if !IR_SENSOR_ANALOG
-	lcd_printf_P(PSTR("\n"));
-#endif //!IR_SENSOR_ANALOG
-     lcd_printf_P(PSTR(" PWR:      %4.1fV\n" " BED:      %4.1fV"), volt_pwr, volt_bed);
-#if IR_SENSOR_ANALOG
-     float volt_IR = VOLT_DIV_REF * ((float)current_voltage_raw_IR / (1023 * OVERSAMPLENR));
-     lcd_printf_P(PSTR("\n IR :       %3.1fV"),volt_IR);
+    lcd_timeoutToStatus.stop(); //infinite timeout
+    float volt_pwr = VOLT_DIV_REF * ((float)current_voltage_raw_pwr / (1023 * OVERSAMPLENR)) / VOLT_DIV_FAC;
+    float volt_bed = VOLT_DIV_REF * ((float)current_voltage_raw_bed / (1023 * OVERSAMPLENR)) / VOLT_DIV_FAC;
+    lcd_home();
+    lcd_printf_P(PSTR(" PWR:      %4.1fV\n" " BED:      %4.1fV"), volt_pwr, volt_bed);
+#ifdef IR_SENSOR_ANALOG
+    float volt_IR = VOLT_DIV_REF * ((float)current_voltage_raw_IR / (1023 * OVERSAMPLENR));
+    lcd_printf_P(PSTR("\n IR :       %3.1fV"),volt_IR);
 #endif //IR_SENSOR_ANALOG
-     menu_back_if_clicked();
+    menu_back_if_clicked();
 }
-#endif //defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN) || IR_SENSOR_ANALOG
+#endif //defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN) || defined(IR_SENSOR_ANALOG)
 
 #ifdef TMC2130
 //! @brief Show Belt Status
@@ -2149,6 +2163,23 @@ static void lcd_support_menu()
   MENU_ITEM_BACK_P(_i("Date:"));////MSG_DATE c=17 r=1
   MENU_ITEM_BACK_P(PSTR(__DATE__));
 
+#ifdef IR_SENSOR_ANALOG
+  MENU_ITEM_BACK_P(STR_SEPARATOR);
+  MENU_ITEM_BACK_P(PSTR("Fil. sensor v.:"));
+  switch(oFsensorPCB)
+       {
+       case ClFsensorPCB::_Old:
+            MENU_ITEM_BACK_P(MSG_03_OR_OLDER);
+            break;
+       case ClFsensorPCB::_Rev04:
+            MENU_ITEM_BACK_P(MSG_04_OR_NEWER);
+            break;
+       case ClFsensorPCB::_Undef:
+       default:
+            MENU_ITEM_BACK_P(PSTR(" unknown state"));
+       }
+#endif // IR_SENSOR_ANALOG
+
 	MENU_ITEM_BACK_P(STR_SEPARATOR);
 	if (mmu_enabled)
 	{
@@ -2238,10 +2269,12 @@ void lcd_set_filament_autoload() {
      fsensor_autoload_set(!fsensor_autoload_enabled);
 }
 
+#if defined(FILAMENT_SENSOR) && defined(PAT9125)
 void lcd_set_filament_oq_meass()
 {
      fsensor_oq_meassure_set(!fsensor_oq_meassure_enabled);
 }
+#endif
 
 
 FilamentAction eFilamentAction=FilamentAction::None; // must be initialized as 'non-autoLoad'
@@ -2801,9 +2834,9 @@ static void lcd_LoadFilament()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Filament used:      | c=18 r=1
-//! |         00.00m     |
-//! |Print time:         | c=18 r=1
+//! |Filament used:      | c=19 r=1
+//! |           0000.00m |
+//! |Print time:         | c=19 r=1
 //! |        00h 00m 00s |
 //! ----------------------
 //! @endcode
@@ -2812,32 +2845,33 @@ static void lcd_LoadFilament()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Total filament :    | c=18 r=1
-//! |           000.00 m |
-//! |Total print time :  | c=18 r=1
-//! |     00d :00h :00 m |
+//! |Total filament:     | c=19 r=1
+//! |           0000.00m |
+//! |Total print time:   | c=19 r=1
+//! |        00d 00h 00m |
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations. Translations missing for "d"days, "h"ours, "m"inutes", "s"seconds".
 void lcd_menu_statistics()
 {
+    lcd_timeoutToStatus.stop(); //infinite timeout
 	if (IS_SD_PRINTING)
 	{
 		const float _met = ((float)total_filament_used) / (100000.f);
 		const uint32_t _t = (_millis() - starttime) / 1000ul;
-		const int _h = _t / 3600;
-		const int _m = (_t - (_h * 3600ul)) / 60ul;
-		const int _s = _t - ((_h * 3600ul) + (_m * 60ul));
+		const uint32_t _h = _t / 3600;
+		const uint8_t _m = (_t - (_h * 3600ul)) / 60ul;
+		const uint8_t _s = _t - ((_h * 3600ul) + (_m * 60ul));
 
-		lcd_clear();
+        lcd_home();
 		lcd_printf_P(_N(
 			"%S:\n"
-			"%17.2fm  \n"
+			"%18.2fm \n"
 			"%S:\n"
-			"%2dh %02dm %02ds"
+			"%10ldh %02hhdm %02hhds"
 		    ),
-            _i("Filament used"), _met,  ////c=18 r=1
-            _i("Print time"), _h, _m, _s);  ////c=18 r=1
+            _i("Filament used"), _met,  ////c=19 r=1
+            _i("Print time"), _h, _m, _s);  ////c=19 r=1
 		menu_back_if_clicked_fb();
 	}
 	else
@@ -2847,29 +2881,20 @@ void lcd_menu_statistics()
 		uint8_t _hours, _minutes;
 		uint32_t _days;
 		float _filament_m = (float)_filament/100;
-//		int _filament_km = (_filament >= 100000) ? _filament / 100000 : 0;
-//		if (_filament_km > 0)  _filament_m = _filament - (_filament_km * 100000);
 		_days = _time / 1440;
 		_hours = (_time - (_days * 1440)) / 60;
 		_minutes = _time - ((_days * 1440) + (_hours * 60));
 
-		lcd_clear();
+		lcd_home();
 		lcd_printf_P(_N(
 			"%S:\n"
-			"%17.2fm  \n"
+			"%18.2fm \n"
 			"%S:\n"
-			"%7ldd :%2hhdh :%02hhdm"
-		), _i("Total filament"), _filament_m, _i("Total print time"), _days, _hours, _minutes);
-		KEEPALIVE_STATE(PAUSED_FOR_USER);
-		while (!lcd_clicked())
-		{
-			manage_heater();
-			manage_inactivity(true);
-			_delay(100);
-		}
-		KEEPALIVE_STATE(NOT_BUSY);
-		lcd_quick_feedback();
-		menu_back();
+			"%10ldd %02hhdh %02hhdm"
+            ),
+            _i("Total filament"), _filament_m,  ////c=19 r=1
+            _i("Total print time"), _days, _hours, _minutes);  ////c=19 r=1
+        menu_back_if_clicked_fb();
 	}
 }
 
@@ -5636,7 +5661,7 @@ SETTINGS_VERSION;
 MENU_END();
 }
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 static void lcd_fsensor_actionNA_set(void)
 {
 switch(oFsensorActionNA)
@@ -5702,8 +5727,9 @@ void lcd_hw_setup_menu(void)                      // can not be "static"
     SETTINGS_NOZZLE;
     MENU_ITEM_SUBMENU_P(_i("Checks"), lcd_checking_menu);
 
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
     FSENSOR_ACTION_NA;
+    MENU_ITEM_FUNCTION_P(PSTR("Fsensor Detection"), lcd_detect_IRsensor);
 #endif //IR_SENSOR_ANALOG
     MENU_END();
 }
@@ -7122,7 +7148,7 @@ static void lcd_tune_menu()
 	else {
 		MENU_ITEM_TOGGLE_P(_T(MSG_FSENSOR), _T(MSG_ON), lcd_fsensor_state_set);
 	}
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
      FSENSOR_ACTION_NA;
 #endif //IR_SENSOR_ANALOG
 #endif //FILAMENT_SENSOR
@@ -7350,10 +7376,7 @@ void lcd_print_stop()
 
     planner_abort_hard(); //needs to be done since plan_buffer_line resets waiting_inside_plan_buffer_line_print_aborted to false. Also copies current to destination.
     
-    axis_relative_modes[X_AXIS] = false;
-    axis_relative_modes[Y_AXIS] = false;
-    axis_relative_modes[Z_AXIS] = false;
-    axis_relative_modes[E_AXIS] = true;
+    axis_relative_modes = E_AXIS_MASK; //XYZ absolute, E relative
     
     isPrintPaused = false; //clear isPrintPaused flag to allow starting next print after pause->stop scenario.
 }
@@ -7451,90 +7474,95 @@ static void lcd_belttest_v()
     lcd_belttest();
     menu_back_if_clicked();
 }
-void lcd_belttest_print(const char* msg, uint16_t X, uint16_t Y)
-{
-    lcd_clear();
-    lcd_printf_P(
-              _N(
-                 "%S:\n"
-                 "%S\n"
-                 "X:%d\n"
-                 "Y:%d"
-                 ),
-              _i("Belt status"),
-              msg,
-              X,Y
-            );
-}
+
 void lcd_belttest()
 {
-    int _progress = 0;
-    bool _result = true;
+    lcd_clear();
+	// Belttest requires high power mode. Enable it.
+	FORCE_HIGH_POWER_START;
+    
     uint16_t   X = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X));
     uint16_t   Y = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y));
-    lcd_belttest_print(_i("Checking X..."), X, Y);
-
-    _delay(2000);
+	lcd_printf_P(_i("Checking X axis  ")); // share message with selftest
+	lcd_set_cursor(0,1), lcd_printf_P(PSTR("X: %u -> ..."),X);
     KEEPALIVE_STATE(IN_HANDLER);
-
-    _result = lcd_selfcheck_axis_sg(X_AXIS);
-    X = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X));
-    if (!_result){
-        lcd_belttest_print(_i("Error"), X, Y);
-        return;
-    }
-
-    lcd_belttest_print(_i("Checking Y..."), X, Y);
-    _result = lcd_selfcheck_axis_sg(Y_AXIS);
-    Y = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y));
-
-    if (!_result){
-        lcd_belttest_print(_i("Error"), X, Y);
-        lcd_clear();
-        return;
+    
+	// N.B: it doesn't make sense to handle !lcd_selfcheck...() because selftest_sg throws its own error screen
+	// that clobbers ours, with more info than we could provide. So on fail we just fall through to take us back to status.
+    if (lcd_selfcheck_axis_sg(X_AXIS)){
+		X = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X));
+		lcd_set_cursor(10,1), lcd_printf_P(PSTR("%u"),X); // Show new X value next to old one.
+        lcd_puts_at_P(0,2,_i("Checking Y axis  "));
+		lcd_set_cursor(0,3), lcd_printf_P(PSTR("Y: %u -> ..."),Y);
+		if (lcd_selfcheck_axis_sg(Y_AXIS))
+		{
+			Y = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y));
+			lcd_set_cursor(10,3),lcd_printf_P(PSTR("%u"),Y);
+			lcd_set_cursor(19, 3);
+			lcd_print(LCD_STR_UPLEVEL);
+			lcd_wait_for_click_delay(10);
+		}
     }
-
-
-    lcd_belttest_print(_i("Done"), X, Y);
-
+	
+	FORCE_HIGH_POWER_END;
     KEEPALIVE_STATE(NOT_BUSY);
-    _delay(3000);
 }
 #endif //TMC2130
 
-#if IR_SENSOR_ANALOG
-static bool lcd_selftest_IRsensor()
+#ifdef IR_SENSOR_ANALOG
+// called also from marlin_main.cpp
+void printf_IRSensorAnalogBoardChange(bool bPCBrev04){
+    printf_P(PSTR("Filament sensor board change detected: revision %S\n"), bPCBrev04 ? MSG_04_OR_NEWER : MSG_03_OR_OLDER);
+}
+
+static bool lcd_selftest_IRsensor(bool bStandalone)
 {
-bool bAction;
-bool bPCBrev03b;
-uint16_t volt_IR_int;
-float volt_IR;
+    bool bAction;
+    bool bPCBrev04;
+    uint16_t volt_IR_int;
+    float volt_IR;
 
-volt_IR_int=current_voltage_raw_IR;
-bPCBrev03b=(volt_IR_int<((int)IRsensor_Hopen_TRESHOLD));
-volt_IR=VOLT_DIV_REF*((float)volt_IR_int/(1023*OVERSAMPLENR));
-printf_P(PSTR("Measured filament sensor high level: %4.2fV\n"),volt_IR);
-if(volt_IR_int<((int)IRsensor_Hmin_TRESHOLD))
-     {
-     lcd_selftest_error(TestError::FsensorLevel,"HIGH","");
-     return(false);
-     }
-lcd_show_fullscreen_message_and_wait_P(_i("Please insert filament (but not load them!) into extruder and then press the knob."));
-volt_IR_int=current_voltage_raw_IR;
-volt_IR=VOLT_DIV_REF*((float)volt_IR_int/(1023*OVERSAMPLENR));
-printf_P(PSTR("Measured filament sensor low level: %4.2fV\n"),volt_IR);
-if(volt_IR_int>((int)IRsensor_Lmax_TRESHOLD))
-     {
-     lcd_selftest_error(TestError::FsensorLevel,"LOW","");
-     return(false);
-     }
-if((bPCBrev03b?1:0)!=(uint8_t)oFsensorPCB)        // safer then "(uint8_t)bPCBrev03b"
-     {
-     printf_P(PSTR("Filament sensor board change detected: revision %S\n"),bPCBrev03b?PSTR("03b or newer"):PSTR("03 or older"));
-     oFsensorPCB=bPCBrev03b?ClFsensorPCB::_Rev03b:ClFsensorPCB::_Old;
-     eeprom_update_byte((uint8_t*)EEPROM_FSENSOR_PCB,(uint8_t)oFsensorPCB);
-     }
-return(true);
+    volt_IR_int=current_voltage_raw_IR;
+    bPCBrev04=(volt_IR_int<((int)IRsensor_Hopen_TRESHOLD));
+    volt_IR=VOLT_DIV_REF*((float)volt_IR_int/(1023*OVERSAMPLENR));
+    printf_P(PSTR("Measured filament sensor high level: %4.2fV\n"),volt_IR);
+    if(volt_IR_int < ((int)IRsensor_Hmin_TRESHOLD)){
+        if(!bStandalone)
+            lcd_selftest_error(TestError::FsensorLevel,"HIGH","");
+        return(false);
+    }
+    lcd_show_fullscreen_message_and_wait_P(_i("Insert the filament (do not load it) into the extruder and then press the knob."));
+    volt_IR_int=current_voltage_raw_IR;
+    volt_IR=VOLT_DIV_REF*((float)volt_IR_int/(1023*OVERSAMPLENR));
+    printf_P(PSTR("Measured filament sensor low level: %4.2fV\n"),volt_IR);
+    if(volt_IR_int > ((int)IRsensor_Lmax_TRESHOLD)){
+        if(!bStandalone)
+            lcd_selftest_error(TestError::FsensorLevel,"LOW","");
+        return(false);
+    }
+    if((bPCBrev04?1:0)!=(uint8_t)oFsensorPCB){        // safer then "(uint8_t)bPCBrev04"
+        printf_IRSensorAnalogBoardChange(bPCBrev04);
+        oFsensorPCB=bPCBrev04?ClFsensorPCB::_Rev04:ClFsensorPCB::_Old;
+        eeprom_update_byte((uint8_t*)EEPROM_FSENSOR_PCB,(uint8_t)oFsensorPCB);
+    }
+    return(true);
+}
+
+static void lcd_detect_IRsensor(){
+    bool bAction;
+
+    bMenuFSDetect = true;                               // inhibits some code inside "manage_inactivity()"
+    bAction = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is filament loaded?"), false, false);
+    if(bAction){
+        lcd_show_fullscreen_message_and_wait_P(_i("Please unload the filament first, then repeat this action."));
+        return;
+    }
+    bAction = lcd_selftest_IRsensor(true);
+    if(bAction)
+        lcd_show_fullscreen_message_and_wait_P(_i("Sensor verified, remove the filament now."));
+    else
+        lcd_show_fullscreen_message_and_wait_P(_i("Verification failed, remove the filament and try again."));
+    bMenuFSDetect=false;                              // de-inhibits some code inside "manage_inactivity()"
 }
 #endif //IR_SENSOR_ANALOG
 
@@ -7548,26 +7576,22 @@ bool lcd_selftest()
 	int _progress = 0;
 	bool _result = true;
 	bool _swapped_fan = false;
+//#ifdef IR_SENSOR_ANALOG
+#if (0)
+     bool bAction;
+     bAction=lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is the filament unloaded?"),false,true);
+     if(!bAction)
+          return(false);
+#endif //IR_SENSOR_ANALOG
 	lcd_wait_for_cool_down();
 	lcd_clear();
 	lcd_set_cursor(0, 0); lcd_puts_P(_i("Self test start  "));////MSG_SELFTEST_START c=20
 	#ifdef TMC2130
 	  FORCE_HIGH_POWER_START;
 	#endif // TMC2130
-#if !IR_SENSOR_ANALOG
-     _delay(2000);
-#endif //!IR_SENSOR_ANALOG
-    
-    FORCE_BL_ON_START;
-    
+	FORCE_BL_ON_START;
 	_delay(2000);
 	KEEPALIVE_STATE(IN_HANDLER);
-#if IR_SENSOR_ANALOG
-     bool bAction;
-     bAction=lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is filament unloaded?"),false,true);
-     if(!bAction)
-          return(false);
-#endif //IR_SENSOR_ANALOG
 
 	_progress = lcd_selftest_screen(TestScreen::ExtruderFan, _progress, 3, true, 2000);
 #if (defined(FANCHECK) && defined(TACH_0))
@@ -7633,11 +7657,7 @@ bool lcd_selftest()
 	if (_result)
 	{
 		_progress = lcd_selftest_screen(TestScreen::FansOk, _progress, 3, true, 2000);
-#ifndef TMC2130
-		_result = lcd_selfcheck_endstops();
-#else
-		_result = true;
-#endif
+		_result = lcd_selfcheck_endstops(); //With TMC2130, only the Z probe is tested.
 	}
 
 	if (_result)
@@ -7688,21 +7708,31 @@ bool lcd_selftest()
 #ifdef TMC2130
 		tmc2130_home_exit();
 		enable_endstops(false);
-		current_position[X_AXIS] = current_position[X_AXIS] + 14;
-		current_position[Y_AXIS] = current_position[Y_AXIS] + 12;
 #endif
 
 		//homeaxis(X_AXIS);
 		//homeaxis(Y_AXIS);
+        current_position[X_AXIS] = pgm_read_float(bed_ref_points_4);
+		current_position[Y_AXIS] = pgm_read_float(bed_ref_points_4+1);
+#ifdef TMC2130
+		//current_position[X_AXIS] += 0;
+		current_position[Y_AXIS] += 4;
+#endif //TMC2130
 		current_position[Z_AXIS] = current_position[Z_AXIS] + 10;
 		plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
 		st_synchronize();
+        set_destination_to_current();
 		_progress = lcd_selftest_screen(TestScreen::AxisZ, _progress, 3, true, 1500);
-		_result = lcd_selfcheck_axis(2, Z_MAX_POS);
-		if (eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE) != 1) {
-			enquecommand_P(PSTR("G28 W"));
-			enquecommand_P(PSTR("G1 Z15 F1000"));
-		}
+#ifdef TMC2130
+		homeaxis(Z_AXIS); //In case of failure, the code gets stuck in this function.
+#else
+        _result = lcd_selfcheck_axis(Z_AXIS, Z_MAX_POS);
+#endif //TMC2130
+
+		//raise Z to not damage the bed during and hotend testing
+		current_position[Z_AXIS] += 20;
+		plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
+		st_synchronize();
 	}
 
 #ifdef TMC2130
@@ -7759,7 +7789,8 @@ bool lcd_selftest()
 				_progress = lcd_selftest_screen(TestScreen::FsensorOk, _progress, 3, true, 2000); //fil sensor OK
 			}
 #endif //PAT9125
-#if IR_SENSOR_ANALOG
+//#ifdef IR_SENSOR_ANALOG
+#if (0)
 			_progress = lcd_selftest_screen(TestScreen::Fsensor, _progress, 3, true, 2000); //check filament sensor
                _result = lcd_selftest_IRsensor();
 			if (_result)
@@ -7825,14 +7856,10 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 	tmc2130_home_exit();
 	enable_endstops(true);
 
-	if (axis == X_AXIS) { //there is collision between cables and PSU cover in X axis if Z coordinate is too low
-		
-		current_position[Z_AXIS] += 17;
-		plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
-		tmc2130_home_enter(Z_AXIS_MASK);
-		st_synchronize();
-		tmc2130_home_exit();
-	}
+
+	raise_z_above(MESH_HOME_Z_SEARCH);
+	st_synchronize();
+	tmc2130_home_enter(1 << axis);
 
 // first axis length measurement begin	
 	
@@ -7879,6 +7906,7 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 
 	measured_axis_length[1] = abs(current_position_final - current_position_init);
 
+	tmc2130_home_exit();
 
 //end of second measurement, now check for possible errors:
 
@@ -7897,6 +7925,8 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 			current_position[axis] = 0;
 			plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
 			reset_crash_det(axis);
+			enable_endstops(true);
+			endstops_hit_on_purpose();
 			return false;
 		}
 	}
@@ -7915,17 +7945,18 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 			current_position[axis] = 0;
 			plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
 			reset_crash_det(axis);
-
+			endstops_hit_on_purpose();
 			return false;
 		}
 		current_position[axis] = 0;
 		plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
 		reset_crash_det(axis);
+		endstops_hit_on_purpose();
 		return true;
 }
 #endif //TMC2130
 
-//#ifndef TMC2130
+#ifndef TMC2130
 
 static bool lcd_selfcheck_axis(int _axis, int _travel)
 {
@@ -8024,12 +8055,13 @@ static bool lcd_selfcheck_axis(int _axis, int _travel)
 		{
 			lcd_selftest_error(TestError::Motor, _error_1, _error_2);
 		}
-	}
+	}    
+	current_position[_axis] = 0; //simulate axis home to avoid negative numbers for axis position, especially Z.
+	plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
 
 	return _stepresult;
 }
 
-#ifndef TMC2130
 static bool lcd_selfcheck_pulleys(int axis)
 {
 	float tmp_motor_loud[3] = DEFAULT_PWM_MOTOR_CURRENT_LOUD;
@@ -8074,7 +8106,7 @@ static bool lcd_selfcheck_pulleys(int axis)
 			((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1)) {
 			endstop_triggered = true;
 			if (current_position_init - 1 <= current_position[axis] && current_position_init + 1 >= current_position[axis]) {
-				current_position[axis] += (axis == X_AXIS) ? 13 : 9;
+				current_position[axis] += 10;
 				plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
 				st_synchronize();
 				return(true);
@@ -8096,31 +8128,42 @@ static bool lcd_selfcheck_pulleys(int axis)
 	}
 	return(true);
 }
+#endif //not defined TMC2130
 
 
 static bool lcd_selfcheck_endstops()
 {
 	bool _result = true;
 
-	if (((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
+	if (
+	#ifndef TMC2130
+		((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
 		((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) ||
+	#endif //!TMC2130
 		((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1))
 	{
+	#ifndef TMC2130
 		if ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) current_position[0] += 10;
 		if ((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) current_position[1] += 10;
+	#endif //!TMC2130
 		if ((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1) current_position[2] += 10;
 	}
 	plan_buffer_line_curposXYZE(manual_feedrate[0] / 60, active_extruder);
-	_delay(500);
+	st_synchronize();
 
-	if (((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
+	if (
+	#ifndef TMC2130
+		((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) ||
 		((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) ||
+	#endif //!TMC2130
 		((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1))
 	{
 		_result = false;
 		char _error[4] = "";
+	#ifndef TMC2130
 		if ((READ(X_MIN_PIN) ^ X_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "X");
 		if ((READ(Y_MIN_PIN) ^ Y_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "Y");
+	#endif //!TMC2130
 		if ((READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1) strcat(_error, "Z");
 		lcd_selftest_error(TestError::Endstops, _error, "");
 	}
@@ -8128,7 +8171,6 @@ static bool lcd_selfcheck_endstops()
 	manage_inactivity(true);
 	return _result;
 }
-#endif //not defined TMC2130
 
 static bool lcd_selfcheck_check_heater(bool _isbed)
 {

+ 8 - 1
Firmware/ultralcd.h

@@ -55,8 +55,10 @@ extern bool lcd_selftest();
 
 void lcd_menu_statistics(); 
 
+void lcd_status_screen();                         // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
 void lcd_menu_extruder_info();                    // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
 void lcd_menu_show_sensors_state();               // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
+
 #ifdef TMC2130
 bool lcd_crash_detect_enabled();
 void lcd_crash_detect_enable();
@@ -138,6 +140,11 @@ extern uint8_t farm_status;
 #define SILENT_MODE_OFF SILENT_MODE_POWER
 #endif
 
+#ifdef IR_SENSOR_ANALOG
+extern bool bMenuFSDetect;
+void printf_IRSensorAnalogBoardChange(bool bPCBrev04);
+#endif //IR_SENSOR_ANALOG
+
 extern int8_t SilentModeMenu;
 extern uint8_t SilentModeMenu_MMU;
 
@@ -251,7 +258,7 @@ enum class WizState : uint8_t
 void lcd_wizard(WizState state);
 
 #define VOLT_DIV_REF 5
-#if IR_SENSOR_ANALOG
+#ifdef IR_SENSOR_ANALOG
 #define IRsensor_Hmin_TRESHOLD (3.0*1023*OVERSAMPLENR/VOLT_DIV_REF) // ~3.0V (0.6*Vcc)
 #define IRsensor_Lmax_TRESHOLD (1.5*1023*OVERSAMPLENR/VOLT_DIV_REF) // ~1.5V (0.3*Vcc)
 #define IRsensor_Hopen_TRESHOLD (4.6*1023*OVERSAMPLENR/VOLT_DIV_REF) // ~4.6V (N.C. @ Ru~20-50k, Rd'=56k, Ru'=10k)

+ 8 - 4
Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h

@@ -151,8 +151,8 @@
 // this value is litlebit higher that real limit, because ambient termistor is on the board and is temperated from it,
 // temperature inside the case is around 31C for ambient temperature 25C, when the printer is powered on long time and idle
 // the real limit is 15C (same as MINTEMP limit), this is because 15C is end of scale for both used thermistors (bed, heater)
-#define MINTEMP_MINAMBIENT      25
-#define MINTEMP_MINAMBIENT_RAW  978
+#define MINTEMP_MINAMBIENT      10
+#define MINTEMP_MINAMBIENT_RAW  1002
 
 #define DEBUG_DCODE3
 
@@ -282,14 +282,14 @@
  *------------------------------------*/
 
 // Mintemps
-#define HEATER_0_MINTEMP 15
+#define HEATER_0_MINTEMP 10
 #define HEATER_1_MINTEMP 5
 #define HEATER_2_MINTEMP 5
 #define HEATER_MINTEMP_DELAY 15000                // [ms] ! if changed, check maximal allowed value @ ShortTimer
 #if HEATER_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see HEATER_MINTEMP_DELAY definition)"
 #endif
-#define BED_MINTEMP 15
+#define BED_MINTEMP 10
 #define BED_MINTEMP_DELAY 50000                   // [ms] ! if changed, check maximal allowed value @ ShortTimer
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
@@ -618,6 +618,10 @@
 // The following example, 12 * (4 * 16 / 400) = 12 * 0.16mm = 1.92mm.
 //#define UVLO_Z_AXIS_SHIFT 1.92
 #define UVLO_Z_AXIS_SHIFT 0.64
+// When powered off during PP recovery, the Z axis position can still be re-adjusted. In this case
+// we just need to shift to the nearest fullstep, but we need a move which is at least
+// "dropsegments" steps long. All the above rules still need to apply.
+#define UVLO_TINY_Z_AXIS_SHIFT 0.16
 // If power panic occured, and the current temperature is higher then target temperature before interrupt minus this offset, print will be recovered automatically. 
 #define AUTOMATIC_UVLO_BED_TEMP_OFFSET 5 
 

+ 9 - 5
Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h

@@ -153,8 +153,8 @@
 // this value is litlebit higher that real limit, because ambient termistor is on the board and is temperated from it,
 // temperature inside the case is around 31C for ambient temperature 25C, when the printer is powered on long time and idle
 // the real limit is 15C (same as MINTEMP limit), this is because 15C is end of scale for both used thermistors (bed, heater)
-#define MINTEMP_MINAMBIENT      25
-#define MINTEMP_MINAMBIENT_RAW  978
+#define MINTEMP_MINAMBIENT      10
+#define MINTEMP_MINAMBIENT_RAW  1002
 
 #define DEBUG_DCODE3
 
@@ -284,14 +284,14 @@
  *------------------------------------*/
 
 // Mintemps
-#define HEATER_0_MINTEMP 15
+#define HEATER_0_MINTEMP 10
 #define HEATER_1_MINTEMP 5
 #define HEATER_2_MINTEMP 5
 #define HEATER_MINTEMP_DELAY 15000                // [ms] ! if changed, check maximal allowed value @ ShortTimer
 #if HEATER_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see HEATER_MINTEMP_DELAY definition)"
 #endif
-#define BED_MINTEMP 15
+#define BED_MINTEMP 10
 #define BED_MINTEMP_DELAY 50000                   // [ms] ! if changed, check maximal allowed value @ ShortTimer
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
@@ -620,7 +620,11 @@
 // The following example, 12 * (4 * 16 / 400) = 12 * 0.16mm = 1.92mm.
 //#define UVLO_Z_AXIS_SHIFT 1.92
 #define UVLO_Z_AXIS_SHIFT 0.64
-// If power panic occured, and the current temperature is higher then target temperature before interrupt minus this offset, print will be recovered automatically. 
+// When powered off during PP recovery, the Z axis position can still be re-adjusted. In this case
+// we just need to shift to the nearest fullstep, but we need a move which is at least
+// "dropsegments" steps long. All the above rules still need to apply.
+#define UVLO_TINY_Z_AXIS_SHIFT 0.16
+// If power panic occured, and the current temperature is higher then target temperature before interrupt minus this offset, print will be recovered automatically.
 #define AUTOMATIC_UVLO_BED_TEMP_OFFSET 5 
 
 #define HEATBED_V2

+ 19 - 12
PF-build.sh

@@ -56,7 +56,7 @@
 #   Some may argue that this is only used by a script, BUT as soon someone accidentally or on purpose starts Arduino IDE
 #   it will use the default Arduino IDE folders and so can corrupt the build environment.
 #
-# Version: 1.0.6-Build_10
+# Version: 1.0.6-Build_13
 # Change log:
 # 12 Jan 2019, 3d-gussner, Fixed "compiler.c.elf.flags=-w -Os -Wl,-u,vfprintf -lprintf_flt -lm -Wl,--gc-sections" in 'platform.txt'
 # 16 Jan 2019, 3d-gussner, Build_2, Added development check to modify 'Configuration.h' to prevent unwanted LCD messages that Firmware is unknown
@@ -114,7 +114,10 @@
 # 26 Jul 2019, 3d-gussner, Change JSON repository to prusa3d after PR https://github.com/prusa3d/Arduino_Boards/pull/1 was merged
 # 23 Sep 2019, 3d-gussner, Prepare PF-build.sh for comming Prusa3d/Arduino_Boards version 1.0.2 Pull Request
 # 17 Oct 2019, 3d-gussner, Changed folder and check file names to have seperated build enviroments depening on Arduino IDE version and
-#                          board-versions. 
+#                          board-versions.
+# 15 Dec 2019, 3d-gussner, Prepare for switch to Prusa3d/PF-build-env repository
+# 15 Dec 2019, 3d-gussner, Fix Audrino user preferences for the chosen board.
+# 17 Dec 2019, 3d-gussner, Fix "timer0_fract = 0" warning by using Arduino_boards v1.0.3
 #### Start check if OSTYPE is supported
 OS_FOUND=$( command -v uname)
 
@@ -207,19 +210,22 @@ if ! type python > /dev/null; then
 	fi
 fi
 
-#### End prepare bash environment
+#### End prepare bash / Linux environment
 
 
 #### Set build environment 
 ARDUINO_ENV="1.8.5"
 BUILD_ENV="1.0.6"
-BOARD="rambo"
-BOARD_PACKAGE_NAME="PrusaResearchRambo"
-BOARD_VERSION="1.0.1"
+BOARD="prusa_einsy_rambo"
+BOARD_PACKAGE_NAME="PrusaResearch"
+BOARD_VERSION="1.0.3"
+#BOARD_URL="https://raw.githubusercontent.com/3d-gussner/Arduino_Boards/Prusa_Merge_v1.0.3/IDE_Board_Manager/package_prusa3d_index.json"
 BOARD_URL="https://raw.githubusercontent.com/prusa3d/Arduino_Boards/master/IDE_Board_Manager/package_prusa3d_index.json"
-BOARD_FILENAME="prusa3drambo"
-BOARD_FILE_URL="https://raw.githubusercontent.com/prusa3d/Arduino_Boards/master/IDE_Board_Manager/prusa3drambo-1.0.1.tar.bz2"
-PF_BUILD_FILE_URL="https://github.com/3d-gussner/PF-build-env/releases/download/$BUILD_ENV/PF-build-env-$BUILD_ENV.zip"
+BOARD_FILENAME="prusa3dboards"
+#BOARD_FILE_URL="https://raw.githubusercontent.com/3d-gussner/Arduino_Boards/Prusa_Merge_v1.0.3/IDE_Board_Manager/prusa3dboards-1.0.3.tar.bz2"
+BOARD_FILE_URL="https://raw.githubusercontent.com/prusa3d/Arduino_Boards/master/IDE_Board_Manager/prusa3dboards-1.0.3.tar.bz2"
+#PF_BUILD_FILE_URL="https://github.com/3d-gussner/PF-build-env-1/releases/download/$BUILD_ENV-WinLin/PF-build-env-WinLin-$BUILD_ENV.zip"
+PF_BUILD_FILE_URL="https://github.com/prusa3d/PF-build-env/releases/download/$BUILD_ENV-WinLin/PF-build-env-WinLin-$BUILD_ENV.zip"
 LIB="PrusaLibrary"
 SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
 
@@ -233,6 +239,7 @@ echo "Ardunio IDE :" $ARDUINO_ENV
 echo "Build env   :" $BUILD_ENV
 echo "Board       :" $BOARD
 echo "Package name:" $BOARD_PACKAGE_NAME
+echo "Board v.    :" $BOARD_VERSION
 echo "Specific Lib:" $LIB
 echo ""
 
@@ -321,7 +328,7 @@ if [ ! -e ../PF-build-env-$BUILD_ENV/Preferences-$ARDUINO_ENV-$BOARD_VERSION-$TA
 	echo "update.check"
 	sed -i 's/update.check = true/update.check = false/g' ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor/lib/preferences.txt
 	echo "board"
-	sed -i 's/board = uno/board = $BOARD/g' ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor/lib/preferences.txt
+	sed -i "s/board = uno/board = $BOARD/g" ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor/lib/preferences.txt
 	echo "editor.linenumbers"
 	sed -i 's/editor.linenumbers = false/editor.linenumbers = true/g' ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor/lib/preferences.txt
 	echo "boardsmanager.additional.urls"
@@ -364,7 +371,7 @@ if [[ ! -d "../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$P
 fi	
 
 # Download and extract Prusa Firmware specific library files
-if [ ! -f "PF-build-env-$BUILD_ENV.zip" ]; then
+if [ ! -f "PF-build-env-WinLin-$BUILD_ENV.zip" ]; then
 	echo "$(tput setaf 6)Downloading Prusa Firmware build environment...$(tput setaf 2)"
 	sleep 2
 	wget $PF_BUILD_FILE_URL || exit 11
@@ -373,7 +380,7 @@ fi
 if [ ! -e "../PF-build-env-$BUILD_ENV/PF-build-env-$BUILD_ENV-$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor.txt" ]; then
 	echo "$(tput setaf 6)Unzipping Prusa Firmware build environment...$(tput setaf 2)"
 	sleep 2
-	unzip -o PF-build-env-$BUILD_ENV.zip -d ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor || exit 12
+	unzip -o PF-build-env-WinLin-$BUILD_ENV.zip -d ../PF-build-env-$BUILD_ENV/$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor || exit 12
 	echo "# PF-build-env-$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor-$BUILD_ENV" >> ../PF-build-env-$BUILD_ENV/PF-build-env-$BUILD_ENV-$ARDUINO_ENV-$BOARD_VERSION-$TARGET_OS-$Processor.txt
 	echo "$(tput sgr0)"
 fi

+ 2 - 2
README.md

@@ -21,7 +21,7 @@
    - For MK3 --> skip to step 3. 
    - If you have a different printer model, follow step [2.b](#2b) from Windows build
    
-3. Run `sudo ./build.sh`
+3. Run `./build.sh`
    - Output hex file is at `"PrusaFirmware/lang/firmware.hex"` . In the same folder you can hex files for other languages as well.
 
 4. Connect your printer and flash with PrusaSlicer ( Configuration --> Flash printer firmware ) or Slic3r PE.
@@ -46,7 +46,7 @@ _Note: Multi language build is not supported._
 
 * Open Arduino and navigate to File -> Preferences -> Settings
 * To the text field `"Additional Boards Manager URLSs"` add `https://raw.githubusercontent.com/prusa3d/Arduino_Boards/master/IDE_Board_Manager/package_prusa3d_index.json`
-* Open Board manager (`Tools->Board->Board manager`), and install `Prusa Research AVR MK3 RAMBo EINSy board`
+* Open Board manager (`Tools->Board->Board manager`), and install `Prusa Research AVR Boards by Prusa Research`
 
 **c.** Modify compiler flags in `platform.txt` file
      

+ 3 - 2
build.sh

@@ -1,5 +1,5 @@
 #!/bin/bash 
-BUILD_ENV="1.0.6"
+BUILD_ENV="1.0.6.1"
 SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
 
 if [ ! -d "build-env" ]; then
@@ -8,7 +8,8 @@ fi
 cd build-env || exit 2
 
 if [ ! -f "PF-build-env-Linux64-$BUILD_ENV.zip" ]; then
-    wget https://github.com/mkbel/PF-build-env/releases/download/$BUILD_ENV/PF-build-env-Linux64-$BUILD_ENV.zip || exit 3
+    #wget https://github.com/3d-gussner/PF-build-env-1/releases/download/$BUILD_ENV-Linux64/PF-build-env-Linux64-$BUILD_ENV.zip || exit 3
+	wget https://github.com/prusa3d/PF-build-env/releases/download/$BUILD_ENV-Linux64/PF-build-env-Linux64-$BUILD_ENV.zip || exit 3
 fi
 
 if [ ! -d "../../PF-build-env-$BUILD_ENV" ]; then

+ 2 - 2
lang/lang_en_fr.txt

@@ -20,7 +20,7 @@
 
 #MSG_CRASH_DET_STEALTH_FORCE_OFF c=20 r=4
 "WARNING:\x0aCrash detection\x0adisabled in\x0aStealth mode"
-"ATTENTION:\x0aDetection de crash\x0adesactivee en\x0amode feutre"
+"ATTENTION:\x0aDetection de crash\x0adesactivee en\x0amode furtif"
 
 #
 ">Cancel"
@@ -550,7 +550,7 @@
 
 #MSG_SILENT
 "Silent"
-"Feutre"
+"Furtif"
 
 #
 "MMU needs user attention."