Browse Source

Optimize Power-Loss Recovery (#12440)

Scott Lahteine 6 years ago
parent
commit
d97e31db4c
No account linked to committer's email address

+ 5
- 5
Marlin/src/Marlin.cpp View File

@@ -924,11 +924,11 @@ void setup() {
924 924
   #endif
925 925
 
926 926
   #if DO_SWITCH_EXTRUDER
927
-    move_extruder_servo(0);  // Initialize extruder servo
927
+    move_extruder_servo(0);   // Initialize extruder servo
928 928
   #endif
929 929
 
930 930
   #if ENABLED(SWITCHING_NOZZLE)
931
-    move_nozzle_servo(0);  // Initialize nozzle servo
931
+    move_nozzle_servo(0);     // Initialize nozzle servo
932 932
   #endif
933 933
 
934 934
   #if ENABLED(PARKING_EXTRUDER)
@@ -936,11 +936,11 @@ void setup() {
936 936
   #endif
937 937
 
938 938
   #if ENABLED(POWER_LOSS_RECOVERY)
939
-    check_print_job_recovery();
939
+    recovery.check();
940 940
   #endif
941 941
 
942
-  #if ENABLED(USE_WATCHDOG) // Reinit watchdog after HAL_get_reset_source call
943
-    watchdog_init();
942
+  #if ENABLED(USE_WATCHDOG)
943
+    watchdog_init();          // Reinit watchdog after HAL_get_reset_source call
944 944
   #endif
945 945
 
946 946
   #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)

+ 304
- 203
Marlin/src/feature/power_loss_recovery.cpp View File

@@ -29,247 +29,145 @@
29 29
 #if ENABLED(POWER_LOSS_RECOVERY)
30 30
 
31 31
 #include "power_loss_recovery.h"
32
+#include "../core/macros.h"
32 33
 
34
+bool PrintJobRecovery::enabled; // Initialized by settings.load()
35
+
36
+SdFile PrintJobRecovery::file;
37
+job_recovery_info_t PrintJobRecovery::info;
38
+
39
+#include "../sd/cardreader.h"
33 40
 #include "../lcd/ultralcd.h"
34 41
 #include "../gcode/queue.h"
42
+#include "../gcode/gcode.h"
35 43
 #include "../module/motion.h"
36 44
 #include "../module/planner.h"
37 45
 #include "../module/printcounter.h"
38 46
 #include "../module/temperature.h"
39
-#include "../sd/cardreader.h"
40 47
 #include "../core/serial.h"
41 48
 
42 49
 #if ENABLED(FWRETRACT)
43 50
   #include "fwretract.h"
44 51
 #endif
45 52
 
46
-// Recovery data
47
-job_recovery_info_t job_recovery_info;
48
-JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE;
49
-uint8_t job_recovery_commands_count; //=0
50
-char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
51
-
52
-extern uint8_t commands_in_queue, cmd_queue_index_r;
53
-
54
-#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
55
-  void debug_print_job_recovery(const bool recovery) {
56
-    SERIAL_PROTOCOLLNPGM("---- Job Recovery Info ----");
57
-    SERIAL_PROTOCOLPAIR("valid_head:", int(job_recovery_info.valid_head));
58
-    SERIAL_PROTOCOLLNPAIR(" valid_foot:", int(job_recovery_info.valid_foot));
59
-    if (job_recovery_info.valid_head) {
60
-      if (job_recovery_info.valid_head == job_recovery_info.valid_foot) {
61
-        SERIAL_PROTOCOLPGM("current_position: ");
62
-        LOOP_XYZE(i) {
63
-          SERIAL_PROTOCOL(job_recovery_info.current_position[i]);
64
-          if (i < E_AXIS) SERIAL_CHAR(',');
65
-        }
66
-        SERIAL_EOL();
67
-        SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate);
53
+PrintJobRecovery recovery;
68 54
 
69
-        #if HOTENDS > 1
70
-          SERIAL_PROTOCOLLNPAIR("active_hotend: ", int(job_recovery_info.active_hotend));
71
-        #endif
72
-
73
-        SERIAL_PROTOCOLPGM("target_temperature: ");
74
-        HOTEND_LOOP() {
75
-          SERIAL_PROTOCOL(job_recovery_info.target_temperature[e]);
76
-          if (e < HOTENDS - 1) SERIAL_CHAR(',');
77
-        }
78
-        SERIAL_EOL();
79
-
80
-        #if HAS_HEATED_BED
81
-          SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed);
82
-        #endif
55
+/**
56
+ * Clear the recovery info
57
+ */
58
+void PrintJobRecovery::init() { memset(&info, 0, sizeof(info)); }
83 59
 
84
-        #if FAN_COUNT
85
-          SERIAL_PROTOCOLPGM("fan_speed: ");
86
-          for (int8_t i = 0; i < FAN_COUNT; i++) {
87
-            SERIAL_PROTOCOL(job_recovery_info.fan_speed[i]);
88
-            if (i < FAN_COUNT - 1) SERIAL_CHAR(',');
89
-          }
90
-          SERIAL_EOL();
91
-        #endif
60
+/**
61
+ * Enable or disable then call changed()
62
+ */
63
+void PrintJobRecovery::enable(const bool onoff) {
64
+  enabled = onoff;
65
+  changed();
66
+}
92 67
 
93
-        #if HAS_LEVELING
94
-          SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling));
95
-          SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade));
96
-        #endif
97
-        #if ENABLED(FWRETRACT)
98
-          SERIAL_PROTOCOLPGM("retract: ");
99
-          for (int8_t e = 0; e < EXTRUDERS; e++) {
100
-            SERIAL_PROTOCOL(job_recovery_info.retract[e]);
101
-            if (e < EXTRUDERS - 1) SERIAL_CHAR(',');
102
-          }
103
-          SERIAL_EOL();
104
-          SERIAL_PROTOCOLLNPAIR("retract_hop: ", job_recovery_info.retract_hop);
105
-        #endif
106
-        SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", int(job_recovery_info.cmd_queue_index_r));
107
-        SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", int(job_recovery_info.commands_in_queue));
108
-        if (recovery)
109
-          for (uint8_t i = 0; i < job_recovery_commands_count; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_commands[i]);
110
-        else
111
-          for (uint8_t i = 0; i < job_recovery_info.commands_in_queue; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_info.command_queue[i]);
112
-        SERIAL_PROTOCOLLNPAIR("sd_filename: ", job_recovery_info.sd_filename);
113
-        SERIAL_PROTOCOLLNPAIR("sdpos: ", job_recovery_info.sdpos);
114
-        SERIAL_PROTOCOLLNPAIR("print_job_elapsed: ", job_recovery_info.print_job_elapsed);
115
-      }
116
-      else
117
-        SERIAL_PROTOCOLLNPGM("INVALID DATA");
118
-    }
119
-    SERIAL_PROTOCOLLNPGM("---------------------------");
120
-  }
121
-#endif // DEBUG_POWER_LOSS_RECOVERY
68
+/**
69
+ * The enabled state was changed:
70
+ *  - Enabled: Purge the job recovery file
71
+ *  - Disabled: Write the job recovery file
72
+ */
73
+void PrintJobRecovery::changed() {
74
+  if (!enabled)
75
+    purge();
76
+  else if (IS_SD_PRINTING())
77
+    save(true);
78
+}
122 79
 
123 80
 /**
124 81
  * Check for Print Job Recovery during setup()
125 82
  *
126
- * If a saved state exists, populate job_recovery_commands with
127
- * commands to restore the machine state and continue the file.
83
+ * If a saved state exists send 'M1000 S' to initiate job recovery.
128 84
  */
129
-void check_print_job_recovery() {
130
-  memset(&job_recovery_info, 0, sizeof(job_recovery_info));
131
-  ZERO(job_recovery_commands);
132
-
133
-  if (!card.cardOK) card.initsd();
134
-
135
-  if (card.cardOK) {
136
-
137
-    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
138
-      SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", int(sizeof(job_recovery_info)));
139
-    #endif
140
-
141
-    if (card.jobRecoverFileExists()) {
142
-      card.openJobRecoveryFile(true);
143
-      card.loadJobRecoveryInfo();
144
-      card.closeJobRecoveryFile();
145
-      //card.removeJobRecoveryFile();
146
-
147
-      if (job_recovery_info.valid_head && job_recovery_info.valid_head == job_recovery_info.valid_foot) {
148
-
149
-        uint8_t ind = 0;
150
-
151
-        #if HAS_LEVELING
152
-          strcpy_P(job_recovery_commands[ind++], PSTR("M420 S0 Z0"));               // Leveling off before G92 or G28
153
-        #endif
154
-
155
-        strcpy_P(job_recovery_commands[ind++], PSTR("G92.0 Z0"));                   // Ensure Z is equal to 0
156
-        strcpy_P(job_recovery_commands[ind++], PSTR("G1 Z2"));                      // Raise Z by 2mm (we hope!)
157
-        strcpy_P(job_recovery_commands[ind++], PSTR("G28 R0"
158
-          #if ENABLED(MARLIN_DEV_MODE)
159
-            " S"
160
-          #elif !IS_KINEMATIC
161
-            " X Y"                                                                  // Home X and Y for Cartesian
162
-          #endif
163
-        ));
164
-
165
-        char str_1[16], str_2[16];
166
-
167
-        #if HAS_LEVELING
168
-          if (job_recovery_info.fade || job_recovery_info.leveling) {
169
-            // Restore leveling state before G92 sets Z
170
-            // This ensures the steppers correspond to the native Z
171
-            dtostrf(job_recovery_info.fade, 1, 1, str_1);
172
-            sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1);
173
-          }
174
-        #endif
175
-
176
-        #if ENABLED(FWRETRACT)
177
-          for (uint8_t e = 0; e < EXTRUDERS; e++) {
178
-            if (job_recovery_info.retract[e] != 0.0)
179
-              fwretract.current_retract[e] = job_recovery_info.retract[e];
180
-              fwretract.retracted[e] = true;
181
-          }
182
-          fwretract.current_hop = job_recovery_info.retract_hop;
183
-        #endif
184
-
185
-        dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1);
186
-        dtostrf(job_recovery_info.current_position[E_AXIS]
187
-          #if ENABLED(SAVE_EACH_CMD_MODE)
188
-            - 5
189
-          #endif
190
-          , 1, 3, str_2
191
-        );
192
-        sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E
193
-
194
-        uint8_t r = job_recovery_info.cmd_queue_index_r, c = job_recovery_info.commands_in_queue;
195
-        while (c--) {
196
-          strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]);
197
-          r = (r + 1) % BUFSIZE;
198
-        }
199
-
200
-        if (job_recovery_info.sd_filename[0] == '/') job_recovery_info.sd_filename[0] = ' ';
201
-        sprintf_P(job_recovery_commands[ind++], PSTR("M23 %s"), job_recovery_info.sd_filename);
202
-        sprintf_P(job_recovery_commands[ind++], PSTR("M24 S%ld T%ld"), job_recovery_info.sdpos, job_recovery_info.print_job_elapsed);
85
+void PrintJobRecovery::check() {
86
+  if (enabled) {
87
+    if (!card.cardOK) card.initsd();
88
+    if (card.cardOK) {
89
+      load();
90
+      if (!valid()) return purge();
91
+      enqueue_and_echo_commands_P(PSTR("M1000 S"));
92
+    }
93
+  }
94
+}
203 95
 
204
-        job_recovery_commands_count = ind;
96
+/**
97
+ * Delete the recovery file and clear the recovery data
98
+ */
99
+void PrintJobRecovery::purge() {
100
+  init();
101
+  card.removeJobRecoveryFile();
102
+}
205 103
 
206
-        #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
207
-          debug_print_job_recovery(true);
208
-        #endif
209
-      }
210
-      else {
211
-        if (job_recovery_info.valid_head != job_recovery_info.valid_foot)
212
-          LCD_ALERTMESSAGEPGM("INVALID DATA");
213
-        memset(&job_recovery_info, 0, sizeof(job_recovery_info));
214
-      }
215
-    }
104
+/**
105
+ * Load the recovery data, if it exists
106
+ */
107
+void PrintJobRecovery::load() {
108
+  if (exists()) {
109
+    open(true);
110
+    (void)file.read(&info, sizeof(info));
111
+    close();
216 112
   }
113
+  #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
114
+    debug(PSTR("Load"));
115
+  #endif
217 116
 }
218 117
 
219 118
 /**
220 119
  * Save the current machine state to the power-loss recovery file
221 120
  */
222
-void save_job_recovery_info() {
121
+void PrintJobRecovery::save(const bool force/*=false*/) {
122
+
223 123
   #if SAVE_INFO_INTERVAL_MS > 0
224
-    static millis_t next_save_ms; // = 0;  // Init on reset
124
+    static millis_t next_save_ms; // = 0
225 125
     millis_t ms = millis();
226 126
   #endif
227
-  if (
228
-    // Save on every command
229
-    #if ENABLED(SAVE_EACH_CMD_MODE)
230
-      true
231
-    #else
232
-      // Save if power loss pin is triggered
233
-      #if PIN_EXISTS(POWER_LOSS)
234
-        READ(POWER_LOSS_PIN) == POWER_LOSS_STATE ||
127
+
128
+  if (force
129
+    #if DISABLED(SAVE_EACH_CMD_MODE)      // Always save state when enabled
130
+      #if PIN_EXISTS(POWER_LOSS)          // Save if power loss pin is triggered
131
+        || READ(POWER_LOSS_PIN) == POWER_LOSS_STATE
235 132
       #endif
236
-      // Save if interval is elapsed
237
-      #if SAVE_INFO_INTERVAL_MS > 0
238
-        ELAPSED(ms, next_save_ms) ||
133
+      #if SAVE_INFO_INTERVAL_MS > 0       // Save if interval is elapsed
134
+        || ELAPSED(ms, next_save_ms)
239 135
       #endif
240
-      // Save on every new Z height
241
-      (current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS])
136
+      // Save every time Z is higher than the last call
137
+      || current_position[Z_AXIS] > info.current_position[Z_AXIS]
242 138
     #endif
243 139
   ) {
140
+
244 141
     #if SAVE_INFO_INTERVAL_MS > 0
245 142
       next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
246 143
     #endif
247 144
 
248
-    // Head and foot will match if valid data was saved
249
-    if (!++job_recovery_info.valid_head) ++job_recovery_info.valid_head; // non-zero in sequence
250
-    job_recovery_info.valid_foot = job_recovery_info.valid_head;
145
+    // Set Head and Foot to matching non-zero values
146
+    if (!++info.valid_head) ++info.valid_head; // non-zero in sequence
147
+    //if (!IS_SD_PRINTING()) info.valid_head = 0;
148
+    info.valid_foot = info.valid_head;
251 149
 
252 150
     // Machine state
253
-    COPY(job_recovery_info.current_position, current_position);
254
-    job_recovery_info.feedrate = feedrate_mm_s;
151
+    COPY(info.current_position, current_position);
152
+    info.feedrate = uint16_t(feedrate_mm_s * 60.0f);
255 153
 
256 154
     #if HOTENDS > 1
257
-      job_recovery_info.active_hotend = active_extruder;
155
+      info.active_hotend = active_extruder;
258 156
     #endif
259 157
 
260
-    COPY(job_recovery_info.target_temperature, thermalManager.target_temperature);
158
+    COPY(info.target_temperature, thermalManager.target_temperature);
261 159
 
262 160
     #if HAS_HEATED_BED
263
-      job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed;
161
+      info.target_temperature_bed = thermalManager.target_temperature_bed;
264 162
     #endif
265 163
 
266 164
     #if FAN_COUNT
267
-      COPY(job_recovery_info.fan_speed, fan_speed);
165
+      COPY(info.fan_speed, fan_speed);
268 166
     #endif
269 167
 
270 168
     #if HAS_LEVELING
271
-      job_recovery_info.leveling = planner.leveling_active;
272
-      job_recovery_info.fade = (
169
+      info.leveling = planner.leveling_active;
170
+      info.fade = (
273 171
         #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
274 172
           planner.z_fade_height
275 173
         #else
@@ -279,35 +177,238 @@ void save_job_recovery_info() {
279 177
     #endif
280 178
 
281 179
     #if ENABLED(FWRETRACT)
282
-      COPY(job_recovery_info.retract, fwretract.current_retract);
283
-      job_recovery_info.retract_hop = fwretract.current_hop;
180
+      COPY(info.retract, fwretract.current_retract);
181
+      info.retract_hop = fwretract.current_hop;
284 182
     #endif
285 183
 
286 184
     // Commands in the queue
287
-    job_recovery_info.cmd_queue_index_r = cmd_queue_index_r;
288
-    job_recovery_info.commands_in_queue = commands_in_queue;
289
-    COPY(job_recovery_info.command_queue, command_queue);
185
+    info.cmd_queue_index_r = cmd_queue_index_r;
186
+    info.commands_in_queue = commands_in_queue;
187
+    COPY(info.command_queue, command_queue);
290 188
 
291 189
     // Elapsed print job time
292
-    job_recovery_info.print_job_elapsed = print_job_timer.duration();
190
+    info.print_job_elapsed = print_job_timer.duration();
293 191
 
294 192
     // SD file position
295
-    card.getAbsFilename(job_recovery_info.sd_filename);
296
-    job_recovery_info.sdpos = card.getIndex();
193
+    card.getAbsFilename(info.sd_filename);
194
+    info.sdpos = card.getIndex();
195
+
196
+    write();
297 197
 
298
-    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
299
-      SERIAL_PROTOCOLLNPGM("Saving...");
300
-      debug_print_job_recovery(false);
198
+    // KILL now if the power-loss pin was triggered
199
+    #if PIN_EXISTS(POWER_LOSS)
200
+      if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(MSG_OUTAGE_RECOVERY);
301 201
     #endif
202
+  }
203
+}
302 204
 
303
-    card.openJobRecoveryFile(false);
304
-    (void)card.saveJobRecoveryInfo();
205
+/**
206
+ * Save the recovery info the recovery file
207
+ */
208
+void PrintJobRecovery::write() {
305 209
 
306
-    // If power-loss pin was triggered, write just once then kill
307
-    #if PIN_EXISTS(POWER_LOSS)
308
-      if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(MSG_POWER_LOSS_RECOVERY);
210
+  #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
211
+    debug(PSTR("Write"));
212
+  #endif
213
+
214
+  open(false);
215
+  file.seekSet(0);
216
+  const int16_t ret = file.write(&info, sizeof(info));
217
+  #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
218
+    if (ret == -1) SERIAL_ECHOLNPGM("Power-loss file write failed.");
219
+  #endif
220
+}
221
+
222
+/**
223
+ * Resume the saved print job
224
+ */
225
+void PrintJobRecovery::resume() {
226
+
227
+  #define RECOVERY_ZRAISE 2
228
+
229
+  #if HAS_LEVELING
230
+    // Make sure leveling is off before any G92 and G28
231
+    gcode.process_subcommands_now_P(PSTR("M420 S0 Z0"));
232
+  #endif
233
+
234
+  // Set Z to 0, raise Z by 2mm, and Home (XY only for Cartesian) with no raise
235
+  // (Only do simulated homing in Marlin Dev Mode.)
236
+  gcode.process_subcommands_now_P(PSTR("G92.0 Z0|G1 Z" STRINGIFY(RECOVERY_ZRAISE) "|G28 R0"
237
+    #if ENABLED(MARLIN_DEV_MODE)
238
+      " S"
239
+    #elif !IS_KINEMATIC
240
+      " X Y"
309 241
     #endif
242
+  ));
243
+
244
+  // Pretend that all axes are homed
245
+  axis_homed = axis_known_position = xyz_bits;
246
+
247
+  char cmd[40], str_1[16], str_2[16];
248
+
249
+  // Select the previously active tool (with no_move)
250
+  #if EXTRUDERS > 1
251
+    sprintf_P(cmd, PSTR("T%i S"), info.active_hotend);
252
+    gcode.process_subcommands_now(cmd);
253
+  #endif
254
+
255
+  #if HAS_HEATED_BED
256
+    const int16_t bt = info.target_temperature_bed;
257
+    if (bt) {
258
+      // Restore the bed temperature
259
+      sprintf_P(cmd, PSTR("M190 S%i"), bt);
260
+      gcode.process_subcommands_now(cmd);
261
+    }
262
+  #endif
263
+
264
+  // Restore all hotend temperatures
265
+  HOTEND_LOOP() {
266
+    const int16_t et = info.target_temperature[e];
267
+    if (et) {
268
+      #if HOTENDS > 1
269
+        sprintf_P(cmd, PSTR("T%i"), e);
270
+        gcode.process_subcommands_now(cmd);
271
+      #endif
272
+      sprintf_P(cmd, PSTR("M109 S%i"), et);
273
+      gcode.process_subcommands_now(cmd);
274
+    }
310 275
   }
276
+
277
+  // Restore print cooling fan speeds
278
+  for (uint8_t i = 0; i < FAN_COUNT; i++) {
279
+    uint8_t f = info.fan_speed[i];
280
+    if (f) {
281
+      sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f);
282
+      gcode.process_subcommands_now(cmd);
283
+    }
284
+  }
285
+
286
+  // Restore retract and hop state
287
+  #if ENABLED(FWRETRACT)
288
+    for (uint8_t e = 0; e < EXTRUDERS; e++) {
289
+      if (info.retract[e] != 0.0)
290
+        fwretract.current_retract[e] = info.retract[e];
291
+        fwretract.retracted[e] = true;
292
+    }
293
+    fwretract.current_hop = info.retract_hop;
294
+  #endif
295
+
296
+  #if HAS_LEVELING
297
+    // Restore leveling state before 'G92 Z' to ensure
298
+    // the Z stepper count corresponds to the native Z.
299
+    if (info.fade || info.leveling) {
300
+      dtostrf(info.fade, 1, 1, str_1);
301
+      sprintf_P(cmd, PSTR("M420 S%i Z%s"), int(info.leveling), str_1);
302
+      gcode.process_subcommands_now(cmd);
303
+    }
304
+  #endif
305
+
306
+  // Restore Z (plus raise) and E positions with G92.0
307
+  dtostrf(info.current_position[Z_AXIS] + RECOVERY_ZRAISE, 1, 3, str_1);
308
+  dtostrf(info.current_position[E_AXIS]
309
+    #if ENABLED(SAVE_EACH_CMD_MODE)
310
+      - 5 // Extra extrusion on restart
311
+    #endif
312
+    , 1, 3, str_2
313
+  );
314
+  sprintf_P(cmd, PSTR("G92.0 Z%s E%s"), str_1, str_2);
315
+  gcode.process_subcommands_now(cmd);
316
+
317
+  // Move back to the saved XY
318
+  dtostrf(info.current_position[X_AXIS], 1, 3, str_1);
319
+  dtostrf(info.current_position[Y_AXIS], 1, 3, str_2);
320
+  sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"), str_1, str_2);
321
+  gcode.process_subcommands_now(cmd);
322
+
323
+  // Move back to the saved Z
324
+  dtostrf(info.current_position[Z_AXIS], 1, 3, str_1);
325
+  sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1);
326
+  gcode.process_subcommands_now(cmd);
327
+
328
+  // Restore the feedrate
329
+  sprintf_P(cmd, PSTR("G1 F%d"), info.feedrate);
330
+  gcode.process_subcommands_now(cmd);
331
+
332
+  // Process commands from the old pending queue
333
+  uint8_t r = info.cmd_queue_index_r, c = info.commands_in_queue;
334
+  for (; c--; r = (r + 1) % BUFSIZE)
335
+    gcode.process_subcommands_now(info.command_queue[r]);
336
+
337
+  // Resume the SD file from the last position
338
+  char *fn = info.sd_filename;
339
+  while (*fn == '/') fn++;
340
+  sprintf_P(cmd, PSTR("M23 %s"), fn);
341
+  gcode.process_subcommands_now(cmd);
342
+  sprintf_P(cmd, PSTR("M24 S%ld T%ld"), info.sdpos, info.print_job_elapsed);
343
+  gcode.process_subcommands_now(cmd);
311 344
 }
312 345
 
346
+#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
347
+
348
+  void PrintJobRecovery::debug(PGM_P const prefix) {
349
+    serialprintPGM(prefix);
350
+    SERIAL_ECHOPAIR(" Job Recovery Info...\nvalid_head:", int(info.valid_head));
351
+    SERIAL_ECHOLNPAIR(" valid_foot:", int(info.valid_foot));
352
+    if (info.valid_head) {
353
+      if (info.valid_head == info.valid_foot) {
354
+        SERIAL_ECHOPGM("current_position: ");
355
+        LOOP_XYZE(i) {
356
+          SERIAL_ECHO(info.current_position[i]);
357
+          if (i < E_AXIS) SERIAL_CHAR(',');
358
+        }
359
+        SERIAL_EOL();
360
+        SERIAL_ECHOLNPAIR("feedrate: ", info.feedrate);
361
+
362
+        #if HOTENDS > 1
363
+          SERIAL_ECHOLNPAIR("active_hotend: ", int(info.active_hotend));
364
+        #endif
365
+
366
+        SERIAL_ECHOPGM("target_temperature: ");
367
+        HOTEND_LOOP() {
368
+          SERIAL_ECHO(info.target_temperature[e]);
369
+          if (e < HOTENDS - 1) SERIAL_CHAR(',');
370
+        }
371
+        SERIAL_EOL();
372
+
373
+        #if HAS_HEATED_BED
374
+          SERIAL_ECHOLNPAIR("target_temperature_bed: ", info.target_temperature_bed);
375
+        #endif
376
+
377
+        #if FAN_COUNT
378
+          SERIAL_ECHOPGM("fan_speed: ");
379
+          for (int8_t i = 0; i < FAN_COUNT; i++) {
380
+            SERIAL_ECHO(int(info.fan_speed[i]));
381
+            if (i < FAN_COUNT - 1) SERIAL_CHAR(',');
382
+          }
383
+          SERIAL_EOL();
384
+        #endif
385
+
386
+        #if HAS_LEVELING
387
+          SERIAL_ECHOPAIR("leveling: ", int(info.leveling));
388
+          SERIAL_ECHOLNPAIR(" fade: ", int(info.fade));
389
+        #endif
390
+        #if ENABLED(FWRETRACT)
391
+          SERIAL_ECHOPGM("retract: ");
392
+          for (int8_t e = 0; e < EXTRUDERS; e++) {
393
+            SERIAL_ECHO(info.retract[e]);
394
+            if (e < EXTRUDERS - 1) SERIAL_CHAR(',');
395
+          }
396
+          SERIAL_EOL();
397
+          SERIAL_ECHOLNPAIR("retract_hop: ", info.retract_hop);
398
+        #endif
399
+        SERIAL_ECHOLNPAIR("cmd_queue_index_r: ", int(info.cmd_queue_index_r));
400
+        SERIAL_ECHOLNPAIR("commands_in_queue: ", int(info.commands_in_queue));
401
+        for (uint8_t i = 0; i < info.commands_in_queue; i++) SERIAL_ECHOLNPAIR("> ", info.command_queue[i]);
402
+        SERIAL_ECHOLNPAIR("sd_filename: ", info.sd_filename);
403
+        SERIAL_ECHOLNPAIR("sdpos: ", info.sdpos);
404
+        SERIAL_ECHOLNPAIR("print_job_elapsed: ", info.print_job_elapsed);
405
+      }
406
+      else
407
+        SERIAL_ECHOLNPGM("INVALID DATA");
408
+    }
409
+    SERIAL_ECHOLNPGM("---");
410
+  }
411
+
412
+#endif // DEBUG_POWER_LOSS_RECOVERY
413
+
313 414
 #endif // POWER_LOSS_RECOVERY

+ 39
- 19
Marlin/src/feature/power_loss_recovery.h View File

@@ -26,7 +26,6 @@
26 26
  */
27 27
 
28 28
 #include "../sd/cardreader.h"
29
-#include "../core/millis_t.h"
30 29
 #include "../inc/MarlinConfigPre.h"
31 30
 
32 31
 #define SAVE_INFO_INTERVAL_MS 0
@@ -37,7 +36,9 @@ typedef struct {
37 36
   uint8_t valid_head;
38 37
 
39 38
   // Machine state
40
-  float current_position[NUM_AXIS], feedrate;
39
+  float current_position[NUM_AXIS];
40
+
41
+  uint16_t feedrate;
41 42
 
42 43
   #if HOTENDS > 1
43 44
     uint8_t active_hotend;
@@ -74,26 +75,45 @@ typedef struct {
74 75
   millis_t print_job_elapsed;
75 76
 
76 77
   uint8_t valid_foot;
78
+
77 79
 } job_recovery_info_t;
78 80
 
79
-extern job_recovery_info_t job_recovery_info;
81
+class PrintJobRecovery {
82
+  public:
83
+    static SdFile file;
84
+    static job_recovery_info_t info;
80 85
 
81
-enum JobRecoveryPhase : unsigned char {
82
-  JOB_RECOVERY_IDLE,
83
-  JOB_RECOVERY_MAYBE,
84
-  JOB_RECOVERY_YES,
85
-  JOB_RECOVERY_DONE
86
-};
87
-extern JobRecoveryPhase job_recovery_phase;
86
+    static void init();
87
+
88
+    static bool enabled;
89
+    static void enable(const bool onoff);
90
+    static void changed();
91
+
92
+    static void check();
93
+    static void resume();
88 94
 
89
-#if HAS_LEVELING
90
-  #define APPEND_CMD_COUNT 9
91
-#else
92
-  #define APPEND_CMD_COUNT 7
93
-#endif
95
+    static inline bool exists() { return card.jobRecoverFileExists(); }
96
+    static inline void open(const bool read) { card.openJobRecoveryFile(read); }
97
+    static inline void close() { file.close(); }
94 98
 
95
-extern char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
96
-extern uint8_t job_recovery_commands_count;
99
+    static void purge();
100
+    static void load();
101
+    static void save(const bool force=
102
+      #if ENABLED(SAVE_EACH_CMD_MODE)
103
+        true
104
+      #else
105
+        false
106
+      #endif
107
+    );
108
+
109
+  static inline bool valid() { return info.valid_head && info.valid_head == info.valid_foot; }
110
+
111
+    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
112
+      static void debug(PGM_P const prefix);
113
+    #endif
114
+
115
+  private:
116
+    static void write();
117
+};
97 118
 
98
-void check_print_job_recovery();
99
-void save_job_recovery_info();
119
+extern PrintJobRecovery recovery;

+ 65
- 0
Marlin/src/gcode/feature/powerloss/M1000.cpp View File

@@ -0,0 +1,65 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+#include "../../../inc/MarlinConfig.h"
24
+
25
+#if ENABLED(POWER_LOSS_RECOVERY)
26
+
27
+#include "../../gcode.h"
28
+#include "../../../feature/power_loss_recovery.h"
29
+#include "../../../module/motion.h"
30
+#include "../../../lcd/ultralcd.h"
31
+
32
+void menu_job_recovery();
33
+
34
+#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
35
+
36
+  inline void plr_error(PGM_P const prefix) {
37
+    SERIAL_ECHO_START();
38
+    serialprintPGM(prefix);
39
+    SERIAL_ECHOLNPGM(" Power-Loss Recovery Data");
40
+  }
41
+
42
+#endif
43
+
44
+/**
45
+ * M1000: Resume from power-loss (undocumented)
46
+ *   - With 'S' go to the Resume/Cancel menu
47
+ *   - With no parameters, run recovery commands
48
+ */
49
+void GcodeSuite::M1000() {
50
+
51
+  if (recovery.valid()) {
52
+    if (parser.seen('S'))
53
+      ui.goto_screen(menu_job_recovery);
54
+    else
55
+      recovery.resume();
56
+  }
57
+  else {
58
+    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
59
+      plr_error(recovery.info.valid_head ? PSTR("No") : PSTR("Invalid"));
60
+    #endif
61
+  }
62
+
63
+}
64
+
65
+#endif // POWER_LOSS_RECOVERY

+ 58
- 0
Marlin/src/gcode/feature/powerloss/M413.cpp View File

@@ -0,0 +1,58 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+#include "../../../inc/MarlinConfig.h"
24
+
25
+#if ENABLED(POWER_LOSS_RECOVERY)
26
+
27
+#include "../../gcode.h"
28
+#include "../../../feature/power_loss_recovery.h"
29
+#include "../../../module/motion.h"
30
+#include "../../../lcd/ultralcd.h"
31
+
32
+/**
33
+ * M413: Enable / Disable power-loss recovery
34
+ *
35
+ * Parameters
36
+ *   S[bool] - Flag to enable / disable.
37
+ *             If omitted, report current state.
38
+ */
39
+void GcodeSuite::M413() {
40
+
41
+  if (parser.seen('S'))
42
+    recovery.enable(parser.value_bool());
43
+  else {
44
+    SERIAL_ECHO_START();
45
+    SERIAL_ECHOPGM("Power-loss recovery ");
46
+    serialprintln_onoff(recovery.enabled);
47
+  }
48
+
49
+  #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
50
+    if (parser.seen('R') || parser.seen('L')) recovery.load();
51
+    if (parser.seen('W')) recovery.save(true);
52
+    if (parser.seen('P')) recovery.purge();
53
+    if (parser.seen('E')) serialprintPGM(recovery.exists() ? PSTR("BIN Exists\n") : PSTR("No BIN\n"));
54
+    if (parser.seen('V')) serialprintPGM(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n"));
55
+  #endif
56
+}
57
+
58
+#endif // POWER_LOSS_RECOVERY

+ 5
- 0
Marlin/src/gcode/gcode.cpp View File

@@ -695,6 +695,11 @@ void GcodeSuite::process_parsed_command(
695 695
 
696 696
       case 999: M999(); break;                                    // M999: Restart after being Stopped
697 697
 
698
+      #if ENABLED(POWER_LOSS_RECOVERY)
699
+        case 413: M413(); break;                                  // M413: Enable/disable/query Power-Loss Recovery
700
+        case 1000: M1000(); break;                                // M1000: Resume from power-loss
701
+      #endif
702
+
698 703
       default: parser.unknown_command_error(); break;
699 704
     }
700 705
     break;

+ 7
- 1
Marlin/src/gcode/gcode.h View File

@@ -196,7 +196,8 @@
196 196
  * M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
197 197
  * M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
198 198
  * M410 - Quickstop. Abort all planned moves.
199
- * M412 - Enable / Disable filament runout detection. (Requires FILAMENT_RUNOUT_SENSOR)
199
+ * M412 - Enable / Disable Filament Runout Detection. (Requires FILAMENT_RUNOUT_SENSOR)
200
+ * M413 - Enable / Disable Power-Loss Recovery. (Requires POWER_LOSS_RECOVERY)
200 201
  * M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
201 202
  * M421 - Set a single Z coordinate in the Mesh Leveling grid. X<units> Y<units> Z<units> (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, or AUTO_BED_LEVELING_UBL)
202 203
  * M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
@@ -822,6 +823,11 @@ private:
822 823
 
823 824
   static void M999();
824 825
 
826
+  #if ENABLED(POWER_LOSS_RECOVERY)
827
+    static void M413();
828
+    static void M1000();
829
+  #endif
830
+
825 831
   static void T(const uint8_t tool_index);
826 832
 
827 833
 };

+ 1
- 22
Marlin/src/gcode/queue.cpp View File

@@ -809,22 +809,6 @@ inline void get_serial_commands() {
809 809
     }
810 810
   }
811 811
 
812
-  #if ENABLED(POWER_LOSS_RECOVERY)
813
-
814
-    inline bool drain_job_recovery_commands() {
815
-      static uint8_t job_recovery_commands_index = 0; // Resets on reboot
816
-      if (job_recovery_commands_count) {
817
-        if (_enqueuecommand(job_recovery_commands[job_recovery_commands_index])) {
818
-          ++job_recovery_commands_index;
819
-          if (!--job_recovery_commands_count) job_recovery_phase = JOB_RECOVERY_DONE;
820
-        }
821
-        return true;
822
-      }
823
-      return false;
824
-    }
825
-
826
-  #endif
827
-
828 812
 #endif // SDSUPPORT
829 813
 
830 814
 /**
@@ -840,11 +824,6 @@ void get_available_commands() {
840 824
 
841 825
   get_serial_commands();
842 826
 
843
-  #if ENABLED(POWER_LOSS_RECOVERY)
844
-    // Commands for power-loss recovery take precedence
845
-    if (job_recovery_phase == JOB_RECOVERY_YES && drain_job_recovery_commands()) return;
846
-  #endif
847
-
848 827
   #if ENABLED(SDSUPPORT)
849 828
     get_sdcard_commands();
850 829
   #endif
@@ -890,7 +869,7 @@ void advance_command_queue() {
890 869
     else {
891 870
       gcode.process_next_command();
892 871
       #if ENABLED(POWER_LOSS_RECOVERY)
893
-        if (card.cardOK && IS_SD_PRINTING()) save_job_recovery_info();
872
+        if (IS_SD_PRINTING()) recovery.save();
894 873
       #endif
895 874
     }
896 875
 

+ 1
- 1
Marlin/src/inc/Conditionals_post.h View File

@@ -1629,7 +1629,7 @@
1629 1629
 // If platform requires early initialization of watchdog to properly boot
1630 1630
 #define EARLY_WATCHDOG (ENABLED(USE_WATCHDOG) && defined(ARDUINO_ARCH_SAM))
1631 1631
 
1632
-#define USE_EXECUTE_COMMANDS_IMMEDIATE (ENABLED(G29_RETRY_AND_RECOVER) || ENABLED(GCODE_MACROS))
1632
+#define USE_EXECUTE_COMMANDS_IMMEDIATE (ENABLED(G29_RETRY_AND_RECOVER) || ENABLED(GCODE_MACROS) || ENABLED(POWER_LOSS_RECOVERY))
1633 1633
 
1634 1634
 #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
1635 1635
   #define Z_STEPPER_COUNT 3

+ 1
- 1
Marlin/src/lcd/language/language_cz.h View File

@@ -253,7 +253,7 @@
253 253
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastavit tisk")
254 254
 #define MSG_RESUME_PRINT                    _UxGT("Obnovit tisk")
255 255
 #define MSG_STOP_PRINT                      _UxGT("Zastavit tisk")
256
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Obnova vypadku")
256
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Obnova vypadku")
257 257
 #define MSG_CARD_MENU                       _UxGT("Tisknout z SD")
258 258
 #define MSG_NO_CARD                         _UxGT("Žádná SD karta")
259 259
 #define MSG_DWELL                           _UxGT("Uspáno...")

+ 1
- 1
Marlin/src/lcd/language/language_de.h View File

@@ -268,7 +268,7 @@
268 268
 #define MSG_PAUSE_PRINT                     _UxGT("SD-Druck pausieren")
269 269
 #define MSG_RESUME_PRINT                    _UxGT("SD-Druck fortsetzen")
270 270
 #define MSG_STOP_PRINT                      _UxGT("SD-Druck abbrechen")
271
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Wiederh. n. Stroma.")
271
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Wiederh. n. Stroma.")
272 272
 #define MSG_CARD_MENU                       _UxGT("Druck v. SD-Karte")
273 273
 #define MSG_NO_CARD                         _UxGT("Keine SD-Karte")
274 274
 #define MSG_DWELL                           _UxGT("Warten...")

+ 2
- 2
Marlin/src/lcd/language/language_en.h View File

@@ -728,8 +728,8 @@
728 728
 #ifndef MSG_STOP_PRINT
729 729
   #define MSG_STOP_PRINT                      _UxGT("Stop print")
730 730
 #endif
731
-#ifndef MSG_POWER_LOSS_RECOVERY
732
-  #define MSG_POWER_LOSS_RECOVERY             _UxGT("Power-Loss Recovery")
731
+#ifndef MSG_OUTAGE_RECOVERY
732
+  #define MSG_OUTAGE_RECOVERY                 _UxGT("Outage Recovery")
733 733
 #endif
734 734
 #ifndef MSG_CARD_MENU
735 735
   #define MSG_CARD_MENU                       _UxGT("Print from SD")

+ 1
- 1
Marlin/src/lcd/language/language_it.h View File

@@ -266,7 +266,7 @@
266 266
 #define MSG_PAUSE_PRINT                     _UxGT("Pausa stampa")
267 267
 #define MSG_RESUME_PRINT                    _UxGT("Riprendi stampa")
268 268
 #define MSG_STOP_PRINT                      _UxGT("Arresta stampa")
269
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Ripresa da PowerLoss")
269
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Ripresa da PowerLoss")
270 270
 #define MSG_CARD_MENU                       _UxGT("Stampa da SD")
271 271
 #define MSG_NO_CARD                         _UxGT("SD non presente")
272 272
 #define MSG_DWELL                           _UxGT("Sospensione...")

+ 1
- 1
Marlin/src/lcd/language/language_ko_KR.h View File

@@ -259,7 +259,7 @@
259 259
 #define MSG_PAUSE_PRINT                     _UxGT("일시정지")
260 260
 #define MSG_RESUME_PRINT                    _UxGT("재시작")
261 261
 #define MSG_STOP_PRINT                      _UxGT("출력중지")
262
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Power-Loss Recovery")
262
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Outage Recovery")
263 263
 #define MSG_CARD_MENU                       _UxGT("SD 카드출력")
264 264
 #define MSG_NO_CARD                         _UxGT("SD 카드없음")
265 265
 #define MSG_DWELL                           _UxGT("슬립모드...")

+ 1
- 1
Marlin/src/lcd/language/language_pt-br.h View File

@@ -273,7 +273,7 @@
273 273
 #define MSG_PAUSE_PRINT                     _UxGT("Pausar impressão")
274 274
 #define MSG_RESUME_PRINT                    _UxGT("Resumir impressão")
275 275
 #define MSG_STOP_PRINT                      _UxGT("Parar impressão")
276
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Recuperar Impressão")
276
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Recuperar Impressão")
277 277
 #define MSG_CARD_MENU                       _UxGT("Imprimir do SD")
278 278
 #define MSG_NO_CARD                         _UxGT("Sem cartão SD")
279 279
 #define MSG_DWELL                           _UxGT("Dormindo...")

+ 1
- 1
Marlin/src/lcd/language/language_sk.h View File

@@ -278,7 +278,7 @@
278 278
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastaviť tlač")
279 279
 #define MSG_RESUME_PRINT                    _UxGT("Obnoviť tlač")
280 280
 #define MSG_STOP_PRINT                      _UxGT("Zastaviť tlač")
281
-#define MSG_POWER_LOSS_RECOVERY             _UxGT("Obnova po výp. nap.")
281
+#define MSG_OUTAGE_RECOVERY                 _UxGT("Obnova po výp. nap.")
282 282
 #define MSG_CARD_MENU                       _UxGT("Tlačiť z SD")
283 283
 #define MSG_NO_CARD                         _UxGT("Žiadna SD karta")
284 284
 #define MSG_DWELL                           _UxGT("Spím...")

+ 8
- 0
Marlin/src/lcd/menu/menu_configuration.cpp View File

@@ -36,6 +36,10 @@
36 36
   #include "../../feature/runout.h"
37 37
 #endif
38 38
 
39
+#if ENABLED(POWER_LOSS_RECOVERY)
40
+  #include "../../feature/power_loss_recovery.h"
41
+#endif
42
+
39 43
 #define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST)
40 44
 
41 45
 void menu_advanced_settings();
@@ -350,6 +354,10 @@ void menu_configuration() {
350 354
     MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset);
351 355
   #endif
352 356
 
357
+  #if ENABLED(POWER_LOSS_RECOVERY)
358
+    MENU_ITEM_EDIT_CALLBACK(bool, MSG_OUTAGE_RECOVERY, &recovery.enabled, recovery.changed);
359
+  #endif
360
+
353 361
   #if DISABLED(SLIM_LCD_MENUS)
354 362
     // Preheat configurations
355 363
     MENU_ITEM(submenu, MSG_PREHEAT_1_SETTINGS, menu_preheat_material1_settings);

+ 2
- 52
Marlin/src/lcd/menu/menu_job_recovery.cpp View File

@@ -34,58 +34,8 @@
34 34
 #include "../../feature/power_loss_recovery.h"
35 35
 
36 36
 static void lcd_power_loss_recovery_resume() {
37
-  char cmd[20];
38
-
39
-  // Return to status now
40 37
   ui.return_to_status();
41
-
42
-  // Turn leveling off and home
43
-  enqueue_and_echo_commands_P(PSTR("M420 S0\nG28 R0"
44
-    #if ENABLED(MARLIN_DEV_MODE)
45
-      " S"
46
-    #elif !IS_KINEMATIC
47
-      " X Y"
48
-    #endif
49
-  ));
50
-
51
-  #if HAS_HEATED_BED
52
-    const int16_t bt = job_recovery_info.target_temperature_bed;
53
-    if (bt) {
54
-      // Restore the bed temperature
55
-      sprintf_P(cmd, PSTR("M190 S%i"), bt);
56
-      enqueue_and_echo_command(cmd);
57
-    }
58
-  #endif
59
-
60
-  // Restore all hotend temperatures
61
-  HOTEND_LOOP() {
62
-    const int16_t et = job_recovery_info.target_temperature[e];
63
-    if (et) {
64
-      #if HOTENDS > 1
65
-        sprintf_P(cmd, PSTR("T%i"), e);
66
-        enqueue_and_echo_command(cmd);
67
-      #endif
68
-      sprintf_P(cmd, PSTR("M109 S%i"), et);
69
-      enqueue_and_echo_command(cmd);
70
-    }
71
-  }
72
-
73
-  #if HOTENDS > 1
74
-    sprintf_P(cmd, PSTR("T%i"), job_recovery_info.active_hotend);
75
-    enqueue_and_echo_command(cmd);
76
-  #endif
77
-
78
-  // Restore print cooling fan speeds
79
-  for (uint8_t i = 0; i < FAN_COUNT; i++) {
80
-    uint8_t f = job_recovery_info.fan_speed[i];
81
-    if (f) {
82
-      sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f);
83
-      enqueue_and_echo_command(cmd);
84
-    }
85
-  }
86
-
87
-  // Start draining the job recovery command queue
88
-  job_recovery_phase = JOB_RECOVERY_YES;
38
+  enqueue_and_echo_commands_P(PSTR("M1000"));
89 39
 }
90 40
 
91 41
 static void lcd_power_loss_recovery_cancel() {
@@ -97,7 +47,7 @@ static void lcd_power_loss_recovery_cancel() {
97 47
 void menu_job_recovery() {
98 48
   ui.defer_status_screen(true);
99 49
   START_MENU();
100
-  STATIC_ITEM(MSG_POWER_LOSS_RECOVERY);
50
+  STATIC_ITEM(MSG_OUTAGE_RECOVERY);
101 51
   MENU_ITEM(function, MSG_RESUME_PRINT, lcd_power_loss_recovery_resume);
102 52
   MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel);
103 53
   END_MENU();

+ 1
- 1
Marlin/src/lcd/menu/menu_main.cpp View File

@@ -65,7 +65,7 @@
65 65
 
66 66
   #if ENABLED(MENU_ADDAUTOSTART)
67 67
 
68
-    void lcd_autostart_sd() { card.beginautostart(); }
68
+    inline void lcd_autostart_sd() { card.beginautostart(); }
69 69
 
70 70
   #endif
71 71
 

+ 0
- 7
Marlin/src/lcd/ultralcd.cpp View File

@@ -659,13 +659,6 @@ void MarlinUI::update() {
659 659
 
660 660
   #endif // SDSUPPORT && SD_DETECT_PIN
661 661
 
662
-  #if ENABLED(POWER_LOSS_RECOVERY)
663
-    if (job_recovery_commands_count && job_recovery_phase == JOB_RECOVERY_IDLE) {
664
-      goto_screen(menu_job_recovery);
665
-      job_recovery_phase = JOB_RECOVERY_MAYBE; // Waiting for a response
666
-    }
667
-  #endif
668
-
669 662
   const millis_t ms = millis();
670 663
   if (ELAPSED(ms, next_lcd_update_ms)
671 664
     #if HAS_GRAPHICAL_LCD

+ 56
- 3
Marlin/src/module/configuration_store.cpp View File

@@ -37,7 +37,7 @@
37 37
  */
38 38
 
39 39
 // Change EEPROM version if the structure changes
40
-#define EEPROM_VERSION "V62"
40
+#define EEPROM_VERSION "V63"
41 41
 #define EEPROM_OFFSET 100
42 42
 
43 43
 // Check the integrity of data offsets.
@@ -82,6 +82,11 @@
82 82
 #endif
83 83
 
84 84
 #include "../feature/fwretract.h"
85
+
86
+#if ENABLED(POWER_LOSS_RECOVERY)
87
+  #include "../feature/power_loss_recovery.h"
88
+#endif
89
+
85 90
 #include "../feature/pause.h"
86 91
 
87 92
 #if EXTRUDERS > 1
@@ -222,6 +227,11 @@ typedef struct SettingsDataStruct {
222 227
   int16_t lcd_contrast;                                 // M250 C
223 228
 
224 229
   //
230
+  // POWER_LOSS_RECOVERY
231
+  //
232
+  bool recovery_enabled;                                // M413 S
233
+
234
+  //
225 235
   // FWRETRACT
226 236
   //
227 237
   fwretract_settings_t fwretract_settings;              // M207 S F Z W, M208 S F W R
@@ -269,7 +279,7 @@ typedef struct SettingsDataStruct {
269 279
   // Tool-change settings
270 280
   //
271 281
   #if EXTRUDERS > 1
272
-    toolchange_settings_t toolchange_settings;                // M217 S P R
282
+    toolchange_settings_t toolchange_settings;          // M217 S P R
273 283
   #endif
274 284
 
275 285
 } SettingsData;
@@ -747,6 +757,22 @@ void MarlinSettings::postprocess() {
747 757
     }
748 758
 
749 759
     //
760
+    // Power-Loss Recovery
761
+    //
762
+    {
763
+      _FIELD_TEST(recovery_enabled);
764
+
765
+      const bool recovery_enabled =
766
+        #if ENABLED(POWER_LOSS_RECOVERY)
767
+          recovery.enabled
768
+        #else
769
+          true
770
+        #endif
771
+      ;
772
+      EEPROM_WRITE(recovery_enabled);
773
+    }
774
+
775
+    //
750 776
     // Firmware Retraction
751 777
     //
752 778
     {
@@ -1388,6 +1414,20 @@ void MarlinSettings::postprocess() {
1388 1414
       }
1389 1415
 
1390 1416
       //
1417
+      // Power-Loss Recovery
1418
+      //
1419
+      {
1420
+        _FIELD_TEST(recovery_enabled);
1421
+
1422
+        #if ENABLED(POWER_LOSS_RECOVERY)
1423
+          EEPROM_READ(recovery.enabled);
1424
+        #else
1425
+          bool recovery_enabled;
1426
+          EEPROM_READ(recovery_enabled);
1427
+        #endif
1428
+      }
1429
+
1430
+      //
1391 1431
       // Firmware Retraction
1392 1432
       //
1393 1433
       {
@@ -2075,6 +2115,10 @@ void MarlinSettings::reset(PORTARG_SOLO) {
2075 2115
     ui.set_contrast(DEFAULT_LCD_CONTRAST);
2076 2116
   #endif
2077 2117
 
2118
+  #if ENABLED(POWER_LOSS_RECOVERY)
2119
+    recovery.enable(true);
2120
+  #endif
2121
+
2078 2122
   #if ENABLED(FWRETRACT)
2079 2123
     fwretract.reset();
2080 2124
   #endif
@@ -2643,6 +2687,15 @@ void MarlinSettings::reset(PORTARG_SOLO) {
2643 2687
       SERIAL_ECHOLNPAIR_P(port, "  M250 C", ui.contrast);
2644 2688
     #endif
2645 2689
 
2690
+    #if ENABLED(POWER_LOSS_RECOVERY)
2691
+      if (!forReplay) {
2692
+        CONFIG_ECHO_START;
2693
+        SERIAL_ECHOLNPGM_P(port, "Power-Loss Recovery:");
2694
+      }
2695
+      CONFIG_ECHO_START;
2696
+      SERIAL_ECHOLNPAIR_P(port, "  M413 S", int(recovery.enabled));
2697
+    #endif
2698
+
2646 2699
     #if ENABLED(FWRETRACT)
2647 2700
 
2648 2701
       if (!forReplay) {
@@ -2683,7 +2736,7 @@ void MarlinSettings::reset(PORTARG_SOLO) {
2683 2736
     #if HAS_BED_PROBE
2684 2737
       if (!forReplay) {
2685 2738
         CONFIG_ECHO_START;
2686
-        SERIAL_ECHOPGM_P(port, "Z-Probe Offset (mm):");
2739
+        SERIAL_ECHOPGM_P(port, "Z-Probe Offset");
2687 2740
         SAY_UNITS_P(port, true);
2688 2741
       }
2689 2742
       CONFIG_ECHO_START;

+ 22
- 30
Marlin/src/sd/cardreader.cpp View File

@@ -478,7 +478,7 @@ void CardReader::openFile(char * const path, const bool read, const bool subcall
478 478
 void CardReader::removeFile(const char * const name) {
479 479
   if (!cardOK) return;
480 480
 
481
-  stopSDPrint();
481
+  //stopSDPrint();
482 482
 
483 483
   SdFile *curDir;
484 484
   const char * const fname = diveToFile(curDir, name, false);
@@ -549,7 +549,7 @@ void CardReader::checkautostart() {
549 549
 
550 550
   if (cardOK
551 551
     #if ENABLED(POWER_LOSS_RECOVERY)
552
-      && !jobRecoverFileExists() // Don't run auto#.g when a resume file exists
552
+      && !recovery.valid() // Don't run auto#.g when a resume file exists
553 553
     #endif
554 554
   ) {
555 555
     char autoname[8];
@@ -577,6 +577,7 @@ void CardReader::closefile(const bool store_location) {
577 577
   file.sync();
578 578
   file.close();
579 579
   saving = logging = false;
580
+  sdpos = 0;
580 581
   #if ENABLED(EMERGENCY_PARSER)
581 582
     emergency_parser.enable();
582 583
   #endif
@@ -622,8 +623,12 @@ uint16_t CardReader::getnrfilenames() {
622 623
 }
623 624
 
624 625
 /**
625
- * Dive to the given file path, with optional echo.
626
- * On exit set curDir and return the name part of the path.
626
+ * Dive to the given DOS 8.3 file path, with optional echo of the dive paths.
627
+ *
628
+ * On exit, curDir contains an SdFile reference to the file's directory.
629
+ *
630
+ * Returns a pointer to the last segment (filename) of the given DOS 8.3 path.
631
+ *
627 632
  * A NULL result indicates an unrecoverable error.
628 633
  */
629 634
 const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) {
@@ -993,12 +998,18 @@ void CardReader::printingHasFinished() {
993 998
 
994 999
 #if ENABLED(POWER_LOSS_RECOVERY)
995 1000
 
996
-  char job_recovery_file_name[4] = "bin";
1001
+  constexpr char job_recovery_file_name[4] = "BIN";
1002
+
1003
+  bool CardReader::jobRecoverFileExists() {
1004
+    const bool exists = recovery.file.open(&root, job_recovery_file_name, O_READ);
1005
+    if (exists) recovery.file.close();
1006
+    return exists;
1007
+  }
997 1008
 
998 1009
   void CardReader::openJobRecoveryFile(const bool read) {
999 1010
     if (!cardOK) return;
1000
-    if (jobRecoveryFile.isOpen()) return;
1001
-    if (!jobRecoveryFile.open(&root, job_recovery_file_name, read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC)) {
1011
+    if (recovery.file.isOpen()) return;
1012
+    if (!recovery.file.open(&root, job_recovery_file_name, read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC)) {
1002 1013
       SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name);
1003 1014
       SERIAL_PROTOCOLCHAR('.');
1004 1015
       SERIAL_EOL();
@@ -1007,31 +1018,12 @@ void CardReader::printingHasFinished() {
1007 1018
       SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name);
1008 1019
   }
1009 1020
 
1010
-  void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); }
1011
-
1012
-  bool CardReader::jobRecoverFileExists() {
1013
-    const bool exists = jobRecoveryFile.open(&root, job_recovery_file_name, O_READ);
1014
-    if (exists) jobRecoveryFile.close();
1015
-    return exists;
1016
-  }
1017
-
1018
-  int16_t CardReader::saveJobRecoveryInfo() {
1019
-    jobRecoveryFile.seekSet(0);
1020
-    const int16_t ret = jobRecoveryFile.write(&job_recovery_info, sizeof(job_recovery_info));
1021
-    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
1022
-      if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed.");
1023
-    #endif
1024
-    return ret;
1025
-  }
1026
-
1027
-  int16_t CardReader::loadJobRecoveryInfo() {
1028
-    return jobRecoveryFile.read(&job_recovery_info, sizeof(job_recovery_info));
1029
-  }
1030
-
1021
+  // Removing the job recovery file currently requires closing
1022
+  // the file being printed, so during SD printing the file should
1023
+  // be zeroed and written instead of deleted.
1031 1024
   void CardReader::removeJobRecoveryFile() {
1032
-    job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0;
1033 1025
     if (jobRecoverFileExists()) {
1034
-      closefile();
1026
+      //closefile();
1035 1027
       removeFile(job_recovery_file_name);
1036 1028
       #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
1037 1029
         SERIAL_PROTOCOLPGM("Power-loss file delete");

+ 1
- 8
Marlin/src/sd/cardreader.h View File

@@ -106,11 +106,8 @@ public:
106 106
   #endif
107 107
 
108 108
   #if ENABLED(POWER_LOSS_RECOVERY)
109
-    void openJobRecoveryFile(const bool read);
110
-    void closeJobRecoveryFile();
111 109
     bool jobRecoverFileExists();
112
-    int16_t saveJobRecoveryInfo();
113
-    int16_t loadJobRecoveryInfo();
110
+    void openJobRecoveryFile(const bool read);
114 111
     void removeJobRecoveryFile();
115 112
   #endif
116 113
 
@@ -217,10 +214,6 @@ private:
217 214
   SdVolume volume;
218 215
   SdFile file;
219 216
 
220
-  #if ENABLED(POWER_LOSS_RECOVERY)
221
-    SdFile jobRecoveryFile;
222
-  #endif
223
-
224 217
   #define SD_PROCEDURE_DEPTH 1
225 218
   #define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH + MAX_DIR_DEPTH + 1)
226 219
   uint8_t file_subcall_ctr;

Loading…
Cancel
Save