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
   #endif
924
   #endif
925
 
925
 
926
   #if DO_SWITCH_EXTRUDER
926
   #if DO_SWITCH_EXTRUDER
927
-    move_extruder_servo(0);  // Initialize extruder servo
927
+    move_extruder_servo(0);   // Initialize extruder servo
928
   #endif
928
   #endif
929
 
929
 
930
   #if ENABLED(SWITCHING_NOZZLE)
930
   #if ENABLED(SWITCHING_NOZZLE)
931
-    move_nozzle_servo(0);  // Initialize nozzle servo
931
+    move_nozzle_servo(0);     // Initialize nozzle servo
932
   #endif
932
   #endif
933
 
933
 
934
   #if ENABLED(PARKING_EXTRUDER)
934
   #if ENABLED(PARKING_EXTRUDER)
936
   #endif
936
   #endif
937
 
937
 
938
   #if ENABLED(POWER_LOSS_RECOVERY)
938
   #if ENABLED(POWER_LOSS_RECOVERY)
939
-    check_print_job_recovery();
939
+    recovery.check();
940
   #endif
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
   #endif
944
   #endif
945
 
945
 
946
   #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
946
   #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)

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

29
 #if ENABLED(POWER_LOSS_RECOVERY)
29
 #if ENABLED(POWER_LOSS_RECOVERY)
30
 
30
 
31
 #include "power_loss_recovery.h"
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
 #include "../lcd/ultralcd.h"
40
 #include "../lcd/ultralcd.h"
34
 #include "../gcode/queue.h"
41
 #include "../gcode/queue.h"
42
+#include "../gcode/gcode.h"
35
 #include "../module/motion.h"
43
 #include "../module/motion.h"
36
 #include "../module/planner.h"
44
 #include "../module/planner.h"
37
 #include "../module/printcounter.h"
45
 #include "../module/printcounter.h"
38
 #include "../module/temperature.h"
46
 #include "../module/temperature.h"
39
-#include "../sd/cardreader.h"
40
 #include "../core/serial.h"
47
 #include "../core/serial.h"
41
 
48
 
42
 #if ENABLED(FWRETRACT)
49
 #if ENABLED(FWRETRACT)
43
   #include "fwretract.h"
50
   #include "fwretract.h"
44
 #endif
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
  * Check for Print Job Recovery during setup()
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
  * Save the current machine state to the power-loss recovery file
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
   #if SAVE_INFO_INTERVAL_MS > 0
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
     millis_t ms = millis();
125
     millis_t ms = millis();
226
   #endif
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
       #endif
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
       #endif
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
     #endif
138
     #endif
243
   ) {
139
   ) {
140
+
244
     #if SAVE_INFO_INTERVAL_MS > 0
141
     #if SAVE_INFO_INTERVAL_MS > 0
245
       next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
142
       next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
246
     #endif
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
     // Machine state
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
     #if HOTENDS > 1
154
     #if HOTENDS > 1
257
-      job_recovery_info.active_hotend = active_extruder;
155
+      info.active_hotend = active_extruder;
258
     #endif
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
     #if HAS_HEATED_BED
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
     #endif
162
     #endif
265
 
163
 
266
     #if FAN_COUNT
164
     #if FAN_COUNT
267
-      COPY(job_recovery_info.fan_speed, fan_speed);
165
+      COPY(info.fan_speed, fan_speed);
268
     #endif
166
     #endif
269
 
167
 
270
     #if HAS_LEVELING
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
         #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
171
         #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
274
           planner.z_fade_height
172
           planner.z_fade_height
275
         #else
173
         #else
279
     #endif
177
     #endif
280
 
178
 
281
     #if ENABLED(FWRETRACT)
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
     #endif
182
     #endif
285
 
183
 
286
     // Commands in the queue
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
     // Elapsed print job time
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
     // SD file position
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
     #endif
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
     #endif
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
 #endif // POWER_LOSS_RECOVERY
414
 #endif // POWER_LOSS_RECOVERY

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

26
  */
26
  */
27
 
27
 
28
 #include "../sd/cardreader.h"
28
 #include "../sd/cardreader.h"
29
-#include "../core/millis_t.h"
30
 #include "../inc/MarlinConfigPre.h"
29
 #include "../inc/MarlinConfigPre.h"
31
 
30
 
32
 #define SAVE_INFO_INTERVAL_MS 0
31
 #define SAVE_INFO_INTERVAL_MS 0
37
   uint8_t valid_head;
36
   uint8_t valid_head;
38
 
37
 
39
   // Machine state
38
   // Machine state
40
-  float current_position[NUM_AXIS], feedrate;
39
+  float current_position[NUM_AXIS];
40
+
41
+  uint16_t feedrate;
41
 
42
 
42
   #if HOTENDS > 1
43
   #if HOTENDS > 1
43
     uint8_t active_hotend;
44
     uint8_t active_hotend;
74
   millis_t print_job_elapsed;
75
   millis_t print_job_elapsed;
75
 
76
 
76
   uint8_t valid_foot;
77
   uint8_t valid_foot;
78
+
77
 } job_recovery_info_t;
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

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

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
 
695
 
696
       case 999: M999(); break;                                    // M999: Restart after being Stopped
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
       default: parser.unknown_command_error(); break;
703
       default: parser.unknown_command_error(); break;
699
     }
704
     }
700
     break;
705
     break;

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

196
  * M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
196
  * M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
197
  * M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
197
  * M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
198
  * M410 - Quickstop. Abort all planned moves.
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
  * M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
201
  * M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
201
  * 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
  * 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
  * M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
203
  * M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
822
 
823
 
823
   static void M999();
824
   static void M999();
824
 
825
 
826
+  #if ENABLED(POWER_LOSS_RECOVERY)
827
+    static void M413();
828
+    static void M1000();
829
+  #endif
830
+
825
   static void T(const uint8_t tool_index);
831
   static void T(const uint8_t tool_index);
826
 
832
 
827
 };
833
 };

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

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
 #endif // SDSUPPORT
812
 #endif // SDSUPPORT
829
 
813
 
830
 /**
814
 /**
840
 
824
 
841
   get_serial_commands();
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
   #if ENABLED(SDSUPPORT)
827
   #if ENABLED(SDSUPPORT)
849
     get_sdcard_commands();
828
     get_sdcard_commands();
850
   #endif
829
   #endif
890
     else {
869
     else {
891
       gcode.process_next_command();
870
       gcode.process_next_command();
892
       #if ENABLED(POWER_LOSS_RECOVERY)
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
       #endif
873
       #endif
895
     }
874
     }
896
 
875
 

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

1629
 // If platform requires early initialization of watchdog to properly boot
1629
 // If platform requires early initialization of watchdog to properly boot
1630
 #define EARLY_WATCHDOG (ENABLED(USE_WATCHDOG) && defined(ARDUINO_ARCH_SAM))
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
 #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
1634
 #if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
1635
   #define Z_STEPPER_COUNT 3
1635
   #define Z_STEPPER_COUNT 3

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

253
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastavit tisk")
253
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastavit tisk")
254
 #define MSG_RESUME_PRINT                    _UxGT("Obnovit tisk")
254
 #define MSG_RESUME_PRINT                    _UxGT("Obnovit tisk")
255
 #define MSG_STOP_PRINT                      _UxGT("Zastavit tisk")
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
 #define MSG_CARD_MENU                       _UxGT("Tisknout z SD")
257
 #define MSG_CARD_MENU                       _UxGT("Tisknout z SD")
258
 #define MSG_NO_CARD                         _UxGT("Žádná SD karta")
258
 #define MSG_NO_CARD                         _UxGT("Žádná SD karta")
259
 #define MSG_DWELL                           _UxGT("Uspáno...")
259
 #define MSG_DWELL                           _UxGT("Uspáno...")

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

268
 #define MSG_PAUSE_PRINT                     _UxGT("SD-Druck pausieren")
268
 #define MSG_PAUSE_PRINT                     _UxGT("SD-Druck pausieren")
269
 #define MSG_RESUME_PRINT                    _UxGT("SD-Druck fortsetzen")
269
 #define MSG_RESUME_PRINT                    _UxGT("SD-Druck fortsetzen")
270
 #define MSG_STOP_PRINT                      _UxGT("SD-Druck abbrechen")
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
 #define MSG_CARD_MENU                       _UxGT("Druck v. SD-Karte")
272
 #define MSG_CARD_MENU                       _UxGT("Druck v. SD-Karte")
273
 #define MSG_NO_CARD                         _UxGT("Keine SD-Karte")
273
 #define MSG_NO_CARD                         _UxGT("Keine SD-Karte")
274
 #define MSG_DWELL                           _UxGT("Warten...")
274
 #define MSG_DWELL                           _UxGT("Warten...")

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

728
 #ifndef MSG_STOP_PRINT
728
 #ifndef MSG_STOP_PRINT
729
   #define MSG_STOP_PRINT                      _UxGT("Stop print")
729
   #define MSG_STOP_PRINT                      _UxGT("Stop print")
730
 #endif
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
 #endif
733
 #endif
734
 #ifndef MSG_CARD_MENU
734
 #ifndef MSG_CARD_MENU
735
   #define MSG_CARD_MENU                       _UxGT("Print from SD")
735
   #define MSG_CARD_MENU                       _UxGT("Print from SD")

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

266
 #define MSG_PAUSE_PRINT                     _UxGT("Pausa stampa")
266
 #define MSG_PAUSE_PRINT                     _UxGT("Pausa stampa")
267
 #define MSG_RESUME_PRINT                    _UxGT("Riprendi stampa")
267
 #define MSG_RESUME_PRINT                    _UxGT("Riprendi stampa")
268
 #define MSG_STOP_PRINT                      _UxGT("Arresta stampa")
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
 #define MSG_CARD_MENU                       _UxGT("Stampa da SD")
270
 #define MSG_CARD_MENU                       _UxGT("Stampa da SD")
271
 #define MSG_NO_CARD                         _UxGT("SD non presente")
271
 #define MSG_NO_CARD                         _UxGT("SD non presente")
272
 #define MSG_DWELL                           _UxGT("Sospensione...")
272
 #define MSG_DWELL                           _UxGT("Sospensione...")

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

259
 #define MSG_PAUSE_PRINT                     _UxGT("일시정지")
259
 #define MSG_PAUSE_PRINT                     _UxGT("일시정지")
260
 #define MSG_RESUME_PRINT                    _UxGT("재시작")
260
 #define MSG_RESUME_PRINT                    _UxGT("재시작")
261
 #define MSG_STOP_PRINT                      _UxGT("출력중지")
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
 #define MSG_CARD_MENU                       _UxGT("SD 카드출력")
263
 #define MSG_CARD_MENU                       _UxGT("SD 카드출력")
264
 #define MSG_NO_CARD                         _UxGT("SD 카드없음")
264
 #define MSG_NO_CARD                         _UxGT("SD 카드없음")
265
 #define MSG_DWELL                           _UxGT("슬립모드...")
265
 #define MSG_DWELL                           _UxGT("슬립모드...")

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

273
 #define MSG_PAUSE_PRINT                     _UxGT("Pausar impressão")
273
 #define MSG_PAUSE_PRINT                     _UxGT("Pausar impressão")
274
 #define MSG_RESUME_PRINT                    _UxGT("Resumir impressão")
274
 #define MSG_RESUME_PRINT                    _UxGT("Resumir impressão")
275
 #define MSG_STOP_PRINT                      _UxGT("Parar impressão")
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
 #define MSG_CARD_MENU                       _UxGT("Imprimir do SD")
277
 #define MSG_CARD_MENU                       _UxGT("Imprimir do SD")
278
 #define MSG_NO_CARD                         _UxGT("Sem cartão SD")
278
 #define MSG_NO_CARD                         _UxGT("Sem cartão SD")
279
 #define MSG_DWELL                           _UxGT("Dormindo...")
279
 #define MSG_DWELL                           _UxGT("Dormindo...")

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

278
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastaviť tlač")
278
 #define MSG_PAUSE_PRINT                     _UxGT("Pozastaviť tlač")
279
 #define MSG_RESUME_PRINT                    _UxGT("Obnoviť tlač")
279
 #define MSG_RESUME_PRINT                    _UxGT("Obnoviť tlač")
280
 #define MSG_STOP_PRINT                      _UxGT("Zastaviť tlač")
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
 #define MSG_CARD_MENU                       _UxGT("Tlačiť z SD")
282
 #define MSG_CARD_MENU                       _UxGT("Tlačiť z SD")
283
 #define MSG_NO_CARD                         _UxGT("Žiadna SD karta")
283
 #define MSG_NO_CARD                         _UxGT("Žiadna SD karta")
284
 #define MSG_DWELL                           _UxGT("Spím...")
284
 #define MSG_DWELL                           _UxGT("Spím...")

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

36
   #include "../../feature/runout.h"
36
   #include "../../feature/runout.h"
37
 #endif
37
 #endif
38
 
38
 
39
+#if ENABLED(POWER_LOSS_RECOVERY)
40
+  #include "../../feature/power_loss_recovery.h"
41
+#endif
42
+
39
 #define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST)
43
 #define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST)
40
 
44
 
41
 void menu_advanced_settings();
45
 void menu_advanced_settings();
350
     MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset);
354
     MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset);
351
   #endif
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
   #if DISABLED(SLIM_LCD_MENUS)
361
   #if DISABLED(SLIM_LCD_MENUS)
354
     // Preheat configurations
362
     // Preheat configurations
355
     MENU_ITEM(submenu, MSG_PREHEAT_1_SETTINGS, menu_preheat_material1_settings);
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
 #include "../../feature/power_loss_recovery.h"
34
 #include "../../feature/power_loss_recovery.h"
35
 
35
 
36
 static void lcd_power_loss_recovery_resume() {
36
 static void lcd_power_loss_recovery_resume() {
37
-  char cmd[20];
38
-
39
-  // Return to status now
40
   ui.return_to_status();
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
 static void lcd_power_loss_recovery_cancel() {
41
 static void lcd_power_loss_recovery_cancel() {
97
 void menu_job_recovery() {
47
 void menu_job_recovery() {
98
   ui.defer_status_screen(true);
48
   ui.defer_status_screen(true);
99
   START_MENU();
49
   START_MENU();
100
-  STATIC_ITEM(MSG_POWER_LOSS_RECOVERY);
50
+  STATIC_ITEM(MSG_OUTAGE_RECOVERY);
101
   MENU_ITEM(function, MSG_RESUME_PRINT, lcd_power_loss_recovery_resume);
51
   MENU_ITEM(function, MSG_RESUME_PRINT, lcd_power_loss_recovery_resume);
102
   MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel);
52
   MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel);
103
   END_MENU();
53
   END_MENU();

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

65
 
65
 
66
   #if ENABLED(MENU_ADDAUTOSTART)
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
   #endif
70
   #endif
71
 
71
 

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

659
 
659
 
660
   #endif // SDSUPPORT && SD_DETECT_PIN
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
   const millis_t ms = millis();
662
   const millis_t ms = millis();
670
   if (ELAPSED(ms, next_lcd_update_ms)
663
   if (ELAPSED(ms, next_lcd_update_ms)
671
     #if HAS_GRAPHICAL_LCD
664
     #if HAS_GRAPHICAL_LCD

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

37
  */
37
  */
38
 
38
 
39
 // Change EEPROM version if the structure changes
39
 // Change EEPROM version if the structure changes
40
-#define EEPROM_VERSION "V62"
40
+#define EEPROM_VERSION "V63"
41
 #define EEPROM_OFFSET 100
41
 #define EEPROM_OFFSET 100
42
 
42
 
43
 // Check the integrity of data offsets.
43
 // Check the integrity of data offsets.
82
 #endif
82
 #endif
83
 
83
 
84
 #include "../feature/fwretract.h"
84
 #include "../feature/fwretract.h"
85
+
86
+#if ENABLED(POWER_LOSS_RECOVERY)
87
+  #include "../feature/power_loss_recovery.h"
88
+#endif
89
+
85
 #include "../feature/pause.h"
90
 #include "../feature/pause.h"
86
 
91
 
87
 #if EXTRUDERS > 1
92
 #if EXTRUDERS > 1
222
   int16_t lcd_contrast;                                 // M250 C
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
   // FWRETRACT
235
   // FWRETRACT
226
   //
236
   //
227
   fwretract_settings_t fwretract_settings;              // M207 S F Z W, M208 S F W R
237
   fwretract_settings_t fwretract_settings;              // M207 S F Z W, M208 S F W R
269
   // Tool-change settings
279
   // Tool-change settings
270
   //
280
   //
271
   #if EXTRUDERS > 1
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
   #endif
283
   #endif
274
 
284
 
275
 } SettingsData;
285
 } SettingsData;
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
     // Firmware Retraction
776
     // Firmware Retraction
751
     //
777
     //
752
     {
778
     {
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
       // Firmware Retraction
1431
       // Firmware Retraction
1392
       //
1432
       //
1393
       {
1433
       {
2075
     ui.set_contrast(DEFAULT_LCD_CONTRAST);
2115
     ui.set_contrast(DEFAULT_LCD_CONTRAST);
2076
   #endif
2116
   #endif
2077
 
2117
 
2118
+  #if ENABLED(POWER_LOSS_RECOVERY)
2119
+    recovery.enable(true);
2120
+  #endif
2121
+
2078
   #if ENABLED(FWRETRACT)
2122
   #if ENABLED(FWRETRACT)
2079
     fwretract.reset();
2123
     fwretract.reset();
2080
   #endif
2124
   #endif
2643
       SERIAL_ECHOLNPAIR_P(port, "  M250 C", ui.contrast);
2687
       SERIAL_ECHOLNPAIR_P(port, "  M250 C", ui.contrast);
2644
     #endif
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
     #if ENABLED(FWRETRACT)
2699
     #if ENABLED(FWRETRACT)
2647
 
2700
 
2648
       if (!forReplay) {
2701
       if (!forReplay) {
2683
     #if HAS_BED_PROBE
2736
     #if HAS_BED_PROBE
2684
       if (!forReplay) {
2737
       if (!forReplay) {
2685
         CONFIG_ECHO_START;
2738
         CONFIG_ECHO_START;
2686
-        SERIAL_ECHOPGM_P(port, "Z-Probe Offset (mm):");
2739
+        SERIAL_ECHOPGM_P(port, "Z-Probe Offset");
2687
         SAY_UNITS_P(port, true);
2740
         SAY_UNITS_P(port, true);
2688
       }
2741
       }
2689
       CONFIG_ECHO_START;
2742
       CONFIG_ECHO_START;

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

478
 void CardReader::removeFile(const char * const name) {
478
 void CardReader::removeFile(const char * const name) {
479
   if (!cardOK) return;
479
   if (!cardOK) return;
480
 
480
 
481
-  stopSDPrint();
481
+  //stopSDPrint();
482
 
482
 
483
   SdFile *curDir;
483
   SdFile *curDir;
484
   const char * const fname = diveToFile(curDir, name, false);
484
   const char * const fname = diveToFile(curDir, name, false);
549
 
549
 
550
   if (cardOK
550
   if (cardOK
551
     #if ENABLED(POWER_LOSS_RECOVERY)
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
     #endif
553
     #endif
554
   ) {
554
   ) {
555
     char autoname[8];
555
     char autoname[8];
577
   file.sync();
577
   file.sync();
578
   file.close();
578
   file.close();
579
   saving = logging = false;
579
   saving = logging = false;
580
+  sdpos = 0;
580
   #if ENABLED(EMERGENCY_PARSER)
581
   #if ENABLED(EMERGENCY_PARSER)
581
     emergency_parser.enable();
582
     emergency_parser.enable();
582
   #endif
583
   #endif
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
  * A NULL result indicates an unrecoverable error.
632
  * A NULL result indicates an unrecoverable error.
628
  */
633
  */
629
 const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) {
634
 const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) {
993
 
998
 
994
 #if ENABLED(POWER_LOSS_RECOVERY)
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
   void CardReader::openJobRecoveryFile(const bool read) {
1009
   void CardReader::openJobRecoveryFile(const bool read) {
999
     if (!cardOK) return;
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
       SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name);
1013
       SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name);
1003
       SERIAL_PROTOCOLCHAR('.');
1014
       SERIAL_PROTOCOLCHAR('.');
1004
       SERIAL_EOL();
1015
       SERIAL_EOL();
1007
       SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name);
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
   void CardReader::removeJobRecoveryFile() {
1024
   void CardReader::removeJobRecoveryFile() {
1032
-    job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0;
1033
     if (jobRecoverFileExists()) {
1025
     if (jobRecoverFileExists()) {
1034
-      closefile();
1026
+      //closefile();
1035
       removeFile(job_recovery_file_name);
1027
       removeFile(job_recovery_file_name);
1036
       #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
1028
       #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
1037
         SERIAL_PROTOCOLPGM("Power-loss file delete");
1029
         SERIAL_PROTOCOLPGM("Power-loss file delete");

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

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

Loading…
Cancel
Save