From 986b508ff73839ff062e0cade279dbd23137cefa Mon Sep 17 00:00:00 2001
From: jbrazio <jbrazio@gmail.com>
Date: Tue, 8 Mar 2016 18:11:02 +0000
Subject: [PATCH 1/3] Print timer now stops when it sees the last extruder
 temperature being shutdown

---
 Marlin/Marlin_main.cpp                        | 37 +++++++++++++++++--
 Marlin/dogm_lcd_implementation.h              |  3 +-
 Marlin/temperature.cpp                        |  3 ++
 .../ultralcd_implementation_hitachi_HD44780.h | 11 +++---
 4 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index fb1d3b3b22c..dd9c0b53b00 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -3833,6 +3833,19 @@ inline void gcode_M104() {
         setTargetHotend1(temp == 0.0 ? 0.0 : temp + duplicate_extruder_temp_offset);
     #endif
   }
+
+  // Detect if a print job has finished.
+  // When the target temperature for all extruders is zero then we must have
+  // finished printing.
+  if( print_job_start_ms != 0 ) {
+    bool all_extruders_cooling = true;
+    for (int i = 0; i < EXTRUDERS; i++) if( degTargetHotend(i) > 0 ) {
+      all_extruders_cooling = false;
+      break;
+    }
+
+    if( all_extruders_cooling ) print_job_stop_ms = millis();
+  }
 }
 
 #if HAS_TEMP_0 || HAS_TEMP_BED || ENABLED(HEATER_0_USES_MAX6675)
@@ -3944,16 +3957,15 @@ inline void gcode_M105() {
  *       Rxxx Wait for extruder(s) to reach temperature. Waits when heating and cooling.
  */
 inline void gcode_M109() {
+  float temp;
   bool no_wait_for_cooling = true;
 
   if (setTargetedHotend(109)) return;
   if (marlin_debug_flags & DEBUG_DRYRUN) return;
 
-  LCD_MESSAGEPGM(MSG_HEATING);
-
   no_wait_for_cooling = code_seen('S');
   if (no_wait_for_cooling || code_seen('R')) {
-    float temp = code_value();
+    temp = code_value();
     setTargetHotend(temp, target_extruder);
     #if ENABLED(DUAL_X_CARRIAGE)
       if (dual_x_carriage_mode == DXC_DUPLICATION_MODE && target_extruder == 0)
@@ -3961,6 +3973,9 @@ inline void gcode_M109() {
     #endif
   }
 
+  // Only makes sense to show the heating message if we're in fact heating.
+  if( temp > 0 ) LCD_MESSAGEPGM(MSG_HEATING);
+
   #if ENABLED(AUTOTEMP)
     autotemp_enabled = code_seen('F');
     if (autotemp_enabled) autotemp_factor = code_value();
@@ -3968,6 +3983,22 @@ inline void gcode_M109() {
     if (code_seen('B')) autotemp_max = code_value();
   #endif
 
+  // Detect if a print job has finished.
+  // When the target temperature for all extruders is zero then we must have
+  // finished printing.
+  if( print_job_start_ms != 0 ) {
+    bool all_extruders_cooling = true;
+    for (int i = 0; i < EXTRUDERS; i++) if( degTargetHotend(i) > 0 ) {
+      all_extruders_cooling = false;
+      break;
+    }
+
+    if( all_extruders_cooling ) {
+      print_job_stop_ms = millis();
+      LCD_MESSAGEPGM(WELCOME_MSG);
+    }
+  }
+
   // Exit if the temperature is above target and not waiting for cooling
   if (no_wait_for_cooling && !isHeatingHotend(target_extruder)) return;
 
diff --git a/Marlin/dogm_lcd_implementation.h b/Marlin/dogm_lcd_implementation.h
index 6e257041433..12a7d998041 100644
--- a/Marlin/dogm_lcd_implementation.h
+++ b/Marlin/dogm_lcd_implementation.h
@@ -306,7 +306,8 @@ static void lcd_implementation_status_screen() {
 
     u8g.setPrintPos(80,48);
     if (print_job_start_ms != 0) {
-      uint16_t time = (millis() - print_job_start_ms) / 60000;
+      uint16_t time = ((print_job_stop_ms > print_job_start_ms)
+                       ? print_job_stop_ms : millis()) / 60000 - print_job_start_ms / 60000;
       lcd_print(itostr2(time/60));
       lcd_print(':');
       lcd_print(itostr2(time%60));
diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp
index 9beffc4e7d7..18410f2fcea 100644
--- a/Marlin/temperature.cpp
+++ b/Marlin/temperature.cpp
@@ -1111,6 +1111,9 @@ void disable_all_heaters() {
   for (int i = 0; i < EXTRUDERS; i++) setTargetHotend(0, i);
   setTargetBed(0);
 
+  // If all heaters go down then for sure our print job has stopped
+  if( print_job_start_ms != 0 ) print_job_stop_ms = millis();
+
   #define DISABLE_HEATER(NR) { \
     target_temperature[NR] = 0; \
     soft_pwm[NR] = 0; \
diff --git a/Marlin/ultralcd_implementation_hitachi_HD44780.h b/Marlin/ultralcd_implementation_hitachi_HD44780.h
index f0b3c9a8775..5357081aede 100644
--- a/Marlin/ultralcd_implementation_hitachi_HD44780.h
+++ b/Marlin/ultralcd_implementation_hitachi_HD44780.h
@@ -137,7 +137,7 @@ extern volatile uint8_t buttons;  //an extended version of the last checked butt
   #define LCD_I2C_PIN_D5  5
   #define LCD_I2C_PIN_D6  6
   #define LCD_I2C_PIN_D7  7
-  
+
   #include <Wire.h>
   #include <LCD.h>
   #include <LiquidCrystal_I2C.h>
@@ -632,7 +632,7 @@ static void lcd_implementation_status_screen() {
         else {
           if (!axis_homed[X_AXIS])
             lcd_printPGM(PSTR("?"));
-          else 
+          else
             #if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
               if (!axis_known_position[X_AXIS])
                 lcd_printPGM(PSTR(" "));
@@ -649,7 +649,7 @@ static void lcd_implementation_status_screen() {
         else {
           if (!axis_homed[Y_AXIS])
             lcd_printPGM(PSTR("?"));
-          else 
+          else
             #if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
               if (!axis_known_position[Y_AXIS])
                 lcd_printPGM(PSTR(" "));
@@ -669,7 +669,7 @@ static void lcd_implementation_status_screen() {
     else {
       if (!axis_homed[Z_AXIS])
         lcd_printPGM(PSTR("?"));
-      else 
+      else
         #if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
           if (!axis_known_position[Z_AXIS])
             lcd_printPGM(PSTR(" "));
@@ -707,7 +707,8 @@ static void lcd_implementation_status_screen() {
     lcd.setCursor(LCD_WIDTH - 6, 2);
     lcd.print(LCD_STR_CLOCK[0]);
     if (print_job_start_ms != 0) {
-      uint16_t time = millis() / 60000 - print_job_start_ms / 60000;
+      uint16_t time = ((print_job_stop_ms > print_job_start_ms)
+                       ? print_job_stop_ms : millis()) / 60000 - print_job_start_ms / 60000;
       lcd.print(itostr2(time / 60));
       lcd.print(':');
       lcd.print(itostr2(time % 60));

From 793cd0ae3b13bcc31f116ead6da9dbe527128817 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Br=C3=A1zio?= <jbrazio@gmail.com>
Date: Thu, 10 Mar 2016 11:52:43 +0000
Subject: [PATCH 2/3] Clean up the code a bit

---
 Marlin/Marlin_main.cpp                           | 11 +++++------
 Marlin/dogm_lcd_implementation.h                 |  4 ++--
 Marlin/ultralcd_implementation_hitachi_HD44780.h |  4 ++--
 3 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index dd9c0b53b00..5fa631f1a33 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -3837,7 +3837,7 @@ inline void gcode_M104() {
   // Detect if a print job has finished.
   // When the target temperature for all extruders is zero then we must have
   // finished printing.
-  if( print_job_start_ms != 0 ) {
+  if (print_job_start_ms) {
     bool all_extruders_cooling = true;
     for (int i = 0; i < EXTRUDERS; i++) if( degTargetHotend(i) > 0 ) {
       all_extruders_cooling = false;
@@ -3957,7 +3957,6 @@ inline void gcode_M105() {
  *       Rxxx Wait for extruder(s) to reach temperature. Waits when heating and cooling.
  */
 inline void gcode_M109() {
-  float temp;
   bool no_wait_for_cooling = true;
 
   if (setTargetedHotend(109)) return;
@@ -3965,16 +3964,16 @@ inline void gcode_M109() {
 
   no_wait_for_cooling = code_seen('S');
   if (no_wait_for_cooling || code_seen('R')) {
-    temp = code_value();
+    float temp = code_value();
     setTargetHotend(temp, target_extruder);
     #if ENABLED(DUAL_X_CARRIAGE)
       if (dual_x_carriage_mode == DXC_DUPLICATION_MODE && target_extruder == 0)
         setTargetHotend1(temp == 0.0 ? 0.0 : temp + duplicate_extruder_temp_offset);
     #endif
-  }
 
-  // Only makes sense to show the heating message if we're in fact heating.
-  if( temp > 0 ) LCD_MESSAGEPGM(MSG_HEATING);
+    // Only makes sense to show the heating message if we're in fact heating.
+    if (temp > 0) LCD_MESSAGEPGM(MSG_HEATING);
+  }
 
   #if ENABLED(AUTOTEMP)
     autotemp_enabled = code_seen('F');
diff --git a/Marlin/dogm_lcd_implementation.h b/Marlin/dogm_lcd_implementation.h
index 12a7d998041..e7ebea71d1d 100644
--- a/Marlin/dogm_lcd_implementation.h
+++ b/Marlin/dogm_lcd_implementation.h
@@ -306,8 +306,8 @@ static void lcd_implementation_status_screen() {
 
     u8g.setPrintPos(80,48);
     if (print_job_start_ms != 0) {
-      uint16_t time = ((print_job_stop_ms > print_job_start_ms)
-                       ? print_job_stop_ms : millis()) / 60000 - print_job_start_ms / 60000;
+      uint16_t time = (((print_job_stop_ms > print_job_start_ms)
+                       ? print_job_stop_ms : millis()) - print_job_start_ms) / 60000;
       lcd_print(itostr2(time/60));
       lcd_print(':');
       lcd_print(itostr2(time%60));
diff --git a/Marlin/ultralcd_implementation_hitachi_HD44780.h b/Marlin/ultralcd_implementation_hitachi_HD44780.h
index 5357081aede..92114db4916 100644
--- a/Marlin/ultralcd_implementation_hitachi_HD44780.h
+++ b/Marlin/ultralcd_implementation_hitachi_HD44780.h
@@ -707,8 +707,8 @@ static void lcd_implementation_status_screen() {
     lcd.setCursor(LCD_WIDTH - 6, 2);
     lcd.print(LCD_STR_CLOCK[0]);
     if (print_job_start_ms != 0) {
-      uint16_t time = ((print_job_stop_ms > print_job_start_ms)
-                       ? print_job_stop_ms : millis()) / 60000 - print_job_start_ms / 60000;
+      uint16_t time = (((print_job_stop_ms > print_job_start_ms)
+                       ? print_job_stop_ms : millis()) - print_job_start_ms) / 60000;
       lcd.print(itostr2(time / 60));
       lcd.print(':');
       lcd.print(itostr2(time % 60));

From a645860431b1a05e0e7502b0a65d1dfc8b0b2a03 Mon Sep 17 00:00:00 2001
From: jbrazio <jbrazio@gmail.com>
Date: Sat, 12 Mar 2016 07:16:39 +0000
Subject: [PATCH 3/3] All print timer related activity now uses
 print_job_start(), print_job_timer() or print_job_stop()

---
 Marlin/Marlin.h        |  5 +++
 Marlin/Marlin_main.cpp | 99 ++++++++++++++++++++++++++----------------
 Marlin/temperature.cpp |  2 +-
 3 files changed, 67 insertions(+), 39 deletions(-)

diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h
index 42f8af2c6e7..28d2dcc98cc 100644
--- a/Marlin/Marlin.h
+++ b/Marlin/Marlin.h
@@ -351,4 +351,9 @@ extern uint8_t active_extruder;
 
 extern void calculate_volumetric_multipliers();
 
+// Print job timer related functions
+millis_t print_job_timer();
+bool print_job_start(millis_t t = 0);
+bool print_job_stop(bool force = false);
+
 #endif //MARLIN_H
diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index 5fa631f1a33..ebb5dc2bf97 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -943,9 +943,9 @@ void get_command() {
       ) {
         if (card.eof()) {
           SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED);
-          print_job_stop_ms = millis();
+          print_job_stop(true);
           char time[30];
-          millis_t t = (print_job_stop_ms - print_job_start_ms) / 1000;
+          millis_t t = print_job_timer();
           int hours = t / 60 / 60, minutes = (t / 60) % 60;
           sprintf_P(time, PSTR("%i " MSG_END_HOUR " %i " MSG_END_MINUTE), hours, minutes);
           SERIAL_ECHO_START;
@@ -3411,7 +3411,7 @@ inline void gcode_M17() {
    */
   inline void gcode_M24() {
     card.startFileprint();
-    print_job_start_ms = millis();
+    print_job_start();
   }
 
   /**
@@ -3467,8 +3467,7 @@ inline void gcode_M17() {
  * M31: Get the time since the start of SD Print (or last M109)
  */
 inline void gcode_M31() {
-  print_job_stop_ms = millis();
-  millis_t t = (print_job_stop_ms - print_job_start_ms) / 1000;
+  millis_t t = print_job_timer();
   int min = t / 60, sec = t % 60;
   char time[30];
   sprintf_P(time, PSTR("%i min, %i sec"), min, sec);
@@ -3502,8 +3501,9 @@ inline void gcode_M31() {
         card.setIndex(code_value_short());
 
       card.startFileprint();
-      if (!call_procedure)
-        print_job_start_ms = millis(); //procedure calls count as normal print time.
+
+      // Procedure calls count as normal print time.
+      if (!call_procedure) print_job_start();
     }
   }
 
@@ -3834,18 +3834,7 @@ inline void gcode_M104() {
     #endif
   }
 
-  // Detect if a print job has finished.
-  // When the target temperature for all extruders is zero then we must have
-  // finished printing.
-  if (print_job_start_ms) {
-    bool all_extruders_cooling = true;
-    for (int i = 0; i < EXTRUDERS; i++) if( degTargetHotend(i) > 0 ) {
-      all_extruders_cooling = false;
-      break;
-    }
-
-    if( all_extruders_cooling ) print_job_stop_ms = millis();
-  }
+  print_job_stop();
 }
 
 #if HAS_TEMP_0 || HAS_TEMP_BED || ENABLED(HEATER_0_USES_MAX6675)
@@ -3959,6 +3948,9 @@ inline void gcode_M105() {
 inline void gcode_M109() {
   bool no_wait_for_cooling = true;
 
+  // Start hook must happen before setTargetHotend()
+  print_job_start();
+
   if (setTargetedHotend(109)) return;
   if (marlin_debug_flags & DEBUG_DRYRUN) return;
 
@@ -3971,10 +3963,11 @@ inline void gcode_M109() {
         setTargetHotend1(temp == 0.0 ? 0.0 : temp + duplicate_extruder_temp_offset);
     #endif
 
-    // Only makes sense to show the heating message if we're in fact heating.
-    if (temp > 0) LCD_MESSAGEPGM(MSG_HEATING);
+    if (temp > degHotend(target_extruder)) LCD_MESSAGEPGM(MSG_HEATING);
   }
 
+  if (print_job_stop()) LCD_MESSAGEPGM(WELCOME_MSG);
+
   #if ENABLED(AUTOTEMP)
     autotemp_enabled = code_seen('F');
     if (autotemp_enabled) autotemp_factor = code_value();
@@ -3982,22 +3975,6 @@ inline void gcode_M109() {
     if (code_seen('B')) autotemp_max = code_value();
   #endif
 
-  // Detect if a print job has finished.
-  // When the target temperature for all extruders is zero then we must have
-  // finished printing.
-  if( print_job_start_ms != 0 ) {
-    bool all_extruders_cooling = true;
-    for (int i = 0; i < EXTRUDERS; i++) if( degTargetHotend(i) > 0 ) {
-      all_extruders_cooling = false;
-      break;
-    }
-
-    if( all_extruders_cooling ) {
-      print_job_stop_ms = millis();
-      LCD_MESSAGEPGM(WELCOME_MSG);
-    }
-  }
-
   // Exit if the temperature is above target and not waiting for cooling
   if (no_wait_for_cooling && !isHeatingHotend(target_extruder)) return;
 
@@ -4046,7 +4023,6 @@ inline void gcode_M109() {
   } // while(!cancel_heatup && TEMP_CONDITIONS)
 
   LCD_MESSAGEPGM(MSG_HEATING_COMPLETE);
-  print_job_start_ms = previous_cmd_ms;
 }
 
 #if HAS_TEMP_BED
@@ -7336,3 +7312,50 @@ void calculate_volumetric_multipliers() {
   for (int i = 0; i < EXTRUDERS; i++)
     volumetric_multiplier[i] = calculate_volumetric_multiplier(filament_size[i]);
 }
+
+/**
+ * Start the print job timer
+ *
+ * The print job is only started if all extruders have their target temp at zero
+ * otherwise the print job timew would be reset everytime a M109 is received.
+ *
+ * @param t start timer timestamp
+ *
+ * @return true if the timer was started at function call
+ */
+bool print_job_start(millis_t t /* = 0 */) {
+  for (int i = 0; i < EXTRUDERS; i++) if (degTargetHotend(i) > 0) return false;
+  print_job_start_ms = (t) ? t : millis();
+  print_job_stop_ms = 0;
+  return true;
+}
+
+/**
+ * Output the print job timer in seconds
+ *
+ * @return the number of seconds
+ */
+millis_t print_job_timer() {
+  if (!print_job_start_ms) return 0;
+  return (((print_job_stop_ms > print_job_start_ms)
+    ? print_job_stop_ms : millis()) - print_job_start_ms) / 1000;
+}
+
+/**
+ * Check if the running print job has finished and stop the timer
+ *
+ * When the target temperature for all extruders is zero then we assume that the
+ * print job has finished printing. There are some special conditions under which
+ * this assumption may not be valid: If during a print job for some reason the
+ * user decides to bring a nozzle temp down and only then heat the other afterwards.
+ *
+ * @param force stops the timer ignoring all pre-checks
+ *
+ * @return boolean true if the print job has finished printing
+ */
+bool print_job_stop(bool force /* = false */) {
+  if (!print_job_start_ms) return false;
+  if (!force) for (int i = 0; i < EXTRUDERS; i++) if (degTargetHotend(i) > 0) return false;
+  print_job_stop_ms = millis();
+  return true;
+}
diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp
index 18410f2fcea..74035cc58bf 100644
--- a/Marlin/temperature.cpp
+++ b/Marlin/temperature.cpp
@@ -1112,7 +1112,7 @@ void disable_all_heaters() {
   setTargetBed(0);
 
   // If all heaters go down then for sure our print job has stopped
-  if( print_job_start_ms != 0 ) print_job_stop_ms = millis();
+  print_job_stop(true);
 
   #define DISABLE_HEATER(NR) { \
     target_temperature[NR] = 0; \