Subversion Repositories Tronxy-X3A-Marlin

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 ron 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
/**
24
 * power_loss_recovery.cpp - Resume an SD print after power-loss
25
 */
26
 
27
#include "MarlinConfig.h"
28
 
29
#if ENABLED(POWER_LOSS_RECOVERY)
30
 
31
#include "power_loss_recovery.h"
32
 
33
#include "cardreader.h"
34
#include "planner.h"
35
#include "printcounter.h"
36
#include "serial.h"
37
#include "temperature.h"
38
#include "ultralcd.h"
39
 
40
// Recovery data
41
job_recovery_info_t job_recovery_info;
42
JobRecoveryPhase job_recovery_phase = JOB_RECOVERY_IDLE;
43
uint8_t job_recovery_commands_count; //=0
44
char job_recovery_commands[BUFSIZE + APPEND_CMD_COUNT][MAX_CMD_SIZE];
45
// Extern
46
extern uint8_t active_extruder, commands_in_queue, cmd_queue_index_r;
47
 
48
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
49
  void debug_print_job_recovery(const bool recovery) {
50
    SERIAL_PROTOCOLLNPGM("---- Job Recovery Info ----");
51
    SERIAL_PROTOCOLPAIR("valid_head:", int(job_recovery_info.valid_head));
52
    SERIAL_PROTOCOLLNPAIR(" valid_foot:", int(job_recovery_info.valid_foot));
53
    if (job_recovery_info.valid_head) {
54
      if (job_recovery_info.valid_head == job_recovery_info.valid_foot) {
55
        SERIAL_PROTOCOLPGM("current_position: ");
56
        LOOP_XYZE(i) {
57
          SERIAL_PROTOCOL(job_recovery_info.current_position[i]);
58
          if (i < E_AXIS) SERIAL_CHAR(',');
59
        }
60
        SERIAL_EOL();
61
        SERIAL_PROTOCOLLNPAIR("feedrate: ", job_recovery_info.feedrate);
62
 
63
        #if HOTENDS > 1
64
          SERIAL_PROTOCOLLNPAIR("active_hotend: ", int(job_recovery_info.active_hotend));
65
        #endif
66
 
67
        SERIAL_PROTOCOLPGM("target_temperature: ");
68
        HOTEND_LOOP() {
69
          SERIAL_PROTOCOL(job_recovery_info.target_temperature[e]);
70
          if (e < HOTENDS - 1) SERIAL_CHAR(',');
71
        }
72
        SERIAL_EOL();
73
 
74
        #if HAS_HEATED_BED
75
          SERIAL_PROTOCOLLNPAIR("target_temperature_bed: ", job_recovery_info.target_temperature_bed);
76
        #endif
77
 
78
        #if FAN_COUNT
79
          SERIAL_PROTOCOLPGM("fanSpeeds: ");
80
          for (int8_t i = 0; i < FAN_COUNT; i++) {
81
            SERIAL_PROTOCOL(job_recovery_info.fanSpeeds[i]);
82
            if (i < FAN_COUNT - 1) SERIAL_CHAR(',');
83
          }
84
          SERIAL_EOL();
85
        #endif
86
 
87
        #if HAS_LEVELING
88
          SERIAL_PROTOCOLPAIR("leveling: ", int(job_recovery_info.leveling));
89
          SERIAL_PROTOCOLLNPAIR(" fade: ", int(job_recovery_info.fade));
90
        #endif
91
        SERIAL_PROTOCOLLNPAIR("cmd_queue_index_r: ", int(job_recovery_info.cmd_queue_index_r));
92
        SERIAL_PROTOCOLLNPAIR("commands_in_queue: ", int(job_recovery_info.commands_in_queue));
93
        if (recovery)
94
          for (uint8_t i = 0; i < job_recovery_commands_count; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_commands[i]);
95
        else
96
          for (uint8_t i = 0; i < job_recovery_info.commands_in_queue; i++) SERIAL_PROTOCOLLNPAIR("> ", job_recovery_info.command_queue[i]);
97
        SERIAL_PROTOCOLLNPAIR("sd_filename: ", job_recovery_info.sd_filename);
98
        SERIAL_PROTOCOLLNPAIR("sdpos: ", job_recovery_info.sdpos);
99
        SERIAL_PROTOCOLLNPAIR("print_job_elapsed: ", job_recovery_info.print_job_elapsed);
100
      }
101
      else
102
        SERIAL_PROTOCOLLNPGM("INVALID DATA");
103
    }
104
    SERIAL_PROTOCOLLNPGM("---------------------------");
105
  }
106
#endif // DEBUG_POWER_LOSS_RECOVERY
107
 
108
/**
109
 * Check for Print Job Recovery during setup()
110
 *
111
 * If a saved state exists, populate job_recovery_commands with
112
 * commands to restore the machine state and continue the file.
113
 */
114
void check_print_job_recovery() {
115
  memset(&job_recovery_info, 0, sizeof(job_recovery_info));
116
  ZERO(job_recovery_commands);
117
 
118
  if (!card.cardOK) card.initsd();
119
 
120
  if (card.cardOK) {
121
 
122
    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
123
      SERIAL_PROTOCOLLNPAIR("Init job recovery info. Size: ", int(sizeof(job_recovery_info)));
124
    #endif
125
 
126
    if (card.jobRecoverFileExists()) {
127
      card.openJobRecoveryFile(true);
128
      card.loadJobRecoveryInfo();
129
      card.closeJobRecoveryFile();
130
      //card.removeJobRecoveryFile();
131
 
132
      if (job_recovery_info.valid_head && job_recovery_info.valid_head == job_recovery_info.valid_foot) {
133
 
134
        uint8_t ind = 0;
135
 
136
        #if HAS_LEVELING
137
          strcpy_P(job_recovery_commands[ind++], PSTR("M420 S0 Z0"));               // Leveling off before G92 or G28
138
        #endif
139
 
140
        strcpy_P(job_recovery_commands[ind++], PSTR("G92.0 Z0"));                   // Ensure Z is equal to 0
141
        strcpy_P(job_recovery_commands[ind++], PSTR("G1 Z2"));                      // Raise Z by 2mm (we hope!)
142
        strcpy_P(job_recovery_commands[ind++], PSTR("G28 R0"
143
          #if ENABLED(MARLIN_DEV_MODE)
144
            " S"
145
          #elif !IS_KINEMATIC
146
            " X Y"                                                                  // Home X and Y for Cartesian
147
          #endif
148
        ));
149
 
150
        char str_1[16], str_2[16];
151
 
152
        #if HAS_LEVELING
153
          if (job_recovery_info.fade || job_recovery_info.leveling) {
154
            // Restore leveling state before G92 sets Z
155
            // This ensures the steppers correspond to the native Z
156
            dtostrf(job_recovery_info.fade, 1, 1, str_1);
157
            sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1);
158
          }
159
        #endif
160
 
161
        dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1);
162
        dtostrf(job_recovery_info.current_position[E_CART]
163
          #if ENABLED(SAVE_EACH_CMD_MODE)
164
            - 5
165
          #endif
166
          , 1, 3, str_2
167
        );
168
        sprintf_P(job_recovery_commands[ind++], PSTR("G92.0 Z%s E%s"), str_1, str_2); // Current Z + 2 and E
169
 
170
        uint8_t r = job_recovery_info.cmd_queue_index_r, c = job_recovery_info.commands_in_queue;
171
        while (c--) {
172
          strcpy(job_recovery_commands[ind++], job_recovery_info.command_queue[r]);
173
          r = (r + 1) % BUFSIZE;
174
        }
175
 
176
        if (job_recovery_info.sd_filename[0] == '/') job_recovery_info.sd_filename[0] = ' ';
177
        sprintf_P(job_recovery_commands[ind++], PSTR("M23 %s"), job_recovery_info.sd_filename);
178
        sprintf_P(job_recovery_commands[ind++], PSTR("M24 S%ld T%ld"), job_recovery_info.sdpos, job_recovery_info.print_job_elapsed);
179
 
180
        job_recovery_commands_count = ind;
181
 
182
        #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
183
          debug_print_job_recovery(true);
184
        #endif
185
      }
186
      else {
187
        if (job_recovery_info.valid_head != job_recovery_info.valid_foot)
188
          LCD_ALERTMESSAGEPGM("INVALID DATA");
189
        memset(&job_recovery_info, 0, sizeof(job_recovery_info));
190
      }
191
    }
192
  }
193
}
194
 
195
/**
196
 * Save the current machine state to the power-loss recovery file
197
 */
198
void save_job_recovery_info() {
199
  #if SAVE_INFO_INTERVAL_MS > 0
200
    static millis_t next_save_ms; // = 0;  // Init on reset
201
    millis_t ms = millis();
202
  #endif
203
  if (
204
    // Save on every command
205
    #if ENABLED(SAVE_EACH_CMD_MODE)
206
      true
207
    #else
208
      // Save if power loss pin is triggered
209
      #if PIN_EXISTS(POWER_LOSS)
210
        READ(POWER_LOSS_PIN) == POWER_LOSS_STATE ||
211
      #endif
212
      // Save if interval is elapsed
213
      #if SAVE_INFO_INTERVAL_MS > 0
214
        ELAPSED(ms, next_save_ms) ||
215
      #endif
216
      // Save on every new Z height
217
      (current_position[Z_AXIS] > 0 && current_position[Z_AXIS] > job_recovery_info.current_position[Z_AXIS])
218
    #endif
219
  ) {
220
    #if SAVE_INFO_INTERVAL_MS > 0
221
      next_save_ms = ms + SAVE_INFO_INTERVAL_MS;
222
    #endif
223
 
224
    // Head and foot will match if valid data was saved
225
    if (!++job_recovery_info.valid_head) ++job_recovery_info.valid_head; // non-zero in sequence
226
    job_recovery_info.valid_foot = job_recovery_info.valid_head;
227
 
228
    // Machine state
229
    COPY(job_recovery_info.current_position, current_position);
230
    job_recovery_info.feedrate = feedrate_mm_s;
231
 
232
    #if HOTENDS > 1
233
      job_recovery_info.active_hotend = active_extruder;
234
    #endif
235
 
236
    COPY(job_recovery_info.target_temperature, thermalManager.target_temperature);
237
 
238
    #if HAS_HEATED_BED
239
      job_recovery_info.target_temperature_bed = thermalManager.target_temperature_bed;
240
    #endif
241
 
242
    #if FAN_COUNT
243
      COPY(job_recovery_info.fanSpeeds, fanSpeeds);
244
    #endif
245
 
246
    #if HAS_LEVELING
247
      job_recovery_info.leveling = planner.leveling_active;
248
      job_recovery_info.fade = (
249
        #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
250
          planner.z_fade_height
251
        #else
252
 
253
        #endif
254
      );
255
    #endif
256
 
257
    // Commands in the queue
258
    job_recovery_info.cmd_queue_index_r = cmd_queue_index_r;
259
    job_recovery_info.commands_in_queue = commands_in_queue;
260
    COPY(job_recovery_info.command_queue, command_queue);
261
 
262
    // Elapsed print job time
263
    job_recovery_info.print_job_elapsed = print_job_timer.duration();
264
 
265
    // SD file position
266
    card.getAbsFilename(job_recovery_info.sd_filename);
267
    job_recovery_info.sdpos = card.getIndex();
268
 
269
    #if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
270
      SERIAL_PROTOCOLLNPGM("Saving...");
271
      debug_print_job_recovery(false);
272
    #endif
273
 
274
    card.openJobRecoveryFile(false);
275
    (void)card.saveJobRecoveryInfo();
276
 
277
    // If power-loss pin was triggered, write just once then kill
278
    #if PIN_EXISTS(POWER_LOSS)
279
      if (READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) kill(PSTR(MSG_POWER_LOSS_RECOVERY));
280
    #endif
281
  }
282
}
283
 
284
#endif // POWER_LOSS_RECOVERY