/** * Prime the nozzle if needed. Return true on error. */ bool prime_nozzle() { float Total_Prime = 0.0; if (prime_flag == -1) { // The user wants to control how much filament gets purged ubl.has_control_of_lcd_panel = true; lcd_setstatuspgm(PSTR("User-Controlled Prime"), 99); chirp_at_user(); set_destination_to_current(); un_retract_filament(destination); // Make sure G26 doesn't think the filament is retracted(). while (!ubl_lcd_clicked()) { chirp_at_user(); destination[E_AXIS] += 0.25; #ifdef PREVENT_LENGTHY_EXTRUDE Total_Prime += 0.25; if (Total_Prime >= EXTRUDE_MAXLENGTH) return UBL_ERR; #endif ubl_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0, 0); stepper.synchronize(); // Without this synchronize, the purge is more consistent, // but because the planner has a buffer, we won't be able // to stop as quickly. So we put up with the less smooth // action to give the user a more responsive 'Stop'. set_destination_to_current(); idle(); } while (ubl_lcd_clicked()) idle(); // Debounce Encoder Wheel #if ENABLED(ULTRA_LCD) strcpy_P(lcd_status_message, PSTR("Done Priming")); // We can't do lcd_setstatuspgm() without having it continue; // So... We cheat to get a message up. lcd_setstatuspgm(PSTR("Done Priming"), 99); lcd_quick_feedback(); #endif ubl.has_control_of_lcd_panel = false; } else { #if ENABLED(ULTRA_LCD) lcd_setstatuspgm(PSTR("Fixed Length Prime."), 99); lcd_quick_feedback(); #endif set_destination_to_current(); destination[E_AXIS] += prime_length; ubl_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0, 0); stepper.synchronize(); set_destination_to_current(); retract_filament(destination); } return UBL_OK; }
/** * Turn on the bed and nozzle heat and * wait for them to get up to temperature. */ bool unified_bed_leveling::turn_on_heaters() { millis_t next = millis() + 5000UL; #if HAS_TEMP_BED #if ENABLED(ULTRA_LCD) if (g26_bed_temp > 25) { lcd_setstatusPGM(PSTR("G26 Heating Bed."), 99); lcd_quick_feedback(); #endif has_control_of_lcd_panel = true; thermalManager.setTargetBed(g26_bed_temp); while (abs(thermalManager.degBed() - g26_bed_temp) > 3) { #if ENABLED(NEWPANEL) if (ubl_lcd_clicked()) return exit_from_g26(); #endif if (PENDING(millis(), next)) { next = millis() + 5000UL; print_heaterstates(); } idle(); } #if ENABLED(ULTRA_LCD) } lcd_setstatusPGM(PSTR("G26 Heating Nozzle."), 99); lcd_quick_feedback(); #endif #endif // Start heating the nozzle and wait for it to reach temperature. thermalManager.setTargetHotend(g26_hotend_temp, 0); while (abs(thermalManager.degHotend(0) - g26_hotend_temp) > 3) { #if ENABLED(NEWPANEL) if (ubl_lcd_clicked()) return exit_from_g26(); #endif if (PENDING(millis(), next)) { next = millis() + 5000UL; print_heaterstates(); } idle(); } #if ENABLED(ULTRA_LCD) lcd_reset_status(); lcd_quick_feedback(); #endif return UBL_OK; }
bool exit_from_g26() { //strcpy(lcd_status_message, "Leaving G26"); // We can't do lcd_setstatus() without having it continue; lcd_reset_alert_level(); lcd_setstatuspgm(PSTR("Leaving G26")); while (ubl_lcd_clicked()) idle(); return UBL_ERR; }
/** * Detect ubl_lcd_clicked, debounce it, and return true for cancel */ bool user_canceled() { if (!ubl_lcd_clicked()) return false; safe_delay(10); // Wait for click to settle #if ENABLED(ULTRA_LCD) lcd_setstatusPGM(PSTR("Mesh Validation Stopped."), 99); lcd_quick_feedback(); #endif while (!ubl_lcd_clicked()) idle(); // Wait for button release // If the button is suddenly pressed again, // ask the user to resolve the issue lcd_setstatusPGM(PSTR("Release button"), 99); // will never appear... while (ubl_lcd_clicked()) idle(); // unless this loop happens lcd_reset_status(); return true; }
bool unified_bed_leveling::exit_from_g26() { lcd_setstatusPGM(PSTR("Leaving G26"), -1); while (ubl_lcd_clicked()) idle(); return UBL_ERR; }
/** * G26: Mesh Validation Pattern generation. * * Used to interactively edit UBL's Mesh by placing the * nozzle in a problem area and doing a G29 P4 R command. */ void gcode_G26() { SERIAL_ECHOLNPGM("G26 command started. Waiting for heater(s)."); float tmp, start_angle, end_angle; int i, xi, yi; mesh_index_pair location; // Don't allow Mesh Validation without homing first, // or if the parameter parsing did not go OK, abort if (axis_unhomed_error(true, true, true) || parse_G26_parameters()) return; if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) { do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); stepper.synchronize(); set_current_to_destination(); } if (turn_on_heaters()) goto LEAVE; current_position[E_AXIS] = 0.0; sync_plan_position_e(); if (prime_flag && prime_nozzle()) goto LEAVE; /** * Bed is preheated * * Nozzle is at temperature * * Filament is primed! * * It's "Show Time" !!! */ ZERO(circle_flags); ZERO(horizontal_mesh_line_flags); ZERO(vertical_mesh_line_flags); // Move nozzle to the specified height for the first layer set_destination_to_current(); destination[Z_AXIS] = layer_height; move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0.0); move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], ooze_amount); ubl.has_control_of_lcd_panel = true; //debug_current_and_destination(PSTR("Starting G26 Mesh Validation Pattern.")); /** * Declare and generate a sin() & cos() table to be used during the circle drawing. This will lighten * the CPU load and make the arc drawing faster and more smooth */ float sin_table[360 / 30 + 1], cos_table[360 / 30 + 1]; for (i = 0; i <= 360 / 30; i++) { cos_table[i] = SIZE_OF_INTERSECTION_CIRCLES * cos(RADIANS(valid_trig_angle(i * 30.0))); sin_table[i] = SIZE_OF_INTERSECTION_CIRCLES * sin(RADIANS(valid_trig_angle(i * 30.0))); } do { if (ubl_lcd_clicked()) { // Check if the user wants to stop the Mesh Validation #if ENABLED(ULTRA_LCD) lcd_setstatuspgm(PSTR("Mesh Validation Stopped."), 99); lcd_quick_feedback(); #endif while (!ubl_lcd_clicked()) { // Wait until the user is done pressing the idle(); // Encoder Wheel if that is why we are leaving lcd_reset_alert_level(); lcd_setstatuspgm(PSTR("")); } while (ubl_lcd_clicked()) { // Wait until the user is done pressing the idle(); // Encoder Wheel if that is why we are leaving lcd_setstatuspgm(PSTR("Unpress Wheel"), 99); } goto LEAVE; } location = continue_with_closest ? find_closest_circle_to_print(current_position[X_AXIS], current_position[Y_AXIS]) : find_closest_circle_to_print(x_pos, y_pos); // Find the closest Mesh Intersection to where we are now. if (location.x_index >= 0 && location.y_index >= 0) { const float circle_x = pgm_read_float(&ubl.mesh_index_to_xpos[location.x_index]), circle_y = pgm_read_float(&ubl.mesh_index_to_ypos[location.y_index]); // Let's do a couple of quick sanity checks. We can pull this code out later if we never see it catch a problem #ifdef DELTA if (HYPOT2(circle_x, circle_y) > sq(DELTA_PRINTABLE_RADIUS)) { SERIAL_ERROR_START; SERIAL_ERRORLNPGM("Attempt to print outside of DELTA_PRINTABLE_RADIUS."); goto LEAVE; } #endif // TODO: Change this to use `position_is_reachable` if (!WITHIN(circle_x, X_MIN_POS, X_MAX_POS) || !WITHIN(circle_y, Y_MIN_POS, Y_MAX_POS)) { SERIAL_ERROR_START; SERIAL_ERRORLNPGM("Attempt to print off the bed."); goto LEAVE; } xi = location.x_index; // Just to shrink the next few lines and make them easier to understand yi = location.y_index; if (ubl.g26_debug_flag) { SERIAL_ECHOPAIR(" Doing circle at: (xi=", xi); SERIAL_ECHOPAIR(", yi=", yi); SERIAL_CHAR(')'); SERIAL_EOL; } start_angle = 0.0; // assume it is going to be a full circle end_angle = 360.0; if (xi == 0) { // Check for bottom edge start_angle = -90.0; end_angle = 90.0; if (yi == 0) // it is an edge, check for the two left corners start_angle = 0.0; else if (yi == GRID_MAX_POINTS_Y - 1) end_angle = 0.0; } else if (xi == GRID_MAX_POINTS_X - 1) { // Check for top edge start_angle = 90.0; end_angle = 270.0; if (yi == 0) // it is an edge, check for the two right corners end_angle = 180.0; else if (yi == GRID_MAX_POINTS_Y - 1) start_angle = 180.0; } else if (yi == 0) { start_angle = 0.0; // only do the top side of the cirlce end_angle = 180.0; } else if (yi == GRID_MAX_POINTS_Y - 1) { start_angle = 180.0; // only do the bottom side of the cirlce end_angle = 360.0; } for (tmp = start_angle; tmp < end_angle - 0.1; tmp += 30.0) { int tmp_div_30 = tmp / 30.0; if (tmp_div_30 < 0) tmp_div_30 += 360 / 30; if (tmp_div_30 > 11) tmp_div_30 -= 360 / 30; float x = circle_x + cos_table[tmp_div_30], // for speed, these are now a lookup table entry y = circle_y + sin_table[tmp_div_30], xe = circle_x + cos_table[tmp_div_30 + 1], ye = circle_y + sin_table[tmp_div_30 + 1]; #ifdef DELTA if (HYPOT2(x, y) > sq(DELTA_PRINTABLE_RADIUS)) // Check to make sure this part of continue; // the 'circle' is on the bed. If #else // not, we need to skip x = constrain(x, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops y = constrain(y, Y_MIN_POS + 1, Y_MAX_POS - 1); xe = constrain(xe, X_MIN_POS + 1, X_MAX_POS - 1); ye = constrain(ye, Y_MIN_POS + 1, Y_MAX_POS - 1); #endif //if (ubl.g26_debug_flag) { // char ccc, *cptr, seg_msg[50], seg_num[10]; // strcpy(seg_msg, " segment: "); // strcpy(seg_num, " \n"); // cptr = (char*) "01234567890ABCDEF????????"; // ccc = cptr[tmp_div_30]; // seg_num[1] = ccc; // strcat(seg_msg, seg_num); // debug_current_and_destination(seg_msg); //} print_line_from_here_to_there(LOGICAL_X_POSITION(x), LOGICAL_Y_POSITION(y), layer_height, LOGICAL_X_POSITION(xe), LOGICAL_Y_POSITION(ye), layer_height); } //debug_current_and_destination(PSTR("Looking for lines to connect.")); look_for_lines_to_connect(); //debug_current_and_destination(PSTR("Done with line connect.")); } //debug_current_and_destination(PSTR("Done with current circle.")); } while (location.x_index >= 0 && location.y_index >= 0); LEAVE: lcd_reset_alert_level(); lcd_setstatuspgm(PSTR("Leaving G26")); retract_filament(destination); destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES; //debug_current_and_destination(PSTR("ready to do Z-Raise.")); move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0); // Raise the nozzle //debug_current_and_destination(PSTR("done doing Z-Raise.")); destination[X_AXIS] = x_pos; // Move back to the starting position destination[Y_AXIS] = y_pos; //destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES; // Keep the nozzle where it is move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], 0); // Move back to the starting position //debug_current_and_destination(PSTR("done doing X/Y move.")); ubl.has_control_of_lcd_panel = false; // Give back control of the LCD Panel! if (!keep_heaters_on) { #if HAS_TEMP_BED thermalManager.setTargetBed(0); #endif thermalManager.setTargetHotend(0, 0); } }