From d97e31db4c2b8b27ab1c6fbbe05f11aa9d5e94e2 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 16 Nov 2018 20:47:07 -0600 Subject: [PATCH] Optimize Power-Loss Recovery (#12440) --- Marlin/src/Marlin.cpp | 10 +- Marlin/src/feature/power_loss_recovery.cpp | 511 +++++++++++-------- Marlin/src/feature/power_loss_recovery.h | 60 ++- Marlin/src/gcode/feature/powerloss/M1000.cpp | 65 +++ Marlin/src/gcode/feature/powerloss/M413.cpp | 58 +++ Marlin/src/gcode/gcode.cpp | 5 + Marlin/src/gcode/gcode.h | 8 +- Marlin/src/gcode/queue.cpp | 23 +- Marlin/src/inc/Conditionals_post.h | 2 +- Marlin/src/lcd/language/language_cz.h | 2 +- Marlin/src/lcd/language/language_de.h | 2 +- Marlin/src/lcd/language/language_en.h | 4 +- Marlin/src/lcd/language/language_it.h | 2 +- Marlin/src/lcd/language/language_ko_KR.h | 2 +- Marlin/src/lcd/language/language_pt-br.h | 2 +- Marlin/src/lcd/language/language_sk.h | 2 +- Marlin/src/lcd/menu/menu_configuration.cpp | 8 + Marlin/src/lcd/menu/menu_job_recovery.cpp | 54 +- Marlin/src/lcd/menu/menu_main.cpp | 2 +- Marlin/src/lcd/ultralcd.cpp | 7 - Marlin/src/module/configuration_store.cpp | 59 ++- Marlin/src/sd/cardreader.cpp | 52 +- Marlin/src/sd/cardreader.h | 9 +- 23 files changed, 586 insertions(+), 363 deletions(-) create mode 100644 Marlin/src/gcode/feature/powerloss/M1000.cpp create mode 100644 Marlin/src/gcode/feature/powerloss/M413.cpp 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;