diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 13d6245239..1a53a1ab71 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -394,6 +394,13 @@ void disable_all_steppers() { constexpr bool did_pause_print = false; #endif +/** + * A Print Job exists when the timer is running or SD printing + */ +bool printJobOngoing() { + return print_job_timer.isRunning() || IS_SD_PRINTING(); +} + /** * Printing is active when the print job timer is running */ @@ -690,7 +697,7 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) { // Handle Power-Loss Recovery #if ENABLED(POWER_LOSS_RECOVERY) && PIN_EXISTS(POWER_LOSS) - recovery.outage(); + if (printJobOngoing()) recovery.outage(); #endif // Run StallGuard endstop checks diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp index 97b30435f1..b9f8dfaa67 100644 --- a/Marlin/src/feature/powerloss.cpp +++ b/Marlin/src/feature/powerloss.cpp @@ -66,12 +66,16 @@ PrintJobRecovery recovery; #ifndef POWER_LOSS_PURGE_LEN #define POWER_LOSS_PURGE_LEN 0 #endif +#ifndef POWER_LOSS_ZRAISE + #define POWER_LOSS_ZRAISE 2 // Move on loss with backup power, or on resume without it +#endif + +#if DISABLED(BACKUP_POWER_SUPPLY) + #undef POWER_LOSS_RETRACT_LEN // No retract at outage without backup power +#endif #ifndef POWER_LOSS_RETRACT_LEN #define POWER_LOSS_RETRACT_LEN 0 #endif -#ifndef POWER_LOSS_ZRAISE - #define POWER_LOSS_ZRAISE 2 -#endif /** * Clear the recovery info @@ -144,7 +148,7 @@ void PrintJobRecovery::prepare() { /** * Save the current machine state to the power-loss recovery file */ -void PrintJobRecovery::save(const bool force/*=false*/) { +void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=0*/) { #if SAVE_INFO_INTERVAL_MS > 0 static millis_t next_save_ms; // = 0 @@ -177,6 +181,7 @@ void PrintJobRecovery::save(const bool force/*=false*/) { // Machine state info.current_position = current_position; + info.zraise = zraise; TERN_(HAS_HOME_OFFSET, info.home_offset = home_offset); TERN_(HAS_POSITION_SHIFT, info.position_shift = position_shift); info.feedrate = uint16_t(feedrate_mm_s * 60.0f); @@ -228,31 +233,74 @@ void PrintJobRecovery::save(const bool force/*=false*/) { #if PIN_EXISTS(POWER_LOSS) - void PrintJobRecovery::_outage() { - #if ENABLED(BACKUP_POWER_SUPPLY) - static bool lock = false; - if (lock) return; // No re-entrance from idle() during raise_z() - lock = true; - #endif - if (IS_SD_PRINTING()) save(true); - TERN_(BACKUP_POWER_SUPPLY, raise_z()); - - kill(GET_TEXT(MSG_OUTAGE_RECOVERY)); - } - #if ENABLED(BACKUP_POWER_SUPPLY) - void PrintJobRecovery::raise_z() { - // Disable all heaters to reduce power loss - thermalManager.disable_all_heaters(); - quickstop_stepper(); - // Raise Z axis - gcode.process_subcommands_now_P(PSTR("G91\nG0 Z" STRINGIFY(POWER_LOSS_ZRAISE))); - planner.synchronize(); + void PrintJobRecovery::retract_and_lift(const float &zraise) { + #if POWER_LOSS_RETRACT_LEN || POWER_LOSS_ZRAISE + + gcode.set_relative_mode(true); // Use relative coordinates + + #if POWER_LOSS_RETRACT_LEN + // Retract filament now + gcode.process_subcommands_now_P(PSTR("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN))); + #endif + + #if POWER_LOSS_ZRAISE + // Raise the Z axis now + if (zraise) { + char cmd[20], str_1[16]; + sprintf_P(cmd, PSTR("G0 Z%s"), dtostrf(zraise, 1, 3, str_1)); + gcode.process_subcommands_now(cmd); + } + #else + UNUSED(zraise); + #endif + + //gcode.axis_relative = info.axis_relative; + planner.synchronize(); + #endif } #endif + /** + * An outage was detected by a sensor pin. + * - If not SD printing, let the machine turn off on its own with no "KILL" screen + * - Disable all heaters first to save energy + * - Save the recovery data for the current instant + * - If backup power is available Retract E and Raise Z + * - Go to the KILL screen + */ + void PrintJobRecovery::_outage() { + #if ENABLED(BACKUP_POWER_SUPPLY) + static bool lock = false; + if (lock) return; // No re-entrance from idle() during retract_and_lift() + lock = true; + #endif + + #if POWER_LOSS_ZRAISE + // Get the limited Z-raise to do now or on resume + const float zraise = _MAX(0, _MIN(current_position.z + POWER_LOSS_ZRAISE, Z_MAX_POS - 1) - current_position.z); + #else + constexpr float zraise = 0; + #endif + + // Save, including the limited Z raise + if (IS_SD_PRINTING()) save(true, zraise); + + // Disable all heaters to reduce power loss + thermalManager.disable_all_heaters(); + + #if ENABLED(BACKUP_POWER_SUPPLY) + // Do a hard-stop of the steppers (with possibly a loud thud) + quickstop_stepper(); + // With backup power a retract and raise can be done now + retract_and_lift(zraise); + #endif + + kill(GET_TEXT(MSG_OUTAGE_RECOVERY)); + } + #endif /** @@ -274,6 +322,8 @@ void PrintJobRecovery::write() { */ void PrintJobRecovery::resume() { + char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16]; + const uint32_t resume_sdpos = info.sdpos; // Get here before the stepper ISR overwrites it #if HAS_LEVELING @@ -282,52 +332,46 @@ void PrintJobRecovery::resume() { #endif // Reset E, raise Z, home XY... - gcode.process_subcommands_now_P(PSTR("G92.9 E0" - #if Z_HOME_DIR > 0 + #if Z_HOME_DIR > 0 - // If Z homing goes to max, just reset E and home all - "\n" - "G28R0" - TERN_(MARLIN_DEV_MODE, "S") + // If Z homing goes to max, just reset E and home all + gcode.process_subcommands_now_P(PSTR( + "G92.9 E0\n" + "G28R0" TERN_(MARLIN_DEV_MODE, "S") + )); - #else // "G92.9 E0 ..." + #else // "G92.9 E0 ..." - // Set Z to 0, raise Z by RECOVERY_ZRAISE, and Home (XY only for Cartesian) - // with no raise. (Only do simulated homing in Marlin Dev Mode.) - #if ENABLED(BACKUP_POWER_SUPPLY) - "Z" STRINGIFY(POWER_LOSS_ZRAISE) // Z-axis was already raised at outage - #else - "Z0\n" // Set Z=0 - "G1Z" STRINGIFY(POWER_LOSS_ZRAISE) // Raise Z - #endif - "\n" + // Set Z to 0, raise Z by info.zraise, and Home (XY only for Cartesian) + // with no raise. (Only do simulated homing in Marlin Dev Mode.) - "G28R0" - #if ENABLED(MARLIN_DEV_MODE) - "S" - #elif !IS_KINEMATIC - "XY" - #endif - #endif - )); + sprintf_P(cmd, PSTR("G92.9 E0 " + #if ENABLED(BACKUP_POWER_SUPPLY) + "Z%s" // Z was already raised at outage + #else + "Z0\nG1Z%s" // Set Z=0 and Raise Z now + #endif + ), + dtostrf(info.zraise, 1, 3, str_1) + ); + gcode.process_subcommands_now(cmd); + + gcode.process_subcommands_now_P(PSTR( + "G28R0" // No raise during G28 + TERN_(MARLIN_DEV_MODE, "S") // Simulated Homing + TERN_(IS_CARTESIAN, "XY") // Don't home Z on Cartesian + )); + + #endif // Pretend that all axes are homed axis_homed = axis_known_position = xyz_bits; - char cmd[MAX_CMD_SIZE+16], 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_extruder); - gcode.process_subcommands_now(cmd); - #endif - // Recover volumetric extrusion state #if DISABLED(NO_VOLUMETRICS) #if EXTRUDERS > 1 for (int8_t e = 0; e < EXTRUDERS; e++) { - dtostrf(info.filament_size[e], 1, 3, str_1); - sprintf_P(cmd, PSTR("M200 T%i D%s"), e, str_1); + sprintf_P(cmd, PSTR("M200 T%i D%s"), e, dtostrf(info.filament_size[e], 1, 3, str_1)); gcode.process_subcommands_now(cmd); } if (!info.volumetric_enabled) { @@ -336,8 +380,7 @@ void PrintJobRecovery::resume() { } #else if (info.volumetric_enabled) { - dtostrf(info.filament_size[0], 1, 3, str_1); - sprintf_P(cmd, PSTR("M200 D%s"), str_1); + sprintf_P(cmd, PSTR("M200 D%s"), dtostrf(info.filament_size[0], 1, 3, str_1)); gcode.process_subcommands_now(cmd); } #endif @@ -358,7 +401,7 @@ void PrintJobRecovery::resume() { const int16_t et = info.target_temperature[e]; if (et) { #if HAS_MULTI_HOTEND - sprintf_P(cmd, PSTR("T%i"), e); + sprintf_P(cmd, PSTR("T%i S"), e); gcode.process_subcommands_now(cmd); #endif sprintf_P(cmd, PSTR("M109 S%i"), et); @@ -367,6 +410,12 @@ void PrintJobRecovery::resume() { } #endif + // Select the previously active tool (with no_move) + #if EXTRUDERS > 1 + sprintf_P(cmd, PSTR("T%i S"), info.active_extruder); + gcode.process_subcommands_now(cmd); + #endif + // Restore print cooling fan speeds FANS_LOOP(i) { uint8_t f = info.fan_speed[i]; @@ -400,18 +449,21 @@ void PrintJobRecovery::resume() { memcpy(&mixer.gradient, &info.gradient, sizeof(info.gradient)); #endif - // Extrude and retract to clean the nozzle - #if POWER_LOSS_PURGE_LEN - //sprintf_P(cmd, PSTR("G1 E%d F200"), POWER_LOSS_PURGE_LEN); - //gcode.process_subcommands_now(cmd); - gcode.process_subcommands_now_P(PSTR("G1 E" STRINGIFY(POWER_LOSS_PURGE_LEN) " F200")); + // Un-retract if there was a retract at outage + #if POWER_LOSS_RETRACT_LEN + gcode.process_subcommands_now_P(PSTR("G1 E" STRINGIFY(POWER_LOSS_RETRACT_LEN) " F3000")); #endif - #if POWER_LOSS_RETRACT_LEN - sprintf_P(cmd, PSTR("G1 E%d F3000"), POWER_LOSS_PURGE_LEN - (POWER_LOSS_RETRACT_LEN)); + // Additional purge if configured + #if POWER_LOSS_PURGE_LEN + sprintf_P(cmd, PSTR("G1 E%d F200"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN)); gcode.process_subcommands_now(cmd); #endif + #if ENABLED(NOZZLE_CLEAN_FEATURE) + gcode.process_subcommands_now_P(PSTR("G12")); + #endif + // Move back to the saved XY sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"), dtostrf(info.current_position.x, 1, 3, str_1), @@ -429,13 +481,6 @@ void PrintJobRecovery::resume() { #endif gcode.process_subcommands_now(cmd); - // Un-retract - #if POWER_LOSS_PURGE_LEN - //sprintf_P(cmd, PSTR("G1 E%d F3000"), POWER_LOSS_PURGE_LEN); - //gcode.process_subcommands_now(cmd); - gcode.process_subcommands_now_P(PSTR("G1 E" STRINGIFY(POWER_LOSS_PURGE_LEN) " F3000")); - #endif - // Restore the feedrate sprintf_P(cmd, PSTR("G1 F%d"), info.feedrate); gcode.process_subcommands_now(cmd); @@ -476,6 +521,8 @@ void PrintJobRecovery::resume() { } DEBUG_EOL(); + DEBUG_ECHOLNPAIR("zraise: ", info.zraise); + #if HAS_HOME_OFFSET DEBUG_ECHOPGM("home_offset: "); LOOP_XYZ(i) { diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h index 3645f57c3c..89b619285d 100644 --- a/Marlin/src/feature/powerloss.h +++ b/Marlin/src/feature/powerloss.h @@ -26,6 +26,8 @@ */ #include "../sd/cardreader.h" +#include "../gcode/gcode.h" + #include "../inc/MarlinConfig.h" #if ENABLED(MIXING_EXTRUDER) @@ -45,6 +47,7 @@ typedef struct { // Machine state xyze_pos_t current_position; + float zraise; #if HAS_HOME_OFFSET xyz_pos_t home_offset; @@ -161,33 +164,34 @@ class PrintJobRecovery { static inline void cancel() { purge(); card.autostart_index = 0; } static void load(); - static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE)); + static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE), const float zraise=0); - #if PIN_EXISTS(POWER_LOSS) - static inline void outage() { - if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) - _outage(); - } - #endif + #if PIN_EXISTS(POWER_LOSS) + static inline void outage() { + if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) + _outage(); + } + #endif - static inline bool valid() { return info.valid(); } + static inline bool valid() { return info.valid(); } - #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - static void debug(PGM_P const prefix); - #else - static inline void debug(PGM_P const) {} - #endif + #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) + static void debug(PGM_P const prefix); + #else + static inline void debug(PGM_P const) {} + #endif private: static void write(); - #if ENABLED(BACKUP_POWER_SUPPLY) - static void raise_z(); - #endif + #if ENABLED(BACKUP_POWER_SUPPLY) + static void retract_and_lift(const float &zraise); + #endif - #if PIN_EXISTS(POWER_LOSS) - static void _outage(); - #endif + #if PIN_EXISTS(POWER_LOSS) + friend class GcodeSuite; + static void _outage(); + #endif }; extern PrintJobRecovery recovery; diff --git a/Marlin/src/gcode/feature/powerloss/M413.cpp b/Marlin/src/gcode/feature/powerloss/M413.cpp index 67e756d5d2..ea933872be 100644 --- a/Marlin/src/gcode/feature/powerloss/M413.cpp +++ b/Marlin/src/gcode/feature/powerloss/M413.cpp @@ -50,6 +50,9 @@ void GcodeSuite::M413() { if (parser.seen("RL")) recovery.load(); if (parser.seen('W')) recovery.save(true); if (parser.seen('P')) recovery.purge(); + #if PIN_EXISTS(POWER_LOSS) + if (parser.seen('O')) recovery._outage(); + #endif if (parser.seen('E')) serialprintPGM(recovery.exists() ? PSTR("PLR Exists\n") : PSTR("No PLR\n")); if (parser.seen('V')) serialprintPGM(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n")); #endif diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index d7701f4ae9..ba8cf1cfec 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -363,7 +363,7 @@ namespace Language_en { PROGMEM Language_Str MSG_PRINTING_OBJECT = _UxGT("Printing Object"); PROGMEM Language_Str MSG_CANCEL_OBJECT = _UxGT("Cancel Object"); PROGMEM Language_Str MSG_CANCEL_OBJECT_N = _UxGT("Cancel Object ="); - PROGMEM Language_Str MSG_OUTAGE_RECOVERY = _UxGT("Outage Recovery"); + PROGMEM Language_Str MSG_OUTAGE_RECOVERY = _UxGT("Power Outage"); PROGMEM Language_Str MSG_MEDIA_MENU = _UxGT("Print from Media"); PROGMEM Language_Str MSG_NO_MEDIA = _UxGT("No Media"); PROGMEM Language_Str MSG_DWELL = _UxGT("Sleep..."); diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index e62754a301..2e8b0314aa 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -2026,33 +2026,29 @@ void Temperature::disable_all_heaters() { TERN_(AUTOTEMP, planner.autotemp_enabled = false); - #if HAS_HOTEND - HOTEND_LOOP() setTargetHotend(0, e); - #endif - TERN_(HAS_HEATED_BED, setTargetBed(0)); - TERN_(HAS_HEATED_CHAMBER, setTargetChamber(0)); - // Unpause and reset everything TERN_(PROBING_HEATERS_OFF, pause(false)); - #define DISABLE_HEATER(N) { \ - setTargetHotend(0, N); \ - temp_hotend[N].soft_pwm_amount = 0; \ - WRITE_HEATER_##N(LOW); \ - } + #if HAS_HOTEND + HOTEND_LOOP() { + setTargetHotend(0, e); + temp_hotend[e].soft_pwm_amount = 0; + } + #endif #if HAS_TEMP_HOTEND + #define DISABLE_HEATER(N) WRITE_HEATER_##N(LOW) REPEAT(HOTENDS, DISABLE_HEATER); #endif #if HAS_HEATED_BED - temp_bed.target = 0; + setTargetBed(0); temp_bed.soft_pwm_amount = 0; WRITE_HEATER_BED(LOW); #endif #if HAS_HEATED_CHAMBER - temp_chamber.target = 0; + setTargetChamber(0); temp_chamber.soft_pwm_amount = 0; WRITE_HEATER_CHAMBER(LOW); #endif diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index cfd9f957c1..37700c7b8b 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -576,7 +576,7 @@ class Temperature { else if (temp_hotend[ee].target == 0) start_preheat_time(ee); #endif - TERN_(AUTO_POWER_CONTROL, powerManager.power_on()); + TERN_(AUTO_POWER_CONTROL, if (celsius) powerManager.power_on()); temp_hotend[ee].target = _MIN(celsius, temp_range[ee].maxtemp - HOTEND_OVERSHOOT); start_watching_hotend(ee); } @@ -624,7 +624,7 @@ class Temperature { #endif static void setTargetBed(const int16_t celsius) { - TERN_(AUTO_POWER_CONTROL, powerManager.power_on()); + TERN_(AUTO_POWER_CONTROL, if (celsius) powerManager.power_on()); temp_bed.target = #ifdef BED_MAX_TARGET _MIN(celsius, BED_MAX_TARGET) diff --git a/buildroot/tests/rambo-tests b/buildroot/tests/rambo-tests index 53ac729cbc..0b249e2149 100644 --- a/buildroot/tests/rambo-tests +++ b/buildroot/tests/rambo-tests @@ -40,6 +40,7 @@ opt_enable REPRAP_DISCOUNT_SMART_CONTROLLER LCD_PROGRESS_BAR LCD_PROGRESS_BAR_TE PSU_CONTROL AUTO_POWER_CONTROL POWER_LOSS_RECOVERY POWER_LOSS_PIN POWER_LOSS_STATE \ SLOW_PWM_HEATERS THERMAL_PROTECTION_CHAMBER LIN_ADVANCE EXTRA_LIN_ADVANCE_K \ HOST_ACTION_COMMANDS HOST_PROMPT_SUPPORT PINS_DEBUGGING MAX7219_DEBUG M114_DETAIL +opt_add DEBUG_POWER_LOSS_RECOVERY exec_test $1 $2 "RAMBO | EXTRUDERS 2 | CHAR LCD + SD | FIX Probe | ABL-Linear | Advanced Pause | PLR | LEDs ..." #