Optimize Power-Loss Recovery (#12440)

This commit is contained in:
Scott Lahteine 2018-11-16 20:47:07 -06:00 committed by GitHub
parent ca21ac6b9b
commit d97e31db4c
Signed by: GitHub
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 586 additions and 363 deletions

View file

@ -924,11 +924,11 @@ void setup() {
#endif #endif
#if DO_SWITCH_EXTRUDER #if DO_SWITCH_EXTRUDER
move_extruder_servo(0); // Initialize extruder servo move_extruder_servo(0); // Initialize extruder servo
#endif #endif
#if ENABLED(SWITCHING_NOZZLE) #if ENABLED(SWITCHING_NOZZLE)
move_nozzle_servo(0); // Initialize nozzle servo move_nozzle_servo(0); // Initialize nozzle servo
#endif #endif
#if ENABLED(PARKING_EXTRUDER) #if ENABLED(PARKING_EXTRUDER)
@ -936,11 +936,11 @@ void setup() {
#endif #endif
#if ENABLED(POWER_LOSS_RECOVERY) #if ENABLED(POWER_LOSS_RECOVERY)
check_print_job_recovery(); recovery.check();
#endif #endif
#if ENABLED(USE_WATCHDOG) // Reinit watchdog after HAL_get_reset_source call #if ENABLED(USE_WATCHDOG)
watchdog_init(); watchdog_init(); // Reinit watchdog after HAL_get_reset_source call
#endif #endif
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER) #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)

View file

@ -29,247 +29,145 @@
#if ENABLED(POWER_LOSS_RECOVERY) #if ENABLED(POWER_LOSS_RECOVERY)
#include "power_loss_recovery.h" #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 "../lcd/ultralcd.h"
#include "../gcode/queue.h" #include "../gcode/queue.h"
#include "../gcode/gcode.h"
#include "../module/motion.h" #include "../module/motion.h"
#include "../module/planner.h" #include "../module/planner.h"
#include "../module/printcounter.h" #include "../module/printcounter.h"
#include "../module/temperature.h" #include "../module/temperature.h"
#include "../sd/cardreader.h"
#include "../core/serial.h" #include "../core/serial.h"
#if ENABLED(FWRETRACT) #if ENABLED(FWRETRACT)
#include "fwretract.h" #include "fwretract.h"
#endif #endif
// Recovery data PrintJobRecovery recovery;
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];
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) { * Enable or disable then call changed()
SERIAL_PROTOCOLLNPGM("---- Job Recovery Info ----"); */
SERIAL_PROTOCOLPAIR("valid_head:", int(job_recovery_info.valid_head)); void PrintJobRecovery::enable(const bool onoff) {
SERIAL_PROTOCOLLNPAIR(" valid_foot:", int(job_recovery_info.valid_foot)); enabled = onoff;
if (job_recovery_info.valid_head) { changed();
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);
#if HOTENDS > 1 /**
SERIAL_PROTOCOLLNPAIR("active_hotend: ", int(job_recovery_info.active_hotend)); * The enabled state was changed:
#endif * - Enabled: Purge the job recovery file
* - Disabled: Write the job recovery file
SERIAL_PROTOCOLPGM("target_temperature: "); */
HOTEND_LOOP() { void PrintJobRecovery::changed() {
SERIAL_PROTOCOL(job_recovery_info.target_temperature[e]); if (!enabled)
if (e < HOTENDS - 1) SERIAL_CHAR(','); purge();
} else if (IS_SD_PRINTING())
SERIAL_EOL(); save(true);
}
#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
/** /**
* Check for Print Job Recovery during setup() * Check for Print Job Recovery during setup()
* *
* If a saved state exists, populate job_recovery_commands with * If a saved state exists send 'M1000 S' to initiate job recovery.
* commands to restore the machine state and continue the file.
*/ */
void check_print_job_recovery() { void PrintJobRecovery::check() {
memset(&job_recovery_info, 0, sizeof(job_recovery_info)); if (enabled) {
ZERO(job_recovery_commands); if (!card.cardOK) card.initsd();
if (card.cardOK) {
if (!card.cardOK) card.initsd(); load();
if (!valid()) return purge();
if (card.cardOK) { enqueue_and_echo_commands_P(PSTR("M1000 S"));
#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));
}
} }
} }
} }
/**
* 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 * 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 #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(); millis_t ms = millis();
#endif #endif
if (
// Save on every command if (force
#if ENABLED(SAVE_EACH_CMD_MODE) #if DISABLED(SAVE_EACH_CMD_MODE) // Always save state when enabled
true #if PIN_EXISTS(POWER_LOSS) // Save if power loss pin is triggered
#else || READ(POWER_LOSS_PIN) == POWER_LOSS_STATE
// Save if power loss pin is triggered
#if PIN_EXISTS(POWER_LOSS)
READ(POWER_LOSS_PIN) == POWER_LOSS_STATE ||
#endif #endif
// Save if interval is elapsed #if SAVE_INFO_INTERVAL_MS > 0 // Save if interval is elapsed
#if SAVE_INFO_INTERVAL_MS > 0 || ELAPSED(ms, next_save_ms)
ELAPSED(ms, next_save_ms) ||
#endif #endif
// Save on every new Z height // Save every time Z is higher than the last call
(current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS]) || current_position[Z_AXIS] > info.current_position[Z_AXIS]
#endif #endif
) { ) {
#if SAVE_INFO_INTERVAL_MS > 0 #if SAVE_INFO_INTERVAL_MS > 0
next_save_ms = ms + SAVE_INFO_INTERVAL_MS; next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
#endif #endif
// Head and foot will match if valid data was saved // Set Head and Foot to matching non-zero values
if (!++job_recovery_info.valid_head) ++job_recovery_info.valid_head; // non-zero in sequence if (!++info.valid_head) ++info.valid_head; // non-zero in sequence
job_recovery_info.valid_foot = job_recovery_info.valid_head; //if (!IS_SD_PRINTING()) info.valid_head = 0;
info.valid_foot = info.valid_head;
// Machine state // Machine state
COPY(job_recovery_info.current_position, current_position); COPY(info.current_position, current_position);
job_recovery_info.feedrate = feedrate_mm_s; info.feedrate = uint16_t(feedrate_mm_s * 60.0f);
#if HOTENDS > 1 #if HOTENDS > 1
job_recovery_info.active_hotend = active_extruder; info.active_hotend = active_extruder;
#endif #endif
COPY(job_recovery_info.target_temperature, thermalManager.target_temperature); COPY(info.target_temperature, thermalManager.target_temperature);
#if HAS_HEATED_BED #if HAS_HEATED_BED
job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed; info.target_temperature_bed = thermalManager.target_temperature_bed;
#endif #endif
#if FAN_COUNT #if FAN_COUNT
COPY(job_recovery_info.fan_speed, fan_speed); COPY(info.fan_speed, fan_speed);
#endif #endif
#if HAS_LEVELING #if HAS_LEVELING
job_recovery_info.leveling = planner.leveling_active; info.leveling = planner.leveling_active;
job_recovery_info.fade = ( info.fade = (
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
planner.z_fade_height planner.z_fade_height
#else #else
@ -279,35 +177,238 @@ void save_job_recovery_info() {
#endif #endif
#if ENABLED(FWRETRACT) #if ENABLED(FWRETRACT)
COPY(job_recovery_info.retract, fwretract.current_retract); COPY(info.retract, fwretract.current_retract);
job_recovery_info.retract_hop = fwretract.current_hop; info.retract_hop = fwretract.current_hop;
#endif #endif
// Commands in the queue // Commands in the queue
job_recovery_info.cmd_queue_index_r = cmd_queue_index_r; info.cmd_queue_index_r = cmd_queue_index_r;
job_recovery_info.commands_in_queue = commands_in_queue; info.commands_in_queue = commands_in_queue;
COPY(job_recovery_info.command_queue, command_queue); COPY(info.command_queue, command_queue);
// Elapsed print job time // 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 // SD file position
card.getAbsFilename(job_recovery_info.sd_filename); card.getAbsFilename(info.sd_filename);
job_recovery_info.sdpos = card.getIndex(); info.sdpos = card.getIndex();
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY) write();
SERIAL_PROTOCOLLNPGM("Saving...");
debug_print_job_recovery(false);
#endif
card.openJobRecoveryFile(false); // KILL now if the power-loss pin was triggered
(void)card.saveJobRecoveryInfo();
// If power-loss pin was triggered, write just once then kill
#if PIN_EXISTS(POWER_LOSS) #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 #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 #endif // POWER_LOSS_RECOVERY

View file

@ -26,7 +26,6 @@
*/ */
#include "../sd/cardreader.h" #include "../sd/cardreader.h"
#include "../core/millis_t.h"
#include "../inc/MarlinConfigPre.h" #include "../inc/MarlinConfigPre.h"
#define SAVE_INFO_INTERVAL_MS 0 #define SAVE_INFO_INTERVAL_MS 0
@ -37,7 +36,9 @@ typedef struct {
uint8_t valid_head; uint8_t valid_head;
// Machine state // Machine state
float current_position[NUM_AXIS], feedrate; float current_position[NUM_AXIS];
uint16_t feedrate;
#if HOTENDS > 1 #if HOTENDS > 1
uint8_t active_hotend; uint8_t active_hotend;
@ -74,26 +75,45 @@ typedef struct {
millis_t print_job_elapsed; millis_t print_job_elapsed;
uint8_t valid_foot; uint8_t valid_foot;
} job_recovery_info_t; } 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 { static void init();
JOB_RECOVERY_IDLE,
JOB_RECOVERY_MAYBE, static bool enabled;
JOB_RECOVERY_YES, static void enable(const bool onoff);
JOB_RECOVERY_DONE 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 extern PrintJobRecovery recovery;
#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();

View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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

View file

@ -695,6 +695,11 @@ void GcodeSuite::process_parsed_command(
case 999: M999(); break; // M999: Restart after being Stopped 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; default: parser.unknown_command_error(); break;
} }
break; break;

View file

@ -196,7 +196,8 @@
* M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR) * M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
* M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR) * M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
* M410 - Quickstop. Abort all planned moves. * 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) * 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<units> Y<units> Z<units> (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, or AUTO_BED_LEVELING_UBL) * M421 - Set a single Z coordinate in the Mesh Leveling grid. X<units> Y<units> Z<units> (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, or AUTO_BED_LEVELING_UBL)
* M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN) * M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
@ -822,6 +823,11 @@ private:
static void M999(); static void M999();
#if ENABLED(POWER_LOSS_RECOVERY)
static void M413();
static void M1000();
#endif
static void T(const uint8_t tool_index); static void T(const uint8_t tool_index);
}; };

View file

@ -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 #endif // SDSUPPORT
/** /**
@ -840,11 +824,6 @@ void get_available_commands() {
get_serial_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) #if ENABLED(SDSUPPORT)
get_sdcard_commands(); get_sdcard_commands();
#endif #endif
@ -890,7 +869,7 @@ void advance_command_queue() {
else { else {
gcode.process_next_command(); gcode.process_next_command();
#if ENABLED(POWER_LOSS_RECOVERY) #if ENABLED(POWER_LOSS_RECOVERY)
if (card.cardOK && IS_SD_PRINTING()) save_job_recovery_info(); if (IS_SD_PRINTING()) recovery.save();
#endif #endif
} }

View file

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

View file

@ -253,7 +253,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pozastavit tisk") #define MSG_PAUSE_PRINT _UxGT("Pozastavit tisk")
#define MSG_RESUME_PRINT _UxGT("Obnovit tisk") #define MSG_RESUME_PRINT _UxGT("Obnovit tisk")
#define MSG_STOP_PRINT _UxGT("Zastavit 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_CARD_MENU _UxGT("Tisknout z SD")
#define MSG_NO_CARD _UxGT("Žádná SD karta") #define MSG_NO_CARD _UxGT("Žádná SD karta")
#define MSG_DWELL _UxGT("Uspáno...") #define MSG_DWELL _UxGT("Uspáno...")

View file

@ -268,7 +268,7 @@
#define MSG_PAUSE_PRINT _UxGT("SD-Druck pausieren") #define MSG_PAUSE_PRINT _UxGT("SD-Druck pausieren")
#define MSG_RESUME_PRINT _UxGT("SD-Druck fortsetzen") #define MSG_RESUME_PRINT _UxGT("SD-Druck fortsetzen")
#define MSG_STOP_PRINT _UxGT("SD-Druck abbrechen") #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_CARD_MENU _UxGT("Druck v. SD-Karte")
#define MSG_NO_CARD _UxGT("Keine SD-Karte") #define MSG_NO_CARD _UxGT("Keine SD-Karte")
#define MSG_DWELL _UxGT("Warten...") #define MSG_DWELL _UxGT("Warten...")

View file

@ -728,8 +728,8 @@
#ifndef MSG_STOP_PRINT #ifndef MSG_STOP_PRINT
#define MSG_STOP_PRINT _UxGT("Stop print") #define MSG_STOP_PRINT _UxGT("Stop print")
#endif #endif
#ifndef MSG_POWER_LOSS_RECOVERY #ifndef MSG_OUTAGE_RECOVERY
#define MSG_POWER_LOSS_RECOVERY _UxGT("Power-Loss Recovery") #define MSG_OUTAGE_RECOVERY _UxGT("Outage Recovery")
#endif #endif
#ifndef MSG_CARD_MENU #ifndef MSG_CARD_MENU
#define MSG_CARD_MENU _UxGT("Print from SD") #define MSG_CARD_MENU _UxGT("Print from SD")

View file

@ -266,7 +266,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pausa stampa") #define MSG_PAUSE_PRINT _UxGT("Pausa stampa")
#define MSG_RESUME_PRINT _UxGT("Riprendi stampa") #define MSG_RESUME_PRINT _UxGT("Riprendi stampa")
#define MSG_STOP_PRINT _UxGT("Arresta 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_CARD_MENU _UxGT("Stampa da SD")
#define MSG_NO_CARD _UxGT("SD non presente") #define MSG_NO_CARD _UxGT("SD non presente")
#define MSG_DWELL _UxGT("Sospensione...") #define MSG_DWELL _UxGT("Sospensione...")

View file

@ -259,7 +259,7 @@
#define MSG_PAUSE_PRINT _UxGT("일시정지") #define MSG_PAUSE_PRINT _UxGT("일시정지")
#define MSG_RESUME_PRINT _UxGT("재시작") #define MSG_RESUME_PRINT _UxGT("재시작")
#define MSG_STOP_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_CARD_MENU _UxGT("SD 카드출력")
#define MSG_NO_CARD _UxGT("SD 카드없음") #define MSG_NO_CARD _UxGT("SD 카드없음")
#define MSG_DWELL _UxGT("슬립모드...") #define MSG_DWELL _UxGT("슬립모드...")

View file

@ -273,7 +273,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pausar impressão") #define MSG_PAUSE_PRINT _UxGT("Pausar impressão")
#define MSG_RESUME_PRINT _UxGT("Resumir impressão") #define MSG_RESUME_PRINT _UxGT("Resumir impressão")
#define MSG_STOP_PRINT _UxGT("Parar 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_CARD_MENU _UxGT("Imprimir do SD")
#define MSG_NO_CARD _UxGT("Sem cartão SD") #define MSG_NO_CARD _UxGT("Sem cartão SD")
#define MSG_DWELL _UxGT("Dormindo...") #define MSG_DWELL _UxGT("Dormindo...")

View file

@ -278,7 +278,7 @@
#define MSG_PAUSE_PRINT _UxGT("Pozastaviť tlač") #define MSG_PAUSE_PRINT _UxGT("Pozastaviť tlač")
#define MSG_RESUME_PRINT _UxGT("Obnoviť tlač") #define MSG_RESUME_PRINT _UxGT("Obnoviť tlač")
#define MSG_STOP_PRINT _UxGT("Zastaviť 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_CARD_MENU _UxGT("Tlačiť z SD")
#define MSG_NO_CARD _UxGT("Žiadna SD karta") #define MSG_NO_CARD _UxGT("Žiadna SD karta")
#define MSG_DWELL _UxGT("Spím...") #define MSG_DWELL _UxGT("Spím...")

View file

@ -36,6 +36,10 @@
#include "../../feature/runout.h" #include "../../feature/runout.h"
#endif #endif
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../../feature/power_loss_recovery.h"
#endif
#define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST) #define HAS_DEBUG_MENU ENABLED(LCD_PROGRESS_BAR_TEST)
void menu_advanced_settings(); void menu_advanced_settings();
@ -350,6 +354,10 @@ void menu_configuration() {
MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset); MENU_ITEM_EDIT_CALLBACK(bool, MSG_RUNOUT_SENSOR_ENABLE, &runout.enabled, runout.reset);
#endif #endif
#if ENABLED(POWER_LOSS_RECOVERY)
MENU_ITEM_EDIT_CALLBACK(bool, MSG_OUTAGE_RECOVERY, &recovery.enabled, recovery.changed);
#endif
#if DISABLED(SLIM_LCD_MENUS) #if DISABLED(SLIM_LCD_MENUS)
// Preheat configurations // Preheat configurations
MENU_ITEM(submenu, MSG_PREHEAT_1_SETTINGS, menu_preheat_material1_settings); MENU_ITEM(submenu, MSG_PREHEAT_1_SETTINGS, menu_preheat_material1_settings);

View file

@ -34,58 +34,8 @@
#include "../../feature/power_loss_recovery.h" #include "../../feature/power_loss_recovery.h"
static void lcd_power_loss_recovery_resume() { static void lcd_power_loss_recovery_resume() {
char cmd[20];
// Return to status now
ui.return_to_status(); ui.return_to_status();
enqueue_and_echo_commands_P(PSTR("M1000"));
// 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;
} }
static void lcd_power_loss_recovery_cancel() { static void lcd_power_loss_recovery_cancel() {
@ -97,7 +47,7 @@ static void lcd_power_loss_recovery_cancel() {
void menu_job_recovery() { void menu_job_recovery() {
ui.defer_status_screen(true); ui.defer_status_screen(true);
START_MENU(); 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_RESUME_PRINT, lcd_power_loss_recovery_resume);
MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel); MENU_ITEM(function, MSG_STOP_PRINT, lcd_power_loss_recovery_cancel);
END_MENU(); END_MENU();

View file

@ -65,7 +65,7 @@
#if ENABLED(MENU_ADDAUTOSTART) #if ENABLED(MENU_ADDAUTOSTART)
void lcd_autostart_sd() { card.beginautostart(); } inline void lcd_autostart_sd() { card.beginautostart(); }
#endif #endif

View file

@ -659,13 +659,6 @@ void MarlinUI::update() {
#endif // SDSUPPORT && SD_DETECT_PIN #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(); const millis_t ms = millis();
if (ELAPSED(ms, next_lcd_update_ms) if (ELAPSED(ms, next_lcd_update_ms)
#if HAS_GRAPHICAL_LCD #if HAS_GRAPHICAL_LCD

View file

@ -37,7 +37,7 @@
*/ */
// Change EEPROM version if the structure changes // Change EEPROM version if the structure changes
#define EEPROM_VERSION "V62" #define EEPROM_VERSION "V63"
#define EEPROM_OFFSET 100 #define EEPROM_OFFSET 100
// Check the integrity of data offsets. // Check the integrity of data offsets.
@ -82,6 +82,11 @@
#endif #endif
#include "../feature/fwretract.h" #include "../feature/fwretract.h"
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../feature/power_loss_recovery.h"
#endif
#include "../feature/pause.h" #include "../feature/pause.h"
#if EXTRUDERS > 1 #if EXTRUDERS > 1
@ -221,6 +226,11 @@ typedef struct SettingsDataStruct {
// //
int16_t lcd_contrast; // M250 C int16_t lcd_contrast; // M250 C
//
// POWER_LOSS_RECOVERY
//
bool recovery_enabled; // M413 S
// //
// FWRETRACT // FWRETRACT
// //
@ -269,7 +279,7 @@ typedef struct SettingsDataStruct {
// Tool-change settings // Tool-change settings
// //
#if EXTRUDERS > 1 #if EXTRUDERS > 1
toolchange_settings_t toolchange_settings; // M217 S P R toolchange_settings_t toolchange_settings; // M217 S P R
#endif #endif
} SettingsData; } SettingsData;
@ -746,6 +756,22 @@ void MarlinSettings::postprocess() {
EEPROM_WRITE(lcd_contrast); 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 // Firmware Retraction
// //
@ -1387,6 +1413,20 @@ void MarlinSettings::postprocess() {
#endif #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 // Firmware Retraction
// //
@ -2075,6 +2115,10 @@ void MarlinSettings::reset(PORTARG_SOLO) {
ui.set_contrast(DEFAULT_LCD_CONTRAST); ui.set_contrast(DEFAULT_LCD_CONTRAST);
#endif #endif
#if ENABLED(POWER_LOSS_RECOVERY)
recovery.enable(true);
#endif
#if ENABLED(FWRETRACT) #if ENABLED(FWRETRACT)
fwretract.reset(); fwretract.reset();
#endif #endif
@ -2643,6 +2687,15 @@ void MarlinSettings::reset(PORTARG_SOLO) {
SERIAL_ECHOLNPAIR_P(port, " M250 C", ui.contrast); SERIAL_ECHOLNPAIR_P(port, " M250 C", ui.contrast);
#endif #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 ENABLED(FWRETRACT)
if (!forReplay) { if (!forReplay) {
@ -2683,7 +2736,7 @@ void MarlinSettings::reset(PORTARG_SOLO) {
#if HAS_BED_PROBE #if HAS_BED_PROBE
if (!forReplay) { if (!forReplay) {
CONFIG_ECHO_START; CONFIG_ECHO_START;
SERIAL_ECHOPGM_P(port, "Z-Probe Offset (mm):"); SERIAL_ECHOPGM_P(port, "Z-Probe Offset");
SAY_UNITS_P(port, true); SAY_UNITS_P(port, true);
} }
CONFIG_ECHO_START; CONFIG_ECHO_START;

View file

@ -478,7 +478,7 @@ void CardReader::openFile(char * const path, const bool read, const bool subcall
void CardReader::removeFile(const char * const name) { void CardReader::removeFile(const char * const name) {
if (!cardOK) return; if (!cardOK) return;
stopSDPrint(); //stopSDPrint();
SdFile *curDir; SdFile *curDir;
const char * const fname = diveToFile(curDir, name, false); const char * const fname = diveToFile(curDir, name, false);
@ -549,7 +549,7 @@ void CardReader::checkautostart() {
if (cardOK if (cardOK
#if ENABLED(POWER_LOSS_RECOVERY) #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 #endif
) { ) {
char autoname[8]; char autoname[8];
@ -577,6 +577,7 @@ void CardReader::closefile(const bool store_location) {
file.sync(); file.sync();
file.close(); file.close();
saving = logging = false; saving = logging = false;
sdpos = 0;
#if ENABLED(EMERGENCY_PARSER) #if ENABLED(EMERGENCY_PARSER)
emergency_parser.enable(); emergency_parser.enable();
#endif #endif
@ -622,8 +623,12 @@ uint16_t CardReader::getnrfilenames() {
} }
/** /**
* Dive to the given file path, with optional echo. * Dive to the given DOS 8.3 file path, with optional echo of the dive paths.
* On exit set curDir and return the name part of the path. *
* 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. * A NULL result indicates an unrecoverable error.
*/ */
const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) { 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) #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) { void CardReader::openJobRecoveryFile(const bool read) {
if (!cardOK) return; if (!cardOK) return;
if (jobRecoveryFile.isOpen()) return; if (recovery.file.isOpen()) return;
if (!jobRecoveryFile.open(&root, job_recovery_file_name, read ? O_READ : O_CREAT | O_WRITE | O_TRUNC | O_SYNC)) { 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_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name);
SERIAL_PROTOCOLCHAR('.'); SERIAL_PROTOCOLCHAR('.');
SERIAL_EOL(); SERIAL_EOL();
@ -1007,31 +1018,12 @@ void CardReader::printingHasFinished() {
SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name); SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name);
} }
void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); } // Removing the job recovery file currently requires closing
// the file being printed, so during SD printing the file should
bool CardReader::jobRecoverFileExists() { // be zeroed and written instead of deleted.
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));
}
void CardReader::removeJobRecoveryFile() { void CardReader::removeJobRecoveryFile() {
job_recovery_info.valid_head = job_recovery_info.valid_foot = job_recovery_commands_count = 0;
if (jobRecoverFileExists()) { if (jobRecoverFileExists()) {
closefile(); //closefile();
removeFile(job_recovery_file_name); removeFile(job_recovery_file_name);
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY) #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
SERIAL_PROTOCOLPGM("Power-loss file delete"); SERIAL_PROTOCOLPGM("Power-loss file delete");

View file

@ -106,11 +106,8 @@ public:
#endif #endif
#if ENABLED(POWER_LOSS_RECOVERY) #if ENABLED(POWER_LOSS_RECOVERY)
void openJobRecoveryFile(const bool read);
void closeJobRecoveryFile();
bool jobRecoverFileExists(); bool jobRecoverFileExists();
int16_t saveJobRecoveryInfo(); void openJobRecoveryFile(const bool read);
int16_t loadJobRecoveryInfo();
void removeJobRecoveryFile(); void removeJobRecoveryFile();
#endif #endif
@ -217,10 +214,6 @@ private:
SdVolume volume; SdVolume volume;
SdFile file; SdFile file;
#if ENABLED(POWER_LOSS_RECOVERY)
SdFile jobRecoveryFile;
#endif
#define SD_PROCEDURE_DEPTH 1 #define SD_PROCEDURE_DEPTH 1
#define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH + MAX_DIR_DEPTH + 1) #define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH + MAX_DIR_DEPTH + 1)
uint8_t file_subcall_ctr; uint8_t file_subcall_ctr;