From 1c4a7dd30e8f6ddc1cb876dafe3712d4557a3ca1 Mon Sep 17 00:00:00 2001 From: Systemic Date: Mon, 23 Apr 2018 10:18:05 +0200 Subject: [PATCH] Power Resume implementation --- Marlin/AnycubicTFT.cpp | 17 +- Marlin/Conditionals_post.h | 2 +- Marlin/Configuration.h | 25 +-- Marlin/Configuration_adv.h | 20 +- Marlin/Marlin.h | 43 +++++ Marlin/Marlin_main.cpp | 379 ++++++++++++++++++++++++++++++++++++- Marlin/Version.h | 2 +- Marlin/cardreader.cpp | 51 +++++ Marlin/cardreader.h | 16 +- Marlin/pins_TRIGORILLA.h | 27 +-- Marlin/stopwatch.cpp | 59 +++--- Marlin/stopwatch.h | 83 ++++---- Marlin/types.h | 2 + Marlin/ultralcd.cpp | 19 ++ 14 files changed, 636 insertions(+), 109 deletions(-) diff --git a/Marlin/AnycubicTFT.cpp b/Marlin/AnycubicTFT.cpp index d5ffd3ec85da..0f0aa86618df 100644 --- a/Marlin/AnycubicTFT.cpp +++ b/Marlin/AnycubicTFT.cpp @@ -191,7 +191,7 @@ void AnycubicTFTClass::HandleSpecialMenu() { if(strcmp(SelectedDirectory, "")==0) { SpecialMenu=true; - } else if (strcmp(SelectedDirectory, "")==0) { + } else if (strcmp(SelectedDirectory, "")==0) { SERIAL_PROTOCOLLNPGM("Special Menu: Auto Tune PID"); enqueue_and_echo_commands_P(PSTR("M303 C8 S200 U1")); } else if (strcmp(SelectedDirectory, "")==0) { @@ -209,6 +209,9 @@ void AnycubicTFTClass::HandleSpecialMenu() } else if (strcmp(SelectedDirectory, "")==0) { SERIAL_PROTOCOLLNPGM("Special Menu: PreHeat 200 60"); enqueue_and_echo_commands_P(PSTR("M140 S60\nM104 S200 T0")); + } else if (strcmp(SelectedDirectory, "")==0) { + SERIAL_PROTOCOLLNPGM("Special Menu: Outage Recover"); + enqueue_and_echo_commands_P(PSTR("G8")); } else if (strcmp(SelectedDirectory, "")==0) { SpecialMenu=false; } @@ -234,9 +237,11 @@ void AnycubicTFTClass::Ls() ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); - ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); - ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); - break; + ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); + ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); + ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); + ANYCUBIC_SERIAL_PROTOCOLLNPGM(""); + break; default: break; @@ -875,11 +880,11 @@ void AnycubicTFTClass::GetCommandFromTFT() case 30: // A30 assist leveling, the original function was canceled if(CodeSeen('S')) { #ifdef ANYCUBIC_TFT_DEBUG - SERIAL_ECHOLNPGM("TFT Entering level menue..."); + SERIAL_ECHOLNPGM("TFT Entering level menu..."); #endif } else if(CodeSeen('O')) { #ifdef ANYCUBIC_TFT_DEBUG - SERIAL_ECHOLNPGM("TFT Leveling started and movint to front left..."); + SERIAL_ECHOLNPGM("TFT Leveling started and moving to front left..."); #endif enqueue_and_echo_commands_P(PSTR("G91\nG1 Z10 F240\nG90\nG28\nG29\nG1 X20 Y20 F6000\nG1 Z0 F240")); } else if(CodeSeen('T')) { diff --git a/Marlin/Conditionals_post.h b/Marlin/Conditionals_post.h index 9b69e1ffcbf3..28649938f6ad 100644 --- a/Marlin/Conditionals_post.h +++ b/Marlin/Conditionals_post.h @@ -1105,7 +1105,7 @@ #define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y)) // Add commands that need sub-codes to this list - #define USE_GCODE_SUBCODES ENABLED(G38_PROBE_TARGET) || ENABLED(CNC_COORDINATE_SYSTEMS) + #define USE_GCODE_SUBCODES ENABLED(G38_PROBE_TARGET) || ENABLED(CNC_COORDINATE_SYSTEMS) || ENABLED(POWER_LOSS_RECOVERY) // Parking Extruder #if ENABLED(PARKING_EXTRUDER) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 7d28febefb43..ae4e4a898419 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -82,9 +82,9 @@ // startup. Implementation of an idea by Prof Braino to inform user that any changes made to this // build by the user have been successfully uploaded into firmware. #define STRING_CONFIG_H_AUTHOR "(Systemic, OriginalTFT)" // Who made the changes. -#define SHOW_BOOTSCREEN -#define STRING_SPLASH_LINE1 SHORT_BUILD_VERSION // will be shown during bootup in line 1 -#define STRING_SPLASH_LINE2 WEBSITE_URL // will be shown during bootup in line 2 +//#define SHOW_BOOTSCREEN +//#define STRING_SPLASH_LINE1 SHORT_BUILD_VERSION // will be shown during bootup in line 1 +//#define STRING_SPLASH_LINE2 WEBSITE_URL // will be shown during bootup in line 2 // // *** VENDORS PLEASE READ ***************************************************** @@ -586,8 +586,8 @@ * When changing speed and direction, if the difference is less than the * value set here, it may happen instantaneously. */ -#define DEFAULT_XJERK 10.0 -#define DEFAULT_YJERK 10.0 +#define DEFAULT_XJERK 8.0 +#define DEFAULT_YJERK 8.0 #define DEFAULT_ZJERK 0.4 #define DEFAULT_EJERK 5.0 @@ -745,7 +745,7 @@ #define Z_PROBE_OFFSET_RANGE_MAX 20 // Enable the M48 repeatability test to test probe accuracy -#define Z_MIN_PROBE_REPEATABILITY_TEST +// #define Z_MIN_PROBE_REPEATABILITY_TEST // For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 // :{ 0:'Low', 1:'High' } @@ -790,7 +790,9 @@ //#define NO_MOTION_BEFORE_HOMING // Inhibit movement until all axes have been homed -#define Z_HOMING_HEIGHT 30 // (in mm) Minimal z height before homing (G28) for Z clearance above the bed, clamps, ... +#define UNKNOWN_Z_NO_RAISE // Don't raise Z (lower the bed) if Z is "unknown." For beds that fall when Z is powered off. + +#define Z_HOMING_HEIGHT 10 // (in mm) Minimal z height before homing (G28) for Z clearance above the bed, clamps, ... // Be sure you have this distance over your Z_MAX_POS in case. // Direction of endstops when homing; 1=MAX, -1=MIN @@ -893,7 +895,7 @@ */ //#define AUTO_BED_LEVELING_3POINT //#define AUTO_BED_LEVELING_LINEAR -#define AUTO_BED_LEVELING_BILINEAR +//#define AUTO_BED_LEVELING_BILINEAR //#define AUTO_BED_LEVELING_UBL //#define MESH_BED_LEVELING @@ -919,7 +921,7 @@ /** * Enable the G26 Mesh Validation Pattern tool. */ - #define G26_MESH_VALIDATION // Enable G26 mesh validation + //#define G26_MESH_VALIDATION // Enable G26 mesh validation #if ENABLED(G26_MESH_VALIDATION) #define MESH_TEST_NOZZLE_SIZE 0.4 // (mm) Diameter of primary nozzle. #define MESH_TEST_LAYER_HEIGHT 0.2 // (mm) Default layer height for the G26 Mesh Validation Tool. @@ -1805,6 +1807,7 @@ // Enable Anycubic TFT #define ANYCUBIC_TFT_MODEL #define ANYCUBIC_FILAMENT_RUNOUT_SENSOR -#define ANYCUBIC_TFT_DEBUG - +//#define ANYCUBIC_TFT_DEBUG +#define POWER_LOSS_RECOVERY +#define DEBUG_POWER_LOSS_RECOVERY #endif // CONFIGURATION_H diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index f675803eb217..4bebc76ae55d 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -66,14 +66,6 @@ #if ENABLED(THERMAL_PROTECTION_HOTENDS) #define THERMAL_PROTECTION_PERIOD 40 // Seconds #define THERMAL_PROTECTION_HYSTERESIS 4 // Degrees Celsius - -// I3 Mega Protection settings : - -//#if ENABLED(THERMAL_PROTECTION_HOTENDS) -// #define THERMAL_PROTECTION_PERIOD 250 // Seconds- -// #define THERMAL_PROTECTION_HYSTERESIS 45 // Degrees Celsius - -// I3 MEGA END /** * Whenever an M104, M109, or M303 increases the target temperature, the @@ -536,6 +528,16 @@ // Add an option in the menu to run all auto#.g files //#define MENU_ADDAUTOSTART + /** + * Continue after Power-Loss (Creality3D) + * + * Store the current state to the SD Card at the start of each layer + * during SD printing. If the recovery file is found at boot time, present + * an option on the LCD screen to continue the print from the last-known + * point in the file. + */ + #define POWER_LOSS_RECOVERY + /** * Sort SD file listings in alphabetical order. * @@ -694,7 +696,7 @@ * K=0 means advance disabled. * See Marlin documentation for calibration instructions. */ -#define LIN_ADVANCE +//#define LIN_ADVANCE #if ENABLED(LIN_ADVANCE) #define LIN_ADVANCE_K 0 diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h index 2c68b8cbfab3..fcb3c1e32d7b 100644 --- a/Marlin/Marlin.h +++ b/Marlin/Marlin.h @@ -539,4 +539,47 @@ void do_blocking_move_to_xy(const float &x, const float &y, const float &fr_mm_s #endif // CARTESIAN +#if ENABLED(POWER_LOSS_RECOVERY) + + #include "cardreader.h" + + typedef struct { + uint8_t valid_head; + + // Machine state + float current_position[NUM_AXIS], feedrate; + int16_t target_temperature[HOTENDS], + target_temperature_bed, + fanSpeeds[FAN_COUNT]; + + #if HAS_LEVELING + bool leveling; + float fade; + #endif + + // Command queue + uint8_t cmd_queue_index_r, commands_in_queue; + char command_queue[BUFSIZE][MAX_CMD_SIZE]; + + // SD File position + char sd_filename[MAXPATHNAMELENGTH]; + uint32_t sdpos; + + // Job elapsed time + millis_t print_job_elapsed; + + uint8_t valid_foot; + } job_recovery_info_t; + + extern job_recovery_info_t job_recovery_info; + extern uint8_t job_recovery_commands_count; + enum JobRecoveryPhase : unsigned char { + JOB_RECOVERY_IDLE, + JOB_RECOVERY_MAYBE, + JOB_RECOVERY_YES + }; + extern JobRecoveryPhase job_recovery_phase; + +#endif // POWER_LOSS_RECOVERY + #endif // MARLIN_H diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index e8e69c82ee95..c912d4885a68 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -352,6 +352,11 @@ #include "AnycubicTFT.h" #endif +#if ENABLED(POWER_LOSS_RECOVERY) + int PowerInt= 6; +#endif + + bool Running = true; uint8_t marlin_debug_flags = DEBUG_NONE; @@ -769,6 +774,14 @@ void report_current_position_detail(); print_xyz(PSTR(" " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n"), VAR); }while(0) #endif +#if ENABLED(POWER_LOSS_RECOVERY) + void setup_OutageTestPin(){ + pinMode(OUTAGETEST_PIN,INPUT); + pinMode(OUTAGECON_PIN,OUTPUT); + WRITE(OUTAGECON_PIN,LOW); + } +#endif + /** * sync_plan_position * @@ -1210,6 +1223,33 @@ inline void get_serial_commands() { } } + #if ENABLED(POWER_LOSS_RECOVERY) + + #if HAS_LEVELING + #define APPEND_CMD_COUNT 7 + #else + #define APPEND_CMD_COUNT 5 + #endif + + job_recovery_info_t job_recovery_info; + static char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE]; + uint8_t job_recovery_commands_count; // = 0 + JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE; + + 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_IDLE; + } + return true; + } + return false; + } + + #endif + #endif // SDSUPPORT /** @@ -1225,6 +1265,11 @@ 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 @@ -3248,6 +3293,239 @@ static void homeaxis(const AxisEnum axis) { #endif + +#if ENABLED(POWER_LOSS_RECOVERY) + + //#define SAVE_EACH_CMD_MODE + //#define SAVE_INFO_INTERVAL (1000 * 10) + + //#define DEBUG_POWER_LOSS_RECOVERY + + #ifdef DEBUG_POWER_LOSS_RECOVERY + void debug_print_job_recovery(const bool recovery) { + 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_PROTOCOLPAIR(": ", job_recovery_info.current_position[i]); + SERIAL_EOL(); + SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate); + SERIAL_PROTOCOLPGM("target_temperature"); + HOTEND_LOOP() SERIAL_PROTOCOLPAIR(": ", job_recovery_info.target_temperature[e]); + SERIAL_EOL(); + SERIAL_PROTOCOLPGM("fanSpeeds"); + for(uint8_t i = 0; i < FAN_COUNT; i++) SERIAL_PROTOCOLPAIR(": ", job_recovery_info.fanSpeeds[i]); + SERIAL_EOL(); + #if HAS_LEVELING + SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling)); + SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade)); + #endif + SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed); + SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", job_recovery_info.cmd_queue_index_r); + SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", 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"); + } + } + #endif // DEBUG_POWER_LOSS_RECOVERY + + /** + * Check for Print Job Recovery + * If the file has a saved state, populate the job_recovery_commands queue + */ + void do_print_job_recovery() { + memset(&job_recovery_info, 0, sizeof(job_recovery_info)); + ZERO(job_recovery_commands); + + if (!card.cardOK) card.initsd(); + + if (card.cardOK) { + + #ifdef 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" + #if !IS_KINEMATIC + " X Y" // Home X and Y for Cartesian + #endif + )); + + #if HAS_LEVELING + // Restore leveling state before G92 sets Z + // This ensures the steppers correspond to the native Z + sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), job_recovery_info.fade); + #endif + + char str_1[16], str_2[16]; + 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 + + strcpy_P(job_recovery_commands[ind++], PSTR("M117 Continuing...")); + + uint8_t r = job_recovery_info.cmd_queue_index_r; + while (job_recovery_info.commands_in_queue) { + strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]); + job_recovery_info.commands_in_queue--; + r = (r + 1) % BUFSIZE; + } + + job_recovery_commands_count = ind; + + #ifdef DEBUG_POWER_LOSS_RECOVERY + debug_print_job_recovery(true); + #endif + + card.openFile(job_recovery_info.sd_filename, true); + card.setIndex(job_recovery_info.sdpos); + + // need to check for start or for resume + // print_job_timer.start(); + + // RECOVER JOB START + char cmd[20]; + + // Turn leveling off and home + enqueue_and_echo_commands_P(PSTR("M420 S0\nG28" + #if !IS_KINEMATIC + " X Y" + #endif + )); + + // Restore the bed temperature + sprintf_P(cmd, PSTR("M190 S%i"), job_recovery_info.target_temperature_bed); + enqueue_and_echo_command(cmd); + + // Restore all hotend temperatures + HOTEND_LOOP() { + sprintf_P(cmd, PSTR("M109 S%i"), job_recovery_info.target_temperature[e]); + enqueue_and_echo_command(cmd); + } + + // Restore print cooling fan speeds + for (uint8_t i = 0; i < FAN_COUNT; i++) { + sprintf_P(cmd, PSTR("M106 P%i S%i"), i, job_recovery_info.fanSpeeds[i]); + enqueue_and_echo_command(cmd); + } + + // Start draining the job recovery command queue + job_recovery_phase = JOB_RECOVERY_YES; + + // Resume the print job timer + if (job_recovery_info.print_job_elapsed) + print_job_timer.resume(job_recovery_info.print_job_elapsed); + + // Start getting commands from SD + card.startFileprint(); + + // RECOVER JOB END + + } + else { + if (job_recovery_info.valid_head != job_recovery_info.valid_foot) + lcd_setstatusPGM(PSTR("INVALID DATA")); + memset(&job_recovery_info, 0, sizeof(job_recovery_info)); + } + } + } + } + + void save_job_recovery_info() { + //static millis_t save_time; // = 0; // Init on reset + //millis_t cur_time = millis(); + if ( + #if ENABLED(SAVE_EACH_CMD_MODE) + true + #else + (current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS]) + #endif + // || ELAPSED(cur_time, save_time) + ) { + //save_time = cur_time + SAVE_INFO_INTERVAL; + + // 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; + + // Machine state + COPY(job_recovery_info.current_position, current_position); + job_recovery_info.feedrate = feedrate_mm_s; + COPY(job_recovery_info.target_temperature, thermalManager.target_temperature); + job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed; + COPY(job_recovery_info.fanSpeeds, fanSpeeds); + + #if HAS_LEVELING + job_recovery_info.leveling = planner.leveling_active; + job_recovery_info.fade = ( + #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) + planner.z_fade_height + #else + 0 + #endif + ); + #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); + + // Elapsed print job time + job_recovery_info.print_job_elapsed = print_job_timer.duration() * 1000UL; + + // SD file position + card.getAbsFilename(job_recovery_info.sd_filename); + //strcpy (job_recovery_info.sd_filename ,"RECOVER.GCO"); + job_recovery_info.sdpos = card.getIndex(); + + #ifdef DEBUG_POWER_LOSS_RECOVERY + SERIAL_PROTOCOLLNPGM("Saving job_recovery_info"); + debug_print_job_recovery(false); + #endif + + card.openJobRecoveryFile(false); + (void)card.saveJobRecoveryInfo(); + } + } + +#endif // POWER_LOSS_RECOVERY + + + + + + /** * *************************************************************************** * ***************************** G-CODE HANDLING ***************************** @@ -3537,6 +3815,70 @@ inline void gcode_G4() { #endif // BEZIER_CURVE_SUPPORT +#if ENABLED(POWER_LOSS_RECOVERY) + + void PowerKill() + { + #ifdef POWER_LOSS_RECOVERY + //debug_print_job_recovery(false); + SERIAL_ECHOLNPGM("G6 POWER LOSS RECOVERY ACTIVATED"); + save_job_recovery_info(); + #endif + + /*#ifdef DEBUG_POWER_LOSS_RECOVERY + //save_job_recovery_info(); + SERIAL_ECHOLNPGM("G5 POWER LOSS RECOVERY DEBUG"); + debug_print_job_recovery(false); + #endif*/ + } + + void PowerRecoveryInfo() + { + #ifdef DEBUG_POWER_LOSS_RECOVERY + //debug_print_job_recovery(false); + SERIAL_ECHOLNPGM("G7 POWER LOSS RECOVERY INFO"); + debug_print_job_recovery(true); + #endif + } + + void PowerRecovery() + { + #ifdef POWER_LOSS_RECOVERY + //debug_print_job_recovery(false); + SERIAL_ECHOLNPGM("G8 POWER LOSS RECOVERY FROM SD"); + //lcd_sdcard_recover_job(); + do_print_job_recovery(); + #endif + + } + + inline void gcode_G6() { + #ifdef POWER_LOSS_RECOVERY + WRITE(OUTAGECON_PIN,HIGH); + if(1==READ(OUTAGETEST_PIN)) + { + #ifdef POWER_LOSS_RECOVERY + attachInterrupt(PowerInt,PowerKill,CHANGE); //INITIAL SET + SERIAL_ECHOLNPGM("G6 POWER LOSS RECOVERY ACTIVATED"); + #endif + } + #endif + } + + inline void gcode_G7() { + #ifdef POWER_LOSS_RECOVERY + PowerRecoveryInfo(); + #endif + } + + inline void gcode_G8() { + #ifdef POWER_LOSS_RECOVERY + PowerRecovery(); + #endif + } + +#endif // POWER_LOSS_RECOVERY + #if ENABLED(FWRETRACT) /** @@ -4041,9 +4383,16 @@ inline void gcode_G28(const bool always_home_all) { #endif - if (home_all || homeX || homeY) { + const float z_homing_height = ( + #if ENABLED(UNKNOWN_Z_NO_RAISE) + !axis_known_position[Z_AXIS] ? 0 : + #endif + (parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT) + ); + + if (z_homing_height && (home_all || homeX || homeY)) { // Raise Z before homing any other axes and z is not already high enough (never lower z) - destination[Z_AXIS] = Z_HOMING_HEIGHT; + destination[Z_AXIS] = z_homing_height; if (destination[Z_AXIS] > current_position[Z_AXIS]) { #if ENABLED(DEBUG_LEVELING_FEATURE) @@ -6773,6 +7122,11 @@ inline void gcode_M17() { * M24: Start or Resume SD Print */ inline void gcode_M24() { + + #if ENABLED(POWER_LOSS_RECOVERY) + card.removeJobRecoveryFile(); + #endif + #if ENABLED(PARK_HEAD_ON_PAUSE) resume_print(); #endif @@ -11660,6 +12014,18 @@ void process_parsed_command() { break; #endif // BEZIER_CURVE_SUPPORT + #if ENABLED(POWER_LOSS_RECOVERY) + case 6: // G6: Enable Power Loss + gcode_G6(); + break; + case 7: // G7: Power Loss Recovery Info + gcode_G7(); + break; + case 8: // G8: Power Loss Recovery Start + gcode_G8(); + break; + #endif // BEZIER_CURVE_SUPPORT + #if ENABLED(FWRETRACT) case 10: // G10: retract gcode_G10(); @@ -14386,6 +14752,7 @@ void setup() { #ifdef ANYCUBIC_TFT_MODEL // Setup AnycubicTFT AnycubicTFT.Setup(); + setup_OutageTestPin(); #endif #if ENABLED(HAVE_TMC2208) @@ -14438,6 +14805,10 @@ void setup() { thermalManager.init(); // Initialize temperature loop + //#if ENABLED(POWER_LOSS_RECOVERY) + // do_print_job_recovery(); + //#endif + #if ENABLED(USE_WATCHDOG) watchdog_init(); #endif @@ -14643,7 +15014,9 @@ void loop() { } else process_next_command(); - + #if ENABLED(POWER_LOSS_RECOVERY) + // if (card.cardOK && card.sdprinting) save_job_recovery_info(); + #endif #else process_next_command(); diff --git a/Marlin/Version.h b/Marlin/Version.h index 29458653774b..abf5c9015d20 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -48,7 +48,7 @@ * here we define this default string as the date where the latest release * version was tagged. */ - #define STRING_DISTRIBUTION_DATE "2017-12-25 12:00" + #define STRING_DISTRIBUTION_DATE "2018-04-22 12:00" /** * Required minimum Configuration.h and Configuration_adv.h file versions. diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp index 4d564389eb36..a14ed7830898 100644 --- a/Marlin/cardreader.cpp +++ b/Marlin/cardreader.cpp @@ -891,6 +891,15 @@ void CardReader::printingHasFinished() { } else { sdprinting = false; + + #if ENABLED(POWER_LOSS_RECOVERY) + openJobRecoveryFile(false); + job_recovery_info.valid_head = job_recovery_info.valid_foot = 0; + (void)saveJobRecoveryInfo(); + closeJobRecoveryFile(); + job_recovery_commands_count = 0; + #endif + #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND) stepper.cleaning_buffer_counter = 1; // The command will fire from the Stepper ISR #endif @@ -907,4 +916,46 @@ void CardReader::printingHasFinished() { } } +#if ENABLED(POWER_LOSS_RECOVERY) + + char job_recovery_file_name[4] = "bin"; + + 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)) { + SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, job_recovery_file_name); + SERIAL_PROTOCOLCHAR('.'); + SERIAL_EOL(); + } + else + SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, job_recovery_file_name); + } + + void CardReader::closeJobRecoveryFile() { jobRecoveryFile.close(); } + + bool CardReader::jobRecoverFileExists() { + return jobRecoveryFile.open(&root, job_recovery_file_name, O_READ); + } + + int16_t CardReader::saveJobRecoveryInfo() { + jobRecoveryFile.seekSet(0); + const int16_t ret = jobRecoveryFile.write(&job_recovery_info, sizeof(job_recovery_info)); + if (ret == -1) SERIAL_PROTOCOLLNPGM("Power-loss file write failed."); + return ret; + } + + int16_t CardReader::loadJobRecoveryInfo() { + return jobRecoveryFile.read(&job_recovery_info, sizeof(job_recovery_info)); + } + + void CardReader::removeJobRecoveryFile() { + if (jobRecoveryFile.remove(&root, job_recovery_file_name)) + SERIAL_PROTOCOLLNPGM("Power-loss file deleted."); + else + SERIAL_PROTOCOLLNPGM("Power-loss file delete failed."); + } + +#endif // POWER_LOSS_RECOVERY + #endif // SDSUPPORT diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h index 5bcdd2b6b047..c779daf85e6b 100644 --- a/Marlin/cardreader.h +++ b/Marlin/cardreader.h @@ -82,11 +82,21 @@ class CardReader { #endif #endif + #if ENABLED(POWER_LOSS_RECOVERY) + void openJobRecoveryFile(const bool read); + void closeJobRecoveryFile(); + bool jobRecoverFileExists(); + int16_t saveJobRecoveryInfo(); + int16_t loadJobRecoveryInfo(); + void removeJobRecoveryFile(); + #endif + FORCE_INLINE void pauseSDPrint() { sdprinting = false; } FORCE_INLINE bool isFileOpen() { return file.isOpen(); } FORCE_INLINE bool eof() { return sdpos >= filesize; } FORCE_INLINE int16_t get() { sdpos = file.curPosition(); return (int16_t)file.read(); } - FORCE_INLINE void setIndex(long index) { sdpos = index; file.seekSet(index); } + FORCE_INLINE void setIndex(const uint32_t index) { sdpos = index; file.seekSet(index); } + FORCE_INLINE uint32_t getIndex() { return sdpos; } FORCE_INLINE uint8_t percentDone() { return (isFileOpen() && filesize) ? sdpos / ((filesize + 99) / 100) : 0; } FORCE_INLINE char* getWorkDirName() { workDir.getFilename(filename); return filename; } @@ -152,6 +162,10 @@ class CardReader { 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; diff --git a/Marlin/pins_TRIGORILLA.h b/Marlin/pins_TRIGORILLA.h index 5fc4c232642a..903278d500ee 100755 --- a/Marlin/pins_TRIGORILLA.h +++ b/Marlin/pins_TRIGORILLA.h @@ -6,7 +6,7 @@ #define BOARD_NAME "TRIGORILLA" #define LARGE_FLASH true -// Divers +// Misc PIN #define BUZZER 31 #define SDPOWER -1 #define SDSS 53 @@ -19,7 +19,7 @@ #define OUTAGECON_PIN 58 #endif -// Moteurs +// Steppers #define X_STEP_PIN 54 #define X_DIR_PIN 55 #define X_ENABLE_PIN 38 @@ -46,7 +46,7 @@ // EndStops #define X_MIN_PIN 3 -#define Y_MIN_PIN 42 // Origine 42. +#define Y_MIN_PIN 42 #define Z_MIN_PIN 18 #define X_MAX_PIN 43 @@ -59,15 +59,15 @@ #define CONTROLLER_FAN_PIN 7 // Heaters -#define HEATER_0_PIN 10 // Extrudeur 1. -#define HEATER_1_PIN 45 // Extrudeur 2. +#define HEATER_0_PIN 10 // Extruder 1. +#define HEATER_1_PIN 45 // Extruder 2. #define HEATER_BED_PIN 8 -// Sondes température -#define TEMP_0_PIN 13 // Température Ext1. -#define TEMP_1_PIN 15 // Température Ext2. -#define TEMP_2_PIN 12 // Température Ext3. -#define TEMP_BED_PIN 14 // Température Bed. +// Temp sensors +#define TEMP_0_PIN 13 // Temp Ext1. +#define TEMP_1_PIN 15 // Temp Ext2. +#define TEMP_2_PIN 12 // Temp Ext3. +#define TEMP_BED_PIN 14 // Temp Bed. // Servos #ifdef NUM_SERVOS @@ -91,6 +91,11 @@ #define SD_DETECT_PIN 49 #endif +#if defined(POWER_LOSS_RECOVERY) + #define OUTAGETEST_PIN 79 + #define OUTAGECON_PIN 58 +#endif + // LCD #if defined(REPRAP_DISCOUNT_SMART_CONTROLLER) || defined(G3D_PANEL) || defined(ANYCUBIC_TFT_MODEL) #define KILL_PIN 41 @@ -204,4 +209,4 @@ #define LCD_PINS_D7 29 #endif -#endif \ No newline at end of file +#endif diff --git a/Marlin/stopwatch.cpp b/Marlin/stopwatch.cpp index 5958000f722d..e5bc01590422 100644 --- a/Marlin/stopwatch.cpp +++ b/Marlin/stopwatch.cpp @@ -20,21 +20,23 @@ * */ -#include "Marlin.h" #include "stopwatch.h" -Stopwatch::Stopwatch() { - this->reset(); -} +#include "Marlin.h" + +Stopwatch::State Stopwatch::state; +millis_t Stopwatch::accumulator; +millis_t Stopwatch::startTimestamp; +millis_t Stopwatch::stopTimestamp; bool Stopwatch::stop() { #if ENABLED(DEBUG_STOPWATCH) Stopwatch::debug(PSTR("stop")); #endif - if (this->isRunning() || this->isPaused()) { - this->state = STOPPED; - this->stopTimestamp = millis(); + if (isRunning() || isPaused()) { + state = STOPPED; + stopTimestamp = millis(); return true; } else return false; @@ -45,9 +47,9 @@ bool Stopwatch::pause() { Stopwatch::debug(PSTR("pause")); #endif - if (this->isRunning()) { - this->state = PAUSED; - this->stopTimestamp = millis(); + if (isRunning()) { + state = PAUSED; + stopTimestamp = millis(); return true; } else return false; @@ -58,39 +60,40 @@ bool Stopwatch::start() { Stopwatch::debug(PSTR("start")); #endif - if (!this->isRunning()) { - if (this->isPaused()) this->accumulator = this->duration(); - else this->reset(); + if (!isRunning()) { + if (isPaused()) accumulator = duration(); + else reset(); - this->state = RUNNING; - this->startTimestamp = millis(); + state = RUNNING; + startTimestamp = millis(); return true; } else return false; } -void Stopwatch::reset() { +void Stopwatch::resume(const millis_t duration) { #if ENABLED(DEBUG_STOPWATCH) - Stopwatch::debug(PSTR("reset")); + Stopwatch::debug(PSTR("resume")); #endif - this->state = STOPPED; - this->startTimestamp = 0; - this->stopTimestamp = 0; - this->accumulator = 0; + reset(); + if ((accumulator = duration)) state = RUNNING; } -bool Stopwatch::isRunning() { - return (this->state == RUNNING) ? true : false; -} +void Stopwatch::reset() { + #if ENABLED(DEBUG_STOPWATCH) + Stopwatch::debug(PSTR("reset")); + #endif -bool Stopwatch::isPaused() { - return (this->state == PAUSED) ? true : false; + state = STOPPED; + startTimestamp = 0; + stopTimestamp = 0; + accumulator = 0; } millis_t Stopwatch::duration() { - return (((this->isRunning()) ? millis() : this->stopTimestamp) - - this->startTimestamp) / 1000UL + this->accumulator; + return ((isRunning() ? millis() : stopTimestamp) + - startTimestamp) / 1000UL + accumulator; } #if ENABLED(DEBUG_STOPWATCH) diff --git a/Marlin/stopwatch.h b/Marlin/stopwatch.h index ae3c998fb117..f5e04bbde8df 100644 --- a/Marlin/stopwatch.h +++ b/Marlin/stopwatch.h @@ -23,11 +23,12 @@ #ifndef STOPWATCH_H #define STOPWATCH_H -#include "macros.h" - // Print debug messages with M111 S2 (Uses 156 bytes of PROGMEM) //#define DEBUG_STOPWATCH +#include "macros.h" +#include "types.h" + /** * @brief Stopwatch class * @details This class acts as a timer proving stopwatch functionality including @@ -35,79 +36,85 @@ */ class Stopwatch { private: - enum State { + enum State : char { STOPPED, RUNNING, PAUSED }; - Stopwatch::State state; - millis_t accumulator; - millis_t startTimestamp; - millis_t stopTimestamp; + static Stopwatch::State state; + static millis_t accumulator; + static millis_t startTimestamp; + static millis_t stopTimestamp; public: /** - * @brief Class constructor + * @brief Initialize the stopwatch */ - Stopwatch(); + FORCE_INLINE static void init() { reset(); } /** - * @brief Stops the stopwatch - * @details Stops the running timer, it will silently ignore the request if - * no timer is currently running. - * @return true is method was successful + * @brief Stop the stopwatch + * @details Stop the running timer. Silently ignore the request if + * no timer is running. + * @return true on success */ - bool stop(); + static bool stop(); /** * @brief Pause the stopwatch - * @details Pauses the running timer, it will silently ignore the request if - * no timer is currently running. - * @return true is method was successful + * @details Pause the running timer, it will silently ignore the request if + * no timer is running. + * @return true on success + */ + static bool pause(); + + /** + * @brief Start the stopwatch + * @details Start the timer, it will silently ignore the request if the + * timer is already running. + * @return true on success */ - bool pause(); + static bool start(); /** - * @brief Starts the stopwatch - * @details Starts the timer, it will silently ignore the request if the - * timer is already running. - * @return true is method was successful + * @brief Resume the stopwatch + * @details Resume a timer from a given duration */ - bool start(); + static void resume(const millis_t duration); /** - * @brief Resets the stopwatch - * @details Resets all settings to their default values. + * @brief Reset the stopwatch + * @details Reset all settings to their default values. */ - void reset(); + static void reset(); /** - * @brief Checks if the timer is running - * @details Returns true if the timer is currently running, false otherwise. + * @brief Check if the timer is running + * @details Return true if the timer is currently running, false otherwise. * @return true if stopwatch is running */ - bool isRunning(); + FORCE_INLINE static bool isRunning() { return state == RUNNING; } /** - * @brief Checks if the timer is paused - * @details Returns true if the timer is currently paused, false otherwise. + * @brief Check if the timer is paused + * @details Return true if the timer is currently paused, false otherwise. * @return true if stopwatch is paused */ - bool isPaused(); + FORCE_INLINE static bool isPaused() { return state == PAUSED; } /** - * @brief Gets the running time - * @details Returns the total number of seconds the timer has been running. + * @brief Get the running time + * @details Return the total number of seconds the timer has been running. * @return the delta since starting the stopwatch */ - millis_t duration(); + static millis_t duration(); - #if ENABLED(DEBUG_STOPWATCH) + #ifdef DEBUG_STOPWATCH /** - * @brief Prints a debug message - * @details Prints a simple debug message "Stopwatch::function" + * @brief Print a debug message + * @details Print a simple debug message "Stopwatch::function" */ static void debug(const char func[]); diff --git a/Marlin/types.h b/Marlin/types.h index e4f4ce197fe9..8d4a0bd04cea 100644 --- a/Marlin/types.h +++ b/Marlin/types.h @@ -23,6 +23,8 @@ #ifndef __TYPES_H__ #define __TYPES_H__ +#include + typedef unsigned long millis_t; typedef struct { diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 8d78d5a503bb..19abe91d6566 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -804,10 +804,20 @@ void kill_screen(const char* lcd_msg) { wait_for_heatup = false; lcd_setstatusPGM(PSTR(MSG_PRINT_ABORTED), -1); lcd_return_to_status(); + + #if ENABLED(POWER_LOSS_RECOVERY) + card.openJobRecoveryFile(false); + job_recovery_info.valid_head = job_recovery_info.valid_foot = 0; + (void)card.saveJobRecoveryInfo(); + card.closeJobRecoveryFile(); + job_recovery_commands_count = 0; + #endif + } #endif // SDSUPPORT + #if ENABLED(MENU_ITEM_CASE_LIGHT) extern uint8_t case_light_brightness; @@ -4654,6 +4664,15 @@ void lcd_update() { #endif // SDSUPPORT && SD_DETECT_PIN + /* + #if ENABLED(POWER_LOSS_RECOVERY) + if (job_recovery_commands_count && job_recovery_phase == JOB_RECOVERY_IDLE) { + lcd_goto_screen(lcd_job_recovery_menu); + job_recovery_phase = JOB_RECOVERY_MAYBE; // Waiting for a response + } + #endif + */ + const millis_t ms = millis(); if (ELAPSED(ms, next_lcd_update_ms) #if ENABLED(DOGLCD)