diff --git a/Marlin/src/Marlin.cpp b/Marlin/src/Marlin.cpp index e288e4837c..fa86ce9abf 100644 --- a/Marlin/src/Marlin.cpp +++ b/Marlin/src/Marlin.cpp @@ -672,6 +672,9 @@ void idle( bool no_stepper_sleep/*=false*/ #endif ) { + #if ENABLED(POWER_LOSS_RECOVERY) && PIN_EXISTS(POWER_LOSS) + recovery.outage(); + #endif #if ENABLED(SPI_ENDSTOPS) if (endstops.tmc_spi_homing.any diff --git a/Marlin/src/feature/power_loss_recovery.cpp b/Marlin/src/feature/power_loss_recovery.cpp index eb25bd4274..282b5d7851 100644 --- a/Marlin/src/feature/power_loss_recovery.cpp +++ b/Marlin/src/feature/power_loss_recovery.cpp @@ -36,6 +36,9 @@ bool PrintJobRecovery::enabled; // Initialized by settings.load() SdFile PrintJobRecovery::file; job_recovery_info_t PrintJobRecovery::info; const char PrintJobRecovery::filename[5] = "/PLR"; +uint8_t PrintJobRecovery::queue_index_r; +uint32_t PrintJobRecovery::cmd_sdpos, // = 0 + PrintJobRecovery::sdpos[BUFSIZE]; #include "../sd/cardreader.h" #include "../lcd/ultralcd.h" @@ -124,6 +127,14 @@ void PrintJobRecovery::load() { debug(PSTR("Load")); } +/** + * Set info fields that won't change + */ +void PrintJobRecovery::prepare() { + card.getAbsFilename(info.sd_filename); // SD filename + cmd_sdpos = 0; +} + /** * Save the current machine state to the power-loss recovery file */ @@ -141,9 +152,6 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*= // Did Z change since the last call? 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 #if SAVE_INFO_INTERVAL_MS > 0 // Save if interval is elapsed || ELAPSED(ms, next_save_ms) #endif @@ -211,27 +219,20 @@ void PrintJobRecovery::save(const bool force/*=false*/, const bool save_queue/*= info.relative_mode = relative_mode; info.relative_modes_e = gcode.axis_relative_modes[E_AXIS]; - // Commands in the queue - info.queue_length = save_queue ? queue.length : 0; - info.queue_index_r = queue.index_r; - COPY(info.queue_buffer, queue.command_buffer); - // Elapsed print job time info.print_job_elapsed = print_job_timer.duration(); - // SD file position - card.getAbsFilename(info.sd_filename); - info.sdpos = card.getIndex(); - write(); - - // KILL now if the power-loss pin was triggered - #if PIN_EXISTS(POWER_LOSS) - if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(PSTR(MSG_OUTAGE_RECOVERY)); - #endif } } +#if PIN_EXISTS(POWER_LOSS) + void PrintJobRecovery::_outage() { + save(true); + kill(PSTR(MSG_OUTAGE_RECOVERY)); + } +#endif + /** * Save the recovery info the recovery file */ @@ -253,6 +254,8 @@ void PrintJobRecovery::resume() { #define RECOVERY_ZRAISE 2 + const uint32_t resume_sdpos = info.sdpos; // Get here before the stepper ISR overwrites it + #if HAS_LEVELING // Make sure leveling is off before any G92 and G28 gcode.process_subcommands_now_P(PSTR("M420 S0 Z0")); @@ -405,16 +408,11 @@ void PrintJobRecovery::resume() { } #endif - // Process commands from the old pending queue - uint8_t c = info.queue_length, r = info.queue_index_r; - for (; c--; r = (r + 1) % BUFSIZE) - gcode.process_subcommands_now(info.queue_buffer[r]); - // Resume the SD file from the last position char *fn = info.sd_filename; 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); + sprintf_P(cmd, PSTR("M24 S%ld T%ld"), resume_sdpos, info.print_job_elapsed); gcode.process_subcommands_now(cmd); } @@ -490,9 +488,6 @@ void PrintJobRecovery::resume() { DEBUG_EOL(); DEBUG_ECHOLNPAIR("retract_hop: ", info.retract_hop); #endif - DEBUG_ECHOLNPAIR("queue_index_r: ", int(info.queue_index_r)); - DEBUG_ECHOLNPAIR("queue_length: ", int(info.queue_length)); - for (uint8_t i = 0; i < info.queue_length; i++) DEBUG_ECHOLNPAIR("> ", info.queue_buffer[i]); DEBUG_ECHOLNPAIR("sd_filename: ", info.sd_filename); DEBUG_ECHOLNPAIR("sdpos: ", info.sdpos); DEBUG_ECHOLNPAIR("print_job_elapsed: ", info.print_job_elapsed); diff --git a/Marlin/src/feature/power_loss_recovery.h b/Marlin/src/feature/power_loss_recovery.h index 2cfde03506..05aa25c4db 100644 --- a/Marlin/src/feature/power_loss_recovery.h +++ b/Marlin/src/feature/power_loss_recovery.h @@ -92,13 +92,9 @@ typedef struct { // Relative mode bool relative_mode, relative_modes_e; - // Command queue - uint8_t queue_length, queue_index_r; - char queue_buffer[BUFSIZE][MAX_CMD_SIZE]; - // SD Filename and position char sd_filename[MAXPATHNAMELENGTH]; - uint32_t sdpos; + volatile uint32_t sdpos; // Job elapsed time millis_t print_job_elapsed; @@ -114,7 +110,12 @@ class PrintJobRecovery { static SdFile file; static job_recovery_info_t info; + static uint8_t queue_index_r; //!< Queue index of the active command + static uint32_t cmd_sdpos, //!< SD position of the next command + sdpos[BUFSIZE]; //!< SD positions of queued commands + static void init(); + static void prepare(); static inline void setup() { #if PIN_EXISTS(POWER_LOSS) @@ -130,6 +131,10 @@ class PrintJobRecovery { #endif } + // Track each command's file offsets + static inline uint32_t command_sdpos() { return sdpos[queue_index_r]; } + static inline void commit_sdpos(const uint8_t index_w) { sdpos[index_w] = cmd_sdpos; } + static bool enabled; static void enable(const bool onoff); static void changed(); @@ -152,6 +157,13 @@ class PrintJobRecovery { , const bool save_queue=true ); + #if PIN_EXISTS(POWER_LOSS) + static inline void outage() { + if (enabled && IS_SD_PRINTING() && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) + _outage(); + } + #endif + static inline bool valid() { return info.valid_head && info.valid_head == info.valid_foot; } #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) @@ -162,6 +174,10 @@ class PrintJobRecovery { private: static void write(); + + #if PIN_EXISTS(POWER_LOSS) + static void _outage(); + #endif }; extern PrintJobRecovery recovery; diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 680fa35974..a554232216 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -118,7 +118,7 @@ void GcodeSuite::get_destination_from_command() { destination[i] = current_position[i]; } - #if ENABLED(POWER_LOSS_RECOVERY) + #if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS) // Only update power loss recovery on moves with E if (recovery.enabled && IS_SD_PRINTING() && seen[E_AXIS] && (seen[X_AXIS] || seen[Y_AXIS])) recovery.save(); @@ -823,6 +823,10 @@ void GcodeSuite::process_next_command() { PORT_REDIRECT(queue.port[queue.index_r]); + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.queue_index_r = queue.index_r; + #endif + if (DEBUGGING(ECHO)) { SERIAL_ECHO_START(); SERIAL_ECHOLN(current_command); diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index e741b01fb4..a9bd714c1e 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -43,6 +43,9 @@ GCodeQueue queue; #include "../feature/binary_protocol.h" #endif +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../feature/power_loss_recovery.h" +#endif /** * GCode line number handling. Hosts may opt to include line numbers when @@ -120,6 +123,9 @@ void GCodeQueue::_commit_command(bool say_ok #if NUM_SERIAL > 1 port[index_w] = p; #endif + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.commit_sdpos(index_w); + #endif if (++index_w >= BUFSIZE) index_w = 0; length++; } @@ -557,6 +563,10 @@ void GCodeQueue::get_serial_commands() { sd_count = 0; // clear sd line buffer _commit_command(false); + + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.cmd_sdpos = card.getIndex(); // Prime for the next _commit_command + #endif } else if (sd_count >= MAX_CMD_SIZE - 1) { /** diff --git a/Marlin/src/gcode/sdcard/M24_M25.cpp b/Marlin/src/gcode/sdcard/M24_M25.cpp index 6f27c5558b..12d5313fcf 100644 --- a/Marlin/src/gcode/sdcard/M24_M25.cpp +++ b/Marlin/src/gcode/sdcard/M24_M25.cpp @@ -38,6 +38,10 @@ #include "../../feature/host_actions.h" #endif +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../feature/power_loss_recovery.h" +#endif + /** * M24: Start or Resume SD Print */ @@ -58,6 +62,9 @@ void GcodeSuite::M24() { if (card.isFileOpen()) { card.startFileprint(); print_job_timer.start(); + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.prepare(); + #endif } #if ENABLED(HOST_ACTION_COMMANDS) diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index d01acd8426..9618846f31 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -96,6 +96,10 @@ #include "../feature/backlash.h" #endif +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../feature/power_loss_recovery.h" +#endif + // Delay for delivery of first block to the stepper ISR, if the queue contains 2 or // fewer movements. The delay is measured in milliseconds, and must be less than 250ms #define BLOCK_DELAY_FOR_1ST_MOVE 100 @@ -2507,6 +2511,10 @@ bool Planner::_populate_block(block_t * const block, bool split_move, mixer.gradient_control(target_float[Z_AXIS]); #endif + #if ENABLED(POWER_LOSS_RECOVERY) + block->sdpos = recovery.command_sdpos(); + #endif + // Movement was accepted return true; } // _populate_block() diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 2de0336251..16486b4076 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -159,6 +159,10 @@ typedef struct block_t { uint32_t segment_time_us; #endif + #if ENABLED(POWER_LOSS_RECOVERY) + uint32_t sdpos; + #endif + } block_t; #define HAS_POSITION_FLOAT ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index cd868e7db6..7595ac1f84 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -121,6 +121,10 @@ Stepper stepper; // Singleton #include "../libs/L6470/L6470_Marlin.h" #endif +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../feature/power_loss_recovery.h" +#endif + // public: #if HAS_EXTRA_ENDSTOPS || ENABLED(Z_STEPPER_AUTO_ALIGN) @@ -1663,6 +1667,10 @@ uint32_t Stepper::stepper_block_phase_isr() { return interval; // No more queued movements! } + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.info.sdpos = current_block->sdpos; + #endif + // Flag all moving axes for proper endstop handling #if IS_CORE