My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

power_loss_recovery.cpp 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * power_loss_recovery.cpp - Resume an SD print after power-loss
  24. */
  25. #include "../inc/MarlinConfigPre.h"
  26. #if ENABLED(POWER_LOSS_RECOVERY)
  27. #include "power_loss_recovery.h"
  28. #include "../lcd/ultralcd.h"
  29. #include "../gcode/queue.h"
  30. #include "../module/planner.h"
  31. #include "../module/printcounter.h"
  32. #include "../module/temperature.h"
  33. #include "../sd/cardreader.h"
  34. #include "../core/serial.h"
  35. // Recovery data
  36. job_recovery_info_t job_recovery_info;
  37. JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE;
  38. uint8_t job_recovery_commands_count; //=0
  39. char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
  40. // Private
  41. static char sd_filename[MAXPATHNAMELENGTH];
  42. #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
  43. void debug_print_job_recovery(const bool recovery) {
  44. SERIAL_PROTOCOLPAIR("valid_head:", (int)job_recovery_info.valid_head);
  45. SERIAL_PROTOCOLLNPAIR(" valid_foot:", (int)job_recovery_info.valid_foot);
  46. if (job_recovery_info.valid_head) {
  47. if (job_recovery_info.valid_head == job_recovery_info.valid_foot) {
  48. SERIAL_PROTOCOLPGM("current_position");
  49. LOOP_XYZE(i) SERIAL_PROTOCOLPAIR(": ", job_recovery_info.current_position[i]);
  50. SERIAL_EOL();
  51. SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate);
  52. SERIAL_PROTOCOLPGM("target_temperature");
  53. HOTEND_LOOP() SERIAL_PROTOCOLPAIR(": ", job_recovery_info.target_temperature[e]);
  54. SERIAL_EOL();
  55. SERIAL_PROTOCOLPGM("fanSpeeds");
  56. for(uint8_t i = 0; i < FAN_COUNT; i++) SERIAL_PROTOCOLPAIR(": ", job_recovery_info.fanSpeeds[i]);
  57. SERIAL_EOL();
  58. #if HAS_LEVELING
  59. SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling));
  60. SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade));
  61. #endif
  62. SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed);
  63. SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", job_recovery_info.cmd_queue_index_r);
  64. SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", job_recovery_info.commands_in_queue);
  65. if (recovery)
  66. for (uint8_t i = 0; i < job_recovery_commands_count; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_commands[i]);
  67. else
  68. for (uint8_t i = 0; i < job_recovery_info.commands_in_queue; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_info.command_queue[i]);
  69. SERIAL_PROTOCOLLNPAIR("sd_filename: ", sd_filename);
  70. SERIAL_PROTOCOLLNPAIR("sdpos: ", job_recovery_info.sdpos);
  71. SERIAL_PROTOCOLLNPAIR("print_job_elapsed: ", job_recovery_info.print_job_elapsed);
  72. }
  73. else
  74. SERIAL_PROTOCOLLNPGM("INVALID DATA");
  75. }
  76. }
  77. #endif // DEBUG_POWER_LOSS_RECOVERY
  78. /**
  79. * Check for Print Job Recovery
  80. * If the file has a saved state, populate the job_recovery_commands queue
  81. */
  82. void do_print_job_recovery() {
  83. //if (job_recovery_commands_count > 0) return;
  84. memset(&job_recovery_info, 0, sizeof(job_recovery_info));
  85. ZERO(job_recovery_commands);
  86. if (!card.cardOK) card.initsd();
  87. if (card.cardOK) {
  88. #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
  89. SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", (int)sizeof(job_recovery_info));
  90. #endif
  91. if (card.jobRecoverFileExists()) {
  92. card.openJobRecoveryFile(true);
  93. card.loadJobRecoveryInfo();
  94. card.closeJobRecoveryFile();
  95. //card.removeJobRecoveryFile();
  96. if (job_recovery_info.valid_head && job_recovery_info.valid_head == job_recovery_info.valid_foot) {
  97. uint8_t ind = 0;
  98. #if HAS_LEVELING
  99. strcpy_P(job_recovery_commands[ind++], PSTR("M420 S0 Z0")); // Leveling off before G92 or G28
  100. #endif
  101. strcpy_P(job_recovery_commands[ind++], PSTR("G92.0 Z0")); // Ensure Z is equal to 0
  102. strcpy_P(job_recovery_commands[ind++], PSTR("G1 Z2")); // Raise Z by 2mm (we hope!)
  103. strcpy_P(job_recovery_commands[ind++], PSTR("G28 R0"
  104. #if !IS_KINEMATIC
  105. " X Y" // Home X and Y for Cartesian
  106. #endif
  107. ));
  108. #if HAS_LEVELING
  109. // Restore leveling state before G92 sets Z
  110. // This ensures the steppers correspond to the native Z
  111. sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), job_recovery_info.fade);
  112. #endif
  113. char str_1[16], str_2[16];
  114. dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1);
  115. dtostrf(job_recovery_info.current_position[E_AXIS]
  116. #if ENABLED(SAVE_EACH_CMD_MODE)
  117. - 5
  118. #endif
  119. , 1, 3, str_2
  120. );
  121. sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E
  122. strcpy_P(job_recovery_commands[ind++], PSTR("M117 Continuing..."));
  123. uint8_t r = job_recovery_info.cmd_queue_index_r;
  124. while (job_recovery_info.commands_in_queue) {
  125. strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]);
  126. job_recovery_info.commands_in_queue--;
  127. r = (r + 1) % BUFSIZE;
  128. }
  129. job_recovery_commands_count = ind;
  130. #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
  131. debug_print_job_recovery(true);
  132. #endif
  133. card.openFile(sd_filename, true);
  134. card.setIndex(job_recovery_info.sdpos);
  135. }
  136. else {
  137. if (job_recovery_info.valid_head != job_recovery_info.valid_foot)
  138. LCD_ALERTMESSAGEPGM("INVALID DATA");
  139. memset(&job_recovery_info, 0, sizeof(job_recovery_info));
  140. }
  141. }
  142. }
  143. }
  144. /**
  145. * Save the current machine state to the "bin" file
  146. */
  147. void save_job_recovery_info() {
  148. #if SAVE_INFO_INTERVAL_MS > 0
  149. static millis_t next_save_ms; // = 0; // Init on reset
  150. millis_t ms = millis();
  151. #endif
  152. if (
  153. #if SAVE_INFO_INTERVAL_MS > 0
  154. ELAPSED(ms, next_save_ms) ||
  155. #endif
  156. #if ENABLED(SAVE_EACH_CMD_MODE)
  157. true
  158. #else
  159. (current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS])
  160. #endif
  161. ) {
  162. #if SAVE_INFO_INTERVAL_MS > 0
  163. next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
  164. #endif
  165. // Head and foot will match if valid data was saved
  166. if (!++job_recovery_info.valid_head) ++job_recovery_info.valid_head; // non-zero in sequence
  167. job_recovery_info.valid_foot = job_recovery_info.valid_head;
  168. // Machine state
  169. COPY(job_recovery_info.current_position, current_position);
  170. job_recovery_info.feedrate = feedrate_mm_s;
  171. COPY(job_recovery_info.target_temperature, thermalManager.target_temperature);
  172. job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed;
  173. COPY(job_recovery_info.fanSpeeds, fanSpeeds);
  174. #if HAS_LEVELING
  175. job_recovery_info.leveling = planner.leveling_active;
  176. job_recovery_info.fade = (
  177. #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
  178. planner.z_fade_height
  179. #else
  180. 0
  181. #endif
  182. );
  183. #endif
  184. // Commands in the queue
  185. job_recovery_info.cmd_queue_index_r = cmd_queue_index_r;
  186. job_recovery_info.commands_in_queue = commands_in_queue;
  187. COPY(job_recovery_info.command_queue, command_queue);
  188. // Elapsed print job time
  189. job_recovery_info.print_job_elapsed = print_job_timer.duration() * 1000UL;
  190. // SD file position
  191. card.getAbsFilename(sd_filename);
  192. job_recovery_info.sdpos = card.getIndex();
  193. #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
  194. SERIAL_PROTOCOLLNPGM("Saving job_recovery_info");
  195. debug_print_job_recovery(false);
  196. #endif
  197. card.openJobRecoveryFile(false);
  198. (void)card.saveJobRecoveryInfo();
  199. }
  200. }
  201. #endif // POWER_LOSS_RECOVERY