diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp index 38e5302ea8..3c9fbc6667 100644 --- a/Marlin/src/Marlin.cpp +++ b/Marlin/src/Marlin.cpp @@ -897,7 +897,7 @@ void setup() { #endif #if ENABLED(POWER_LOSS_RECOVERY) - do_print_job_recovery(); + check_print_job_recovery(); #endif #if ENABLED(USE_WATCHDOG) // Reinit watchdog after HAL_get_reset_source call @@ -937,6 +937,9 @@ void loop() { for (uint8_t i = 0; i < FAN_COUNT; i++) fanSpeeds[i] = 0; #endif wait_for_heatup = false; + #if ENABLED(POWER_LOSS_RECOVERY) + card.removeJobRecoveryFile(); + #endif } #endif // SDSUPPORT && ULTIPANEL diff --git a/Marlin/src/feature/power_loss_recovery.cpp b/Marlin/src/feature/power_loss_recovery.cpp index 903a1471cf..eace4f7df9 100644 --- a/Marlin/src/feature/power_loss_recovery.cpp +++ b/Marlin/src/feature/power_loss_recovery.cpp @@ -43,14 +43,14 @@ 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]; - -// Private -static char sd_filename[MAXPATHNAMELENGTH]; +// Extern +extern uint8_t active_extruder, commands_in_queue, cmd_queue_index_r; #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) void debug_print_job_recovery(const bool recovery) { - SERIAL_PROTOCOLPAIR("valid_head:", (int)job_recovery_info.valid_head); - SERIAL_PROTOCOLLNPAIR(" valid_foot:", (int)job_recovery_info.valid_foot); + 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: "); @@ -60,6 +60,11 @@ static char sd_filename[MAXPATHNAMELENGTH]; } SERIAL_EOL(); SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate); + + #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]); @@ -84,8 +89,8 @@ static char sd_filename[MAXPATHNAMELENGTH]; SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling)); SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade)); #endif - SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", job_recovery_info.cmd_queue_index_r); - SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", job_recovery_info.commands_in_queue); + 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 @@ -97,15 +102,17 @@ static char sd_filename[MAXPATHNAMELENGTH]; else SERIAL_PROTOCOLLNPGM("INVALID DATA"); } + SERIAL_PROTOCOLLNPGM("---------------------------"); } #endif // DEBUG_POWER_LOSS_RECOVERY /** - * Check for Print Job Recovery - * If the file has a saved state, populate the job_recovery_commands queue + * 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. */ -void do_print_job_recovery() { - //if (job_recovery_commands_count > 0) return; +void check_print_job_recovery() { memset(&job_recovery_info, 0, sizeof(job_recovery_info)); ZERO(job_recovery_commands); @@ -114,7 +121,7 @@ void do_print_job_recovery() { if (card.cardOK) { #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", (int)sizeof(job_recovery_info)); + SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", int(sizeof(job_recovery_info))); #endif if (card.jobRecoverFileExists()) { @@ -134,7 +141,9 @@ void do_print_job_recovery() { 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 !IS_KINEMATIC + #if ENABLED(MARLIN_DEV_MODE) + " S" + #elif !IS_KINEMATIC " X Y" // Home X and Y for Cartesian #endif )); @@ -142,10 +151,12 @@ void do_print_job_recovery() { char str_1[16], str_2[16]; #if HAS_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); + 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 dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1); @@ -157,23 +168,21 @@ void do_print_job_recovery() { ); sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E - strcpy_P(job_recovery_commands[ind++], PSTR("M117 Continuing...")); - - uint8_t r = job_recovery_info.cmd_queue_index_r; - while (job_recovery_info.commands_in_queue) { + 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]); - job_recovery_info.commands_in_queue--; 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 - - card.openFile(job_recovery_info.sd_filename, true); - card.setIndex(job_recovery_info.sdpos); } else { if (job_recovery_info.valid_head != job_recovery_info.valid_foot) @@ -213,6 +222,11 @@ void save_job_recovery_info() { // Machine state COPY(job_recovery_info.current_position, current_position); job_recovery_info.feedrate = feedrate_mm_s; + + #if HOTENDS > 1 + job_recovery_info.active_hotend = active_extruder; + #endif + COPY(job_recovery_info.target_temperature, thermalManager.target_temperature); #if HAS_HEATED_BED @@ -240,14 +254,14 @@ void save_job_recovery_info() { COPY(job_recovery_info.command_queue, command_queue); // Elapsed print job time - job_recovery_info.print_job_elapsed = print_job_timer.duration() * 1000UL; + job_recovery_info.print_job_elapsed = print_job_timer.duration(); // SD file position card.getAbsFilename(job_recovery_info.sd_filename); job_recovery_info.sdpos = card.getIndex(); #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - SERIAL_PROTOCOLLNPGM("Saving job_recovery_info"); + SERIAL_PROTOCOLLNPGM("Saving..."); debug_print_job_recovery(false); #endif diff --git a/Marlin/src/feature/power_loss_recovery.h b/Marlin/src/feature/power_loss_recovery.h index e6bf56b313..46a8fd52de 100644 --- a/Marlin/src/feature/power_loss_recovery.h +++ b/Marlin/src/feature/power_loss_recovery.h @@ -40,6 +40,11 @@ typedef struct { // Machine state float current_position[NUM_AXIS], feedrate; + + #if HOTENDS > 1 + uint8_t active_hotend; + #endif + int16_t target_temperature[HOTENDS]; #if HAS_HEATED_BED @@ -74,20 +79,21 @@ extern job_recovery_info_t job_recovery_info; enum JobRecoveryPhase : unsigned char { JOB_RECOVERY_IDLE, JOB_RECOVERY_MAYBE, - JOB_RECOVERY_YES + JOB_RECOVERY_YES, + JOB_RECOVERY_DONE }; extern JobRecoveryPhase job_recovery_phase; #if HAS_LEVELING - #define APPEND_CMD_COUNT 7 + #define APPEND_CMD_COUNT 9 #else - #define APPEND_CMD_COUNT 5 + #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 do_print_job_recovery(); +void check_print_job_recovery(); void save_job_recovery_info(); #endif // _POWER_LOSS_RECOVERY_H_ diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 7e92579eed..9bf161e200 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -496,7 +496,7 @@ inline void get_serial_commands() { 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_IDLE; + if (!--job_recovery_commands_count) job_recovery_phase = JOB_RECOVERY_DONE; } return true; } diff --git a/Marlin/src/gcode/sdcard/M20-M30_M32-M34_M928.cpp b/Marlin/src/gcode/sdcard/M20-M30_M32-M34_M928.cpp index 85b352bb0e..6a982b74b0 100644 --- a/Marlin/src/gcode/sdcard/M20-M30_M32-M34_M928.cpp +++ b/Marlin/src/gcode/sdcard/M20-M30_M32-M34_M928.cpp @@ -72,6 +72,9 @@ void GcodeSuite::M22() { card.release(); } * M23: Open a file */ void GcodeSuite::M23() { + #if ENABLED(POWER_LOSS_RECOVERY) + card.removeJobRecoveryFile(); + #endif // Simplify3D includes the size, so zero out all spaces (#7227) for (char *fn = parser.string_arg; *fn; ++fn) if (*fn == ' ') *fn = '\0'; card.openFile(parser.string_arg, true); @@ -81,16 +84,22 @@ void GcodeSuite::M23() { * M24: Start or Resume SD Print */ void GcodeSuite::M24() { - #if ENABLED(POWER_LOSS_RECOVERY) - card.removeJobRecoveryFile(); - #endif - #if ENABLED(PARK_HEAD_ON_PAUSE) resume_print(); #endif + #if ENABLED(POWER_LOSS_RECOVERY) + if (parser.seenval('S')) card.setIndex(parser.value_long()); + #endif + card.startFileprint(); - print_job_timer.start(); + + #if ENABLED(POWER_LOSS_RECOVERY) + if (parser.seenval('T')) + print_job_timer.resume(parser.value_long()); + else + #endif + print_job_timer.start(); } /** diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 1cf3567662..1f5ad92508 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -666,6 +666,9 @@ #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") +#endif #ifndef MSG_CARD_MENU #define MSG_CARD_MENU _UxGT("Print from SD") #endif diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp index df03380537..7599e5f7f0 100644 --- a/Marlin/src/lcd/ultralcd.cpp +++ b/Marlin/src/lcd/ultralcd.cpp @@ -873,17 +873,13 @@ void lcd_quick_feedback(const bool clear_buttons) { abort_sd_printing = true; lcd_setstatusPGM(PSTR(MSG_PRINT_ABORTED), -1); lcd_return_to_status(); - - #if ENABLED(POWER_LOSS_RECOVERY) - card.removeJobRecoveryFile(); - #endif } #endif // SDSUPPORT #if ENABLED(POWER_LOSS_RECOVERY) - static void lcd_sdcard_recover_job() { + static void lcd_power_loss_recovery_resume() { char cmd[20]; // Return to status now @@ -891,45 +887,65 @@ void lcd_quick_feedback(const bool clear_buttons) { // Turn leveling off and home enqueue_and_echo_commands_P(PSTR("M420 S0\nG28" - #if !IS_KINEMATIC + #if ENABLED(MARLIN_DEV_MODE) + " S" + #elif !IS_KINEMATIC " X Y" #endif )); #if HAS_HEATED_BED - // Restore the bed temperature - sprintf_P(cmd, PSTR("M190 S%i"), job_recovery_info.target_temperature_bed); - enqueue_and_echo_command(cmd); + 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() { - sprintf_P(cmd, PSTR("M109 S%i"), job_recovery_info.target_temperature[e]); - enqueue_and_echo_command(cmd); + 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++) { - sprintf_P(cmd, PSTR("M106 P%i S%i"), i, job_recovery_info.fanSpeeds[i]); - enqueue_and_echo_command(cmd); + int16_t f = job_recovery_info.fanSpeeds[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; + } - // Resume the print job timer - if (job_recovery_info.print_job_elapsed) - print_job_timer.resume(job_recovery_info.print_job_elapsed); - - // Start getting commands from SD - card.startFileprint(); + static void lcd_power_loss_recovery_cancel() { + card.removeJobRecoveryFile(); + card.autostart_index = 0; + lcd_return_to_status(); } static void lcd_job_recovery_menu() { defer_return_to_status = true; START_MENU(); - MENU_ITEM(function, MSG_RESUME_PRINT, lcd_sdcard_recover_job); - MENU_ITEM(function, MSG_STOP_PRINT, lcd_sdcard_stop); + STATIC_ITEM(MSG_POWER_LOSS_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/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index 3da9b03b5c..ccb1d20257 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -541,9 +541,13 @@ void CardReader::checkautostart() { if (!cardOK) initsd(); - if (cardOK) { + if (cardOK + #if ENABLED(POWER_LOSS_RECOVERY) + && !jobRecoverFileExists() // Don't run auto#.g when a resume file exists + #endif + ) { char autoname[10]; - sprintf_P(autoname, PSTR("auto%i.g"), autostart_index); + sprintf_P(autoname, PSTR("auto%i.g"), int(autostart_index)); dir_t p; root.rewind(); while (root.readDir(&p, NULL) > 0) { @@ -990,20 +994,24 @@ void CardReader::printingHasFinished() { SERIAL_PROTOCOLCHAR('.'); SERIAL_EOL(); } - else + else if (!read) SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name); } void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); } bool CardReader::jobRecoverFileExists() { - return jobRecoveryFile.open(&root, job_recovery_file_name, O_READ); + 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 (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed."); + #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) + if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed."); + #endif return ret; } @@ -1013,13 +1021,16 @@ void CardReader::printingHasFinished() { void CardReader::removeJobRecoveryFile() { job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0; - const bool success = jobRecoveryFile.remove(&root, job_recovery_file_name); - #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - SERIAL_PROTOCOLPGM("Power-loss file delete"); - serialprintPGM(success ? PSTR("d.\n") : PSTR(" failed.\n")); - #else - UNUSED(success); - #endif + if (jobRecoverFileExists()) { + closefile(); + removeFile(job_recovery_file_name); + #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) + SERIAL_PROTOCOLPGM("Power-loss file delete"); + serialprintPGM(jobRecoverFileExists() ? PSTR(" failed.\n") : PSTR("d.\n")); + #else + UNUSED(success); + #endif + } } #endif // POWER_LOSS_RECOVERY diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 276b7b7b3d..59b238e8d2 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -142,7 +142,7 @@ public: public: bool saving, logging, sdprinting, cardOK, filenameIsDir; char filename[FILENAME_LENGTH], longFilename[LONG_FILENAME_LENGTH]; - int autostart_index; + int8_t autostart_index; private: SdFile root, workDir, workDirParents[MAX_DIR_DEPTH]; uint8_t workDirDepth;