diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp
index 3d77548ee..f2522f2ee 100644
--- a/Marlin/src/Marlin.cpp
+++ b/Marlin/src/Marlin.cpp
@@ -924,11 +924,11 @@ void setup() {
#endif
#if DO_SWITCH_EXTRUDER
- move_extruder_servo(0); // Initialize extruder servo
+ move_extruder_servo(0); // Initialize extruder servo
#endif
#if ENABLED(SWITCHING_NOZZLE)
- move_nozzle_servo(0); // Initialize nozzle servo
+ move_nozzle_servo(0); // Initialize nozzle servo
#endif
#if ENABLED(PARKING_EXTRUDER)
@@ -936,11 +936,11 @@ void setup() {
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
- check_print_job_recovery();
+ recovery.check();
#endif
- #if ENABLED(USE_WATCHDOG) // Reinit watchdog after HAL_get_reset_source call
- watchdog_init();
+ #if ENABLED(USE_WATCHDOG)
+ watchdog_init(); // Reinit watchdog after HAL_get_reset_source call
#endif
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
diff --git a/Marlin/src/feature/power_loss_recovery.cpp b/Marlin/src/feature/power_loss_recovery.cpp
index 10e824667..3661f5551 100644
--- a/Marlin/src/feature/power_loss_recovery.cpp
+++ b/Marlin/src/feature/power_loss_recovery.cpp
@@ -29,247 +29,145 @@
#if ENABLED(POWER_LOSS_RECOVERY)
#include "power_loss_recovery.h"
+#include "../core/macros.h"
+bool PrintJobRecovery::enabled; // Initialized by settings.load()
+
+SdFile PrintJobRecovery::file;
+job_recovery_info_t PrintJobRecovery::info;
+
+#include "../sd/cardreader.h"
#include "../lcd/ultralcd.h"
#include "../gcode/queue.h"
+#include "../gcode/gcode.h"
#include "../module/motion.h"
#include "../module/planner.h"
#include "../module/printcounter.h"
#include "../module/temperature.h"
-#include "../sd/cardreader.h"
#include "../core/serial.h"
#if ENABLED(FWRETRACT)
#include "fwretract.h"
#endif
-// Recovery data
-job_recovery_info_t job_recovery_info;
-JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE;
-uint8_t job_recovery_commands_count; //=0
-char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
+PrintJobRecovery recovery;
-extern uint8_t commands_in_queue, cmd_queue_index_r;
+/**
+ * Clear the recovery info
+ */
+void PrintJobRecovery::init() { memset(&info, 0, sizeof(info)); }
-#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
- void debug_print_job_recovery(const bool recovery) {
- SERIAL_PROTOCOLLNPGM("---- Job Recovery Info ----");
- SERIAL_PROTOCOLPAIR("valid_head:", int(job_recovery_info.valid_head));
- SERIAL_PROTOCOLLNPAIR(" valid_foot:", int(job_recovery_info.valid_foot));
- if (job_recovery_info.valid_head) {
- if (job_recovery_info.valid_head == job_recovery_info.valid_foot) {
- SERIAL_PROTOCOLPGM("current_position: ");
- LOOP_XYZE(i) {
- SERIAL_PROTOCOL(job_recovery_info.current_position[i]);
- if (i < E_AXIS) SERIAL_CHAR(',');
- }
- SERIAL_EOL();
- SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate);
+/**
+ * Enable or disable then call changed()
+ */
+void PrintJobRecovery::enable(const bool onoff) {
+ enabled = onoff;
+ changed();
+}
- #if HOTENDS > 1
- SERIAL_PROTOCOLLNPAIR("active_hotend: ", int(job_recovery_info.active_hotend));
- #endif
-
- SERIAL_PROTOCOLPGM("target_temperature: ");
- HOTEND_LOOP() {
- SERIAL_PROTOCOL(job_recovery_info.target_temperature[e]);
- if (e < HOTENDS - 1) SERIAL_CHAR(',');
- }
- SERIAL_EOL();
-
- #if HAS_HEATED_BED
- SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed);
- #endif
-
- #if FAN_COUNT
- SERIAL_PROTOCOLPGM("fan_speed: ");
- for (int8_t i = 0; i < FAN_COUNT; i++) {
- SERIAL_PROTOCOL(job_recovery_info.fan_speed[i]);
- if (i < FAN_COUNT - 1) SERIAL_CHAR(',');
- }
- SERIAL_EOL();
- #endif
-
- #if HAS_LEVELING
- SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling));
- SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade));
- #endif
- #if ENABLED(FWRETRACT)
- SERIAL_PROTOCOLPGM("retract: ");
- for (int8_t e = 0; e < EXTRUDERS; e++) {
- SERIAL_PROTOCOL(job_recovery_info.retract[e]);
- if (e < EXTRUDERS - 1) SERIAL_CHAR(',');
- }
- SERIAL_EOL();
- SERIAL_PROTOCOLLNPAIR("retract_hop: ", job_recovery_info.retract_hop);
- #endif
- SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", int(job_recovery_info.cmd_queue_index_r));
- SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", int(job_recovery_info.commands_in_queue));
- if (recovery)
- for (uint8_t i = 0; i < job_recovery_commands_count; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_commands[i]);
- else
- for (uint8_t i = 0; i < job_recovery_info.commands_in_queue; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_info.command_queue[i]);
- SERIAL_PROTOCOLLNPAIR("sd_filename: ", job_recovery_info.sd_filename);
- SERIAL_PROTOCOLLNPAIR("sdpos: ", job_recovery_info.sdpos);
- SERIAL_PROTOCOLLNPAIR("print_job_elapsed: ", job_recovery_info.print_job_elapsed);
- }
- else
- SERIAL_PROTOCOLLNPGM("INVALID DATA");
- }
- SERIAL_PROTOCOLLNPGM("---------------------------");
- }
-#endif // DEBUG_POWER_LOSS_RECOVERY
+/**
+ * The enabled state was changed:
+ * - Enabled: Purge the job recovery file
+ * - Disabled: Write the job recovery file
+ */
+void PrintJobRecovery::changed() {
+ if (!enabled)
+ purge();
+ else if (IS_SD_PRINTING())
+ save(true);
+}
/**
* Check for Print Job Recovery during setup()
*
- * If a saved state exists, populate job_recovery_commands with
- * commands to restore the machine state and continue the file.
+ * If a saved state exists send 'M1000 S' to initiate job recovery.
*/
-void check_print_job_recovery() {
- memset(&job_recovery_info, 0, sizeof(job_recovery_info));
- ZERO(job_recovery_commands);
-
- if (!card.cardOK) card.initsd();
-
- if (card.cardOK) {
-
- #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
- SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", int(sizeof(job_recovery_info)));
- #endif
-
- if (card.jobRecoverFileExists()) {
- card.openJobRecoveryFile(true);
- card.loadJobRecoveryInfo();
- card.closeJobRecoveryFile();
- //card.removeJobRecoveryFile();
-
- if (job_recovery_info.valid_head && job_recovery_info.valid_head == job_recovery_info.valid_foot) {
-
- uint8_t ind = 0;
-
- #if HAS_LEVELING
- strcpy_P(job_recovery_commands[ind++], PSTR("M420 S0 Z0")); // Leveling off before G92 or G28
- #endif
-
- strcpy_P(job_recovery_commands[ind++], PSTR("G92.0 Z0")); // Ensure Z is equal to 0
- strcpy_P(job_recovery_commands[ind++], PSTR("G1 Z2")); // Raise Z by 2mm (we hope!)
- strcpy_P(job_recovery_commands[ind++], PSTR("G28 R0"
- #if ENABLED(MARLIN_DEV_MODE)
- " S"
- #elif !IS_KINEMATIC
- " X Y" // Home X and Y for Cartesian
- #endif
- ));
-
- char str_1[16], str_2[16];
-
- #if HAS_LEVELING
- if (job_recovery_info.fade || job_recovery_info.leveling) {
- // Restore leveling state before G92 sets Z
- // This ensures the steppers correspond to the native Z
- dtostrf(job_recovery_info.fade, 1, 1, str_1);
- sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1);
- }
- #endif
-
- #if ENABLED(FWRETRACT)
- for (uint8_t e = 0; e < EXTRUDERS; e++) {
- if (job_recovery_info.retract[e] != 0.0)
- fwretract.current_retract[e] = job_recovery_info.retract[e];
- fwretract.retracted[e] = true;
- }
- fwretract.current_hop = job_recovery_info.retract_hop;
- #endif
-
- dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1);
- dtostrf(job_recovery_info.current_position[E_AXIS]
- #if ENABLED(SAVE_EACH_CMD_MODE)
- - 5
- #endif
- , 1, 3, str_2
- );
- sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E
-
- uint8_t r = job_recovery_info.cmd_queue_index_r, c = job_recovery_info.commands_in_queue;
- while (c--) {
- strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]);
- r = (r + 1) % BUFSIZE;
- }
-
- if (job_recovery_info.sd_filename[0] == '/') job_recovery_info.sd_filename[0] = ' ';
- sprintf_P(job_recovery_commands[ind++], PSTR("M23 %s"), job_recovery_info.sd_filename);
- sprintf_P(job_recovery_commands[ind++], PSTR("M24 S%ld T%ld"), job_recovery_info.sdpos, job_recovery_info.print_job_elapsed);
-
- job_recovery_commands_count = ind;
-
- #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
- debug_print_job_recovery(true);
- #endif
- }
- else {
- if (job_recovery_info.valid_head != job_recovery_info.valid_foot)
- LCD_ALERTMESSAGEPGM("INVALID DATA");
- memset(&job_recovery_info, 0, sizeof(job_recovery_info));
- }
+void PrintJobRecovery::check() {
+ if (enabled) {
+ if (!card.cardOK) card.initsd();
+ if (card.cardOK) {
+ load();
+ if (!valid()) return purge();
+ enqueue_and_echo_commands_P(PSTR("M1000 S"));
}
}
}
+/**
+ * Delete the recovery file and clear the recovery data
+ */
+void PrintJobRecovery::purge() {
+ init();
+ card.removeJobRecoveryFile();
+}
+
+/**
+ * Load the recovery data, if it exists
+ */
+void PrintJobRecovery::load() {
+ if (exists()) {
+ open(true);
+ (void)file.read(&info, sizeof(info));
+ close();
+ }
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ debug(PSTR("Load"));
+ #endif
+}
+
/**
* Save the current machine state to the power-loss recovery file
*/
-void save_job_recovery_info() {
+void PrintJobRecovery::save(const bool force/*=false*/) {
+
#if SAVE_INFO_INTERVAL_MS > 0
- static millis_t next_save_ms; // = 0; // Init on reset
+ static millis_t next_save_ms; // = 0
millis_t ms = millis();
#endif
- if (
- // Save on every command
- #if ENABLED(SAVE_EACH_CMD_MODE)
- true
- #else
- // Save if power loss pin is triggered
- #if PIN_EXISTS(POWER_LOSS)
- READ(POWER_LOSS_PIN) == POWER_LOSS_STATE ||
+
+ if (force
+ #if DISABLED(SAVE_EACH_CMD_MODE) // Always save state when enabled
+ #if PIN_EXISTS(POWER_LOSS) // Save if power loss pin is triggered
+ || READ(POWER_LOSS_PIN) == POWER_LOSS_STATE
#endif
- // Save if interval is elapsed
- #if SAVE_INFO_INTERVAL_MS > 0
- ELAPSED(ms, next_save_ms) ||
+ #if SAVE_INFO_INTERVAL_MS > 0 // Save if interval is elapsed
+ || ELAPSED(ms, next_save_ms)
#endif
- // Save on every new Z height
- (current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS])
+ // Save every time Z is higher than the last call
+ || current_position[Z_AXIS] > info.current_position[Z_AXIS]
#endif
) {
+
#if SAVE_INFO_INTERVAL_MS > 0
next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
#endif
- // Head and foot will match if valid data was saved
- if (!++job_recovery_info.valid_head) ++job_recovery_info.valid_head; // non-zero in sequence
- job_recovery_info.valid_foot = job_recovery_info.valid_head;
+ // Set Head and Foot to matching non-zero values
+ if (!++info.valid_head) ++info.valid_head; // non-zero in sequence
+ //if (!IS_SD_PRINTING()) info.valid_head = 0;
+ info.valid_foot = info.valid_head;
// Machine state
- COPY(job_recovery_info.current_position, current_position);
- job_recovery_info.feedrate = feedrate_mm_s;
+ COPY(info.current_position, current_position);
+ info.feedrate = uint16_t(feedrate_mm_s * 60.0f);
#if HOTENDS > 1
- job_recovery_info.active_hotend = active_extruder;
+ info.active_hotend = active_extruder;
#endif
- COPY(job_recovery_info.target_temperature, thermalManager.target_temperature);
+ COPY(info.target_temperature, thermalManager.target_temperature);
#if HAS_HEATED_BED
- job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed;
+ info.target_temperature_bed = thermalManager.target_temperature_bed;
#endif
#if FAN_COUNT
- COPY(job_recovery_info.fan_speed, fan_speed);
+ COPY(info.fan_speed, fan_speed);
#endif
#if HAS_LEVELING
- job_recovery_info.leveling = planner.leveling_active;
- job_recovery_info.fade = (
+ info.leveling = planner.leveling_active;
+ info.fade = (
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
planner.z_fade_height
#else
@@ -279,35 +177,238 @@ void save_job_recovery_info() {
#endif
#if ENABLED(FWRETRACT)
- COPY(job_recovery_info.retract, fwretract.current_retract);
- job_recovery_info.retract_hop = fwretract.current_hop;
+ COPY(info.retract, fwretract.current_retract);
+ info.retract_hop = fwretract.current_hop;
#endif
// Commands in the queue
- job_recovery_info.cmd_queue_index_r = cmd_queue_index_r;
- job_recovery_info.commands_in_queue = commands_in_queue;
- COPY(job_recovery_info.command_queue, command_queue);
+ info.cmd_queue_index_r = cmd_queue_index_r;
+ info.commands_in_queue = commands_in_queue;
+ COPY(info.command_queue, command_queue);
// Elapsed print job time
- job_recovery_info.print_job_elapsed = print_job_timer.duration();
+ info.print_job_elapsed = print_job_timer.duration();
// SD file position
- card.getAbsFilename(job_recovery_info.sd_filename);
- job_recovery_info.sdpos = card.getIndex();
+ card.getAbsFilename(info.sd_filename);
+ info.sdpos = card.getIndex();
- #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
- SERIAL_PROTOCOLLNPGM("Saving...");
- debug_print_job_recovery(false);
- #endif
+ write();
- card.openJobRecoveryFile(false);
- (void)card.saveJobRecoveryInfo();
-
- // If power-loss pin was triggered, write just once then kill
+ // KILL now if the power-loss pin was triggered
#if PIN_EXISTS(POWER_LOSS)
- if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(MSG_POWER_LOSS_RECOVERY);
+ if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(MSG_OUTAGE_RECOVERY);
#endif
}
}
+/**
+ * Save the recovery info the recovery file
+ */
+void PrintJobRecovery::write() {
+
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ debug(PSTR("Write"));
+ #endif
+
+ open(false);
+ file.seekSet(0);
+ const int16_t ret = file.write(&info, sizeof(info));
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ if (ret == -1) SERIAL_ECHOLNPGM("Power-loss file write failed.");
+ #endif
+}
+
+/**
+ * Resume the saved print job
+ */
+void PrintJobRecovery::resume() {
+
+ #define RECOVERY_ZRAISE 2
+
+ #if HAS_LEVELING
+ // Make sure leveling is off before any G92 and G28
+ gcode.process_subcommands_now_P(PSTR("M420 S0 Z0"));
+ #endif
+
+ // Set Z to 0, raise Z by 2mm, and Home (XY only for Cartesian) with no raise
+ // (Only do simulated homing in Marlin Dev Mode.)
+ gcode.process_subcommands_now_P(PSTR("G92.0 Z0|G1 Z" STRINGIFY(RECOVERY_ZRAISE) "|G28 R0"
+ #if ENABLED(MARLIN_DEV_MODE)
+ " S"
+ #elif !IS_KINEMATIC
+ " X Y"
+ #endif
+ ));
+
+ // Pretend that all axes are homed
+ axis_homed = axis_known_position = xyz_bits;
+
+ char cmd[40], str_1[16], str_2[16];
+
+ // Select the previously active tool (with no_move)
+ #if EXTRUDERS > 1
+ sprintf_P(cmd, PSTR("T%i S"), info.active_hotend);
+ gcode.process_subcommands_now(cmd);
+ #endif
+
+ #if HAS_HEATED_BED
+ const int16_t bt = info.target_temperature_bed;
+ if (bt) {
+ // Restore the bed temperature
+ sprintf_P(cmd, PSTR("M190 S%i"), bt);
+ gcode.process_subcommands_now(cmd);
+ }
+ #endif
+
+ // Restore all hotend temperatures
+ HOTEND_LOOP() {
+ const int16_t et = info.target_temperature[e];
+ if (et) {
+ #if HOTENDS > 1
+ sprintf_P(cmd, PSTR("T%i"), e);
+ gcode.process_subcommands_now(cmd);
+ #endif
+ sprintf_P(cmd, PSTR("M109 S%i"), et);
+ gcode.process_subcommands_now(cmd);
+ }
+ }
+
+ // Restore print cooling fan speeds
+ for (uint8_t i = 0; i < FAN_COUNT; i++) {
+ uint8_t f = info.fan_speed[i];
+ if (f) {
+ sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f);
+ gcode.process_subcommands_now(cmd);
+ }
+ }
+
+ // Restore retract and hop state
+ #if ENABLED(FWRETRACT)
+ for (uint8_t e = 0; e < EXTRUDERS; e++) {
+ if (info.retract[e] != 0.0)
+ fwretract.current_retract[e] = info.retract[e];
+ fwretract.retracted[e] = true;
+ }
+ fwretract.current_hop = info.retract_hop;
+ #endif
+
+ #if HAS_LEVELING
+ // Restore leveling state before 'G92 Z' to ensure
+ // the Z stepper count corresponds to the native Z.
+ if (info.fade || info.leveling) {
+ dtostrf(info.fade, 1, 1, str_1);
+ sprintf_P(cmd, PSTR("M420 S%i Z%s"), int(info.leveling), str_1);
+ gcode.process_subcommands_now(cmd);
+ }
+ #endif
+
+ // Restore Z (plus raise) and E positions with G92.0
+ dtostrf(info.current_position[Z_AXIS] + RECOVERY_ZRAISE, 1, 3, str_1);
+ dtostrf(info.current_position[E_AXIS]
+ #if ENABLED(SAVE_EACH_CMD_MODE)
+ - 5 // Extra extrusion on restart
+ #endif
+ , 1, 3, str_2
+ );
+ sprintf_P(cmd, PSTR("G92.0 Z%s E%s"), str_1, str_2);
+ gcode.process_subcommands_now(cmd);
+
+ // Move back to the saved XY
+ dtostrf(info.current_position[X_AXIS], 1, 3, str_1);
+ dtostrf(info.current_position[Y_AXIS], 1, 3, str_2);
+ sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"), str_1, str_2);
+ gcode.process_subcommands_now(cmd);
+
+ // Move back to the saved Z
+ dtostrf(info.current_position[Z_AXIS], 1, 3, str_1);
+ sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1);
+ gcode.process_subcommands_now(cmd);
+
+ // Restore the feedrate
+ sprintf_P(cmd, PSTR("G1 F%d"), info.feedrate);
+ gcode.process_subcommands_now(cmd);
+
+ // Process commands from the old pending queue
+ uint8_t r = info.cmd_queue_index_r, c = info.commands_in_queue;
+ for (; c--; r = (r + 1) % BUFSIZE)
+ gcode.process_subcommands_now(info.command_queue[r]);
+
+ // Resume the SD file from the last position
+ char *fn = info.sd_filename;
+ while (*fn == '/') fn++;
+ sprintf_P(cmd, PSTR("M23 %s"), fn);
+ gcode.process_subcommands_now(cmd);
+ sprintf_P(cmd, PSTR("M24 S%ld T%ld"), info.sdpos, info.print_job_elapsed);
+ gcode.process_subcommands_now(cmd);
+}
+
+#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+
+ void PrintJobRecovery::debug(PGM_P const prefix) {
+ serialprintPGM(prefix);
+ SERIAL_ECHOPAIR(" Job Recovery Info...\nvalid_head:", int(info.valid_head));
+ SERIAL_ECHOLNPAIR(" valid_foot:", int(info.valid_foot));
+ if (info.valid_head) {
+ if (info.valid_head == info.valid_foot) {
+ SERIAL_ECHOPGM("current_position: ");
+ LOOP_XYZE(i) {
+ SERIAL_ECHO(info.current_position[i]);
+ if (i < E_AXIS) SERIAL_CHAR(',');
+ }
+ SERIAL_EOL();
+ SERIAL_ECHOLNPAIR("feedrate: ", info.feedrate);
+
+ #if HOTENDS > 1
+ SERIAL_ECHOLNPAIR("active_hotend: ", int(info.active_hotend));
+ #endif
+
+ SERIAL_ECHOPGM("target_temperature: ");
+ HOTEND_LOOP() {
+ SERIAL_ECHO(info.target_temperature[e]);
+ if (e < HOTENDS - 1) SERIAL_CHAR(',');
+ }
+ SERIAL_EOL();
+
+ #if HAS_HEATED_BED
+ SERIAL_ECHOLNPAIR("target_temperature_bed: ", info.target_temperature_bed);
+ #endif
+
+ #if FAN_COUNT
+ SERIAL_ECHOPGM("fan_speed: ");
+ for (int8_t i = 0; i < FAN_COUNT; i++) {
+ SERIAL_ECHO(int(info.fan_speed[i]));
+ if (i < FAN_COUNT - 1) SERIAL_CHAR(',');
+ }
+ SERIAL_EOL();
+ #endif
+
+ #if HAS_LEVELING
+ SERIAL_ECHOPAIR("leveling: ", int(info.leveling));
+ SERIAL_ECHOLNPAIR(" fade: ", int(info.fade));
+ #endif
+ #if ENABLED(FWRETRACT)
+ SERIAL_ECHOPGM("retract: ");
+ for (int8_t e = 0; e < EXTRUDERS; e++) {
+ SERIAL_ECHO(info.retract[e]);
+ if (e < EXTRUDERS - 1) SERIAL_CHAR(',');
+ }
+ SERIAL_EOL();
+ SERIAL_ECHOLNPAIR("retract_hop: ", info.retract_hop);
+ #endif
+ SERIAL_ECHOLNPAIR("cmd_queue_index_r: ", int(info.cmd_queue_index_r));
+ SERIAL_ECHOLNPAIR("commands_in_queue: ", int(info.commands_in_queue));
+ for (uint8_t i = 0; i < info.commands_in_queue; i++) SERIAL_ECHOLNPAIR("> ", info.command_queue[i]);
+ SERIAL_ECHOLNPAIR("sd_filename: ", info.sd_filename);
+ SERIAL_ECHOLNPAIR("sdpos: ", info.sdpos);
+ SERIAL_ECHOLNPAIR("print_job_elapsed: ", info.print_job_elapsed);
+ }
+ else
+ SERIAL_ECHOLNPGM("INVALID DATA");
+ }
+ SERIAL_ECHOLNPGM("---");
+ }
+
+#endif // DEBUG_POWER_LOSS_RECOVERY
+
#endif // POWER_LOSS_RECOVERY
diff --git a/Marlin/src/feature/power_loss_recovery.h b/Marlin/src/feature/power_loss_recovery.h
index d5a0aea69..0de0457c0 100644
--- a/Marlin/src/feature/power_loss_recovery.h
+++ b/Marlin/src/feature/power_loss_recovery.h
@@ -26,7 +26,6 @@
*/
#include "../sd/cardreader.h"
-#include "../core/millis_t.h"
#include "../inc/MarlinConfigPre.h"
#define SAVE_INFO_INTERVAL_MS 0
@@ -37,7 +36,9 @@ typedef struct {
uint8_t valid_head;
// Machine state
- float current_position[NUM_AXIS], feedrate;
+ float current_position[NUM_AXIS];
+
+ uint16_t feedrate;
#if HOTENDS > 1
uint8_t active_hotend;
@@ -74,26 +75,45 @@ typedef struct {
millis_t print_job_elapsed;
uint8_t valid_foot;
+
} job_recovery_info_t;
-extern job_recovery_info_t job_recovery_info;
+class PrintJobRecovery {
+ public:
+ static SdFile file;
+ static job_recovery_info_t info;
-enum JobRecoveryPhase : unsigned char {
- JOB_RECOVERY_IDLE,
- JOB_RECOVERY_MAYBE,
- JOB_RECOVERY_YES,
- JOB_RECOVERY_DONE
+ static void init();
+
+ static bool enabled;
+ static void enable(const bool onoff);
+ static void changed();
+
+ static void check();
+ static void resume();
+
+ static inline bool exists() { return card.jobRecoverFileExists(); }
+ static inline void open(const bool read) { card.openJobRecoveryFile(read); }
+ static inline void close() { file.close(); }
+
+ static void purge();
+ static void load();
+ static void save(const bool force=
+ #if ENABLED(SAVE_EACH_CMD_MODE)
+ true
+ #else
+ false
+ #endif
+ );
+
+ static inline bool valid() { return info.valid_head && info.valid_head == info.valid_foot; }
+
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ static void debug(PGM_P const prefix);
+ #endif
+
+ private:
+ static void write();
};
-extern JobRecoveryPhase job_recovery_phase;
-#if HAS_LEVELING
- #define APPEND_CMD_COUNT 9
-#else
- #define APPEND_CMD_COUNT 7
-#endif
-
-extern char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
-extern uint8_t job_recovery_commands_count;
-
-void check_print_job_recovery();
-void save_job_recovery_info();
+extern PrintJobRecovery recovery;
diff --git a/Marlin/src/gcode/feature/powerloss/M1000.cpp b/Marlin/src/gcode/feature/powerloss/M1000.cpp
new file mode 100644
index 000000000..21862aa55
--- /dev/null
+++ b/Marlin/src/gcode/feature/powerloss/M1000.cpp
@@ -0,0 +1,65 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+
+#include "../../gcode.h"
+#include "../../../feature/power_loss_recovery.h"
+#include "../../../module/motion.h"
+#include "../../../lcd/ultralcd.h"
+
+void menu_job_recovery();
+
+#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+
+ inline void plr_error(PGM_P const prefix) {
+ SERIAL_ECHO_START();
+ serialprintPGM(prefix);
+ SERIAL_ECHOLNPGM(" Power-Loss Recovery Data");
+ }
+
+#endif
+
+/**
+ * M1000: Resume from power-loss (undocumented)
+ * - With 'S' go to the Resume/Cancel menu
+ * - With no parameters, run recovery commands
+ */
+void GcodeSuite::M1000() {
+
+ if (recovery.valid()) {
+ if (parser.seen('S'))
+ ui.goto_screen(menu_job_recovery);
+ else
+ recovery.resume();
+ }
+ else {
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ plr_error(recovery.info.valid_head ? PSTR("No") : PSTR("Invalid"));
+ #endif
+ }
+
+}
+
+#endif // POWER_LOSS_RECOVERY
diff --git a/Marlin/src/gcode/feature/powerloss/M413.cpp b/Marlin/src/gcode/feature/powerloss/M413.cpp
new file mode 100644
index 000000000..b63645a2a
--- /dev/null
+++ b/Marlin/src/gcode/feature/powerloss/M413.cpp
@@ -0,0 +1,58 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+
+#include "../../gcode.h"
+#include "../../../feature/power_loss_recovery.h"
+#include "../../../module/motion.h"
+#include "../../../lcd/ultralcd.h"
+
+/**
+ * M413: Enable / Disable power-loss recovery
+ *
+ * Parameters
+ * S[bool] - Flag to enable / disable.
+ * If omitted, report current state.
+ */
+void GcodeSuite::M413() {
+
+ if (parser.seen('S'))
+ recovery.enable(parser.value_bool());
+ else {
+ SERIAL_ECHO_START();
+ SERIAL_ECHOPGM("Power-loss recovery ");
+ serialprintln_onoff(recovery.enabled);
+ }
+
+ #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
+ if (parser.seen('R') || parser.seen('L')) recovery.load();
+ if (parser.seen('W')) recovery.save(true);
+ if (parser.seen('P')) recovery.purge();
+ if (parser.seen('E')) serialprintPGM(recovery.exists() ? PSTR("BIN Exists\n") : PSTR("No BIN\n"));
+ if (parser.seen('V')) serialprintPGM(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n"));
+ #endif
+}
+
+#endif // POWER_LOSS_RECOVERY
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index 0124d7b04..e1e3f1065 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -695,6 +695,11 @@ void GcodeSuite::process_parsed_command(
case 999: M999(); break; // M999: Restart after being Stopped
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ case 413: M413(); break; // M413: Enable/disable/query Power-Loss Recovery
+ case 1000: M1000(); break; // M1000: Resume from power-loss
+ #endif
+
default: parser.unknown_command_error(); break;
}
break;
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 375a6f9ae..42671b624 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -196,7 +196,8 @@
* M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
* M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
* M410 - Quickstop. Abort all planned moves.
- * M412 - Enable / Disable filament runout detection. (Requires FILAMENT_RUNOUT_SENSOR)
+ * M412 - Enable / Disable Filament Runout Detection. (Requires FILAMENT_RUNOUT_SENSOR)
+ * M413 - Enable / Disable Power-Loss Recovery. (Requires POWER_LOSS_RECOVERY)
* M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
* M421 - Set a single Z coordinate in the Mesh Leveling grid. X Y Z (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, or AUTO_BED_LEVELING_UBL)
* M422 - Set Z Stepper automatic alignment position using probe. X Y A (Requires Z_STEPPER_AUTO_ALIGN)
@@ -822,6 +823,11 @@ private:
static void M999();
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ static void M413();
+ static void M1000();
+ #endif
+
static void T(const uint8_t tool_index);
};
diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp
index 70c2b3e0c..4cf508dca 100644
--- a/Marlin/src/gcode/queue.cpp
+++ b/Marlin/src/gcode/queue.cpp
@@ -809,22 +809,6 @@ inline void get_serial_commands() {
}
}
- #if ENABLED(POWER_LOSS_RECOVERY)
-
- inline bool drain_job_recovery_commands() {
- static uint8_t job_recovery_commands_index = 0; // Resets on reboot
- if (job_recovery_commands_count) {
- if (_enqueuecommand(job_recovery_commands[job_recovery_commands_index])) {
- ++job_recovery_commands_index;
- if (!--job_recovery_commands_count) job_recovery_phase = JOB_RECOVERY_DONE;
- }
- return true;
- }
- return false;
- }
-
- #endif
-
#endif // SDSUPPORT
/**
@@ -840,11 +824,6 @@ void get_available_commands() {
get_serial_commands();
- #if ENABLED(POWER_LOSS_RECOVERY)
- // Commands for power-loss recovery take precedence
- if (job_recovery_phase == JOB_RECOVERY_YES && drain_job_recovery_commands()) return;
- #endif
-
#if ENABLED(SDSUPPORT)
get_sdcard_commands();
#endif
@@ -890,7 +869,7 @@ void advance_command_queue() {
else {
gcode.process_next_command();
#if ENABLED(POWER_LOSS_RECOVERY)
- if (card.cardOK && IS_SD_PRINTING()) save_job_recovery_info();
+ if (IS_SD_PRINTING()) recovery.save();
#endif
}
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index 40ecd5412..4d4cf07a0 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -1629,7 +1629,7 @@
// If platform requires early initialization of watchdog to properly boot
#define EARLY_WATCHDOG (ENABLED(USE_WATCHDOG) && defined(ARDUINO_ARCH_SAM))
-#define USE_EXECUTE_COMMANDS_IMMEDIATE (ENABLED(G29_RETRY_AND_RECOVER) || ENABLED(GCODE_MACROS))
+#define USE_EXECUTE_COMMANDS_IMMEDIATE (ENABLED(G29_RETRY_AND_RECOVER) || ENABLED(GCODE_MACROS) || ENABLED(POWER_LOSS_RECOVERY))
#if ENABLED(Z_TRIPLE_STEPPER_DRIVERS)
#define Z_STEPPER_COUNT 3
diff --git a/Marlin/src/lcd/language/language_cz.h b/Marlin/src/lcd/language/language_cz.h
index a3514ac74..e2a9256b8 100644
--- a/Marlin/src/lcd/language/language_cz.h
+++ b/Marlin/src/lcd/language/language_cz.h
@@ -253,7 +253,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pozastavit tisk")
#define MSG_RESUME_PRINT _UxGT("Obnovit tisk")
#define MSG_STOP_PRINT _UxGT("Zastavit tisk")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Obnova vypadku")
+#define MSG_OUTAGE_RECOVERY _UxGT("Obnova vypadku")
#define MSG_CARD_MENU _UxGT("Tisknout z SD")
#define MSG_NO_CARD _UxGT("Žádná SD karta")
#define MSG_DWELL _UxGT("Uspáno...")
diff --git a/Marlin/src/lcd/language/language_de.h b/Marlin/src/lcd/language/language_de.h
index e0e24462c..f218efdd0 100644
--- a/Marlin/src/lcd/language/language_de.h
+++ b/Marlin/src/lcd/language/language_de.h
@@ -268,7 +268,7 @@
#define MSG_PAUSE_PRINT _UxGT("SD-Druck pausieren")
#define MSG_RESUME_PRINT _UxGT("SD-Druck fortsetzen")
#define MSG_STOP_PRINT _UxGT("SD-Druck abbrechen")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Wiederh. n. Stroma.")
+#define MSG_OUTAGE_RECOVERY _UxGT("Wiederh. n. Stroma.")
#define MSG_CARD_MENU _UxGT("Druck v. SD-Karte")
#define MSG_NO_CARD _UxGT("Keine SD-Karte")
#define MSG_DWELL _UxGT("Warten...")
diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h
index 12f131dc3..1553302fe 100644
--- a/Marlin/src/lcd/language/language_en.h
+++ b/Marlin/src/lcd/language/language_en.h
@@ -728,8 +728,8 @@
#ifndef MSG_STOP_PRINT
#define MSG_STOP_PRINT _UxGT("Stop print")
#endif
-#ifndef MSG_POWER_LOSS_RECOVERY
- #define MSG_POWER_LOSS_RECOVERY _UxGT("Power-Loss Recovery")
+#ifndef MSG_OUTAGE_RECOVERY
+ #define MSG_OUTAGE_RECOVERY _UxGT("Outage Recovery")
#endif
#ifndef MSG_CARD_MENU
#define MSG_CARD_MENU _UxGT("Print from SD")
diff --git a/Marlin/src/lcd/language/language_it.h b/Marlin/src/lcd/language/language_it.h
index a380952fd..4dbabb3b7 100644
--- a/Marlin/src/lcd/language/language_it.h
+++ b/Marlin/src/lcd/language/language_it.h
@@ -266,7 +266,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pausa stampa")
#define MSG_RESUME_PRINT _UxGT("Riprendi stampa")
#define MSG_STOP_PRINT _UxGT("Arresta stampa")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Ripresa da PowerLoss")
+#define MSG_OUTAGE_RECOVERY _UxGT("Ripresa da PowerLoss")
#define MSG_CARD_MENU _UxGT("Stampa da SD")
#define MSG_NO_CARD _UxGT("SD non presente")
#define MSG_DWELL _UxGT("Sospensione...")
diff --git a/Marlin/src/lcd/language/language_ko_KR.h b/Marlin/src/lcd/language/language_ko_KR.h
index aee4b4962..d6e9023b1 100644
--- a/Marlin/src/lcd/language/language_ko_KR.h
+++ b/Marlin/src/lcd/language/language_ko_KR.h
@@ -259,7 +259,7 @@
#define MSG_PAUSE_PRINT _UxGT("일시정지")
#define MSG_RESUME_PRINT _UxGT("재시작")
#define MSG_STOP_PRINT _UxGT("출력중지")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Power-Loss Recovery")
+#define MSG_OUTAGE_RECOVERY _UxGT("Outage Recovery")
#define MSG_CARD_MENU _UxGT("SD 카드출력")
#define MSG_NO_CARD _UxGT("SD 카드없음")
#define MSG_DWELL _UxGT("슬립모드...")
diff --git a/Marlin/src/lcd/language/language_pt-br.h b/Marlin/src/lcd/language/language_pt-br.h
index 986c9556c..4e3839b66 100644
--- a/Marlin/src/lcd/language/language_pt-br.h
+++ b/Marlin/src/lcd/language/language_pt-br.h
@@ -273,7 +273,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pausar impressão")
#define MSG_RESUME_PRINT _UxGT("Resumir impressão")
#define MSG_STOP_PRINT _UxGT("Parar impressão")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Recuperar Impressão")
+#define MSG_OUTAGE_RECOVERY _UxGT("Recuperar Impressão")
#define MSG_CARD_MENU _UxGT("Imprimir do SD")
#define MSG_NO_CARD _UxGT("Sem cartão SD")
#define MSG_DWELL _UxGT("Dormindo...")
diff --git a/Marlin/src/lcd/language/language_sk.h b/Marlin/src/lcd/language/language_sk.h
index 1d133918a..4a6efaec4 100644
--- a/Marlin/src/lcd/language/language_sk.h
+++ b/Marlin/src/lcd/language/language_sk.h
@@ -278,7 +278,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pozastaviť tlač")
#define MSG_RESUME_PRINT _UxGT("Obnoviť tlač")
#define MSG_STOP_PRINT _UxGT("Zastaviť tlač")
-#define MSG_POWER_LOSS_RECOVERY _UxGT("Obnova po výp. nap.")
+#define MSG_OUTAGE_RECOVERY _UxGT("Obnova po výp. nap.")
#define MSG_CARD_MENU _UxGT("Tlačiť z SD")
#define MSG_NO_CARD _UxGT("Žiadna SD karta")
#define MSG_DWELL _UxGT("Spím...")
diff --git a/Marlin/src/lcd/menu/menu_configuration.cpp b/Marlin/src/lcd/menu/menu_configuration.cpp
index ea2a1c3c0..480c2829d 100644
--- a/Marlin/src/lcd/menu/menu_configuration.cpp
+++ b/Marlin/src/lcd/menu/menu_configuration.cpp
@@ -36,6 +36,10 @@
#include "../../feature/runout.h"
#endif
+#if ENABLED(POWER_LOSS_RECOVERY)
+ #include "../../feature/power_loss_recovery.h"
+#endif
+
#define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST)
void menu_advanced_settings();
@@ -350,6 +354,10 @@ void menu_configuration() {
MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset);
#endif
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ MENU_ITEM_EDIT_CALLBACK(bool, MSG_OUTAGE_RECOVERY, &recovery.enabled, recovery.changed);
+ #endif
+
#if DISABLED(SLIM_LCD_MENUS)
// Preheat configurations
MENU_ITEM(submenu, MSG_PREHEAT_1_SETTINGS, menu_preheat_material1_settings);
diff --git a/Marlin/src/lcd/menu/menu_job_recovery.cpp b/Marlin/src/lcd/menu/menu_job_recovery.cpp
index c0a38525d..18063b224 100644
--- a/Marlin/src/lcd/menu/menu_job_recovery.cpp
+++ b/Marlin/src/lcd/menu/menu_job_recovery.cpp
@@ -34,58 +34,8 @@
#include "../../feature/power_loss_recovery.h"
static void lcd_power_loss_recovery_resume() {
- char cmd[20];
-
- // Return to status now
ui.return_to_status();
-
- // Turn leveling off and home
- enqueue_and_echo_commands_P(PSTR("M420 S0\nG28 R0"
- #if ENABLED(MARLIN_DEV_MODE)
- " S"
- #elif !IS_KINEMATIC
- " X Y"
- #endif
- ));
-
- #if HAS_HEATED_BED
- const int16_t bt = job_recovery_info.target_temperature_bed;
- if (bt) {
- // Restore the bed temperature
- sprintf_P(cmd, PSTR("M190 S%i"), bt);
- enqueue_and_echo_command(cmd);
- }
- #endif
-
- // Restore all hotend temperatures
- HOTEND_LOOP() {
- const int16_t et = job_recovery_info.target_temperature[e];
- if (et) {
- #if HOTENDS > 1
- sprintf_P(cmd, PSTR("T%i"), e);
- enqueue_and_echo_command(cmd);
- #endif
- sprintf_P(cmd, PSTR("M109 S%i"), et);
- enqueue_and_echo_command(cmd);
- }
- }
-
- #if HOTENDS > 1
- sprintf_P(cmd, PSTR("T%i"), job_recovery_info.active_hotend);
- enqueue_and_echo_command(cmd);
- #endif
-
- // Restore print cooling fan speeds
- for (uint8_t i = 0; i < FAN_COUNT; i++) {
- uint8_t f = job_recovery_info.fan_speed[i];
- if (f) {
- sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f);
- enqueue_and_echo_command(cmd);
- }
- }
-
- // Start draining the job recovery command queue
- job_recovery_phase = JOB_RECOVERY_YES;
+ enqueue_and_echo_commands_P(PSTR("M1000"));
}
static void lcd_power_loss_recovery_cancel() {
@@ -97,7 +47,7 @@ static void lcd_power_loss_recovery_cancel() {
void menu_job_recovery() {
ui.defer_status_screen(true);
START_MENU();
- STATIC_ITEM(MSG_POWER_LOSS_RECOVERY);
+ STATIC_ITEM(MSG_OUTAGE_RECOVERY);
MENU_ITEM(function, MSG_RESUME_PRINT, lcd_power_loss_recovery_resume);
MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel);
END_MENU();
diff --git a/Marlin/src/lcd/menu/menu_main.cpp b/Marlin/src/lcd/menu/menu_main.cpp
index e3332d538..fdddc95b1 100644
--- a/Marlin/src/lcd/menu/menu_main.cpp
+++ b/Marlin/src/lcd/menu/menu_main.cpp
@@ -65,7 +65,7 @@
#if ENABLED(MENU_ADDAUTOSTART)
- void lcd_autostart_sd() { card.beginautostart(); }
+ inline void lcd_autostart_sd() { card.beginautostart(); }
#endif
diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp
index 8baab7e91..5150264bc 100644
--- a/Marlin/src/lcd/ultralcd.cpp
+++ b/Marlin/src/lcd/ultralcd.cpp
@@ -659,13 +659,6 @@ void MarlinUI::update() {
#endif // SDSUPPORT && SD_DETECT_PIN
- #if ENABLED(POWER_LOSS_RECOVERY)
- if (job_recovery_commands_count && job_recovery_phase == JOB_RECOVERY_IDLE) {
- goto_screen(menu_job_recovery);
- job_recovery_phase = JOB_RECOVERY_MAYBE; // Waiting for a response
- }
- #endif
-
const millis_t ms = millis();
if (ELAPSED(ms, next_lcd_update_ms)
#if HAS_GRAPHICAL_LCD
diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp
index 3919f99ea..0844b8af6 100644
--- a/Marlin/src/module/configuration_store.cpp
+++ b/Marlin/src/module/configuration_store.cpp
@@ -37,7 +37,7 @@
*/
// Change EEPROM version if the structure changes
-#define EEPROM_VERSION "V62"
+#define EEPROM_VERSION "V63"
#define EEPROM_OFFSET 100
// Check the integrity of data offsets.
@@ -82,6 +82,11 @@
#endif
#include "../feature/fwretract.h"
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+ #include "../feature/power_loss_recovery.h"
+#endif
+
#include "../feature/pause.h"
#if EXTRUDERS > 1
@@ -221,6 +226,11 @@ typedef struct SettingsDataStruct {
//
int16_t lcd_contrast; // M250 C
+ //
+ // POWER_LOSS_RECOVERY
+ //
+ bool recovery_enabled; // M413 S
+
//
// FWRETRACT
//
@@ -269,7 +279,7 @@ typedef struct SettingsDataStruct {
// Tool-change settings
//
#if EXTRUDERS > 1
- toolchange_settings_t toolchange_settings; // M217 S P R
+ toolchange_settings_t toolchange_settings; // M217 S P R
#endif
} SettingsData;
@@ -746,6 +756,22 @@ void MarlinSettings::postprocess() {
EEPROM_WRITE(lcd_contrast);
}
+ //
+ // Power-Loss Recovery
+ //
+ {
+ _FIELD_TEST(recovery_enabled);
+
+ const bool recovery_enabled =
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ recovery.enabled
+ #else
+ true
+ #endif
+ ;
+ EEPROM_WRITE(recovery_enabled);
+ }
+
//
// Firmware Retraction
//
@@ -1387,6 +1413,20 @@ void MarlinSettings::postprocess() {
#endif
}
+ //
+ // Power-Loss Recovery
+ //
+ {
+ _FIELD_TEST(recovery_enabled);
+
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ EEPROM_READ(recovery.enabled);
+ #else
+ bool recovery_enabled;
+ EEPROM_READ(recovery_enabled);
+ #endif
+ }
+
//
// Firmware Retraction
//
@@ -2075,6 +2115,10 @@ void MarlinSettings::reset(PORTARG_SOLO) {
ui.set_contrast(DEFAULT_LCD_CONTRAST);
#endif
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ recovery.enable(true);
+ #endif
+
#if ENABLED(FWRETRACT)
fwretract.reset();
#endif
@@ -2643,6 +2687,15 @@ void MarlinSettings::reset(PORTARG_SOLO) {
SERIAL_ECHOLNPAIR_P(port, " M250 C", ui.contrast);
#endif
+ #if ENABLED(POWER_LOSS_RECOVERY)
+ if (!forReplay) {
+ CONFIG_ECHO_START;
+ SERIAL_ECHOLNPGM_P(port, "Power-Loss Recovery:");
+ }
+ CONFIG_ECHO_START;
+ SERIAL_ECHOLNPAIR_P(port, " M413 S", int(recovery.enabled));
+ #endif
+
#if ENABLED(FWRETRACT)
if (!forReplay) {
@@ -2683,7 +2736,7 @@ void MarlinSettings::reset(PORTARG_SOLO) {
#if HAS_BED_PROBE
if (!forReplay) {
CONFIG_ECHO_START;
- SERIAL_ECHOPGM_P(port, "Z-Probe Offset (mm):");
+ SERIAL_ECHOPGM_P(port, "Z-Probe Offset");
SAY_UNITS_P(port, true);
}
CONFIG_ECHO_START;
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index 9c86f48fa..1610f2ff4 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -478,7 +478,7 @@ void CardReader::openFile(char * const path, const bool read, const bool subcall
void CardReader::removeFile(const char * const name) {
if (!cardOK) return;
- stopSDPrint();
+ //stopSDPrint();
SdFile *curDir;
const char * const fname = diveToFile(curDir, name, false);
@@ -549,7 +549,7 @@ void CardReader::checkautostart() {
if (cardOK
#if ENABLED(POWER_LOSS_RECOVERY)
- && !jobRecoverFileExists() // Don't run auto#.g when a resume file exists
+ && !recovery.valid() // Don't run auto#.g when a resume file exists
#endif
) {
char autoname[8];
@@ -577,6 +577,7 @@ void CardReader::closefile(const bool store_location) {
file.sync();
file.close();
saving = logging = false;
+ sdpos = 0;
#if ENABLED(EMERGENCY_PARSER)
emergency_parser.enable();
#endif
@@ -622,8 +623,12 @@ uint16_t CardReader::getnrfilenames() {
}
/**
- * Dive to the given file path, with optional echo.
- * On exit set curDir and return the name part of the path.
+ * Dive to the given DOS 8.3 file path, with optional echo of the dive paths.
+ *
+ * On exit, curDir contains an SdFile reference to the file's directory.
+ *
+ * Returns a pointer to the last segment (filename) of the given DOS 8.3 path.
+ *
* A NULL result indicates an unrecoverable error.
*/
const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) {
@@ -993,12 +998,18 @@ void CardReader::printingHasFinished() {
#if ENABLED(POWER_LOSS_RECOVERY)
- char job_recovery_file_name[4] = "bin";
+ constexpr char job_recovery_file_name[4] = "BIN";
+
+ bool CardReader::jobRecoverFileExists() {
+ const bool exists = recovery.file.open(&root, job_recovery_file_name, O_READ);
+ if (exists) recovery.file.close();
+ return exists;
+ }
void CardReader::openJobRecoveryFile(const bool read) {
if (!cardOK) return;
- if (jobRecoveryFile.isOpen()) return;
- if (!jobRecoveryFile.open(&root, job_recovery_file_name, read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC)) {
+ if (recovery.file.isOpen()) return;
+ if (!recovery.file.open(&root, job_recovery_file_name, read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC)) {
SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name);
SERIAL_PROTOCOLCHAR('.');
SERIAL_EOL();
@@ -1007,31 +1018,12 @@ void CardReader::printingHasFinished() {
SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name);
}
- void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); }
-
- bool CardReader::jobRecoverFileExists() {
- const bool exists = jobRecoveryFile.open(&root, job_recovery_file_name, O_READ);
- if (exists) jobRecoveryFile.close();
- return exists;
- }
-
- int16_t CardReader::saveJobRecoveryInfo() {
- jobRecoveryFile.seekSet(0);
- const int16_t ret = jobRecoveryFile.write(&job_recovery_info, sizeof(job_recovery_info));
- #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
- if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed.");
- #endif
- return ret;
- }
-
- int16_t CardReader::loadJobRecoveryInfo() {
- return jobRecoveryFile.read(&job_recovery_info, sizeof(job_recovery_info));
- }
-
+ // Removing the job recovery file currently requires closing
+ // the file being printed, so during SD printing the file should
+ // be zeroed and written instead of deleted.
void CardReader::removeJobRecoveryFile() {
- job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0;
if (jobRecoverFileExists()) {
- closefile();
+ //closefile();
removeFile(job_recovery_file_name);
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
SERIAL_PROTOCOLPGM("Power-loss file delete");
diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h
index 82244261d..c8f114822 100644
--- a/Marlin/src/sd/cardreader.h
+++ b/Marlin/src/sd/cardreader.h
@@ -106,11 +106,8 @@ public:
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
- void openJobRecoveryFile(const bool read);
- void closeJobRecoveryFile();
bool jobRecoverFileExists();
- int16_t saveJobRecoveryInfo();
- int16_t loadJobRecoveryInfo();
+ void openJobRecoveryFile(const bool read);
void removeJobRecoveryFile();
#endif
@@ -217,10 +214,6 @@ private:
SdVolume volume;
SdFile file;
- #if ENABLED(POWER_LOSS_RECOVERY)
- SdFile jobRecoveryFile;
- #endif
-
#define SD_PROCEDURE_DEPTH 1
#define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH + MAX_DIR_DEPTH + 1)
uint8_t file_subcall_ctr;