diff --git a/CMakeLists.txt b/CMakeLists.txt index d45f79e85..d16f6e78e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,7 @@ set(FW_SOURCES optiboot_xflash.cpp pat9125.cpp planner.cpp + power_panic.cpp Prusa_farm.cpp qr_solve.cpp rbuf.c diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index a51d568a2..99bdc1c6a 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -380,14 +380,6 @@ float temp_compensation_pinda_thermistor_offset(float temperature_pinda); void serialecho_temperatures(); bool check_commands(); -void uvlo_(); -void uvlo_tiny(); -void recover_print(uint8_t automatic); -void setup_uvlo_interrupt(); - -extern bool recover_machine_state_after_power_panic(); -extern void restore_print_from_eeprom(bool mbl_was_active); - extern void print_world_coordinates(); extern void print_physical_coordinates(); extern void print_mesh_bed_leveling_table(); @@ -397,6 +389,13 @@ void restore_extruder_temperature_from_ram(); extern void restore_print_from_ram_and_continue(float e_move); extern void cancel_saved_printing(); +// Define some coordinates outside the clamp limits (making them invalid past the parsing stage) so +// that they can be used later for various logical checks +#define X_COORD_INVALID (X_MIN_POS-1) +#define SAVED_START_POSITION_UNSET X_COORD_INVALID +extern float saved_start_position[NUM_AXIS]; +extern uint16_t saved_segment_idx; + //estimated time to end of the print extern uint8_t calc_percent_done(); diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 2e5995f13..ec6316760 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -78,6 +78,7 @@ #include "math.h" #include "util.h" #include "Timer.h" +#include "power_panic.h" #include "Prusa_farm.h" #include @@ -179,9 +180,6 @@ bool mesh_bed_leveling_flag = false; uint32_t total_filament_used; HeatingStatus heating_status; bool loading_flag = false; - -#define XY_NO_RESTORE_FLAG (mesh_bed_leveling_flag || homing_flag) - int fan_edge_counter[2]; int fan_speed[2]; @@ -246,14 +244,9 @@ static uint8_t host_keepalive_interval = HOST_KEEPALIVE_INTERVAL; const char errormagic[] PROGMEM = "Error:"; const char echomagic[] PROGMEM = "echo:"; -// Define some coordinates outside the clamp limits (making them invalid past the parsing stage) so -// that they can be used later for various logical checks -#define X_COORD_INVALID (X_MIN_POS-1) +float saved_start_position[NUM_AXIS] = {SAVED_START_POSITION_UNSET, 0, 0, 0}; -#define SAVED_START_POSITION_UNSET X_COORD_INVALID -static float saved_start_position[NUM_AXIS] = {SAVED_START_POSITION_UNSET, 0, 0, 0}; - -static uint16_t saved_segment_idx = 0; +uint16_t saved_segment_idx = 0; // storing estimated time to end of print counted by slicer uint8_t print_percent_done_normal = PRINT_PERCENT_DONE_INIT; @@ -10342,493 +10335,6 @@ void serialecho_temperatures() { SERIAL_PROTOCOLLN(); } -#ifdef UVLO_SUPPORT -void uvlo_drain_reset() -{ - // burn all that residual power - wdt_enable(WDTO_1S); - WRITE(BEEPER,HIGH); - lcd_clear(); - lcd_puts_at_P(0, 1, MSG_POWERPANIC_DETECTED); - while(1); -} - - -void uvlo_() -{ - unsigned long time_start = _millis(); - bool sd_print = card.sdprinting; - // Conserve power as soon as possible. -#ifdef LCD_BL_PIN - backlightMode = BACKLIGHT_MODE_DIM; - backlightLevel_LOW = 0; - backlight_update(); -#endif //LCD_BL_PIN - disable_x(); - disable_y(); - -#ifdef TMC2130 - tmc2130_set_current_h(Z_AXIS, 20); - tmc2130_set_current_r(Z_AXIS, 20); - tmc2130_set_current_h(E_AXIS, 20); - tmc2130_set_current_r(E_AXIS, 20); -#endif //TMC2130 - - // Stop all heaters - uint8_t saved_target_temperature_bed = target_temperature_bed; - uint16_t saved_target_temperature_ext = target_temperature[active_extruder]; - setTargetHotend(0); - setTargetBed(0); - - // Calculate the file position, from which to resume this print. - long sd_position = sdpos_atomic; //atomic sd position of last command added in queue - { - uint16_t sdlen_planner = planner_calc_sd_length(); //length of sd commands in planner - sd_position -= sdlen_planner; - uint16_t sdlen_cmdqueue = cmdqueue_calc_sd_length(); //length of sd commands in cmdqueue - sd_position -= sdlen_cmdqueue; - if (sd_position < 0) sd_position = 0; - } - - // save the global state at planning time - bool pos_invalid = XY_NO_RESTORE_FLAG; - uint16_t feedrate_bckp; - if (current_block && !pos_invalid) - { - memcpy(saved_start_position, current_block->gcode_start_position, sizeof(saved_start_position)); - feedrate_bckp = current_block->gcode_feedrate; - saved_segment_idx = current_block->segment_idx; - } - else - { - saved_start_position[0] = SAVED_START_POSITION_UNSET; - feedrate_bckp = feedrate; - saved_segment_idx = 0; - } - - // From this point on and up to the print recovery, Z should not move during X/Y travels and - // should be controlled precisely. Reset the MBL status before planner_abort_hard in order to - // get the physical Z for further manipulation. - bool mbl_was_active = mbl.active; - mbl.active = false; - - // After this call, the planner queue is emptied and the current_position is set to a current logical coordinate. - // The logical coordinate will likely differ from the machine coordinate if the skew calibration and mesh bed leveling - // are in action. - planner_abort_hard(); - - // Store the print logical Z position, which we need to recover (a slight error here would be - // recovered on the next Gcode instruction, while a physical location error would not) - float logical_z = current_position[Z_AXIS]; - if(mbl_was_active) logical_z -= mbl.get_z(st_get_position_mm(X_AXIS), st_get_position_mm(Y_AXIS)); - eeprom_update_float((float*)EEPROM_UVLO_CURRENT_POSITION_Z, logical_z); - - // Store the print E position before we lose track - eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E), current_position[E_AXIS]); - eeprom_update_byte((uint8_t*)EEPROM_UVLO_E_ABS, (axis_relative_modes & E_AXIS_MASK)?0:1); - - // Clean the input command queue, inhibit serial processing using saved_printing - cmdqueue_reset(); - card.sdprinting = false; - saved_printing = true; - - // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and - // command queues are empty, SD card printing is disabled, usb is inhibited. - planner_aborted = false; - sei(); - - // Retract - current_position[E_AXIS] -= default_retraction; - plan_buffer_line_curposXYZE(95); - st_synchronize(); - disable_e0(); - - // Read out the current Z motor microstep counter to move the axis up towards - // a full step before powering off. NOTE: we need to ensure to schedule more - // than "dropsegments" steps in order to move (this is always the case here - // due to UVLO_Z_AXIS_SHIFT being used) - uint16_t z_res = tmc2130_get_res(Z_AXIS); - uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); - current_position[Z_AXIS] += float(1024 - z_microsteps) - / (z_res * cs.axis_steps_per_unit[Z_AXIS]) - + UVLO_Z_AXIS_SHIFT; - plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60); - st_synchronize(); - poweroff_z(); - - // Write the file position. - eeprom_update_dword((uint32_t*)(EEPROM_FILE_POSITION), sd_position); - - // Store the mesh bed leveling offsets. This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case. - for (uint8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) { - uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 - uint8_t iy = mesh_point / MESH_NUM_X_POINTS; - // Scale the z value to 1u resolution. - int16_t v = mbl_was_active ? int16_t(floor(mbl.z_values[iy][ix] * 1000.f + 0.5f)) : 0; - eeprom_update_word((uint16_t*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL +2*mesh_point), *reinterpret_cast(&v)); - } - - // Write the _final_ Z position and motor microstep counter (unused). - eeprom_update_float((float*)EEPROM_UVLO_TINY_CURRENT_POSITION_Z, current_position[Z_AXIS]); - z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); - eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps); - - // Store the current position. - if (pos_invalid) - eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), X_COORD_INVALID); - else - { - eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]); - eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]); - } - - // Store the current feed rate, temperatures, fan speed and extruder multipliers (flow rates) - eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDRATE, feedrate_bckp); - eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY, feedmultiply); - eeprom_update_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND, saved_target_temperature_ext); - eeprom_update_byte((uint8_t*)EEPROM_UVLO_TARGET_BED, saved_target_temperature_bed); - eeprom_update_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED, fanSpeed); - eeprom_update_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_0), extruder_multiplier[0]); -#if EXTRUDERS > 1 - eeprom_update_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_1), extruder_multiplier[1]); -#if EXTRUDERS > 2 - eeprom_update_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_2), extruder_multiplier[2]); -#endif -#endif - eeprom_update_word((uint16_t*)(EEPROM_EXTRUDEMULTIPLY), (uint16_t)extrudemultiply); - - eeprom_update_float((float*)(EEPROM_UVLO_ACCELL), cs.acceleration); - eeprom_update_float((float*)(EEPROM_UVLO_RETRACT_ACCELL), cs.retract_acceleration); - eeprom_update_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL), cs.travel_acceleration); - - // Store the saved target - eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+0*4), saved_start_position[X_AXIS]); - eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+1*4), saved_start_position[Y_AXIS]); - eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+2*4), saved_start_position[Z_AXIS]); - eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+3*4), saved_start_position[E_AXIS]); - - eeprom_update_word((uint16_t*)EEPROM_UVLO_SAVED_SEGMENT_IDX, saved_segment_idx); - -#ifdef LIN_ADVANCE - eeprom_update_float((float*)(EEPROM_UVLO_LA_K), extruder_advance_K); -#endif - - // Finaly store the "power outage" flag. - if(sd_print) eeprom_update_byte((uint8_t*)EEPROM_UVLO, 1); - - // Increment power failure counter - eeprom_increment_byte((uint8_t*)EEPROM_POWER_COUNT); - eeprom_increment_word((uint16_t*)EEPROM_POWER_COUNT_TOT); - - printf_P(_N("UVLO - end %d\n"), _millis() - time_start); - WRITE(BEEPER,HIGH); - - // All is set: with all the juice left, try to move extruder away to detach the nozzle completely from the print - poweron_z(); - current_position[X_AXIS] = (current_position[X_AXIS] < 0.5f * (X_MIN_POS + X_MAX_POS)) ? X_MIN_POS : X_MAX_POS; - plan_buffer_line_curposXYZE(500); - st_synchronize(); - - wdt_enable(WDTO_1S); - while(1); -} - - -void uvlo_tiny() -{ - unsigned long time_start = _millis(); - - // Conserve power as soon as possible. - disable_x(); - disable_y(); - disable_e0(); - -#ifdef TMC2130 - tmc2130_set_current_h(Z_AXIS, 20); - tmc2130_set_current_r(Z_AXIS, 20); -#endif //TMC2130 - - // Stop all heaters - setTargetHotend(0); - setTargetBed(0); - - // When power is interrupted on the _first_ recovery an attempt can be made to raise the - // extruder, causing the Z position to change. Similarly, when recovering, the Z position is - // lowered. In such cases we cannot just save Z, we need to re-align the steppers to a fullstep. - // Disable MBL (if not already) to work with physical coordinates. - mbl.active = false; - planner_abort_hard(); - - // Allow for small roundoffs to be ignored - if(fabs(current_position[Z_AXIS] - eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))) >= 1.f/cs.axis_steps_per_unit[Z_AXIS]) - { - // Clean the input command queue, inhibit serial processing using saved_printing - cmdqueue_reset(); - card.sdprinting = false; - saved_printing = true; - - // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and - // command queues are empty, SD card printing is disabled, usb is inhibited. - planner_aborted = false; - sei(); - - // The axis was moved: adjust Z as done on a regular UVLO. - uint16_t z_res = tmc2130_get_res(Z_AXIS); - uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); - current_position[Z_AXIS] += float(1024 - z_microsteps) - / (z_res * cs.axis_steps_per_unit[Z_AXIS]) - + UVLO_TINY_Z_AXIS_SHIFT; - plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60); - st_synchronize(); - poweroff_z(); - - // Update Z position - eeprom_update_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z), current_position[Z_AXIS]); - - // Update the _final_ Z motor microstep counter (unused). - z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); - eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps); - } - - // Update the the "power outage" flag. - eeprom_update_byte((uint8_t*)EEPROM_UVLO,2); - - // Increment power failure counter - eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) + 1); - eeprom_update_word((uint16_t*)EEPROM_POWER_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) + 1); - - printf_P(_N("UVLO_TINY - end %d\n"), _millis() - time_start); - uvlo_drain_reset(); -} - -void setup_uvlo_interrupt() { - DDRE &= ~(1 << 4); //input pin - PORTE &= ~(1 << 4); //no internal pull-up - - // sensing falling edge - EICRB |= (1 << 0); - EICRB &= ~(1 << 1); - - // enable INT4 interrupt - EIMSK |= (1 << 4); - - // check if power was lost before we armed the interrupt - if(!(PINE & (1 << 4)) && eeprom_read_byte((uint8_t*)EEPROM_UVLO)) - { - SERIAL_ECHOLNPGM("INT4"); - uvlo_drain_reset(); - } -} - -ISR(INT4_vect) { - EIMSK &= ~(1 << 4); //disable INT4 interrupt to make sure that this code will be executed just once - SERIAL_ECHOLNPGM("INT4"); - //fire normal uvlo only in case where EEPROM_UVLO is 0 or if IS_SD_PRINTING is 1. - if(printer_active() && (!(eeprom_read_byte((uint8_t*)EEPROM_UVLO)))) uvlo_(); - if(eeprom_read_byte((uint8_t*)EEPROM_UVLO)) uvlo_tiny(); -} - -void recover_print(uint8_t automatic) { - lcd_update_enable(true); - lcd_update(2); - lcd_setstatuspgm(_i("Recovering print"));////MSG_RECOVERING_PRINT c=20 - - // Recover position, temperatures and extrude_multipliers - bool mbl_was_active = recover_machine_state_after_power_panic(); - - // Lift the print head 25mm, first to avoid collisions with oozed material with the print, - // and second also so one may remove the excess priming material. - if(eeprom_read_byte((uint8_t*)EEPROM_UVLO) == 1) - { - enquecommandf_P(PSTR("G1 Z%.3f F800"), current_position[Z_AXIS] + 25); - } - - // Home X and Y axes. Homing just X and Y shall not touch the babystep and the world2machine - // transformation status. G28 will not touch Z when MBL is off. - enquecommand_P(PSTR("G28 X Y")); - // Set the target bed and nozzle temperatures and wait. - enquecommandf_P(PSTR("M104 S%d"), target_temperature[active_extruder]); - enquecommandf_P(PSTR("M140 S%d"), target_temperature_bed); - enquecommandf_P(PSTR("M109 S%d"), target_temperature[active_extruder]); - enquecommand_P(MSG_M83); //E axis relative mode - - // If not automatically recoreverd (long power loss) - if(automatic == 0){ - //Extrude some filament to stabilize the pressure - enquecommand_P(PSTR("G1 E5 F120")); - // Retract to be consistent with a short pause - enquecommandf_P(G1_E_F2700, default_retraction); - } - - printf_P(_N("After waiting for temp:\nCurrent pos X_AXIS:%.3f\nCurrent pos Y_AXIS:%.3f\n"), current_position[X_AXIS], current_position[Y_AXIS]); - - // Restart the print. - restore_print_from_eeprom(mbl_was_active); - printf_P(_N("Current pos Z_AXIS:%.3f\nCurrent pos E_AXIS:%.3f\n"), current_position[Z_AXIS], current_position[E_AXIS]); -} - -bool recover_machine_state_after_power_panic() -{ - // 1) Preset some dummy values for the XY axes - current_position[X_AXIS] = 0; - current_position[Y_AXIS] = 0; - - // 2) Restore the mesh bed leveling offsets, but not the MBL status. - // This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case. - bool mbl_was_active = false; - for (int8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) { - uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 - uint8_t iy = mesh_point / MESH_NUM_X_POINTS; - // Scale the z value to 10u resolution. - int16_t v; - eeprom_read_block(&v, (void*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL+2*mesh_point), 2); - if (v != 0) - mbl_was_active = true; - mbl.z_values[iy][ix] = float(v) * 0.001f; - } - - // Recover the physical coordinate of the Z axis at the time of the power panic. - // The current position after power panic is moved to the next closest 0th full step. - current_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z)); - - // Recover last E axis position - current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E)); - - // 3) Initialize the logical to physical coordinate system transformation. - world2machine_initialize(); -// SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial "); -// print_mesh_bed_leveling_table(); - - // 4) Load the baby stepping value, which is expected to be active at the time of power panic. - // The baby stepping value is used to reset the physical Z axis when rehoming the Z axis. - babystep_load(); - - // 5) Set the physical positions from the logical positions using the world2machine transformation - // This is only done to inizialize Z/E axes with physical locations, since X/Y are unknown. - clamp_to_software_endstops(current_position); - set_destination_to_current(); - plan_set_position_curposXYZE(); - SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial "); - print_world_coordinates(); - - // 6) Power up the Z motors, mark their positions as known. - axis_known_position[Z_AXIS] = true; - enable_z(); - - // 7) Recover the target temperatures. - target_temperature[active_extruder] = eeprom_read_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND); - target_temperature_bed = eeprom_read_byte((uint8_t*)EEPROM_UVLO_TARGET_BED); - - // 8) Recover extruder multipilers - extruder_multiplier[0] = eeprom_read_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_0)); -#if EXTRUDERS > 1 - extruder_multiplier[1] = eeprom_read_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_1)); -#if EXTRUDERS > 2 - extruder_multiplier[2] = eeprom_read_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_2)); -#endif -#endif - extrudemultiply = (int)eeprom_read_word((uint16_t*)(EEPROM_EXTRUDEMULTIPLY)); - - // 9) Recover the saved target - saved_start_position[X_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+0*4)); - saved_start_position[Y_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+1*4)); - saved_start_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+2*4)); - saved_start_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+3*4)); - - saved_segment_idx = eeprom_read_word((uint16_t*)EEPROM_UVLO_SAVED_SEGMENT_IDX); - -#ifdef LIN_ADVANCE - extruder_advance_K = eeprom_read_float((float*)EEPROM_UVLO_LA_K); -#endif - - return mbl_was_active; -} - -void restore_print_from_eeprom(bool mbl_was_active) { - int feedrate_rec; - int feedmultiply_rec; - uint8_t fan_speed_rec; - char filename[FILENAME_LENGTH]; - uint8_t depth = 0; - char dir_name[9]; - - fan_speed_rec = eeprom_read_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED); - feedrate_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDRATE); - feedmultiply_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY); - SERIAL_ECHOPGM("Feedrate:"); - MYSERIAL.print(feedrate_rec); - SERIAL_ECHOPGM(", feedmultiply:"); - MYSERIAL.println(feedmultiply_rec); - - depth = eeprom_read_byte((uint8_t*)EEPROM_DIR_DEPTH); - - MYSERIAL.println(int(depth)); - for (uint8_t i = 0; i < depth; i++) { - for (uint8_t j = 0; j < 8; j++) { - dir_name[j] = eeprom_read_byte((uint8_t*)EEPROM_DIRS + j + 8 * i); - } - dir_name[8] = '\0'; - MYSERIAL.println(dir_name); - // strcpy(card.dir_names[i], dir_name); - card.chdir(dir_name, false); - } - - for (uint8_t i = 0; i < 8; i++) { - filename[i] = eeprom_read_byte((uint8_t*)EEPROM_FILENAME + i); - } - filename[8] = '\0'; - - MYSERIAL.print(filename); - strcat_P(filename, PSTR(".gco")); - enquecommandf_P(MSG_M23, filename); - uint32_t position = eeprom_read_dword((uint32_t*)(EEPROM_FILE_POSITION)); - SERIAL_ECHOPGM("Position read from eeprom:"); - MYSERIAL.println(position); - - // Move to the XY print position in logical coordinates, where the print has been killed, but - // without shifting Z along the way. This requires performing the move without mbl. - float pos_x = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0)); - float pos_y = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4)); - if (pos_x != X_COORD_INVALID) - { - enquecommandf_P(PSTR("G1 X%-.3f Y%-.3f F3000"), pos_x, pos_y); - } - - // Enable MBL and switch to logical positioning - if (mbl_was_active) - enquecommand_P(PSTR("PRUSA MBL V1")); - - // Move the Z axis down to the print, in logical coordinates. - enquecommandf_P(PSTR("G1 Z%-.3f"), eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z))); - - // Restore acceleration settings - float acceleration = eeprom_read_float((float*)(EEPROM_UVLO_ACCELL)); - float retract_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_RETRACT_ACCELL)); - float travel_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL)); - // accelerations are usually ordinary numbers, no need to keep extensive amount of decimal places - enquecommandf_P(PSTR("M204 P%-.1f R%-.1f T%-.1f"), acceleration, retract_acceleration, travel_acceleration); - - // Unretract. - enquecommandf_P(G1_E_F2700, default_retraction); - // Recover final E axis position and mode - float pos_e = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E)); - enquecommandf_P(PSTR("G92 E%-.3f"), pos_e); - if (eeprom_read_byte((uint8_t*)EEPROM_UVLO_E_ABS)) - enquecommand_P(PSTR("M82")); //E axis abslute mode - // Set the feedrates saved at the power panic. - enquecommandf_P(PSTR("G1 F%d"), feedrate_rec); - enquecommandf_P(MSG_M220, feedmultiply_rec); - // Set the fan speed saved at the power panic. - enquecommandf_P(PSTR("M106 S%u"), fan_speed_rec); - - // Set a position in the file. - enquecommandf_P(PSTR("M26 S%lu"), position); - enquecommand_P(PSTR("G4 S0")); - enquecommand_P(PSTR("PRUSA uvlo")); -} -#endif //UVLO_SUPPORT - - //! @brief Immediately stop print moves //! //! Immediately stop print moves, save current extruder temperature and position to RAM. @@ -10956,7 +10462,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move) #endif // save the global state at planning time - bool pos_invalid = XY_NO_RESTORE_FLAG; + bool pos_invalid = mesh_bed_leveling_flag || homing_flag; if (current_block && !pos_invalid) { memcpy(saved_start_position, current_block->gcode_start_position, sizeof(saved_start_position)); diff --git a/Firmware/power_panic.cpp b/Firmware/power_panic.cpp new file mode 100644 index 000000000..24683f74c --- /dev/null +++ b/Firmware/power_panic.cpp @@ -0,0 +1,492 @@ +#include "Configuration.h" +#include "config.h" + +#ifdef UVLO_SUPPORT +#include +#include // For HIGH and LOW macros +#include "backlight.h" +#include "cardreader.h" +#include "cmdqueue.h" +#include "eeprom.h" +#include "fastio.h" +#include "lcd.h" +#include "mesh_bed_leveling.h" +#include "mesh_bed_calibration.h" +#include "messages.h" +#include "planner.h" +#include "power_panic.h" +#include "stepper.h" +#include "system_timer.h" +#include "tmc2130.h" +#include "temperature.h" +#include "ultralcd.h" + +static bool recover_machine_state_after_power_panic(); +static void restore_print_from_eeprom(bool mbl_was_active); + +static void uvlo_drain_reset() { + // burn all that residual power + wdt_enable(WDTO_1S); + WRITE(BEEPER,HIGH); + lcd_clear(); + lcd_puts_at_P(0, 1, MSG_POWERPANIC_DETECTED); + while(1); +} + + +void uvlo_() { + unsigned long time_start = _millis(); + bool sd_print = card.sdprinting; + // Conserve power as soon as possible. +#ifdef LCD_BL_PIN + backlightMode = BACKLIGHT_MODE_DIM; + backlightLevel_LOW = 0; + backlight_update(); +#endif //LCD_BL_PIN + disable_x(); + disable_y(); + +#ifdef TMC2130 + tmc2130_set_current_h(Z_AXIS, 20); + tmc2130_set_current_r(Z_AXIS, 20); + tmc2130_set_current_h(E_AXIS, 20); + tmc2130_set_current_r(E_AXIS, 20); +#endif //TMC2130 + + // Stop all heaters + uint8_t saved_target_temperature_bed = target_temperature_bed; + uint16_t saved_target_temperature_ext = target_temperature[active_extruder]; + setTargetHotend(0); + setTargetBed(0); + + // Calculate the file position, from which to resume this print. + long sd_position = sdpos_atomic; //atomic sd position of last command added in queue + uint16_t sdlen_planner = planner_calc_sd_length(); //length of sd commands in planner + sd_position -= sdlen_planner; + uint16_t sdlen_cmdqueue = cmdqueue_calc_sd_length(); //length of sd commands in cmdqueue + sd_position -= sdlen_cmdqueue; + if (sd_position < 0) sd_position = 0; + + // save the global state at planning time + bool pos_invalid = mesh_bed_leveling_flag || homing_flag; + uint16_t feedrate_bckp; + if (current_block && !pos_invalid) + { + memcpy(saved_start_position, current_block->gcode_start_position, sizeof(saved_start_position)); + feedrate_bckp = current_block->gcode_feedrate; + saved_segment_idx = current_block->segment_idx; + } + else + { + saved_start_position[0] = SAVED_START_POSITION_UNSET; + feedrate_bckp = feedrate; + saved_segment_idx = 0; + } + + // From this point on and up to the print recovery, Z should not move during X/Y travels and + // should be controlled precisely. Reset the MBL status before planner_abort_hard in order to + // get the physical Z for further manipulation. + bool mbl_was_active = mbl.active; + mbl.active = false; + + // After this call, the planner queue is emptied and the current_position is set to a current logical coordinate. + // The logical coordinate will likely differ from the machine coordinate if the skew calibration and mesh bed leveling + // are in action. + planner_abort_hard(); + + // Store the print logical Z position, which we need to recover (a slight error here would be + // recovered on the next Gcode instruction, while a physical location error would not) + float logical_z = current_position[Z_AXIS]; + if(mbl_was_active) logical_z -= mbl.get_z(st_get_position_mm(X_AXIS), st_get_position_mm(Y_AXIS)); + eeprom_update_float((float*)EEPROM_UVLO_CURRENT_POSITION_Z, logical_z); + + // Store the print E position before we lose track + eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E), current_position[E_AXIS]); + eeprom_update_byte((uint8_t*)EEPROM_UVLO_E_ABS, (axis_relative_modes & E_AXIS_MASK)?0:1); + + // Clean the input command queue, inhibit serial processing using saved_printing + cmdqueue_reset(); + card.sdprinting = false; + saved_printing = true; + + // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and + // command queues are empty, SD card printing is disabled, usb is inhibited. + planner_aborted = false; + sei(); + + // Retract + current_position[E_AXIS] -= default_retraction; + plan_buffer_line_curposXYZE(95); + st_synchronize(); + disable_e0(); + + // Read out the current Z motor microstep counter to move the axis up towards + // a full step before powering off. NOTE: we need to ensure to schedule more + // than "dropsegments" steps in order to move (this is always the case here + // due to UVLO_Z_AXIS_SHIFT being used) + uint16_t z_res = tmc2130_get_res(Z_AXIS); + uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); + current_position[Z_AXIS] += float(1024 - z_microsteps) + / (z_res * cs.axis_steps_per_unit[Z_AXIS]) + + UVLO_Z_AXIS_SHIFT; + plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60); + st_synchronize(); + poweroff_z(); + + // Write the file position. + eeprom_update_dword((uint32_t*)(EEPROM_FILE_POSITION), sd_position); + + // Store the mesh bed leveling offsets. This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case. + for (uint8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) + { + uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 + uint8_t iy = mesh_point / MESH_NUM_X_POINTS; + // Scale the z value to 1u resolution. + int16_t v = mbl_was_active ? int16_t(floor(mbl.z_values[iy][ix] * 1000.f + 0.5f)) : 0; + eeprom_update_word((uint16_t*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL +2*mesh_point), *reinterpret_cast(&v)); + } + + // Write the _final_ Z position and motor microstep counter (unused). + eeprom_update_float((float*)EEPROM_UVLO_TINY_CURRENT_POSITION_Z, current_position[Z_AXIS]); + z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); + eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps); + + // Store the current position. + if (pos_invalid) + eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), X_COORD_INVALID); + else + { + eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]); + eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]); + } + + // Store the current feed rate, temperatures, fan speed and extruder multipliers (flow rates) + eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDRATE, feedrate_bckp); + eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY, feedmultiply); + eeprom_update_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND, saved_target_temperature_ext); + eeprom_update_byte((uint8_t*)EEPROM_UVLO_TARGET_BED, saved_target_temperature_bed); + eeprom_update_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED, fanSpeed); + eeprom_update_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_0), extruder_multiplier[0]); + eeprom_update_word((uint16_t*)(EEPROM_EXTRUDEMULTIPLY), (uint16_t)extrudemultiply); + + eeprom_update_float((float*)(EEPROM_UVLO_ACCELL), cs.acceleration); + eeprom_update_float((float*)(EEPROM_UVLO_RETRACT_ACCELL), cs.retract_acceleration); + eeprom_update_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL), cs.travel_acceleration); + + // Store the saved target + eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+0*4), saved_start_position[X_AXIS]); + eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+1*4), saved_start_position[Y_AXIS]); + eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+2*4), saved_start_position[Z_AXIS]); + eeprom_update_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+3*4), saved_start_position[E_AXIS]); + + eeprom_update_word((uint16_t*)EEPROM_UVLO_SAVED_SEGMENT_IDX, saved_segment_idx); + +#ifdef LIN_ADVANCE + eeprom_update_float((float*)(EEPROM_UVLO_LA_K), extruder_advance_K); +#endif + + // Finaly store the "power outage" flag. + if(sd_print) eeprom_update_byte((uint8_t*)EEPROM_UVLO, 1); + + // Increment power failure counter + eeprom_increment_byte((uint8_t*)EEPROM_POWER_COUNT); + eeprom_increment_word((uint16_t*)EEPROM_POWER_COUNT_TOT); + + printf_P(_N("UVLO - end %d\n"), _millis() - time_start); + WRITE(BEEPER,HIGH); + + // All is set: with all the juice left, try to move extruder away to detach the nozzle completely from the print + poweron_z(); + current_position[X_AXIS] = (current_position[X_AXIS] < 0.5f * (X_MIN_POS + X_MAX_POS)) ? X_MIN_POS : X_MAX_POS; + plan_buffer_line_curposXYZE(500); + st_synchronize(); + + wdt_enable(WDTO_1S); + while(1); +} + + +static void uvlo_tiny() { + unsigned long time_start = _millis(); + + // Conserve power as soon as possible. + disable_x(); + disable_y(); + disable_e0(); + +#ifdef TMC2130 + tmc2130_set_current_h(Z_AXIS, 20); + tmc2130_set_current_r(Z_AXIS, 20); +#endif //TMC2130 + + // Stop all heaters + setTargetHotend(0); + setTargetBed(0); + + // When power is interrupted on the _first_ recovery an attempt can be made to raise the + // extruder, causing the Z position to change. Similarly, when recovering, the Z position is + // lowered. In such cases we cannot just save Z, we need to re-align the steppers to a fullstep. + // Disable MBL (if not already) to work with physical coordinates. + mbl.active = false; + planner_abort_hard(); + + // Allow for small roundoffs to be ignored + if(fabs(current_position[Z_AXIS] - eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))) >= 1.f/cs.axis_steps_per_unit[Z_AXIS]) + { + // Clean the input command queue, inhibit serial processing using saved_printing + cmdqueue_reset(); + card.sdprinting = false; + saved_printing = true; + + // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and + // command queues are empty, SD card printing is disabled, usb is inhibited. + planner_aborted = false; + sei(); + + // The axis was moved: adjust Z as done on a regular UVLO. + uint16_t z_res = tmc2130_get_res(Z_AXIS); + uint16_t z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); + current_position[Z_AXIS] += float(1024 - z_microsteps) + / (z_res * cs.axis_steps_per_unit[Z_AXIS]) + + UVLO_TINY_Z_AXIS_SHIFT; + plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS]/60); + st_synchronize(); + poweroff_z(); + + // Update Z position + eeprom_update_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z), current_position[Z_AXIS]); + + // Update the _final_ Z motor microstep counter (unused). + z_microsteps = tmc2130_rd_MSCNT(Z_AXIS); + eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps); + } + + // Update the the "power outage" flag. + eeprom_update_byte((uint8_t*)EEPROM_UVLO,2); + + // Increment power failure counter + eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, eeprom_read_byte((uint8_t*)EEPROM_POWER_COUNT) + 1); + eeprom_update_word((uint16_t*)EEPROM_POWER_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) + 1); + + printf_P(_N("UVLO_TINY - end %d\n"), _millis() - time_start); + uvlo_drain_reset(); +} + +void setup_uvlo_interrupt() { + DDRE &= ~(1 << 4); //input pin + PORTE &= ~(1 << 4); //no internal pull-up + + // sensing falling edge + EICRB |= (1 << 0); + EICRB &= ~(1 << 1); + + // enable INT4 interrupt + EIMSK |= (1 << 4); + + // check if power was lost before we armed the interrupt + if(!(PINE & (1 << 4)) && eeprom_read_byte((uint8_t*)EEPROM_UVLO)) + { + SERIAL_ECHOLNPGM("INT4"); + uvlo_drain_reset(); + } +} + +ISR(INT4_vect) { + EIMSK &= ~(1 << 4); //disable INT4 interrupt to make sure that this code will be executed just once + SERIAL_ECHOLNPGM("INT4"); + //fire normal uvlo only in case where EEPROM_UVLO is 0 or if IS_SD_PRINTING is 1. + if(printer_active() && (!(eeprom_read_byte((uint8_t*)EEPROM_UVLO)))) uvlo_(); + if(eeprom_read_byte((uint8_t*)EEPROM_UVLO)) uvlo_tiny(); +} + +void recover_print(uint8_t automatic) { + lcd_update_enable(true); + lcd_update(2); + lcd_setstatuspgm(_i("Recovering print"));////MSG_RECOVERING_PRINT c=20 + + // Recover position, temperatures and extrude_multipliers + bool mbl_was_active = recover_machine_state_after_power_panic(); + + // Lift the print head 25mm, first to avoid collisions with oozed material with the print, + // and second also so one may remove the excess priming material. + if(eeprom_read_byte((uint8_t*)EEPROM_UVLO) == 1) + { + enquecommandf_P(PSTR("G1 Z%.3f F800"), current_position[Z_AXIS] + 25); + } + + // Home X and Y axes. Homing just X and Y shall not touch the babystep and the world2machine + // transformation status. G28 will not touch Z when MBL is off. + enquecommand_P(PSTR("G28 X Y")); + // Set the target bed and nozzle temperatures and wait. + enquecommandf_P(PSTR("M104 S%d"), target_temperature[active_extruder]); + enquecommandf_P(PSTR("M140 S%d"), target_temperature_bed); + enquecommandf_P(PSTR("M109 S%d"), target_temperature[active_extruder]); + enquecommand_P(MSG_M83); //E axis relative mode + + // If not automatically recoreverd (long power loss) + if(automatic == 0){ + //Extrude some filament to stabilize the pressure + enquecommand_P(PSTR("G1 E5 F120")); + // Retract to be consistent with a short pause + enquecommandf_P(G1_E_F2700, default_retraction); + } + + printf_P(_N("After waiting for temp:\nCurrent pos X_AXIS:%.3f\nCurrent pos Y_AXIS:%.3f\n"), current_position[X_AXIS], current_position[Y_AXIS]); + + // Restart the print. + restore_print_from_eeprom(mbl_was_active); + printf_P(_N("Current pos Z_AXIS:%.3f\nCurrent pos E_AXIS:%.3f\n"), current_position[Z_AXIS], current_position[E_AXIS]); +} + +bool recover_machine_state_after_power_panic() { + // 1) Preset some dummy values for the XY axes + current_position[X_AXIS] = 0; + current_position[Y_AXIS] = 0; + + // 2) Restore the mesh bed leveling offsets, but not the MBL status. + // This is 2*7*7=98 bytes, which takes 98*3.4us=333us in worst case. + bool mbl_was_active = false; + for (int8_t mesh_point = 0; mesh_point < MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS; ++ mesh_point) { + uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 + uint8_t iy = mesh_point / MESH_NUM_X_POINTS; + // Scale the z value to 10u resolution. + int16_t v; + eeprom_read_block(&v, (void*)(EEPROM_UVLO_MESH_BED_LEVELING_FULL+2*mesh_point), 2); + if (v != 0) + mbl_was_active = true; + mbl.z_values[iy][ix] = float(v) * 0.001f; + } + + // Recover the physical coordinate of the Z axis at the time of the power panic. + // The current position after power panic is moved to the next closest 0th full step. + current_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z)); + + // Recover last E axis position + current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E)); + + // 3) Initialize the logical to physical coordinate system transformation. + world2machine_initialize(); + // SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial "); + // print_mesh_bed_leveling_table(); + + // 4) Load the baby stepping value, which is expected to be active at the time of power panic. + // The baby stepping value is used to reset the physical Z axis when rehoming the Z axis. + babystep_load(); + + // 5) Set the physical positions from the logical positions using the world2machine transformation + // This is only done to inizialize Z/E axes with physical locations, since X/Y are unknown. + clamp_to_software_endstops(current_position); + set_destination_to_current(); + plan_set_position_curposXYZE(); + SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial "); + print_world_coordinates(); + + // 6) Power up the Z motors, mark their positions as known. + axis_known_position[Z_AXIS] = true; + enable_z(); + + // 7) Recover the target temperatures. + target_temperature[active_extruder] = eeprom_read_word((uint16_t*)EEPROM_UVLO_TARGET_HOTEND); + target_temperature_bed = eeprom_read_byte((uint8_t*)EEPROM_UVLO_TARGET_BED); + + // 8) Recover extruder multipilers + extruder_multiplier[0] = eeprom_read_float((float*)(EEPROM_EXTRUDER_MULTIPLIER_0)); + extrudemultiply = (int)eeprom_read_word((uint16_t*)(EEPROM_EXTRUDEMULTIPLY)); + + // 9) Recover the saved target + saved_start_position[X_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+0*4)); + saved_start_position[Y_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+1*4)); + saved_start_position[Z_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+2*4)); + saved_start_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_SAVED_START_POSITION+3*4)); + + saved_segment_idx = eeprom_read_word((uint16_t*)EEPROM_UVLO_SAVED_SEGMENT_IDX); + +#ifdef LIN_ADVANCE + extruder_advance_K = eeprom_read_float((float*)EEPROM_UVLO_LA_K); +#endif + + return mbl_was_active; +} + +void restore_print_from_eeprom(bool mbl_was_active) { + int feedrate_rec; + int feedmultiply_rec; + uint8_t fan_speed_rec; + char filename[FILENAME_LENGTH]; + uint8_t depth = 0; + char dir_name[9]; + + fan_speed_rec = eeprom_read_byte((uint8_t*)EEPROM_UVLO_FAN_SPEED); + feedrate_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDRATE); + feedmultiply_rec = eeprom_read_word((uint16_t*)EEPROM_UVLO_FEEDMULTIPLY); + SERIAL_ECHOPGM("Feedrate:"); + MYSERIAL.print(feedrate_rec); + SERIAL_ECHOPGM(", feedmultiply:"); + MYSERIAL.println(feedmultiply_rec); + + depth = eeprom_read_byte((uint8_t*)EEPROM_DIR_DEPTH); + + MYSERIAL.println(int(depth)); + for (uint8_t i = 0; i < depth; i++) { + for (uint8_t j = 0; j < 8; j++) { + dir_name[j] = eeprom_read_byte((uint8_t*)EEPROM_DIRS + j + 8 * i); + } + dir_name[8] = '\0'; + MYSERIAL.println(dir_name); + // strcpy(card.dir_names[i], dir_name); + card.chdir(dir_name, false); + } + + for (uint8_t i = 0; i < 8; i++) { + filename[i] = eeprom_read_byte((uint8_t*)EEPROM_FILENAME + i); + } + filename[8] = '\0'; + + MYSERIAL.print(filename); + strcat_P(filename, PSTR(".gco")); + enquecommandf_P(MSG_M23, filename); + uint32_t position = eeprom_read_dword((uint32_t*)(EEPROM_FILE_POSITION)); + SERIAL_ECHOPGM("Position read from eeprom:"); + MYSERIAL.println(position); + + // Move to the XY print position in logical coordinates, where the print has been killed, but + // without shifting Z along the way. This requires performing the move without mbl. + float pos_x = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0)); + float pos_y = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4)); + if (pos_x != X_COORD_INVALID) { + enquecommandf_P(PSTR("G1 X%-.3f Y%-.3f F3000"), pos_x, pos_y); + } + + // Enable MBL and switch to logical positioning + if (mbl_was_active) + enquecommand_P(PSTR("PRUSA MBL V1")); + + // Move the Z axis down to the print, in logical coordinates. + enquecommandf_P(PSTR("G1 Z%-.3f"), eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z))); + + // Restore acceleration settings + float acceleration = eeprom_read_float((float*)(EEPROM_UVLO_ACCELL)); + float retract_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_RETRACT_ACCELL)); + float travel_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL)); + // accelerations are usually ordinary numbers, no need to keep extensive amount of decimal places + enquecommandf_P(PSTR("M204 P%-.1f R%-.1f T%-.1f"), acceleration, retract_acceleration, travel_acceleration); + + // Unretract. + enquecommandf_P(G1_E_F2700, default_retraction); + // Recover final E axis position and mode + float pos_e = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E)); + enquecommandf_P(PSTR("G92 E%-.3f"), pos_e); + if (eeprom_read_byte((uint8_t*)EEPROM_UVLO_E_ABS)) + enquecommand_P(PSTR("M82")); //E axis abslute mode + // Set the feedrates saved at the power panic. + enquecommandf_P(PSTR("G1 F%d"), feedrate_rec); + enquecommandf_P(MSG_M220, feedmultiply_rec); + // Set the fan speed saved at the power panic. + enquecommandf_P(PSTR("M106 S%u"), fan_speed_rec); + + // Set a position in the file. + enquecommandf_P(PSTR("M26 S%lu"), position); + enquecommand_P(PSTR("G4 S0")); + enquecommand_P(PSTR("PRUSA uvlo")); +} +#endif //UVLO_SUPPORT diff --git a/Firmware/power_panic.h b/Firmware/power_panic.h new file mode 100644 index 000000000..cc9926fe6 --- /dev/null +++ b/Firmware/power_panic.h @@ -0,0 +1,5 @@ +#pragma once + +void uvlo_(); +void recover_print(uint8_t automatic); +void setup_uvlo_interrupt();