diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 370fbaf6ec..00f89662c7 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -1049,6 +1049,10 @@ #define EVENT_GCODE_SD_STOP "G28XY" // G-code to run on Stop Print (e.g., "G28XY" or "G27") + #if ENABLED(PRINTER_EVENT_LEDS) + #define PE_LEDS_COMPLETED_TIME (30*60) // (seconds) Time to keep the LED "done" color before restoring normal illumination + #endif + /** * Continue after Power-Loss (Creality3D) * diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 7adc8272ea..8130affe30 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -210,6 +210,24 @@ bool wait_for_heatup = true; // For M0/M1, this flag may be cleared (by M108) to exit the wait-for-user loop #if HAS_RESUME_CONTINUE bool wait_for_user; // = false; + + void wait_for_user_response(millis_t ms/*=0*/, const bool no_sleep/*=false*/) { + #if DISABLED(ADVANCED_PAUSE_FEATURE) + UNUSED(no_sleep); + #endif + KEEPALIVE_STATE(PAUSED_FOR_USER); + wait_for_user = true; + if (ms) ms += millis(); // expire time + while (wait_for_user && !(ms && ELAPSED(millis(), ms))) { + idle( + #if ENABLED(ADVANCED_PAUSE_FEATURE) + no_sleep + #endif + ); + } + wait_for_user = false; + } + #endif // Inactivity shutdown @@ -418,53 +436,8 @@ void startOrResumeJob() { } inline void finishSDPrinting() { - - bool did_state = true; - switch (card.sdprinting_done_state) { - - case 1: - if (print_job_timer.duration() > 60) - did_state = queue.enqueue_one_P(PSTR("M31")); - break; - - case 2: - did_state = queue.enqueue_one_P(PSTR("M77")); - break; - - case 3: - #if ENABLED(LCD_SET_PROGRESS_MANUALLY) - ui.set_progress_done(); - #endif - break; - - case 4: // Display "Click to Continue..." - #if HAS_LEDS_OFF_FLAG // 30 min timeout with LCD, 1 min without - did_state = queue.enqueue_one_P( - print_job_timer.duration() < 60 ? PSTR("M0Q1P1") : PSTR("M0Q1S" TERN(HAS_LCD_MENU, "1800", "60")) - ); - #endif - break; - - case 5: - #if ENABLED(POWER_LOSS_RECOVERY) - recovery.purge(); - #endif - - #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) - planner.finish_and_disable(); - #endif - - #if ENABLED(SD_REPRINT_LAST_SELECTED_FILE) - ui.reselect_last_file(); - #endif - - SERIAL_ECHOLNPGM(STR_FILE_PRINTED); - - default: - did_state = false; - card.sdprinting_done_state = 0; - } - if (did_state) ++card.sdprinting_done_state; + if (queue.enqueue_one_P(PSTR("M1001"))) + marlin_state = MF_RUNNING; } #endif // SDSUPPORT @@ -1209,7 +1182,7 @@ void loop() { #if ENABLED(SDSUPPORT) card.checkautostart(); if (card.flag.abort_sd_printing) abortSDPrinting(); - if (card.sdprinting_done_state) finishSDPrinting(); + if (marlin_state == MF_SD_COMPLETE) finishSDPrinting(); #endif queue.advance(); diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h index 3bce72ab80..e6678c5b18 100644 --- a/Marlin/src/MarlinCore.h +++ b/Marlin/src/MarlinCore.h @@ -83,6 +83,7 @@ enum MarlinState : uint8_t { MF_PAUSED = _BV(1), MF_WAITING = _BV(2), MF_STOPPED = _BV(3), + MF_SD_COMPLETE = _BV(4), MF_KILLED = _BV(7) }; @@ -98,6 +99,7 @@ extern bool wait_for_heatup; #if HAS_RESUME_CONTINUE extern bool wait_for_user; + void wait_for_user_response(millis_t ms=0, const bool no_sleep=false); #endif // Inactivity shutdown timer diff --git a/Marlin/src/feature/mmu2/mmu2.cpp b/Marlin/src/feature/mmu2/mmu2.cpp index 2df34176da..4506883f46 100644 --- a/Marlin/src/feature/mmu2/mmu2.cpp +++ b/Marlin/src/feature/mmu2/mmu2.cpp @@ -707,14 +707,13 @@ void MMU2::filament_runout() { if (recover) { LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER); BUZZ(200, 404); - wait_for_user = true; #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_USER_CONTINUE, PSTR("MMU2 Eject Recover"), CONTINUE_STR); #endif #if ENABLED(EXTENSIBLE_UI) ExtUI::onUserConfirmRequired_P(PSTR("MMU2 Eject Recover")); #endif - while (wait_for_user) idle(); + wait_for_user_response(); BUZZ(200, 404); BUZZ(200, 404); diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp index fb69803ee7..de73163cbd 100644 --- a/Marlin/src/feature/pause.cpp +++ b/Marlin/src/feature/pause.cpp @@ -184,7 +184,6 @@ bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_l #endif KEEPALIVE_STATE(PAUSED_FOR_USER); - wait_for_user = true; // LCD click or M108 will clear this #if ENABLED(HOST_PROMPT_SUPPORT) const char tool = '0' #if NUM_RUNOUT_SENSORS > 1 @@ -246,13 +245,13 @@ bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_l if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_PURGE); #endif - wait_for_user = true; #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Filament Purging..."), CONTINUE_STR); #endif #if ENABLED(EXTENSIBLE_UI) ExtUI::onUserConfirmRequired_P(PSTR("Filament Purging...")); #endif + wait_for_user = true; // A click or M108 breaks the purge_length loop for (float purge_count = purge_length; purge_count > 0 && wait_for_user; --purge_count) do_pause_e_move(1, ADVANCED_PAUSE_PURGE_FEEDRATE); wait_for_user = false; @@ -508,13 +507,13 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep // Wait for filament insert by user and press button KEEPALIVE_STATE(PAUSED_FOR_USER); - wait_for_user = true; // LCD click or M108 will clear this #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Nozzle Parked"), CONTINUE_STR); #endif #if ENABLED(EXTENSIBLE_UI) ExtUI::onUserConfirmRequired_P(PSTR("Nozzle Parked")); #endif + wait_for_user = true; // LCD click or M108 will clear this while (wait_for_user) { #if HAS_BUZZER filament_change_beep(max_beep_count); @@ -540,8 +539,7 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep ExtUI::onUserConfirmRequired_P(PSTR("HeaterTimeout")); #endif - // Wait for LCD click or M108 - while (wait_for_user) idle_no_sleep(); + wait_for_user_response(0, true); // Wait for LCD click or M108 #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_INFO, PSTR("Reheating")); diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 1ce1e3d5cb..1f9d710bb4 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -857,7 +857,11 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { #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 + case 1000: M1000(); break; // M1000: [INTERNAL] Resume from power-loss + #endif + + #if ENABLED(SDSUPPORT) + case 1001: M1001(); break; // M1001: [INTERNAL] Handle SD completion #endif #if ENABLED(MAX7219_GCODE) diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index c1024ac440..8e5d054d99 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -968,6 +968,10 @@ private: static void M1000(); #endif + #if ENABLED(SDSUPPORT) + static void M1001(); + #endif + #if ENABLED(MAX7219_GCODE) static void M7219(); #endif diff --git a/Marlin/src/gcode/lcd/M0_M1.cpp b/Marlin/src/gcode/lcd/M0_M1.cpp index 43b39226f7..190023b090 100644 --- a/Marlin/src/gcode/lcd/M0_M1.cpp +++ b/Marlin/src/gcode/lcd/M0_M1.cpp @@ -24,23 +24,19 @@ #if HAS_RESUME_CONTINUE -#include "../gcode.h" -#include "../../module/planner.h" - #include "../../inc/MarlinConfig.h" +#include "../gcode.h" + +#include "../../module/planner.h" // for synchronize() +#include "../../MarlinCore.h" // for wait_for_user_response() + #if HAS_LCD_MENU #include "../../lcd/ultralcd.h" -#endif - -#if ENABLED(EXTENSIBLE_UI) +#elif ENABLED(EXTENSIBLE_UI) #include "../../lcd/extui/ui_api.h" #endif -#if HAS_LEDS_OFF_FLAG - #include "../../feature/leds/printer_event_leds.h" -#endif - #if ENABLED(HOST_PROMPT_SUPPORT) #include "../../feature/host_actions.h" #endif @@ -56,18 +52,11 @@ void GcodeSuite::M0_M1() { planner.synchronize(); - #if HAS_LEDS_OFF_FLAG - const bool seenQ = parser.seen('Q'); - if (seenQ) printerEventLEDs.onPrintCompleted(); // Change LED color for Print Completed - #else - constexpr bool seenQ = false; - #endif - #if HAS_LCD_MENU if (parser.string_arg) ui.set_status(parser.string_arg, true); - else if (!seenQ) { + else { LCD_MESSAGEPGM(MSG_USERWAIT); #if ENABLED(LCD_PROGRESS_BAR) && PROGRESS_MSG_EXPIRE > 0 ui.reset_progress_bar_timeout(); @@ -75,12 +64,10 @@ void GcodeSuite::M0_M1() { } #elif ENABLED(EXTENSIBLE_UI) - if (!seenQ) { - if (parser.string_arg) - ExtUI::onUserConfirmRequired(parser.string_arg); // Can this take an SRAM string?? - else - ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_USERWAIT)); - } + if (parser.string_arg) + ExtUI::onUserConfirmRequired(parser.string_arg); // Can this take an SRAM string?? + else + ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_USERWAIT)); #else if (parser.string_arg) { @@ -90,25 +77,15 @@ void GcodeSuite::M0_M1() { #endif - KEEPALIVE_STATE(PAUSED_FOR_USER); - wait_for_user = true; - #if ENABLED(HOST_PROMPT_SUPPORT) - if (!seenQ) host_prompt_do(PROMPT_USER_CONTINUE, parser.codenum ? PSTR("M1 Stop") : PSTR("M0 Stop"), CONTINUE_STR); + host_prompt_do(PROMPT_USER_CONTINUE, parser.codenum ? PSTR("M1 Stop") : PSTR("M0 Stop"), CONTINUE_STR); #endif - if (ms > 0) ms += millis(); // wait until this time for a click - while (wait_for_user && (ms == 0 || PENDING(millis(), ms))) idle(); - - #if HAS_LEDS_OFF_FLAG - printerEventLEDs.onResumeAfterWait(); - #endif + wait_for_user_response(ms); #if HAS_LCD_MENU - if (!seenQ) ui.reset_status(); + ui.reset_status(); #endif - - wait_for_user = false; } #endif // HAS_RESUME_CONTINUE diff --git a/Marlin/src/gcode/sd/M1001.cpp b/Marlin/src/gcode/sd/M1001.cpp new file mode 100644 index 0000000000..4261b57f97 --- /dev/null +++ b/Marlin/src/gcode/sd/M1001.cpp @@ -0,0 +1,109 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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(SDSUPPORT) + +#include "../gcode.h" +#include "../../module/printcounter.h" + +#if EITHER(LCD_SET_PROGRESS_MANUALLY, SD_REPRINT_LAST_SELECTED_FILE) + #include "../../lcd/ultralcd.h" +#endif + +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../feature/powerloss.h" +#endif + +#if HAS_LEDS_OFF_FLAG + #include "../../feature/leds/printer_event_leds.h" +#endif + +#if ENABLED(EXTENSIBLE_UI) + #include "../../lcd/extui/ui_api.h" +#endif + +#if ENABLED(HOST_ACTION_COMMANDS) + #include "../../feature/host_actions.h" +#endif + +#if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) + #include "../../module/planner.h" +#endif + +#ifndef PE_LEDS_COMPLETED_TIME + #define PE_LEDS_COMPLETED_TIME (30*60) +#endif + +/** + * M1001: Execute actions for SD print completion + */ +void GcodeSuite::M1001() { + + // Report total print time + const bool long_print = print_job_timer.duration() > 60; + if (long_print) gcode.process_subcommands_now_P(PSTR("M31")); + + // Stop the print job timer + gcode.process_subcommands_now_P(PSTR("M77")); + + // Set the progress bar "done" state + #if ENABLED(LCD_SET_PROGRESS_MANUALLY) + ui.set_progress_done(); + #endif + + // Purge the recovery file + #if ENABLED(POWER_LOSS_RECOVERY) + recovery.purge(); + #endif + + // Announce SD file completion + SERIAL_ECHOLNPGM(STR_FILE_PRINTED); + + // Update the status LED color + #if HAS_LEDS_OFF_FLAG + if (long_print) { + printerEventLEDs.onPrintCompleted(); + #if ENABLED(EXTENSIBLE_UI) + ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_PRINT_DONE)); + #endif + #if ENABLED(HOST_PROMPT_SUPPORT) + host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_PRINT_DONE), CONTINUE_STR); + #endif + wait_for_user_response(1000UL * TERN(HAS_LCD_MENU, PE_LEDS_COMPLETED_TIME, 30)); + printerEventLEDs.onResumeAfterWait(); + } + #endif + + // Wait for the queue to empty (and "clean"), inject SD_FINISHED_RELEASECOMMAND + #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) + planner.finish_and_disable(); + #endif + + // Re-select the last printed file in the UI + #if ENABLED(SD_REPRINT_LAST_SELECTED_FILE) + ui.reselect_last_file(); + #endif +} + +#endif // SDSUPPORT diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 75c13de9c7..8fa22e2c71 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -352,6 +352,7 @@ namespace Language_en { PROGMEM Language_Str MSG_PRINT_PAUSED = _UxGT("Print Paused"); PROGMEM Language_Str MSG_PRINTING = _UxGT("Printing..."); PROGMEM Language_Str MSG_PRINT_ABORTED = _UxGT("Print Aborted"); + PROGMEM Language_Str MSG_PRINT_DONE = _UxGT("Print Done"); PROGMEM Language_Str MSG_NO_MOVE = _UxGT("No Move."); PROGMEM Language_Str MSG_KILLED = _UxGT("KILLED. "); PROGMEM Language_Str MSG_STOPPED = _UxGT("STOPPED. "); diff --git a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp index a27e2c9726..ac80870ac8 100644 --- a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp +++ b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp @@ -61,16 +61,14 @@ void _man_probe_pt(const xy_pos_t &xy) { float lcd_probe_pt(const xy_pos_t &xy) { _man_probe_pt(xy); - KEEPALIVE_STATE(PAUSED_FOR_USER); ui.defer_status_screen(); - wait_for_user = true; #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Delta Calibration in progress"), CONTINUE_STR); #endif #if ENABLED(EXTENSIBLE_UI) ExtUI::onUserConfirmRequired_P(PSTR("Delta Calibration in progress")); #endif - while (wait_for_user) idle(); + wait_for_user_response(); ui.goto_previous_screen_no_defer(); return current_position.z; } diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp index 7ff1440559..bbcb678dd8 100644 --- a/Marlin/src/lcd/ultralcd.cpp +++ b/Marlin/src/lcd/ultralcd.cpp @@ -776,6 +776,13 @@ void MarlinUI::update() { // If the action button is pressed... static bool wait_for_unclick; // = false + auto do_click = [&]{ + wait_for_unclick = true; // - Set debounce flag to ignore continous clicks + lcd_clicked = !wait_for_user && !no_reentry; // - Keep the click if not waiting for a user-click + wait_for_user = false; // - Any click clears wait for user + quick_feedback(); // - Always make a click sound + }; + #if ENABLED(TOUCH_BUTTONS) if (touch_buttons) { RESET_STATUS_TIMEOUT(); @@ -796,12 +803,8 @@ void MarlinUI::update() { } } } - else if (!wait_for_unclick && (buttons & EN_C)) { // OK button, if not waiting for a debounce release: - wait_for_unclick = true; // - Set debounce flag to ignore continous clicks - lcd_clicked = !wait_for_user && !no_reentry; // - Keep the click if not waiting for a user-click - wait_for_user = false; // - Any click clears wait for user - quick_feedback(); // - Always make a click sound - } + else if (!wait_for_unclick && (buttons & EN_C)) // OK button, if not waiting for a debounce release: + do_click(); } else // keep wait_for_unclick value @@ -810,12 +813,7 @@ void MarlinUI::update() { { // Integrated LCD click handling via button_pressed if (!external_control && button_pressed()) { - if (!wait_for_unclick) { // If not waiting for a debounce release: - wait_for_unclick = true; // - Set debounce flag to ignore continous clicks - lcd_clicked = !wait_for_user && !no_reentry; // - Keep the click if not waiting for a user-click - wait_for_user = false; // - Any click clears wait for user - quick_feedback(); // - Always make a click sound - } + if (!wait_for_unclick) do_click(); // Handle the click } else wait_for_unclick = false; diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index 962c230775..6e3f2baa66 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -134,12 +134,10 @@ xyz_pos_t Probe::offset; // Initialized by settings.load() LCD_MESSAGEPGM(MSG_MANUAL_DEPLOY_TOUCHMI); ui.return_to_status(); - KEEPALIVE_STATE(PAUSED_FOR_USER); - wait_for_user = true; // LCD click or M108 will clear this #if ENABLED(HOST_PROMPT_SUPPORT) - host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Deploy TouchMI probe."), CONTINUE_STR); + host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Deploy TouchMI"), CONTINUE_STR); #endif - while (wait_for_user) idle(); + wait_for_user_response(); ui.reset_status(); ui.goto_screen(prev_screen); @@ -297,15 +295,13 @@ FORCE_INLINE void probe_specific_action(const bool deploy) { serialprintPGM(ds_str); SERIAL_EOL(); - KEEPALIVE_STATE(PAUSED_FOR_USER); - wait_for_user = true; #if ENABLED(HOST_PROMPT_SUPPORT) host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Stow Probe"), CONTINUE_STR); #endif #if ENABLED(EXTENSIBLE_UI) ExtUI::onUserConfirmRequired_P(PSTR("Stow Probe")); #endif - while (wait_for_user) idle(); + wait_for_user_response(); ui.reset_status(); } while( diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index aadd355afd..80e94b88b5 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -49,7 +49,6 @@ // public: card_flags_t CardReader::flag; -uint8_t CardReader::sdprinting_done_state; char CardReader::filename[FILENAME_LENGTH], CardReader::longFilename[LONG_FILENAME_LENGTH]; int8_t CardReader::autostart_index; @@ -1089,7 +1088,7 @@ void CardReader::fileHasFinished() { presort(); #endif - sdprinting_done_state = 1; + marlin_state = MF_SD_COMPLETE; } } diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 749ece01d3..955a8b69b3 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -49,7 +49,6 @@ typedef struct { class CardReader { public: - static uint8_t sdprinting_done_state; static card_flags_t flag; // Flags (above) static char filename[FILENAME_LENGTH], // DOS 8.3 filename of the selected item longFilename[LONG_FILENAME_LENGTH]; // Long name of the selected item