This commit is contained in:
Xavier Perignon
2020-06-15 16:07:19 +02:00
parent b5c4eadb9b
commit f6ab6cb434
2511 changed files with 2005689 additions and 1974 deletions

View File

@@ -0,0 +1,855 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* Marlin Firmware -- G26 - Mesh Validation Tool
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(G26_MESH_VALIDATION)
#define G26_OK false
#define G26_ERR true
#include "../../gcode/gcode.h"
#include "../../feature/bedlevel/bedlevel.h"
#include "../../MarlinCore.h"
#include "../../module/planner.h"
#include "../../module/stepper.h"
#include "../../module/motion.h"
#include "../../module/tool_change.h"
#include "../../module/temperature.h"
#include "../../lcd/ultralcd.h"
#define EXTRUSION_MULTIPLIER 1.0
#define PRIME_LENGTH 10.0
#define OOZE_AMOUNT 0.3
#define INTERSECTION_CIRCLE_RADIUS 5
#define CROSSHAIRS_SIZE 3
#ifndef G26_RETRACT_MULTIPLIER
#define G26_RETRACT_MULTIPLIER 1.0 // x 1mm
#endif
#ifndef G26_XY_FEEDRATE
#define G26_XY_FEEDRATE (PLANNER_XY_FEEDRATE() / 3.0)
#endif
#if CROSSHAIRS_SIZE >= INTERSECTION_CIRCLE_RADIUS
#error "CROSSHAIRS_SIZE must be less than INTERSECTION_CIRCLE_RADIUS."
#endif
#define G26_OK false
#define G26_ERR true
#if ENABLED(ARC_SUPPORT)
void plan_arc(const xyze_pos_t &cart, const ab_float_t &offset, const uint8_t clockwise);
#endif
/**
* G26 Mesh Validation Tool
*
* G26 is a Mesh Validation Tool intended to provide support for the Marlin Unified Bed Leveling System.
* In order to fully utilize and benefit from the Marlin Unified Bed Leveling System an accurate Mesh must
* be defined. G29 is designed to allow the user to quickly validate the correctness of her Mesh. It will
* first heat the bed and nozzle. It will then print lines and circles along the Mesh Cell boundaries and
* the intersections of those lines (respectively).
*
* This action allows the user to immediately see where the Mesh is properly defined and where it needs to
* be edited. The command will generate the Mesh lines closest to the nozzle's starting position. Alternatively
* the user can specify the X and Y position of interest with command parameters. This allows the user to
* focus on a particular area of the Mesh where attention is needed.
*
* B # Bed Set the Bed Temperature. If not specified, a default of 60 C. will be assumed.
*
* C Current When searching for Mesh Intersection points to draw, use the current nozzle location
* as the base for any distance comparison.
*
* D Disable Disable the Unified Bed Leveling System. In the normal case the user is invoking this
* command to see how well a Mesh as been adjusted to match a print surface. In order to do
* this the Unified Bed Leveling System is turned on by the G26 command. The D parameter
* alters the command's normal behavior and disables the Unified Bed Leveling System even if
* it is on.
*
* H # Hotend Set the Nozzle Temperature. If not specified, a default of 205 C. will be assumed.
*
* F # Filament Used to specify the diameter of the filament being used. If not specified
* 1.75mm filament is assumed. If you are not getting acceptable results by using the
* 'correct' numbers, you can scale this number up or down a little bit to change the amount
* of filament that is being extruded during the printing of the various lines on the bed.
*
* K Keep-On Keep the heaters turned on at the end of the command.
*
* L # Layer Layer height. (Height of nozzle above bed) If not specified .20mm will be used.
*
* O # Ooooze How much your nozzle will Ooooze filament while getting in position to print. This
* is over kill, but using this parameter will let you get the very first 'circle' perfect
* so you have a trophy to peel off of the bed and hang up to show how perfectly you have your
* Mesh calibrated. If not specified, a filament length of .3mm is assumed.
*
* P # Prime Prime the nozzle with specified length of filament. If this parameter is not
* given, no prime action will take place. If the parameter specifies an amount, that much
* will be purged before continuing. If no amount is specified the command will start
* purging filament until the user provides an LCD Click and then it will continue with
* printing the Mesh. You can carefully remove the spent filament with a needle nose
* pliers while holding the LCD Click wheel in a depressed state. If you do not have
* an LCD, you must specify a value if you use P.
*
* Q # Multiplier Retraction Multiplier. Normally not needed. Retraction defaults to 1.0mm and
* un-retraction is at 1.2mm These numbers will be scaled by the specified amount
*
* R # Repeat Prints the number of patterns given as a parameter, starting at the current location.
* If a parameter isn't given, every point will be printed unless G26 is interrupted.
* This works the same way that the UBL G29 P4 R parameter works.
*
* NOTE: If you do not have an LCD, you -must- specify R. This is to ensure that you are
* aware that there's some risk associated with printing without the ability to abort in
* cases where mesh point Z value may be inaccurate. As above, if you do not include a
* parameter, every point will be printed.
*
* S # Nozzle Used to control the size of nozzle diameter. If not specified, a .4mm nozzle is assumed.
*
* U # Random Randomize the order that the circles are drawn on the bed. The search for the closest
* un-drawn circle is still done. But the distance to the location for each circle has a
* random number of the specified size added to it. Specifying S50 will give an interesting
* deviation from the normal behavior on a 10 x 10 Mesh.
*
* X # X Coord. Specify the starting location of the drawing activity.
*
* Y # Y Coord. Specify the starting location of the drawing activity.
*/
// External references
// Private functions
static MeshFlags circle_flags, horizontal_mesh_line_flags, vertical_mesh_line_flags;
float g26_e_axis_feedrate = 0.025,
random_deviation = 0.0;
static bool g26_retracted = false; // Track the retracted state of the nozzle so mismatched
// retracts/recovers won't result in a bad state.
float g26_extrusion_multiplier,
g26_retraction_multiplier,
g26_layer_height,
g26_prime_length;
xy_pos_t g26_xy_pos; // = { 0, 0 }
int16_t g26_bed_temp,
g26_hotend_temp;
int8_t g26_prime_flag;
#if HAS_LCD_MENU
/**
* If the LCD is clicked, cancel, wait for release, return true
*/
bool user_canceled() {
if (!ui.button_pressed()) return false; // Return if the button isn't pressed
ui.set_status_P(GET_TEXT(MSG_G26_CANCELED), 99);
#if HAS_LCD_MENU
ui.quick_feedback();
#endif
ui.wait_for_release();
return true;
}
#endif
mesh_index_pair find_closest_circle_to_print(const xy_pos_t &pos) {
float closest = 99999.99;
mesh_index_pair out_point;
out_point.pos = -1;
GRID_LOOP(i, j) {
if (!circle_flags.marked(i, j)) {
// We found a circle that needs to be printed
const xy_pos_t m = { _GET_MESH_X(i), _GET_MESH_Y(j) };
// Get the distance to this intersection
float f = (pos - m).magnitude();
// It is possible that we are being called with the values
// to let us find the closest circle to the start position.
// But if this is not the case, add a small weighting to the
// distance calculation to help it choose a better place to continue.
f += (g26_xy_pos - m).magnitude() / 15.0f;
// Add the specified amount of Random Noise to our search
if (random_deviation > 1.0) f += random(0.0, random_deviation);
if (f < closest) {
closest = f; // Found a closer un-printed location
out_point.pos.set(i, j); // Save its data
out_point.distance = closest;
}
}
}
circle_flags.mark(out_point); // Mark this location as done.
return out_point;
}
void move_to(const float &rx, const float &ry, const float &z, const float &e_delta) {
static float last_z = -999.99;
const xy_pos_t dest = { rx, ry };
const bool has_xy_component = dest != current_position; // Check if X or Y is involved in the movement.
destination = current_position;
if (z != last_z) {
last_z = destination.z = z;
const feedRate_t feed_value = planner.settings.max_feedrate_mm_s[Z_AXIS] * 0.5f; // Use half of the Z_AXIS max feed rate
prepare_internal_move_to_destination(feed_value);
destination = current_position;
}
// If X or Y is involved do a 'normal' move. Otherwise retract/recover/hop.
destination = dest;
destination.e += e_delta;
const feedRate_t feed_value = has_xy_component ? feedRate_t(G26_XY_FEEDRATE) : planner.settings.max_feedrate_mm_s[E_AXIS] * 0.666f;
prepare_internal_move_to_destination(feed_value);
destination = current_position;
}
FORCE_INLINE void move_to(const xyz_pos_t &where, const float &de) { move_to(where.x, where.y, where.z, de); }
void retract_filament(const xyz_pos_t &where) {
if (!g26_retracted) { // Only retract if we are not already retracted!
g26_retracted = true;
move_to(where, -1.0f * g26_retraction_multiplier);
}
}
// TODO: Parameterize the Z lift with a define
void retract_lift_move(const xyz_pos_t &s) {
retract_filament(destination);
move_to(current_position.x, current_position.y, current_position.z + 0.5f, 0.0); // Z lift to minimize scraping
move_to(s.x, s.y, s.z + 0.5f, 0.0); // Get to the starting point with no extrusion while lifted
}
void recover_filament(const xyz_pos_t &where) {
if (g26_retracted) { // Only un-retract if we are retracted.
move_to(where, 1.2f * g26_retraction_multiplier);
g26_retracted = false;
}
}
/**
* print_line_from_here_to_there() takes two cartesian coordinates and draws a line from one
* to the other. But there are really three sets of coordinates involved. The first coordinate
* is the present location of the nozzle. We don't necessarily want to print from this location.
* We first need to move the nozzle to the start of line segment where we want to print. Once
* there, we can use the two coordinates supplied to draw the line.
*
* Note: Although we assume the first set of coordinates is the start of the line and the second
* set of coordinates is the end of the line, it does not always work out that way. This function
* optimizes the movement to minimize the travel distance before it can start printing. This saves
* a lot of time and eliminates a lot of nonsensical movement of the nozzle. However, it does
* cause a lot of very little short retracement of th nozzle when it draws the very first line
* segment of a 'circle'. The time this requires is very short and is easily saved by the other
* cases where the optimization comes into play.
*/
void print_line_from_here_to_there(const xyz_pos_t &s, const xyz_pos_t &e) {
// Distances to the start / end of the line
xy_float_t svec = current_position - s, evec = current_position - e;
const float dist_start = HYPOT2(svec.x, svec.y),
dist_end = HYPOT2(evec.x, evec.y),
line_length = HYPOT(e.x - s.x, e.y - s.y);
// If the end point of the line is closer to the nozzle, flip the direction,
// moving from the end to the start. On very small lines the optimization isn't worth it.
if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length))
return print_line_from_here_to_there(e, s);
// Decide whether to retract & lift
if (dist_start > 2.0) retract_lift_move(s);
move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift
const float e_pos_delta = line_length * g26_e_axis_feedrate * g26_extrusion_multiplier;
recover_filament(destination);
move_to(e, e_pos_delta); // Get to the ending point with an appropriate amount of extrusion
}
inline bool look_for_lines_to_connect() {
xyz_pos_t s, e;
s.z = e.z = g26_layer_height;
GRID_LOOP(i, j) {
#if HAS_LCD_MENU
if (user_canceled()) return true;
#endif
if (i < (GRID_MAX_POINTS_X)) { // Can't connect to anything farther to the right than GRID_MAX_POINTS_X.
// Already a half circle at the edge of the bed.
if (circle_flags.marked(i, j) && circle_flags.marked(i + 1, j)) { // Test whether a leftward line can be done
if (!horizontal_mesh_line_flags.marked(i, j)) {
// Two circles need a horizontal line to connect them
s.x = _GET_MESH_X( i ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // right edge
e.x = _GET_MESH_X(i + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // left edge
LIMIT(s.x, X_MIN_POS + 1, X_MAX_POS - 1);
s.y = e.y = constrain(_GET_MESH_Y(j), Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(e.x, X_MIN_POS + 1, X_MAX_POS - 1);
if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
print_line_from_here_to_there(s, e);
horizontal_mesh_line_flags.mark(i, j); // Mark done, even if skipped
}
}
if (j < (GRID_MAX_POINTS_Y)) { // Can't connect to anything further back than GRID_MAX_POINTS_Y.
// Already a half circle at the edge of the bed.
if (circle_flags.marked(i, j) && circle_flags.marked(i, j + 1)) { // Test whether a downward line can be done
if (!vertical_mesh_line_flags.marked(i, j)) {
// Two circles that need a vertical line to connect them
s.y = _GET_MESH_Y( j ) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // top edge
e.y = _GET_MESH_Y(j + 1) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)); // bottom edge
s.x = e.x = constrain(_GET_MESH_X(i), X_MIN_POS + 1, X_MAX_POS - 1);
LIMIT(s.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(e.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y))
print_line_from_here_to_there(s, e);
vertical_mesh_line_flags.mark(i, j); // Mark done, even if skipped
}
}
}
}
}
return false;
}
/**
* Turn on the bed and nozzle heat and
* wait for them to get up to temperature.
*/
inline bool turn_on_heaters() {
SERIAL_ECHOLNPGM("Waiting for heatup.");
#if HAS_HEATED_BED
if (g26_bed_temp > 25) {
#if HAS_SPI_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_BED), 99);
ui.quick_feedback();
#if HAS_LCD_MENU
ui.capture();
#endif
#endif
thermalManager.setTargetBed(g26_bed_temp);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_bed(true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)
) return G26_ERR;
}
#endif // HAS_HEATED_BED
// Start heating the active nozzle
#if HAS_SPI_LCD
ui.set_status_P(GET_TEXT(MSG_G26_HEATING_NOZZLE), 99);
ui.quick_feedback();
#endif
thermalManager.setTargetHotend(g26_hotend_temp, active_extruder);
// Wait for the temperature to stabilize
if (!thermalManager.wait_for_hotend(active_extruder, true
#if G26_CLICK_CAN_CANCEL
, true
#endif
)
) return G26_ERR;
#if HAS_SPI_LCD
ui.reset_status();
ui.quick_feedback();
#endif
return G26_OK;
}
/**
* Prime the nozzle if needed. Return true on error.
*/
inline bool prime_nozzle() {
const feedRate_t fr_slow_e = planner.settings.max_feedrate_mm_s[E_AXIS] / 15.0f;
#if HAS_LCD_MENU && DISABLED(TOUCH_BUTTONS) // ui.button_pressed issue with touchscreen
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
float Total_Prime = 0.0;
#endif
if (g26_prime_flag == -1) { // The user wants to control how much filament gets purged
ui.capture();
ui.set_status_P(GET_TEXT(MSG_G26_MANUAL_PRIME), 99);
ui.chirp();
destination = current_position;
recover_filament(destination); // Make sure G26 doesn't think the filament is retracted().
while (!ui.button_pressed()) {
ui.chirp();
destination.e += 0.25;
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
Total_Prime += 0.25;
if (Total_Prime >= EXTRUDE_MAXLENGTH) {
ui.release();
return G26_ERR;
}
#endif
prepare_internal_move_to_destination(fr_slow_e);
destination = current_position;
planner.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'.
}
ui.wait_for_release();
ui.set_status_P(GET_TEXT(MSG_G26_PRIME_DONE), 99);
ui.quick_feedback();
ui.release();
}
else
#endif
{
#if HAS_SPI_LCD
ui.set_status_P(GET_TEXT(MSG_G26_FIXED_LENGTH), 99);
ui.quick_feedback();
#endif
destination = current_position;
destination.e += g26_prime_length;
prepare_internal_move_to_destination(fr_slow_e);
destination.e -= g26_prime_length;
retract_filament(destination);
}
return G26_OK;
}
/**
* G26: Mesh Validation Pattern generation.
*
* Used to interactively edit the mesh by placing the
* nozzle in a problem area and doing a G29 P4 R command.
*
* Parameters:
*
* B Bed Temperature
* C Continue from the Closest mesh point
* D Disable leveling before starting
* F Filament diameter
* H Hotend Temperature
* K Keep heaters on when completed
* L Layer Height
* O Ooze extrusion length
* P Prime length
* Q Retraction multiplier
* R Repetitions (number of grid points)
* S Nozzle Size (diameter) in mm
* T Tool index to change to, if included
* U Random deviation (50 if no value given)
* X X position
* Y Y position
*/
void GcodeSuite::G26() {
SERIAL_ECHOLNPGM("G26 starting...");
// Don't allow Mesh Validation without homing first,
// or if the parameter parsing did not go OK, abort
if (axis_unhomed_error()) return;
// Change the tool first, if specified
if (parser.seenval('T')) tool_change(parser.value_int());
g26_extrusion_multiplier = EXTRUSION_MULTIPLIER;
g26_retraction_multiplier = G26_RETRACT_MULTIPLIER;
g26_layer_height = MESH_TEST_LAYER_HEIGHT;
g26_prime_length = PRIME_LENGTH;
g26_bed_temp = MESH_TEST_BED_TEMP;
g26_hotend_temp = MESH_TEST_HOTEND_TEMP;
g26_prime_flag = 0;
float g26_nozzle = MESH_TEST_NOZZLE_SIZE,
g26_filament_diameter = DEFAULT_NOMINAL_FILAMENT_DIA,
g26_ooze_amount = parser.linearval('O', OOZE_AMOUNT);
bool g26_continue_with_closest = parser.boolval('C'),
g26_keep_heaters_on = parser.boolval('K');
#if HAS_HEATED_BED
if (parser.seenval('B')) {
g26_bed_temp = parser.value_celsius();
if (g26_bed_temp && !WITHIN(g26_bed_temp, 40, (BED_MAXTEMP - 10))) {
SERIAL_ECHOLNPAIR("?Specified bed temperature not plausible (40-", int(BED_MAXTEMP - 10), "C).");
return;
}
}
#endif
if (parser.seenval('L')) {
g26_layer_height = parser.value_linear_units();
if (!WITHIN(g26_layer_height, 0.0, 2.0)) {
SERIAL_ECHOLNPGM("?Specified layer height not plausible.");
return;
}
}
if (parser.seen('Q')) {
if (parser.has_value()) {
g26_retraction_multiplier = parser.value_float();
if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) {
SERIAL_ECHOLNPGM("?Specified Retraction Multiplier not plausible.");
return;
}
}
else {
SERIAL_ECHOLNPGM("?Retraction Multiplier must be specified.");
return;
}
}
if (parser.seenval('S')) {
g26_nozzle = parser.value_float();
if (!WITHIN(g26_nozzle, 0.1, 2.0)) {
SERIAL_ECHOLNPGM("?Specified nozzle size not plausible.");
return;
}
}
if (parser.seen('P')) {
if (!parser.has_value()) {
#if HAS_LCD_MENU
g26_prime_flag = -1;
#else
SERIAL_ECHOLNPGM("?Prime length must be specified when not using an LCD.");
return;
#endif
}
else {
g26_prime_flag++;
g26_prime_length = parser.value_linear_units();
if (!WITHIN(g26_prime_length, 0.0, 25.0)) {
SERIAL_ECHOLNPGM("?Specified prime length not plausible.");
return;
}
}
}
if (parser.seenval('F')) {
g26_filament_diameter = parser.value_linear_units();
if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) {
SERIAL_ECHOLNPGM("?Specified filament size not plausible.");
return;
}
}
g26_extrusion_multiplier *= sq(1.75) / sq(g26_filament_diameter); // If we aren't using 1.75mm filament, we need to
// scale up or down the length needed to get the
// same volume of filament
g26_extrusion_multiplier *= g26_filament_diameter * sq(g26_nozzle) / sq(0.3); // Scale up by nozzle size
if (parser.seenval('H')) {
g26_hotend_temp = parser.value_celsius();
if (!WITHIN(g26_hotend_temp, 165, (HEATER_0_MAXTEMP - 15))) {
SERIAL_ECHOLNPGM("?Specified nozzle temperature not plausible.");
return;
}
}
if (parser.seen('U')) {
randomSeed(millis());
// This setting will persist for the next G26
random_deviation = parser.has_value() ? parser.value_float() : 50.0;
}
int16_t g26_repeats;
#if HAS_LCD_MENU
g26_repeats = parser.intval('R', GRID_MAX_POINTS + 1);
#else
if (!parser.seen('R')) {
SERIAL_ECHOLNPGM("?(R)epeat must be specified when not using an LCD.");
return;
}
else
g26_repeats = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS + 1;
#endif
if (g26_repeats < 1) {
SERIAL_ECHOLNPGM("?(R)epeat value not plausible; must be at least 1.");
return;
}
g26_xy_pos.set(parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : current_position.x,
parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : current_position.y);
if (!position_is_reachable(g26_xy_pos)) {
SERIAL_ECHOLNPGM("?Specified X,Y coordinate out of bounds.");
return;
}
/**
* Wait until all parameters are verified before altering the state!
*/
set_bed_leveling_enabled(!parser.seen('D'));
if (current_position.z < Z_CLEARANCE_BETWEEN_PROBES)
do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
#if DISABLED(NO_VOLUMETRICS)
bool volumetric_was_enabled = parser.volumetric_enabled;
parser.volumetric_enabled = false;
planner.calculate_volumetric_multipliers();
#endif
if (turn_on_heaters() != G26_OK) goto LEAVE;
current_position.e = 0.0;
sync_plan_position_e();
if (g26_prime_flag && prime_nozzle() != G26_OK) goto LEAVE;
/**
* Bed is preheated
*
* Nozzle is at temperature
*
* Filament is primed!
*
* It's "Show Time" !!!
*/
circle_flags.reset();
horizontal_mesh_line_flags.reset();
vertical_mesh_line_flags.reset();
// Move nozzle to the specified height for the first layer
destination = current_position;
destination.z = g26_layer_height;
move_to(destination, 0.0);
move_to(destination, g26_ooze_amount);
#if HAS_LCD_MENU
ui.capture();
#endif
#if DISABLED(ARC_SUPPORT)
/**
* Pre-generate radius offset values at 30 degree intervals to reduce CPU load.
*/
#define A_INT 30
#define _ANGS (360 / A_INT)
#define A_CNT (_ANGS / 2)
#define _IND(A) ((A + _ANGS * 8) % _ANGS)
#define _COS(A) (trig_table[_IND(A) % A_CNT] * (_IND(A) >= A_CNT ? -1 : 1))
#define _SIN(A) (-_COS((A + A_CNT / 2) % _ANGS))
#if A_CNT & 1
#error "A_CNT must be a positive value. Please change A_INT."
#endif
float trig_table[A_CNT];
LOOP_L_N(i, A_CNT)
trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cos(RADIANS(i * A_INT));
#endif // !ARC_SUPPORT
mesh_index_pair location;
do {
// Find the nearest confluence
location = find_closest_circle_to_print(g26_continue_with_closest ? xy_pos_t(current_position) : g26_xy_pos);
if (location.valid()) {
const xy_pos_t circle = _GET_MESH_POS(location.pos);
// If this mesh location is outside the printable radius, skip it.
if (!position_is_reachable(circle)) continue;
// Determine where to start and end the circle,
// which is always drawn counter-clockwise.
const xy_int8_t st = location;
const bool f = st.y == 0,
r = st.x >= GRID_MAX_POINTS_X - 1,
b = st.y >= GRID_MAX_POINTS_Y - 1;
#if ENABLED(ARC_SUPPORT)
#define ARC_LENGTH(quarters) (INTERSECTION_CIRCLE_RADIUS * M_PI * (quarters) / 2)
#define INTERSECTION_CIRCLE_DIAM ((INTERSECTION_CIRCLE_RADIUS) * 2)
xy_float_t e = { circle.x + INTERSECTION_CIRCLE_RADIUS, circle.y };
xyz_float_t s = e;
// Figure out where to start and end the arc - we always print counterclockwise
float arc_length = ARC_LENGTH(4);
if (st.x == 0) { // left edge
if (!f) { s.x = circle.x; s.y -= INTERSECTION_CIRCLE_RADIUS; }
if (!b) { e.x = circle.x; e.y += INTERSECTION_CIRCLE_RADIUS; }
arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
}
else if (r) { // right edge
if (b) s.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
else s.set(circle.x, circle.y + INTERSECTION_CIRCLE_RADIUS);
if (f) e.set(circle.x - (INTERSECTION_CIRCLE_RADIUS), circle.y);
else e.set(circle.x, circle.y - (INTERSECTION_CIRCLE_RADIUS));
arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
}
else if (f) {
e.x -= INTERSECTION_CIRCLE_DIAM;
arc_length = ARC_LENGTH(2);
}
else if (b) {
s.x -= INTERSECTION_CIRCLE_DIAM;
arc_length = ARC_LENGTH(2);
}
const ab_float_t arc_offset = circle - s;
const xy_float_t dist = current_position - s; // Distance from the start of the actual circle
const float dist_start = HYPOT2(dist.x, dist.y);
const xyze_pos_t endpoint = {
e.x, e.y, g26_layer_height,
current_position.e + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier)
};
if (dist_start > 2.0) {
s.z = g26_layer_height + 0.5f;
retract_lift_move(s);
}
s.z = g26_layer_height;
move_to(s, 0.0); // Get to the starting point with no extrusion / un-Z lift
recover_filament(destination);
const feedRate_t old_feedrate = feedrate_mm_s;
feedrate_mm_s = PLANNER_XY_FEEDRATE() * 0.1f;
plan_arc(endpoint, arc_offset, false); // Draw a counter-clockwise arc
feedrate_mm_s = old_feedrate;
destination = current_position;
#if HAS_LCD_MENU
if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
#endif
#else // !ARC_SUPPORT
int8_t start_ind = -2, end_ind = 9; // Assume a full circle (from 5:00 to 5:00)
if (st.x == 0) { // Left edge? Just right half.
start_ind = f ? 0 : -3; // 03:00 to 12:00 for front-left
end_ind = b ? 0 : 2; // 06:00 to 03:00 for back-left
}
else if (r) { // Right edge? Just left half.
start_ind = b ? 6 : 3; // 12:00 to 09:00 for front-right
end_ind = f ? 5 : 8; // 09:00 to 06:00 for back-right
}
else if (f) { // Front edge? Just back half.
start_ind = 0; // 03:00
end_ind = 5; // 09:00
}
else if (b) { // Back edge? Just front half.
start_ind = 6; // 09:00
end_ind = 11; // 03:00
}
for (int8_t ind = start_ind; ind <= end_ind; ind++) {
#if HAS_LCD_MENU
if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
#endif
xyz_float_t p = { circle.x + _COS(ind ), circle.y + _SIN(ind ), g26_layer_height },
q = { circle.x + _COS(ind + 1), circle.y + _SIN(ind + 1), g26_layer_height };
#if IS_KINEMATIC
// Check to make sure this segment is entirely on the bed, skip if not.
if (!position_is_reachable(p) || !position_is_reachable(q)) continue;
#else
LIMIT(p.x, X_MIN_POS + 1, X_MAX_POS - 1); // Prevent hitting the endstops
LIMIT(p.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
LIMIT(q.x, X_MIN_POS + 1, X_MAX_POS - 1);
LIMIT(q.y, Y_MIN_POS + 1, Y_MAX_POS - 1);
#endif
print_line_from_here_to_there(p, q);
SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
}
#endif // !ARC_SUPPORT
if (look_for_lines_to_connect()) goto LEAVE;
}
SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
} while (--g26_repeats && location.valid());
LEAVE:
ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1);
retract_filament(destination);
destination.z = Z_CLEARANCE_BETWEEN_PROBES;
move_to(destination, 0); // Raise the nozzle
destination = g26_xy_pos; // Move back to the starting XY position
move_to(destination, 0); // Move back to the starting position
#if DISABLED(NO_VOLUMETRICS)
parser.volumetric_enabled = volumetric_was_enabled;
planner.calculate_volumetric_multipliers();
#endif
#if HAS_LCD_MENU
ui.release(); // Give back control of the LCD
#endif
if (!g26_keep_heaters_on) {
#if HAS_HEATED_BED
thermalManager.setTargetBed(0);
#endif
thermalManager.setTargetHotend(active_extruder, 0);
}
}
#endif // G26_MESH_VALIDATION

View File

@@ -0,0 +1,73 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_MESH
#include "../gcode.h"
#include "../../MarlinCore.h" // for IsRunning()
#include "../../module/motion.h"
#include "../../module/probe.h" // for probe.offset
#include "../../feature/bedlevel/bedlevel.h"
/**
* G42: Move X & Y axes to mesh coordinates (I & J)
*/
void GcodeSuite::G42() {
if (MOTION_CONDITIONS) {
const bool hasI = parser.seenval('I');
const int8_t ix = hasI ? parser.value_int() : 0;
const bool hasJ = parser.seenval('J');
const int8_t iy = hasJ ? parser.value_int() : 0;
if ((hasI && !WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) || (hasJ && !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))) {
SERIAL_ECHOLNPGM(STR_ERR_MESH_XY);
return;
}
// Move to current_position, as modified by I, J, P parameters
destination = current_position;
if (hasI) destination.x = _GET_MESH_X(ix);
if (hasJ) destination.y = _GET_MESH_Y(iy);
#if HAS_PROBE_XY_OFFSET
if (parser.boolval('P')) {
if (hasI) destination.x -= probe.offset_xy.x;
if (hasJ) destination.y -= probe.offset_xy.y;
}
#endif
const feedRate_t fval = parser.linearval('F'),
fr_mm_s = MMM_TO_MMS(fval > 0 ? fval : 0.0f);
// SCARA kinematic has "safe" XY raw moves
#if IS_SCARA
prepare_internal_fast_move_to_destination(fr_mm_s);
#else
prepare_internal_move_to_destination(fr_mm_s);
#endif
}
}
#endif // HAS_MESH

View File

@@ -0,0 +1,257 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_LEVELING
#include "../gcode.h"
#include "../../feature/bedlevel/bedlevel.h"
#include "../../module/planner.h"
#include "../../module/probe.h"
#if ENABLED(EEPROM_SETTINGS)
#include "../../module/configuration_store.h"
#endif
#if ENABLED(EXTENSIBLE_UI)
#include "../../lcd/extui/ui_api.h"
#endif
//#define M420_C_USE_MEAN
/**
* M420: Enable/Disable Bed Leveling and/or set the Z fade height.
*
* S[bool] Turns leveling on or off
* Z[height] Sets the Z fade height (0 or none to disable)
* V[bool] Verbose - Print the leveling grid
*
* With AUTO_BED_LEVELING_UBL only:
*
* L[index] Load UBL mesh from index (0 is default)
* T[map] 0:Human-readable 1:CSV 2:"LCD" 4:Compact
*
* With mesh-based leveling only:
*
* C Center mesh on the mean of the lowest and highest
*
* With MARLIN_DEV_MODE:
* S2 Create a simple random mesh and enable
*/
void GcodeSuite::M420() {
const bool seen_S = parser.seen('S'),
to_enable = seen_S ? parser.value_bool() : planner.leveling_active;
#if ENABLED(MARLIN_DEV_MODE)
if (parser.intval('S') == 2) {
const float x_min = probe.min_x(), x_max = probe.max_x(),
y_min = probe.min_y(), y_max = probe.max_y();
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
bilinear_start.set(x_min, y_min);
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_POINTS_X - 1),
(y_max - y_min) / (GRID_MAX_POINTS_Y - 1));
#endif
GRID_LOOP(x, y) {
Z_VALUES(x, y) = 0.001 * random(-200, 200);
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y));
#endif
}
SERIAL_ECHOPGM("Simulated " STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_Y) " mesh ");
SERIAL_ECHOPAIR(" (", x_min);
SERIAL_CHAR(','); SERIAL_ECHO(y_min);
SERIAL_ECHOPAIR(")-(", x_max);
SERIAL_CHAR(','); SERIAL_ECHO(y_max);
SERIAL_ECHOLNPGM(")");
}
#endif
// If disabling leveling do it right away
// (Don't disable for just M420 or M420 V)
if (seen_S && !to_enable) set_bed_leveling_enabled(false);
xyz_pos_t oldpos = current_position;
#if ENABLED(AUTO_BED_LEVELING_UBL)
// L to load a mesh from the EEPROM
if (parser.seen('L')) {
set_bed_leveling_enabled(false);
#if ENABLED(EEPROM_SETTINGS)
const int8_t storage_slot = parser.has_value() ? parser.value_int() : ubl.storage_slot;
const int16_t a = settings.calc_num_meshes();
if (!a) {
SERIAL_ECHOLNPGM("?EEPROM storage not available.");
return;
}
if (!WITHIN(storage_slot, 0, a - 1)) {
SERIAL_ECHOLNPGM("?Invalid storage slot.");
SERIAL_ECHOLNPAIR("?Use 0 to ", a - 1);
return;
}
settings.load_mesh(storage_slot);
ubl.storage_slot = storage_slot;
#else
SERIAL_ECHOLNPGM("?EEPROM storage not available.");
return;
#endif
}
// L or V display the map info
if (parser.seen("LV")) {
ubl.display_map(parser.byteval('T'));
SERIAL_ECHOPGM("Mesh is ");
if (!ubl.mesh_is_valid()) SERIAL_ECHOPGM("in");
SERIAL_ECHOLNPAIR("valid\nStorage slot: ", ubl.storage_slot);
}
#endif // AUTO_BED_LEVELING_UBL
const bool seenV = parser.seen('V');
#if HAS_MESH
if (leveling_is_valid()) {
// Subtract the given value or the mean from all mesh values
if (parser.seen('C')) {
const float cval = parser.value_float();
#if ENABLED(AUTO_BED_LEVELING_UBL)
set_bed_leveling_enabled(false);
ubl.adjust_mesh_to_mean(true, cval);
#else
#if ENABLED(M420_C_USE_MEAN)
// Get the sum and average of all mesh values
float mesh_sum = 0;
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;)
mesh_sum += Z_VALUES(x, y);
const float zmean = mesh_sum / float(GRID_MAX_POINTS);
#else
// Find the low and high mesh values
float lo_val = 100, hi_val = -100;
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;) {
const float z = Z_VALUES(x, y);
NOMORE(lo_val, z);
NOLESS(hi_val, z);
}
// Take the mean of the lowest and highest
const float zmean = (lo_val + hi_val) / 2.0 + cval;
#endif
// If not very close to 0, adjust the mesh
if (!NEAR_ZERO(zmean)) {
set_bed_leveling_enabled(false);
// Subtract the mean from all values
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;) {
Z_VALUES(x, y) -= zmean;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y));
#endif
}
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate();
#endif
}
#endif
}
}
else if (to_enable || seenV) {
SERIAL_ECHO_MSG("Invalid mesh.");
goto EXIT_M420;
}
#endif // HAS_MESH
// V to print the matrix or mesh
if (seenV) {
#if ABL_PLANAR
planner.bed_level_matrix.debug(PSTR("Bed Level Correction Matrix:"));
#else
if (leveling_is_valid()) {
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
print_bilinear_leveling_grid();
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
print_bilinear_leveling_grid_virt();
#endif
#elif ENABLED(MESH_BED_LEVELING)
SERIAL_ECHOLNPGM("Mesh Bed Level data:");
mbl.report_mesh();
#endif
}
#endif
}
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
if (parser.seen('Z')) set_z_fade_height(parser.value_linear_units(), false);
#endif
// Enable leveling if specified, or if previously active
set_bed_leveling_enabled(to_enable);
#if HAS_MESH
EXIT_M420:
#endif
// Error if leveling failed to enable or reenable
if (to_enable && !planner.leveling_active)
SERIAL_ERROR_MSG(STR_ERR_M420_FAILED);
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Bed Leveling ");
serialprintln_onoff(planner.leveling_active);
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Fade Height ");
if (planner.z_fade_height > 0.0)
SERIAL_ECHOLN(planner.z_fade_height);
else
SERIAL_ECHOLNPGM(STR_OFF);
#endif
// Report change in position
if (oldpos != current_position)
report_current_position();
}
#endif // HAS_LEVELING

View File

@@ -0,0 +1,976 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* G29.cpp - Auto Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_ABL_NOT_UBL
#include "../../gcode.h"
#include "../../../feature/bedlevel/bedlevel.h"
#include "../../../module/motion.h"
#include "../../../module/planner.h"
#include "../../../module/stepper.h"
#include "../../../module/probe.h"
#include "../../queue.h"
#if ENABLED(PROBE_TEMP_COMPENSATION)
#include "../../../feature/probe_temp_comp.h"
#include "../../../module/temperature.h"
#endif
#if HAS_DISPLAY
#include "../../../lcd/ultralcd.h"
#endif
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
#include "../../../libs/least_squares_fit.h"
#endif
#if ABL_PLANAR
#include "../../../libs/vector_3.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../../core/debug_out.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#endif
#if HOTENDS > 1
#include "../../../module/tool_change.h"
#endif
#if ABL_GRID
#if ENABLED(PROBE_Y_FIRST)
#define PR_OUTER_VAR meshCount.x
#define PR_OUTER_END abl_grid_points.x
#define PR_INNER_VAR meshCount.y
#define PR_INNER_END abl_grid_points.y
#else
#define PR_OUTER_VAR meshCount.y
#define PR_OUTER_END abl_grid_points.y
#define PR_INNER_VAR meshCount.x
#define PR_INNER_END abl_grid_points.x
#endif
#endif
#if ENABLED(G29_RETRY_AND_RECOVER)
#define G29_RETURN(b) return b;
#else
#define G29_RETURN(b) return;
#endif
/**
* G29: Detailed Z probe, probes the bed at 3 or more points.
* Will fail if the printer has not been homed with G28.
*
* Enhanced G29 Auto Bed Leveling Probe Routine
*
* O Auto-level only if needed
*
* D Dry-Run mode. Just evaluate the bed Topology - Don't apply
* or alter the bed level data. Useful to check the topology
* after a first run of G29.
*
* J Jettison current bed leveling data
*
* V Set the verbose level (0-4). Example: "G29 V3"
*
* Parameters With LINEAR leveling only:
*
* P Set the size of the grid that will be probed (P x P points).
* Example: "G29 P4"
*
* X Set the X size of the grid that will be probed (X x Y points).
* Example: "G29 X7 Y5"
*
* Y Set the Y size of the grid that will be probed (X x Y points).
*
* T Generate a Bed Topology Report. Example: "G29 P5 T" for a detailed report.
* This is useful for manual bed leveling and finding flaws in the bed (to
* assist with part placement).
* Not supported by non-linear delta printer bed leveling.
*
* Parameters With LINEAR and BILINEAR leveling only:
*
* S Set the XY travel speed between probe points (in units/min)
*
* H Set bounds to a centered square H x H units in size
*
* -or-
*
* F Set the Front limit of the probing grid
* B Set the Back limit of the probing grid
* L Set the Left limit of the probing grid
* R Set the Right limit of the probing grid
*
* Parameters with DEBUG_LEVELING_FEATURE only:
*
* C Make a totally fake grid with no actual probing.
* For use in testing when no probing is possible.
*
* Parameters with BILINEAR leveling only:
*
* Z Supply an additional Z probe offset
*
* Extra parameters with PROBE_MANUALLY:
*
* To do manual probing simply repeat G29 until the procedure is complete.
* The first G29 accepts parameters. 'G29 Q' for status, 'G29 A' to abort.
*
* Q Query leveling and G29 state
*
* A Abort current leveling procedure
*
* Extra parameters with BILINEAR only:
*
* W Write a mesh point. (If G29 is idle.)
* I X index for mesh point
* J Y index for mesh point
* X X for mesh point, overrides I
* Y Y for mesh point, overrides J
* Z Z for mesh point. Otherwise, raw current Z.
*
* Without PROBE_MANUALLY:
*
* E By default G29 will engage the Z probe, test the bed, then disengage.
* Include "E" to engage/disengage the Z probe for each sample.
* There's no extra effect if you have a fixed Z probe.
*
*/
G29_TYPE GcodeSuite::G29() {
#if EITHER(DEBUG_LEVELING_FEATURE, PROBE_MANUALLY)
const bool seenQ = parser.seen('Q');
#else
constexpr bool seenQ = false;
#endif
// G29 Q is also available if debugging
#if ENABLED(DEBUG_LEVELING_FEATURE)
const uint8_t old_debug_flags = marlin_debug_flags;
if (seenQ) marlin_debug_flags |= MARLIN_DEBUG_LEVELING;
if (DEBUGGING(LEVELING)) {
DEBUG_POS(">>> G29", current_position);
log_machine_info();
}
marlin_debug_flags = old_debug_flags;
#if DISABLED(PROBE_MANUALLY)
if (seenQ) G29_RETURN(false);
#endif
#endif
#if ENABLED(PROBE_MANUALLY)
const bool seenA = parser.seen('A');
#else
constexpr bool seenA = false;
#endif
const bool no_action = seenA || seenQ,
faux =
#if ENABLED(DEBUG_LEVELING_FEATURE) && DISABLED(PROBE_MANUALLY)
parser.boolval('C')
#else
no_action
#endif
;
// Don't allow auto-leveling without homing first
if (axis_unhomed_error()) G29_RETURN(false);
if (!no_action && planner.leveling_active && parser.boolval('O')) { // Auto-level only if needed
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Auto-level not needed, skip\n<<< G29");
G29_RETURN(false);
}
// Define local vars 'static' for manual probing, 'auto' otherwise
#if ENABLED(PROBE_MANUALLY)
#define ABL_VAR static
#else
#define ABL_VAR
#endif
ABL_VAR int verbose_level;
ABL_VAR xy_pos_t probePos;
ABL_VAR float measured_z;
ABL_VAR bool dryrun, abl_should_enable;
#if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR)
ABL_VAR int abl_probe_index;
#endif
#if HAS_SOFTWARE_ENDSTOPS && ENABLED(PROBE_MANUALLY)
ABL_VAR bool saved_soft_endstops_state = true;
#endif
#if ABL_GRID
#if ENABLED(PROBE_MANUALLY)
ABL_VAR xy_int8_t meshCount;
#endif
ABL_VAR xy_pos_t probe_position_lf, probe_position_rb;
ABL_VAR xy_float_t gridSpacing = { 0, 0 };
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
ABL_VAR bool do_topography_map;
ABL_VAR xy_uint8_t abl_grid_points;
#else // Bilinear
constexpr xy_uint8_t abl_grid_points = { GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y };
#endif
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
ABL_VAR int abl_points;
#elif ENABLED(PROBE_MANUALLY) // Bilinear
int constexpr abl_points = GRID_MAX_POINTS;
#endif
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
ABL_VAR float zoffset;
#elif ENABLED(AUTO_BED_LEVELING_LINEAR)
ABL_VAR int indexIntoAB[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
ABL_VAR float eqnAMatrix[(GRID_MAX_POINTS) * 3], // "A" matrix of the linear system of equations
eqnBVector[GRID_MAX_POINTS], // "B" vector of Z points
mean;
#endif
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
#if ENABLED(PROBE_MANUALLY)
int constexpr abl_points = 3; // used to show total points
#endif
vector_3 points[3];
probe.get_three_points(points);
#endif // AUTO_BED_LEVELING_3POINT
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
struct linear_fit_data lsf_results;
incremental_LSF_reset(&lsf_results);
#endif
/**
* On the initial G29 fetch command parameters.
*/
if (!g29_in_progress) {
#if HOTENDS > 1
if (active_extruder != 0) tool_change(0);
#endif
#if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR)
abl_probe_index = -1;
#endif
abl_should_enable = planner.leveling_active;
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
const bool seen_w = parser.seen('W');
if (seen_w) {
if (!leveling_is_valid()) {
SERIAL_ERROR_MSG("No bilinear grid");
G29_RETURN(false);
}
const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z;
if (!WITHIN(rz, -10, 10)) {
SERIAL_ERROR_MSG("Bad Z value");
G29_RETURN(false);
}
const float rx = RAW_X_POSITION(parser.linearval('X', NAN)),
ry = RAW_Y_POSITION(parser.linearval('Y', NAN));
int8_t i = parser.byteval('I', -1), j = parser.byteval('J', -1);
if (!isnan(rx) && !isnan(ry)) {
// Get nearest i / j from rx / ry
i = (rx - bilinear_start.x + 0.5 * gridSpacing.x) / gridSpacing.x;
j = (ry - bilinear_start.y + 0.5 * gridSpacing.y) / gridSpacing.y;
LIMIT(i, 0, GRID_MAX_POINTS_X - 1);
LIMIT(j, 0, GRID_MAX_POINTS_Y - 1);
}
if (WITHIN(i, 0, GRID_MAX_POINTS_X - 1) && WITHIN(j, 0, GRID_MAX_POINTS_Y)) {
set_bed_leveling_enabled(false);
z_values[i][j] = rz;
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate();
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(i, j, rz);
#endif
set_bed_leveling_enabled(abl_should_enable);
if (abl_should_enable) report_current_position();
}
G29_RETURN(false);
} // parser.seen('W')
#else
constexpr bool seen_w = false;
#endif
// Jettison bed leveling data
if (!seen_w && parser.seen('J')) {
reset_bed_level();
G29_RETURN(false);
}
verbose_level = parser.intval('V');
if (!WITHIN(verbose_level, 0, 4)) {
SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4).");
G29_RETURN(false);
}
dryrun = parser.boolval('D')
#if ENABLED(PROBE_MANUALLY)
|| no_action
#endif
;
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
do_topography_map = verbose_level > 2 || parser.boolval('T');
// X and Y specify points in each direction, overriding the default
// These values may be saved with the completed mesh
abl_grid_points.set(
parser.byteval('X', GRID_MAX_POINTS_X),
parser.byteval('Y', GRID_MAX_POINTS_Y)
);
if (parser.seenval('P')) abl_grid_points.x = abl_grid_points.y = parser.value_int();
if (!WITHIN(abl_grid_points.x, 2, GRID_MAX_POINTS_X)) {
SERIAL_ECHOLNPGM("?Probe points (X) implausible (2-" STRINGIFY(GRID_MAX_POINTS_X) ").");
G29_RETURN(false);
}
if (!WITHIN(abl_grid_points.y, 2, GRID_MAX_POINTS_Y)) {
SERIAL_ECHOLNPGM("?Probe points (Y) implausible (2-" STRINGIFY(GRID_MAX_POINTS_Y) ").");
G29_RETURN(false);
}
abl_points = abl_grid_points.x * abl_grid_points.y;
mean = 0;
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
zoffset = parser.linearval('Z');
#endif
#if ABL_GRID
xy_probe_feedrate_mm_s = MMM_TO_MMS(parser.linearval('S', XY_PROBE_SPEED));
const float x_min = probe.min_x(), x_max = probe.max_x(),
y_min = probe.min_y(), y_max = probe.max_y();
if (parser.seen('H')) {
const int16_t size = (int16_t)parser.value_linear_units();
probe_position_lf.set(
_MAX(X_CENTER - size / 2, x_min),
_MAX(Y_CENTER - size / 2, y_min)
);
probe_position_rb.set(
_MIN(probe_position_lf.x + size, x_max),
_MIN(probe_position_lf.y + size, y_max)
);
}
else {
probe_position_lf.set(
parser.seenval('L') ? RAW_X_POSITION(parser.value_linear_units()) : x_min,
parser.seenval('F') ? RAW_Y_POSITION(parser.value_linear_units()) : y_min
);
probe_position_rb.set(
parser.seenval('R') ? RAW_X_POSITION(parser.value_linear_units()) : x_max,
parser.seenval('B') ? RAW_Y_POSITION(parser.value_linear_units()) : y_max
);
}
if (!probe.good_bounds(probe_position_lf, probe_position_rb)) {
SERIAL_ECHOLNPGM("? (L,R,F,B) out of bounds.");
G29_RETURN(false);
}
// probe at the points of a lattice grid
gridSpacing.set((probe_position_rb.x - probe_position_lf.x) / (abl_grid_points.x - 1),
(probe_position_rb.y - probe_position_lf.y) / (abl_grid_points.y - 1));
#endif // ABL_GRID
if (verbose_level > 0) {
SERIAL_ECHOPGM("G29 Auto Bed Leveling");
if (dryrun) SERIAL_ECHOPGM(" (DRYRUN)");
SERIAL_EOL();
}
planner.synchronize();
// Disable auto bed leveling during G29.
// Be formal so G29 can be done successively without G28.
if (!no_action) set_bed_leveling_enabled(false);
#if HAS_BED_PROBE
// Deploy the probe. Probe will raise if needed.
if (probe.deploy()) {
set_bed_leveling_enabled(abl_should_enable);
G29_RETURN(false);
}
#endif
if (!faux) remember_feedrate_scaling_off();
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
#if ENABLED(PROBE_MANUALLY)
if (!no_action)
#endif
if (gridSpacing != bilinear_grid_spacing || probe_position_lf != bilinear_start) {
// Reset grid to 0.0 or "not probed". (Also disables ABL)
reset_bed_level();
// Initialize a grid with the given dimensions
bilinear_grid_spacing = gridSpacing;
bilinear_start = probe_position_lf;
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false;
}
#endif // AUTO_BED_LEVELING_BILINEAR
#if ENABLED(AUTO_BED_LEVELING_3POINT)
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> 3-point Leveling");
// Probe at 3 arbitrary points
points[0].z = points[1].z = points[2].z = 0;
#endif // AUTO_BED_LEVELING_3POINT
} // !g29_in_progress
#if ENABLED(PROBE_MANUALLY)
// For manual probing, get the next index to probe now.
// On the first probe this will be incremented to 0.
if (!no_action) {
++abl_probe_index;
g29_in_progress = true;
}
// Abort current G29 procedure, go back to idle state
if (seenA && g29_in_progress) {
SERIAL_ECHOLNPGM("Manual G29 aborted");
#if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = saved_soft_endstops_state;
#endif
set_bed_leveling_enabled(abl_should_enable);
g29_in_progress = false;
#if ENABLED(LCD_BED_LEVELING)
ui.wait_for_move = false;
#endif
}
// Query G29 status
if (verbose_level || seenQ) {
SERIAL_ECHOPGM("Manual G29 ");
if (g29_in_progress) {
SERIAL_ECHOPAIR("point ", _MIN(abl_probe_index + 1, abl_points));
SERIAL_ECHOLNPAIR(" of ", abl_points);
}
else
SERIAL_ECHOLNPGM("idle");
}
if (no_action) G29_RETURN(false);
if (abl_probe_index == 0) {
// For the initial G29 S2 save software endstop state
#if HAS_SOFTWARE_ENDSTOPS
saved_soft_endstops_state = soft_endstops_enabled;
#endif
// Move close to the bed before the first point
do_blocking_move_to_z(0);
}
else {
#if EITHER(AUTO_BED_LEVELING_LINEAR, AUTO_BED_LEVELING_3POINT)
const uint16_t index = abl_probe_index - 1;
#endif
// For G29 after adjusting Z.
// Save the previous Z before going to the next point
measured_z = current_position.z;
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
mean += measured_z;
eqnBVector[index] = measured_z;
eqnAMatrix[index + 0 * abl_points] = probePos.x;
eqnAMatrix[index + 1 * abl_points] = probePos.y;
eqnAMatrix[index + 2 * abl_points] = 1;
incremental_LSF(&lsf_results, probePos, measured_z);
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
points[index].z = measured_z;
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
const float newz = measured_z + zoffset;
z_values[meshCount.x][meshCount.y] = newz;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(meshCount, newz);
#endif
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR_P(PSTR("Save X"), meshCount.x, SP_Y_STR, meshCount.y, SP_Z_STR, measured_z + zoffset);
#endif
}
//
// If there's another point to sample, move there with optional lift.
//
#if ABL_GRID
// Skip any unreachable points
while (abl_probe_index < abl_points) {
// Set meshCount.x, meshCount.y based on abl_probe_index, with zig-zag
PR_OUTER_VAR = abl_probe_index / PR_INNER_END;
PR_INNER_VAR = abl_probe_index - (PR_OUTER_VAR * PR_INNER_END);
// Probe in reverse order for every other row/column
bool zig = (PR_OUTER_VAR & 1); // != ((PR_OUTER_END) & 1);
if (zig) PR_INNER_VAR = (PR_INNER_END - 1) - PR_INNER_VAR;
probePos = probe_position_lf + gridSpacing * meshCount.asFloat();
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
indexIntoAB[meshCount.x][meshCount.y] = abl_probe_index;
#endif
// Keep looping till a reachable point is found
if (position_is_reachable(probePos)) break;
++abl_probe_index;
}
// Is there a next point to move to?
if (abl_probe_index < abl_points) {
_manual_goto_xy(probePos); // Can be used here too!
#if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false;
#endif
G29_RETURN(false);
}
else {
// Leveling done! Fall through to G29 finishing code below
SERIAL_ECHOLNPGM("Grid probing done.");
// Re-enable software endstops, if needed
#if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = saved_soft_endstops_state;
#endif
}
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
// Probe at 3 arbitrary points
if (abl_probe_index < abl_points) {
probePos = points[abl_probe_index];
_manual_goto_xy(probePos);
#if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false;
#endif
G29_RETURN(false);
}
else {
SERIAL_ECHOLNPGM("3-point probing done.");
// Re-enable software endstops, if needed
#if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = saved_soft_endstops_state;
#endif
if (!dryrun) {
vector_3 planeNormal = vector_3::cross(points[0] - points[1], points[2] - points[1]).get_normal();
if (planeNormal.z < 0) planeNormal *= -1;
planner.bed_level_matrix = matrix_3x3::create_look_at(planeNormal);
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false;
}
}
#endif // AUTO_BED_LEVELING_3POINT
#else // !PROBE_MANUALLY
{
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
measured_z = 0;
#if ABL_GRID
bool zig = PR_OUTER_END & 1; // Always end at RIGHT and BACK_PROBE_BED_POSITION
measured_z = 0;
xy_int8_t meshCount;
// Outer loop is X with PROBE_Y_FIRST enabled
// Outer loop is Y with PROBE_Y_FIRST disabled
for (PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_END && !isnan(measured_z); PR_OUTER_VAR++) {
int8_t inStart, inStop, inInc;
if (zig) { // Zig away from origin
inStart = 0; // Left or front
inStop = PR_INNER_END; // Right or back
inInc = 1; // Zig right
}
else { // Zag towards origin
inStart = PR_INNER_END - 1; // Right or back
inStop = -1; // Left or front
inInc = -1; // Zag left
}
zig ^= true; // zag
// An index to print current state
uint8_t pt_index = (PR_OUTER_VAR) * (PR_INNER_END) + 1;
// Inner loop is Y with PROBE_Y_FIRST enabled
// Inner loop is X with PROBE_Y_FIRST disabled
for (PR_INNER_VAR = inStart; PR_INNER_VAR != inStop; pt_index++, PR_INNER_VAR += inInc) {
probePos = probe_position_lf + gridSpacing * meshCount.asFloat();
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
indexIntoAB[meshCount.x][meshCount.y] = ++abl_probe_index; // 0...
#endif
#if IS_KINEMATIC
// Avoid probing outside the round or hexagonal area
if (!probe.can_reach(probePos)) continue;
#endif
if (verbose_level) SERIAL_ECHOLNPAIR("Probing mesh point ", int(pt_index), "/", int(GRID_MAX_POINTS), ".");
#if HAS_DISPLAY
ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_MESH), int(pt_index), int(GRID_MAX_POINTS));
#endif
measured_z = faux ? 0.001f * random(-100, 101) : probe.probe_at_point(probePos, raise_after, verbose_level);
if (isnan(measured_z)) {
set_bed_leveling_enabled(abl_should_enable);
break; // Breaks out of both loops
}
#if ENABLED(PROBE_TEMP_COMPENSATION)
temp_comp.compensate_measurement(TSI_BED, thermalManager.degBed(), measured_z);
temp_comp.compensate_measurement(TSI_PROBE, thermalManager.degProbe(), measured_z);
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
temp_comp.compensate_measurement(TSI_EXT, thermalManager.degHotend(), measured_z);
#endif
#endif
#if ENABLED(AUTO_BED_LEVELING_LINEAR)
mean += measured_z;
eqnBVector[abl_probe_index] = measured_z;
eqnAMatrix[abl_probe_index + 0 * abl_points] = probePos.x;
eqnAMatrix[abl_probe_index + 1 * abl_points] = probePos.y;
eqnAMatrix[abl_probe_index + 2 * abl_points] = 1;
incremental_LSF(&lsf_results, probePos, measured_z);
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
z_values[meshCount.x][meshCount.y] = measured_z + zoffset;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(meshCount, z_values[meshCount.x][meshCount.y]);
#endif
#endif
abl_should_enable = false;
idle();
} // inner
} // outer
#elif ENABLED(AUTO_BED_LEVELING_3POINT)
// Probe at 3 arbitrary points
LOOP_L_N(i, 3) {
if (verbose_level) SERIAL_ECHOLNPAIR("Probing point ", int(i), "/3.");
#if HAS_DISPLAY
ui.status_printf_P(0, PSTR(S_FMT " %i/3"), GET_TEXT(MSG_PROBING_MESH), int(i));
#endif
// Retain the last probe position
probePos = points[i];
measured_z = faux ? 0.001 * random(-100, 101) : probe.probe_at_point(probePos, raise_after, verbose_level);
if (isnan(measured_z)) {
set_bed_leveling_enabled(abl_should_enable);
break;
}
points[i].z = measured_z;
}
if (!dryrun && !isnan(measured_z)) {
vector_3 planeNormal = vector_3::cross(points[0] - points[1], points[2] - points[1]).get_normal();
if (planeNormal.z < 0) planeNormal *= -1;
planner.bed_level_matrix = matrix_3x3::create_look_at(planeNormal);
// Can't re-enable (on error) until the new grid is written
abl_should_enable = false;
}
#endif // AUTO_BED_LEVELING_3POINT
#if HAS_DISPLAY
ui.reset_status();
#endif
// Stow the probe. No raise for FIX_MOUNTED_PROBE.
if (probe.stow()) {
set_bed_leveling_enabled(abl_should_enable);
measured_z = NAN;
}
}
#endif // !PROBE_MANUALLY
//
// G29 Finishing Code
//
// Unless this is a dry run, auto bed leveling will
// definitely be enabled after this point.
//
// If code above wants to continue leveling, it should
// return or loop before this point.
//
if (DEBUGGING(LEVELING)) DEBUG_POS("> probing complete", current_position);
#if ENABLED(PROBE_MANUALLY)
g29_in_progress = false;
#if ENABLED(LCD_BED_LEVELING)
ui.wait_for_move = false;
#endif
#endif
// Calculate leveling, print reports, correct the position
if (!isnan(measured_z)) {
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
if (!dryrun) extrapolate_unprobed_bed_level();
print_bilinear_leveling_grid();
refresh_bed_level();
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
print_bilinear_leveling_grid_virt();
#endif
#elif ENABLED(AUTO_BED_LEVELING_LINEAR)
// For LINEAR leveling calculate matrix, print reports, correct the position
/**
* solve the plane equation ax + by + d = z
* A is the matrix with rows [x y 1] for all the probed points
* B is the vector of the Z positions
* the normal vector to the plane is formed by the coefficients of the
* plane equation in the standard form, which is Vx*x+Vy*y+Vz*z+d = 0
* so Vx = -a Vy = -b Vz = 1 (we want the vector facing towards positive Z
*/
struct { float a, b, d; } plane_equation_coefficients;
finish_incremental_LSF(&lsf_results);
plane_equation_coefficients.a = -lsf_results.A; // We should be able to eliminate the '-' on these three lines and down below
plane_equation_coefficients.b = -lsf_results.B; // but that is not yet tested.
plane_equation_coefficients.d = -lsf_results.D;
mean /= abl_points;
if (verbose_level) {
SERIAL_ECHOPAIR_F("Eqn coefficients: a: ", plane_equation_coefficients.a, 8);
SERIAL_ECHOPAIR_F(" b: ", plane_equation_coefficients.b, 8);
SERIAL_ECHOPAIR_F(" d: ", plane_equation_coefficients.d, 8);
if (verbose_level > 2)
SERIAL_ECHOPAIR_F("\nMean of sampled points: ", mean, 8);
SERIAL_EOL();
}
// Create the matrix but don't correct the position yet
if (!dryrun)
planner.bed_level_matrix = matrix_3x3::create_look_at(
vector_3(-plane_equation_coefficients.a, -plane_equation_coefficients.b, 1) // We can eliminate the '-' here and up above
);
// Show the Topography map if enabled
if (do_topography_map) {
float min_diff = 999;
auto print_topo_map = [&](PGM_P const title, const bool get_min) {
serialprintPGM(title);
for (int8_t yy = abl_grid_points.y - 1; yy >= 0; yy--) {
LOOP_L_N(xx, abl_grid_points.x) {
const int ind = indexIntoAB[xx][yy];
xyz_float_t tmp = { eqnAMatrix[ind + 0 * abl_points],
eqnAMatrix[ind + 1 * abl_points], 0 };
apply_rotation_xyz(planner.bed_level_matrix, tmp);
if (get_min) NOMORE(min_diff, eqnBVector[ind] - tmp.z);
const float subval = get_min ? mean : tmp.z + min_diff,
diff = eqnBVector[ind] - subval;
SERIAL_CHAR(' '); if (diff >= 0.0) SERIAL_CHAR('+'); // Include + for column alignment
SERIAL_ECHO_F(diff, 5);
} // xx
SERIAL_EOL();
} // yy
SERIAL_EOL();
};
print_topo_map(PSTR("\nBed Height Topography:\n"
" +--- BACK --+\n"
" | |\n"
" L | (+) | R\n"
" E | | I\n"
" F | (-) N (+) | G\n"
" T | | H\n"
" | (-) | T\n"
" | |\n"
" O-- FRONT --+\n"
" (0,0)\n"), true);
if (verbose_level > 3)
print_topo_map(PSTR("\nCorrected Bed Height vs. Bed Topology:\n"), false);
} //do_topography_map
#endif // AUTO_BED_LEVELING_LINEAR
#if ABL_PLANAR
// For LINEAR and 3POINT leveling correct the current position
if (verbose_level > 0)
planner.bed_level_matrix.debug(PSTR("\n\nBed Level Correction Matrix:"));
if (!dryrun) {
//
// Correct the current XYZ position based on the tilted plane.
//
if (DEBUGGING(LEVELING)) DEBUG_POS("G29 uncorrected XYZ", current_position);
xyze_pos_t converted = current_position;
planner.force_unapply_leveling(converted); // use conversion machinery
// Use the last measured distance to the bed, if possible
if ( NEAR(current_position.x, probePos.x - probe.offset_xy.x)
&& NEAR(current_position.y, probePos.y - probe.offset_xy.y)
) {
const float simple_z = current_position.z - measured_z;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Probed Z", simple_z, " Matrix Z", converted.z, " Discrepancy ", simple_z - converted.z);
converted.z = simple_z;
}
// The rotated XY and corrected Z are now current_position
current_position = converted;
if (DEBUGGING(LEVELING)) DEBUG_POS("G29 corrected XYZ", current_position);
}
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
if (!dryrun) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("G29 uncorrected Z:", current_position.z);
// Unapply the offset because it is going to be immediately applied
// and cause compensation movement in Z
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
const float fade_scaling_factor = planner.fade_scaling_factor_for_z(current_position.z);
#else
constexpr float fade_scaling_factor = 1.0f;
#endif
current_position.z -= fade_scaling_factor * bilinear_z_offset(current_position);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR(" corrected Z:", current_position.z);
}
#endif // ABL_PLANAR
// Auto Bed Leveling is complete! Enable if possible.
planner.leveling_active = dryrun ? abl_should_enable : true;
} // !isnan(measured_z)
// Restore state after probing
if (!faux) restore_feedrate_and_scaling();
// Sync the planner from the current_position
if (planner.leveling_active) sync_plan_position();
#if HAS_BED_PROBE && defined(Z_AFTER_PROBING)
probe.move_z_after_probing();
#endif
#ifdef Z_PROBE_END_SCRIPT
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Z Probe End Script: ", Z_PROBE_END_SCRIPT);
planner.synchronize();
process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT));
#endif
report_current_position();
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G29");
G29_RETURN(isnan(measured_z));
}
#endif // HAS_ABL_NOT_UBL

View File

@@ -0,0 +1,67 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* M421.cpp - Auto Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
#include "../../gcode.h"
#include "../../../feature/bedlevel/bedlevel.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#endif
/**
* M421: Set a single Mesh Bed Leveling Z coordinate
*
* Usage:
* M421 I<xindex> J<yindex> Z<linear>
* M421 I<xindex> J<yindex> Q<offset>
*/
void GcodeSuite::M421() {
int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1);
const bool hasI = ix >= 0,
hasJ = iy >= 0,
hasZ = parser.seen('Z'),
hasQ = !hasZ && parser.seen('Q');
if (!hasI || !hasJ || !(hasZ || hasQ))
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
else {
z_values[ix][iy] = parser.value_linear_units() + (hasQ ? z_values[ix][iy] : 0);
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate();
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ix, iy, z_values[ix][iy]);
#endif
}
}
#endif // AUTO_BED_LEVELING_BILINEAR

View File

@@ -0,0 +1,210 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* G29.cpp - Mesh Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(MESH_BED_LEVELING)
#include "../../../feature/bedlevel/bedlevel.h"
#include "../../gcode.h"
#include "../../queue.h"
#include "../../../libs/buzzer.h"
#include "../../../lcd/ultralcd.h"
#include "../../../module/motion.h"
#include "../../../module/stepper.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#endif
// Save 130 bytes with non-duplication of PSTR
inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM(" not entered."); }
/**
* G29: Mesh-based Z probe, probes a grid and produces a
* mesh to compensate for variable bed height
*
* Parameters With MESH_BED_LEVELING:
*
* S0 Report the current mesh values
* S1 Start probing mesh points
* S2 Probe the next mesh point
* S3 In Jn Zn.nn Manually modify a single point
* S4 Zn.nn Set z offset. Positive away from bed, negative closer to bed.
* S5 Reset and disable mesh
*
*/
void GcodeSuite::G29() {
static int mbl_probe_index = -1;
#if HAS_SOFTWARE_ENDSTOPS
static bool saved_soft_endstops_state;
#endif
MeshLevelingState state = (MeshLevelingState)parser.byteval('S', (int8_t)MeshReport);
if (!WITHIN(state, 0, 5)) {
SERIAL_ECHOLNPGM("S out of range (0-5).");
return;
}
int8_t ix, iy;
switch (state) {
case MeshReport:
SERIAL_ECHOPGM("Mesh Bed Leveling ");
if (leveling_is_valid()) {
serialprintln_onoff(planner.leveling_active);
mbl.report_mesh();
}
else
SERIAL_ECHOLNPGM("has no data.");
break;
case MeshStart:
mbl.reset();
mbl_probe_index = 0;
if (!ui.wait_for_move) {
queue.inject_P(PSTR("G28\nG29 S2"));
return;
}
state = MeshNext;
case MeshNext:
if (mbl_probe_index < 0) {
SERIAL_ECHOLNPGM("Start mesh probing with \"G29 S1\" first.");
return;
}
// For each G29 S2...
if (mbl_probe_index == 0) {
#if HAS_SOFTWARE_ENDSTOPS
// For the initial G29 S2 save software endstop state
saved_soft_endstops_state = soft_endstops_enabled;
#endif
// Move close to the bed before the first point
do_blocking_move_to_z(0);
}
else {
// Save Z for the previous mesh position
mbl.set_zigzag_z(mbl_probe_index - 1, current_position.z);
#if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = saved_soft_endstops_state;
#endif
}
// If there's another point to sample, move there with optional lift.
if (mbl_probe_index < GRID_MAX_POINTS) {
#if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false;
#endif
mbl.zigzag(mbl_probe_index++, ix, iy);
_manual_goto_xy({ mbl.index_to_xpos[ix], mbl.index_to_ypos[iy] });
}
else {
// One last "return to the bed" (as originally coded) at completion
current_position.z = MANUAL_PROBE_HEIGHT;
line_to_current_position();
planner.synchronize();
// After recording the last point, activate home and activate
mbl_probe_index = -1;
SERIAL_ECHOLNPGM("Mesh probing done.");
BUZZ(100, 659);
BUZZ(100, 698);
home_all_axes();
set_bed_leveling_enabled(true);
#if ENABLED(MESH_G28_REST_ORIGIN)
current_position.z = 0;
line_to_current_position(homing_feedrate(Z_AXIS));
planner.synchronize();
#endif
#if ENABLED(LCD_BED_LEVELING)
ui.wait_for_move = false;
#endif
}
break;
case MeshSet:
if (parser.seenval('I')) {
ix = parser.value_int();
if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) {
SERIAL_ECHOPAIR("I out of range (0-", int(GRID_MAX_POINTS_X - 1));
SERIAL_ECHOLNPGM(")");
return;
}
}
else
return echo_not_entered('J');
if (parser.seenval('J')) {
iy = parser.value_int();
if (!WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) {
SERIAL_ECHOPAIR("J out of range (0-", int(GRID_MAX_POINTS_Y - 1));
SERIAL_ECHOLNPGM(")");
return;
}
}
else
return echo_not_entered('J');
if (parser.seenval('Z')) {
mbl.z_values[ix][iy] = parser.value_linear_units();
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ix, iy, mbl.z_values[ix][iy]);
#endif
}
else
return echo_not_entered('Z');
break;
case MeshSetZOffset:
if (parser.seenval('Z'))
mbl.z_offset = parser.value_linear_units();
else
return echo_not_entered('Z');
break;
case MeshReset:
reset_bed_level();
break;
} // switch(state)
if (state == MeshNext) {
SERIAL_ECHOPAIR("MBL G29 point ", _MIN(mbl_probe_index, GRID_MAX_POINTS));
SERIAL_ECHOLNPAIR(" of ", int(GRID_MAX_POINTS));
}
report_current_position();
}
#endif // MESH_BED_LEVELING

View File

@@ -0,0 +1,59 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* M421.cpp - Mesh Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(MESH_BED_LEVELING)
#include "../../gcode.h"
#include "../../../module/motion.h"
#include "../../../feature/bedlevel/mbl/mesh_bed_leveling.h"
/**
* M421: Set a single Mesh Bed Leveling Z coordinate
*
* Usage:
* M421 X<linear> Y<linear> Z<linear>
* M421 X<linear> Y<linear> Q<offset>
* M421 I<xindex> J<yindex> Z<linear>
* M421 I<xindex> J<yindex> Q<offset>
*/
void GcodeSuite::M421() {
const bool hasX = parser.seen('X'), hasI = parser.seen('I');
const int8_t ix = hasI ? parser.value_int() : hasX ? mbl.probe_index_x(RAW_X_POSITION(parser.value_linear_units())) : -1;
const bool hasY = parser.seen('Y'), hasJ = parser.seen('J');
const int8_t iy = hasJ ? parser.value_int() : hasY ? mbl.probe_index_y(RAW_Y_POSITION(parser.value_linear_units())) : -1;
const bool hasZ = parser.seen('Z'), hasQ = !hasZ && parser.seen('Q');
if (int(hasI && hasJ) + int(hasX && hasY) != 1 || !(hasZ || hasQ))
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
else if (ix < 0 || iy < 0)
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
else
mbl.set_z(ix, iy, parser.value_linear_units() + (hasQ ? mbl.z_values[ix][iy] : 0));
}
#endif // MESH_BED_LEVELING

View File

@@ -0,0 +1,36 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* G29.cpp - Unified Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "../../gcode.h"
#include "../../../feature/bedlevel/bedlevel.h"
void GcodeSuite::G29() { ubl.G29(); }
#endif // AUTO_BED_LEVELING_UBL

View File

@@ -0,0 +1,72 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* unified.cpp - Unified Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "../../gcode.h"
#include "../../../feature/bedlevel/bedlevel.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#endif
/**
* M421: Set a single Mesh Bed Leveling Z coordinate
*
* Usage:
* M421 I<xindex> J<yindex> Z<linear>
* M421 I<xindex> J<yindex> Q<offset>
* M421 I<xindex> J<yindex> N
* M421 C Z<linear>
* M421 C Q<offset>
*/
void GcodeSuite::M421() {
xy_int8_t ij = { int8_t(parser.intval('I', -1)), int8_t(parser.intval('J', -1)) };
const bool hasI = ij.x >= 0,
hasJ = ij.y >= 0,
hasC = parser.seen('C'),
hasN = parser.seen('N'),
hasZ = parser.seen('Z'),
hasQ = !hasZ && parser.seen('Q');
if (hasC) ij = ubl.find_closest_mesh_point_of_type(REAL, current_position);
if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN))
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
else if (!WITHIN(ij.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(ij.y, 0, GRID_MAX_POINTS_Y - 1))
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
else {
float &zval = ubl.z_values[ij.x][ij.y];
zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0);
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ij.x, ij.y, zval);
#endif
}
}
#endif // AUTO_BED_LEVELING_UBL

View File

@@ -0,0 +1,520 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#include "../gcode.h"
#include "../../module/stepper.h"
#include "../../module/endstops.h"
#if HOTENDS > 1
#include "../../module/tool_change.h"
#endif
#if HAS_LEVELING
#include "../../feature/bedlevel/bedlevel.h"
#endif
#if ENABLED(SENSORLESS_HOMING)
#include "../../feature/tmc_util.h"
#endif
#include "../../module/probe.h"
#if ENABLED(BLTOUCH)
#include "../../feature/bltouch.h"
#endif
#include "../../lcd/ultralcd.h"
#if HAS_L64XX // set L6470 absolute position registers to counts
#include "../../libs/L64XX/L64XX_Marlin.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
#if ENABLED(QUICK_HOME)
static void quick_home_xy() {
// Pretend the current position is 0,0
current_position.set(0.0, 0.0);
sync_plan_position();
const int x_axis_home_dir = x_home_dir(active_extruder);
const float mlx = max_length(X_AXIS),
mly = max_length(Y_AXIS),
mlratio = mlx > mly ? mly / mlx : mlx / mly,
fr_mm_s = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0);
#if ENABLED(SENSORLESS_HOMING)
sensorless_t stealth_states {
tmc_enable_stallguard(stepperX)
, tmc_enable_stallguard(stepperY)
, false
, false
#if AXIS_HAS_STALLGUARD(X2)
|| tmc_enable_stallguard(stepperX2)
#endif
, false
#if AXIS_HAS_STALLGUARD(Y2)
|| tmc_enable_stallguard(stepperY2)
#endif
};
#endif
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s);
endstops.validate_homing_move();
current_position.set(0.0, 0.0);
#if ENABLED(SENSORLESS_HOMING)
tmc_disable_stallguard(stepperX, stealth_states.x);
tmc_disable_stallguard(stepperY, stealth_states.y);
#if AXIS_HAS_STALLGUARD(X2)
tmc_disable_stallguard(stepperX2, stealth_states.x2);
#endif
#if AXIS_HAS_STALLGUARD(Y2)
tmc_disable_stallguard(stepperY2, stealth_states.y2);
#endif
#endif
}
#endif // QUICK_HOME
#if ENABLED(Z_SAFE_HOMING)
inline void home_z_safely() {
// Disallow Z homing if X or Y are unknown
if (!TEST(axis_known_position, X_AXIS) || !TEST(axis_known_position, Y_AXIS)) {
LCD_MESSAGEPGM(MSG_ERR_Z_HOMING);
SERIAL_ECHO_MSG(STR_ERR_Z_HOMING_SER);
return;
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("home_z_safely >>>");
sync_plan_position();
/**
* Move the Z probe (or just the nozzle) to the safe homing point
* (Z is already at the right height)
*/
destination.set(safe_homing_xy, current_position.z);
#if HOMING_Z_WITH_PROBE
destination -= probe.offset_xy;
#endif
if (position_is_reachable(destination)) {
if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", destination);
// This causes the carriage on Dual X to unpark
#if ENABLED(DUAL_X_CARRIAGE)
active_extruder_parked = false;
#endif
#if ENABLED(SENSORLESS_HOMING)
safe_delay(500); // Short delay needed to settle
#endif
do_blocking_move_to_xy(destination);
homeaxis(Z_AXIS);
}
else {
LCD_MESSAGEPGM(MSG_ZPROBE_OUT);
SERIAL_ECHO_MSG(STR_ZPROBE_OUT_SER);
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< home_z_safely");
}
#endif // Z_SAFE_HOMING
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing_t begin_slow_homing() {
slow_homing_t slow_homing{0};
slow_homing.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS],
planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100;
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100;
#if HAS_CLASSIC_JERK
slow_homing.jerk_xy = planner.max_jerk;
planner.max_jerk.set(0, 0);
#endif
planner.reset_acceleration_rates();
return slow_homing;
}
void end_slow_homing(const slow_homing_t &slow_homing) {
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = slow_homing.acceleration.x;
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = slow_homing.acceleration.y;
#if HAS_CLASSIC_JERK
planner.max_jerk = slow_homing.jerk_xy;
#endif
planner.reset_acceleration_rates();
}
#endif // IMPROVE_HOMING_RELIABILITY
/**
* G28: Home all axes according to settings
*
* Parameters
*
* None Home to all axes with no parameters.
* With QUICK_HOME enabled XY will home together, then Z.
*
* O Home only if position is unknown
*
* Rn Raise by n mm/inches before homing
*
* Cartesian/SCARA parameters
*
* X Home to the X endstop
* Y Home to the Y endstop
* Z Home to the Z endstop
*
*/
void GcodeSuite::G28() {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPGM(">>> G28");
log_machine_info();
}
#if ENABLED(DUAL_X_CARRIAGE)
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
DualXMode IDEX_saved_mode = dual_x_carriage_mode;
#endif
#if ENABLED(MARLIN_DEV_MODE)
if (parser.seen('S')) {
LOOP_XYZ(a) set_axis_is_at_home((AxisEnum)a);
sync_plan_position();
SERIAL_ECHOLNPGM("Simulated Homing");
report_current_position();
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
return;
}
#endif
// Home (O)nly if position is unknown
if (!homing_needed() && parser.boolval('O')) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> homing not needed, skip\n<<< G28");
return;
}
// Wait for planner moves to finish!
planner.synchronize();
// Disable the leveling matrix before homing
#if HAS_LEVELING
// Cancel the active G29 session
#if ENABLED(PROBE_MANUALLY)
g29_in_progress = false;
#endif
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
const bool leveling_was_active = planner.leveling_active;
#endif
set_bed_leveling_enabled(false);
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
workspace_plane = PLANE_XY;
#endif
#define HAS_CURRENT_HOME(N) (defined(N##_CURRENT_HOME) && N##_CURRENT_HOME != N##_CURRENT)
#define HAS_HOMING_CURRENT (HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2))
#if HAS_HOMING_CURRENT
auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b){
serialprintPGM(s); DEBUG_ECHOLNPAIR(" current: ", a, " -> ", b);
};
#if HAS_CURRENT_HOME(X)
const int16_t tmc_save_current_X = stepperX.getMilliamps();
stepperX.rms_current(X_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("X"), tmc_save_current_X, X_CURRENT_HOME);
#endif
#if HAS_CURRENT_HOME(X2)
const int16_t tmc_save_current_X2 = stepperX2.getMilliamps();
stepperX2.rms_current(X2_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("X2"), tmc_save_current_X2, X2_CURRENT_HOME);
#endif
#if HAS_CURRENT_HOME(Y)
const int16_t tmc_save_current_Y = stepperY.getMilliamps();
stepperY.rms_current(Y_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("Y"), tmc_save_current_Y, Y_CURRENT_HOME);
#endif
#if HAS_CURRENT_HOME(Y2)
const int16_t tmc_save_current_Y2 = stepperY2.getMilliamps();
stepperY2.rms_current(Y2_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("Y2"), tmc_save_current_Y2, Y2_CURRENT_HOME);
#endif
#endif
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing_t slow_homing = begin_slow_homing();
#endif
// Always home with tool 0 active
#if HOTENDS > 1
#if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE)
const uint8_t old_tool_index = active_extruder;
#endif
tool_change(0, true);
#endif
#if HAS_DUPLICATION_MODE
extruder_duplication_enabled = false;
#endif
remember_feedrate_scaling_off();
endstops.enable(true); // Enable endstops for next homing move
#if ENABLED(DELTA)
constexpr bool doZ = true; // for NANODLP_Z_SYNC if your DLP is on a DELTA
home_delta();
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
#endif
#else // NOT DELTA
const bool homeX = parser.seen('X'), homeY = parser.seen('Y'), homeZ = parser.seen('Z'),
home_all = homeX == homeY && homeX == homeZ, // All or None
doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ;
destination = current_position;
#if Z_HOME_DIR > 0 // If homing away from BED do Z first
if (doZ) homeaxis(Z_AXIS);
#endif
const float z_homing_height =
(DISABLED(UNKNOWN_Z_NO_RAISE) || TEST(axis_known_position, Z_AXIS))
? (parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT)
: 0;
if (z_homing_height && (doX || doY)) {
// Raise Z before homing any other axes and z is not already high enough (never lower z)
destination.z = z_homing_height + (TEST(axis_known_position, Z_AXIS) ? 0.0f : current_position.z);
if (destination.z > current_position.z) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination.z);
do_blocking_move_to_z(destination.z);
}
}
#if ENABLED(QUICK_HOME)
if (doX && doY) quick_home_xy();
#endif
// Home Y (before X)
if (ENABLED(HOME_Y_BEFORE_X) && (doY || (ENABLED(CODEPENDENT_XY_HOMING) && doX)))
homeaxis(Y_AXIS);
// Home X
if (doX || (doY && ENABLED(CODEPENDENT_XY_HOMING) && DISABLED(HOME_Y_BEFORE_X))) {
#if ENABLED(DUAL_X_CARRIAGE)
// Always home the 2nd (right) extruder first
active_extruder = 1;
homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x_pos = current_position.x;
// Home the 1st (left) extruder
active_extruder = 0;
homeaxis(X_AXIS);
// Consider the active extruder to be parked
raised_parked_position = current_position;
delayed_move_time = 0;
active_extruder_parked = true;
#else
homeaxis(X_AXIS);
#endif
}
// Home Y (after X)
if (DISABLED(HOME_Y_BEFORE_X) && doY)
homeaxis(Y_AXIS);
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
#endif
// Home Z last if homing towards the bed
#if Z_HOME_DIR < 0
if (doZ) {
#if ENABLED(BLTOUCH)
bltouch.init();
#endif
#if ENABLED(Z_SAFE_HOMING)
home_z_safely();
#else
homeaxis(Z_AXIS);
#endif
#if HOMING_Z_WITH_PROBE && defined(Z_AFTER_PROBING)
#if Z_AFTER_HOMING > Z_AFTER_PROBING
do_blocking_move_to_z(Z_AFTER_HOMING);
#else
probe.move_z_after_probing();
#endif
#elif defined(Z_AFTER_HOMING)
do_blocking_move_to_z(Z_AFTER_HOMING);
#endif
} // doZ
#endif // Z_HOME_DIR < 0
sync_plan_position();
#endif // !DELTA (G28)
/**
* Preserve DXC mode across a G28 for IDEX printers in DXC_DUPLICATION_MODE.
* This is important because it lets a user use the LCD Panel to set an IDEX Duplication mode, and
* then print a standard GCode file that contains a single print that does a G28 and has no other
* IDEX specific commands in it.
*/
#if ENABLED(DUAL_X_CARRIAGE)
if (dxc_is_duplicating()) {
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing = begin_slow_homing();
#endif
// Always home the 2nd (right) extruder first
active_extruder = 1;
homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x_pos = current_position.x;
// Home the 1st (left) extruder
active_extruder = 0;
homeaxis(X_AXIS);
// Consider the active extruder to be parked
raised_parked_position = current_position;
delayed_move_time = 0;
active_extruder_parked = true;
extruder_duplication_enabled = IDEX_saved_duplication_state;
dual_x_carriage_mode = IDEX_saved_mode;
stepper.set_directions();
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
#endif
}
#endif // DUAL_X_CARRIAGE
endstops.not_homing();
// Clear endstop state for polled stallGuard endstops
#if ENABLED(SPI_ENDSTOPS)
endstops.clear_endstop_state();
#endif
#if BOTH(DELTA, DELTA_HOME_TO_SAFE_ZONE)
// move to a height where we can use the full xy-area
do_blocking_move_to_z(delta_clip_start_height);
#endif
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
set_bed_leveling_enabled(leveling_was_active);
#endif
restore_feedrate_and_scaling();
// Restore the active tool after homing
#if HOTENDS > 1 && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
tool_change(old_tool_index, NONE(PARKING_EXTRUDER, DUAL_X_CARRIAGE)); // Do move if one of these
#endif
#if HAS_HOMING_CURRENT
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore driver current...");
#if HAS_CURRENT_HOME(X)
stepperX.rms_current(tmc_save_current_X);
#endif
#if HAS_CURRENT_HOME(X2)
stepperX2.rms_current(tmc_save_current_X2);
#endif
#if HAS_CURRENT_HOME(Y)
stepperY.rms_current(tmc_save_current_Y);
#endif
#if HAS_CURRENT_HOME(Y2)
stepperY2.rms_current(tmc_save_current_Y2);
#endif
#endif
ui.refresh();
report_current_position();
if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS)))
SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
#if HAS_L64XX
// Set L6470 absolute position registers to counts
// constexpr *might* move this to PROGMEM.
// If not, this will need a PROGMEM directive and an accessor.
static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = {
X_AXIS, Y_AXIS, Z_AXIS,
X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS,
E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
};
for (uint8_t j = 1; j <= L64XX::chain[0]; j++) {
const uint8_t cv = L64XX::chain[j];
L64xxManager.set_param((L64XX_axis_t)cv, L6470_ABS_POS, stepper.position(L64XX_axis_xref[cv]));
}
#endif
}

View File

@@ -0,0 +1,665 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(DELTA_AUTO_CALIBRATION)
#include "../gcode.h"
#include "../../module/delta.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#include "../../module/endstops.h"
#include "../../lcd/ultralcd.h"
#if HAS_BED_PROBE
#include "../../module/probe.h"
#endif
#if HOTENDS > 1
#include "../../module/tool_change.h"
#endif
#if HAS_LEVELING
#include "../../feature/bedlevel/bedlevel.h"
#endif
constexpr uint8_t _7P_STEP = 1, // 7-point step - to change number of calibration points
_4P_STEP = _7P_STEP * 2, // 4-point step
NPP = _7P_STEP * 6; // number of calibration points on the radius
enum CalEnum : char { // the 7 main calibration points - add definitions if needed
CEN = 0,
__A = 1,
_AB = __A + _7P_STEP,
__B = _AB + _7P_STEP,
_BC = __B + _7P_STEP,
__C = _BC + _7P_STEP,
_CA = __C + _7P_STEP,
};
#define LOOP_CAL_PT(VAR, S, N) for (uint8_t VAR=S; VAR<=NPP; VAR+=N)
#define F_LOOP_CAL_PT(VAR, S, N) for (float VAR=S; VAR<NPP+0.9999; VAR+=N)
#define I_LOOP_CAL_PT(VAR, S, N) for (float VAR=S; VAR>CEN+0.9999; VAR-=N)
#define LOOP_CAL_ALL(VAR) LOOP_CAL_PT(VAR, CEN, 1)
#define LOOP_CAL_RAD(VAR) LOOP_CAL_PT(VAR, __A, _7P_STEP)
#define LOOP_CAL_ACT(VAR, _4P, _OP) LOOP_CAL_PT(VAR, _OP ? _AB : __A, _4P ? _4P_STEP : _7P_STEP)
#if HOTENDS > 1
const uint8_t old_tool_index = active_extruder;
#define AC_CLEANUP() ac_cleanup(old_tool_index)
#else
#define AC_CLEANUP() ac_cleanup()
#endif
float lcd_probe_pt(const xy_pos_t &xy);
void ac_home() {
endstops.enable(true);
home_delta();
endstops.not_homing();
}
void ac_setup(const bool reset_bed) {
#if HOTENDS > 1
tool_change(0, true);
#endif
planner.synchronize();
remember_feedrate_scaling_off();
#if HAS_LEVELING
if (reset_bed) reset_bed_level(); // After full calibration bed-level data is no longer valid
#endif
}
void ac_cleanup(
#if HOTENDS > 1
const uint8_t old_tool_index
#endif
) {
#if ENABLED(DELTA_HOME_TO_SAFE_ZONE)
do_blocking_move_to_z(delta_clip_start_height);
#endif
#if HAS_BED_PROBE
probe.stow();
#endif
restore_feedrate_and_scaling();
#if HOTENDS > 1
tool_change(old_tool_index, true);
#endif
}
void print_signed_float(PGM_P const prefix, const float &f) {
SERIAL_ECHOPGM(" ");
serialprintPGM(prefix);
SERIAL_CHAR(':');
if (f >= 0) SERIAL_CHAR('+');
SERIAL_ECHO_F(f, 2);
}
/**
* - Print the delta settings
*/
static void print_calibration_settings(const bool end_stops, const bool tower_angles) {
SERIAL_ECHOPAIR(".Height:", delta_height);
if (end_stops) {
print_signed_float(PSTR("Ex"), delta_endstop_adj.a);
print_signed_float(PSTR("Ey"), delta_endstop_adj.b);
print_signed_float(PSTR("Ez"), delta_endstop_adj.c);
}
if (end_stops && tower_angles) {
SERIAL_ECHOPAIR(" Radius:", delta_radius);
SERIAL_EOL();
SERIAL_CHAR('.');
SERIAL_ECHO_SP(13);
}
if (tower_angles) {
print_signed_float(PSTR("Tx"), delta_tower_angle_trim.a);
print_signed_float(PSTR("Ty"), delta_tower_angle_trim.b);
print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c);
}
if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR
SERIAL_ECHOPAIR(" Radius:", delta_radius);
}
SERIAL_EOL();
}
/**
* - Print the probe results
*/
static void print_calibration_results(const float z_pt[NPP + 1], const bool tower_points, const bool opposite_points) {
SERIAL_ECHOPGM(". ");
print_signed_float(PSTR("c"), z_pt[CEN]);
if (tower_points) {
print_signed_float(PSTR(" x"), z_pt[__A]);
print_signed_float(PSTR(" y"), z_pt[__B]);
print_signed_float(PSTR(" z"), z_pt[__C]);
}
if (tower_points && opposite_points) {
SERIAL_EOL();
SERIAL_CHAR('.');
SERIAL_ECHO_SP(13);
}
if (opposite_points) {
print_signed_float(PSTR("yz"), z_pt[_BC]);
print_signed_float(PSTR("zx"), z_pt[_CA]);
print_signed_float(PSTR("xy"), z_pt[_AB]);
}
SERIAL_EOL();
}
/**
* - Calculate the standard deviation from the zero plane
*/
static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool _1p_cal, const bool _4p_cal, const bool _4p_opp) {
if (!_0p_cal) {
float S2 = sq(z_pt[CEN]);
int16_t N = 1;
if (!_1p_cal) { // std dev from zero plane
LOOP_CAL_ACT(rad, _4p_cal, _4p_opp) {
S2 += sq(z_pt[rad]);
N++;
}
return LROUND(SQRT(S2 / N) * 1000.0f) / 1000.0f + 0.00001f;
}
}
return 0.00001f;
}
/**
* - Probe a point
*/
static float calibration_probe(const xy_pos_t &xy, const bool stow) {
#if HAS_BED_PROBE
return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, false);
#else
UNUSED(stow);
return lcd_probe_pt(xy);
#endif
}
/**
* - Probe a grid
*/
static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each) {
const bool _0p_calibration = probe_points == 0,
_1p_calibration = probe_points == 1 || probe_points == -1,
_4p_calibration = probe_points == 2,
_4p_opposite_points = _4p_calibration && !towers_set,
_7p_calibration = probe_points >= 3,
_7p_no_intermediates = probe_points == 3,
_7p_1_intermediates = probe_points == 4,
_7p_2_intermediates = probe_points == 5,
_7p_4_intermediates = probe_points == 6,
_7p_6_intermediates = probe_points == 7,
_7p_8_intermediates = probe_points == 8,
_7p_11_intermediates = probe_points == 9,
_7p_14_intermediates = probe_points == 10,
_7p_intermed_points = probe_points >= 4,
_7p_6_center = probe_points >= 5 && probe_points <= 7,
_7p_9_center = probe_points >= 8;
LOOP_CAL_ALL(rad) z_pt[rad] = 0.0f;
if (!_0p_calibration) {
const float dcr = delta_calibration_radius();
if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center
const xy_pos_t center{0};
z_pt[CEN] += calibration_probe(center, stow_after_each);
if (isnan(z_pt[CEN])) return false;
}
if (_7p_calibration) { // probe extra center points
const float start = _7p_9_center ? float(_CA) + _7P_STEP / 3.0f : _7p_6_center ? float(_CA) : float(__C),
steps = _7p_9_center ? _4P_STEP / 3.0f : _7p_6_center ? _7P_STEP : _4P_STEP;
I_LOOP_CAL_PT(rad, start, steps) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = dcr * 0.1;
const xy_pos_t vec = { cos(a), sin(a) };
z_pt[CEN] += calibration_probe(vec * r, stow_after_each);
if (isnan(z_pt[CEN])) return false;
}
z_pt[CEN] /= float(_7p_2_intermediates ? 7 : probe_points);
}
if (!_1p_calibration) { // probe the radius
const CalEnum start = _4p_opposite_points ? _AB : __A;
const float steps = _7p_14_intermediates ? _7P_STEP / 15.0f : // 15r * 6 + 10c = 100
_7p_11_intermediates ? _7P_STEP / 12.0f : // 12r * 6 + 9c = 81
_7p_8_intermediates ? _7P_STEP / 9.0f : // 9r * 6 + 10c = 64
_7p_6_intermediates ? _7P_STEP / 7.0f : // 7r * 6 + 7c = 49
_7p_4_intermediates ? _7P_STEP / 5.0f : // 5r * 6 + 6c = 36
_7p_2_intermediates ? _7P_STEP / 3.0f : // 3r * 6 + 7c = 25
_7p_1_intermediates ? _7P_STEP / 2.0f : // 2r * 6 + 4c = 16
_7p_no_intermediates ? _7P_STEP : // 1r * 6 + 3c = 9
_4P_STEP; // .5r * 6 + 1c = 4
bool zig_zag = true;
F_LOOP_CAL_PT(rad, start, _7p_9_center ? steps * 3 : steps) {
const int8_t offset = _7p_9_center ? 2 : 0;
for (int8_t circle = 0; circle <= offset; circle++) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = dcr * (1 - 0.1 * (zig_zag ? offset - circle : circle)),
interpol = FMOD(rad, 1);
const xy_pos_t vec = { cos(a), sin(a) };
const float z_temp = calibration_probe(vec * r, stow_after_each);
if (isnan(z_temp)) return false;
// split probe point to neighbouring calibration points
z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90)));
z_pt[uint8_t(LROUND(rad - interpol)) % NPP + 1] += z_temp * sq(sin(RADIANS(interpol * 90)));
}
zig_zag = !zig_zag;
}
if (_7p_intermed_points)
LOOP_CAL_RAD(rad)
z_pt[rad] /= _7P_STEP / steps;
do_blocking_move_to_xy(0.0f, 0.0f);
}
}
return true;
}
/**
* kinematics routines and auto tune matrix scaling parameters:
* see https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for
* - formulae for approximative forward kinematics in the end-stop displacement matrix
* - definition of the matrix scaling parameters
*/
static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1]) {
xyz_pos_t pos{0};
const float dcr = delta_calibration_radius();
LOOP_CAL_ALL(rad) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = (rad == CEN ? 0.0f : dcr);
pos.set(cos(a) * r, sin(a) * r, z_pt[rad]);
inverse_kinematics(pos);
mm_at_pt_axis[rad] = delta;
}
}
static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) {
const float r_quot = delta_calibration_radius() / delta_radius;
#define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A)
#define Z00(I, A) ZPP( 0, I, A)
#define Zp1(I, A) ZPP(+1, I, A)
#define Zm1(I, A) ZPP(-1, I, A)
#define Zp2(I, A) ZPP(+2, I, A)
#define Zm2(I, A) ZPP(-2, I, A)
z_pt[CEN] = Z00(CEN, a) + Z00(CEN, b) + Z00(CEN, c);
z_pt[__A] = Zp2(__A, a) + Zm1(__A, b) + Zm1(__A, c);
z_pt[__B] = Zm1(__B, a) + Zp2(__B, b) + Zm1(__B, c);
z_pt[__C] = Zm1(__C, a) + Zm1(__C, b) + Zp2(__C, c);
z_pt[_BC] = Zm2(_BC, a) + Zp1(_BC, b) + Zp1(_BC, c);
z_pt[_CA] = Zp1(_CA, a) + Zm2(_CA, b) + Zp1(_CA, c);
z_pt[_AB] = Zp1(_AB, a) + Zp1(_AB, b) + Zm2(_AB, c);
}
static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t delta_e, const float delta_r, abc_float_t delta_t) {
const float z_center = z_pt[CEN];
abc_float_t diff_mm_at_pt_axis[NPP + 1], new_mm_at_pt_axis[NPP + 1];
reverse_kinematics_probe_points(z_pt, diff_mm_at_pt_axis);
delta_radius += delta_r;
delta_tower_angle_trim += delta_t;
recalc_delta_settings();
reverse_kinematics_probe_points(z_pt, new_mm_at_pt_axis);
LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad] -= new_mm_at_pt_axis[rad] + delta_e;
forward_kinematics_probe_points(diff_mm_at_pt_axis, z_pt);
LOOP_CAL_RAD(rad) z_pt[rad] -= z_pt[CEN] - z_center;
z_pt[CEN] = z_center;
delta_radius -= delta_r;
delta_tower_angle_trim -= delta_t;
recalc_delta_settings();
}
static float auto_tune_h() {
const float r_quot = delta_calibration_radius() / delta_radius;
return RECIPROCAL(r_quot / (2.0f / 3.0f)); // (2/3)/CR
}
static float auto_tune_r() {
constexpr float diff = 0.01f, delta_r = diff;
float r_fac = 0.0f, z_pt[NPP + 1] = { 0.0f };
abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
r_fac = -(z_pt[__A] + z_pt[__B] + z_pt[__C] + z_pt[_BC] + z_pt[_CA] + z_pt[_AB]) / 6.0f;
r_fac = diff / r_fac / 3.0f; // 1/(3*delta_Z)
return r_fac;
}
static float auto_tune_a() {
constexpr float diff = 0.01f, delta_r = 0.0f;
float a_fac = 0.0f, z_pt[NPP + 1] = { 0.0f };
abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
delta_t.reset();
LOOP_XYZ(axis) {
delta_t[axis] = diff;
calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
delta_t[axis] = 0;
a_fac += z_pt[uint8_t((axis * _4P_STEP) - _7P_STEP + NPP) % NPP + 1] / 6.0f;
a_fac -= z_pt[uint8_t((axis * _4P_STEP) + 1 + _7P_STEP)] / 6.0f;
}
a_fac = diff / a_fac / 3.0f; // 1/(3*delta_Z)
return a_fac;
}
/**
* G33 - Delta '1-4-7-point' Auto-Calibration
* Calibrate height, z_offset, endstops, delta radius, and tower angles.
*
* Parameters:
*
* Pn Number of probe points:
* P0 Normalizes calibration.
* P1 Calibrates height only with center probe.
* P2 Probe center and towers. Calibrate height, endstops and delta radius.
* P3 Probe all positions: center, towers and opposite towers. Calibrate all.
* P4-P10 Probe all positions at different intermediate locations and average them.
*
* T Don't calibrate tower angle corrections
*
* Cn.nn Calibration precision; when omitted calibrates to maximum precision
*
* Fn Force to run at least n iterations and take the best result
*
* Vn Verbose level:
* V0 Dry-run mode. Report settings and probe results. No calibration.
* V1 Report start and end settings only
* V2 Report settings at each iteration
* V3 Report settings and probe results
*
* E Engage the probe for each point
*/
void GcodeSuite::G33() {
const int8_t probe_points = parser.intval('P', DELTA_CALIBRATION_DEFAULT_POINTS);
if (!WITHIN(probe_points, 0, 10)) {
SERIAL_ECHOLNPGM("?(P)oints implausible (0-10).");
return;
}
const bool towers_set = !parser.seen('T');
const float calibration_precision = parser.floatval('C', 0.0f);
if (calibration_precision < 0) {
SERIAL_ECHOLNPGM("?(C)alibration precision implausible (>=0).");
return;
}
const int8_t force_iterations = parser.intval('F', 0);
if (!WITHIN(force_iterations, 0, 30)) {
SERIAL_ECHOLNPGM("?(F)orce iteration implausible (0-30).");
return;
}
const int8_t verbose_level = parser.byteval('V', 1);
if (!WITHIN(verbose_level, 0, 3)) {
SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-3).");
return;
}
const bool stow_after_each = parser.seen('E');
const bool _0p_calibration = probe_points == 0,
_1p_calibration = probe_points == 1 || probe_points == -1,
_4p_calibration = probe_points == 2,
_4p_opposite_points = _4p_calibration && !towers_set,
_7p_9_center = probe_points >= 8,
_tower_results = (_4p_calibration && towers_set) || probe_points >= 3,
_opposite_results = (_4p_calibration && !towers_set) || probe_points >= 3,
_endstop_results = probe_points != 1 && probe_points != -1 && probe_points != 0,
_angle_results = probe_points >= 3 && towers_set;
int8_t iterations = 0;
float test_precision,
zero_std_dev = (verbose_level ? 999.0f : 0.0f), // 0.0 in dry-run mode : forced end
zero_std_dev_min = zero_std_dev,
zero_std_dev_old = zero_std_dev,
h_factor, r_factor, a_factor,
r_old = delta_radius,
h_old = delta_height;
abc_pos_t e_old = delta_endstop_adj, a_old = delta_tower_angle_trim;
SERIAL_ECHOLNPGM("G33 Auto Calibrate");
const float dcr = delta_calibration_radius();
if (!_1p_calibration && !_0p_calibration) { // test if the outer radius is reachable
LOOP_CAL_RAD(axis) {
const float a = RADIANS(210 + (360 / NPP) * (axis - 1));
if (!position_is_reachable(cos(a) * dcr, sin(a) * dcr)) {
SERIAL_ECHOLNPGM("?Bed calibration radius implausible.");
return;
}
}
}
// Report settings
PGM_P checkingac = PSTR("Checking... AC");
serialprintPGM(checkingac);
if (verbose_level == 0) SERIAL_ECHOPGM(" (DRY-RUN)");
SERIAL_EOL();
ui.set_status_P(checkingac);
print_calibration_settings(_endstop_results, _angle_results);
ac_setup(!_0p_calibration && !_1p_calibration);
if (!_0p_calibration) ac_home();
do { // start iterations
float z_at_pt[NPP + 1] = { 0.0f };
test_precision = zero_std_dev_old != 999.0f ? (zero_std_dev + zero_std_dev_old) / 2.0f : zero_std_dev;
iterations++;
// Probe the points
zero_std_dev_old = zero_std_dev;
if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each)) {
SERIAL_ECHOLNPGM("Correct delta settings with M665 and M666");
return AC_CLEANUP();
}
zero_std_dev = std_dev_points(z_at_pt, _0p_calibration, _1p_calibration, _4p_calibration, _4p_opposite_points);
// Solve matrices
if ((zero_std_dev < test_precision || iterations <= force_iterations) && zero_std_dev > calibration_precision) {
#if !HAS_BED_PROBE
test_precision = 0.0f; // forced end
#endif
if (zero_std_dev < zero_std_dev_min) {
// set roll-back point
e_old = delta_endstop_adj;
r_old = delta_radius;
h_old = delta_height;
a_old = delta_tower_angle_trim;
}
abc_float_t e_delta = { 0.0f }, t_delta = { 0.0f };
float r_delta = 0.0f;
/**
* convergence matrices:
* see https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for
* - definition of the matrix scaling parameters
* - matrices for 4 and 7 point calibration
*/
#define ZP(N,I) ((N) * z_at_pt[I] / 4.0f) // 4.0 = divider to normalize to integers
#define Z12(I) ZP(12, I)
#define Z4(I) ZP(4, I)
#define Z2(I) ZP(2, I)
#define Z1(I) ZP(1, I)
#define Z0(I) ZP(0, I)
// calculate factors
if (_7p_9_center) calibration_radius_factor = 0.9f;
h_factor = auto_tune_h();
r_factor = auto_tune_r();
a_factor = auto_tune_a();
calibration_radius_factor = 1.0f;
switch (probe_points) {
case 0:
test_precision = 0.0f; // forced end
break;
case 1:
test_precision = 0.0f; // forced end
LOOP_XYZ(axis) e_delta[axis] = +Z4(CEN);
break;
case 2:
if (towers_set) { // see 4 point calibration (towers) matrix
e_delta.set((+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor +Z4(CEN),
(-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor +Z4(CEN),
(-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor +Z4(CEN));
r_delta = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor;
}
else { // see 4 point calibration (opposites) matrix
e_delta.set((-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor +Z4(CEN),
(+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor +Z4(CEN),
(+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor +Z4(CEN));
r_delta = (+Z4(_BC) +Z4(_CA) +Z4(_AB) -Z12(CEN)) * r_factor;
}
break;
default: // see 7 point calibration (towers & opposites) matrix
e_delta.set((+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor +Z4(CEN),
(-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor +Z4(CEN),
(-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor +Z4(CEN));
r_delta = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor;
if (towers_set) { // see 7 point tower angle calibration (towers & opposites) matrix
t_delta.set((+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor,
(+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor,
(-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor);
}
break;
}
delta_endstop_adj += e_delta;
delta_radius += r_delta;
delta_tower_angle_trim += t_delta;
}
else if (zero_std_dev >= test_precision) {
// roll back
delta_endstop_adj = e_old;
delta_radius = r_old;
delta_height = h_old;
delta_tower_angle_trim = a_old;
}
if (verbose_level != 0) { // !dry run
// Normalize angles to least-squares
if (_angle_results) {
float a_sum = 0.0f;
LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis];
LOOP_XYZ(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f;
}
// adjust delta_height and endstops by the max amount
const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c);
delta_height -= z_temp;
LOOP_XYZ(axis) delta_endstop_adj[axis] -= z_temp;
}
recalc_delta_settings();
NOMORE(zero_std_dev_min, zero_std_dev);
// print report
if (verbose_level == 3)
print_calibration_results(z_at_pt, _tower_results, _opposite_results);
if (verbose_level != 0) { // !dry run
if ((zero_std_dev >= test_precision && iterations > force_iterations) || zero_std_dev <= calibration_precision) { // end iterations
SERIAL_ECHOPGM("Calibration OK");
SERIAL_ECHO_SP(32);
#if HAS_BED_PROBE
if (zero_std_dev >= test_precision && !_1p_calibration && !_0p_calibration)
SERIAL_ECHOPGM("rolling back.");
else
#endif
{
SERIAL_ECHOPAIR_F("std dev:", zero_std_dev_min, 3);
}
SERIAL_EOL();
char mess[21];
strcpy_P(mess, PSTR("Calibration sd:"));
if (zero_std_dev_min < 1)
sprintf_P(&mess[15], PSTR("0.%03i"), (int)LROUND(zero_std_dev_min * 1000.0f));
else
sprintf_P(&mess[15], PSTR("%03i.x"), (int)LROUND(zero_std_dev_min));
ui.set_status(mess);
print_calibration_settings(_endstop_results, _angle_results);
SERIAL_ECHOLNPGM("Save with M500 and/or copy to Configuration.h");
}
else { // !end iterations
char mess[15];
if (iterations < 31)
sprintf_P(mess, PSTR("Iteration : %02i"), (unsigned int)iterations);
else
strcpy_P(mess, PSTR("No convergence"));
SERIAL_ECHO(mess);
SERIAL_ECHO_SP(32);
SERIAL_ECHOLNPAIR_F("std dev:", zero_std_dev, 3);
ui.set_status(mess);
if (verbose_level > 1)
print_calibration_settings(_endstop_results, _angle_results);
}
}
else { // dry run
PGM_P enddryrun = PSTR("End DRY-RUN");
serialprintPGM(enddryrun);
SERIAL_ECHO_SP(35);
SERIAL_ECHOLNPAIR_F("std dev:", zero_std_dev, 3);
char mess[21];
strcpy_P(mess, enddryrun);
strcpy_P(&mess[11], PSTR(" sd:"));
if (zero_std_dev < 1)
sprintf_P(&mess[15], PSTR("0.%03i"), (int)LROUND(zero_std_dev * 1000.0f));
else
sprintf_P(&mess[15], PSTR("%03i.x"), (int)LROUND(zero_std_dev));
ui.set_status(mess);
}
ac_home();
}
while (((zero_std_dev < test_precision && iterations < 31) || iterations <= force_iterations) && zero_std_dev > calibration_precision);
AC_CLEANUP();
}
#endif // DELTA_AUTO_CALIBRATION

View File

@@ -0,0 +1,504 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
#include "../../feature/z_stepper_align.h"
#include "../gcode.h"
#include "../../module/planner.h"
#include "../../module/stepper.h"
#include "../../module/motion.h"
#include "../../module/probe.h"
#if HOTENDS > 1
#include "../../module/tool_change.h"
#endif
#if HAS_LEVELING
#include "../../feature/bedlevel/bedlevel.h"
#endif
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
#include "../../libs/least_squares_fit.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
inline void set_all_z_lock(const bool lock) {
stepper.set_z_lock(lock);
stepper.set_z2_lock(lock);
#if NUM_Z_STEPPER_DRIVERS >= 3
stepper.set_z3_lock(lock);
#if NUM_Z_STEPPER_DRIVERS >= 4
stepper.set_z4_lock(lock);
#endif
#endif
}
/**
* G34: Z-Stepper automatic alignment
*
* I<iterations>
* T<accuracy>
* A<amplification>
* R<recalculate> points based on current probe offsets
*/
void GcodeSuite::G34() {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPGM(">>> G34");
log_machine_info();
}
do { // break out on error
#if NUM_Z_STEPPER_DRIVERS == 4
SERIAL_ECHOLNPGM("Alignment for 4 steppers is Experimental!");
#elif NUM_Z_STEPPER_DRIVERS > 4
SERIAL_ECHOLNPGM("Alignment not supported for over 4 steppers");
break;
#endif
const int8_t z_auto_align_iterations = parser.intval('I', Z_STEPPER_ALIGN_ITERATIONS);
if (!WITHIN(z_auto_align_iterations, 1, 30)) {
SERIAL_ECHOLNPGM("?(I)teration out of bounds (1-30).");
break;
}
const float z_auto_align_accuracy = parser.floatval('T', Z_STEPPER_ALIGN_ACC);
if (!WITHIN(z_auto_align_accuracy, 0.01f, 1.0f)) {
SERIAL_ECHOLNPGM("?(T)arget accuracy out of bounds (0.01-1.0).");
break;
}
const float z_auto_align_amplification =
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
Z_STEPPER_ALIGN_AMP;
#else
parser.floatval('A', Z_STEPPER_ALIGN_AMP);
if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) {
SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0).");
break;
}
#endif
if (parser.seen('R')) z_stepper_align.reset_to_default();
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
// Wait for planner moves to finish!
planner.synchronize();
// Disable the leveling matrix before auto-aligning
#if HAS_LEVELING
#if ENABLED(RESTORE_LEVELING_AFTER_G34)
const bool leveling_was_active = planner.leveling_active;
#endif
set_bed_leveling_enabled(false);
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
workspace_plane = PLANE_XY;
#endif
// Always home with tool 0 active
#if HOTENDS > 1
const uint8_t old_tool_index = active_extruder;
tool_change(0, true);
#endif
#if HAS_DUPLICATION_MODE
extruder_duplication_enabled = false;
#endif
#if BOTH(BLTOUCH, BLTOUCH_HS_MODE)
// In BLTOUCH HS mode, the probe travels in a deployed state.
// Users of G34 might have a badly misaligned bed, so raise Z by the
// length of the deployed pin (BLTOUCH stroke < 7mm)
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES + 7.0f
#else
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES
#endif
// Compute a worst-case clearance height to probe from. After the first
// iteration this will be re-calculated based on the actual bed position
auto magnitude2 = [&](const uint8_t i, const uint8_t j) {
const xy_pos_t diff = z_stepper_align.xy[i] - z_stepper_align.xy[j];
return HYPOT2(diff.x, diff.y);
};
float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * SQRT(
#if NUM_Z_STEPPER_DRIVERS == 3
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 0))
#elif NUM_Z_STEPPER_DRIVERS == 4
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 3),
magnitude2(3, 0), magnitude2(0, 2), magnitude2(1, 3))
#else
magnitude2(0, 1)
#endif
);
// Home before the alignment procedure
if (!all_axes_known()) home_all_axes();
// Move the Z coordinate realm towards the positive - dirty trick
current_position.z += z_probe * 0.5f;
sync_plan_position();
// Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error.
// This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration.
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N(NUM_Z_STEPPER_DRIVERS, 10000.0f, 10000.0f, 10000.0f, 10000.0f);
#else
float last_z_align_level_indicator = 10000.0f;
#endif
float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 },
z_maxdiff = 0.0f,
amplification = z_auto_align_amplification;
// These are needed after the for-loop
uint8_t iteration;
bool err_break = false;
float z_measured_min;
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
bool adjustment_reverse = false;
#endif
// 'iteration' is declared above and is also used after the for-loop.
// *not* the same as LOOP_L_N(iteration, z_auto_align_iterations)
for (iteration = 0; iteration < z_auto_align_iterations; ++iteration) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
SERIAL_ECHOLNPAIR("\nITERATION: ", int(iteration + 1));
// Initialize minimum value
z_measured_min = 100000.0f;
float z_measured_max = -100000.0f;
// Probe all positions (one per Z-Stepper)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
// iteration odd/even --> downward / upward stepper sequence
const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i;
// Safe clearance even on an incline
if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe);
if (DEBUGGING(LEVELING))
DEBUG_ECHOLNPAIR_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y);
// Probe a Z height for each stepper.
// Probing sanity check is disabled, as it would trigger even in normal cases because
// current_position.z has been manually altered in the "dirty trick" above.
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
if (isnan(z_probed_height)) {
SERIAL_ECHOLNPGM("Probing failed.");
err_break = true;
break;
}
// Add height to each value, to provide a more useful target height for
// the next iteration of probing. This allows adjustments to be made away from the bed.
z_measured[iprobe] = z_probed_height + Z_CLEARANCE_BETWEEN_PROBES;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(iprobe + 1), " measured position is ", z_measured[iprobe]);
// Remember the minimum measurement to calculate the correction later on
z_measured_min = _MIN(z_measured_min, z_measured[iprobe]);
z_measured_max = _MAX(z_measured_max, z_measured[iprobe]);
} // for (i)
if (err_break) break;
// Adapt the next probe clearance height based on the new measurements.
// Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment.
z_maxdiff = z_measured_max - z_measured_min;
z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff;
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Replace the initial values in z_measured with calculated heights at
// each stepper position. This allows the adjustment algorithm to be
// shared between both possible probing mechanisms.
// This must be done after the next z_probe height is calculated, so that
// the height is calculated from actual print area positions, and not
// extrapolated motor movements.
// Compute the least-squares fit for all probed points.
// Calculate the Z position of each stepper and store it in z_measured.
// This allows the actual adjustment logic to be shared by both algorithms.
linear_fit_data lfd;
incremental_LSF_reset(&lfd);
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
SERIAL_ECHOLNPAIR("PROBEPT_", int(i), ": ", z_measured[i]);
incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]);
}
finish_incremental_LSF(&lfd);
z_measured_min = 100000.0f;
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D);
z_measured_min = _MIN(z_measured_min, z_measured[i]);
}
SERIAL_ECHOLNPAIR("CALCULATED STEPPER POSITIONS: Z1=", z_measured[0], " Z2=", z_measured[1], " Z3=", z_measured[2]);
#endif
SERIAL_ECHOLNPAIR("\n"
"DIFFERENCE Z1-Z2=", ABS(z_measured[0] - z_measured[1])
#if NUM_Z_STEPPER_DRIVERS == 3
, " Z2-Z3=", ABS(z_measured[1] - z_measured[2])
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
#endif
);
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Check if the applied corrections go in the correct direction.
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
// Compare to the last iteration to ensure it's getting better.
// Calculate mean value as a reference
float z_measured_mean = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper];
z_measured_mean /= NUM_Z_STEPPER_DRIVERS;
// Calculate the sum of the absolute deviations from the mean value
float z_align_level_indicator = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS)
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
// If it's getting worse, stop and throw an error
if (last_z_align_level_indicator < z_align_level_indicator * 0.7f) {
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
err_break = true;
break;
}
last_z_align_level_indicator = z_align_level_indicator;
#endif
// The following correction actions are to be enabled for select Z-steppers only
stepper.set_separate_multi_axis(true);
bool success_break = true;
// Correct the individual stepper offsets
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) {
// Calculate current stepper move
float z_align_move = z_measured[zstepper] - z_measured_min;
const float z_align_abs = ABS(z_align_move);
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Optimize one iteration's correction based on the first measurements
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
// Check for less accuracy compared to last move
if (last_z_align_move[zstepper] < z_align_abs * 0.7f) {
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
adjustment_reverse = !adjustment_reverse;
}
// Remember the alignment for the next iteration
last_z_align_move[zstepper] = z_align_abs;
#endif
// Stop early if all measured points achieve accuracy target
if (z_align_abs > z_auto_align_accuracy) success_break = false;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(zstepper + 1), " corrected by ", z_align_move);
// Lock all steppers except one
set_all_z_lock(true);
switch (zstepper) {
case 0: stepper.set_z_lock(false); break;
case 1: stepper.set_z2_lock(false); break;
#if NUM_Z_STEPPER_DRIVERS >= 3
case 2: stepper.set_z3_lock(false); break;
#endif
#if NUM_Z_STEPPER_DRIVERS == 4
case 3: stepper.set_z4_lock(false); break;
#endif
}
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Decreasing accuracy was detected so move was inverted.
// Will match reversed Z steppers on dual steppers. Triple will need more work to map.
if (adjustment_reverse)
z_align_move = -z_align_move;
#endif
// Do a move to correct part of the misalignment for the current stepper
do_blocking_move_to_z(amplification * z_align_move + current_position.z);
} // for (zstepper)
// Back to normal stepper operations
set_all_z_lock(false);
stepper.set_separate_multi_axis(false);
if (err_break) break;
if (success_break) { SERIAL_ECHOLNPGM("Target accuracy achieved."); break; }
} // for (iteration)
if (err_break)
SERIAL_ECHOLNPGM("G34 aborted.");
else {
SERIAL_ECHOLNPAIR("Did ", int(iteration + (iteration != z_auto_align_iterations)), " of ", int(z_auto_align_iterations));
SERIAL_ECHOLNPAIR_F("Accuracy: ", z_maxdiff);
}
// Stow the probe, as the last call to probe.probe_at_point(...) left
// the probe deployed if it was successful.
probe.stow();
#if ENABLED(HOME_AFTER_G34)
// After this operation the z position needs correction
set_axis_not_trusted(Z_AXIS);
// Home Z after the alignment procedure
process_subcommands_now_P(PSTR("G28Z"));
#else
// Use the probed height from the last iteration to determine the Z height.
// z_measured_min is used, because all steppers are aligned to z_measured_min.
// Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier.
current_position.z -= z_measured_min - (float)Z_CLEARANCE_BETWEEN_PROBES;
sync_plan_position();
#endif
// Restore the active tool after homing
#if HOTENDS > 1
tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous tool for parking extruder
#endif
#if HAS_LEVELING && ENABLED(RESTORE_LEVELING_AFTER_G34)
set_bed_leveling_enabled(leveling_was_active);
#endif
}while(0);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G34");
}
/**
* M422: Set a Z-Stepper automatic alignment XY point.
* Use repeatedly to set multiple points.
*
* S<index> : Index of the probe point to set
*
* With Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS:
* W<index> : Index of the Z stepper position to set
* The W and S parameters may not be combined.
*
* S and W require an X and/or Y parameter
* X<pos> : X position to set (Unchanged if omitted)
* Y<pos> : Y position to set (Unchanged if omitted)
*
* R : Recalculate points based on current probe offsets
*/
void GcodeSuite::M422() {
if (parser.seen('R')) {
z_stepper_align.reset_to_default();
return;
}
if (!parser.seen_any()) {
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
SERIAL_ECHOLNPAIR_P(PSTR("M422 S"), int(i + 1), SP_X_STR, z_stepper_align.xy[i].x, SP_Y_STR, z_stepper_align.xy[i].y);
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
SERIAL_ECHOLNPAIR_P(PSTR("M422 W"), int(i + 1), SP_X_STR, z_stepper_align.stepper_xy[i].x, SP_Y_STR, z_stepper_align.stepper_xy[i].y);
#endif
return;
}
const bool is_probe_point = parser.seen('S');
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
if (is_probe_point && parser.seen('W')) {
SERIAL_ECHOLNPGM("?(S) and (W) may not be combined.");
return;
}
#endif
xy_pos_t *pos_dest = (
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
!is_probe_point ? z_stepper_align.stepper_xy :
#endif
z_stepper_align.xy
);
if (!is_probe_point
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
&& !parser.seen('W')
#endif
) {
SERIAL_ECHOLNPGM(
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
"?(S) or (W) is required."
#else
"?(S) is required."
#endif
);
return;
}
// Get the Probe Position Index or Z Stepper Index
int8_t position_index;
if (is_probe_point) {
position_index = parser.intval('S') - 1;
if (!WITHIN(position_index, 0, int8_t(NUM_Z_STEPPER_DRIVERS) - 1)) {
SERIAL_ECHOLNPGM("?(S) Z-ProbePosition index invalid.");
return;
}
}
else {
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
position_index = parser.intval('W') - 1;
if (!WITHIN(position_index, 0, NUM_Z_STEPPER_DRIVERS - 1)) {
SERIAL_ECHOLNPGM("?(W) Z-Stepper index invalid.");
return;
}
#endif
}
const xy_pos_t pos = {
parser.floatval('X', pos_dest[position_index].x),
parser.floatval('Y', pos_dest[position_index].y)
};
if (is_probe_point) {
if (!probe.can_reach(pos.x, Y_CENTER)) {
SERIAL_ECHOLNPGM("?(X) out of bounds.");
return;
}
if (!probe.can_reach(pos)) {
SERIAL_ECHOLNPGM("?(Y) out of bounds.");
return;
}
}
pos_dest[position_index] = pos;
}
#endif // Z_STEPPER_AUTO_ALIGN

View File

@@ -0,0 +1,640 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../MarlinCore.h"
#if ENABLED(CALIBRATION_GCODE)
#include "../gcode.h"
#if ENABLED(BACKLASH_GCODE)
#include "../../feature/backlash.h"
#endif
#include "../../lcd/ultralcd.h"
#include "../../module/motion.h"
#include "../../module/planner.h"
#include "../../module/tool_change.h"
#include "../../module/endstops.h"
#include "../../feature/bedlevel/bedlevel.h"
#if !AXIS_CAN_CALIBRATE(X)
#undef CALIBRATION_MEASURE_LEFT
#undef CALIBRATION_MEASURE_RIGHT
#endif
#if !AXIS_CAN_CALIBRATE(Y)
#undef CALIBRATION_MEASURE_FRONT
#undef CALIBRATION_MEASURE_BACK
#endif
#if !AXIS_CAN_CALIBRATE(Z)
#undef CALIBRATION_MEASURE_AT_TOP_EDGES
#endif
/**
* G425 backs away from the calibration object by various distances
* depending on the confidence level:
*
* UNKNOWN - No real notion on where the calibration object is on the bed
* UNCERTAIN - Measurement may be uncertain due to backlash
* CERTAIN - Measurement obtained with backlash compensation
*/
#ifndef CALIBRATION_MEASUREMENT_UNKNOWN
#define CALIBRATION_MEASUREMENT_UNKNOWN 5.0 // mm
#endif
#ifndef CALIBRATION_MEASUREMENT_UNCERTAIN
#define CALIBRATION_MEASUREMENT_UNCERTAIN 1.0 // mm
#endif
#ifndef CALIBRATION_MEASUREMENT_CERTAIN
#define CALIBRATION_MEASUREMENT_CERTAIN 0.5 // mm
#endif
#if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT)
#define HAS_X_CENTER 1
#endif
#if BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
#define HAS_Y_CENTER 1
#endif
enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES };
static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
static constexpr xy_float_t nod = { CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER };
struct measurements_t {
xyz_pos_t obj_center = true_center; // Non-static must be assigned from xyz_pos_t
float obj_side[NUM_SIDES], backlash[NUM_SIDES];
xyz_float_t pos_error;
xy_float_t nozzle_outer_dimension = nod;
};
#define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable);
#if ENABLED(BACKLASH_GCODE)
#define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value)
#else
#define TEMPORARY_BACKLASH_CORRECTION(value)
#endif
#if ENABLED(BACKLASH_GCODE) && defined(BACKLASH_SMOOTHING_MM)
#define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash.smoothing_mm, value)
#else
#define TEMPORARY_BACKLASH_SMOOTHING(value)
#endif
inline void calibration_move() {
do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
}
/**
* Move to the exact center above the calibration object
*
* m in - Measurement record
* uncertainty in - How far away from the object top to park
*/
inline void park_above_object(measurements_t &m, const float uncertainty) {
// Move to safe distance above calibration object
current_position.z = m.obj_center.z + dimensions.z / 2 + uncertainty;
calibration_move();
// Move to center of calibration object in XY
current_position = xy_pos_t(m.obj_center);
calibration_move();
}
#if HOTENDS > 1
inline void set_nozzle(measurements_t &m, const uint8_t extruder) {
if (extruder != active_extruder) {
park_above_object(m, CALIBRATION_MEASUREMENT_UNKNOWN);
tool_change(extruder);
}
}
#endif
#if HAS_HOTEND_OFFSET
inline void normalize_hotend_offsets() {
LOOP_S_L_N(e, 1, HOTENDS)
hotend_offset[e] -= hotend_offset[0];
hotend_offset[0].reset();
}
#endif
inline bool read_calibration_pin() {
return (
#if PIN_EXISTS(CALIBRATION)
READ(CALIBRATION_PIN) != CALIBRATION_PIN_INVERTING
#elif HAS_CUSTOM_PROBE_PIN
READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING
#else
READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING
#endif
);
}
/**
* Move along axis in the specified dir until the probe value becomes stop_state,
* then return the axis value.
*
* axis in - Axis along which the measurement will take place
* dir in - Direction along that axis (-1 or 1)
* stop_state in - Move until probe pin becomes this value
* fast in - Fast vs. precise measurement
*/
float measuring_movement(const AxisEnum axis, const int dir, const bool stop_state, const bool fast) {
const float step = fast ? 0.25 : CALIBRATION_MEASUREMENT_RESOLUTION;
const feedRate_t mms = fast ? MMM_TO_MMS(CALIBRATION_FEEDRATE_FAST) : MMM_TO_MMS(CALIBRATION_FEEDRATE_SLOW);
const float limit = fast ? 50 : 5;
destination = current_position;
for (float travel = 0; travel < limit; travel += step) {
destination[axis] += dir * step;
do_blocking_move_to(destination, mms);
planner.synchronize();
if (read_calibration_pin() == stop_state) break;
}
return destination[axis];
}
/**
* Move along axis until the probe is triggered. Move toolhead to its starting
* point and return the measured value.
*
* axis in - Axis along which the measurement will take place
* dir in - Direction along that axis (-1 or 1)
* stop_state in - Move until probe pin becomes this value
* backlash_ptr in/out - When not nullptr, measure and record axis backlash
* uncertainty in - If uncertainty is CALIBRATION_MEASUREMENT_UNKNOWN, do a fast probe.
*/
inline float measure(const AxisEnum axis, const int dir, const bool stop_state, float * const backlash_ptr, const float uncertainty) {
const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN;
// Save position
destination = current_position;
const float start_pos = destination[axis];
const float measured_pos = measuring_movement(axis, dir, stop_state, fast);
// Measure backlash
if (backlash_ptr && !fast) {
const float release_pos = measuring_movement(axis, -dir, !stop_state, fast);
*backlash_ptr = ABS(release_pos - measured_pos);
}
// Return to starting position
destination[axis] = start_pos;
do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
return measured_pos;
}
/**
* Probe one side of the calibration object
*
* m in/out - Measurement record, m.obj_center and m.obj_side will be updated.
* uncertainty in - How far away from the calibration object to begin probing
* side in - Side of probe where probe will occur
* probe_top_at_edge in - When probing sides, probe top of calibration object nearest edge
* to find out height of edge
*/
inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) {
const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS;
AxisEnum axis;
float dir = 1;
park_above_object(m, uncertainty);
switch (side) {
#if AXIS_CAN_CALIBRATE(Z)
case TOP: {
const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
m.obj_center.z = measurement - dimensions.z / 2;
m.obj_side[TOP] = measurement;
return;
}
#endif
#if AXIS_CAN_CALIBRATE(X)
case LEFT: axis = X_AXIS; break;
case RIGHT: axis = X_AXIS; dir = -1; break;
#endif
#if AXIS_CAN_CALIBRATE(Y)
case FRONT: axis = Y_AXIS; break;
case BACK: axis = Y_AXIS; dir = -1; break;
#endif
default: return;
}
if (probe_top_at_edge) {
#if AXIS_CAN_CALIBRATE(Z)
// Probe top nearest the side we are probing
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]);
calibration_move();
m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2;
#endif
}
if (AXIS_CAN_CALIBRATE(X) && axis == X_AXIS || AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS) {
// Move to safe distance to the side of the calibration object
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
calibration_move();
// Plunge below the side of the calibration object and measure
current_position.z = m.obj_side[TOP] - (CALIBRATION_NOZZLE_TIP_HEIGHT) * 0.7f;
calibration_move();
const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty);
m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2);
m.obj_side[side] = measurement;
}
}
/**
* Probe all sides of the calibration calibration object
*
* m in/out - Measurement record: center, backlash and error values be updated.
* uncertainty in - How far away from the calibration object to begin probing
*/
inline void probe_sides(measurements_t &m, const float uncertainty) {
#if ENABLED(CALIBRATION_MEASURE_AT_TOP_EDGES)
constexpr bool probe_top_at_edge = true;
#else
// Probing at the exact center only works if the center is flat. Probing on a washer
// or bolt will require probing the top near the side edges, away from the center.
constexpr bool probe_top_at_edge = false;
probe_side(m, uncertainty, TOP);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
probe_side(m, uncertainty, RIGHT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_FRONT)
probe_side(m, uncertainty, FRONT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_LEFT)
probe_side(m, uncertainty, LEFT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
probe_side(m, uncertainty, BACK, probe_top_at_edge);
#endif
// Compute the measured center of the calibration object.
#if HAS_X_CENTER
m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2;
#endif
#if HAS_Y_CENTER
m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2;
#endif
// Compute the outside diameter of the nozzle at the height
// at which it makes contact with the calibration object
#if HAS_X_CENTER
m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x;
#endif
#if HAS_Y_CENTER
m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y;
#endif
park_above_object(m, uncertainty);
// The difference between the known and the measured location
// of the calibration object is the positional error
m.pos_error.x = (0
#if HAS_X_CENTER
+ true_center.x - m.obj_center.x
#endif
);
m.pos_error.y = (0
#if HAS_Y_CENTER
+ true_center.y - m.obj_center.y
#endif
);
m.pos_error.z = true_center.z - m.obj_center.z;
}
#if ENABLED(CALIBRATION_REPORTING)
inline void report_measured_faces(const measurements_t &m) {
SERIAL_ECHOLNPGM("Sides:");
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.obj_side[TOP]);
#endif
#if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.obj_side[LEFT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
SERIAL_ECHOLNPAIR(" Right: ", m.obj_side[RIGHT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPAIR(" Front: ", m.obj_side[FRONT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPAIR(" Back: ", m.obj_side[BACK]);
#endif
SERIAL_EOL();
}
inline void report_measured_center(const measurements_t &m) {
SERIAL_ECHOLNPGM("Center:");
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.obj_center.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.obj_center.y);
#endif
SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.obj_center.z);
SERIAL_EOL();
}
inline void report_measured_backlash(const measurements_t &m) {
SERIAL_ECHOLNPGM("Backlash:");
#if AXIS_CAN_CALIBRATE(X)
#if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.backlash[LEFT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
SERIAL_ECHOLNPAIR(" Right: ", m.backlash[RIGHT]);
#endif
#endif
#if AXIS_CAN_CALIBRATE(Y)
#if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPAIR(" Front: ", m.backlash[FRONT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPAIR(" Back: ", m.backlash[BACK]);
#endif
#endif
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.backlash[TOP]);
#endif
SERIAL_EOL();
}
inline void report_measured_positional_error(const measurements_t &m) {
SERIAL_CHAR('T');
SERIAL_ECHO(int(active_extruder));
SERIAL_ECHOLNPGM(" Positional Error:");
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.pos_error.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y);
#endif
if (AXIS_CAN_CALIBRATE(Z)) SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
SERIAL_EOL();
}
inline void report_measured_nozzle_dimensions(const measurements_t &m) {
SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
#if HAS_X_CENTER || HAS_Y_CENTER
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.nozzle_outer_dimension.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.nozzle_outer_dimension.y);
#endif
#else
UNUSED(m);
#endif
SERIAL_EOL();
}
#if HAS_HOTEND_OFFSET
//
// This function requires normalize_hotend_offsets() to be called
//
inline void report_hotend_offsets() {
LOOP_S_L_N(e, 1, HOTENDS)
SERIAL_ECHOLNPAIR_P(PSTR("T"), int(e), PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
}
#endif
#endif // CALIBRATION_REPORTING
/**
* Probe around the calibration object to measure backlash
*
* m in/out - Measurement record, updated with new readings
* uncertainty in - How far away from the object to begin probing
*/
inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
// Backlash compensation should be off while measuring backlash
{
// New scope for TEMPORARY_BACKLASH_CORRECTION
TEMPORARY_BACKLASH_CORRECTION(all_off);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
probe_sides(m, uncertainty);
#if ENABLED(BACKLASH_GCODE)
#if HAS_X_CENTER
backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_LEFT)
backlash.distance_mm.x = m.backlash[LEFT];
#elif ENABLED(CALIBRATION_MEASURE_RIGHT)
backlash.distance_mm.x = m.backlash[RIGHT];
#endif
#if HAS_Y_CENTER
backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_FRONT)
backlash.distance_mm.y = m.backlash[FRONT];
#elif ENABLED(CALIBRATION_MEASURE_BACK)
backlash.distance_mm.y = m.backlash[BACK];
#endif
if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP];
#endif
}
#if ENABLED(BACKLASH_GCODE)
// Turn on backlash compensation and move in all
// allowed directions to take up any backlash
{
// New scope for TEMPORARY_BACKLASH_CORRECTION
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
const xyz_float_t move = { AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3 };
current_position += move; calibration_move();
current_position -= move; calibration_move();
}
#endif
}
inline void update_measurements(measurements_t &m, const AxisEnum axis) {
current_position[axis] += m.pos_error[axis];
m.obj_center[axis] = true_center[axis];
m.pos_error[axis] = 0;
}
/**
* Probe around the calibration object. Adjust the position and toolhead offset
* using the deviation from the known position of the calibration object.
*
* m in/out - Measurement record, updated with new readings
* uncertainty in - How far away from the object to begin probing
* extruder in - What extruder to probe
*
* Prerequisites:
* - Call calibrate_backlash() beforehand for best accuracy
*/
inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const uint8_t extruder) {
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
#if HOTENDS > 1
set_nozzle(m, extruder);
#else
UNUSED(extruder);
#endif
probe_sides(m, uncertainty);
// Adjust the hotend offset
#if HAS_HOTEND_OFFSET
if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) hotend_offset[extruder].x += m.pos_error.x;
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) hotend_offset[extruder].y += m.pos_error.y;
if (AXIS_CAN_CALIBRATE(Z)) hotend_offset[extruder].z += m.pos_error.z;
normalize_hotend_offsets();
#endif
// Correct for positional error, so the object
// is at the known actual spot
planner.synchronize();
if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) update_measurements(m, X_AXIS);
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
sync_plan_position();
}
/**
* Probe around the calibration object for all toolheads, adjusting the coordinate
* system for the first nozzle and the nozzle offset for subsequent nozzles.
*
* m in/out - Measurement record, updated with new readings
* uncertainty in - How far away from the object to begin probing
*/
inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty) {
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e);
#if HAS_HOTEND_OFFSET
normalize_hotend_offsets();
#endif
#if HOTENDS > 1
set_nozzle(m, 0);
#endif
}
/**
* Perform a full auto-calibration routine:
*
* 1) For each nozzle, touch top and sides of object to determine object position and
* nozzle offsets. Do a fast but rough search over a wider area.
* 2) With the first nozzle, touch top and sides of object to determine backlash values
* for all axis (if BACKLASH_GCODE is enabled)
* 3) For each nozzle, touch top and sides of object slowly to determine precise
* position of object. Adjust coordinate system and nozzle offsets so probed object
* location corresponds to known object location with a high degree of precision.
*/
inline void calibrate_all() {
measurements_t m;
#if HAS_HOTEND_OFFSET
reset_hotend_offsets();
#endif
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
// Do a fast and rough calibration of the toolheads
calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNKNOWN);
#if ENABLED(BACKLASH_GCODE)
calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
#endif
// Cycle the toolheads so the servos settle into their "natural" positions
#if HOTENDS > 1
HOTEND_LOOP() set_nozzle(m, e);
#endif
// Do a slow and precise calibration of the toolheads
calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
current_position.x = X_CENTER;
calibration_move(); // Park nozzle away from calibration object
}
/**
* G425: Perform calibration with calibration object.
*
* B - Perform calibration of backlash only.
* T<extruder> - Perform calibration of toolhead only.
* V - Probe object and print position, error, backlash and hotend offset.
* U - Uncertainty, how far to start probe away from the object (mm)
*
* no args - Perform entire calibration sequence (backlash + position on all toolheads)
*/
void GcodeSuite::G425() {
TEMPORARY_SOFT_ENDSTOP_STATE(false);
TEMPORARY_BED_LEVELING_STATE(false);
if (axis_unhomed_error()) return;
measurements_t m;
float uncertainty = parser.seenval('U') ? parser.value_float() : CALIBRATION_MEASUREMENT_UNCERTAIN;
if (parser.seen('B'))
calibrate_backlash(m, uncertainty);
else if (parser.seen('T'))
calibrate_toolhead(m, uncertainty, parser.has_value() ? parser.value_int() : active_extruder);
#if ENABLED(CALIBRATION_REPORTING)
else if (parser.seen('V')) {
probe_sides(m, uncertainty);
SERIAL_EOL();
report_measured_faces(m);
report_measured_center(m);
report_measured_backlash(m);
report_measured_nozzle_dimensions(m);
report_measured_positional_error(m);
#if HAS_HOTEND_OFFSET
normalize_hotend_offsets();
report_hotend_offsets();
#endif
}
#endif
else
calibrate_all();
}
#endif // CALIBRATION_GCODE

View File

@@ -0,0 +1,338 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* G76_M871.cpp - Temperature calibration/compensation for z-probing
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(PROBE_TEMP_COMPENSATION)
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/planner.h"
#include "../../module/probe.h"
#include "../../feature/bedlevel/bedlevel.h"
#include "../../module/temperature.h"
#include "../../module/probe.h"
#include "../../feature/probe_temp_comp.h"
/**
* G76: calibrate probe and/or bed temperature offsets
* Notes:
* - When calibrating probe, bed temperature is held constant.
* Compensation values are deltas to first probe measurement at probe temp. = 30°C.
* - When calibrating bed, probe temperature is held constant.
* Compensation values are deltas to first probe measurement at bed temp. = 60°C.
* - The hotend will not be heated at any time.
* - On my Prusa MK3S clone I put a piece of paper between the probe and the hotend
* so the hotend fan would not cool my probe constantly. Alternativly you could just
* make sure the fan is not running while running the calibration process.
*
* Probe calibration:
* - Moves probe to cooldown point.
* - Heats up bed to 100°C.
* - Moves probe to probing point (1mm above heatbed).
* - Waits until probe reaches target temperature (30°C).
* - Does a z-probing (=base value) and increases target temperature by 5°C.
* - Waits until probe reaches increased target temperature.
* - Does a z-probing (delta to base value will be a compensation value) and increases target temperature by 5°C.
* - Repeats last two steps until max. temperature reached or timeout (i.e. probe does not heat up any further).
* - Compensation values of higher temperatures will be extrapolated (using linear regression first).
* While this is not exact by any means it is still better than simply using the last compensation value.
*
* Bed calibration:
* - Moves probe to cooldown point.
* - Heats up bed to 60°C.
* - Moves probe to probing point (1mm above heatbed).
* - Waits until probe reaches target temperature (30°C).
* - Does a z-probing (=base value) and increases bed temperature by 5°C.
* - Moves probe to cooldown point.
* - Waits until probe is below 30°C and bed has reached target temperature.
* - Moves probe to probing point and waits until it reaches target temperature (30°C).
* - Does a z-probing (delta to base value will be a compensation value) and increases bed temperature by 5°C.
* - Repeats last four points until max. bed temperature reached (110°C) or timeout.
* - Compensation values of higher temperatures will be extrapolated (using linear regression first).
* While this is not exact by any means it is still better than simply using the last compensation value.
*
* G76 [B | P]
* - no flag - Both calibration procedures will be run.
* - `B` - Run bed temperature calibration.
* - `P` - Run probe temperature calibration.
*/
void GcodeSuite::G76() {
// Check if heated bed is available and z-homing is done with probe
#if TEMP_SENSOR_BED == 0 || !(HOMING_Z_WITH_PROBE)
return;
#endif
auto report_temps = [](millis_t &ntr, millis_t timeout=0) {
idle_no_sleep();
const millis_t ms = millis();
if (ELAPSED(ms, ntr)) {
ntr = ms + 1000;
thermalManager.print_heater_states(active_extruder);
}
return (timeout && ELAPSED(ms, timeout));
};
auto wait_for_temps = [&](const float tb, const float tp, millis_t &ntr, const millis_t timeout=0) {
SERIAL_ECHOLNPGM("Waiting for bed and probe temperature.");
while (fabs(thermalManager.degBed() - tb) > 0.1f || thermalManager.degProbe() > tp)
if (report_temps(ntr, timeout)) return true;
return false;
};
auto g76_probe = [](const xy_pos_t &xypos) {
do_blocking_move_to_z(5.0); // Raise nozzle before probing
const float measured_z = probe.probe_at_point(xypos, PROBE_PT_NONE, 0, false); // verbose=0, probe_relative=false
if (isnan(measured_z))
SERIAL_ECHOLNPGM("!Received NAN. Aborting.");
else
SERIAL_ECHOLNPAIR_F("Measured: ", measured_z);
return measured_z;
};
#if ENABLED(BLTOUCH)
// Make sure any BLTouch error condition is cleared
bltouch_command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY);
set_bltouch_deployed(false);
#endif
bool do_bed_cal = parser.boolval('B'), do_probe_cal = parser.boolval('P');
if (!do_bed_cal && !do_probe_cal) do_bed_cal = do_probe_cal = true;
// Synchronize with planner
planner.synchronize();
const xyz_pos_t parkpos = { temp_comp.park_point_x, temp_comp.park_point_y, temp_comp.park_point_z };
const xy_pos_t ppos = { temp_comp.measure_point_x, temp_comp.measure_point_y };
if (do_bed_cal || do_probe_cal) {
// Ensure park position is reachable
bool reachable = position_is_reachable(parkpos) || WITHIN(parkpos.z, Z_MIN_POS - fslop, Z_MAX_POS + fslop);
if (!reachable)
SERIAL_ECHOLNPGM("!Park");
else {
// Ensure probe position is reachable
reachable = probe.can_reach(ppos);
if (!reachable) SERIAL_ECHOLNPGM("!Probe");
}
if (!reachable) {
SERIAL_ECHOLNPGM(" position unreachable - aborting.");
return;
}
process_subcommands_now_P(PSTR("G28"));
}
remember_feedrate_scaling_off();
// Nozzle position based on probe position
const xy_pos_t noz_pos = ppos - probe.offset_xy;
/******************************************
* Calibrate bed temperature offsets
******************************************/
// Report temperatures every second and handle heating timeouts
millis_t next_temp_report = millis() + 1000;
if (do_bed_cal) {
uint16_t target_bed = temp_comp.cali_info_init[TSI_BED].start_temp,
target_probe = temp_comp.bed_calib_probe_temp;
SERIAL_ECHOLNPGM("Waiting for cooling.");
while (thermalManager.degBed() > target_bed || thermalManager.degProbe() > target_probe)
report_temps(next_temp_report);
// Disable leveling so it won't mess with us
#if HAS_LEVELING
set_bed_leveling_enabled(false);
#endif
for (;;) {
thermalManager.setTargetBed(target_bed);
SERIAL_ECHOLNPAIR("Target Bed:", target_bed, " Probe:", target_probe);
// Park nozzle
do_blocking_move_to(parkpos);
// Wait for heatbed to reach target temp and probe to cool below target temp
if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + 900UL * 1000UL)) {
SERIAL_ECHOLNPGM("!Bed heating timeout.");
break;
}
// Move the nozzle to the probing point and wait for the probe to reach target temp
do_blocking_move_to_xy(noz_pos);
SERIAL_ECHOLNPGM("Waiting for probe heating.");
while (thermalManager.degProbe() < target_probe)
report_temps(next_temp_report);
const float measured_z = g76_probe(noz_pos);
if (isnan(measured_z)) break;
if (target_bed == temp_comp.cali_info_init[TSI_BED].start_temp)
temp_comp.prepare_new_calibration(measured_z);
else
temp_comp.push_back_new_measurement(TSI_BED, measured_z);
target_bed += temp_comp.cali_info_init[TSI_BED].temp_res;
if (target_bed > temp_comp.max_bed_temp) break;
}
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
if (temp_comp.finish_calibration(TSI_BED))
SERIAL_ECHOLNPGM("Successfully calibrated bed.");
else
SERIAL_ECHOLNPGM("!Failed to calibrate bed. Values reset.");
// Cleanup
thermalManager.setTargetBed(0);
#if HAS_LEVELING
set_bed_leveling_enabled(true);
#endif
} // do_bed_cal
/********************************************
* Calibrate probe temperature offsets
********************************************/
if (do_probe_cal) {
// Park nozzle
do_blocking_move_to(parkpos);
// Initialize temperatures
const uint16_t target_bed = temp_comp.probe_calib_bed_temp;
thermalManager.setTargetBed(target_bed);
uint16_t target_probe = temp_comp.cali_info_init[TSI_PROBE].start_temp;
// Wait for heatbed to reach target temp and probe to cool below target temp
wait_for_temps(target_bed, target_probe, next_temp_report);
// Disable leveling so it won't mess with us
#if HAS_LEVELING
set_bed_leveling_enabled(false);
#endif
bool timeout = false;
for (;;) {
// Move probe to probing point and wait for it to reach target temperature
do_blocking_move_to_xy(noz_pos);
SERIAL_ECHOLNPAIR("Waiting for probe heating. Bed:", target_bed, " Probe:", target_probe);
const millis_t probe_timeout_ms = millis() + 900UL * 1000UL;
while (thermalManager.degProbe() < target_probe) {
if (report_temps(next_temp_report, probe_timeout_ms)) {
SERIAL_ECHOLNPGM("!Probe heating timed out.");
timeout = true;
break;
}
}
if (timeout) break;
const float measured_z = g76_probe(noz_pos);
if (isnan(measured_z)) break;
if (target_probe == temp_comp.cali_info_init[TSI_PROBE].start_temp)
temp_comp.prepare_new_calibration(measured_z);
else
temp_comp.push_back_new_measurement(TSI_PROBE, measured_z);
target_probe += temp_comp.cali_info_init[TSI_PROBE].temp_res;
if (target_probe > temp_comp.cali_info_init[TSI_PROBE].end_temp) break;
}
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
if (temp_comp.finish_calibration(TSI_PROBE))
SERIAL_ECHOPGM("Successfully calibrated");
else
SERIAL_ECHOPGM("!Failed to calibrate");
SERIAL_ECHOLNPGM(" probe.");
// Cleanup
thermalManager.setTargetBed(0);
#if HAS_LEVELING
set_bed_leveling_enabled(true);
#endif
SERIAL_ECHOLNPGM("Final compensation values:");
temp_comp.print_offsets();
} // do_probe_cal
restore_feedrate_and_scaling();
}
/**
* M871: Report / reset temperature compensation offsets.
* Note: This does not affect values in EEPROM until M500.
*
* M871 [ R | B | P | E ]
*
* No Parameters - Print current offset values.
*
* Select only one of these flags:
* R - Reset all offsets to zero (i.e., disable compensation).
* B - Manually set offset for bed
* P - Manually set offset for probe
* E - Manually set offset for extruder
*
* With B, P, or E:
* I[index] - Index in the array
* V[value] - Adjustment in µm
*/
void GcodeSuite::M871() {
if (parser.seen('R')) {
// Reset z-probe offsets to factory defaults
temp_comp.clear_all_offsets();
SERIAL_ECHOLNPGM("Offsets reset to default.");
}
else if (parser.seen("BPE")) {
if (!parser.seenval('V')) return;
const int16_t val = parser.value_int();
if (!parser.seenval('I')) return;
const int16_t idx = parser.value_int();
const TempSensorID mod = (parser.seen('B') ? TSI_BED :
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
parser.seen('E') ? TSI_EXT :
#endif
TSI_PROBE
);
if (idx > 0 && temp_comp.set_offset(mod, idx - 1, val))
SERIAL_ECHOLNPAIR("Set value: ", val);
else
SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant).");
}
else // Print current Z-probe adjustments. Note: Values in EEPROM might differ.
temp_comp.print_offsets();
}
#endif // PROBE_TEMP_COMPENSATION

View File

@@ -0,0 +1,374 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(M100_FREE_MEMORY_WATCHER)
#include "../gcode.h"
#include "../queue.h"
#include "../../libs/hex_print_routines.h"
#include "../../MarlinCore.h" // for idle()
/**
* M100 Free Memory Watcher
*
* This code watches the free memory block between the bottom of the heap and the top of the stack.
* This memory block is initialized and watched via the M100 command.
*
* M100 I Initializes the free memory block and prints vitals statistics about the area
*
* M100 F Identifies how much of the free memory block remains free and unused. It also
* detects and reports any corruption within the free memory block that may have
* happened due to errant firmware.
*
* M100 D Does a hex display of the free memory block along with a flag for any errant
* data that does not match the expected value.
*
* M100 C x Corrupts x locations within the free memory block. This is useful to check the
* correctness of the M100 F and M100 D commands.
*
* Also, there are two support functions that can be called from a developer's C code.
*
* uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
* void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
*
* Initial version by Roxy-3D
*/
#define M100_FREE_MEMORY_DUMPER // Enable for the `M100 D` Dump sub-command
#define M100_FREE_MEMORY_CORRUPTOR // Enable for the `M100 C` Corrupt sub-command
#define TEST_BYTE ((char) 0xE5)
#if defined(__AVR__) || IS_32BIT_TEENSY
extern char __bss_end;
char *end_bss = &__bss_end,
*free_memory_start = end_bss, *free_memory_end = 0,
*stacklimit = 0, *heaplimit = 0;
#define MEMORY_END_CORRECTION 0
#elif defined(TARGET_LPC1768)
extern char __bss_end__, __StackLimit, __HeapLimit;
char *end_bss = &__bss_end__,
*stacklimit = &__StackLimit,
*heaplimit = &__HeapLimit;
#define MEMORY_END_CORRECTION 0x200
char *free_memory_start = heaplimit,
*free_memory_end = stacklimit - MEMORY_END_CORRECTION;
#elif defined(__SAM3X8E__)
extern char _ebss;
char *end_bss = &_ebss,
*free_memory_start = end_bss,
*free_memory_end = 0,
*stacklimit = 0,
*heaplimit = 0;
#define MEMORY_END_CORRECTION 0x10000 // need to stay well below 0x20080000 or M100 F crashes
#elif defined(__SAMD51__)
extern unsigned int __bss_end__, __StackLimit, __HeapLimit;
extern "C" void * _sbrk(int incr);
void *end_bss = &__bss_end__,
*stacklimit = &__StackLimit,
*heaplimit = &__HeapLimit;
#define MEMORY_END_CORRECTION 0x400
char *free_memory_start = (char *)_sbrk(0) + 0x200, // Leave some heap space
*free_memory_end = (char *)stacklimit - MEMORY_END_CORRECTION;
#else
#error "M100 - unsupported CPU"
#endif
//
// Utility functions
//
// Location of a variable on its stack frame. Returns a value above
// the stack (once the function returns to the caller).
char* top_of_stack() {
char x;
return &x + 1; // x is pulled on return;
}
// Count the number of test bytes at the specified location.
inline int32_t count_test_bytes(const char * const start_free_memory) {
for (uint32_t i = 0; i < 32000; i++)
if (char(start_free_memory[i]) != TEST_BYTE)
return i - 1;
return -1;
}
//
// M100 sub-commands
//
#if ENABLED(M100_FREE_MEMORY_DUMPER)
/**
* M100 D
* Dump the free memory block from brkval to the stack pointer.
* malloc() eats memory from the start of the block and the stack grows
* up from the bottom of the block. Solid test bytes indicate nothing has
* used that memory yet. There should not be anything but test bytes within
* the block. If so, it may indicate memory corruption due to a bad pointer.
* Unexpected bytes are flagged in the right column.
*/
inline void dump_free_memory(char *start_free_memory, char *end_free_memory) {
//
// Start and end the dump on a nice 16 byte boundary
// (even though the values are not 16-byte aligned).
//
start_free_memory = (char*)(ptr_int_t(uint32_t(start_free_memory) & ~0xFUL)); // Align to 16-byte boundary
end_free_memory = (char*)(ptr_int_t(uint32_t(end_free_memory) | 0xFUL)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
// Dump command main loop
while (start_free_memory < end_free_memory) {
print_hex_address(start_free_memory); // Print the address
SERIAL_CHAR(':');
LOOP_L_N(i, 16) { // and 16 data bytes
if (i == 8) SERIAL_CHAR('-');
print_hex_byte(start_free_memory[i]);
SERIAL_CHAR(' ');
}
serial_delay(25);
SERIAL_CHAR('|'); // Point out non test bytes
LOOP_L_N(i, 16) {
char ccc = (char)start_free_memory[i]; // cast to char before automatically casting to char on assignment, in case the compiler is broken
ccc = (ccc == TEST_BYTE) ? ' ' : '?';
SERIAL_CHAR(ccc);
}
SERIAL_EOL();
start_free_memory += 16;
serial_delay(25);
idle();
}
}
void M100_dump_routine(PGM_P const title, const char * const start, const char * const end) {
serialprintPGM(title);
SERIAL_EOL();
//
// Round the start and end locations to produce full lines of output
//
dump_free_memory(
(char*)(ptr_int_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
(char*)(ptr_int_t(uint32_t(end) | 0xFUL)) // Align end_free_memory to the 15th byte (at or above end_free_memory)
);
}
#endif // M100_FREE_MEMORY_DUMPER
inline int check_for_free_memory_corruption(PGM_P const title) {
serialprintPGM(title);
char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
int n = end_free_memory - start_free_memory;
SERIAL_ECHOPAIR("\nfmc() n=", n);
SERIAL_ECHOPAIR("\nfree_memory_start=", hex_address(free_memory_start));
SERIAL_ECHOLNPAIR(" end_free_memory=", hex_address(end_free_memory));
if (end_free_memory < start_free_memory) {
SERIAL_ECHOPGM(" end_free_memory < Heap ");
// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
// safe_delay(5); // this code can be enabled to pause the display as soon as the
// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
// idle(); // being on pin-63 which is unassigend and available on most controller
// safe_delay(20); // boards.
// while ( !READ(63))
// idle();
serial_delay(20);
#if ENABLED(M100_FREE_MEMORY_DUMPER)
M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, (const char*)0x21FF);
#endif
}
// Scan through the range looking for the biggest block of 0xE5's we can find
int block_cnt = 0;
for (int i = 0; i < n; i++) {
if (start_free_memory[i] == TEST_BYTE) {
int32_t j = count_test_bytes(start_free_memory + i);
if (j > 8) {
// SERIAL_ECHOPAIR("Found ", j);
// SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
i += j;
block_cnt++;
SERIAL_ECHOPAIR(" (", block_cnt);
SERIAL_ECHOPAIR(") found=", j);
SERIAL_ECHOLNPGM(" ");
}
}
}
SERIAL_ECHOPAIR(" block_found=", block_cnt);
if (block_cnt != 1)
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
if (block_cnt == 0) // Make sure the special case of no free blocks shows up as an
block_cnt = -1; // error to the calling code!
SERIAL_ECHOPGM(" return=");
if (block_cnt == 1) {
SERIAL_CHAR('0'); // If the block_cnt is 1, nothing has broken up the free memory
SERIAL_EOL(); // area and it is appropriate to say 'no corruption'.
return 0;
}
SERIAL_ECHOLNPGM("true");
return block_cnt;
}
/**
* M100 F
* Return the number of free bytes in the memory pool,
* with other vital statistics defining the pool.
*/
inline void free_memory_pool_report(char * const start_free_memory, const int32_t size) {
int32_t max_cnt = -1, block_cnt = 0;
char *max_addr = nullptr;
// Find the longest block of test bytes in the buffer
for (int32_t i = 0; i < size; i++) {
char *addr = start_free_memory + i;
if (*addr == TEST_BYTE) {
const int32_t j = count_test_bytes(addr);
if (j > 8) {
SERIAL_ECHOPAIR("Found ", j);
SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
if (j > max_cnt) {
max_cnt = j;
max_addr = addr;
}
i += j;
block_cnt++;
}
}
}
if (block_cnt > 1) {
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
}
SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
}
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
/**
* M100 C<num>
* Corrupt <num> locations in the free memory pool and report the corrupt addresses.
* This is useful to check the correctness of the M100 D and the M100 F commands.
*/
inline void corrupt_free_memory(char *start_free_memory, const uint32_t size) {
start_free_memory += 8;
const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
j = near_top / (size + 1);
SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
for (uint32_t i = 1; i <= size; i++) {
char * const addr = start_free_memory + i * j;
*addr = i;
SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
}
SERIAL_EOL();
}
#endif // M100_FREE_MEMORY_CORRUPTOR
/**
* M100 I
* Init memory for the M100 tests. (Automatically applied on the first M100.)
*/
inline void init_free_memory(char *start_free_memory, int32_t size) {
SERIAL_ECHOLNPGM("Initializing free memory block.\n\n");
size -= 250; // -250 to avoid interrupt activity that's altered the stack.
if (size < 0) {
SERIAL_ECHOLNPGM("Unable to initialize.\n");
return;
}
start_free_memory += 8; // move a few bytes away from the heap just because we don't want
// to be altering memory that close to it.
memset(start_free_memory, TEST_BYTE, size);
SERIAL_ECHO(size);
SERIAL_ECHOLNPGM(" bytes of memory initialized.\n");
for (int32_t i = 0; i < size; i++) {
if (start_free_memory[i] != TEST_BYTE) {
SERIAL_ECHOPAIR("? address : ", hex_address(start_free_memory + i));
SERIAL_ECHOLNPAIR("=", hex_byte(start_free_memory[i]));
SERIAL_EOL();
}
}
}
/**
* M100: Free Memory Check
*/
void GcodeSuite::M100() {
char *sp = top_of_stack();
if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
SERIAL_ECHOPAIR("\nbss_end : ", hex_address(end_bss));
if (heaplimit) SERIAL_ECHOPAIR("\n__heaplimit : ", hex_address(heaplimit));
SERIAL_ECHOPAIR("\nfree_memory_start : ", hex_address(free_memory_start));
if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit : ", hex_address(stacklimit));
SERIAL_ECHOPAIR("\nfree_memory_end : ", hex_address(free_memory_end));
if (MEMORY_END_CORRECTION) SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION: ", MEMORY_END_CORRECTION);
SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
// Always init on the first invocation of M100
static bool m100_not_initialized = true;
if (m100_not_initialized || parser.seen('I')) {
m100_not_initialized = false;
init_free_memory(free_memory_start, free_memory_end - free_memory_start);
}
#if ENABLED(M100_FREE_MEMORY_DUMPER)
if (parser.seen('D'))
return dump_free_memory(free_memory_start, free_memory_end);
#endif
if (parser.seen('F'))
return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
if (parser.seen('C'))
return corrupt_free_memory(free_memory_start, parser.value_int());
#endif
}
#endif // M100_FREE_MEMORY_WATCHER

View File

@@ -0,0 +1,36 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
#include "../gcode.h"
#include "../../module/planner.h"
#include "../../feature/closedloop.h"
void GcodeSuite::M12() {
planner.synchronize();
if (parser.seenval('S'))
set_closedloop(parser.value_int()); // Force a CLC set
}
#endif

View File

@@ -0,0 +1,102 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(BACKLASH_GCODE)
#include "../../feature/backlash.h"
#include "../../module/planner.h"
#include "../gcode.h"
/**
* M425: Enable and tune backlash correction.
*
* F<fraction> Enable/disable/fade-out backlash correction (0.0 to 1.0)
* S<smoothing_mm> Distance over which backlash correction is spread
* X<distance_mm> Set the backlash distance on X (0 to disable)
* Y<distance_mm> ... on Y
* Z<distance_mm> ... on Z
* X If a backlash measurement was done on X, copy that value
* Y ... on Y
* Z ... on Z
*
* Type M425 without any arguments to show active values.
*/
void GcodeSuite::M425() {
bool noArgs = true;
LOOP_XYZ(a) {
if (CAN_CALIBRATE(a) && parser.seen(XYZ_CHAR(a))) {
planner.synchronize();
backlash.distance_mm[a] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(AxisEnum(a));
noArgs = false;
}
}
if (parser.seen('F')) {
planner.synchronize();
backlash.set_correction(parser.value_float());
noArgs = false;
}
#ifdef BACKLASH_SMOOTHING_MM
if (parser.seen('S')) {
planner.synchronize();
backlash.smoothing_mm = parser.value_linear_units();
noArgs = false;
}
#endif
if (noArgs) {
SERIAL_ECHOPGM("Backlash Correction ");
if (!backlash.correction) SERIAL_ECHOPGM("in");
SERIAL_ECHOLNPGM("active:");
SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
SERIAL_ECHOPGM(" Backlash Distance (mm): ");
LOOP_XYZ(a) if (CAN_CALIBRATE(a)) {
SERIAL_CHAR(' ', XYZ_CHAR(a));
SERIAL_ECHO(backlash.distance_mm[a]);
SERIAL_EOL();
}
#ifdef BACKLASH_SMOOTHING_MM
SERIAL_ECHOLNPAIR(" Smoothing (mm): S", backlash.smoothing_mm);
#endif
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
SERIAL_ECHOPGM(" Average measured backlash (mm):");
if (backlash.has_any_measurement()) {
LOOP_XYZ(a) if (CAN_CALIBRATE(a) && backlash.has_measurement(AxisEnum(a))) {
SERIAL_CHAR(' ', XYZ_CHAR(a));
SERIAL_ECHO(backlash.get_measurement(AxisEnum(a)));
}
}
else
SERIAL_ECHOPGM(" (Not yet measured)");
SERIAL_EOL();
#endif
}
}
#endif // BACKLASH_GCODE

View File

@@ -0,0 +1,273 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/probe.h"
#include "../../feature/bedlevel/bedlevel.h"
#if HAS_SPI_LCD
#include "../../lcd/ultralcd.h"
#endif
#if HAS_LEVELING
#include "../../module/planner.h"
#endif
/**
* M48: Z probe repeatability measurement function.
*
* Usage:
* M48 <P#> <X#> <Y#> <V#> <E> <L#> <S>
* P = Number of sampled points (4-50, default 10)
* X = Sample X position
* Y = Sample Y position
* V = Verbose level (0-4, default=1)
* E = Engage Z probe for each reading
* L = Number of legs of movement before probe
* S = Schizoid (Or Star if you prefer)
*
* This function requires the machine to be homed before invocation.
*/
extern const char SP_Y_STR[];
void GcodeSuite::M48() {
if (axis_unhomed_error()) return;
const int8_t verbose_level = parser.byteval('V', 1);
if (!WITHIN(verbose_level, 0, 4)) {
SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4).");
return;
}
if (verbose_level > 0)
SERIAL_ECHOLNPGM("M48 Z-Probe Repeatability Test");
const int8_t n_samples = parser.byteval('P', 10);
if (!WITHIN(n_samples, 4, 50)) {
SERIAL_ECHOLNPGM("?Sample size not plausible (4-50).");
return;
}
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
xy_float_t next_pos = current_position;
const xy_pos_t probe_pos = {
parser.linearval('X', next_pos.x + probe.offset_xy.x), // If no X use the probe's current X position
parser.linearval('Y', next_pos.y + probe.offset_xy.y) // If no Y, ditto
};
if (!probe.can_reach(probe_pos)) {
SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
return;
}
bool seen_L = parser.seen('L');
uint8_t n_legs = seen_L ? parser.value_byte() : 0;
if (n_legs > 15) {
SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15).");
return;
}
if (n_legs == 1) n_legs = 2;
const bool schizoid_flag = parser.boolval('S');
if (schizoid_flag && !seen_L) n_legs = 7;
/**
* Now get everything to the specified probe point So we can safely do a
* probe to get us close to the bed. If the Z-Axis is far from the bed,
* we don't want to use that as a starting point for each probe.
*/
if (verbose_level > 2)
SERIAL_ECHOLNPGM("Positioning the probe...");
// Disable bed level correction in M48 because we want the raw data when we probe
#if HAS_LEVELING
const bool was_enabled = planner.leveling_active;
set_bed_leveling_enabled(false);
#endif
remember_feedrate_scaling_off();
float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
// Move to the first point, deploy, and probe
const float t = probe.probe_at_point(probe_pos, raise_after, verbose_level);
bool probing_good = !isnan(t);
if (probing_good) {
randomSeed(millis());
LOOP_L_N(n, n_samples) {
#if HAS_SPI_LCD
// Display M48 progress in the status bar
ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
#endif
if (n_legs) {
const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
float angle = random(0, 360);
const float radius = random(
#if ENABLED(DELTA)
int(0.1250000000 * (DELTA_PRINTABLE_RADIUS)),
int(0.3333333333 * (DELTA_PRINTABLE_RADIUS))
#else
int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
#endif
);
if (verbose_level > 3) {
SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
if (dir > 0) SERIAL_CHAR('C');
SERIAL_ECHOLNPGM("CW");
}
LOOP_L_N(l, n_legs - 1) {
float delta_angle;
if (schizoid_flag) {
// The points of a 5 point star are 72 degrees apart. We need to
// skip a point and go to the next one on the star.
delta_angle = dir * 2.0 * 72.0;
}
else {
// If we do this line, we are just trying to move further
// around the circle.
delta_angle = dir * (float) random(25, 45);
}
angle += delta_angle;
while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
// Arduino documentation says the trig functions should not be given values
while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
// numbers outside of the range, but just to be safe we clamp them.
const xy_pos_t noz_pos = probe_pos - probe.offset_xy;
next_pos.set(noz_pos.x + cos(RADIANS(angle)) * radius,
noz_pos.y + sin(RADIANS(angle)) * radius);
#if DISABLED(DELTA)
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
#else
// If we have gone out too far, we can do a simple fix and scale the numbers
// back in closer to the origin.
while (!probe.can_reach(next_pos)) {
next_pos *= 0.8f;
if (verbose_level > 3)
SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
}
#endif
if (verbose_level > 3)
SERIAL_ECHOLNPAIR_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
do_blocking_move_to_xy(next_pos);
} // n_legs loop
} // n_legs
// Probe a single point
sample_set[n] = probe.probe_at_point(probe_pos, raise_after, 0);
// Break the loop if the probe fails
probing_good = !isnan(sample_set[n]);
if (!probing_good) break;
/**
* Get the current mean for the data points we have so far
*/
float sum = 0.0;
LOOP_LE_N(j, n) sum += sample_set[j];
mean = sum / (n + 1);
NOMORE(min, sample_set[n]);
NOLESS(max, sample_set[n]);
/**
* Now, use that mean to calculate the standard deviation for the
* data points we have so far
*/
sum = 0.0;
LOOP_LE_N(j, n)
sum += sq(sample_set[j] - mean);
sigma = SQRT(sum / (n + 1));
if (verbose_level > 0) {
if (verbose_level > 1) {
SERIAL_ECHO(n + 1);
SERIAL_ECHOPAIR(" of ", int(n_samples));
SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3);
if (verbose_level > 2) {
SERIAL_ECHOPAIR_F(" mean: ", mean, 4);
SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6);
SERIAL_ECHOPAIR_F(" min: ", min, 3);
SERIAL_ECHOPAIR_F(" max: ", max, 3);
SERIAL_ECHOPAIR_F(" range: ", max-min, 3);
}
SERIAL_EOL();
}
}
} // n_samples loop
}
probe.stow();
if (probing_good) {
SERIAL_ECHOLNPGM("Finished!");
if (verbose_level > 0) {
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3);
}
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
SERIAL_EOL();
#if HAS_SPI_LCD
// Display M48 results in the status bar
char sigma_str[8];
ui.status_printf_P(0, PSTR(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
#endif
}
restore_feedrate_and_scaling();
// Re-enable bed level correction if it had been on
#if HAS_LEVELING
set_bed_leveling_enabled(was_enabled);
#endif
report_current_position();
}
#endif // Z_MIN_PROBE_REPEATABILITY_TEST

View File

@@ -0,0 +1,106 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if IS_KINEMATIC
#include "../gcode.h"
#include "../../module/motion.h"
#if ENABLED(DELTA)
#include "../../module/delta.h"
/**
* M665: Set delta configurations
*
* H = delta height
* L = diagonal rod
* R = delta radius
* S = segments per second
* X = Alpha (Tower 1) angle trim
* Y = Beta (Tower 2) angle trim
* Z = Gamma (Tower 3) angle trim
*/
void GcodeSuite::M665() {
if (parser.seen('H')) delta_height = parser.value_linear_units();
if (parser.seen('L')) delta_diagonal_rod = parser.value_linear_units();
if (parser.seen('R')) delta_radius = parser.value_linear_units();
if (parser.seen('S')) delta_segments_per_second = parser.value_float();
if (parser.seen('X')) delta_tower_angle_trim.a = parser.value_float();
if (parser.seen('Y')) delta_tower_angle_trim.b = parser.value_float();
if (parser.seen('Z')) delta_tower_angle_trim.c = parser.value_float();
recalc_delta_settings();
}
#elif IS_SCARA
#include "../../module/scara.h"
/**
* M665: Set SCARA settings
*
* Parameters:
*
* S[segments-per-second] - Segments-per-second
* P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle
* T[theta-offset] - Theta offset, added to the elbow (B/Y) angle
* Z[z-offset] - Z offset, added to Z
*
* A, P, and X are all aliases for the shoulder angle
* B, T, and Y are all aliases for the elbow angle
*/
void GcodeSuite::M665() {
if (parser.seenval('S')) delta_segments_per_second = parser.value_float();
#if HAS_SCARA_OFFSET
if (parser.seenval('Z')) scara_home_offset.z = parser.value_linear_units();
const bool hasA = parser.seenval('A'), hasP = parser.seenval('P'), hasX = parser.seenval('X');
const uint8_t sumAPX = hasA + hasP + hasX;
if (sumAPX) {
if (sumAPX == 1)
scara_home_offset.a = parser.value_float();
else {
SERIAL_ERROR_MSG("Only one of A, P, or X is allowed.");
return;
}
}
const bool hasB = parser.seenval('B'), hasT = parser.seenval('T'), hasY = parser.seenval('Y');
const uint8_t sumBTY = hasB + hasT + hasY;
if (sumBTY) {
if (sumBTY == 1)
scara_home_offset.b = parser.value_float();
else {
SERIAL_ERROR_MSG("Only one of B, T, or Y is allowed.");
return;
}
}
#endif // HAS_SCARA_OFFSET
}
#endif
#endif // IS_KINEMATIC

View File

@@ -0,0 +1,106 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(DELTA) || HAS_EXTRA_ENDSTOPS
#include "../gcode.h"
#if ENABLED(DELTA)
#include "../../module/delta.h"
#include "../../module/motion.h"
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
/**
* M666: Set delta endstop adjustment
*/
void GcodeSuite::M666() {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(">>> M666");
LOOP_XYZ(i) {
if (parser.seen(XYZ_CHAR(i))) {
const float v = parser.value_linear_units();
if (v * Z_HOME_DIR <= 0) delta_endstop_adj[i] = v;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("delta_endstop_adj[", XYZ_CHAR(i), "] = ", delta_endstop_adj[i]);
}
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< M666");
}
#elif HAS_EXTRA_ENDSTOPS
#include "../../module/endstops.h"
/**
* M666: Set Dual Endstops offsets for X, Y, and/or Z.
* With no parameters report current offsets.
*
* For Triple / Quad Z Endstops:
* Set Z2 Only: M666 S2 Z<offset>
* Set Z3 Only: M666 S3 Z<offset>
* Set Z4 Only: M666 S4 Z<offset>
* Set All: M666 Z<offset>
*/
void GcodeSuite::M666() {
#if ENABLED(X_DUAL_ENDSTOPS)
if (parser.seenval('X')) endstops.x2_endstop_adj = parser.value_linear_units();
#endif
#if ENABLED(Y_DUAL_ENDSTOPS)
if (parser.seenval('Y')) endstops.y2_endstop_adj = parser.value_linear_units();
#endif
#if ENABLED(Z_MULTI_ENDSTOPS)
if (parser.seenval('Z')) {
#if NUM_Z_STEPPER_DRIVERS >= 3
const float z_adj = parser.value_linear_units();
const int ind = parser.intval('S');
if (!ind || ind == 2) endstops.z2_endstop_adj = z_adj;
if (!ind || ind == 3) endstops.z3_endstop_adj = z_adj;
#if NUM_Z_STEPPER_DRIVERS >= 4
if (!ind || ind == 4) endstops.z4_endstop_adj = z_adj;
#endif
#else
endstops.z2_endstop_adj = parser.value_linear_units();
#endif
}
#endif
if (!parser.seen("XYZ")) {
SERIAL_ECHOPGM("Dual Endstop Adjustment (mm): ");
#if ENABLED(X_DUAL_ENDSTOPS)
SERIAL_ECHOPAIR(" X2:", endstops.x2_endstop_adj);
#endif
#if ENABLED(Y_DUAL_ENDSTOPS)
SERIAL_ECHOPAIR(" Y2:", endstops.y2_endstop_adj);
#endif
#if ENABLED(Z_MULTI_ENDSTOPS)
#define _ECHO_ZADJ(N) SERIAL_ECHOPAIR(" Z" STRINGIFY(N) ":", endstops.z##N##_endstop_adj);
REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _ECHO_ZADJ)
#endif
SERIAL_EOL();
}
}
#endif // HAS_EXTRA_ENDSTOPS
#endif // DELTA || HAS_EXTRA_ENDSTOPS

View File

@@ -0,0 +1,106 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(SKEW_CORRECTION_GCODE)
#include "../gcode.h"
#include "../../module/planner.h"
/**
* M852: Get or set the machine skew factors. Reports current values with no arguments.
*
* S[xy_factor] - Alias for 'I'
* I[xy_factor] - New XY skew factor
* J[xz_factor] - New XZ skew factor
* K[yz_factor] - New YZ skew factor
*/
void GcodeSuite::M852() {
uint8_t ijk = 0, badval = 0, setval = 0;
if (parser.seen('I') || parser.seen('S')) {
++ijk;
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.xy != value) {
planner.skew_factor.xy = value;
++setval;
}
}
else
++badval;
}
#if ENABLED(SKEW_CORRECTION_FOR_Z)
if (parser.seen('J')) {
++ijk;
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.xz != value) {
planner.skew_factor.xz = value;
++setval;
}
}
else
++badval;
}
if (parser.seen('K')) {
++ijk;
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.yz != value) {
planner.skew_factor.yz = value;
++setval;
}
}
else
++badval;
}
#endif
if (badval)
SERIAL_ECHOLNPGM(STR_SKEW_MIN " " STRINGIFY(SKEW_FACTOR_MIN) " " STR_SKEW_MAX " " STRINGIFY(SKEW_FACTOR_MAX));
// When skew is changed the current position changes
if (setval) {
set_current_from_steppers_for_axis(ALL_AXES);
sync_plan_position();
report_current_position();
}
if (!ijk) {
SERIAL_ECHO_START();
serialprintPGM(GET_TEXT(MSG_SKEW_FACTOR));
SERIAL_ECHOPAIR_F(" XY: ", planner.skew_factor.xy, 6);
#if ENABLED(SKEW_CORRECTION_FOR_Z)
SERIAL_ECHOPAIR_F(" XZ: ", planner.skew_factor.xz, 6);
SERIAL_ECHOPAIR_F(" YZ: ", planner.skew_factor.yz, 6);
#endif
SERIAL_EOL();
}
}
#endif // SKEW_CORRECTION_GCODE

View File

@@ -0,0 +1,167 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h"
#include "../../module/planner.h"
#if DISABLED(NO_VOLUMETRICS)
/**
* M200: Set filament diameter and set E axis units to cubic units
*
* T<extruder> - Optional extruder number. Current extruder if omitted.
* D<linear> - Diameter of the filament. Use "D0" to switch back to linear units on the E axis.
*/
void GcodeSuite::M200() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
if (parser.seen('D')) {
// setting any extruder filament size disables volumetric on the assumption that
// slicers either generate in extruder values as cubic mm or as as filament feeds
// for all extruders
const float dval = parser.value_linear_units();
if ( (parser.volumetric_enabled = (dval != 0)) )
planner.set_filament_size(target_extruder, dval);
}
planner.calculate_volumetric_multipliers();
}
#endif // !NO_VOLUMETRICS
/**
* M201: Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000)
*
* With multiple extruders use T to specify which one.
*/
void GcodeSuite::M201() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
LOOP_XYZE(i) {
if (parser.seen(axis_codes[i])) {
const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
planner.set_max_acceleration(a, parser.value_axis_units((AxisEnum)a));
}
}
}
/**
* M203: Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in units/sec
*
* With multiple extruders use T to specify which one.
*/
void GcodeSuite::M203() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
LOOP_XYZE(i)
if (parser.seen(axis_codes[i])) {
const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
planner.set_max_feedrate(a, parser.value_axis_units((AxisEnum)a));
}
}
/**
* M204: Set Accelerations in units/sec^2 (M204 P1200 R3000 T3000)
*
* P = Printing moves
* R = Retract only (no X, Y, Z) moves
* T = Travel (non printing) moves
*/
void GcodeSuite::M204() {
if (!parser.seen("PRST")) {
SERIAL_ECHOPAIR("Acceleration: P", planner.settings.acceleration);
SERIAL_ECHOPAIR(" R", planner.settings.retract_acceleration);
SERIAL_ECHOLNPAIR_P(SP_T_STR, planner.settings.travel_acceleration);
}
else {
//planner.synchronize();
// 'S' for legacy compatibility. Should NOT BE USED for new development
if (parser.seenval('S')) planner.settings.travel_acceleration = planner.settings.acceleration = parser.value_linear_units();
if (parser.seenval('P')) planner.settings.acceleration = parser.value_linear_units();
if (parser.seenval('R')) planner.settings.retract_acceleration = parser.value_linear_units();
if (parser.seenval('T')) planner.settings.travel_acceleration = parser.value_linear_units();
}
}
/**
* M205: Set Advanced Settings
*
* B = Min Segment Time (µs)
* S = Min Feed Rate (units/s)
* T = Min Travel Feed Rate (units/s)
* X = Max X Jerk (units/sec^2)
* Y = Max Y Jerk (units/sec^2)
* Z = Max Z Jerk (units/sec^2)
* E = Max E Jerk (units/sec^2)
* J = Junction Deviation (mm) (If not using CLASSIC_JERK)
*/
void GcodeSuite::M205() {
#if DISABLED(CLASSIC_JERK)
#define J_PARAM "J"
#else
#define J_PARAM
#endif
#if HAS_CLASSIC_JERK
#define XYZE_PARAM "XYZE"
#else
#define XYZE_PARAM
#endif
if (!parser.seen("BST" J_PARAM XYZE_PARAM)) return;
//planner.synchronize();
if (parser.seen('B')) planner.settings.min_segment_time_us = parser.value_ulong();
if (parser.seen('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units();
if (parser.seen('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
#if DISABLED(CLASSIC_JERK)
if (parser.seen('J')) {
const float junc_dev = parser.value_linear_units();
if (WITHIN(junc_dev, 0.01f, 0.3f)) {
planner.junction_deviation_mm = junc_dev;
#if ENABLED(LIN_ADVANCE)
planner.recalculate_max_e_jerk();
#endif
}
else
SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)");
}
#endif
#if HAS_CLASSIC_JERK
if (parser.seen('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units());
if (parser.seen('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units());
if (parser.seen('Z')) {
planner.set_max_jerk(Z_AXIS, parser.value_linear_units());
#if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
if (planner.max_jerk.z <= 0.1f)
SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
#endif
}
#if HAS_CLASSIC_E_JERK
if (parser.seen('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units());
#endif
#endif
}

View File

@@ -0,0 +1,105 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if EXTRUDERS > 1
#include "../gcode.h"
#include "../../module/tool_change.h"
#include "../../MarlinCore.h" // for SP_X_STR, etc.
extern const char SP_X_STR[], SP_Y_STR[], SP_Z_STR[];
void M217_report(const bool eeprom=false) {
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
serialprintPGM(eeprom ? PSTR(" M217") : PSTR("Toolchange:"));
SERIAL_ECHOPAIR(" S", LINEAR_UNIT(toolchange_settings.swap_length));
SERIAL_ECHOPAIR_P(SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime));
SERIAL_ECHOPAIR_P(SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed));
SERIAL_ECHOPAIR(" R", LINEAR_UNIT(toolchange_settings.retract_speed));
#if ENABLED(TOOLCHANGE_PARK)
SERIAL_ECHOPAIR_P(SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x));
SERIAL_ECHOPAIR_P(SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y));
#endif
#else
UNUSED(eeprom);
#endif
SERIAL_ECHOPAIR_P(SP_Z_STR, LINEAR_UNIT(toolchange_settings.z_raise));
SERIAL_EOL();
}
/**
* M217 - Set SINGLENOZZLE toolchange parameters
*
* S[linear] Swap length
* E[linear] Purge length
* P[linear/m] Prime speed
* R[linear/m] Retract speed
* X[linear] Park X (Requires TOOLCHANGE_PARK)
* Y[linear] Park Y (Requires TOOLCHANGE_PARK)
* Z[linear] Z Raise
*/
void GcodeSuite::M217() {
#define SPR_PARAM
#define XY_PARAM
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
#undef SPR_PARAM
#define SPR_PARAM "SPRE"
static constexpr float max_extrude =
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
EXTRUDE_MAXLENGTH
#else
500
#endif
;
if (parser.seenval('S')) { const float v = parser.value_linear_units(); toolchange_settings.swap_length = constrain(v, 0, max_extrude); }
if (parser.seenval('E')) { const float v = parser.value_linear_units(); toolchange_settings.extra_prime = constrain(v, 0, max_extrude); }
if (parser.seenval('P')) { const int16_t v = parser.value_linear_units(); toolchange_settings.prime_speed = constrain(v, 10, 5400); }
if (parser.seenval('R')) { const int16_t v = parser.value_linear_units(); toolchange_settings.retract_speed = constrain(v, 10, 5400); }
#endif
#if ENABLED(TOOLCHANGE_PARK)
#undef XY_PARAM
#define XY_PARAM "XY"
if (parser.seenval('X')) { toolchange_settings.change_point.x = parser.value_linear_units(); }
if (parser.seenval('Y')) { toolchange_settings.change_point.y = parser.value_linear_units(); }
#endif
if (parser.seenval('Z')) { toolchange_settings.z_raise = parser.value_linear_units(); }
if (!parser.seen(SPR_PARAM XY_PARAM "Z")) M217_report();
}
#endif // EXTRUDERS > 1

View File

@@ -0,0 +1,71 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_HOTEND_OFFSET
#include "../gcode.h"
#include "../../module/motion.h"
#if ENABLED(DELTA)
#include "../../module/planner.h"
#endif
/**
* M218 - set hotend offset (in linear units)
*
* T<tool>
* X<xoffset>
* Y<yoffset>
* Z<zoffset>
*/
void GcodeSuite::M218() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
if (parser.seenval('X')) hotend_offset[target_extruder].x = parser.value_linear_units();
if (parser.seenval('Y')) hotend_offset[target_extruder].y = parser.value_linear_units();
if (parser.seenval('Z')) hotend_offset[target_extruder].z = parser.value_linear_units();
if (!parser.seen("XYZ")) {
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_HOTEND_OFFSET);
HOTEND_LOOP() {
SERIAL_CHAR(' ');
SERIAL_ECHO(hotend_offset[e].x);
SERIAL_CHAR(',');
SERIAL_ECHO(hotend_offset[e].y);
SERIAL_CHAR(',');
SERIAL_ECHO_F(hotend_offset[e].z, 3);
}
SERIAL_EOL();
}
#if ENABLED(DELTA)
if (target_extruder == active_extruder)
do_blocking_move_to_xy(current_position, planner.settings.max_feedrate_mm_s[X_AXIS]);
#endif
}
#endif // HAS_HOTEND_OFFSET

View File

@@ -0,0 +1,53 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/motion.h"
/**
* M220: Set speed percentage factor, aka "Feed Rate"
*
* Parameters
* S<percent> : Set the feed rate percentage factor
*
* Report the current speed percentage factor if no parameter is specified
*
* With PRUSA_MMU2...
* B : Flag to back up the current factor
* R : Flag to restore the last-saved factor
*/
void GcodeSuite::M220() {
#if ENABLED(PRUSA_MMU2)
static int16_t backup_feedrate_percentage = 100;
if (parser.seen('B')) backup_feedrate_percentage = feedrate_percentage;
if (parser.seen('R')) feedrate_percentage = backup_feedrate_percentage;
#endif
if (parser.seenval('S')) feedrate_percentage = parser.value_int();
if (!parser.seen_any()) {
SERIAL_ECHOPAIR("FR:", feedrate_percentage);
SERIAL_CHAR('%');
SERIAL_EOL();
}
}

View File

@@ -0,0 +1,49 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/planner.h"
#if EXTRUDERS
/**
* M221: Set extrusion percentage (M221 T0 S95)
*/
void GcodeSuite::M221() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
if (parser.seenval('S')) {
planner.flow_percentage[target_extruder] = parser.value_int();
planner.refresh_e_factor(target_extruder);
}
else {
SERIAL_ECHO_START();
SERIAL_CHAR('E', '0' + target_extruder);
SERIAL_ECHOPAIR(" Flow: ", planner.flow_percentage[target_extruder]);
SERIAL_CHAR('%');
SERIAL_EOL();
}
}
#endif // EXTRUDERS

View File

@@ -0,0 +1,61 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(EDITABLE_SERVO_ANGLES)
#include "../gcode.h"
#include "../../module/servo.h"
void GcodeSuite::M281() {
if (!parser.seenval('P')) return;
const int servo_index = parser.value_int();
if (WITHIN(servo_index, 0, NUM_SERVOS - 1)) {
#if ENABLED(BLTOUCH)
if (servo_index == Z_PROBE_SERVO_NR) {
SERIAL_ERROR_MSG("BLTouch angles can't be changed.");
return;
}
#endif
bool angle_change = false;
if (parser.seen('L')) {
servo_angles[servo_index][0] = parser.value_int();
angle_change = true;
}
if (parser.seen('U')) {
servo_angles[servo_index][1] = parser.value_int();
angle_change = true;
}
if (!angle_change) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Servo ", servo_index,
" L", servo_angles[servo_index][0],
" U", servo_angles[servo_index][1]);
}
}
else {
SERIAL_ERROR_START();
SERIAL_ECHOLNPAIR("Servo ", servo_index, " out of range");
}
}
#endif // EDITABLE_SERVO_ANGLES

View File

@@ -0,0 +1,88 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(PIDTEMP)
#include "../gcode.h"
#include "../../module/temperature.h"
/**
* M301: Set PID parameters P I D (and optionally C, L)
*
* P[float] Kp term
* I[float] Ki term (unscaled)
* D[float] Kd term (unscaled)
*
* With PID_EXTRUSION_SCALING:
*
* C[float] Kc term
* L[int] LPQ length
*
* With PID_FAN_SCALING:
*
* F[float] Kf term
*/
void GcodeSuite::M301() {
// multi-extruder PID patch: M301 updates or prints a single extruder's PID values
// default behavior (omitting E parameter) is to update for extruder 0 only
const uint8_t e = parser.byteval('E'); // extruder being updated
if (e < HOTENDS) { // catch bad input value
if (parser.seen('P')) PID_PARAM(Kp, e) = parser.value_float();
if (parser.seen('I')) PID_PARAM(Ki, e) = scalePID_i(parser.value_float());
if (parser.seen('D')) PID_PARAM(Kd, e) = scalePID_d(parser.value_float());
#if ENABLED(PID_EXTRUSION_SCALING)
if (parser.seen('C')) PID_PARAM(Kc, e) = parser.value_float();
if (parser.seenval('L')) thermalManager.lpq_len = parser.value_int();
NOMORE(thermalManager.lpq_len, LPQ_MAX_LEN);
NOLESS(thermalManager.lpq_len, 0);
#endif
#if ENABLED(PID_FAN_SCALING)
if (parser.seen('F')) PID_PARAM(Kf, e) = parser.value_float();
#endif
thermalManager.updatePID();
SERIAL_ECHO_START();
#if ENABLED(PID_PARAMS_PER_HOTEND)
SERIAL_ECHOPAIR(" e:", e); // specify extruder in serial output
#endif
SERIAL_ECHOPAIR(" p:", PID_PARAM(Kp, e),
" i:", unscalePID_i(PID_PARAM(Ki, e)),
" d:", unscalePID_d(PID_PARAM(Kd, e)));
#if ENABLED(PID_EXTRUSION_SCALING)
SERIAL_ECHOPAIR(" c:", PID_PARAM(Kc, e));
#endif
#if ENABLED(PID_FAN_SCALING)
SERIAL_ECHOPAIR(" f:", PID_PARAM(Kf, e));
#endif
SERIAL_EOL();
}
else
SERIAL_ERROR_MSG(STR_INVALID_EXTRUDER);
}
#endif // PIDTEMP

View File

@@ -0,0 +1,63 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(PREVENT_COLD_EXTRUSION)
#include "../gcode.h"
#include "../../module/temperature.h"
/**
* M302: Allow cold extrudes, or set the minimum extrude temperature
*
* S<temperature> sets the minimum extrude temperature
* P<bool> enables (1) or disables (0) cold extrusion
*
* Examples:
*
* M302 ; report current cold extrusion state
* M302 P0 ; enable cold extrusion checking
* M302 P1 ; disables cold extrusion checking
* M302 S0 ; always allow extrusion (disables checking)
* M302 S170 ; only allow extrusion above 170
* M302 S170 P1 ; set min extrude temp to 170 but leave disabled
*/
void GcodeSuite::M302() {
const bool seen_S = parser.seen('S');
if (seen_S) {
thermalManager.extrude_min_temp = parser.value_celsius();
thermalManager.allow_cold_extrude = (thermalManager.extrude_min_temp == 0);
}
if (parser.seen('P'))
thermalManager.allow_cold_extrude = (thermalManager.extrude_min_temp == 0) || parser.value_bool();
else if (!seen_S) {
// Report current state
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Cold extrudes are ");
serialprintPGM(thermalManager.allow_cold_extrude ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOLNPAIR("abled (min temp ", thermalManager.extrude_min_temp, "C)");
}
}
#endif // PREVENT_COLD_EXTRUSION

View File

@@ -0,0 +1,48 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(PIDTEMPBED)
#include "../gcode.h"
#include "../../module/temperature.h"
/**
* M304 - Set and/or Report the current Bed PID values
*
* P<pval> - Set the P value
* I<ival> - Set the I value
* D<dval> - Set the D value
*/
void GcodeSuite::M304() {
if (parser.seen('P')) thermalManager.temp_bed.pid.Kp = parser.value_float();
if (parser.seen('I')) thermalManager.temp_bed.pid.Ki = scalePID_i(parser.value_float());
if (parser.seen('D')) thermalManager.temp_bed.pid.Kd = scalePID_d(parser.value_float());
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" p:", thermalManager.temp_bed.pid.Kp,
" i:", unscalePID_i(thermalManager.temp_bed.pid.Ki),
" d:", unscalePID_d(thermalManager.temp_bed.pid.Kd));
}
#endif // PIDTEMPBED

View File

@@ -0,0 +1,81 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_USER_THERMISTORS
#include "../gcode.h"
#include "../../module/temperature.h"
/**
* M305: Set (or report) custom thermistor parameters
*
* P[index] Thermistor table index
* R[ohms] Pullup resistor value
* T[ohms] Resistance at 25C
* B[beta] Thermistor "beta" value
* C[coeff] Steinhart-Hart Coefficient 'C'
*
* Format: M305 P[tbl_index] R[pullup_resistor_val] T[therm_25C_resistance] B[therm_beta] C[Steinhart_Hart_C_coeff]
*
* Examples: M305 P0 R4700 T100000 B3950 C0.0
* M305 P0 R4700
* M305 P0 T100000
* M305 P0 B3950
* M305 P0 C0.0
*/
void GcodeSuite::M305() {
const int8_t t_index = parser.intval('P', -1);
const bool do_set = parser.seen("BCRT");
// A valid P index is required
if (t_index >= (USER_THERMISTORS) || (do_set && t_index < 0)) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR("!Invalid index. (0 <= P <= ", int(USER_THERMISTORS - 1), ")");
}
else if (do_set) {
if (parser.seen('R')) // Pullup resistor value
if (!thermalManager.set_pull_up_res(t_index, parser.value_float()))
SERIAL_ECHO_MSG("!Invalid series resistance. (0 < R < 1000000)");
if (parser.seen('T')) // Resistance at 25C
if (!thermalManager.set_res25(t_index, parser.value_float()))
SERIAL_ECHO_MSG("!Invalid 25C resistance. (0 < T < 10000000)");
if (parser.seen('B')) // Beta value
if (!thermalManager.set_beta(t_index, parser.value_float()))
SERIAL_ECHO_MSG("!Invalid beta. (0 < B < 1000000)");
if (parser.seen('C')) // Steinhart-Hart C coefficient
if (!thermalManager.set_sh_coeff(t_index, parser.value_float()))
SERIAL_ECHO_MSG("!Invalid Steinhart-Hart C coeff. (-0.01 < C < +0.01)");
} // If not setting then report parameters
else if (t_index < 0) { // ...all user thermistors
LOOP_L_N(i, USER_THERMISTORS)
thermalManager.log_user_thermistor(i);
}
else // ...one user thermistor
thermalManager.log_user_thermistor(t_index);
}
#endif // HAS_USER_THERMISTORS

View File

@@ -0,0 +1,385 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(PINS_DEBUGGING)
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected
#include "../../pins/pinsDebug.h"
#include "../../module/endstops.h"
#if HAS_Z_SERVO_PROBE
#include "../../module/probe.h"
#include "../../module/servo.h"
#endif
#if ENABLED(BLTOUCH)
#include "../../feature/bltouch.h"
#endif
#if ENABLED(HOST_PROMPT_SUPPORT)
#include "../../feature/host_actions.h"
#endif
#if ENABLED(EXTENSIBLE_UI)
#include "../../lcd/extui/ui_api.h"
#endif
#ifndef GET_PIN_MAP_PIN_M43
#define GET_PIN_MAP_PIN_M43(Q) GET_PIN_MAP_PIN(Q)
#endif
inline void toggle_pins() {
const bool ignore_protection = parser.boolval('I');
const int repeat = parser.intval('R', 1),
start = PARSED_PIN_INDEX('S', 0),
end = PARSED_PIN_INDEX('L', NUM_DIGITAL_PINS - 1),
wait = parser.intval('W', 500);
LOOP_S_LE_N(i, start, end) {
pin_t pin = GET_PIN_MAP_PIN_M43(i);
if (!VALID_PIN(pin)) continue;
if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) {
report_pin_state_extended(pin, ignore_protection, true, PSTR("Untouched "));
SERIAL_EOL();
}
else {
watchdog_refresh();
report_pin_state_extended(pin, ignore_protection, true, PSTR("Pulsing "));
#ifdef __STM32F1__
const auto prior_mode = _GET_MODE(i);
#else
const bool prior_mode = GET_PINMODE(pin);
#endif
#if AVR_AT90USB1286_FAMILY // Teensy IDEs don't know about these pins so must use FASTIO
if (pin == TEENSY_E2) {
SET_OUTPUT(TEENSY_E2);
for (int16_t j = 0; j < repeat; j++) {
WRITE(TEENSY_E2, LOW); safe_delay(wait);
WRITE(TEENSY_E2, HIGH); safe_delay(wait);
WRITE(TEENSY_E2, LOW); safe_delay(wait);
}
}
else if (pin == TEENSY_E3) {
SET_OUTPUT(TEENSY_E3);
for (int16_t j = 0; j < repeat; j++) {
WRITE(TEENSY_E3, LOW); safe_delay(wait);
WRITE(TEENSY_E3, HIGH); safe_delay(wait);
WRITE(TEENSY_E3, LOW); safe_delay(wait);
}
}
else
#endif
{
pinMode(pin, OUTPUT);
for (int16_t j = 0; j < repeat; j++) {
watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait);
watchdog_refresh(); extDigitalWrite(pin, 1); safe_delay(wait);
watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait);
watchdog_refresh();
}
}
#ifdef __STM32F1__
_SET_MODE(i, prior_mode);
#else
pinMode(pin, prior_mode);
#endif
}
SERIAL_EOL();
}
SERIAL_ECHOLNPGM("Done.");
} // toggle_pins
inline void servo_probe_test() {
#if !(NUM_SERVOS > 0 && HAS_SERVO_0)
SERIAL_ERROR_MSG("SERVO not set up.");
#elif !HAS_Z_SERVO_PROBE
SERIAL_ERROR_MSG("Z_PROBE_SERVO_NR not set up.");
#else // HAS_Z_SERVO_PROBE
const uint8_t probe_index = parser.byteval('P', Z_PROBE_SERVO_NR);
SERIAL_ECHOLNPAIR("Servo probe test\n"
". using index: ", int(probe_index),
", deploy angle: ", servo_angles[probe_index][0],
", stow angle: ", servo_angles[probe_index][1]
);
bool deploy_state = false, stow_state;
#if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
#define PROBE_TEST_PIN Z_MIN_PIN
constexpr bool probe_inverting = Z_MIN_ENDSTOP_INVERTING;
SERIAL_ECHOLNPAIR(". Probe Z_MIN_PIN: ", int(PROBE_TEST_PIN));
SERIAL_ECHOPGM(". Z_MIN_ENDSTOP_INVERTING: ");
#else
#define PROBE_TEST_PIN Z_MIN_PROBE_PIN
constexpr bool probe_inverting = Z_MIN_PROBE_ENDSTOP_INVERTING;
SERIAL_ECHOLNPAIR(". Probe Z_MIN_PROBE_PIN: ", int(PROBE_TEST_PIN));
SERIAL_ECHOPGM( ". Z_MIN_PROBE_ENDSTOP_INVERTING: ");
#endif
serialprint_truefalse(probe_inverting);
SERIAL_EOL();
SET_INPUT_PULLUP(PROBE_TEST_PIN);
// First, check for a probe that recognizes an advanced BLTouch sequence.
// In addition to STOW and DEPLOY, it uses SW MODE (and RESET in the beginning)
// to see if this is one of the following: BLTOUCH Classic 1.2, 1.3, or
// BLTouch Smart 1.0, 2.0, 2.2, 3.0, 3.1. But only if the user has actually
// configured a BLTouch as being present. If the user has not configured this,
// the BLTouch will be detected in the last phase of these tests (see further on).
bool blt = false;
// This code will try to detect a BLTouch probe or clone
#if ENABLED(BLTOUCH)
SERIAL_ECHOLNPGM(". Check for BLTOUCH");
bltouch._reset();
bltouch._stow();
if (probe_inverting == READ(PROBE_TEST_PIN)) {
bltouch._set_SW_mode();
if (probe_inverting != READ(PROBE_TEST_PIN)) {
bltouch._deploy();
if (probe_inverting == READ(PROBE_TEST_PIN)) {
bltouch._stow();
SERIAL_ECHOLNPGM("= BLTouch Classic 1.2, 1.3, Smart 1.0, 2.0, 2.2, 3.0, 3.1 detected.");
// Check for a 3.1 by letting the user trigger it, later
blt = true;
}
}
}
#endif
// The following code is common to all kinds of servo probes.
// Since it could be a real servo or a BLTouch (any kind) or a clone,
// use only "common" functions - i.e. SERVO_MOVE. No bltouch.xxxx stuff.
// If it is already recognised as a being a BLTouch, no need for this test
if (!blt) {
// DEPLOY and STOW 4 times and see if the signal follows
// Then it is a mechanical switch
uint8_t i = 0;
SERIAL_ECHOLNPGM(". Deploy & stow 4 times");
do {
MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy
safe_delay(500);
deploy_state = READ(PROBE_TEST_PIN);
MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][1]); // Stow
safe_delay(500);
stow_state = READ(PROBE_TEST_PIN);
} while (++i < 4);
if (probe_inverting != deploy_state) SERIAL_ECHOLNPGM("WARNING: INVERTING setting probably backwards.");
if (deploy_state != stow_state) {
SERIAL_ECHOLNPGM("= Mechanical Switch detected");
if (deploy_state) {
SERIAL_ECHOLNPAIR(" DEPLOYED state: HIGH (logic 1)",
" STOWED (triggered) state: LOW (logic 0)");
}
else {
SERIAL_ECHOLNPAIR(" DEPLOYED state: LOW (logic 0)",
" STOWED (triggered) state: HIGH (logic 1)");
}
#if ENABLED(BLTOUCH)
SERIAL_ECHOLNPGM("FAIL: BLTOUCH enabled - Set up this device as a Servo Probe with INVERTING set to 'true'.");
#endif
return;
}
}
// Ask the user for a trigger event and measure the pulse width.
MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy
safe_delay(500);
SERIAL_ECHOLNPGM("** Please trigger probe within 30 sec **");
uint16_t probe_counter = 0;
// Wait 30 seconds for user to trigger probe
for (uint16_t j = 0; j < 500 * 30 && probe_counter == 0 ; j++) {
safe_delay(2);
if (0 == j % (500 * 1)) gcode.reset_stepper_timeout(); // Keep steppers powered
if (deploy_state != READ(PROBE_TEST_PIN)) { // probe triggered
for (probe_counter = 0; probe_counter < 15 && deploy_state != READ(PROBE_TEST_PIN); ++probe_counter) safe_delay(2);
SERIAL_ECHOPGM(". Pulse width");
if (probe_counter == 15)
SERIAL_ECHOLNPGM(": 30ms or more");
else
SERIAL_ECHOLNPAIR(" (+/- 4ms): ", probe_counter * 2);
if (probe_counter >= 4) {
if (probe_counter == 15) {
if (blt) SERIAL_ECHOPGM("= BLTouch V3.1");
else SERIAL_ECHOPGM("= Z Servo Probe");
}
else SERIAL_ECHOPGM("= BLTouch pre V3.1 (or compatible)");
SERIAL_ECHOLNPGM(" detected.");
}
else SERIAL_ECHOLNPGM("FAIL: Noise detected - please re-run test");
MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][1]); // Stow
return;
}
}
if (!probe_counter) SERIAL_ECHOLNPGM("FAIL: No trigger detected");
#endif // HAS_Z_SERVO_PROBE
} // servo_probe_test
/**
* M43: Pin debug - report pin state, watch pins, toggle pins and servo probe test/report
*
* M43 - report name and state of pin(s)
* P<pin> Pin to read or watch. If omitted, reads all pins.
* I Flag to ignore Marlin's pin protection.
*
* M43 W - Watch pins -reporting changes- until reset, click, or M108.
* P<pin> Pin to read or watch. If omitted, read/watch all pins.
* I Flag to ignore Marlin's pin protection.
*
* M43 E<bool> - Enable / disable background endstop monitoring
* - Machine continues to operate
* - Reports changes to endstops
* - Toggles LED_PIN when an endstop changes
* - Cannot reliably catch the 5mS pulse from BLTouch type probes
*
* M43 T - Toggle pin(s) and report which pin is being toggled
* S<pin> - Start Pin number. If not given, will default to 0
* L<pin> - End Pin number. If not given, will default to last pin defined for this board
* I<bool> - Flag to ignore Marlin's pin protection. Use with caution!!!!
* R - Repeat pulses on each pin this number of times before continueing to next pin
* W - Wait time (in miliseconds) between pulses. If not given will default to 500
*
* M43 S - Servo probe test
* P<index> - Probe index (optional - defaults to 0
*/
void GcodeSuite::M43() {
// 'T' must be first. It uses 'S' and 'E' differently.
if (parser.seen('T')) return toggle_pins();
// 'E' Enable or disable endstop monitoring and return
if (parser.seen('E')) {
endstops.monitor_flag = parser.value_bool();
SERIAL_ECHOPGM("endstop monitor ");
serialprintPGM(endstops.monitor_flag ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOLNPGM("abled");
return;
}
// 'S' Run servo probe test and return
if (parser.seen('S')) return servo_probe_test();
// 'P' Get the range of pins to test or watch
uint8_t first_pin = PARSED_PIN_INDEX('P', 0),
last_pin = parser.seenval('P') ? first_pin : NUMBER_PINS_TOTAL - 1;
if (first_pin > last_pin) return;
// 'I' to ignore protected pins
const bool ignore_protection = parser.boolval('I');
// 'W' Watch until click, M108, or reset
if (parser.boolval('W')) {
SERIAL_ECHOLNPGM("Watching pins");
#ifdef ARDUINO_ARCH_SAM
NOLESS(first_pin, 2); // Don't hijack the UART pins
#endif
uint8_t pin_state[last_pin - first_pin + 1];
LOOP_S_LE_N(i, first_pin, last_pin) {
pin_t pin = GET_PIN_MAP_PIN_M43(i);
if (!VALID_PIN(pin)) continue;
if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) continue;
pinMode(pin, INPUT_PULLUP);
delay(1);
/*
if (IS_ANALOG(pin))
pin_state[pin - first_pin] = analogRead(DIGITAL_PIN_TO_ANALOG_PIN(pin)); // int16_t pin_state[...]
else
//*/
pin_state[i - first_pin] = extDigitalRead(pin);
}
#if HAS_RESUME_CONTINUE
KEEPALIVE_STATE(PAUSED_FOR_USER);
wait_for_user = true;
#if ENABLED(HOST_PROMPT_SUPPORT)
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("M43 Wait Called"), CONTINUE_STR);
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onUserConfirmRequired_P(PSTR("M43 Wait Called"));
#endif
#endif
for (;;) {
LOOP_S_LE_N(i, first_pin, last_pin) {
pin_t pin = GET_PIN_MAP_PIN_M43(i);
if (!VALID_PIN(pin)) continue;
if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) continue;
const byte val =
/*
IS_ANALOG(pin)
? analogRead(DIGITAL_PIN_TO_ANALOG_PIN(pin)) : // int16_t val
:
//*/
extDigitalRead(pin);
if (val != pin_state[i - first_pin]) {
report_pin_state_extended(pin, ignore_protection, false);
pin_state[i - first_pin] = val;
}
}
#if HAS_RESUME_CONTINUE
if (!wait_for_user) break;
#endif
safe_delay(200);
}
}
else {
// Report current state of selected pin(s)
LOOP_S_LE_N(i, first_pin, last_pin) {
pin_t pin = GET_PIN_MAP_PIN_M43(i);
if (VALID_PIN(pin)) report_pin_state_extended(pin, ignore_protection, true);
}
}
}
#endif // PINS_DEBUGGING

View File

@@ -0,0 +1,40 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
#include "../gcode.h"
#include "../../module/stepper.h"
/**
* M540: Set whether SD card print should abort on endstop hit (M540 S<0|1>)
*/
void GcodeSuite::M540() {
if (parser.seen('S'))
planner.abort_on_endstop_hit = parser.value_bool();
}
#endif // SD_ABORT_ON_ENDSTOP_HIT

View File

@@ -0,0 +1,74 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(BAUD_RATE_GCODE)
#include "../gcode.h"
/**
* M575 - Change serial baud rate
*
* P<index> - Serial port index. Omit for all.
* B<baudrate> - Baud rate (bits per second)
*/
void GcodeSuite::M575() {
const int32_t baud = parser.ulongval('B');
switch (baud) {
case 2400: case 9600: case 19200: case 38400: case 57600:
case 115200: case 250000: case 500000: case 1000000: {
const int8_t port = parser.intval('P', -99);
const bool set0 = (port == -99 || port == 0);
if (set0) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Serial "
#if NUM_SERIAL > 1
, '0',
#else
"0"
#endif
" baud rate set to ", baud
);
}
#if NUM_SERIAL > 1
const bool set1 = (port == -99 || port == 1);
if (set1) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Serial ", '1', " baud rate set to ", baud);
}
#endif
SERIAL_FLUSH();
if (set0) { MYSERIAL0.end(); MYSERIAL0.begin(baud); }
#if NUM_SERIAL > 1
if (set1) { MYSERIAL1.end(); MYSERIAL1.begin(baud); }
#endif
} break;
default: SERIAL_ECHO_MSG("?(B)aud rate implausible.");
}
}
#endif // NUM_SERIAL > 0 && BAUD_RATE_GCODE

View File

@@ -0,0 +1,99 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
#include "../gcode.h"
#include "../../HAL/shared/Delay.h"
#include "../parser.h"
/**
* M672 - Set/reset Duet Smart Effector sensitivity
*
* One of these is required:
* S<sensitivity> - 0-255
* R - Flag to reset sensitivity to default
*/
/**
* The Marlin format for the M672 command is different than shown in the Duet Smart Effector
* documentation https://duet3d.dozuki.com/Wiki/Smart_effector_and_carriage_adapters_for_delta_printer
*
* To set custom sensitivity:
* Duet: M672 S105:aaa:bbb
* Marlin: M672 Saaa
*
* (where aaa is the desired sensitivity and bbb is 255 - aaa).
*
* Revert sensitivity to factory settings:
* Duet: M672 S105:131:131
* Marlin: M672 R
*/
#define M672_PROGBYTE 105 // magic byte to start programming custom sensitivity
#define M672_ERASEBYTE 131 // magic byte to clear custom sensitivity
//
// Smart Effector byte send protocol:
//
// 0 0 1 0 ... always 0010
// b7 b6 b5 b4 ~b4 ... hi bits, NOT last bit
// b3 b2 b1 b0 ~b0 ... lo bits, NOT last bit
//
void M672_send(uint8_t b) { // bit rate requirement: 1KHz +/- 30%
LOOP_L_N(bits, 14) {
switch (bits) {
default: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, !!(b & 0x80)); b <<= 1; break; } // send bit, shift next into place
case 7:
case 12: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, !!(b & 0x80)); break; } // send bit. no shift
case 8:
case 13: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, !(b & 0x80)); b <<= 1; break; } // send inverted previous bit
case 0: case 1: // 00
case 3: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, LOW); break; } // 0010
case 2: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, HIGH); break; } // 001
}
DELAY_US(1000);
}
}
void GcodeSuite::M672() {
if (parser.seen('R')) {
M672_send(M672_ERASEBYTE);
M672_send(M672_ERASEBYTE);
}
else if (parser.seenval('S')) {
const int8_t M672_sensitivity = parser.value_byte();
M672_send(M672_PROGBYTE);
M672_send(M672_sensitivity);
M672_send(255 - M672_sensitivity);
}
else {
SERIAL_ECHO_MSG("!'S' or 'R' parameter required.");
return;
}
OUT_WRITE(SMART_EFFECTOR_MOD_PIN, LOW); // Keep Smart Effector in NORMAL mode
}
#endif // SMART_EFFECTOR && SMART_EFFECTOR_MOD_PIN

View File

@@ -0,0 +1,114 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/planner.h"
void report_M92(const bool echo=true, const int8_t e=-1) {
if (echo) SERIAL_ECHO_START(); else SERIAL_CHAR(' ');
SERIAL_ECHOPAIR_P(PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]));
#if DISABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOPAIR_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS]));
#endif
SERIAL_EOL();
#if ENABLED(DISTINCT_E_FACTORS)
LOOP_L_N(i, E_STEPPERS) {
if (e >= 0 && i != e) continue;
if (echo) SERIAL_ECHO_START(); else SERIAL_CHAR(' ');
SERIAL_ECHOLNPAIR_P(PSTR(" M92 T"), (int)i,
SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS_N(i)]));
}
#endif
UNUSED_E(e);
}
/**
* M92: Set axis steps-per-unit for one or more axes, X, Y, Z, and E.
* (Follows the same syntax as G92)
*
* With multiple extruders use T to specify which one.
*
* If no argument is given print the current values.
*
* With MAGIC_NUMBERS_GCODE:
* Use 'H' and/or 'L' to get ideal layer-height information.
* 'H' specifies micro-steps to use. We guess if it's not supplied.
* 'L' specifies a desired layer height. Nearest good heights are shown.
*/
void GcodeSuite::M92() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
// No arguments? Show M92 report.
if (!parser.seen("XYZE"
#if ENABLED(MAGIC_NUMBERS_GCODE)
"HL"
#endif
)) return report_M92(true, target_extruder);
LOOP_XYZE(i) {
if (parser.seenval(axis_codes[i])) {
if (i == E_AXIS) {
const float value = parser.value_per_axis_units((AxisEnum)(E_AXIS_N(target_extruder)));
if (value < 20) {
float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
#if HAS_CLASSIC_JERK && HAS_CLASSIC_E_JERK
planner.max_jerk.e *= factor;
#endif
planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;
}
planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] = value;
}
else {
planner.settings.axis_steps_per_mm[i] = parser.value_per_axis_units((AxisEnum)i);
}
}
}
planner.refresh_positioning();
#if ENABLED(MAGIC_NUMBERS_GCODE)
#ifndef Z_MICROSTEPS
#define Z_MICROSTEPS 16
#endif
const float wanted = parser.floatval('L');
if (parser.seen('H') || wanted) {
const uint16_t argH = parser.ushortval('H'),
micro_steps = argH ?: Z_MICROSTEPS;
const float z_full_step_mm = micro_steps * planner.steps_to_mm[Z_AXIS];
SERIAL_ECHO_START();
SERIAL_ECHOPAIR("{ micro_steps:", micro_steps, ", z_full_step_mm:", z_full_step_mm);
if (wanted) {
const float best = uint16_t(wanted / z_full_step_mm) * z_full_step_mm;
SERIAL_ECHOPAIR(", best:[", best);
if (best != wanted) { SERIAL_CHAR(','); SERIAL_ECHO(best + z_full_step_mm); }
SERIAL_CHAR(']');
}
SERIAL_ECHOLNPGM(" }");
}
#endif
}

View File

@@ -0,0 +1,57 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if DISABLED(EMERGENCY_PARSER)
#include "../gcode.h"
#include "../../MarlinCore.h" // for wait_for_heatup, kill, quickstop_stepper
/**
* M108: Stop the waiting for heaters in M109, M190, M303. Does not affect the target temperature.
*/
void GcodeSuite::M108() {
#if HAS_RESUME_CONTINUE
wait_for_user = false;
#endif
wait_for_heatup = false;
}
/**
* M112: Full Shutdown
*/
void GcodeSuite::M112() {
kill(M112_KILL_STR, nullptr, true);
}
/**
* M410: Quickstop - Abort all planned moves
*
* This will stop the carriages mid-move, so most likely they
* will be out of sync with the stepper position after this.
*/
void GcodeSuite::M410() {
quickstop_stepper();
}
#endif // !EMERGENCY_PARSER

View File

@@ -0,0 +1,80 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
/**
* M111: Set the debug level
*/
void GcodeSuite::M111() {
if (parser.seen('S')) marlin_debug_flags = parser.byteval('S');
static const char str_debug_1[] PROGMEM = STR_DEBUG_ECHO,
str_debug_2[] PROGMEM = STR_DEBUG_INFO,
str_debug_4[] PROGMEM = STR_DEBUG_ERRORS,
str_debug_8[] PROGMEM = STR_DEBUG_DRYRUN,
str_debug_16[] PROGMEM = STR_DEBUG_COMMUNICATION
#if ENABLED(DEBUG_LEVELING_FEATURE)
, str_debug_lvl[] PROGMEM = STR_DEBUG_LEVELING
#endif
;
static PGM_P const debug_strings[] PROGMEM = {
str_debug_1, str_debug_2, str_debug_4, str_debug_8, str_debug_16
#if ENABLED(DEBUG_LEVELING_FEATURE)
, str_debug_lvl
#endif
};
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_DEBUG_PREFIX);
if (marlin_debug_flags) {
uint8_t comma = 0;
LOOP_L_N(i, COUNT(debug_strings)) {
if (TEST(marlin_debug_flags, i)) {
if (comma++) SERIAL_CHAR(',');
serialprintPGM((char*)pgm_read_ptr(&debug_strings[i]));
}
}
}
else {
SERIAL_ECHOPGM(STR_DEBUG_OFF);
#if !defined(__AVR__) || !defined(USBCON)
#if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS)
SERIAL_ECHOPAIR("\nBuffer Overruns: ", MYSERIAL0.buffer_overruns());
#endif
#if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS)
SERIAL_ECHOPAIR("\nFraming Errors: ", MYSERIAL0.framing_errors());
#endif
#if ENABLED(SERIAL_STATS_DROPPED_RX)
SERIAL_ECHOPAIR("\nDropped bytes: ", MYSERIAL0.dropped());
#endif
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
SERIAL_ECHOPAIR("\nMax RX Queue Size: ", MYSERIAL0.rxMaxEnqueued());
#endif
#endif // !defined(__AVR__) || !defined(USBCON)
}
SERIAL_EOL();
}

View File

@@ -0,0 +1,34 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/endstops.h"
/**
* M120: Enable endstops and set non-homing endstop state to "enabled"
*/
void GcodeSuite::M120() { endstops.enable_globally(true); }
/**
* M121: Disable endstops and set non-homing endstop state to "disabled"
*/
void GcodeSuite::M121() { endstops.enable_globally(false); }

View File

@@ -0,0 +1,77 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for stepper_inactive_time, disable_e_steppers
#include "../../lcd/ultralcd.h"
#include "../../module/stepper.h"
#if BOTH(AUTO_BED_LEVELING_UBL, ULTRA_LCD)
#include "../../feature/bedlevel/bedlevel.h"
#endif
/**
* M17: Enable stepper motors
*/
void GcodeSuite::M17() {
if (parser.seen("XYZE")) {
if (parser.seen('X')) ENABLE_AXIS_X();
if (parser.seen('Y')) ENABLE_AXIS_Y();
if (parser.seen('Z')) ENABLE_AXIS_Z();
#if HAS_E_STEPPER_ENABLE
if (parser.seen('E')) enable_e_steppers();
#endif
}
else {
LCD_MESSAGEPGM(MSG_NO_MOVE);
enable_all_steppers();
}
}
/**
* M18, M84: Disable stepper motors
*/
void GcodeSuite::M18_M84() {
if (parser.seenval('S')) {
stepper_inactive_time = parser.value_millis_from_seconds();
}
else {
if (parser.seen("XYZE")) {
planner.synchronize();
if (parser.seen('X')) DISABLE_AXIS_X();
if (parser.seen('Y')) DISABLE_AXIS_Y();
if (parser.seen('Z')) DISABLE_AXIS_Z();
#if HAS_E_STEPPER_ENABLE
if (parser.seen('E')) disable_e_steppers();
#endif
}
else
planner.finish_and_disable();
#if HAS_LCD_MENU && ENABLED(AUTO_BED_LEVELING_UBL)
if (ubl.lcd_map_control) {
ubl.lcd_map_control = false;
ui.defer_status_screen(false);
}
#endif
}
}

View File

@@ -0,0 +1,46 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if HAS_SOFTWARE_ENDSTOPS
#include "../gcode.h"
#include "../../module/motion.h"
/**
* M211: Enable, Disable, and/or Report software endstops
*
* Usage: M211 S1 to enable, M211 S0 to disable, M211 alone for report
*/
void GcodeSuite::M211() {
const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(),
l_soft_max = soft_endstop.max.asLogical();
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_SOFT_ENDSTOPS);
if (parser.seen('S')) soft_endstops_enabled = parser.value_bool();
serialprint_onoff(soft_endstops_enabled);
print_xyz(l_soft_min, PSTR(STR_SOFT_MIN), PSTR(" "));
print_xyz(l_soft_max, PSTR(STR_SOFT_MAX));
}
#endif

View File

@@ -0,0 +1,52 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected and idle()
#include "../../module/stepper.h"
/**
* M226: Wait until the specified pin reaches the state required (M226 P<pin> S<state>)
*/
void GcodeSuite::M226() {
if (parser.seen('P')) {
const int pin_number = PARSED_PIN_INDEX('P', 0),
pin_state = parser.intval('S', -1); // required pin state - default is inverted
const pin_t pin = GET_PIN_MAP_PIN(pin_number);
if (WITHIN(pin_state, -1, 1) && pin > -1) {
if (pin_is_protected(pin))
protected_pin_err();
else {
int target = LOW;
planner.synchronize();
pinMode(pin, INPUT);
switch (pin_state) {
case 1: target = HIGH; break;
case 0: target = LOW; break;
case -1: target = !extDigitalRead(pin); break;
}
while (int(extDigitalRead(pin)) != target) idle();
}
} // pin_state -1 0 1 && pin > -1
} // parser.seen('P')
}

View File

@@ -0,0 +1,55 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_SERVOS
#include "../gcode.h"
#include "../../module/servo.h"
/**
* M280: Get or set servo position. P<index> [S<angle>]
*/
void GcodeSuite::M280() {
if (!parser.seen('P')) return;
const int servo_index = parser.value_int();
if (WITHIN(servo_index, 0, NUM_SERVOS - 1)) {
if (parser.seen('S')) {
const int a = parser.value_int();
if (a == -1)
servo[servo_index].detach();
else
MOVE_SERVO(servo_index, a);
}
else {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Servo ", servo_index, ": ", servo[servo_index].read());
}
}
else {
SERIAL_ERROR_START();
SERIAL_ECHOLNPAIR("Servo ", servo_index, " out of range");
}
}
#endif // HAS_SERVOS

View File

@@ -0,0 +1,100 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_CUTTER
#include "../gcode.h"
#include "../../feature/spindle_laser.h"
#include "../../module/stepper.h"
/**
* Laser:
*
* M3 - Laser ON/Power (Ramped power)
* M4 - Laser ON/Power (Continuous power)
*
* S<power> - Set power. S0 will turn the laser off.
* O<ocr> - Set power and OCR
*
* Spindle:
*
* M3 - Spindle ON (Clockwise)
* M4 - Spindle ON (Counter-clockwise)
*
* S<power> - Set power. S0 will turn the spindle off.
* O<ocr> - Set power and OCR
*
* If no PWM pin is defined then M3/M4 just turns it on.
*
* At least 12.8KHz (50Hz * 256) is needed for Spindle PWM.
* Hardware PWM is required on AVR. ISRs are too slow.
*
* NOTE: WGM for timers 3, 4, and 5 must be either Mode 1 or Mode 5.
* No other settings give a PWM signal that goes from 0 to 5 volts.
*
* The system automatically sets WGM to Mode 1, so no special
* initialization is needed.
*
* WGM bits for timer 2 are automatically set by the system to
* Mode 1. This produces an acceptable 0 to 5 volt signal.
* No special initialization is needed.
*
* NOTE: A minimum PWM frequency of 50 Hz is needed. All prescaler
* factors for timers 2, 3, 4, and 5 are acceptable.
*
* SPINDLE_LASER_ENA_PIN needs an external pullup or it may power on
* the spindle/laser during power-up or when connecting to the host
* (usually goes through a reset which sets all I/O pins to tri-state)
*
* PWM duty cycle goes from 0 (off) to 255 (always on).
*/
void GcodeSuite::M3_M4(const bool is_M4) {
#if ENABLED(SPINDLE_FEATURE)
planner.synchronize(); // Wait for movement to complete before changing power
#endif
cutter.set_direction(is_M4);
#if ENABLED(SPINDLE_LASER_PWM)
if (parser.seenval('O'))
cutter.set_ocr_power(parser.value_byte()); // The OCR is a value from 0 to 255 (uint8_t)
else
cutter.set_power(parser.intval('S', 255));
#else
cutter.set_enabled(true);
#endif
}
/**
* M5 - Cutter OFF
*/
void GcodeSuite::M5() {
#if ENABLED(SPINDLE_FEATURE)
planner.synchronize();
#endif
cutter.set_enabled(false);
}
#endif // HAS_CUTTER

View File

@@ -0,0 +1,64 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_MICROSTEPS
#include "../gcode.h"
#include "../../module/stepper.h"
/**
* M350: Set axis microstepping modes. S sets mode for all drivers.
*
* Warning: Steps-per-unit remains unchanged.
*/
void GcodeSuite::M350() {
if (parser.seen('S')) LOOP_LE_N(i, 4) stepper.microstep_mode(i, parser.value_byte());
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) stepper.microstep_mode(i, parser.value_byte());
if (parser.seen('B')) stepper.microstep_mode(4, parser.value_byte());
stepper.microstep_readings();
}
/**
* M351: Toggle MS1 MS2 pins directly with axis codes X Y Z E B
* S# determines MS1, MS2 or MS3, X# sets the pin high/low.
*/
void GcodeSuite::M351() {
if (parser.seenval('S')) switch (parser.value_byte()) {
case 1:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, parser.value_byte(), -1, -1);
if (parser.seenval('B')) stepper.microstep_ms(4, parser.value_byte(), -1, -1);
break;
case 2:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, parser.value_byte(), -1);
if (parser.seenval('B')) stepper.microstep_ms(4, -1, parser.value_byte(), -1);
break;
case 3:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, -1, parser.value_byte());
if (parser.seenval('B')) stepper.microstep_ms(4, -1, -1, parser.value_byte());
break;
}
stepper.microstep_readings();
}
#endif // HAS_MICROSTEPS

View File

@@ -0,0 +1,56 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if EITHER(EXT_SOLENOID, MANUAL_SOLENOID_CONTROL)
#include "../gcode.h"
#include "../../feature/solenoid.h"
#include "../../module/motion.h"
/**
* M380: Enable solenoid on the active extruder
*
* S<index> to specify a solenoid (Requires MANUAL_SOLENOID_CONTROL)
*/
void GcodeSuite::M380() {
#if ENABLED(MANUAL_SOLENOID_CONTROL)
enable_solenoid(parser.intval('S', active_extruder));
#else
enable_solenoid_on_active_extruder();
#endif
}
/**
* M381: Disable all solenoids if EXT_SOLENOID
* Disable selected/active solenoid if MANUAL_SOLENOID_CONTROL
*/
void GcodeSuite::M381() {
#if ENABLED(MANUAL_SOLENOID_CONTROL)
disable_solenoid(parser.intval('S', active_extruder));
#else
disable_all_solenoids();
#endif
}
#endif // EXT_SOLENOID || MANUAL_SOLENOID_CONTROL

View File

@@ -0,0 +1,33 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/stepper.h"
/**
* M400: Finish all moves
*/
void GcodeSuite::M400() {
planner.synchronize();
}

View File

@@ -0,0 +1,99 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected
#include "../../inc/MarlinConfig.h"
#if FAN_COUNT > 0
#include "../../module/temperature.h"
#endif
/**
* M42: Change pin status via GCode
*
* P<pin> Pin number (LED if omitted)
* For LPC1768 specify pin P1_02 as M42 P102,
* P1_20 as M42 P120, etc.
*
* S<byte> Pin status from 0 - 255
* I Flag to ignore Marlin's pin protection
*
* M<mode> Pin mode: 0=INPUT 1=OUTPUT 2=INPUT_PULLUP 3=INPUT_PULLDOWN
*/
void GcodeSuite::M42() {
const int pin_index = PARSED_PIN_INDEX('P', GET_PIN_MAP_INDEX(LED_PIN));
if (pin_index < 0) return;
const pin_t pin = GET_PIN_MAP_PIN(pin_index);
if (!parser.boolval('I') && pin_is_protected(pin)) return protected_pin_err();
if (parser.seenval('M')) {
switch (parser.value_byte()) {
case 0: pinMode(pin, INPUT); break;
case 1: pinMode(pin, OUTPUT); break;
case 2: pinMode(pin, INPUT_PULLUP); break;
#ifdef INPUT_PULLDOWN
case 3: pinMode(pin, INPUT_PULLDOWN); break;
#endif
default: SERIAL_ECHOLNPGM("Invalid Pin Mode");
}
return;
}
if (!parser.seenval('S')) return;
const byte pin_status = parser.value_byte();
#if FAN_COUNT > 0
switch (pin) {
#if HAS_FAN0
case FAN0_PIN: thermalManager.fan_speed[0] = pin_status; return;
#endif
#if HAS_FAN1
case FAN1_PIN: thermalManager.fan_speed[1] = pin_status; return;
#endif
#if HAS_FAN2
case FAN2_PIN: thermalManager.fan_speed[2] = pin_status; return;
#endif
#if HAS_FAN3
case FAN3_PIN: thermalManager.fan_speed[3] = pin_status; return;
#endif
#if HAS_FAN4
case FAN4_PIN: thermalManager.fan_speed[4] = pin_status; return;
#endif
#if HAS_FAN5
case FAN5_PIN: thermalManager.fan_speed[5] = pin_status; return;
#endif
#if HAS_FAN6
case FAN6_PIN: thermalManager.fan_speed[6] = pin_status; return;
#endif
#if HAS_FAN7
case FAN7_PIN: thermalManager.fan_speed[7] = pin_status; return;
#endif
}
#endif
pinMode(pin, OUTPUT);
extDigitalWrite(pin, pin_status);
analogWrite(pin, pin_status);
}

View File

@@ -0,0 +1,182 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_DUPLICATION_MODE
//#define DEBUG_DXC_MODE
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#include "../../module/tool_change.h"
#include "../../module/planner.h"
#define DEBUG_OUT ENABLED(DEBUG_DXC_MODE)
#include "../../core/debug_out.h"
#if ENABLED(DUAL_X_CARRIAGE)
/**
* M605: Set dual x-carriage movement mode
*
* M605 S0 : (FULL_CONTROL) The slicer has full control over both X-carriages and can achieve optimal travel
* results as long as it supports dual X-carriages.
*
* M605 S1 : (AUTO_PARK) The firmware automatically parks and unparks the X-carriages on tool-change so that
* additional slicer support is not required.
*
* M605 S2 X R : (DUPLICATION) The firmware moves the second X-carriage and extruder in synchronization with
* the first X-carriage and extruder, to print 2 copies of the same object at the same time.
* Set the constant X-offset and temperature differential with M605 S2 X[offs] R[deg] and
* follow with "M605 S2" to initiate duplicated movement. For example, use "M605 S2 X100 R2" to
* make a copy 100mm to the right with E1 2° hotter than E0.
*
* M605 S3 : (MIRRORED) Formbot/Vivedino-inspired mirrored mode in which the second extruder duplicates
* the movement of the first except the second extruder is reversed in the X axis.
* The temperature differential and initial X offset must be set with "M605 S2 X[offs] R[deg]",
* then followed by "M605 S3" to initiate mirrored movement.
*
* M605 W : IDEX What? command.
*
* Note: the X axis should be homed after changing Dual X-carriage mode.
*/
void GcodeSuite::M605() {
planner.synchronize();
if (parser.seen('S')) {
const DualXMode previous_mode = dual_x_carriage_mode;
dual_x_carriage_mode = (DualXMode)parser.value_byte();
mirrored_duplication_mode = false;
if (dual_x_carriage_mode == DXC_MIRRORED_MODE) {
if (previous_mode != DXC_DUPLICATION_MODE) {
SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
return;
}
mirrored_duplication_mode = true;
stepper.set_directions();
float x_jog = current_position.x - .1;
for (uint8_t i = 2; --i;) {
planner.buffer_line(x_jog, current_position.y, current_position.z, current_position.e, feedrate_mm_s, 0);
x_jog += .1;
}
return;
}
switch (dual_x_carriage_mode) {
case DXC_FULL_CONTROL_MODE:
case DXC_AUTO_PARK_MODE:
break;
case DXC_DUPLICATION_MODE:
if (parser.seen('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), X2_MIN_POS - x_home_pos(0));
if (parser.seen('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff();
if (active_extruder != 0) tool_change(0);
break;
default:
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
break;
}
active_extruder_parked = false;
extruder_duplication_enabled = false;
stepper.set_directions();
delayed_move_time = 0;
}
else if (!parser.seen('W')) // if no S or W parameter, the DXC mode gets reset to the user's default
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
#ifdef DEBUG_DXC_MODE
if (parser.seen('W')) {
DEBUG_ECHO_START();
DEBUG_ECHOPGM("Dual X Carriage Mode ");
switch (dual_x_carriage_mode) {
case DXC_FULL_CONTROL_MODE: DEBUG_ECHOPGM("FULL_CONTROL"); break;
case DXC_AUTO_PARK_MODE: DEBUG_ECHOPGM("AUTO_PARK"); break;
case DXC_DUPLICATION_MODE: DEBUG_ECHOPGM("DUPLICATION"); break;
case DXC_MIRRORED_MODE: DEBUG_ECHOPGM("MIRRORED"); break;
}
DEBUG_ECHOPAIR("\nActive Ext: ", int(active_extruder));
if (!active_extruder_parked) DEBUG_ECHOPGM(" NOT ");
DEBUG_ECHOPGM(" parked.");
DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position.x);
DEBUG_ECHOPAIR("\ninactive_extruder_x_pos: ", inactive_extruder_x_pos);
DEBUG_ECHOPAIR("\nextruder_duplication_enabled: ", int(extruder_duplication_enabled));
DEBUG_ECHOPAIR("\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset);
DEBUG_ECHOPAIR("\nduplicate_extruder_temp_offset: ", duplicate_extruder_temp_offset);
DEBUG_ECHOPAIR("\ndelayed_move_time: ", delayed_move_time);
DEBUG_ECHOPAIR("\nX1 Home X: ", x_home_pos(0), "\nX1_MIN_POS=", int(X1_MIN_POS), "\nX1_MAX_POS=", int(X1_MAX_POS));
DEBUG_ECHOPAIR("\nX2 Home X: ", x_home_pos(1), "\nX2_MIN_POS=", int(X2_MIN_POS), "\nX2_MAX_POS=", int(X2_MAX_POS));
DEBUG_ECHOPAIR("\nX2_HOME_DIR=", int(X2_HOME_DIR), "\nX2_HOME_POS=", int(X2_HOME_POS));
DEBUG_ECHOPAIR("\nDEFAULT_DUAL_X_CARRIAGE_MODE=", STRINGIFY(DEFAULT_DUAL_X_CARRIAGE_MODE));
DEBUG_ECHOPAIR("\toolchange_settings.z_raise=", toolchange_settings.z_raise);
DEBUG_ECHOPAIR("\nDEFAULT_DUPLICATION_X_OFFSET=", int(DEFAULT_DUPLICATION_X_OFFSET));
DEBUG_EOL();
HOTEND_LOOP() {
DEBUG_ECHOPAIR_P(SP_T_STR, int(e));
LOOP_XYZ(a) DEBUG_ECHOPAIR(" hotend_offset[", int(e), "].", XYZ_CHAR(a) | 0x20, "=", hotend_offset[e][a]);
DEBUG_EOL();
}
DEBUG_EOL();
}
#endif // DEBUG_DXC_MODE
}
#elif ENABLED(MULTI_NOZZLE_DUPLICATION)
/**
* M605: Set multi-nozzle duplication mode
*
* S2 - Enable duplication mode
* P[mask] - Bit-mask of nozzles to include in the duplication set.
* A value of 0 disables duplication.
* E[index] - Last nozzle index to include in the duplication set.
* A value of 0 disables duplication.
*/
void GcodeSuite::M605() {
bool ena = false;
if (parser.seen("EPS")) {
planner.synchronize();
if (parser.seenval('P')) duplication_e_mask = parser.value_int(); // Set the mask directly
else if (parser.seenval('E')) duplication_e_mask = pow(2, parser.value_int() + 1) - 1; // Set the mask by E index
ena = (2 == parser.intval('S', extruder_duplication_enabled ? 2 : 0));
extruder_duplication_enabled = ena && (duplication_e_mask >= 3);
}
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_DUPLICATION_MODE);
serialprint_onoff(extruder_duplication_enabled);
if (ena) {
SERIAL_ECHOPGM(" ( ");
HOTEND_LOOP() if (TEST(duplication_e_mask, e)) { SERIAL_ECHO(e); SERIAL_CHAR(' '); }
SERIAL_CHAR(')');
}
SERIAL_EOL();
}
#endif // MULTI_NOZZLE_DUPLICATION
#endif // HAS_DUPICATION_MODE

View File

@@ -0,0 +1,63 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(COOLANT_CONTROL)
#include "../gcode.h"
#include "../../module/planner.h"
#if ENABLED(COOLANT_MIST)
/**
* M7: Mist Coolant On
*/
void GcodeSuite::M7() {
planner.synchronize(); // Wait for move to arrive
WRITE(COOLANT_MIST_PIN, !(COOLANT_MIST_INVERT)); // Turn on Mist coolant
}
#endif
#if ENABLED(COOLANT_FLOOD)
/**
* M8: Flood Coolant On
*/
void GcodeSuite::M8() {
planner.synchronize(); // Wait for move to arrive
WRITE(COOLANT_FLOOD_PIN, !(COOLANT_FLOOD_INVERT)); // Turn on Flood coolant
}
#endif
/**
* M9: Coolant OFF
*/
void GcodeSuite::M9() {
planner.synchronize(); // Wait for move to arrive
#if ENABLED(COOLANT_MIST)
WRITE(COOLANT_MIST_PIN, COOLANT_MIST_INVERT); // Turn off Mist coolant
#endif
#if ENABLED(COOLANT_FLOOD)
WRITE(COOLANT_FLOOD_PIN, COOLANT_FLOOD_INVERT); // Turn off Flood coolant
#endif
}
#endif // COOLANT_CONTROL

View File

@@ -0,0 +1,115 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/temperature.h"
#include "../../module/stepper.h"
#include "../../module/printcounter.h" // for print_job_timer
#include "../../inc/MarlinConfig.h"
#if HAS_LCD_MENU
#include "../../lcd/ultralcd.h"
#endif
#if HAS_SUICIDE
#include "../../MarlinCore.h"
#endif
#if ENABLED(PSU_CONTROL)
#if ENABLED(AUTO_POWER_CONTROL)
#include "../../feature/power.h"
#endif
// Could be moved to a feature, but this is all the data
bool powersupply_on;
#if HAS_TRINAMIC_CONFIG
#include "../../feature/tmc_util.h"
#endif
/**
* M80 : Turn on the Power Supply
* M80 S : Report the current state and exit
*/
void GcodeSuite::M80() {
// S: Report the current power supply state and exit
if (parser.seen('S')) {
serialprintPGM(powersupply_on ? PSTR("PS:1\n") : PSTR("PS:0\n"));
return;
}
PSU_ON();
/**
* If you have a switch on suicide pin, this is useful
* if you want to start another print with suicide feature after
* a print without suicide...
*/
#if HAS_SUICIDE
OUT_WRITE(SUICIDE_PIN, !SUICIDE_PIN_INVERTING);
#endif
#if DISABLED(AUTO_POWER_CONTROL)
delay(PSU_POWERUP_DELAY); // Wait for power to settle
restore_stepper_drivers();
#endif
#if HAS_LCD_MENU
ui.reset_status();
#endif
}
#endif // ENABLED(PSU_CONTROL)
/**
* M81: Turn off Power, including Power Supply, if there is one.
*
* This code should ALWAYS be available for FULL SHUTDOWN!
*/
void GcodeSuite::M81() {
thermalManager.disable_all_heaters();
print_job_timer.stop();
planner.finish_and_disable();
#if FAN_COUNT > 0
thermalManager.zero_fan_speeds();
#if ENABLED(PROBING_FANS_OFF)
thermalManager.fans_paused = false;
ZERO(thermalManager.saved_fan_speed);
#endif
#endif
safe_delay(1000); // Wait 1 second before switching off
#if HAS_SUICIDE
suicide();
#elif ENABLED(PSU_CONTROL)
PSU_OFF();
#endif
#if HAS_LCD_MENU
LCD_MESSAGEPGM_P(PSTR(MACHINE_NAME " " STR_OFF "."));
#endif
}

View File

@@ -0,0 +1,36 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for max_inactive_time
/**
* M85: Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
*/
void GcodeSuite::M85() {
if (parser.seen('S')) {
reset_stepper_timeout();
max_inactive_time = parser.value_millis_from_seconds();
}
}

View File

@@ -0,0 +1,34 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#if ENABLED(PLATFORM_M997_SUPPORT)
/**
* M997: Perform in-application firmware update
*/
void GcodeSuite::M997() {
flashFirmware(parser.intval('S'));
}
#endif

View File

@@ -0,0 +1,46 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../lcd/ultralcd.h" // for lcd_reset_alert_level
#include "../../MarlinCore.h" // for marlin_state
#include "../queue.h" // for flush_and_request_resend
/**
* M999: Restart after being stopped
*
* Default behavior is to flush the serial buffer and request
* a resend to the host starting on the last N line received.
*
* Sending "M999 S1" will resume printing without flushing the
* existing command buffer.
*
*/
void GcodeSuite::M999() {
marlin_state = MF_RUNNING;
ui.reset_alert_level();
if (parser.boolval('S')) return;
queue.flush_and_request_resend();
}

View File

@@ -0,0 +1,80 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/tool_change.h"
#if ENABLED(DEBUG_LEVELING_FEATURE) || EXTRUDERS > 1
#include "../../module/motion.h"
#endif
#if ENABLED(PRUSA_MMU2)
#include "../../feature/mmu2/mmu2.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
/**
* T0-T<n>: Switch tool, usually switching extruders
*
* F[units/min] Set the movement feedrate
* S1 Don't move the tool in XY after change
*
* For PRUSA_MMU2:
* T[n] Gcode to extrude at least 38.10 mm at feedrate 19.02 mm/s must follow immediately to load to extruder wheels.
* T? Gcode to extrude shouldn't have to follow. Load to extruder wheels is done automatically.
* Tx Same as T?, but nozzle doesn't have to be preheated. Tc requires a preheated nozzle to finish filament load.
* Tc Load to nozzle after filament was prepared by Tc and nozzle is already heated.
*/
void GcodeSuite::T(const uint8_t tool_index) {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPAIR(">>> T(", tool_index, ")");
DEBUG_POS("BEFORE", current_position);
}
#if ENABLED(PRUSA_MMU2)
if (parser.string_arg) {
mmu2.tool_change(parser.string_arg); // Special commands T?/Tx/Tc
return;
}
#endif
#if EXTRUDERS < 2
tool_change(tool_index);
#else
tool_change(
tool_index,
(tool_index == active_extruder) || parser.boolval('S')
);
#endif
if (DEBUGGING(LEVELING)) {
DEBUG_POS("AFTER", current_position);
DEBUG_ECHOLNPGM("<<< T()");
}
}

View File

@@ -0,0 +1,68 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/configuration_store.h"
#include "../../core/serial.h"
#include "../../inc/MarlinConfig.h"
/**
* M500: Store settings in EEPROM
*/
void GcodeSuite::M500() {
(void)settings.save();
}
/**
* M501: Read settings from EEPROM
*/
void GcodeSuite::M501() {
(void)settings.load();
}
/**
* M502: Revert to default settings
*/
void GcodeSuite::M502() {
(void)settings.reset();
}
#if DISABLED(DISABLE_M503)
/**
* M503: print settings currently in memory
*/
void GcodeSuite::M503() {
(void)settings.report(!parser.boolval('S', true));
}
#endif // !DISABLE_M503
#if ENABLED(EEPROM_SETTINGS)
/**
* M504: Validate EEPROM Contents
*/
void GcodeSuite::M504() {
if (settings.validate())
SERIAL_ECHO_MSG("EEPROM OK");
}
#endif

View File

@@ -0,0 +1,151 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "../../gcode.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#include "../../../module/stepper/indirection.h"
void echo_yes_no(const bool yes);
inline void L6470_say_status(const L64XX_axis_t axis) {
if (L64xxManager.spi_abort) return;
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
L64xxManager.get_status(axis);
L64xxManager.say_axis(axis);
#if ENABLED(L6470_CHITCHAT)
char temp_buf[20];
sprintf_P(temp_buf, PSTR(" status: %4x "), sh.STATUS_AXIS_RAW);
SERIAL_ECHO(temp_buf);
print_bin(sh.STATUS_AXIS_RAW);
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: serialprintPGM(PSTR(" L6470")); break;
case L6474_STATUS_LAYOUT: serialprintPGM(PSTR(" L6474")); break;
case L6480_STATUS_LAYOUT: serialprintPGM(PSTR(" L6480/powerSTEP01")); break;
}
#endif
SERIAL_ECHOPGM("\n...OUTPUT: ");
serialprintPGM(sh.STATUS_AXIS & STATUS_HIZ ? PSTR("OFF") : PSTR("ON "));
SERIAL_ECHOPGM(" BUSY: "); echo_yes_no((sh.STATUS_AXIS & STATUS_BUSY) == 0);
SERIAL_ECHOPGM(" DIR: ");
serialprintPGM((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? PSTR("FORWARD") : PSTR("REVERSE"));
if (sh.STATUS_AXIS_LAYOUT == L6480_STATUS_LAYOUT) {
SERIAL_ECHOPGM(" Last Command: ");
if (sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD) SERIAL_ECHOPGM("VALID");
else SERIAL_ECHOPGM("ERROR");
SERIAL_ECHOPGM("\n...THERMAL: ");
switch ((sh.STATUS_AXIS & (sh.STATUS_AXIS_TH_SD | sh.STATUS_AXIS_TH_WRN)) >> 11) {
case 0: SERIAL_ECHOPGM("DEVICE SHUTDOWN"); break;
case 1: SERIAL_ECHOPGM("BRIDGE SHUTDOWN"); break;
case 2: SERIAL_ECHOPGM("WARNING "); break;
case 3: SERIAL_ECHOPGM("OK "); break;
}
}
else {
SERIAL_ECHOPGM(" Last Command: ");
if (!(sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD)) SERIAL_ECHOPGM("IN");
SERIAL_ECHOPGM("VALID ");
serialprintPGM(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? PSTR("COMPLETED ") : PSTR("Not PERFORMED"));
SERIAL_ECHOPAIR("\n...THERMAL: ", !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_SD) ? "SHUTDOWN " : !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_WRN) ? "WARNING " : "OK ");
}
SERIAL_ECHOPGM(" OVERCURRENT:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_OCD) == 0);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) {
SERIAL_ECHOPGM(" STALL:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_A) == 0 || (sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_B) == 0);
SERIAL_ECHOPGM(" STEP-CLOCK MODE:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_SCK_MOD) != 0);
}
else {
SERIAL_ECHOPGM(" STALL: NA "
" STEP-CLOCK MODE: NA"
" UNDER VOLTAGE LOCKOUT: "); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_UVLO) == 0);
}
SERIAL_EOL();
}
/**
* M122: Debug L6470 drivers
*/
void GcodeSuite::M122() {
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway
//if (parser.seen('S'))
// tmc_set_report_interval(parser.value_bool());
//else
#if AXIS_IS_L64XX(X)
L6470_say_status(X);
#endif
#if AXIS_IS_L64XX(X2)
L6470_say_status(X2);
#endif
#if AXIS_IS_L64XX(Y)
L6470_say_status(Y);
#endif
#if AXIS_IS_L64XX(Y2)
L6470_say_status(Y2);
#endif
#if AXIS_IS_L64XX(Z)
L6470_say_status(Z);
#endif
#if AXIS_IS_L64XX(Z2)
L6470_say_status(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
L6470_say_status(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
L6470_say_status(Z4);
#endif
#if AXIS_IS_L64XX(E0)
L6470_say_status(E0);
#endif
#if AXIS_IS_L64XX(E1)
L6470_say_status(E1);
#endif
#if AXIS_IS_L64XX(E2)
L6470_say_status(E2);
#endif
#if AXIS_IS_L64XX(E3)
L6470_say_status(E3);
#endif
#if AXIS_IS_L64XX(E4)
L6470_say_status(E4);
#endif
#if AXIS_IS_L64XX(E5)
L6470_say_status(E5);
#endif
#if AXIS_IS_L64XX(E6)
L6470_say_status(E6);
#endif
#if AXIS_IS_L64XX(E7)
L6470_say_status(E7);
#endif
L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags
L64xxManager.spi_abort = false;
L64xxManager.pause_monitor(false);
}
#endif // HAS_L64XX

View File

@@ -0,0 +1,372 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "../../gcode.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#include "../../../module/stepper/indirection.h"
#include "../../../module/planner.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../../core/debug_out.h"
/**
*
* M906: report or set KVAL_HOLD which sets the maximum effective voltage provided by the
* PWMs to the steppers
*
* On L6474 this sets the TVAL register (same address).
*
* I - select which driver(s) to change on multi-driver axis
* 0 - (default) all drivers on the axis or E0
* 1 - monitor only X, Y, Z or E1
* 2 - monitor only X2, Y2, Z2 or E2
* 3 - monitor only Z3 or E3
* 4 - monitor only Z4 or E4
* 5 - monitor only E5
* Xxxx, Yxxx, Zxxx, Exxx - axis to change (optional)
* L6474 - current in mA (4A max)
* All others - 0-255
*/
/**
* Sets KVAL_HOLD wich affects the current being driven through the stepper.
*
* L6470 is used in the STEP-CLOCK mode. KVAL_HOLD is the only KVAL_xxx
* that affects the effective voltage seen by the stepper.
*
*/
/**
* MACRO to fetch information on the items associated with current limiting
* and maximum voltage output.
*
* L6470 can be setup to shutdown if either current threshold is exceeded.
*
* L6470 output current can not be set directly. It is set indirectly by
* setting the maximum effective output voltage.
*
* Effective output voltage is set by PWM duty cycle.
*
* Maximum effective output voltage is affected by MANY variables. The main ones are:
* KVAL_HOLD
* KVAL_RUN
* KVAL_ACC
* KVAL_DEC
* Vs compensation (if enabled)
*/
void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
if (L64xxManager.spi_abort) return; // don't do anything if set_directions() has occurred
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
const uint16_t status = L64xxManager.get_status(axis); //also populates shadow structure
const uint8_t OverCurrent_Threshold = uint8_t(motor.GetParam(L6470_OCD_TH));
auto say_axis_status = [](const L64XX_axis_t axis, const uint16_t status) {
L64xxManager.say_axis(axis);
#if ENABLED(L6470_CHITCHAT)
char tmp[10];
sprintf_P(tmp, PSTR("%4x "), status);
DEBUG_ECHOPAIR(" status: ", tmp);
print_bin(status);
#else
UNUSED(status);
#endif
SERIAL_EOL();
};
char temp_buf[10];
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: // L6470
case L6480_STATUS_LAYOUT: { // L6480 & powerstep01
const uint16_t Stall_Threshold = (uint8_t)motor.GetParam(L6470_STALL_TH),
motor_status = (status & (STATUS_MOT_STATUS)) >> 5,
L6470_ADC_out = motor.GetParam(L6470_ADC_OUT),
L6470_ADC_out_limited = constrain(L6470_ADC_out, 8, 24);
const float comp_coef = 1600.0f / L6470_ADC_out_limited;
const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07);
say_axis_status(axis, sh.STATUS_AXIS_RAW);
SERIAL_ECHOPGM("...OverCurrent Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" Stall Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), Stall_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((Stall_Threshold + 1) * motor.STALL_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" Motor Status: ");
switch (motor_status) {
case 0: SERIAL_ECHOPGM("stopped"); break;
case 1: SERIAL_ECHOPGM("accelerating"); break;
case 2: SERIAL_ECHOPGM("decelerating"); break;
case 3: SERIAL_ECHOPGM("at constant speed"); break;
}
SERIAL_EOL();
SERIAL_ECHOPAIR("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOPGM(" Vs_compensation: ");
serialprintPGM((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? PSTR("ENABLED ") : PSTR("DISABLED"));
SERIAL_ECHOLNPAIR(" Compensation coefficient: ~", comp_coef * 0.01f);
SERIAL_ECHOPAIR("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD),
" KVAL_RUN : ", motor.GetParam(L6470_KVAL_RUN),
" KVAL_ACC: ", motor.GetParam(L6470_KVAL_ACC),
" KVAL_DEC: ", motor.GetParam(L6470_KVAL_DEC),
" V motor max = ");
switch (motor_status) {
case 0: SERIAL_ECHO(motor.GetParam(L6470_KVAL_HOLD) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break;
case 1: SERIAL_ECHO(motor.GetParam(L6470_KVAL_RUN) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_RUN)"); break;
case 2: SERIAL_ECHO(motor.GetParam(L6470_KVAL_ACC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_ACC)"); break;
case 3: SERIAL_ECHO(motor.GetParam(L6470_KVAL_DEC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break;
}
SERIAL_EOL();
#if ENABLED(L6470_CHITCHAT)
DEBUG_ECHOPGM("...SLEW RATE: ");
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: {
switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) {
case 0: { DEBUG_ECHOLNPGM("320V/uS") ; break; }
case 1: { DEBUG_ECHOLNPGM("75V/uS") ; break; }
case 2: { DEBUG_ECHOLNPGM("110V/uS") ; break; }
case 3: { DEBUG_ECHOLNPGM("260V/uS") ; break; }
}
break;
}
case L6480_STATUS_LAYOUT: {
switch (motor.GetParam(L6470_GATECFG1) & CONFIG1_SR ) {
case CONFIG1_SR_220V_us: { DEBUG_ECHOLNPGM("220V/uS") ; break; }
case CONFIG1_SR_400V_us: { DEBUG_ECHOLNPGM("400V/uS") ; break; }
case CONFIG1_SR_520V_us: { DEBUG_ECHOLNPGM("520V/uS") ; break; }
case CONFIG1_SR_980V_us: { DEBUG_ECHOLNPGM("980V/uS") ; break; }
default: { DEBUG_ECHOLNPGM("unknown") ; break; }
}
}
}
#endif
SERIAL_EOL();
break;
}
case L6474_STATUS_LAYOUT: { // L6474
const uint16_t L6470_ADC_out = motor.GetParam(L6470_ADC_OUT) & 0x1F,
L6474_TVAL_val = motor.GetParam(L6474_TVAL) & 0x7F;
say_axis_status(axis, sh.STATUS_AXIS_RAW);
SERIAL_ECHOPGM("...OverCurrent Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" TVAL: ");
sprintf_P(temp_buf, PSTR("%2d ("), L6474_TVAL_val);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((L6474_TVAL_val + 1) * motor.STALL_CURRENT_CONSTANT_INV);
SERIAL_ECHOLNPGM(" mA) Motor Status: NA");
const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07); //NOMORE(MicroSteps, 16);
SERIAL_ECHOPAIR("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOLNPGM(" Vs_compensation: NA\n");
SERIAL_ECHOLNPGM("...KVAL_HOLD: NA"
" KVAL_RUN : NA"
" KVAL_ACC: NA"
" KVAL_DEC: NA"
" V motor max = NA");
#if ENABLED(L6470_CHITCHAT)
DEBUG_ECHOPGM("...SLEW RATE: ");
switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) {
case 0: DEBUG_ECHOLNPGM("320V/uS") ; break;
case 1: DEBUG_ECHOLNPGM("75V/uS") ; break;
case 2: DEBUG_ECHOLNPGM("110V/uS") ; break;
case 3: DEBUG_ECHOLNPGM("260V/uS") ; break;
default: DEBUG_ECHOLNPAIR("slew rate: ", (motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT); break;
}
#endif
SERIAL_EOL();
SERIAL_EOL();
break;
}
}
}
void GcodeSuite::M906() {
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
#define L6470_SET_KVAL_HOLD(Q) (AXIS_IS_L64XX(Q) ? stepper##Q.setTVALCurrent(value) : stepper##Q.SetParam(L6470_KVAL_HOLD, uint8_t(value)))
DEBUG_ECHOLNPGM("M906");
uint8_t report_current = true;
#if HAS_L64XX
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) {
report_current = false;
if (planner.has_blocks_queued() || planner.cleaning_buffer_counter) {
SERIAL_ECHOLNPGM("Test aborted. Can't set KVAL_HOLD while steppers are moving.");
return;
}
switch (i) {
case X_AXIS:
#if AXIS_IS_L64XX(X)
if (index == 0) L6470_SET_KVAL_HOLD(X);
#endif
#if AXIS_IS_L64XX(X2)
if (index == 1) L6470_SET_KVAL_HOLD(X2);
#endif
break;
case Y_AXIS:
#if AXIS_IS_L64XX(Y)
if (index == 0) L6470_SET_KVAL_HOLD(Y);
#endif
#if AXIS_IS_L64XX(Y2)
if (index == 1) L6470_SET_KVAL_HOLD(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_IS_L64XX(Z)
if (index == 0) L6470_SET_KVAL_HOLD(Z);
#endif
#if AXIS_IS_L64XX(Z2)
if (index == 1) L6470_SET_KVAL_HOLD(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
if (index == 2) L6470_SET_KVAL_HOLD(Z3);
#endif
#if AXIS_DRIVER_TYPE_Z4(L6470)
if (index == 3) L6470_SET_KVAL_HOLD(Z4);
#endif
break;
case E_AXIS: {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_IS_L64XX(E0)
case 0: L6470_SET_KVAL_HOLD(E0); break;
#endif
#if AXIS_IS_L64XX(E1)
case 1: L6470_SET_KVAL_HOLD(E1); break;
#endif
#if AXIS_IS_L64XX(E2)
case 2: L6470_SET_KVAL_HOLD(E2); break;
#endif
#if AXIS_IS_L64XX(E3)
case 3: L6470_SET_KVAL_HOLD(E3); break;
#endif
#if AXIS_IS_L64XX(E4)
case 4: L6470_SET_KVAL_HOLD(E4); break;
#endif
#if AXIS_IS_L64XX(E5)
case 5: L6470_SET_KVAL_HOLD(E5); break;
#endif
#if AXIS_IS_L64XX(E6)
case 6: L6470_SET_KVAL_HOLD(E6); break;
#endif
#if AXIS_IS_L64XX(E7)
case 7: L6470_SET_KVAL_HOLD(E7); break;
#endif
}
} break;
}
}
if (report_current) {
#define L64XX_REPORT_CURRENT(Q) L64XX_report_current(stepper##Q, Q)
L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway
#if AXIS_IS_L64XX(X)
L64XX_REPORT_CURRENT(X);
#endif
#if AXIS_IS_L64XX(X2)
L64XX_REPORT_CURRENT(X2);
#endif
#if AXIS_IS_L64XX(Y)
L64XX_REPORT_CURRENT(Y);
#endif
#if AXIS_IS_L64XX(Y2)
L64XX_REPORT_CURRENT(Y2);
#endif
#if AXIS_IS_L64XX(Z)
L64XX_REPORT_CURRENT(Z);
#endif
#if AXIS_IS_L64XX(Z2)
L64XX_REPORT_CURRENT(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
L64XX_REPORT_CURRENT(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
L64XX_REPORT_CURRENT(Z4);
#endif
#if AXIS_IS_L64XX(E0)
L64XX_REPORT_CURRENT(E0);
#endif
#if AXIS_IS_L64XX(E1)
L64XX_REPORT_CURRENT(E1);
#endif
#if AXIS_IS_L64XX(E2)
L64XX_REPORT_CURRENT(E2);
#endif
#if AXIS_IS_L64XX(E3)
L64XX_REPORT_CURRENT(E3);
#endif
#if AXIS_IS_L64XX(E4)
L64XX_REPORT_CURRENT(E4);
#endif
#if AXIS_IS_L64XX(E5)
L64XX_REPORT_CURRENT(E5);
#endif
#if AXIS_IS_L64XX(E6)
L64XX_REPORT_CURRENT(E6);
#endif
#if AXIS_IS_L64XX(E7)
L64XX_REPORT_CURRENT(E7);
#endif
L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags
L64xxManager.spi_abort = false;
L64xxManager.pause_monitor(false);
}
}
#endif // HAS_L64XX

View File

@@ -0,0 +1,657 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//
// NOTE: All tests assume each axis uses matching driver chips.
//
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "../../gcode.h"
#include "../../../module/stepper/indirection.h"
#include "../../../module/planner.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../../core/debug_out.h"
/**
*
* M916: increase KVAL_HOLD until get thermal warning
* NOTE - on L6474 it is TVAL that is used
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* 3 - monitor only Z3, E3
* 4 - monitor only Z4, E4
*
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* F - feedrate
* optional - will use default max feedrate from configuration.h if not specified
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
* D - time (in seconds) to run each setting of KVAL_HOLD/TVAL
* optional - defaults to zero (runs each setting once)
*
*/
/**
* This routine is also useful for determining the approximate KVAL_HOLD
* where the stepper stops losing steps. The sound will get noticeably quieter
* as it stops losing steps.
*/
void GcodeSuite::M916() {
DEBUG_ECHOLNPGM("M916");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
// Variables used by L64xxManager.get_user_input function - some may not be used
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max;
float position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = false; // M916 doesn't play with the overcurrent thresholds
#define DRIVER_TYPE_L6474(Q) AXIS_DRIVER_TYPE_##Q(L6474)
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
DEBUG_ECHOLNPAIR("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // clear out any pre-existing error flags
char temp_axis_string[] = " ";
temp_axis_string[0] = axis_mon[0][0]; // need to have a string for use within sprintf format section
char gcode_string[80];
uint16_t status_composite = 0;
uint16_t M91x_counter = kval_hold;
uint16_t M91x_counter_max;
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) {
M91x_counter_max = 128; // TVAL is 7 bits
LIMIT(M91x_counter, 0U, 127U);
}
else
M91x_counter_max = 256; // KVAL_HOLD is 8 bits
uint8_t M91x_delay_s = parser.byteval('D'); // get delay in seconds
millis_t M91x_delay_ms = M91x_delay_s * 60 * 1000;
millis_t M91x_delay_end;
DEBUG_ECHOLNPGM(".\n.");
do {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)
DEBUG_ECHOLNPAIR("TVAL current (mA) = ", (M91x_counter + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV); // report TVAL current for this run
else
DEBUG_ECHOLNPAIR("kval_hold = ", M91x_counter); // report KVAL_HOLD for this run
for (j = 0; j < driver_count; j++)
L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, M91x_counter); //set KVAL_HOLD or TVAL (same register address)
M91x_delay_end = millis() + M91x_delay_ms;
do {
// turn the motor(s) both directions
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate));
gcode.process_subcommands_now_P(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate));
gcode.process_subcommands_now_P(gcode_string);
// get the status after the motors have stopped
planner.synchronize();
status_composite = 0; // clear out the old bits
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite |= axis_status[j] ;
}
if (status_composite) break;
} while (millis() < M91x_delay_end);
if (status_composite) break;
M91x_counter++;
} while (!(status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) && (M91x_counter < M91x_counter_max));
DEBUG_ECHOLNPGM(".");
#if ENABLED(L6470_CHITCHAT)
if (status_composite) {
L64xxManager.error_status_decode(status_composite, axis_index[0],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
DEBUG_ECHOLNPGM(".");
}
#endif
if ((status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)))
DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Thermal warning/shutdown has occurred");
else if (status_composite)
DEBUG_ECHOLNPGM(".\n.\nTest completed abnormally - non-thermal error has occured");
else
DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Unable to get to thermal warning/shutdown");
L64xxManager.pause_monitor(false);
}
/**
*
* M917: Find minimum current thresholds
*
* Decrease OCD current until overcurrent error
* Increase OCD until overcurrent error goes away
* Decrease stall threshold until stall (not done on L6474)
* Increase stall until stall error goes away (not done on L6474)
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* F - feedrate
* optional - will use default max feedrate from Configuration.h if not specified
*
* I - starting over-current threshold
* optional - will report current value from driver if not specified
* if there are multiple drivers on the axis then all will be set the same
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
*/
void GcodeSuite::M917() {
DEBUG_ECHOLNPGM("M917");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max;
float position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = true;
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
DEBUG_ECHOLNPAIR("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // clear error flags
char temp_axis_string[] = " ";
temp_axis_string[0] = axis_mon[0][0]; // need a sprintf format string
char gcode_string[80];
uint16_t status_composite = 0;
uint8_t test_phase = 0; // 0 - decreasing OCD - exit when OCD warning occurs (ignore STALL)
// 1 - increasing OCD - exit when OCD warning stops (ignore STALL)
// 2 - OCD finalized - decreasing STALL - exit when STALL warning happens
// 3 - OCD finalized - increasing STALL - exit when STALL warning stop
// 4 - all testing completed
DEBUG_ECHOPAIR(".\n.\n.\nover_current threshold : ", (OCD_TH_val + 1) * 375); // first status display
DEBUG_ECHOPAIR(" (OCD_TH: : ", OCD_TH_val);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) {
DEBUG_ECHOPAIR(") Stall threshold: ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOPAIR(" (STALL_TH: ", STALL_TH_val);
}
DEBUG_ECHOLNPGM(")");
do {
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) DEBUG_ECHOPAIR("STALL threshold : ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOLNPAIR(" OCD threshold : ", (OCD_TH_val + 1) * 375);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate));
gcode.process_subcommands_now_P(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate));
gcode.process_subcommands_now_P(gcode_string);
planner.synchronize();
status_composite = 0; // clear out the old bits
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite |= axis_status[j];
}
if (status_composite && (status_composite & sh.STATUS_AXIS_UVLO)) {
DEBUG_ECHOLNPGM("Test aborted (Undervoltage lockout active)");
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
#endif
return;
}
if (status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) {
DEBUG_ECHOLNPGM("thermal problem - waiting for chip(s) to cool down ");
uint16_t status_composite_temp = 0;
uint8_t k = 0;
do {
k++;
if (!(k % 4)) {
kval_hold *= 0.95;
DEBUG_EOL();
DEBUG_ECHOLNPAIR("Lowering KVAL_HOLD by about 5% to ", kval_hold);
for (j = 0; j < driver_count; j++)
L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
}
DEBUG_ECHOLNPGM(".");
gcode.reset_stepper_timeout(); // reset_stepper_timeout to keep steppers powered
watchdog_refresh();; // beat the dog
safe_delay(5000);
status_composite_temp = 0;
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite_temp |= axis_status[j];
}
}
while (status_composite_temp & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD));
DEBUG_EOL();
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B | sh.STATUS_AXIS_OCD)) {
switch (test_phase) {
case 0: {
if (status_composite & sh.STATUS_AXIS_OCD) {
// phase 0 with OCD warning - time to go to next phase
if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) {
OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max
test_phase = 2; // at highest value so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC E0A OCD at highest - skip to 2");
DEBUG_ECHOLNPGM("OCD at highest - OCD finalized");
}
else {
OCD_TH_val++; // normal exit to next phase
test_phase = 1; // setup for first pass of phase 1
//DEBUG_ECHOLNPGM("LOGIC E0B - inc OCD & go to 1");
DEBUG_ECHOLNPGM("inc OCD");
}
}
else { // phase 0 without OCD warning - keep on decrementing if can
if (OCD_TH_val) {
OCD_TH_val--; // try lower value
//DEBUG_ECHOLNPGM("LOGIC E0C - dec OCD");
DEBUG_ECHOLNPGM("dec OCD");
}
else {
test_phase = 2; // at lowest value without warning so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC E0D - OCD at latest - go to 2");
DEBUG_ECHOLNPGM("OCD finalized");
}
}
} break;
case 1: {
if (status_composite & sh.STATUS_AXIS_OCD) {
// phase 1 with OCD warning - increment if can
if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) {
OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max
test_phase = 2; // at highest value so go to next phase
//DEBUG_ECHOLNPGM("LOGIC E1A - OCD at max - go to 2");
DEBUG_ECHOLNPGM("OCD finalized");
}
else {
OCD_TH_val++; // try a higher value
//DEBUG_ECHOLNPGM("LOGIC E1B - inc OCD");
DEBUG_ECHOLNPGM("inc OCD");
}
}
else { // phase 1 without OCD warning - normal exit to phase 2
test_phase = 2;
//DEBUG_ECHOLNPGM("LOGIC E1C - no OCD warning - go to 1");
DEBUG_ECHOLNPGM("OCD finalized");
}
} break;
case 2: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) {
// phase 2 with stall warning - time to go to next phase
if (STALL_TH_val >= 127) {
STALL_TH_val = 127; // limit to max
//DEBUG_ECHOLNPGM("LOGIC E2A - STALL warning, STALL at max, quit");
DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning");
test_phase = 4;
}
else {
test_phase = 3; // normal exit to next phase (found failing value of STALL)
STALL_TH_val++; // setup for first pass of phase 3
//DEBUG_ECHOLNPGM("LOGIC E2B - INC - STALL warning, inc Stall, go to 3");
DEBUG_ECHOLNPGM("inc Stall");
}
}
else { // phase 2 without stall warning - decrement if can
if (STALL_TH_val) {
STALL_TH_val--; // try a lower value
//DEBUG_ECHOLNPGM("LOGIC E2C - no STALL, dec STALL");
DEBUG_ECHOLNPGM("dec STALL");
}
else {
DEBUG_ECHOLNPGM("finished - STALL at lowest value but still do NOT have stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E2D - no STALL, at lowest so quit");
}
}
} break;
case 3: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) {
// phase 3 with stall warning - increment if can
if (STALL_TH_val >= 127) {
STALL_TH_val = 127; // limit to max
DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E3A - STALL, at max so quit");
}
else {
STALL_TH_val++; // still looking for passing value
//DEBUG_ECHOLNPGM("LOGIC E3B - STALL, inc stall");
DEBUG_ECHOLNPGM("inc stall");
}
}
else { //phase 3 without stall warning but have OCD warning
DEBUG_ECHOLNPGM("Hardware problem - OCD warning without STALL warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E3C - not STALLED, hardware problem (quit)");
}
} break;
}
}
else {
switch (test_phase) {
case 0: { // phase 0 without OCD warning - keep on decrementing if can
if (OCD_TH_val) {
OCD_TH_val--; // try lower value
//DEBUG_ECHOLNPGM("LOGIC N0A - DEC OCD");
DEBUG_ECHOLNPGM("DEC OCD");
}
else {
test_phase = 2; // at lowest value without warning so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC N0B - OCD at lowest (go to phase 2)");
DEBUG_ECHOLNPGM("OCD finalized");
}
} break;
case 1: //DEBUG_ECHOLNPGM("LOGIC N1 (go directly to 2)"); // phase 1 without OCD warning - drop directly to phase 2
DEBUG_ECHOLNPGM("OCD finalized");
case 2: { // phase 2 without stall warning - keep on decrementing if can
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (STALL_TH_val) {
STALL_TH_val--; // try a lower value (stay in phase 2)
//DEBUG_ECHOLNPGM("LOGIC N2B - dec STALL");
DEBUG_ECHOLNPGM("dec STALL");
}
else {
DEBUG_ECHOLNPGM("finished - STALL at lowest value but still no stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC N2C - STALL at lowest (quit)");
}
} break;
case 3: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC N3 - finished!");
DEBUG_ECHOLNPGM("finished!");
} break; // phase 3 without any warnings - desired exit
} //
} // end of status checks
if (test_phase != 4) {
for (j = 0; j < driver_count; j++) { // update threshold(s)
L64xxManager.set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) L64xxManager.set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val);
if (L64xxManager.get_param(axis_index[j], L6470_OCD_TH) != OCD_TH_val) DEBUG_ECHOLNPGM("OCD mismatch");
if ((L64xxManager.get_param(axis_index[j], L6470_STALL_TH) != STALL_TH_val) && (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT)) DEBUG_ECHOLNPGM("STALL mismatch");
}
}
} while (test_phase != 4);
DEBUG_ECHOLNPGM(".");
if (status_composite) {
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
DEBUG_ECHOLNPGM(".");
#endif
DEBUG_ECHOLNPGM("Completed with errors");
}
else
DEBUG_ECHOLNPGM("Completed with no errors");
DEBUG_ECHOLNPGM(".");
L64xxManager.pause_monitor(false);
}
/**
*
* M918: increase speed until error or max feedrate achieved (as shown in configuration.h))
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* I - over current threshold
* optional - will report current value from driver if not specified
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
* M - value for microsteps (1 - 128) (optional)
* optional - will report current value from driver if not specified
*
*/
void GcodeSuite::M918() {
DEBUG_ECHOLNPGM("M918");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max, position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = true;
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
L64xxManager.get_status(axis_index[0]); // populate shadow array
uint8_t m_steps = parser.byteval('M');
if (m_steps != 0) {
LIMIT(m_steps, 1, sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT ? 16 : 128); // L6474
uint8_t stepVal;
for (stepVal = 0; stepVal < 8; stepVal++) { // convert to L64xx register value
if (m_steps == 1) break;
m_steps >>= 1;
}
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)
stepVal |= 0x98; // NO SYNC
else
stepVal |= (!SYNC_EN) | SYNC_SEL_1 | stepVal;
for (j = 0; j < driver_count; j++) {
L64xxManager.set_param(axis_index[j], dSPIN_HARD_HIZ, 0); // can't write STEP register if stepper being powered
// results in an extra NOOP being sent (data 00)
L64xxManager.set_param(axis_index[j], L6470_STEP_MODE, stepVal); // set microsteps
}
}
m_steps = L64xxManager.get_param(axis_index[0], L6470_STEP_MODE) & 0x07; // get microsteps
DEBUG_ECHOLNPAIR("Microsteps = ", _BV(m_steps));
DEBUG_ECHOLNPAIR("target (maximum) feedrate = ", final_feedrate);
const float feedrate_inc = final_feedrate / 10, // Start at 1/10 of max & go up by 1/10 per step
fr_limit = final_feedrate * 0.99f; // Rounding-safe comparison value
float current_feedrate = 0;
planner.synchronize(); // Wait for moves to complete
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // Clear error flags
char temp_axis_string[2] = " ";
temp_axis_string[0] = axis_mon[0][0]; // Need a sprintf format string
//temp_axis_string[1] = '\n';
char gcode_string[80];
uint16_t status_composite = 0;
DEBUG_ECHOLNPGM(".\n.\n."); // Make feedrate outputs easier to read
do {
current_feedrate += feedrate_inc;
DEBUG_ECHOLNPAIR("...feedrate = ", current_feedrate);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(current_feedrate));
gcode.process_subcommands_now_P(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(current_feedrate));
gcode.process_subcommands_now_P(gcode_string);
planner.synchronize();
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & 0x0800; // Bits of interest are all active LOW
status_composite |= axis_status[j];
}
if (status_composite) break; // Break on any error
} while (current_feedrate < fr_limit);
DEBUG_ECHOPGM("Completed with ");
if (status_composite) {
DEBUG_ECHOLNPGM("errors");
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
#endif
}
else
DEBUG_ECHOLNPGM("no errors");
L64xxManager.pause_monitor(false);
}
#endif // HAS_L64XX

View File

@@ -0,0 +1,147 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(LIN_ADVANCE)
#include "../../gcode.h"
#include "../../../module/planner.h"
#include "../../../module/stepper.h"
#if ENABLED(EXTRA_LIN_ADVANCE_K)
float other_extruder_advance_K[EXTRUDERS];
uint8_t lin_adv_slot = 0;
#endif
/**
* M900: Get or Set Linear Advance K-factor
* T<tool> Which tool to address
* K<factor> Set current advance K factor (Slot 0).
* L<factor> Set secondary advance K factor (Slot 1). Requires EXTRA_LIN_ADVANCE_K.
* S<0/1> Activate slot 0 or 1. Requires EXTRA_LIN_ADVANCE_K.
*/
void GcodeSuite::M900() {
auto echo_value_oor = [](const char ltr, const bool ten=true) {
SERIAL_CHAR('?'); SERIAL_CHAR(ltr);
SERIAL_ECHOPGM(" value out of range");
if (ten) SERIAL_ECHOPGM(" (0-10)");
SERIAL_ECHOLNPGM(".");
};
#if EXTRUDERS < 2
constexpr uint8_t tool_index = 0;
#else
const uint8_t tool_index = parser.intval('T', active_extruder);
if (tool_index >= EXTRUDERS) {
echo_value_oor('T', false);
return;
}
#endif
float &kref = planner.extruder_advance_K[tool_index], newK = kref;
const float oldK = newK;
#if ENABLED(EXTRA_LIN_ADVANCE_K)
float &lref = other_extruder_advance_K[tool_index];
const bool old_slot = TEST(lin_adv_slot, tool_index), // The tool's current slot (0 or 1)
new_slot = parser.boolval('S', old_slot); // The passed slot (default = current)
// If a new slot is being selected swap the current and
// saved K values. Do here so K/L will apply correctly.
if (new_slot != old_slot) { // Not the same slot?
SET_BIT_TO(lin_adv_slot, tool_index, new_slot); // Update the slot for the tool
newK = lref; // Get new K value from backup
lref = oldK; // Save K to backup
}
// Set the main K value. Apply if the main slot is active.
if (parser.seenval('K')) {
const float K = parser.value_float();
if (!WITHIN(K, 0, 10)) echo_value_oor('K');
else if (new_slot) lref = K; // S1 Knn
else newK = K; // S0 Knn
}
// Set the extra K value. Apply if the extra slot is active.
if (parser.seenval('L')) {
const float L = parser.value_float();
if (!WITHIN(L, 0, 10)) echo_value_oor('L');
else if (!new_slot) lref = L; // S0 Lnn
else newK = L; // S1 Lnn
}
#else
if (parser.seenval('K')) {
const float K = parser.value_float();
if (WITHIN(K, 0, 10))
newK = K;
else
echo_value_oor('K');
}
#endif
if (newK != oldK) {
planner.synchronize();
kref = newK;
}
if (!parser.seen_any()) {
#if ENABLED(EXTRA_LIN_ADVANCE_K)
#if EXTRUDERS < 2
SERIAL_ECHOLNPAIR("Advance S", int(new_slot), " K", kref, "(S", int(!new_slot), " K", lref, ")");
#else
LOOP_L_N(i, EXTRUDERS) {
const bool slot = TEST(lin_adv_slot, i);
SERIAL_ECHOLNPAIR("Advance T", int(i), " S", int(slot), " K", planner.extruder_advance_K[i],
"(S", int(!slot), " K", other_extruder_advance_K[i], ")");
SERIAL_EOL();
}
#endif
#else
SERIAL_ECHO_START();
#if EXTRUDERS < 2
SERIAL_ECHOLNPAIR("Advance K=", planner.extruder_advance_K[0]);
#else
SERIAL_ECHOPGM("Advance K");
LOOP_L_N(i, EXTRUDERS) {
SERIAL_CHAR(' ', '0' + i, ':');
SERIAL_ECHO(planner.extruder_advance_K[i]);
}
SERIAL_EOL();
#endif
#endif
}
}
#endif // LIN_ADVANCE

View File

@@ -0,0 +1,58 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(BARICUDA)
#include "../../gcode.h"
#include "../../../feature/baricuda.h"
#if HAS_HEATER_1
/**
* M126: Heater 1 valve open
*/
void GcodeSuite::M126() { baricuda_valve_pressure = parser.byteval('S', 255); }
/**
* M127: Heater 1 valve close
*/
void GcodeSuite::M127() { baricuda_valve_pressure = 0; }
#endif // HAS_HEATER_1
#if HAS_HEATER_2
/**
* M128: Heater 2 valve open
*/
void GcodeSuite::M128() { baricuda_e_to_p_pressure = parser.byteval('S', 255); }
/**
* M129: Heater 2 valve close
*/
void GcodeSuite::M129() { baricuda_e_to_p_pressure = 0; }
#endif // HAS_HEATER_2
#endif // BARICUDA

View File

@@ -0,0 +1,204 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(PHOTO_GCODE)
#include "../../gcode.h"
#include "../../../module/motion.h" // for active_extruder and current_position
#if PIN_EXISTS(CHDK)
millis_t chdk_timeout; // = 0
#endif
#if defined(PHOTO_POSITION) && PHOTO_DELAY_MS > 0
#include "../../../MarlinCore.h" // for idle()
#endif
#ifdef PHOTO_RETRACT_MM
#define _PHOTO_RETRACT_MM (PHOTO_RETRACT_MM + 0)
#include "../../../module/planner.h"
#include "../../../module/temperature.h"
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#include "../../../feature/pause.h"
#endif
#ifdef PHOTO_RETRACT_MM
inline void e_move_m240(const float length, const feedRate_t &fr_mm_s) {
if (length && thermalManager.hotEnoughToExtrude(active_extruder))
unscaled_e_move(length, fr_mm_s);
}
#endif
#endif
#if PIN_EXISTS(PHOTOGRAPH)
FORCE_INLINE void set_photo_pin(const uint8_t state) {
constexpr uint32_t pulse_length = (
#ifdef PHOTO_PULSES_US
PHOTO_PULSE_DELAY_US
#else
15 // 15.24 from _delay_ms(0.01524)
#endif
);
WRITE(PHOTOGRAPH_PIN, state);
delayMicroseconds(pulse_length);
}
FORCE_INLINE void tweak_photo_pin() { set_photo_pin(HIGH); set_photo_pin(LOW); }
#ifdef PHOTO_PULSES_US
inline void pulse_photo_pin(const uint32_t duration, const uint8_t state) {
if (state) {
for (const uint32_t stop = micros() + duration; micros() < stop;)
tweak_photo_pin();
}
else
delayMicroseconds(duration);
}
inline void spin_photo_pin() {
static constexpr uint32_t sequence[] = PHOTO_PULSES_US;
LOOP_L_N(i, COUNT(sequence))
pulse_photo_pin(sequence[i], !(i & 1));
}
#else
constexpr uint8_t NUM_PULSES = 16;
inline void spin_photo_pin() { for (uint8_t i = NUM_PULSES; i--;) tweak_photo_pin(); }
#endif
#endif
/**
* M240: Trigger a camera by...
*
* - CHDK : Emulate a Canon RC-1 with a configurable ON duration.
* http://captain-slow.dk/2014/03/09/3d-printing-timelapses/
* - PHOTOGRAPH_PIN : Pulse a digital pin 16 times.
* See http://www.doc-diy.net/photo/rc-1_hacked/
* - PHOTO_SWITCH_POSITION : Bump a physical switch with the X-carriage using a
* configured position, delay, and retract length.
*
* PHOTO_POSITION parameters:
* A - X offset to the return position
* B - Y offset to the return position
* F - Override the XY movement feedrate
* R - Retract/recover length (current units)
* S - Retract/recover feedrate (mm/m)
* X - Move to X before triggering the shutter
* Y - Move to Y before triggering the shutter
* Z - Raise Z by a distance before triggering the shutter
*
* PHOTO_SWITCH_POSITION parameters:
* D - Duration (ms) to hold down switch (Requires PHOTO_SWITCH_MS)
* P - Delay (ms) after triggering the shutter (Requires PHOTO_SWITCH_MS)
* I - Switch trigger position override X
* J - Switch trigger position override Y
*/
void GcodeSuite::M240() {
#ifdef PHOTO_POSITION
if (axis_unhomed_error()) return;
const xyz_pos_t old_pos = {
current_position.x + parser.linearval('A'),
current_position.y + parser.linearval('B'),
current_position.z
};
#ifdef PHOTO_RETRACT_MM
const float rval = parser.seenval('R') ? parser.value_linear_units() : _PHOTO_RETRACT_MM;
feedRate_t sval = (
#if ENABLED(ADVANCED_PAUSE_FEATURE)
PAUSE_PARK_RETRACT_FEEDRATE
#elif ENABLED(FWRETRACT)
RETRACT_FEEDRATE
#else
45
#endif
);
if (parser.seenval('S')) sval = parser.value_feedrate();
e_move_m240(-rval, sval);
#endif
feedRate_t fr_mm_s = MMM_TO_MMS(parser.linearval('F'));
if (fr_mm_s) NOLESS(fr_mm_s, 10.0f);
constexpr xyz_pos_t photo_position = PHOTO_POSITION;
xyz_pos_t raw = {
parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position.x,
parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position.y,
(parser.seenval('Z') ? parser.value_linear_units() : photo_position.z) + current_position.z
};
apply_motion_limits(raw);
do_blocking_move_to(raw, fr_mm_s);
#ifdef PHOTO_SWITCH_POSITION
constexpr xy_pos_t photo_switch_position = PHOTO_SWITCH_POSITION;
const xy_pos_t sraw = {
parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position.x,
parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position.y
};
do_blocking_move_to_xy(sraw, get_homing_bump_feedrate(X_AXIS));
#if PHOTO_SWITCH_MS > 0
safe_delay(parser.intval('D', PHOTO_SWITCH_MS));
#endif
do_blocking_move_to(raw);
#endif
#endif
#if PIN_EXISTS(CHDK)
OUT_WRITE(CHDK_PIN, HIGH);
chdk_timeout = millis() + parser.intval('D', PHOTO_SWITCH_MS);
#elif HAS_PHOTOGRAPH
spin_photo_pin();
delay(7.33);
spin_photo_pin();
#endif
#ifdef PHOTO_POSITION
#if PHOTO_DELAY_MS > 0
const millis_t timeout = millis() + parser.intval('P', PHOTO_DELAY_MS);
while (PENDING(millis(), timeout)) idle();
#endif
do_blocking_move_to(old_pos, fr_mm_s);
#ifdef PHOTO_RETRACT_MM
e_move_m240(rval, sval);
#endif
#endif
}
#endif // PHOTO_GCODE

View File

@@ -0,0 +1,57 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(CANCEL_OBJECTS)
#include "../../gcode.h"
#include "../../../feature/cancel_object.h"
/**
* M486: A simple interface to cancel objects
*
* T[count] : Reset objects and/or set the count
* S<index> : Start an object with the given index
* P<index> : Cancel the object with the given index
* U<index> : Un-cancel object with the given index
* C : Cancel the current object (the last index given by S<index>)
* S-1 : Start a non-object like a brim or purge tower that should always print
*/
void GcodeSuite::M486() {
if (parser.seen('T')) {
cancelable.reset();
cancelable.object_count = parser.intval('T', 1);
}
if (parser.seen('S'))
cancelable.set_active_object(parser.value_int());
if (parser.seen('C')) cancelable.cancel_active_object();
if (parser.seen('P')) cancelable.cancel_object(parser.value_int());
if (parser.seen('U')) cancelable.uncancel_object(parser.value_int());
}
#endif // CANCEL_OBJECTS

View File

@@ -0,0 +1,64 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../gcode.h"
#include "../../../inc/MarlinConfig.h"
#if HAS_CASE_LIGHT
#include "../../../feature/caselight.h"
/**
* M355: Turn case light on/off and set brightness
*
* P<byte> Set case light brightness (PWM pin required - ignored otherwise)
*
* S<bool> Set case light on/off
*
* When S turns on the light on a PWM pin then the current brightness level is used/restored
*
* M355 P200 S0 turns off the light & sets the brightness level
* M355 S1 turns on the light with a brightness of 200 (assuming a PWM pin)
*/
void GcodeSuite::M355() {
uint8_t args = 0;
if (parser.seenval('P')) {
++args, case_light_brightness = parser.value_byte();
case_light_arg_flag = false;
}
if (parser.seenval('S')) {
++args, case_light_on = parser.value_bool();
case_light_arg_flag = true;
}
if (args) update_case_light();
// always report case light status
SERIAL_ECHO_START();
if (!case_light_on) {
SERIAL_ECHOLNPGM("Case light: off");
}
else {
if (!PWM_PIN(CASE_LIGHT_PIN)) SERIAL_ECHOLNPGM("Case light: on");
else SERIAL_ECHOLNPAIR("Case light: ", case_light_brightness);
}
}
#endif // HAS_CASE_LIGHT

View File

@@ -0,0 +1,66 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(NOZZLE_CLEAN_FEATURE)
#include "../../../libs/nozzle.h"
#include "../../gcode.h"
#include "../../parser.h"
#include "../../../module/motion.h"
#if HAS_LEVELING
#include "../../../module/planner.h"
#include "../../../feature/bedlevel/bedlevel.h"
#endif
/**
* G12: Clean the nozzle
*/
void GcodeSuite::G12() {
// Don't allow nozzle cleaning without homing first
if (axis_unhomed_error()) return;
const uint8_t pattern = parser.ushortval('P', 0),
strokes = parser.ushortval('S', NOZZLE_CLEAN_STROKES),
objects = parser.ushortval('T', NOZZLE_CLEAN_TRIANGLES);
const float radius = parser.floatval('R', NOZZLE_CLEAN_CIRCLE_RADIUS);
const bool seenxyz = parser.seen("XYZ");
const uint8_t cleans = (!seenxyz || parser.boolval('X') ? _BV(X_AXIS) : 0)
| (!seenxyz || parser.boolval('Y') ? _BV(Y_AXIS) : 0)
#if DISABLED(NOZZLE_CLEAN_NO_Z)
| (!seenxyz || parser.boolval('Z') ? _BV(Z_AXIS) : 0)
#endif
;
#if HAS_LEVELING
// Disable bed leveling if cleaning Z
TEMPORARY_BED_LEVELING_STATE(!TEST(cleans, Z_AXIS) && planner.leveling_active);
#endif
nozzle.clean(pattern, strokes, radius, objects, cleans);
}
#endif // NOZZLE_CLEAN_FEATURE

View File

@@ -0,0 +1,81 @@
/**
* Marlin 3D Printer Firmware
* Copyright (C) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(CONTROLLER_FAN_EDITABLE)
#include "../../gcode.h"
#include "../../../feature/controllerfan.h"
void M710_report(const bool forReplay) {
if (!forReplay) { SERIAL_ECHOLNPGM("; Controller Fan"); SERIAL_ECHO_START(); }
SERIAL_ECHOLNPAIR("M710 "
"S", int(controllerFan.settings.active_speed),
"I", int(controllerFan.settings.idle_speed),
"A", int(controllerFan.settings.auto_mode),
"D", controllerFan.settings.duration,
" ; (", (int(controllerFan.settings.active_speed) * 100) / 255, "%"
" ", (int(controllerFan.settings.idle_speed) * 100) / 255, "%)"
);
}
/**
* M710: Set controller fan settings
*
* R : Reset to defaults
* S[0-255] : Fan speed when motors are active
* I[0-255] : Fan speed when motors are idle
* A[0|1] : Turn auto mode on or off
* D : Set auto mode idle duration
*
* Examples:
* M710 ; Report current Settings
* M710 R ; Reset SIAD to defaults
* M710 I64 ; Set controller fan Idle Speed to 25%
* M710 S255 ; Set controller fan Active Speed to 100%
* M710 S0 ; Set controller fan Active Speed to OFF
* M710 I255 A0 ; Set controller fan Idle Speed to 100% with Auto Mode OFF
* M710 I127 A1 S255 D160 ; Set controller fan idle speed 50%, AutoMode On, Fan speed 100%, duration to 160 Secs
*/
void GcodeSuite::M710() {
const bool seenR = parser.seen('R');
if (seenR) controllerFan.reset();
const bool seenS = parser.seenval('S');
if (seenS) controllerFan.settings.active_speed = parser.value_byte();
const bool seenI = parser.seenval('I');
if (seenI) controllerFan.settings.idle_speed = parser.value_byte();
const bool seenA = parser.seenval('A');
if (seenA) controllerFan.settings.auto_mode = parser.value_bool();
const bool seenD = parser.seenval('D');
if (seenD) controllerFan.settings.duration = parser.value_ushort();
if (!(seenR || seenS || seenI || seenA || seenD))
M710_report(false);
}
#endif // CONTROLLER_FAN_EDITABLE

View File

@@ -0,0 +1,106 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM || EITHER(DIGIPOT_I2C, DAC_STEPPER_CURRENT)
#include "../../gcode.h"
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM
#include "../../../module/stepper.h"
#endif
#if ENABLED(DIGIPOT_I2C)
#include "../../../feature/digipot/digipot.h"
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
#include "../../../feature/dac/stepper_dac.h"
#endif
/**
* M907: Set digital trimpot motor current using axis codes X, Y, Z, E, B, S
*/
void GcodeSuite::M907() {
#if HAS_DIGIPOTSS
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.digipot_current(i, parser.value_int());
if (parser.seenval('B')) stepper.digipot_current(4, parser.value_int());
if (parser.seenval('S')) LOOP_LE_N(i, 4) stepper.digipot_current(i, parser.value_int());
#elif HAS_MOTOR_CURRENT_PWM
#if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY)
if (parser.seenval('X') || parser.seenval('Y')) stepper.digipot_current(0, parser.value_int());
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
if (parser.seenval('Z')) stepper.digipot_current(1, parser.value_int());
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
if (parser.seenval('E')) stepper.digipot_current(2, parser.value_int());
#endif
#endif
#if ENABLED(DIGIPOT_I2C)
// this one uses actual amps in floating point
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) digipot_i2c_set_current(i, parser.value_float());
// Additional extruders use B,C,D for channels 4,5,6.
// TODO: Change these parameters because 'E' is used. B<index>?
for (uint8_t i = E_AXIS + 1; i < DIGIPOT_I2C_NUM_CHANNELS; i++)
if (parser.seenval('B' + i - (E_AXIS + 1))) digipot_i2c_set_current(i, parser.value_float());
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
if (parser.seenval('S')) {
const float dac_percent = parser.value_float();
LOOP_LE_N(i, 4) dac_current_percent(i, dac_percent);
}
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) dac_current_percent(i, parser.value_float());
#endif
}
#if HAS_DIGIPOTSS || ENABLED(DAC_STEPPER_CURRENT)
/**
* M908: Control digital trimpot directly (M908 P<pin> S<current>)
*/
void GcodeSuite::M908() {
#if HAS_DIGIPOTSS
stepper.digitalPotWrite(parser.intval('P'), parser.intval('S'));
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
dac_current_raw(parser.byteval('P', -1), parser.ushortval('S', 0));
#endif
}
#endif // HAS_DIGIPOTSS || DAC_STEPPER_CURRENT
#if ENABLED(DAC_STEPPER_CURRENT)
void GcodeSuite::M909() { dac_print_values(); }
void GcodeSuite::M910() { dac_commit_eeprom(); }
#endif // DAC_STEPPER_CURRENT
#endif // HAS_DIGIPOTSS || DAC_STEPPER_CURRENT || HAS_MOTOR_CURRENT_PWM || DIGIPOT_I2C

View File

@@ -0,0 +1,72 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(FILAMENT_WIDTH_SENSOR)
#include "../../../feature/filwidth.h"
#include "../../../module/planner.h"
#include "../../../module/temperature.h"
#include "../../../MarlinCore.h"
#include "../../gcode.h"
/**
* M404: Display or set (in current units) the nominal filament width (3mm, 1.75mm ) W<3.0>
*/
void GcodeSuite::M404() {
if (parser.seenval('W')) {
filwidth.nominal_mm = parser.value_linear_units();
planner.volumetric_area_nominal = CIRCLE_AREA(filwidth.nominal_mm * 0.5);
}
else
SERIAL_ECHOLNPAIR("Filament dia (nominal mm):", filwidth.nominal_mm);
}
/**
* M405: Turn on filament sensor for control
*/
void GcodeSuite::M405() {
// This is technically a linear measurement, but since it's quantized to centimeters and is a different
// unit than everything else, it uses parser.value_byte() instead of parser.value_linear_units().
if (parser.seenval('D'))
filwidth.set_delay_cm(parser.value_byte());
filwidth.enable(true);
}
/**
* M406: Turn off filament sensor for control
*/
void GcodeSuite::M406() {
filwidth.enable(false);
planner.calculate_volumetric_multipliers(); // Restore correct 'volumetric_multiplier' value
}
/**
* M407: Get measured filament diameter on serial output
*/
void GcodeSuite::M407() {
SERIAL_ECHOLNPAIR("Filament dia (measured mm):", filwidth.measured_mm);
}
#endif // FILAMENT_WIDTH_SENSOR

View File

@@ -0,0 +1,51 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(FWRETRACT)
#include "../../../feature/fwretract.h"
#include "../../gcode.h"
#include "../../../module/motion.h"
/**
* G10 - Retract filament according to settings of M207
* TODO: Handle 'G10 P' for tool settings and 'G10 L' for workspace settings
*/
void GcodeSuite::G10() {
#if EXTRUDERS > 1
const bool rs = parser.boolval('S');
#endif
fwretract.retract(true
#if EXTRUDERS > 1
, rs
#endif
);
}
/**
* G11 - Recover filament according to settings of M208
*/
void GcodeSuite::G11() { fwretract.retract(false); }
#endif // FWRETRACT

View File

@@ -0,0 +1,74 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(FWRETRACT)
#include "../../../feature/fwretract.h"
#include "../../gcode.h"
/**
* M207: Set firmware retraction values
*
* S[+units] retract_length
* W[+units] swap_retract_length (multi-extruder)
* F[units/min] retract_feedrate_mm_s
* Z[units] retract_zraise
*/
void GcodeSuite::M207() {
if (parser.seen('S')) fwretract.settings.retract_length = parser.value_axis_units(E_AXIS);
if (parser.seen('F')) fwretract.settings.retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('Z')) fwretract.settings.retract_zraise = parser.value_linear_units();
if (parser.seen('W')) fwretract.settings.swap_retract_length = parser.value_axis_units(E_AXIS);
}
/**
* M208: Set firmware un-retraction values
*
* S[+units] retract_recover_extra (in addition to M207 S*)
* W[+units] swap_retract_recover_extra (multi-extruder)
* F[units/min] retract_recover_feedrate_mm_s
* R[units/min] swap_retract_recover_feedrate_mm_s
*/
void GcodeSuite::M208() {
if (parser.seen('S')) fwretract.settings.retract_recover_extra = parser.value_axis_units(E_AXIS);
if (parser.seen('F')) fwretract.settings.retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('R')) fwretract.settings.swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('W')) fwretract.settings.swap_retract_recover_extra = parser.value_axis_units(E_AXIS);
}
#if ENABLED(FWRETRACT_AUTORETRACT)
/**
* M209: Enable automatic retract (M209 S1)
* For slicers that don't support G10/11, reversed extrude-only
* moves will be classified as retraction.
*/
void GcodeSuite::M209() {
if (MIN_AUTORETRACT <= MAX_AUTORETRACT && parser.seen('S'))
fwretract.enable_autoretract(parser.value_bool());
}
#endif // FWRETRACT_AUTORETRACT
#endif // FWRETRACT

View File

@@ -0,0 +1,77 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(EXPERIMENTAL_I2CBUS)
#include "../../gcode.h"
#include "../../../MarlinCore.h" // for i2c
/**
* M260: Send data to a I2C slave device
*
* This is a PoC, the formating and arguments for the GCODE will
* change to be more compatible, the current proposal is:
*
* M260 A<slave device address base 10> ; Sets the I2C slave address the data will be sent to
*
* M260 B<byte-1 value in base 10>
* M260 B<byte-2 value in base 10>
* M260 B<byte-3 value in base 10>
*
* M260 S1 ; Send the buffered data and reset the buffer
* M260 R1 ; Reset the buffer without sending data
*
*/
void GcodeSuite::M260() {
// Set the target address
if (parser.seen('A')) i2c.address(parser.value_byte());
// Add a new byte to the buffer
if (parser.seen('B')) i2c.addbyte(parser.value_byte());
// Flush the buffer to the bus
if (parser.seen('S')) i2c.send();
// Reset and rewind the buffer
else if (parser.seen('R')) i2c.reset();
}
/**
* M261: Request X bytes from I2C slave device
*
* Usage: M261 A<slave device address base 10> B<number of bytes>
*/
void GcodeSuite::M261() {
if (parser.seen('A')) i2c.address(parser.value_byte());
uint8_t bytes = parser.byteval('B', 1);
if (i2c.addr && bytes && bytes <= TWIBUS_BUFFER_SIZE)
i2c.relay(bytes);
else
SERIAL_ERROR_MSG("Bad i2c request");
}
#endif

View File

@@ -0,0 +1,57 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_COLOR_LEDS
#include "../../gcode.h"
#include "../../../feature/leds/leds.h"
/**
* M150: Set Status LED Color - Use R-U-B-W for R-G-B-W
* and Brightness - Use P (for NEOPIXEL only)
*
* Always sets all 3 or 4 components. If a component is left out, set to 0.
* If brightness is left out, no value changed
*
* Examples:
*
* M150 R255 ; Turn LED red
* M150 R255 U127 ; Turn LED orange (PWM only)
* M150 ; Turn LED off
* M150 R U B ; Turn LED white
* M150 W ; Turn LED white using a white LED
* M150 P127 ; Set LED 50% brightness
* M150 P ; Set LED full brightness
*/
void GcodeSuite::M150() {
leds.set_color(MakeLEDColor(
parser.seen('R') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('U') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('B') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('W') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('P') ? (parser.has_value() ? parser.value_byte() : 255) : neo.brightness()
));
}
#endif // HAS_COLOR_LEDS

View File

@@ -0,0 +1,93 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(MAX7219_GCODE)
#include "../../gcode.h"
#include "../../../feature/max7219.h"
/**
* M7219: Control the Max7219 LED matrix
*
* I - Initialize (clear) the matrix
* F - Fill the matrix (set all bits)
* P - Dump the led_line[] array values
* C<column> - Set a column to the bitmask given by 'V' (Units 0-3 in portrait layout)
* R<row> - Set a row to the bitmask given by 'V' (Units 0-3 in landscape layout)
* X<pos> - X index of an LED to set or toggle
* Y<pos> - Y index of an LED to set or toggle
* V<value> - LED on/off state or row/column bitmask (8, 16, 24, or 32-bits)
* ('C' / 'R' can be used to update up to 4 units at once)
*
* Directly set a native matrix row to the 8-bit value 'V':
* D<line> - Display line (0..7)
* U<unit> - Unit index (0..MAX7219_NUMBER_UNITS-1)
*/
void GcodeSuite::M7219() {
if (parser.seen('I')) {
max7219.register_setup();
max7219.clear();
}
if (parser.seen('F')) max7219.fill();
const uint32_t v = parser.ulongval('V');
if (parser.seenval('R')) {
const uint8_t r = parser.value_byte();
max7219.set_row(r, v);
}
else if (parser.seenval('C')) {
const uint8_t c = parser.value_byte();
max7219.set_column(c, v);
}
else if (parser.seenval('X') || parser.seenval('Y')) {
const uint8_t x = parser.byteval('X'), y = parser.byteval('Y');
if (parser.seenval('V'))
max7219.led_set(x, y, v > 0);
else
max7219.led_toggle(x, y);
}
else if (parser.seen('D')) {
const uint8_t uline = parser.value_byte() & 0x7,
line = uline + (parser.byteval('U') << 3);
if (line < MAX7219_LINES) {
max7219.led_line[line] = v;
return max7219.refresh_line(line);
}
}
if (parser.seen('P')) {
LOOP_L_N(r, MAX7219_LINES) {
SERIAL_ECHOPGM("led_line[");
if (r < 10) SERIAL_CHAR(' ');
SERIAL_ECHO(int(r));
SERIAL_ECHOPGM("]=");
for (uint8_t b = 8; b--;) SERIAL_CHAR('0' + TEST(max7219.led_line[r], b));
SERIAL_EOL();
}
}
}
#endif // MAX7219_GCODE

View File

@@ -0,0 +1,65 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(GCODE_MACROS)
#include "../../gcode.h"
#include "../../queue.h"
#include "../../parser.h"
char gcode_macros[GCODE_MACROS_SLOTS][GCODE_MACROS_SLOT_SIZE + 1] = {{ 0 }};
/**
* M810_819: Set/execute a G-code macro.
*
* Usage:
* M810 <command>|... Set Macro 0 to the given commands, separated by the pipe character
* M810 Execute Macro 0
*/
void GcodeSuite::M810_819() {
const uint8_t index = parser.codenum - 810;
if (index >= GCODE_MACROS_SLOTS) return;
const size_t len = strlen(parser.string_arg);
if (len) {
// Set a macro
if (len > GCODE_MACROS_SLOT_SIZE)
SERIAL_ERROR_MSG("Macro too long.");
else {
char c, *s = parser.string_arg, *d = gcode_macros[index];
do {
c = *s++;
*d++ = c == '|' ? '\n' : c;
} while (c);
}
}
else {
// Execute a macro
char * const cmd = gcode_macros[index];
if (strlen(cmd)) process_subcommands_now(cmd);
}
}
#endif // GCODE_MACROS

View File

@@ -0,0 +1,101 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(MIXING_EXTRUDER)
#include "../../gcode.h"
#include "../../../feature/mixing.h"
/**
* M163: Set a single mix factor for a mixing extruder
* This is called "weight" by some systems.
* Must be followed by M164 to normalize and commit them.
*
* S[index] The channel index to set
* P[float] The mix value
*/
void GcodeSuite::M163() {
const int mix_index = parser.intval('S');
if (mix_index < MIXING_STEPPERS)
mixer.set_collector(mix_index, parser.floatval('P'));
}
/**
* M164: Normalize and commit the mix.
*
* S[index] The virtual tool to store
* If 'S' is omitted update the active virtual tool.
*/
void GcodeSuite::M164() {
#if MIXING_VIRTUAL_TOOLS > 1
const int tool_index = parser.intval('S', -1);
#else
constexpr int tool_index = 0;
#endif
if (tool_index >= 0) {
if (tool_index < MIXING_VIRTUAL_TOOLS)
mixer.normalize(tool_index);
}
else
mixer.normalize();
}
#if ENABLED(DIRECT_MIXING_IN_G1)
/**
* M165: Set multiple mix factors for a mixing extruder.
* Omitted factors will be set to 0.
* The mix is normalized and stored in the current virtual tool.
*
* A[factor] Mix factor for extruder stepper 1
* B[factor] Mix factor for extruder stepper 2
* C[factor] Mix factor for extruder stepper 3
* D[factor] Mix factor for extruder stepper 4
* H[factor] Mix factor for extruder stepper 5
* I[factor] Mix factor for extruder stepper 6
*/
void GcodeSuite::M165() {
// Get mixing parameters from the GCode
// The total "must" be 1.0 (but it will be normalized)
// If no mix factors are given, the old mix is preserved
const char mixing_codes[] = { LIST_N(MIXING_STEPPERS, 'A', 'B', 'C', 'D', 'H', 'I') };
uint8_t mix_bits = 0;
MIXER_STEPPER_LOOP(i) {
if (parser.seenval(mixing_codes[i])) {
SBI(mix_bits, i);
mixer.set_collector(i, parser.value_float());
}
}
// If any mixing factors were included, clear the rest
// If none were included, preserve the last mix
if (mix_bits) {
MIXER_STEPPER_LOOP(i)
if (!TEST(mix_bits, i)) mixer.set_collector(i, 0.0f);
mixer.normalize();
}
}
#endif // DIRECT_MIXING_IN_G1
#endif // MIXING_EXTRUDER

View File

@@ -0,0 +1,96 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(GRADIENT_MIX)
#include "../../gcode.h"
#include "../../../module/motion.h"
#include "../../../module/planner.h"
#include "../../../feature/mixing.h"
inline void echo_mix() {
SERIAL_ECHOPAIR(" (", int(mixer.mix[0]), "%|", int(mixer.mix[1]), "%)");
}
inline void echo_zt(const int t, const float &z) {
mixer.update_mix_from_vtool(t);
SERIAL_ECHOPAIR_P(SP_Z_STR, z, SP_T_STR, t);
echo_mix();
}
/**
* M166: Set a simple gradient mix for a two-component mixer
* based on the Geeetech A10M implementation by Jone Liu.
*
* S[bool] - Enable / disable gradients
* A[float] - Starting Z for the gradient
* Z[float] - Ending Z for the gradient. (Must be greater than the starting Z.)
* I[index] - V-Tool to use as the starting mix.
* J[index] - V-Tool to use as the ending mix.
*
* T[index] - A V-Tool index to use as an alias for the Gradient (Requires GRADIENT_VTOOL)
* T with no index clears the setting. Note: This can match the I or J value.
*
* Example: M166 S1 A0 Z20 I0 J1
*/
void GcodeSuite::M166() {
if (parser.seenval('A')) mixer.gradient.start_z = parser.value_float();
if (parser.seenval('Z')) mixer.gradient.end_z = parser.value_float();
if (parser.seenval('I')) mixer.gradient.start_vtool = (uint8_t)constrain(parser.value_int(), 0, MIXING_VIRTUAL_TOOLS);
if (parser.seenval('J')) mixer.gradient.end_vtool = (uint8_t)constrain(parser.value_int(), 0, MIXING_VIRTUAL_TOOLS);
#if ENABLED(GRADIENT_VTOOL)
if (parser.seen('T')) mixer.gradient.vtool_index = parser.byteval('T', -1);
#endif
if (parser.seen('S')) mixer.gradient.enabled = parser.value_bool();
mixer.refresh_gradient();
SERIAL_ECHOPGM("Gradient Mix ");
serialprint_onoff(mixer.gradient.enabled);
if (mixer.gradient.enabled) {
#if ENABLED(GRADIENT_VTOOL)
if (mixer.gradient.vtool_index >= 0) {
SERIAL_ECHOPAIR(" (T", int(mixer.gradient.vtool_index));
SERIAL_CHAR(')');
}
#endif
SERIAL_ECHOPGM(" ; Start");
echo_zt(mixer.gradient.start_vtool, mixer.gradient.start_z);
SERIAL_ECHOPGM(" ; End");
echo_zt(mixer.gradient.end_vtool, mixer.gradient.end_z);
mixer.update_mix_from_gradient();
SERIAL_ECHOPAIR(" ; Current Z", planner.get_axis_position_mm(Z_AXIS));
echo_mix();
}
SERIAL_EOL();
}
#endif // GRADIENT_MIX

View File

@@ -0,0 +1,41 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(NOZZLE_PARK_FEATURE)
#include "../../gcode.h"
#include "../../../libs/nozzle.h"
#include "../../../module/motion.h"
/**
* G27: Park the nozzle
*/
void GcodeSuite::G27() {
// Don't allow nozzle parking without homing first
if (axis_unhomed_error()) return;
nozzle.park(parser.ushortval('P'));
}
#endif // NOZZLE_PARK_FEATURE

View File

@@ -0,0 +1,59 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if SAVED_POSITIONS
#include "../../../core/language.h"
#include "../../gcode.h"
#include "../../../module/motion.h"
#define DEBUG_OUT ENABLED(SAVED_POSITIONS_DEBUG)
#include "../../../core/debug_out.h"
/**
* G60: Save current position
*
* S<slot> - Memory slot # (0-based) to save into (default 0)
*/
void GcodeSuite::G60() {
const uint8_t slot = parser.byteval('S');
if (slot >= SAVED_POSITIONS) {
SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS));
return;
}
stored_position[slot] = current_position;
SBI(saved_slots[slot >> 3], slot & 0x07);
#if ENABLED(SAVED_POSITIONS_DEBUG)
const xyze_pos_t &pos = stored_position[slot];
DEBUG_ECHOPAIR_F(STR_SAVED_POS " S", slot);
DEBUG_ECHOPAIR_F(" : X", pos.x);
DEBUG_ECHOPAIR_F_P(SP_Y_STR, pos.y);
DEBUG_ECHOLNPAIR_F_P(SP_Z_STR, pos.z);
#endif
}
#endif // SAVED_POSITIONS

View File

@@ -0,0 +1,71 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if SAVED_POSITIONS
#include "../../../core/language.h"
#include "../../../module/planner.h"
#include "../../gcode.h"
#include "../../../module/motion.h"
/**
* G61: Return to saved position
*
* F<rate> - Feedrate (optional) for the move back.
* S<slot> - Slot # (0-based) to restore from (default 0).
* X Y Z - Axes to restore. At least one is required.
*/
void GcodeSuite::G61(void) {
const uint8_t slot = parser.byteval('S');
#if SAVED_POSITIONS < 256
if (slot >= SAVED_POSITIONS) {
SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS));
return;
}
#endif
// No saved position? No axes being restored?
if (!TEST(saved_slots[slot >> 3], slot & 0x07) || !parser.seen("XYZ")) return;
// Apply any given feedrate over 0.0
const float fr = parser.linearval('F');
if (fr > 0.0) feedrate_mm_s = MMM_TO_MMS(fr);
SERIAL_ECHOPAIR(STR_RESTORING_POS " S", int(slot));
LOOP_XYZ(i) {
destination[i] = parser.seen(XYZ_CHAR(i))
? stored_position[slot][i] + parser.value_axis_units((AxisEnum)i)
: current_position[i];
SERIAL_CHAR(' ', XYZ_CHAR(i));
SERIAL_ECHO_F(destination[i]);
}
SERIAL_EOL();
// Move to the saved position
prepare_line_to_destination();
}
#endif // SAVED_POSITIONS

View File

@@ -0,0 +1,102 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(PARK_HEAD_ON_PAUSE)
#include "../../gcode.h"
#include "../../parser.h"
#include "../../../feature/pause.h"
#include "../../../module/motion.h"
#include "../../../sd/cardreader.h"
#include "../../../module/printcounter.h"
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../../../feature/powerloss.h"
#endif
/**
* M125: Store current position and move to parking position.
* Called on pause (by M25) to prevent material leaking onto the
* object. On resume (M24) the head will be moved back and the
* print will resume.
*
* When not actively SD printing, M125 simply moves to the park
* position and waits, resuming with a button click or M108.
* Without PARK_HEAD_ON_PAUSE the M125 command does nothing.
*
* L = override retract length
* X = override X
* Y = override Y
* Z = override Z raise
*/
void GcodeSuite::M125() {
// Initial retract before move to filament change position
const float retract = -ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
);
xyz_pos_t park_point = NOZZLE_PARK_POINT;
// Move XY axes to filament change position or given position
if (parser.seenval('X')) park_point.x = RAW_X_POSITION(parser.linearval('X'));
if (parser.seenval('Y')) park_point.y = RAW_X_POSITION(parser.linearval('Y'));
// Lift Z axis
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[active_extruder];
#endif
#if ENABLED(SDSUPPORT)
const bool sd_printing = IS_SD_PRINTING();
#else
constexpr bool sd_printing = false;
#endif
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_PAUSING, PAUSE_MODE_PAUSE_PRINT);
const bool show_lcd = parser.seenval('P');
#else
constexpr bool show_lcd = false;
#endif
if (pause_print(retract, park_point, 0, show_lcd)) {
#if ENABLED(POWER_LOSS_RECOVERY)
if (recovery.enabled) recovery.save(true);
#endif
if (!sd_printing || show_lcd) {
wait_for_confirmation(false, 0);
resume_print(0, 0, PAUSE_PARK_RETRACT_LENGTH, 0);
}
}
}
#endif // PARK_HEAD_ON_PAUSE

View File

@@ -0,0 +1,181 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#include "../../gcode.h"
#include "../../../feature/pause.h"
#include "../../../module/motion.h"
#include "../../../module/printcounter.h"
#if EXTRUDERS > 1
#include "../../../module/tool_change.h"
#endif
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#endif
#if ENABLED(MMU2_MENUS)
#include "../../../lcd/menu/menu_mmu2.h"
#endif
#if ENABLED(MIXING_EXTRUDER)
#include "../../../feature/mixing.h"
#endif
/**
* M600: Pause for filament change
*
* E[distance] - Retract the filament this far
* Z[distance] - Move the Z axis by this distance
* X[position] - Move to this X position, with Y
* Y[position] - Move to this Y position, with X
* U[distance] - Retract distance for removal (manual reload)
* L[distance] - Extrude distance for insertion (manual reload)
* B[count] - Number of times to beep, -1 for indefinite (if equipped with a buzzer)
* T[toolhead] - Select extruder for filament change
*
* Default values are used for omitted arguments.
*/
void GcodeSuite::M600() {
#if ENABLED(MIXING_EXTRUDER)
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
const uint8_t old_mixing_tool = mixer.get_current_vtool();
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i) mixer.set_collector(i, i == uint8_t(target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
const int8_t target_extruder = active_extruder;
#else
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
#endif
#if ENABLED(DUAL_X_CARRIAGE)
int8_t DXC_ext = target_extruder;
if (!parser.seen('T')) { // If no tool index is specified, M600 was (probably) sent in response to filament runout.
// In this case, for duplicating modes set DXC_ext to the extruder that ran out.
#if HAS_FILAMENT_SENSOR && NUM_RUNOUT_SENSORS > 1
if (dxc_is_duplicating())
DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT_INVERTING) ? 1 : 0;
#else
DXC_ext = active_extruder;
#endif
}
#endif
// Show initial "wait for start" message
#if HAS_LCD_MENU && DISABLED(MMU2_MENUS)
lcd_pause_show_message(PAUSE_MESSAGE_CHANGING, PAUSE_MODE_PAUSE_PRINT, target_extruder);
#endif
#if ENABLED(HOME_BEFORE_FILAMENT_CHANGE)
// Don't allow filament change without homing first
if (axes_need_homing()) home_all_axes();
#endif
#if EXTRUDERS > 1
// Change toolhead if specified
const uint8_t active_extruder_before_filament_change = active_extruder;
if (
active_extruder != target_extruder
#if ENABLED(DUAL_X_CARRIAGE)
&& dual_x_carriage_mode != DXC_DUPLICATION_MODE && dual_x_carriage_mode != DXC_MIRRORED_MODE
#endif
) tool_change(target_extruder, false);
#endif
// Initial retract before move to filament change position
const float retract = -ABS(parser.seen('E') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
);
xyz_pos_t park_point NOZZLE_PARK_POINT;
// Lift Z axis
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Move XY axes to filament change position or given position
if (parser.seenval('X')) park_point.x = parser.linearval('X');
if (parser.seenval('Y')) park_point.y = parser.linearval('Y');
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[active_extruder];
#endif
#if ENABLED(MMU2_MENUS)
// For MMU2 reset retract and load/unload values so they don't mess with MMU filament handling
constexpr float unload_length = 0.5f,
slow_load_length = 0.0f,
fast_load_length = 0.0f;
#else
// Unload filament
const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS)
: fc_settings[active_extruder].unload_length);
// Slow load filament
constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
// Fast load filament
const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS)
: fc_settings[active_extruder].load_length);
#endif
const int beep_count = parser.intval('B',
#ifdef FILAMENT_CHANGE_ALERT_BEEPS
FILAMENT_CHANGE_ALERT_BEEPS
#else
-1
#endif
);
if (pause_print(retract, park_point, unload_length, true DXC_PASS)) {
#if ENABLED(MMU2_MENUS)
mmu2_M600();
resume_print(slow_load_length, fast_load_length, 0, beep_count DXC_PASS);
#else
wait_for_confirmation(true, beep_count DXC_PASS);
resume_print(slow_load_length, fast_load_length, ADVANCED_PAUSE_PURGE_LENGTH, beep_count DXC_PASS);
#endif
}
#if EXTRUDERS > 1
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
}
#endif // ADVANCED_PAUSE_FEATURE

View File

@@ -0,0 +1,66 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#include "../../gcode.h"
#include "../../../feature/pause.h"
#include "../../../module/motion.h"
#include "../../../module/printcounter.h"
#if EXTRUDERS > 1
#include "../../../module/tool_change.h"
#endif
/**
* M603: Configure filament change
*
* T[toolhead] - Select extruder to configure, active extruder if not specified
* U[distance] - Retract distance for removal, for the specified extruder
* L[distance] - Extrude distance for insertion, for the specified extruder
*
*/
void GcodeSuite::M603() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
// Unload length
if (parser.seen('U')) {
fc_settings[target_extruder].unload_length = ABS(parser.value_axis_units(E_AXIS));
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
NOMORE(fc_settings[target_extruder].unload_length, EXTRUDE_MAXLENGTH);
#endif
}
// Load length
if (parser.seen('L')) {
fc_settings[target_extruder].load_length = ABS(parser.value_axis_units(E_AXIS));
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
NOMORE(fc_settings[target_extruder].load_length, EXTRUDE_MAXLENGTH);
#endif
}
}
#endif // ADVANCED_PAUSE_FEATURE

View File

@@ -0,0 +1,252 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
#include "../../gcode.h"
#include "../../../MarlinCore.h"
#include "../../../module/motion.h"
#include "../../../module/temperature.h"
#include "../../../feature/pause.h"
#if EXTRUDERS > 1
#include "../../../module/tool_change.h"
#endif
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#endif
#if ENABLED(PRUSA_MMU2)
#include "../../../feature/mmu2/mmu2.h"
#endif
#if ENABLED(MIXING_EXTRUDER)
#include "../../../feature/mixing.h"
#endif
/**
* M701: Load filament
*
* T<extruder> - Extruder number. Required for mixing extruder.
* For non-mixing, current extruder if omitted.
* Z<distance> - Move the Z axis by this distance
* L<distance> - Extrude distance for insertion (positive value) (manual reload)
*
* Default values are used for omitted arguments.
*/
void GcodeSuite::M701() {
xyz_pos_t park_point = NOZZLE_PARK_POINT;
#if ENABLED(NO_MOTION_BEFORE_HOMING)
// Don't raise Z if the machine isn't homed
if (axes_need_homing()) park_point.z = 0;
#endif
#if ENABLED(MIXING_EXTRUDER)
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
const uint8_t old_mixing_tool = mixer.get_current_vtool();
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i) mixer.set_collector(i, (i == (uint8_t)target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
const int8_t target_extruder = active_extruder;
#else
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
#endif
// Z axis lift
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Show initial "wait for load" message
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder);
#endif
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
// Change toolhead if specified
uint8_t active_extruder_before_filament_change = active_extruder;
if (active_extruder != target_extruder)
tool_change(target_extruder, false);
#endif
// Lift Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
// Load filament
#if ENABLED(PRUSA_MMU2)
mmu2.load_filament_to_nozzle(target_extruder);
#else
constexpr float purge_length = ADVANCED_PAUSE_PURGE_LENGTH,
slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS)
: fc_settings[active_extruder].load_length);
load_filament(
slow_load_length, fast_load_length, purge_length,
FILAMENT_CHANGE_ALERT_BEEPS,
true, // show_lcd
thermalManager.still_heating(target_extruder), // pause_for_user
PAUSE_MODE_LOAD_FILAMENT // pause_mode
#if ENABLED(DUAL_X_CARRIAGE)
, target_extruder // Dual X target
#endif
);
#endif
// Restore Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
// Show status screen
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
#endif
}
/**
* M702: Unload filament
*
* T<extruder> - Extruder number. Required for mixing extruder.
* For non-mixing, if omitted, current extruder
* (or ALL extruders with FILAMENT_UNLOAD_ALL_EXTRUDERS).
* Z<distance> - Move the Z axis by this distance
* U<distance> - Retract distance for removal (manual reload)
*
* Default values are used for omitted arguments.
*/
void GcodeSuite::M702() {
xyz_pos_t park_point = NOZZLE_PARK_POINT;
#if ENABLED(NO_MOTION_BEFORE_HOMING)
// Don't raise Z if the machine isn't homed
if (axes_need_homing()) park_point.z = 0;
#endif
#if ENABLED(MIXING_EXTRUDER)
const uint8_t old_mixing_tool = mixer.get_current_vtool();
#if ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
float mix_multiplier = 1.0;
if (!parser.seenval('T')) {
mixer.T(MIXER_AUTORETRACT_TOOL);
mix_multiplier = MIXING_STEPPERS;
}
else
#endif
{
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i) mixer.set_collector(i, (i == (uint8_t)target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
}
const int8_t target_extruder = active_extruder;
#else
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
#endif
// Z axis lift
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Show initial "wait for unload" message
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder);
#endif
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
// Change toolhead if specified
uint8_t active_extruder_before_filament_change = active_extruder;
if (active_extruder != target_extruder)
tool_change(target_extruder, false);
#endif
// Lift Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
// Unload filament
#if ENABLED(PRUSA_MMU2)
mmu2.unload();
#else
#if EXTRUDERS > 1 && ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
if (!parser.seenval('T')) {
HOTEND_LOOP() {
if (e != active_extruder) tool_change(e, false);
unload_filament(-fc_settings[e].unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT);
}
}
else
#endif
{
// Unload length
const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS)
: fc_settings[target_extruder].unload_length);
unload_filament(unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT
#if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
, mix_multiplier
#endif
);
}
#endif
// Restore Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
// Show status screen
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
#endif
}
#endif // ADVANCED_PAUSE_FEATURE

View File

@@ -0,0 +1,89 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../../gcode.h"
#include "../../../feature/powerloss.h"
#include "../../../module/motion.h"
#include "../../../lcd/ultralcd.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_POWER_LOSS_RECOVERY)
#include "../../../core/debug_out.h"
void menu_job_recovery();
inline void plr_error(PGM_P const prefix) {
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
DEBUG_ECHO_START();
serialprintPGM(prefix);
DEBUG_ECHOLNPGM(" Power-Loss Recovery Data");
#else
UNUSED(prefix);
#endif
}
#if HAS_LCD_MENU
void lcd_power_loss_recovery_cancel();
#endif
/**
* M1000: Resume from power-loss (undocumented)
* - With 'S' go to the Resume/Cancel menu
* - With no parameters, run recovery commands
*/
void GcodeSuite::M1000() {
if (recovery.valid()) {
if (parser.seen('S')) {
#if HAS_LCD_MENU
ui.goto_screen(menu_job_recovery);
#elif ENABLED(EXTENSIBLE_UI)
ExtUI::onPowerLossResume();
#else
SERIAL_ECHO_MSG("Resume requires LCD.");
#endif
}
else if (parser.seen('C')) {
#if HAS_LCD_MENU
lcd_power_loss_recovery_cancel();
#else
recovery.cancel();
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onPrintTimerStopped();
#endif
}
else
recovery.resume();
}
else
plr_error(recovery.info.valid_head ? PSTR("No") : PSTR("Invalid"));
}
#endif // POWER_LOSS_RECOVERY

View File

@@ -0,0 +1,58 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../../gcode.h"
#include "../../../feature/powerloss.h"
#include "../../../module/motion.h"
#include "../../../lcd/ultralcd.h"
/**
* M413: Enable / Disable power-loss recovery
*
* Parameters
* S[bool] - Flag to enable / disable.
* If omitted, report current state.
*/
void GcodeSuite::M413() {
if (parser.seen('S'))
recovery.enable(parser.value_bool());
else {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Power-loss recovery ");
serialprintln_onoff(recovery.enabled);
}
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
if (parser.seen("RL")) recovery.load();
if (parser.seen('W')) recovery.save(true);
if (parser.seen('P')) recovery.purge();
if (parser.seen('E')) serialprintPGM(recovery.exists() ? PSTR("PLR Exists\n") : PSTR("No PLR\n"));
if (parser.seen('V')) serialprintPGM(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n"));
#endif
}
#endif // POWER_LOSS_RECOVERY

View File

@@ -0,0 +1,49 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(PRUSA_MMU2)
#include "../../gcode.h"
#include "../../../feature/mmu2/mmu2.h"
/**
* M403: Set filament type for MMU2
*
* Valid filament type values:
*
* 0 Default
* 1 Flexible
* 2 PVA
*/
void GcodeSuite::M403() {
int8_t index = parser.intval('E', -1),
type = parser.intval('F', -1);
if (WITHIN(index, 0, 4) && WITHIN(type, 0, 2))
mmu2.set_filament_type(index, type);
else
SERIAL_ECHO_MSG("M403 - bad arguments.");
}
#endif // PRUSA_MMU2

View File

@@ -0,0 +1,62 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_FILAMENT_SENSOR
#include "../../gcode.h"
#include "../../../feature/runout.h"
/**
* M412: Enable / Disable filament runout detection
*/
void GcodeSuite::M412() {
if (parser.seen("RS"
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
"D"
#endif
#if ENABLED(HOST_ACTION_COMMANDS)
"H"
#endif
)) {
#if ENABLED(HOST_ACTION_COMMANDS)
if (parser.seen('H')) runout.host_handling = parser.value_bool();
#endif
const bool seenR = parser.seen('R'), seenS = parser.seen('S');
if (seenR || seenS) runout.reset();
if (seenS) runout.enabled = parser.value_bool();
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
if (parser.seen('D')) runout.set_runout_distance(parser.value_linear_units());
#endif
}
else {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Filament runout ");
serialprintln_onoff(runout.enabled);
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
SERIAL_ECHOLNPAIR("Filament runout distance (mm): ", runout.runout_distance());
#endif
}
}
#endif // HAS_FILAMENT_SENSOR

View File

@@ -0,0 +1,56 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_TRINAMIC_CONFIG
#include "../../gcode.h"
#include "../../../feature/tmc_util.h"
/**
* M122: Debug TMC drivers
*/
void GcodeSuite::M122() {
xyze_bool_t print_axis = { false, false, false, false };
bool print_all = true;
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) { print_axis[i] = true; print_all = false; }
if (print_all) LOOP_XYZE(i) print_axis[i] = true;
#if ENABLED(TMC_DEBUG)
#if ENABLED(MONITOR_DRIVER_STATUS)
const bool sflag = parser.seen('S'), s0 = sflag && !parser.value_bool();
if (sflag) tmc_set_report_interval(s0 ? 0 : MONITOR_DRIVER_STATUS_INTERVAL_MS);
if (!s0 && parser.seenval('P')) tmc_set_report_interval(_MIN(parser.value_ushort(), MONITOR_DRIVER_STATUS_INTERVAL_MS));
#endif
if (parser.seen('V'))
tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
else
tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
#endif
test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
}
#endif // HAS_TRINAMIC_CONFIG

View File

@@ -0,0 +1,186 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_STEALTHCHOP
#include "../../gcode.h"
#include "../../../feature/tmc_util.h"
#include "../../../module/stepper/indirection.h"
template<typename TMC>
void tmc_say_stealth_status(TMC &st) {
st.printLabel();
SERIAL_ECHOPGM(" driver mode:\t");
serialprintPGM(st.get_stealthChop_status() ? PSTR("stealthChop") : PSTR("spreadCycle"));
SERIAL_EOL();
}
template<typename TMC>
void tmc_set_stealthChop(TMC &st, const bool enable) {
st.stored.stealthChop_enabled = enable;
st.refresh_stepping_mode();
}
static void set_stealth_status(const bool enable, const int8_t target_extruder) {
#define TMC_SET_STEALTH(Q) tmc_set_stealthChop(stepper##Q, enable)
#if AXIS_HAS_STEALTHCHOP(X) || AXIS_HAS_STEALTHCHOP(X2) \
|| AXIS_HAS_STEALTHCHOP(Y) || AXIS_HAS_STEALTHCHOP(Y2) \
|| AXIS_HAS_STEALTHCHOP(Z) || AXIS_HAS_STEALTHCHOP(Z2) \
|| AXIS_HAS_STEALTHCHOP(Z3) || AXIS_HAS_STEALTHCHOP(Z4)
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) {
switch (i) {
case X_AXIS:
#if AXIS_HAS_STEALTHCHOP(X)
if (index == 0) TMC_SET_STEALTH(X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
if (index == 1) TMC_SET_STEALTH(X2);
#endif
break;
case Y_AXIS:
#if AXIS_HAS_STEALTHCHOP(Y)
if (index == 0) TMC_SET_STEALTH(Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
if (index == 1) TMC_SET_STEALTH(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_HAS_STEALTHCHOP(Z)
if (index == 0) TMC_SET_STEALTH(Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
if (index == 1) TMC_SET_STEALTH(Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
if (index == 2) TMC_SET_STEALTH(Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
if (index == 3) TMC_SET_STEALTH(Z4);
#endif
break;
case E_AXIS: {
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_HAS_STEALTHCHOP(E0)
case 0: TMC_SET_STEALTH(E0); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E1)
case 1: TMC_SET_STEALTH(E1); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E2)
case 2: TMC_SET_STEALTH(E2); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E3)
case 3: TMC_SET_STEALTH(E3); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E4)
case 4: TMC_SET_STEALTH(E4); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E5)
case 5: TMC_SET_STEALTH(E5); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E6)
case 6: TMC_SET_STEALTH(E6); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E7)
case 7: TMC_SET_STEALTH(E7); break;
#endif
}
} break;
}
}
}
static void say_stealth_status() {
#define TMC_SAY_STEALTH_STATUS(Q) tmc_say_stealth_status(stepper##Q)
#if AXIS_HAS_STEALTHCHOP(X)
TMC_SAY_STEALTH_STATUS(X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
TMC_SAY_STEALTH_STATUS(X2);
#endif
#if AXIS_HAS_STEALTHCHOP(Y)
TMC_SAY_STEALTH_STATUS(Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
TMC_SAY_STEALTH_STATUS(Y2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z)
TMC_SAY_STEALTH_STATUS(Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
TMC_SAY_STEALTH_STATUS(Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
TMC_SAY_STEALTH_STATUS(Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
TMC_SAY_STEALTH_STATUS(Z4);
#endif
#if AXIS_HAS_STEALTHCHOP(E0)
TMC_SAY_STEALTH_STATUS(E0);
#endif
#if AXIS_HAS_STEALTHCHOP(E1)
TMC_SAY_STEALTH_STATUS(E1);
#endif
#if AXIS_HAS_STEALTHCHOP(E2)
TMC_SAY_STEALTH_STATUS(E2);
#endif
#if AXIS_HAS_STEALTHCHOP(E3)
TMC_SAY_STEALTH_STATUS(E3);
#endif
#if AXIS_HAS_STEALTHCHOP(E4)
TMC_SAY_STEALTH_STATUS(E4);
#endif
#if AXIS_HAS_STEALTHCHOP(E5)
TMC_SAY_STEALTH_STATUS(E5);
#endif
#if AXIS_HAS_STEALTHCHOP(E6)
TMC_SAY_STEALTH_STATUS(E6);
#endif
#if AXIS_HAS_STEALTHCHOP(E7)
TMC_SAY_STEALTH_STATUS(E7);
#endif
}
/**
* M569: Enable stealthChop on an axis
*
* S[1|0] to enable or disable
* XYZE to target an axis
* No arguments reports the stealthChop status of all capable drivers.
*/
void GcodeSuite::M569() {
if (parser.seen('S'))
set_stealth_status(parser.value_bool(), get_target_extruder_from_command());
else
say_stealth_status();
}
#endif // HAS_STEALTHCHOP

View File

@@ -0,0 +1,173 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_TRINAMIC_CONFIG
#include "../../gcode.h"
#include "../../../feature/tmc_util.h"
#include "../../../module/stepper/indirection.h"
/**
* M906: Set motor current in milliamps.
*
* Parameters:
* X[current] - Set mA current for X driver(s)
* Y[current] - Set mA current for Y driver(s)
* Z[current] - Set mA current for Z driver(s)
* E[current] - Set mA current for E driver(s)
*
* I[index] - Axis sub-index (Omit or 0 for X, Y, Z; 1 for X2, Y2, Z2; 2 for Z3; 3 for Z4.)
* T[index] - Extruder index (Zero-based. Omit for E0 only.)
*
* With no parameters report driver currents.
*/
void GcodeSuite::M906() {
#define TMC_SAY_CURRENT(Q) tmc_print_current(stepper##Q)
#define TMC_SET_CURRENT(Q) stepper##Q.rms_current(value)
bool report = true;
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) {
report = false;
switch (i) {
case X_AXIS:
#if AXIS_IS_TMC(X)
if (index == 0) TMC_SET_CURRENT(X);
#endif
#if AXIS_IS_TMC(X2)
if (index == 1) TMC_SET_CURRENT(X2);
#endif
break;
case Y_AXIS:
#if AXIS_IS_TMC(Y)
if (index == 0) TMC_SET_CURRENT(Y);
#endif
#if AXIS_IS_TMC(Y2)
if (index == 1) TMC_SET_CURRENT(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_IS_TMC(Z)
if (index == 0) TMC_SET_CURRENT(Z);
#endif
#if AXIS_IS_TMC(Z2)
if (index == 1) TMC_SET_CURRENT(Z2);
#endif
#if AXIS_IS_TMC(Z3)
if (index == 2) TMC_SET_CURRENT(Z3);
#endif
#if AXIS_IS_TMC(Z4)
if (index == 3) TMC_SET_CURRENT(Z4);
#endif
break;
case E_AXIS: {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_IS_TMC(E0)
case 0: TMC_SET_CURRENT(E0); break;
#endif
#if AXIS_IS_TMC(E1)
case 1: TMC_SET_CURRENT(E1); break;
#endif
#if AXIS_IS_TMC(E2)
case 2: TMC_SET_CURRENT(E2); break;
#endif
#if AXIS_IS_TMC(E3)
case 3: TMC_SET_CURRENT(E3); break;
#endif
#if AXIS_IS_TMC(E4)
case 4: TMC_SET_CURRENT(E4); break;
#endif
#if AXIS_IS_TMC(E5)
case 5: TMC_SET_CURRENT(E5); break;
#endif
#if AXIS_IS_TMC(E6)
case 6: TMC_SET_CURRENT(E6); break;
#endif
#if AXIS_IS_TMC(E7)
case 7: TMC_SET_CURRENT(E7); break;
#endif
}
} break;
}
}
if (report) {
#if AXIS_IS_TMC(X)
TMC_SAY_CURRENT(X);
#endif
#if AXIS_IS_TMC(X2)
TMC_SAY_CURRENT(X2);
#endif
#if AXIS_IS_TMC(Y)
TMC_SAY_CURRENT(Y);
#endif
#if AXIS_IS_TMC(Y2)
TMC_SAY_CURRENT(Y2);
#endif
#if AXIS_IS_TMC(Z)
TMC_SAY_CURRENT(Z);
#endif
#if AXIS_IS_TMC(Z2)
TMC_SAY_CURRENT(Z2);
#endif
#if AXIS_IS_TMC(Z3)
TMC_SAY_CURRENT(Z3);
#endif
#if AXIS_IS_TMC(Z4)
TMC_SAY_CURRENT(Z4);
#endif
#if AXIS_IS_TMC(E0)
TMC_SAY_CURRENT(E0);
#endif
#if AXIS_IS_TMC(E1)
TMC_SAY_CURRENT(E1);
#endif
#if AXIS_IS_TMC(E2)
TMC_SAY_CURRENT(E2);
#endif
#if AXIS_IS_TMC(E3)
TMC_SAY_CURRENT(E3);
#endif
#if AXIS_IS_TMC(E4)
TMC_SAY_CURRENT(E4);
#endif
#if AXIS_IS_TMC(E5)
TMC_SAY_CURRENT(E5);
#endif
#if AXIS_IS_TMC(E6)
TMC_SAY_CURRENT(E6);
#endif
#if AXIS_IS_TMC(E7)
TMC_SAY_CURRENT(E7);
#endif
}
}
#endif // HAS_TRINAMIC_CONFIG

View File

@@ -0,0 +1,429 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_TRINAMIC_CONFIG
#include "../../gcode.h"
#include "../../../feature/tmc_util.h"
#include "../../../module/stepper/indirection.h"
#include "../../../module/planner.h"
#include "../../queue.h"
#if ENABLED(MONITOR_DRIVER_STATUS)
#define M91x_USE(ST) (AXIS_DRIVER_TYPE(ST, TMC2130) || AXIS_DRIVER_TYPE(ST, TMC2160) || AXIS_DRIVER_TYPE(ST, TMC2208) || AXIS_DRIVER_TYPE(ST, TMC2209) || AXIS_DRIVER_TYPE(ST, TMC2660) || AXIS_DRIVER_TYPE(ST, TMC5130) || AXIS_DRIVER_TYPE(ST, TMC5160))
#define M91x_USE_E(N) (E_STEPPERS > N && M91x_USE(E##N))
#define M91x_SOME_X (M91x_USE(X) || M91x_USE(X2))
#define M91x_SOME_Y (M91x_USE(Y) || M91x_USE(Y2))
#define M91x_SOME_Z (M91x_USE(Z) || M91x_USE(Z2) || M91x_USE(Z3) || M91x_USE(Z4))
#define M91x_SOME_E (M91x_USE_E(0) || M91x_USE_E(1) || M91x_USE_E(2) || M91x_USE_E(3) || M91x_USE_E(4) || M91x_USE_E(5) || M91x_USE_E(6) || M91x_USE_E(7))
#if !M91x_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_SOME_E
#error "MONITOR_DRIVER_STATUS requires at least one TMC2130, 2160, 2208, 2209, 2660, 5130, or 5160."
#endif
/**
* M911: Report TMC stepper driver overtemperature pre-warn flag
* This flag is held by the library, persisting until cleared by M912
*/
void GcodeSuite::M911() {
#if M91x_USE(X)
tmc_report_otpw(stepperX);
#endif
#if M91x_USE(X2)
tmc_report_otpw(stepperX2);
#endif
#if M91x_USE(Y)
tmc_report_otpw(stepperY);
#endif
#if M91x_USE(Y2)
tmc_report_otpw(stepperY2);
#endif
#if M91x_USE(Z)
tmc_report_otpw(stepperZ);
#endif
#if M91x_USE(Z2)
tmc_report_otpw(stepperZ2);
#endif
#if M91x_USE(Z3)
tmc_report_otpw(stepperZ3);
#endif
#if M91x_USE(Z4)
tmc_report_otpw(stepperZ4);
#endif
#if M91x_USE_E(0)
tmc_report_otpw(stepperE0);
#endif
#if M91x_USE_E(1)
tmc_report_otpw(stepperE1);
#endif
#if M91x_USE_E(2)
tmc_report_otpw(stepperE2);
#endif
#if M91x_USE_E(3)
tmc_report_otpw(stepperE3);
#endif
#if M91x_USE_E(4)
tmc_report_otpw(stepperE4);
#endif
#if M91x_USE_E(5)
tmc_report_otpw(stepperE5);
#endif
#if M91x_USE_E(6)
tmc_report_otpw(stepperE6);
#endif
#if M91x_USE_E(7)
tmc_report_otpw(stepperE7);
#endif
}
/**
* M912: Clear TMC stepper driver overtemperature pre-warn flag held by the library
* Specify one or more axes with X, Y, Z, X1, Y1, Z1, X2, Y2, Z2, Z3, Z4 and E[index].
* If no axes are given, clear all.
*
* Examples:
* M912 X ; clear X and X2
* M912 X1 ; clear X1 only
* M912 X2 ; clear X2 only
* M912 X E ; clear X, X2, and all E
* M912 E1 ; clear E1 only
*/
void GcodeSuite::M912() {
#if M91x_SOME_X
const bool hasX = parser.seen(axis_codes.x);
#else
constexpr bool hasX = false;
#endif
#if M91x_SOME_Y
const bool hasY = parser.seen(axis_codes.y);
#else
constexpr bool hasY = false;
#endif
#if M91x_SOME_Z
const bool hasZ = parser.seen(axis_codes.z);
#else
constexpr bool hasZ = false;
#endif
#if M91x_SOME_E
const bool hasE = parser.seen(axis_codes.e);
#else
constexpr bool hasE = false;
#endif
const bool hasNone = !hasX && !hasY && !hasZ && !hasE;
#if M91x_SOME_X
const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF));
#if M91x_USE(X)
if (hasNone || xval == 1 || (hasX && xval < 0)) tmc_clear_otpw(stepperX);
#endif
#if M91x_USE(X2)
if (hasNone || xval == 2 || (hasX && xval < 0)) tmc_clear_otpw(stepperX2);
#endif
#endif
#if M91x_SOME_Y
const int8_t yval = int8_t(parser.byteval(axis_codes.y, 0xFF));
#if M91x_USE(Y)
if (hasNone || yval == 1 || (hasY && yval < 0)) tmc_clear_otpw(stepperY);
#endif
#if M91x_USE(Y2)
if (hasNone || yval == 2 || (hasY && yval < 0)) tmc_clear_otpw(stepperY2);
#endif
#endif
#if M91x_SOME_Z
const int8_t zval = int8_t(parser.byteval(axis_codes.z, 0xFF));
#if M91x_USE(Z)
if (hasNone || zval == 1 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ);
#endif
#if M91x_USE(Z2)
if (hasNone || zval == 2 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ2);
#endif
#if M91x_USE(Z3)
if (hasNone || zval == 3 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ3);
#endif
#if M91x_USE(Z4)
if (hasNone || zval == 4 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ4);
#endif
#endif
#if M91x_SOME_E
const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF));
#if M91x_USE_E(0)
if (hasNone || eval == 0 || (hasE && eval < 0)) tmc_clear_otpw(stepperE0);
#endif
#if M91x_USE_E(1)
if (hasNone || eval == 1 || (hasE && eval < 0)) tmc_clear_otpw(stepperE1);
#endif
#if M91x_USE_E(2)
if (hasNone || eval == 2 || (hasE && eval < 0)) tmc_clear_otpw(stepperE2);
#endif
#if M91x_USE_E(3)
if (hasNone || eval == 3 || (hasE && eval < 0)) tmc_clear_otpw(stepperE3);
#endif
#if M91x_USE_E(4)
if (hasNone || eval == 4 || (hasE && eval < 0)) tmc_clear_otpw(stepperE4);
#endif
#if M91x_USE_E(5)
if (hasNone || eval == 5 || (hasE && eval < 0)) tmc_clear_otpw(stepperE5);
#endif
#if M91x_USE_E(6)
if (hasNone || eval == 6 || (hasE && eval < 0)) tmc_clear_otpw(stepperE6);
#endif
#if M91x_USE_E(7)
if (hasNone || eval == 7 || (hasE && eval < 0)) tmc_clear_otpw(stepperE7);
#endif
#endif
}
#endif // MONITOR_DRIVER_STATUS
/**
* M913: Set HYBRID_THRESHOLD speed.
*/
#if ENABLED(HYBRID_THRESHOLD)
void GcodeSuite::M913() {
#define TMC_SAY_PWMTHRS(A,Q) tmc_print_pwmthrs(stepper##Q)
#define TMC_SET_PWMTHRS(A,Q) stepper##Q.set_pwm_thrs(value)
#define TMC_SAY_PWMTHRS_E(E) tmc_print_pwmthrs(stepperE##E)
#define TMC_SET_PWMTHRS_E(E) stepperE##E.set_pwm_thrs(value)
bool report = true;
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (int32_t value = parser.longval(axis_codes[i])) {
report = false;
switch (i) {
case X_AXIS:
#if AXIS_HAS_STEALTHCHOP(X)
if (index < 2) TMC_SET_PWMTHRS(X,X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
if (!(index & 1)) TMC_SET_PWMTHRS(X,X2);
#endif
break;
case Y_AXIS:
#if AXIS_HAS_STEALTHCHOP(Y)
if (index < 2) TMC_SET_PWMTHRS(Y,Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
if (!(index & 1)) TMC_SET_PWMTHRS(Y,Y2);
#endif
break;
case Z_AXIS:
#if AXIS_HAS_STEALTHCHOP(Z)
if (index < 2) TMC_SET_PWMTHRS(Z,Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
if (index == 0 || index == 2) TMC_SET_PWMTHRS(Z,Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
if (index == 0 || index == 3) TMC_SET_PWMTHRS(Z,Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
if (index == 0 || index == 4) TMC_SET_PWMTHRS(Z,Z4);
#endif
break;
case E_AXIS: {
#if E_STEPPERS
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_HAS_STEALTHCHOP(E0)
case 0: TMC_SET_PWMTHRS_E(0); break;
#endif
#if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
case 1: TMC_SET_PWMTHRS_E(1); break;
#endif
#if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
case 2: TMC_SET_PWMTHRS_E(2); break;
#endif
#if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
case 3: TMC_SET_PWMTHRS_E(3); break;
#endif
#if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
case 4: TMC_SET_PWMTHRS_E(4); break;
#endif
#if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
case 5: TMC_SET_PWMTHRS_E(5); break;
#endif
#if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
case 6: TMC_SET_PWMTHRS_E(6); break;
#endif
#if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
case 7: TMC_SET_PWMTHRS_E(7); break;
#endif
}
#endif // E_STEPPERS
} break;
}
}
if (report) {
#if AXIS_HAS_STEALTHCHOP(X)
TMC_SAY_PWMTHRS(X,X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
TMC_SAY_PWMTHRS(X,X2);
#endif
#if AXIS_HAS_STEALTHCHOP(Y)
TMC_SAY_PWMTHRS(Y,Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
TMC_SAY_PWMTHRS(Y,Y2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z)
TMC_SAY_PWMTHRS(Z,Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
TMC_SAY_PWMTHRS(Z,Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
TMC_SAY_PWMTHRS(Z,Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
TMC_SAY_PWMTHRS(Z,Z4);
#endif
#if E_STEPPERS && AXIS_HAS_STEALTHCHOP(E0)
TMC_SAY_PWMTHRS_E(0);
#endif
#if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
TMC_SAY_PWMTHRS_E(1);
#endif
#if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
TMC_SAY_PWMTHRS_E(2);
#endif
#if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
TMC_SAY_PWMTHRS_E(3);
#endif
#if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
TMC_SAY_PWMTHRS_E(4);
#endif
#if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
TMC_SAY_PWMTHRS_E(5);
#endif
#if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
TMC_SAY_PWMTHRS_E(6);
#endif
#if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
TMC_SAY_PWMTHRS_E(7);
#endif
}
}
#endif // HYBRID_THRESHOLD
/**
* M914: Set StallGuard sensitivity.
*/
#if USE_SENSORLESS
void GcodeSuite::M914() {
bool report = true;
const uint8_t index = parser.byteval('I');
LOOP_XYZ(i) if (parser.seen(XYZ_CHAR(i))) {
const int16_t value = parser.value_int();
report = false;
switch (i) {
#if X_SENSORLESS
case X_AXIS:
#if AXIS_HAS_STALLGUARD(X)
if (index < 2) stepperX.homing_threshold(value);
#endif
#if AXIS_HAS_STALLGUARD(X2)
if (!(index & 1)) stepperX2.homing_threshold(value);
#endif
break;
#endif
#if Y_SENSORLESS
case Y_AXIS:
#if AXIS_HAS_STALLGUARD(Y)
if (index < 2) stepperY.homing_threshold(value);
#endif
#if AXIS_HAS_STALLGUARD(Y2)
if (!(index & 1)) stepperY2.homing_threshold(value);
#endif
break;
#endif
#if Z_SENSORLESS
case Z_AXIS:
#if AXIS_HAS_STALLGUARD(Z)
if (index < 2) stepperZ.homing_threshold(value);
#endif
#if AXIS_HAS_STALLGUARD(Z2)
if (index == 0 || index == 2) stepperZ2.homing_threshold(value);
#endif
#if AXIS_HAS_STALLGUARD(Z3)
if (index == 0 || index == 3) stepperZ3.homing_threshold(value);
#endif
#if AXIS_HAS_STALLGUARD(Z4)
if (index == 0 || index == 4) stepperZ4.homing_threshold(value);
#endif
break;
#endif
}
}
if (report) {
#if X_SENSORLESS
#if AXIS_HAS_STALLGUARD(X)
tmc_print_sgt(stepperX);
#endif
#if AXIS_HAS_STALLGUARD(X2)
tmc_print_sgt(stepperX2);
#endif
#endif
#if Y_SENSORLESS
#if AXIS_HAS_STALLGUARD(Y)
tmc_print_sgt(stepperY);
#endif
#if AXIS_HAS_STALLGUARD(Y2)
tmc_print_sgt(stepperY2);
#endif
#endif
#if Z_SENSORLESS
#if AXIS_HAS_STALLGUARD(Z)
tmc_print_sgt(stepperZ);
#endif
#if AXIS_HAS_STALLGUARD(Z2)
tmc_print_sgt(stepperZ2);
#endif
#if AXIS_HAS_STALLGUARD(Z3)
tmc_print_sgt(stepperZ3);
#endif
#if AXIS_HAS_STALLGUARD(Z4)
tmc_print_sgt(stepperZ4);
#endif
#endif
}
}
#endif // USE_SENSORLESS
#endif // HAS_TRINAMIC_CONFIG

View File

@@ -0,0 +1,978 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* gcode.cpp - Temporary container for all gcode handlers
* Most will migrate to classes, by feature.
*/
#include "gcode.h"
GcodeSuite gcode;
#if ENABLED(WIFI_CUSTOM_COMMAND)
extern bool wifi_custom_command(char * const command_ptr);
#endif
#include "parser.h"
#include "queue.h"
#include "../module/motion.h"
#if ENABLED(PRINTCOUNTER)
#include "../module/printcounter.h"
#endif
#if ENABLED(HOST_PROMPT_SUPPORT)
#include "../feature/host_actions.h"
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../sd/cardreader.h"
#include "../feature/powerloss.h"
#endif
#if ENABLED(CANCEL_OBJECTS)
#include "../feature/cancel_object.h"
#endif
#include "../MarlinCore.h" // for idle()
millis_t GcodeSuite::previous_move_ms;
// Relative motion mode for each logical axis
static constexpr xyze_bool_t ar_init = AXIS_RELATIVE_MODES;
uint8_t GcodeSuite::axis_relative = (
(ar_init.x ? _BV(REL_X) : 0)
| (ar_init.y ? _BV(REL_Y) : 0)
| (ar_init.z ? _BV(REL_Z) : 0)
| (ar_init.e ? _BV(REL_E) : 0)
);
#if HAS_AUTO_REPORTING || ENABLED(HOST_KEEPALIVE_FEATURE)
bool GcodeSuite::autoreport_paused; // = false
#endif
#if ENABLED(HOST_KEEPALIVE_FEATURE)
GcodeSuite::MarlinBusyState GcodeSuite::busy_state = NOT_BUSY;
uint8_t GcodeSuite::host_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
GcodeSuite::WorkspacePlane GcodeSuite::workspace_plane = PLANE_XY;
#endif
#if ENABLED(CNC_COORDINATE_SYSTEMS)
int8_t GcodeSuite::active_coordinate_system = -1; // machine space
xyz_pos_t GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS];
#endif
/**
* Get the target extruder from the T parameter or the active_extruder
* Return -1 if the T parameter is out of range
*/
int8_t GcodeSuite::get_target_extruder_from_command() {
if (parser.seenval('T')) {
const int8_t e = parser.value_byte();
if (e < EXTRUDERS) return e;
SERIAL_ECHO_START();
SERIAL_CHAR('M'); SERIAL_ECHO(parser.codenum);
SERIAL_ECHOLNPAIR(" " STR_INVALID_EXTRUDER " ", int(e));
return -1;
}
return active_extruder;
}
/**
* Get the target e stepper from the T parameter
* Return -1 if the T parameter is out of range or unspecified
*/
int8_t GcodeSuite::get_target_e_stepper_from_command() {
const int8_t e = parser.intval('T', -1);
if (WITHIN(e, 0, E_STEPPERS - 1)) return e;
SERIAL_ECHO_START();
SERIAL_CHAR('M'); SERIAL_ECHO(parser.codenum);
if (e == -1)
SERIAL_ECHOLNPGM(" " STR_E_STEPPER_NOT_SPECIFIED);
else
SERIAL_ECHOLNPAIR(" " STR_INVALID_E_STEPPER " ", int(e));
return -1;
}
/**
* Set XYZE destination and feedrate from the current GCode command
*
* - Set destination from included axis codes
* - Set to current for missing axis codes
* - Set the feedrate, if included
*/
void GcodeSuite::get_destination_from_command() {
xyze_bool_t seen = { false, false, false, false };
#if ENABLED(CANCEL_OBJECTS)
const bool &skip_move = cancelable.skipping;
#else
constexpr bool skip_move = false;
#endif
// Get new XYZ position, whether absolute or relative
LOOP_XYZ(i) {
if ( (seen[i] = parser.seenval(XYZ_CHAR(i))) ) {
const float v = parser.value_axis_units((AxisEnum)i);
if (skip_move)
destination[i] = current_position[i];
else
destination[i] = axis_is_relative(AxisEnum(i)) ? current_position[i] + v : LOGICAL_TO_NATIVE(v, i);
}
else
destination[i] = current_position[i];
}
// Get new E position, whether absolute or relative
if ( (seen.e = parser.seenval('E')) ) {
const float v = parser.value_axis_units(E_AXIS);
destination.e = axis_is_relative(E_AXIS) ? current_position.e + v : v;
}
else
destination.e = current_position.e;
#if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS)
// Only update power loss recovery on moves with E
if (recovery.enabled && IS_SD_PRINTING() && seen.e && (seen.x || seen.y))
recovery.save();
#endif
if (parser.linearval('F') > 0)
feedrate_mm_s = parser.value_feedrate();
#if ENABLED(PRINTCOUNTER)
if (!DEBUGGING(DRYRUN) && !skip_move)
print_job_timer.incFilamentUsed(destination.e - current_position.e);
#endif
// Get ABCDHI mixing factors
#if BOTH(MIXING_EXTRUDER, DIRECT_MIXING_IN_G1)
M165();
#endif
}
/**
* Dwell waits immediately. It does not synchronize. Use M400 instead of G4
*/
void GcodeSuite::dwell(millis_t time) {
time += millis();
while (PENDING(millis(), time)) idle();
}
/**
* When G29_RETRY_AND_RECOVER is enabled, call G29() in
* a loop with recovery and retry handling.
*/
#if HAS_LEVELING && ENABLED(G29_RETRY_AND_RECOVER)
#ifndef G29_MAX_RETRIES
#define G29_MAX_RETRIES 0
#endif
void GcodeSuite::G29_with_retry() {
uint8_t retries = G29_MAX_RETRIES;
while (G29()) { // G29 should return true for failed probes ONLY
if (retries--) event_probe_recover();
else {
event_probe_failure();
return;
}
}
#if ENABLED(HOST_PROMPT_SUPPORT)
host_action_prompt_end();
#endif
#ifdef G29_SUCCESS_COMMANDS
process_subcommands_now_P(PSTR(G29_SUCCESS_COMMANDS));
#endif
}
#endif // HAS_LEVELING && G29_RETRY_AND_RECOVER
//
// Placeholders for non-migrated codes
//
#if ENABLED(M100_FREE_MEMORY_WATCHER)
extern void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
#endif
/**
* Process the parsed command and dispatch it to its handler
*/
void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
KEEPALIVE_STATE(IN_HANDLER);
// Handle a known G, M, or T
switch (parser.command_letter) {
case 'G': switch (parser.codenum) {
case 0: case 1: G0_G1( // G0: Fast Move, G1: Linear Move
#if IS_SCARA || defined(G0_FEEDRATE)
parser.codenum == 0
#endif
);
break;
#if ENABLED(ARC_SUPPORT) && DISABLED(SCARA)
case 2: case 3: G2_G3(parser.codenum == 2); break; // G2: CW ARC, G3: CCW ARC
#endif
case 4: G4(); break; // G4: Dwell
#if ENABLED(BEZIER_CURVE_SUPPORT)
case 5: G5(); break; // G5: Cubic B_spline
#endif
#if ENABLED(FWRETRACT)
case 10: G10(); break; // G10: Retract / Swap Retract
case 11: G11(); break; // G11: Recover / Swap Recover
#endif
#if ENABLED(NOZZLE_CLEAN_FEATURE)
case 12: G12(); break; // G12: Nozzle Clean
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
case 17: G17(); break; // G17: Select Plane XY
case 18: G18(); break; // G18: Select Plane ZX
case 19: G19(); break; // G19: Select Plane YZ
#endif
#if ENABLED(INCH_MODE_SUPPORT)
case 20: G20(); break; // G20: Inch Mode
case 21: G21(); break; // G21: MM Mode
#else
case 21: NOOP; break; // No error on unknown G21
#endif
#if ENABLED(G26_MESH_VALIDATION)
case 26: G26(); break; // G26: Mesh Validation Pattern generation
#endif
#if ENABLED(NOZZLE_PARK_FEATURE)
case 27: G27(); break; // G27: Nozzle Park
#endif
case 28: G28(); break; // G28: Home one or more axes
#if HAS_LEVELING
case 29: // G29: Bed leveling calibration
#if ENABLED(G29_RETRY_AND_RECOVER)
G29_with_retry();
#else
G29();
#endif
break;
#endif // HAS_LEVELING
#if HAS_BED_PROBE
case 30: G30(); break; // G30: Single Z probe
#if ENABLED(Z_PROBE_SLED)
case 31: G31(); break; // G31: dock the sled
case 32: G32(); break; // G32: undock the sled
#endif
#endif
#if ENABLED(DELTA_AUTO_CALIBRATION)
case 33: G33(); break; // G33: Delta Auto-Calibration
#endif
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
case 34: G34(); break; // G34: Z Stepper automatic alignment using probe
#endif
#if ENABLED(G38_PROBE_TARGET)
case 38: // G38.2, G38.3: Probe towards target
if (WITHIN(parser.subcode, 2,
#if ENABLED(G38_PROBE_AWAY)
5
#else
3
#endif
)) G38(parser.subcode); // G38.4, G38.5: Probe away from target
break;
#endif
#if ENABLED(CNC_COORDINATE_SYSTEMS)
case 53: G53(); break; // G53: (prefix) Apply native workspace
case 54: G54(); break; // G54: Switch to Workspace 1
case 55: G55(); break; // G55: Switch to Workspace 2
case 56: G56(); break; // G56: Switch to Workspace 3
case 57: G57(); break; // G57: Switch to Workspace 4
case 58: G58(); break; // G58: Switch to Workspace 5
case 59: G59(); break; // G59.0 - G59.3: Switch to Workspace 6-9
#endif
#if SAVED_POSITIONS
case 60: G60(); break; // G60: save current position
case 61: G61(); break; // G61: Apply/restore saved coordinates.
#endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
case 76: G76(); break; // G76: Calibrate first layer compensation values
#endif
#if ENABLED(GCODE_MOTION_MODES)
case 80: G80(); break; // G80: Reset the current motion mode
#endif
case 90: set_relative_mode(false); break; // G90: Absolute Mode
case 91: set_relative_mode(true); break; // G91: Relative Mode
case 92: G92(); break; // G92: Set current axis position(s)
#if HAS_MESH
case 42: G42(); break; // G42: Coordinated move to a mesh point
#endif
#if ENABLED(CALIBRATION_GCODE)
case 425: G425(); break; // G425: Perform calibration with calibration cube
#endif
#if ENABLED(DEBUG_GCODE_PARSER)
case 800: parser.debug(); break; // G800: GCode Parser Test for G
#endif
default: parser.unknown_command_warning(); break;
}
break;
case 'M': switch (parser.codenum) {
#if HAS_RESUME_CONTINUE
case 0: // M0: Unconditional stop - Wait for user button press on LCD
case 1: M0_M1(); break; // M1: Conditional stop - Wait for user button press on LCD
#endif
#if HAS_CUTTER
case 3: M3_M4(false); break; // M3: Turn ON Laser | Spindle (clockwise), set Power | Speed
case 4: M3_M4(true ); break; // M4: Turn ON Laser | Spindle (counter-clockwise), set Power | Speed
case 5: M5(); break; // M5: Turn OFF Laser | Spindle
#endif
#if ENABLED(COOLANT_CONTROL)
#if ENABLED(COOLANT_MIST)
case 7: M7(); break; // M7: Mist coolant ON
#endif
#if ENABLED(COOLANT_FLOOD)
case 8: M8(); break; // M8: Flood coolant ON
#endif
case 9: M9(); break; // M9: Coolant OFF
#endif
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
case 12: M12(); break; // M12: Synchronize and optionally force a CLC set
#endif
#if ENABLED(EXPECTED_PRINTER_CHECK)
case 16: M16(); break; // M16: Expected printer check
#endif
case 17: M17(); break; // M17: Enable all stepper motors
#if ENABLED(SDSUPPORT)
case 20: M20(); break; // M20: List SD card
case 21: M21(); break; // M21: Init SD card
case 22: M22(); break; // M22: Release SD card
case 23: M23(); break; // M23: Select file
case 24: M24(); break; // M24: Start SD print
case 25: M25(); break; // M25: Pause SD print
case 26: M26(); break; // M26: Set SD index
case 27: M27(); break; // M27: Get SD status
case 28: M28(); break; // M28: Start SD write
case 29: M29(); break; // M29: Stop SD write
case 30: M30(); break; // M30 <filename> Delete File
case 32: M32(); break; // M32: Select file and start SD print
#if ENABLED(LONG_FILENAME_HOST_SUPPORT)
case 33: M33(); break; // M33: Get the long full path to a file or folder
#endif
#if BOTH(SDCARD_SORT_ALPHA, SDSORT_GCODE)
case 34: M34(); break; // M34: Set SD card sorting options
#endif
case 928: M928(); break; // M928: Start SD write
#endif // SDSUPPORT
case 31: M31(); break; // M31: Report time since the start of SD print or last M109
case 42: M42(); break; // M42: Change pin state
#if ENABLED(PINS_DEBUGGING)
case 43: M43(); break; // M43: Read pin state
#endif
#if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
case 48: M48(); break; // M48: Z probe repeatability test
#endif
#if ENABLED(LCD_SET_PROGRESS_MANUALLY)
case 73: M73(); break; // M73: Set progress percentage (for display on LCD)
#endif
case 75: M75(); break; // M75: Start print timer
case 76: M76(); break; // M76: Pause print timer
case 77: M77(); break; // M77: Stop print timer
#if ENABLED(PRINTCOUNTER)
case 78: M78(); break; // M78: Show print statistics
#endif
#if ENABLED(M100_FREE_MEMORY_WATCHER)
case 100: M100(); break; // M100: Free Memory Report
#endif
#if EXTRUDERS
case 104: M104(); break; // M104: Set hot end temperature
case 109: M109(); break; // M109: Wait for hotend temperature to reach target
#endif
case 105: M105(); return; // M105: Report Temperatures (and say "ok")
#if FAN_COUNT > 0
case 106: M106(); break; // M106: Fan On
case 107: M107(); break; // M107: Fan Off
#endif
case 110: M110(); break; // M110: Set Current Line Number
case 111: M111(); break; // M111: Set debug level
#if DISABLED(EMERGENCY_PARSER)
case 108: M108(); break; // M108: Cancel Waiting
case 112: M112(); break; // M112: Full Shutdown
case 410: M410(); break; // M410: Quickstop - Abort all the planned moves.
#if ENABLED(HOST_PROMPT_SUPPORT)
case 876: M876(); break; // M876: Handle Host prompt responses
#endif
#else
case 108: case 112: case 410:
#if ENABLED(HOST_PROMPT_SUPPORT)
case 876:
#endif
break;
#endif
#if ENABLED(HOST_KEEPALIVE_FEATURE)
case 113: M113(); break; // M113: Set Host Keepalive interval
#endif
#if HAS_HEATED_BED
case 140: M140(); break; // M140: Set bed temperature
case 190: M190(); break; // M190: Wait for bed temperature to reach target
#endif
#if HAS_HEATED_CHAMBER
case 141: M141(); break; // M141: Set chamber temperature
case 191: M191(); break; // M191: Wait for chamber temperature to reach target
#endif
#if ENABLED(AUTO_REPORT_TEMPERATURES) && HAS_TEMP_SENSOR
case 155: M155(); break; // M155: Set temperature auto-report interval
#endif
#if ENABLED(PARK_HEAD_ON_PAUSE)
case 125: M125(); break; // M125: Store current position and move to filament change position
#endif
#if ENABLED(BARICUDA)
// PWM for HEATER_1_PIN
#if HAS_HEATER_1
case 126: M126(); break; // M126: valve open
case 127: M127(); break; // M127: valve closed
#endif
// PWM for HEATER_2_PIN
#if HAS_HEATER_2
case 128: M128(); break; // M128: valve open
case 129: M129(); break; // M129: valve closed
#endif
#endif // BARICUDA
#if ENABLED(PSU_CONTROL)
case 80: M80(); break; // M80: Turn on Power Supply
#endif
case 81: M81(); break; // M81: Turn off Power, including Power Supply, if possible
case 82: M82(); break; // M82: Set E axis normal mode (same as other axes)
case 83: M83(); break; // M83: Set E axis relative mode
case 18: case 84: M18_M84(); break; // M18/M84: Disable Steppers / Set Timeout
case 85: M85(); break; // M85: Set inactivity stepper shutdown timeout
case 92: M92(); break; // M92: Set the steps-per-unit for one or more axes
case 114: M114(); break; // M114: Report current position
case 115: M115(); break; // M115: Report capabilities
case 117: M117(); break; // M117: Set LCD message text, if possible
case 118: M118(); break; // M118: Display a message in the host console
case 119: M119(); break; // M119: Report endstop states
case 120: M120(); break; // M120: Enable endstops
case 121: M121(); break; // M121: Disable endstops
#if HOTENDS && HAS_LCD_MENU
case 145: M145(); break; // M145: Set material heatup parameters
#endif
#if ENABLED(TEMPERATURE_UNITS_SUPPORT)
case 149: M149(); break; // M149: Set temperature units
#endif
#if HAS_COLOR_LEDS
case 150: M150(); break; // M150: Set Status LED Color
#endif
#if ENABLED(MIXING_EXTRUDER)
case 163: M163(); break; // M163: Set a component weight for mixing extruder
case 164: M164(); break; // M164: Save current mix as a virtual extruder
#if ENABLED(DIRECT_MIXING_IN_G1)
case 165: M165(); break; // M165: Set multiple mix weights
#endif
#if ENABLED(GRADIENT_MIX)
case 166: M166(); break; // M166: Set Gradient Mix
#endif
#endif
#if DISABLED(NO_VOLUMETRICS)
case 200: M200(); break; // M200: Set filament diameter, E to cubic units
#endif
case 201: M201(); break; // M201: Set max acceleration for print moves (units/s^2)
#if 0
case 202: M202(); break; // M202: Not used for Sprinter/grbl gen6
#endif
case 203: M203(); break; // M203: Set max feedrate (units/sec)
case 204: M204(); break; // M204: Set acceleration
case 205: M205(); break; // M205: Set advanced settings
#if HAS_M206_COMMAND
case 206: M206(); break; // M206: Set home offsets
#endif
#if ENABLED(FWRETRACT)
case 207: M207(); break; // M207: Set Retract Length, Feedrate, and Z lift
case 208: M208(); break; // M208: Set Recover (unretract) Additional Length and Feedrate
#if ENABLED(FWRETRACT_AUTORETRACT)
case 209:
if (MIN_AUTORETRACT <= MAX_AUTORETRACT) M209(); // M209: Turn Automatic Retract Detection on/off
break;
#endif
#endif
#if HAS_SOFTWARE_ENDSTOPS
case 211: M211(); break; // M211: Enable, Disable, and/or Report software endstops
#endif
#if EXTRUDERS > 1
case 217: M217(); break; // M217: Set filament swap parameters
#endif
#if HAS_HOTEND_OFFSET
case 218: M218(); break; // M218: Set a tool offset
#endif
case 220: M220(); break; // M220: Set Feedrate Percentage: S<percent> ("FR" on your LCD)
#if EXTRUDERS
case 221: M221(); break; // M221: Set Flow Percentage
#endif
case 226: M226(); break; // M226: Wait until a pin reaches a state
#if HAS_SERVOS
case 280: M280(); break; // M280: Set servo position absolute
#if ENABLED(EDITABLE_SERVO_ANGLES)
case 281: M281(); break; // M281: Set servo angles
#endif
#endif
#if ENABLED(BABYSTEPPING)
case 290: M290(); break; // M290: Babystepping
#endif
#if HAS_BUZZER
case 300: M300(); break; // M300: Play beep tone
#endif
#if ENABLED(PIDTEMP)
case 301: M301(); break; // M301: Set hotend PID parameters
#endif
#if ENABLED(PIDTEMPBED)
case 304: M304(); break; // M304: Set bed PID parameters
#endif
#if ENABLED(PHOTO_GCODE)
case 240: M240(); break; // M240: Trigger a camera
#endif
#if HAS_LCD_CONTRAST
case 250: M250(); break; // M250: Set LCD contrast
#endif
#if ENABLED(EXPERIMENTAL_I2CBUS)
case 260: M260(); break; // M260: Send data to an i2c slave
case 261: M261(); break; // M261: Request data from an i2c slave
#endif
#if ENABLED(PREVENT_COLD_EXTRUSION)
case 302: M302(); break; // M302: Allow cold extrudes (set the minimum extrude temperature)
#endif
#if HAS_PID_HEATING
case 303: M303(); break; // M303: PID autotune
#endif
#if HAS_USER_THERMISTORS
case 305: M305(); break; // M305: Set user thermistor parameters
#endif
#if ENABLED(MORGAN_SCARA)
case 360: if (M360()) return; break; // M360: SCARA Theta pos1
case 361: if (M361()) return; break; // M361: SCARA Theta pos2
case 362: if (M362()) return; break; // M362: SCARA Psi pos1
case 363: if (M363()) return; break; // M363: SCARA Psi pos2
case 364: if (M364()) return; break; // M364: SCARA Psi pos3 (90 deg to Theta)
#endif
#if EITHER(EXT_SOLENOID, MANUAL_SOLENOID_CONTROL)
case 380: M380(); break; // M380: Activate solenoid on active (or specified) extruder
case 381: M381(); break; // M381: Disable all solenoids or, if MANUAL_SOLENOID_CONTROL, active (or specified) solenoid
#endif
case 400: M400(); break; // M400: Finish all moves
#if HAS_BED_PROBE
case 401: M401(); break; // M401: Deploy probe
case 402: M402(); break; // M402: Stow probe
#endif
#if ENABLED(PRUSA_MMU2)
case 403: M403(); break;
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR)
case 404: M404(); break; // M404: Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or display nominal filament width
case 405: M405(); break; // M405: Turn on filament sensor for control
case 406: M406(); break; // M406: Turn off filament sensor for control
case 407: M407(); break; // M407: Display measured filament diameter
#endif
#if HAS_FILAMENT_SENSOR
case 412: M412(); break; // M412: Enable/Disable filament runout detection
#endif
#if HAS_LEVELING
case 420: M420(); break; // M420: Enable/Disable Bed Leveling
#endif
#if HAS_MESH
case 421: M421(); break; // M421: Set a Mesh Bed Leveling Z coordinate
#endif
#if ENABLED(BACKLASH_GCODE)
case 425: M425(); break; // M425: Tune backlash compensation
#endif
#if HAS_M206_COMMAND
case 428: M428(); break; // M428: Apply current_position to home_offset
#endif
#if ENABLED(CANCEL_OBJECTS)
case 486: M486(); break; // M486: Identify and cancel objects
#endif
case 500: M500(); break; // M500: Store settings in EEPROM
case 501: M501(); break; // M501: Read settings from EEPROM
case 502: M502(); break; // M502: Revert to default settings
#if DISABLED(DISABLE_M503)
case 503: M503(); break; // M503: print settings currently in memory
#endif
#if ENABLED(EEPROM_SETTINGS)
case 504: M504(); break; // M504: Validate EEPROM contents
#endif
#if ENABLED(SDSUPPORT)
case 524: M524(); break; // M524: Abort the current SD print job
#endif
#if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
case 540: M540(); break; // M540: Set abort on endstop hit for SD printing
#endif
#if ENABLED(BAUD_RATE_GCODE)
case 575: M575(); break; // M575: Set serial baudrate
#endif
#if ENABLED(ADVANCED_PAUSE_FEATURE)
case 600: M600(); break; // M600: Pause for Filament Change
case 603: M603(); break; // M603: Configure Filament Change
#endif
#if HAS_DUPLICATION_MODE
case 605: M605(); break; // M605: Set Dual X Carriage movement mode
#endif
#if ENABLED(DELTA)
case 665: M665(); break; // M665: Set delta configurations
#endif
#if ENABLED(DELTA) || HAS_EXTRA_ENDSTOPS
case 666: M666(); break; // M666: Set delta or multiple endstop adjustment
#endif
#if ENABLED(SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
case 672: M672(); break; // M672: Set/clear Duet Smart Effector sensitivity
#endif
#if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
case 701: M701(); break; // M701: Load Filament
case 702: M702(); break; // M702: Unload Filament
#endif
#if ENABLED(CONTROLLER_FAN_EDITABLE)
case 710: M710(); break; // M710: Set Controller Fan settings
#endif
#if ENABLED(GCODE_MACROS)
case 810: case 811: case 812: case 813: case 814:
case 815: case 816: case 817: case 818: case 819:
M810_819(); break; // M810-M819: Define/execute G-code macro
#endif
#if HAS_BED_PROBE
case 851: M851(); break; // M851: Set Z Probe Z Offset
#endif
#if ENABLED(SKEW_CORRECTION_GCODE)
case 852: M852(); break; // M852: Set Skew factors
#endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
case 871: M871(); break; // M871: Print/reset/clear first layer temperature offset values
#endif
#if ENABLED(LIN_ADVANCE)
case 900: M900(); break; // M900: Set advance K factor.
#endif
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM || EITHER(DIGIPOT_I2C, DAC_STEPPER_CURRENT)
case 907: M907(); break; // M907: Set digital trimpot motor current using axis codes.
#if HAS_DIGIPOTSS || ENABLED(DAC_STEPPER_CURRENT)
case 908: M908(); break; // M908: Control digital trimpot directly.
#if ENABLED(DAC_STEPPER_CURRENT)
case 909: M909(); break; // M909: Print digipot/DAC current value
case 910: M910(); break; // M910: Commit digipot/DAC value to external EEPROM
#endif
#endif
#endif
#if HAS_TRINAMIC_CONFIG
case 122: M122(); break; // M122: Report driver configuration and status
case 906: M906(); break; // M906: Set motor current in milliamps using axis codes X, Y, Z, E
#if HAS_STEALTHCHOP
case 569: M569(); break; // M569: Enable stealthChop on an axis.
#endif
#if ENABLED(MONITOR_DRIVER_STATUS)
case 911: M911(); break; // M911: Report TMC2130 prewarn triggered flags
case 912: M912(); break; // M912: Clear TMC2130 prewarn triggered flags
#endif
#if ENABLED(HYBRID_THRESHOLD)
case 913: M913(); break; // M913: Set HYBRID_THRESHOLD speed.
#endif
#if USE_SENSORLESS
case 914: M914(); break; // M914: Set StallGuard sensitivity.
#endif
#endif
#if HAS_L64XX
case 122: M122(); break; // M122: Report status
case 906: M906(); break; // M906: Set or get motor drive level
case 916: M916(); break; // M916: L6470 tuning: Increase drive level until thermal warning
case 917: M917(); break; // M917: L6470 tuning: Find minimum current thresholds
case 918: M918(); break; // M918: L6470 tuning: Increase speed until max or error
#endif
#if HAS_MICROSTEPS
case 350: M350(); break; // M350: Set microstepping mode. Warning: Steps per unit remains unchanged. S code sets stepping mode for all drivers.
case 351: M351(); break; // M351: Toggle MS1 MS2 pins directly, S# determines MS1 or MS2, X# sets the pin high/low.
#endif
#if HAS_CASE_LIGHT
case 355: M355(); break; // M355: Set case light brightness
#endif
#if ENABLED(DEBUG_GCODE_PARSER)
case 800: parser.debug(); break; // M800: GCode Parser Test for M
#endif
#if ENABLED(I2C_POSITION_ENCODERS)
case 860: M860(); break; // M860: Report encoder module position
case 861: M861(); break; // M861: Report encoder module status
case 862: M862(); break; // M862: Perform axis test
case 863: M863(); break; // M863: Calibrate steps/mm
case 864: M864(); break; // M864: Change module address
case 865: M865(); break; // M865: Check module firmware version
case 866: M866(); break; // M866: Report axis error count
case 867: M867(); break; // M867: Toggle error correction
case 868: M868(); break; // M868: Set error correction threshold
case 869: M869(); break; // M869: Report axis error
#endif
#if ENABLED(MAGNETIC_PARKING_EXTRUDER)
case 951: M951(); break; // M951: Set Magnetic Parking Extruder parameters
#endif
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
case 422: M422(); break; // M422: Set Z Stepper automatic alignment position using probe
#endif
#if ENABLED(PLATFORM_M997_SUPPORT)
case 997: M997(); break; // M997: Perform in-application firmware update
#endif
case 999: M999(); break; // M999: Restart after being Stopped
#if ENABLED(POWER_LOSS_RECOVERY)
case 413: M413(); break; // M413: Enable/disable/query Power-Loss Recovery
case 1000: M1000(); break; // M1000: [INTERNAL] Resume from power-loss
#endif
#if ENABLED(SDSUPPORT)
case 1001: M1001(); break; // M1001: [INTERNAL] Handle SD completion
#endif
#if ENABLED(MAX7219_GCODE)
case 7219: M7219(); break; // M7219: Set LEDs, columns, and rows
#endif
default: parser.unknown_command_warning(); break;
}
break;
case 'T': T(parser.codenum); break; // Tn: Tool Change
default:
#if ENABLED(WIFI_CUSTOM_COMMAND)
if (wifi_custom_command(parser.command_ptr)) break;
#endif
parser.unknown_command_warning();
}
if (!no_ok) queue.ok_to_send();
}
/**
* Process a single command and dispatch it to its handler
* This is called from the main loop()
*/
void GcodeSuite::process_next_command() {
char * const current_command = queue.command_buffer[queue.index_r];
PORT_REDIRECT(queue.port[queue.index_r]);
#if ENABLED(POWER_LOSS_RECOVERY)
recovery.queue_index_r = queue.index_r;
#endif
if (DEBUGGING(ECHO)) {
SERIAL_ECHO_START();
SERIAL_ECHOLN(current_command);
#if ENABLED(M100_FREE_MEMORY_DUMPER)
SERIAL_ECHOPAIR("slot:", queue.index_r);
M100_dump_routine(PSTR(" Command Queue:"), &queue.command_buffer[0][0], &queue.command_buffer[BUFSIZE - 1][MAX_CMD_SIZE - 1]);
#endif
}
// Parse the next command in the queue
parser.parse(current_command);
process_parsed_command();
}
/**
* Run a series of commands, bypassing the command queue to allow
* G-code "macros" to be called from within other G-code handlers.
*/
void GcodeSuite::process_subcommands_now_P(PGM_P pgcode) {
char * const saved_cmd = parser.command_ptr; // Save the parser state
for (;;) {
PGM_P const delim = strchr_P(pgcode, '\n'); // Get address of next newline
const size_t len = delim ? delim - pgcode : strlen_P(pgcode); // Get the command length
char cmd[len + 1]; // Allocate a stack buffer
strncpy_P(cmd, pgcode, len); // Copy the command to the stack
cmd[len] = '\0'; // End with a nul
parser.parse(cmd); // Parse the command
process_parsed_command(true); // Process it
if (!delim) break; // Last command?
pgcode = delim + 1; // Get the next command
}
parser.parse(saved_cmd); // Restore the parser state
}
void GcodeSuite::process_subcommands_now(char * gcode) {
char * const saved_cmd = parser.command_ptr; // Save the parser state
for (;;) {
char * const delim = strchr(gcode, '\n'); // Get address of next newline
if (delim) *delim = '\0'; // Replace with nul
parser.parse(gcode); // Parse the current command
if (delim) *delim = '\n'; // Put back the newline
process_parsed_command(true); // Process it
if (!delim) break; // Last command?
gcode = delim + 1; // Get the next command
}
parser.parse(saved_cmd); // Restore the parser state
}
#if ENABLED(HOST_KEEPALIVE_FEATURE)
/**
* Output a "busy" message at regular intervals
* while the machine is not accepting commands.
*/
void GcodeSuite::host_keepalive() {
const millis_t ms = millis();
static millis_t next_busy_signal_ms = 0;
if (!autoreport_paused && host_keepalive_interval && busy_state != NOT_BUSY) {
if (PENDING(ms, next_busy_signal_ms)) return;
switch (busy_state) {
case IN_HANDLER:
case IN_PROCESS:
SERIAL_ECHO_MSG(STR_BUSY_PROCESSING);
break;
case PAUSED_FOR_USER:
SERIAL_ECHO_MSG(STR_BUSY_PAUSED_FOR_USER);
break;
case PAUSED_FOR_INPUT:
SERIAL_ECHO_MSG(STR_BUSY_PAUSED_FOR_INPUT);
break;
default:
break;
}
}
next_busy_signal_ms = ms + host_keepalive_interval * 1000UL;
}
#endif // HOST_KEEPALIVE_FEATURE

View File

@@ -0,0 +1,987 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* gcode.h - Temporary container for all gcode handlers
*/
/**
* -----------------
* G-Codes in Marlin
* -----------------
*
* Helpful G-code references:
* - http://marlinfw.org/meta/gcode
* - https://reprap.org/wiki/G-code
* - http://linuxcnc.org/docs/html/gcode.html
*
* Help to document Marlin's G-codes online:
* - https://github.com/MarlinFirmware/MarlinDocumentation
*
* -----------------
*
* "G" Codes
*
* G0 -> G1
* G1 - Coordinated Movement X Y Z E
* G2 - CW ARC
* G3 - CCW ARC
* G4 - Dwell S<seconds> or P<milliseconds>
* G5 - Cubic B-spline with XYZE destination and IJPQ offsets
* G10 - Retract filament according to settings of M207 (Requires FWRETRACT)
* G11 - Retract recover filament according to settings of M208 (Requires FWRETRACT)
* G12 - Clean tool (Requires NOZZLE_CLEAN_FEATURE)
* G17 - Select Plane XY (Requires CNC_WORKSPACE_PLANES)
* G18 - Select Plane ZX (Requires CNC_WORKSPACE_PLANES)
* G19 - Select Plane YZ (Requires CNC_WORKSPACE_PLANES)
* G20 - Set input units to inches (Requires INCH_MODE_SUPPORT)
* G21 - Set input units to millimeters (Requires INCH_MODE_SUPPORT)
* G26 - Mesh Validation Pattern (Requires G26_MESH_VALIDATION)
* G27 - Park Nozzle (Requires NOZZLE_PARK_FEATURE)
* G28 - Home one or more axes
* G29 - Start or continue the bed leveling probe procedure (Requires bed leveling)
* G30 - Single Z probe, probes bed at X Y location (defaults to current XY location)
* G31 - Dock sled (Z_PROBE_SLED only)
* G32 - Undock sled (Z_PROBE_SLED only)
* G33 - Delta Auto-Calibration (Requires DELTA_AUTO_CALIBRATION)
* G34 - Z Stepper automatic alignment using probe: I<iterations> T<accuracy> A<amplification> (Requires Z_STEPPER_AUTO_ALIGN)
* G38 - Probe in any direction using the Z_MIN_PROBE (Requires G38_PROBE_TARGET)
* G42 - Coordinated move to a mesh point (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BLINEAR, or AUTO_BED_LEVELING_UBL)
* G60 - Save current position. (Requires SAVED_POSITIONS)
* G61 - Apply/restore saved coordinates. (Requires SAVED_POSITIONS)
* G76 - Calibrate first layer temperature offsets. (Requires PROBE_TEMP_COMPENSATION)
* G80 - Cancel current motion mode (Requires GCODE_MOTION_MODES)
* G90 - Use Absolute Coordinates
* G91 - Use Relative Coordinates
* G92 - Set current position to coordinates given
*
* "M" Codes
*
* M0 - Unconditional stop - Wait for user to press a button on the LCD (Only if ULTRA_LCD is enabled)
* M1 -> M0
* M3 - Turn ON Laser | Spindle (clockwise), set Power | Speed. (Requires SPINDLE_FEATURE or LASER_FEATURE)
* M4 - Turn ON Laser | Spindle (counter-clockwise), set Power | Speed. (Requires SPINDLE_FEATURE or LASER_FEATURE)
* M5 - Turn OFF Laser | Spindle. (Requires SPINDLE_FEATURE or LASER_FEATURE)
* M7 - Turn mist coolant ON. (Requires COOLANT_CONTROL)
* M8 - Turn flood coolant ON. (Requires COOLANT_CONTROL)
* M9 - Turn coolant OFF. (Requires COOLANT_CONTROL)
* M12 - Set up closed loop control system. (Requires EXTERNAL_CLOSED_LOOP_CONTROLLER)
* M16 - Expected printer check. (Requires EXPECTED_PRINTER_CHECK)
* M17 - Enable/Power all stepper motors
* M18 - Disable all stepper motors; same as M84
* M20 - List SD card. (Requires SDSUPPORT)
* M21 - Init SD card. (Requires SDSUPPORT)
* M22 - Release SD card. (Requires SDSUPPORT)
* M23 - Select SD file: "M23 /path/file.gco". (Requires SDSUPPORT)
* M24 - Start/resume SD print. (Requires SDSUPPORT)
* M25 - Pause SD print. (Requires SDSUPPORT)
* M26 - Set SD position in bytes: "M26 S12345". (Requires SDSUPPORT)
* M27 - Report SD print status. (Requires SDSUPPORT)
* OR, with 'S<seconds>' set the SD status auto-report interval. (Requires AUTO_REPORT_SD_STATUS)
* OR, with 'C' get the current filename.
* M28 - Start SD write: "M28 /path/file.gco". (Requires SDSUPPORT)
* M29 - Stop SD write. (Requires SDSUPPORT)
* M30 - Delete file from SD: "M30 /path/file.gco"
* M31 - Report time since last M109 or SD card start to serial.
* M32 - Select file and start SD print: "M32 [S<bytepos>] !/path/file.gco#". (Requires SDSUPPORT)
* Use P to run other files as sub-programs: "M32 P !filename#"
* The '#' is necessary when calling from within sd files, as it stops buffer prereading
* M33 - Get the longname version of a path. (Requires LONG_FILENAME_HOST_SUPPORT)
* M34 - Set SD Card sorting options. (Requires SDCARD_SORT_ALPHA)
* M42 - Change pin status via gcode: M42 P<pin> S<value>. LED pin assumed if P is omitted.
* M43 - Display pin status, watch pins for changes, watch endstops & toggle LED, Z servo probe test, toggle pins
* M48 - Measure Z Probe repeatability: M48 P<points> X<pos> Y<pos> V<level> E<engage> L<legs> S<chizoid>. (Requires Z_MIN_PROBE_REPEATABILITY_TEST)
* M73 - Set the progress percentage. (Requires LCD_SET_PROGRESS_MANUALLY)
* M75 - Start the print job timer.
* M76 - Pause the print job timer.
* M77 - Stop the print job timer.
* M78 - Show statistical information about the print jobs. (Requires PRINTCOUNTER)
* M80 - Turn on Power Supply. (Requires PSU_CONTROL)
* M81 - Turn off Power Supply. (Requires PSU_CONTROL)
* M82 - Set E codes absolute (default).
* M83 - Set E codes relative while in Absolute (G90) mode.
* M84 - Disable steppers until next move, or use S<seconds> to specify an idle
* duration after which steppers should turn off. S0 disables the timeout.
* M85 - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
* M92 - Set planner.settings.axis_steps_per_mm for one or more axes.
* M100 - Watch Free Memory (for debugging) (Requires M100_FREE_MEMORY_WATCHER)
* M104 - Set extruder target temp.
* M105 - Report current temperatures.
* M106 - Set print fan speed.
* M107 - Print fan off.
* M108 - Break out of heating loops (M109, M190, M303). With no controller, breaks out of M0/M1. (Requires EMERGENCY_PARSER)
* M109 - S<temp> Wait for extruder current temp to reach target temp. ** Wait only when heating! **
* R<temp> Wait for extruder current temp to reach target temp. ** Wait for heating or cooling. **
* If AUTOTEMP is enabled, S<mintemp> B<maxtemp> F<factor>. Exit autotemp by any M109 without F
* M110 - Set the current line number. (Used by host printing)
* M111 - Set debug flags: "M111 S<flagbits>". See flag bits defined in enum.h.
* M112 - Full Shutdown.
* M113 - Get or set the timeout interval for Host Keepalive "busy" messages. (Requires HOST_KEEPALIVE_FEATURE)
* M114 - Report current position.
* M115 - Report capabilities. (Extended capabilities requires EXTENDED_CAPABILITIES_REPORT)
* M117 - Display a message on the controller screen. (Requires an LCD)
* M118 - Display a message in the host console.
* M119 - Report endstops status.
* M120 - Enable endstops detection.
* M121 - Disable endstops detection.
* M122 - Debug stepper (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470)
* M125 - Save current position and move to filament change position. (Requires PARK_HEAD_ON_PAUSE)
* M126 - Solenoid Air Valve Open. (Requires BARICUDA)
* M127 - Solenoid Air Valve Closed. (Requires BARICUDA)
* M128 - EtoP Open. (Requires BARICUDA)
* M129 - EtoP Closed. (Requires BARICUDA)
* M140 - Set bed target temp. S<temp>
* M141 - Set heated chamber target temp. S<temp> (Requires a chamber heater)
* M145 - Set heatup values for materials on the LCD. H<hotend> B<bed> F<fan speed> for S<material> (0=PLA, 1=ABS)
* M149 - Set temperature units. (Requires TEMPERATURE_UNITS_SUPPORT)
* M150 - Set Status LED Color as R<red> U<green> B<blue> P<bright>. Values 0-255. (Requires BLINKM, RGB_LED, RGBW_LED, NEOPIXEL_LED, PCA9533, or PCA9632).
* M155 - Auto-report temperatures with interval of S<seconds>. (Requires AUTO_REPORT_TEMPERATURES)
* M163 - Set a single proportion for a mixing extruder. (Requires MIXING_EXTRUDER)
* M164 - Commit the mix and save to a virtual tool (current, or as specified by 'S'). (Requires MIXING_EXTRUDER)
* M165 - Set the mix for the mixing extruder (and current virtual tool) with parameters ABCDHI. (Requires MIXING_EXTRUDER and DIRECT_MIXING_IN_G1)
* M166 - Set the Gradient Mix for the mixing extruder. (Requires GRADIENT_MIX)
* M190 - S<temp> Wait for bed current temp to reach target temp. ** Wait only when heating! **
* R<temp> Wait for bed current temp to reach target temp. ** Wait for heating or cooling. **
* M200 - Set filament diameter, D<diameter>, setting E axis units to cubic. (Use S0 to revert to linear units.)
* M201 - Set max acceleration in units/s^2 for print moves: "M201 X<accel> Y<accel> Z<accel> E<accel>"
* M202 - Set max acceleration in units/s^2 for travel moves: "M202 X<accel> Y<accel> Z<accel> E<accel>" ** UNUSED IN MARLIN! **
* M203 - Set maximum feedrate: "M203 X<fr> Y<fr> Z<fr> E<fr>" in units/sec.
* M204 - Set default acceleration in units/sec^2: P<printing> R<extruder_only> T<travel>
* M205 - Set advanced settings. Current units apply:
S<print> T<travel> minimum speeds
B<minimum segment time>
X<max X jerk>, Y<max Y jerk>, Z<max Z jerk>, E<max E jerk>
* M206 - Set additional homing offset. (Disabled by NO_WORKSPACE_OFFSETS or DELTA)
* M207 - Set Retract Length: S<length>, Feedrate: F<units/min>, and Z lift: Z<distance>. (Requires FWRETRACT)
* M208 - Set Recover (unretract) Additional (!) Length: S<length> and Feedrate: F<units/min>. (Requires FWRETRACT)
* M209 - Turn Automatic Retract Detection on/off: S<0|1> (For slicers that don't support G10/11). (Requires FWRETRACT_AUTORETRACT)
Every normal extrude-only move will be classified as retract depending on the direction.
* M211 - Enable, Disable, and/or Report software endstops: S<0|1> (Requires MIN_SOFTWARE_ENDSTOPS or MAX_SOFTWARE_ENDSTOPS)
* M217 - Set filament swap parameters: "M217 S<length> P<feedrate> R<feedrate>". (Requires SINGLENOZZLE)
* M218 - Set/get a tool offset: "M218 T<index> X<offset> Y<offset>". (Requires 2 or more extruders)
* M220 - Set Feedrate Percentage: "M220 S<percent>" (i.e., "FR" on the LCD)
* Use "M220 B" to back up the Feedrate Percentage and "M220 R" to restore it. (Requires PRUSA_MMU2)
* M221 - Set Flow Percentage: "M221 S<percent>"
* M226 - Wait until a pin is in a given state: "M226 P<pin> S<state>"
* M240 - Trigger a camera to take a photograph. (Requires PHOTO_GCODE)
* M250 - Set LCD contrast: "M250 C<contrast>" (0-63). (Requires LCD support)
* M260 - i2c Send Data (Requires EXPERIMENTAL_I2CBUS)
* M261 - i2c Request Data (Requires EXPERIMENTAL_I2CBUS)
* M280 - Set servo position absolute: "M280 P<index> S<angle|µs>". (Requires servos)
* M281 - Set servo min|max position: "M281 P<index> L<min> U<max>". (Requires EDITABLE_SERVO_ANGLES)
* M290 - Babystepping (Requires BABYSTEPPING)
* M300 - Play beep sound S<frequency Hz> P<duration ms>
* M301 - Set PID parameters P I and D. (Requires PIDTEMP)
* M302 - Allow cold extrudes, or set the minimum extrude S<temperature>. (Requires PREVENT_COLD_EXTRUSION)
* M303 - PID relay autotune S<temperature> sets the target temperature. Default 150C. (Requires PIDTEMP)
* M304 - Set bed PID parameters P I and D. (Requires PIDTEMPBED)
* M305 - Set user thermistor parameters R T and P. (Requires TEMP_SENSOR_x 1000)
* M350 - Set microstepping mode. (Requires digital microstepping pins.)
* M351 - Toggle MS1 MS2 pins directly. (Requires digital microstepping pins.)
* M355 - Set Case Light on/off and set brightness. (Requires CASE_LIGHT_PIN)
* M380 - Activate solenoid on active extruder. (Requires EXT_SOLENOID)
* M381 - Disable all solenoids. (Requires EXT_SOLENOID)
* M400 - Finish all moves.
* M401 - Deploy and activate Z probe. (Requires a probe)
* M402 - Deactivate and stow Z probe. (Requires a probe)
* M403 - Set filament type for PRUSA MMU2
* M404 - Display or set the Nominal Filament Width: "W<diameter>". (Requires FILAMENT_WIDTH_SENSOR)
* M405 - Enable Filament Sensor flow control. "M405 D<delay_cm>". (Requires FILAMENT_WIDTH_SENSOR)
* M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
* M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
* M410 - Quickstop. Abort all planned moves.
* M412 - Enable / Disable Filament Runout Detection. (Requires FILAMENT_RUNOUT_SENSOR)
* M413 - Enable / Disable Power-Loss Recovery. (Requires POWER_LOSS_RECOVERY)
* M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
* M421 - Set a single Z coordinate in the Mesh Leveling grid. X<units> Y<units> Z<units> (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BILINEAR, or AUTO_BED_LEVELING_UBL)
* M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
* M425 - Enable/Disable and tune backlash correction. (Requires BACKLASH_COMPENSATION and BACKLASH_GCODE)
* M428 - Set the home_offset based on the current_position. Nearest edge applies. (Disabled by NO_WORKSPACE_OFFSETS or DELTA)
* M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS)
* M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
* M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
* M502 - Revert to the default "factory settings". ** Does not write them to EEPROM! **
* M503 - Print the current settings (in memory): "M503 S<verbose>". S0 specifies compact output.
* M504 - Validate EEPROM contents. (Requires EEPROM_SETTINGS)
* M524 - Abort the current SD print job started with M24. (Requires SDSUPPORT)
* M540 - Enable/disable SD card abort on endstop hit: "M540 S<state>". (Requires SD_ABORT_ON_ENDSTOP_HIT)
* M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
* M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
* M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
* M605 - Set Dual X-Carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
* M665 - Set delta configurations: "M665 H<delta height> L<diagonal rod> R<delta radius> S<segments/s> B<calibration radius> X<Alpha angle trim> Y<Beta angle trim> Z<Gamma angle trim> (Requires DELTA)
* M666 - Set/get offsets for delta (Requires DELTA) or dual endstops. (Requires [XYZ]_DUAL_ENDSTOPS)
* M672 - Set/Reset Duet Smart Effector's sensitivity. (Requires SMART_EFFECTOR and SMART_EFFECTOR_MOD_PIN)
* M701 - Load filament (Requires FILAMENT_LOAD_UNLOAD_GCODES)
* M702 - Unload filament (Requires FILAMENT_LOAD_UNLOAD_GCODES)
* M810-M819 - Define/execute a G-code macro (Requires GCODE_MACROS)
* M851 - Set Z probe's XYZ offsets in current units. (Negative values: X=left, Y=front, Z=below)
* M852 - Set skew factors: "M852 [I<xy>] [J<xz>] [K<yz>]". (Requires SKEW_CORRECTION_GCODE, and SKEW_CORRECTION_FOR_Z for IJ)
* M860 - Report the position of position encoder modules.
* M861 - Report the status of position encoder modules.
* M862 - Perform an axis continuity test for position encoder modules.
* M863 - Perform steps-per-mm calibration for position encoder modules.
* M864 - Change position encoder module I2C address.
* M865 - Check position encoder module firmware version.
* M866 - Report or reset position encoder module error count.
* M867 - Enable/disable or toggle error correction for position encoder modules.
* M868 - Report or set position encoder module error correction threshold.
* M869 - Report position encoder module error.
* M871 - Print/reset/clear first layer temperature offset values. (Requires PROBE_TEMP_COMPENSATION)
* M876 - Handle Prompt Response. (Requires HOST_PROMPT_SUPPORT and not EMERGENCY_PARSER)
* M900 - Get or Set Linear Advance K-factor. (Requires LIN_ADVANCE)
* M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470)
* M907 - Set digital trimpot motor current using axis codes. (Requires a board with digital trimpots)
* M908 - Control digital trimpot directly. (Requires DAC_STEPPER_CURRENT or DIGIPOTSS_PIN)
* M909 - Print digipot/DAC current value. (Requires DAC_STEPPER_CURRENT)
* M910 - Commit digipot/DAC value to external EEPROM via I2C. (Requires DAC_STEPPER_CURRENT)
* M911 - Report stepper driver overtemperature pre-warn condition. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M912 - Clear stepper driver overtemperature pre-warn condition flag. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M913 - Set HYBRID_THRESHOLD speed. (Requires HYBRID_THRESHOLD)
* M914 - Set StallGuard sensitivity. (Requires SENSORLESS_HOMING or SENSORLESS_PROBING)
* M916 - L6470 tuning: Increase KVAL_HOLD until thermal warning. (Requires at least one _DRIVER_TYPE L6470)
* M917 - L6470 tuning: Find minimum current thresholds. (Requires at least one _DRIVER_TYPE L6470)
* M918 - L6470 tuning: Increase speed until max or error. (Requires at least one _DRIVER_TYPE L6470)
* M951 - Set Magnetic Parking Extruder parameters. (Requires MAGNETIC_PARKING_EXTRUDER)
* M7219 - Control Max7219 Matrix LEDs. (Requires MAX7219_GCODE)
*
* M360 - SCARA calibration: Move to cal-position ThetaA (0 deg calibration)
* M361 - SCARA calibration: Move to cal-position ThetaB (90 deg calibration - steps per degree)
* M362 - SCARA calibration: Move to cal-position PsiA (0 deg calibration)
* M363 - SCARA calibration: Move to cal-position PsiB (90 deg calibration - steps per degree)
* M364 - SCARA calibration: Move to cal-position PSIC (90 deg to Theta calibration position)
*
* ************ Custom codes - This can change to suit future G-code regulations
* G425 - Calibrate using a conductive object. (Requires CALIBRATION_GCODE)
* M928 - Start SD logging: "M928 filename.gco". Stop with M29. (Requires SDSUPPORT)
* M997 - Perform in-application firmware update
* M999 - Restart after being stopped by error
*
* "T" Codes
*
* T0-T3 - Select an extruder (tool) by index: "T<n> F<units/min>"
*
*/
#include "../inc/MarlinConfig.h"
#include "parser.h"
#if ENABLED(I2C_POSITION_ENCODERS)
#include "../feature/encoder_i2c.h"
#endif
enum AxisRelative : uint8_t { REL_X, REL_Y, REL_Z, REL_E, E_MODE_ABS, E_MODE_REL };
class GcodeSuite {
public:
static uint8_t axis_relative;
static inline bool axis_is_relative(const AxisEnum a) {
if (a == E_AXIS) {
if (TEST(axis_relative, E_MODE_REL)) return true;
if (TEST(axis_relative, E_MODE_ABS)) return false;
}
return TEST(axis_relative, a);
}
static inline void set_relative_mode(const bool rel) {
axis_relative = rel ? _BV(REL_X) | _BV(REL_Y) | _BV(REL_Z) | _BV(REL_E) : 0;
}
static inline void set_e_relative() {
CBI(axis_relative, E_MODE_ABS);
SBI(axis_relative, E_MODE_REL);
}
static inline void set_e_absolute() {
CBI(axis_relative, E_MODE_REL);
SBI(axis_relative, E_MODE_ABS);
}
#if ENABLED(CNC_WORKSPACE_PLANES)
/**
* Workspace planes only apply to G2/G3 moves
* (and "canned cycles" - not a current feature)
*/
enum WorkspacePlane : char { PLANE_XY, PLANE_ZX, PLANE_YZ };
static WorkspacePlane workspace_plane;
#endif
#define MAX_COORDINATE_SYSTEMS 9
#if ENABLED(CNC_COORDINATE_SYSTEMS)
static int8_t active_coordinate_system;
static xyz_pos_t coordinate_system[MAX_COORDINATE_SYSTEMS];
static bool select_coordinate_system(const int8_t _new);
#endif
static millis_t previous_move_ms;
FORCE_INLINE static void reset_stepper_timeout() { previous_move_ms = millis(); }
static int8_t get_target_extruder_from_command();
static int8_t get_target_e_stepper_from_command();
static void get_destination_from_command();
static void process_parsed_command(const bool no_ok=false);
static void process_next_command();
// Execute G-code in-place, preserving current G-code parameters
static void process_subcommands_now_P(PGM_P pgcode);
static void process_subcommands_now(char * gcode);
static inline void home_all_axes() {
extern const char G28_STR[];
process_subcommands_now_P(G28_STR);
}
#if HAS_AUTO_REPORTING || ENABLED(HOST_KEEPALIVE_FEATURE)
static bool autoreport_paused;
static inline bool set_autoreport_paused(const bool p) {
const bool was = autoreport_paused;
autoreport_paused = p;
return was;
}
#else
static constexpr bool autoreport_paused = false;
static inline bool set_autoreport_paused(const bool) { return false; }
#endif
#if ENABLED(HOST_KEEPALIVE_FEATURE)
/**
* States for managing Marlin and host communication
* Marlin sends messages if blocked or busy
*/
enum MarlinBusyState : char {
NOT_BUSY, // Not in a handler
IN_HANDLER, // Processing a GCode
IN_PROCESS, // Known to be blocking command input (as in G29)
PAUSED_FOR_USER, // Blocking pending any input
PAUSED_FOR_INPUT // Blocking pending text input (concept)
};
static MarlinBusyState busy_state;
static uint8_t host_keepalive_interval;
static void host_keepalive();
#define KEEPALIVE_STATE(N) REMEMBER(_KA_, gcode.busy_state, gcode.N)
#else
#define KEEPALIVE_STATE(N) NOOP
#endif
static void dwell(millis_t time);
private:
static void G0_G1(
#if IS_SCARA || defined(G0_FEEDRATE)
const bool fast_move=false
#endif
);
#if ENABLED(ARC_SUPPORT)
static void G2_G3(const bool clockwise);
#endif
static void G4();
#if ENABLED(BEZIER_CURVE_SUPPORT)
static void G5();
#endif
#if ENABLED(FWRETRACT)
static void G10();
static void G11();
#endif
#if ENABLED(NOZZLE_CLEAN_FEATURE)
static void G12();
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
static void G17();
static void G18();
static void G19();
#endif
#if ENABLED(INCH_MODE_SUPPORT)
static void G20();
static void G21();
#endif
#if ENABLED(G26_MESH_VALIDATION)
static void G26();
#endif
#if ENABLED(NOZZLE_PARK_FEATURE)
static void G27();
#endif
static void G28();
#if HAS_LEVELING
#if ENABLED(G29_RETRY_AND_RECOVER)
static void G29_with_retry();
#define G29_TYPE bool
#else
#define G29_TYPE void
#endif
static G29_TYPE G29();
#endif
#if HAS_BED_PROBE
static void G30();
#if ENABLED(Z_PROBE_SLED)
static void G31();
static void G32();
#endif
#endif
#if ENABLED(DELTA_AUTO_CALIBRATION)
static void G33();
#endif
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
static void G34();
static void M422();
#endif
#if ENABLED(G38_PROBE_TARGET)
static void G38(const int8_t subcode);
#endif
#if HAS_MESH
static void G42();
#endif
#if ENABLED(CNC_COORDINATE_SYSTEMS)
static void G53();
static void G54();
static void G55();
static void G56();
static void G57();
static void G58();
static void G59();
#endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
static void G76();
#endif
#if SAVED_POSITIONS
static void G60();
static void G61();
#endif
#if ENABLED(GCODE_MOTION_MODES)
static void G80();
#endif
static void G92();
#if ENABLED(CALIBRATION_GCODE)
static void G425();
#endif
#if HAS_RESUME_CONTINUE
static void M0_M1();
#endif
#if HAS_CUTTER
static void M3_M4(const bool is_M4);
static void M5();
#endif
#if ENABLED(COOLANT_CONTROL)
#if ENABLED(COOLANT_MIST)
static void M7();
#endif
#if ENABLED(COOLANT_FLOOD)
static void M8();
#endif
static void M9();
#endif
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
static void M12();
#endif
#if ENABLED(EXPECTED_PRINTER_CHECK)
static void M16();
#endif
static void M17();
static void M18_M84();
#if ENABLED(SDSUPPORT)
static void M20();
static void M21();
static void M22();
static void M23();
static void M24();
static void M25();
static void M26();
static void M27();
static void M28();
static void M29();
static void M30();
#endif
static void M31();
#if ENABLED(SDSUPPORT)
static void M32();
#if ENABLED(LONG_FILENAME_HOST_SUPPORT)
static void M33();
#endif
#if BOTH(SDCARD_SORT_ALPHA, SDSORT_GCODE)
static void M34();
#endif
#endif
static void M42();
#if ENABLED(PINS_DEBUGGING)
static void M43();
#endif
#if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
static void M48();
#endif
#if ENABLED(LCD_SET_PROGRESS_MANUALLY)
static void M73();
#endif
static void M75();
static void M76();
static void M77();
#if ENABLED(PRINTCOUNTER)
static void M78();
#endif
#if ENABLED(PSU_CONTROL)
static void M80();
#endif
static void M81();
static void M82();
static void M83();
static void M85();
static void M92();
#if ENABLED(M100_FREE_MEMORY_WATCHER)
static void M100();
#endif
#if EXTRUDERS
static void M104();
static void M109();
#endif
static void M105();
#if FAN_COUNT > 0
static void M106();
static void M107();
#endif
#if DISABLED(EMERGENCY_PARSER)
static void M108();
static void M112();
static void M410();
#if ENABLED(HOST_PROMPT_SUPPORT)
static void M876();
#endif
#endif
static void M110();
static void M111();
#if ENABLED(HOST_KEEPALIVE_FEATURE)
static void M113();
#endif
static void M114();
static void M115();
static void M117();
static void M118();
static void M119();
static void M120();
static void M121();
#if ENABLED(PARK_HEAD_ON_PAUSE)
static void M125();
#endif
#if ENABLED(BARICUDA)
#if HAS_HEATER_1
static void M126();
static void M127();
#endif
#if HAS_HEATER_2
static void M128();
static void M129();
#endif
#endif
#if HAS_HEATED_BED
static void M140();
static void M190();
#endif
#if HAS_HEATED_CHAMBER
static void M141();
static void M191();
#endif
#if HOTENDS && HAS_LCD_MENU
static void M145();
#endif
#if ENABLED(TEMPERATURE_UNITS_SUPPORT)
static void M149();
#endif
#if HAS_COLOR_LEDS
static void M150();
#endif
#if ENABLED(AUTO_REPORT_TEMPERATURES) && HAS_TEMP_SENSOR
static void M155();
#endif
#if ENABLED(MIXING_EXTRUDER)
static void M163();
static void M164();
#if ENABLED(DIRECT_MIXING_IN_G1)
static void M165();
#endif
#if ENABLED(GRADIENT_MIX)
static void M166();
#endif
#endif
static void M200();
static void M201();
#if 0
static void M202(); // Not used for Sprinter/grbl gen6
#endif
static void M203();
static void M204();
static void M205();
#if HAS_M206_COMMAND
static void M206();
#endif
#if ENABLED(FWRETRACT)
static void M207();
static void M208();
#if ENABLED(FWRETRACT_AUTORETRACT)
static void M209();
#endif
#endif
static void M211();
#if EXTRUDERS > 1
static void M217();
#endif
#if HAS_HOTEND_OFFSET
static void M218();
#endif
static void M220();
#if EXTRUDERS
static void M221();
#endif
static void M226();
#if ENABLED(PHOTO_GCODE)
static void M240();
#endif
#if HAS_LCD_CONTRAST
static void M250();
#endif
#if ENABLED(EXPERIMENTAL_I2CBUS)
static void M260();
static void M261();
#endif
#if HAS_SERVOS
static void M280();
#if ENABLED(EDITABLE_SERVO_ANGLES)
static void M281();
#endif
#endif
#if ENABLED(BABYSTEPPING)
static void M290();
#endif
#if HAS_BUZZER
static void M300();
#endif
#if ENABLED(PIDTEMP)
static void M301();
#endif
#if ENABLED(PREVENT_COLD_EXTRUSION)
static void M302();
#endif
#if HAS_PID_HEATING
static void M303();
#endif
#if ENABLED(PIDTEMPBED)
static void M304();
#endif
#if HAS_USER_THERMISTORS
static void M305();
#endif
#if HAS_MICROSTEPS
static void M350();
static void M351();
#endif
#if HAS_CASE_LIGHT
static void M355();
#endif
#if ENABLED(MORGAN_SCARA)
static bool M360();
static bool M361();
static bool M362();
static bool M363();
static bool M364();
#endif
#if EITHER(EXT_SOLENOID, MANUAL_SOLENOID_CONTROL)
static void M380();
static void M381();
#endif
static void M400();
#if HAS_BED_PROBE
static void M401();
static void M402();
#endif
#if ENABLED(PRUSA_MMU2)
static void M403();
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR)
static void M404();
static void M405();
static void M406();
static void M407();
#endif
#if HAS_FILAMENT_SENSOR
static void M412();
#endif
#if HAS_LEVELING
static void M420();
static void M421();
#endif
#if ENABLED(BACKLASH_GCODE)
static void M425();
#endif
#if HAS_M206_COMMAND
static void M428();
#endif
#if ENABLED(CANCEL_OBJECTS)
static void M486();
#endif
static void M500();
static void M501();
static void M502();
#if DISABLED(DISABLE_M503)
static void M503();
#endif
#if ENABLED(EEPROM_SETTINGS)
static void M504();
#endif
#if ENABLED(SDSUPPORT)
static void M524();
#endif
#if ENABLED(SD_ABORT_ON_ENDSTOP_HIT)
static void M540();
#endif
#if ENABLED(BAUD_RATE_GCODE)
static void M575();
#endif
#if ENABLED(ADVANCED_PAUSE_FEATURE)
static void M600();
static void M603();
#endif
#if HAS_DUPLICATION_MODE
static void M605();
#endif
#if IS_KINEMATIC
static void M665();
#endif
#if ENABLED(DELTA) || HAS_EXTRA_ENDSTOPS
static void M666();
#endif
#if ENABLED(SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
static void M672();
#endif
#if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
static void M701();
static void M702();
#endif
#if ENABLED(GCODE_MACROS)
static void M810_819();
#endif
#if HAS_BED_PROBE
static void M851();
#endif
#if ENABLED(SKEW_CORRECTION_GCODE)
static void M852();
#endif
#if ENABLED(I2C_POSITION_ENCODERS)
FORCE_INLINE static void M860() { I2CPEM.M860(); }
FORCE_INLINE static void M861() { I2CPEM.M861(); }
FORCE_INLINE static void M862() { I2CPEM.M862(); }
FORCE_INLINE static void M863() { I2CPEM.M863(); }
FORCE_INLINE static void M864() { I2CPEM.M864(); }
FORCE_INLINE static void M865() { I2CPEM.M865(); }
FORCE_INLINE static void M866() { I2CPEM.M866(); }
FORCE_INLINE static void M867() { I2CPEM.M867(); }
FORCE_INLINE static void M868() { I2CPEM.M868(); }
FORCE_INLINE static void M869() { I2CPEM.M869(); }
#endif
#if ENABLED(PROBE_TEMP_COMPENSATION)
static void M871();
#endif
#if ENABLED(LIN_ADVANCE)
static void M900();
#endif
#if HAS_TRINAMIC_CONFIG
static void M122();
static void M906();
#if HAS_STEALTHCHOP
static void M569();
#endif
#if ENABLED(MONITOR_DRIVER_STATUS)
static void M911();
static void M912();
#endif
#if ENABLED(HYBRID_THRESHOLD)
static void M913();
#endif
#if USE_SENSORLESS
static void M914();
#endif
#endif
#if HAS_L64XX
static void M122();
static void M906();
static void M916();
static void M917();
static void M918();
#endif
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM || EITHER(DIGIPOT_I2C, DAC_STEPPER_CURRENT)
static void M907();
#if HAS_DIGIPOTSS || ENABLED(DAC_STEPPER_CURRENT)
static void M908();
#if ENABLED(DAC_STEPPER_CURRENT)
static void M909();
static void M910();
#endif
#endif
#endif
#if ENABLED(SDSUPPORT)
static void M928();
#endif
#if ENABLED(MAGNETIC_PARKING_EXTRUDER)
static void M951();
#endif
#if ENABLED(PLATFORM_M997_SUPPORT)
static void M997();
#endif
static void M999();
#if ENABLED(POWER_LOSS_RECOVERY)
static void M413();
static void M1000();
#endif
#if ENABLED(SDSUPPORT)
static void M1001();
#endif
#if ENABLED(MAX7219_GCODE)
static void M7219();
#endif
#if ENABLED(CONTROLLER_FAN_EDITABLE)
static void M710();
#endif
static void T(const uint8_t tool_index);
};
extern GcodeSuite gcode;

View File

@@ -0,0 +1,53 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(CNC_WORKSPACE_PLANES)
#include "../gcode.h"
inline void report_workspace_plane() {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Workspace Plane ");
serialprintPGM(
gcode.workspace_plane == GcodeSuite::PLANE_YZ ? PSTR("YZ\n")
: gcode.workspace_plane == GcodeSuite::PLANE_ZX ? PSTR("ZX\n")
: PSTR("XY\n")
);
}
inline void set_workspace_plane(const GcodeSuite::WorkspacePlane plane) {
gcode.workspace_plane = plane;
if (DEBUGGING(INFO)) report_workspace_plane();
}
/**
* G17: Select Plane XY
* G18: Select Plane ZX
* G19: Select Plane YZ
*/
void GcodeSuite::G17() { set_workspace_plane(PLANE_XY); }
void GcodeSuite::G18() { set_workspace_plane(PLANE_ZX); }
void GcodeSuite::G19() { set_workspace_plane(PLANE_YZ); }
#endif // CNC_WORKSPACE_PLANES

View File

@@ -0,0 +1,101 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/motion.h"
#if ENABLED(CNC_COORDINATE_SYSTEMS)
#include "../../module/stepper.h"
//#define DEBUG_M53
/**
* Select a coordinate system and update the workspace offset.
* System index -1 is used to specify machine-native.
*/
bool GcodeSuite::select_coordinate_system(const int8_t _new) {
if (active_coordinate_system == _new) return false;
active_coordinate_system = _new;
xyz_float_t new_offset{0};
if (WITHIN(_new, 0, MAX_COORDINATE_SYSTEMS - 1))
new_offset = coordinate_system[_new];
LOOP_XYZ(i) {
if (position_shift[i] != new_offset[i]) {
position_shift[i] = new_offset[i];
update_workspace_offset((AxisEnum)i);
}
}
return true;
}
/**
* G53: Apply native workspace to the current move
*
* In CNC G-code G53 is a modifier.
* It precedes a movement command (or other modifiers) on the same line.
* This is the first command to use parser.chain() to make this possible.
*
* Marlin also uses G53 on a line by itself to go back to native space.
*/
void GcodeSuite::G53() {
const int8_t old_system = active_coordinate_system;
select_coordinate_system(-1); // Always remove workspace offsets
#ifdef DEBUG_M53
SERIAL_ECHOLNPGM("Go to native space");
report_current_position();
#endif
if (parser.chain()) { // Command to chain?
process_parsed_command(); // ...process the chained command
select_coordinate_system(old_system);
#ifdef DEBUG_M53
SERIAL_ECHOLNPAIR("Go back to workspace ", old_system);
report_current_position();
#endif
}
}
/**
* G54-G59.3: Select a new workspace
*
* A workspace is an XYZ offset to the machine native space.
* All workspaces default to 0,0,0 at start, or with EEPROM
* support they may be restored from a previous session.
*
* G92 is used to set the current workspace's offset.
*/
void G54_59(uint8_t subcode=0) {
const int8_t _space = parser.codenum - 54 + subcode;
if (gcode.select_coordinate_system(_space)) {
SERIAL_ECHOLNPAIR("Select workspace ", _space);
report_current_position();
}
}
void GcodeSuite::G54() { G54_59(); }
void GcodeSuite::G55() { G54_59(); }
void GcodeSuite::G56() { G54_59(); }
void GcodeSuite::G57() { G54_59(); }
void GcodeSuite::G58() { G54_59(); }
void GcodeSuite::G59() { G54_59(parser.subcode); }
#endif // CNC_COORDINATE_SYSTEMS

View File

@@ -0,0 +1,103 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#if ENABLED(I2C_POSITION_ENCODERS)
#include "../../feature/encoder_i2c.h"
#endif
/**
* G92: Set current position to given X Y Z E
*/
void GcodeSuite::G92() {
bool sync_E = false, sync_XYZ = false;
#if ENABLED(USE_GCODE_SUBCODES)
const uint8_t subcode_G92 = parser.subcode;
#else
constexpr uint8_t subcode_G92 = 0;
#endif
switch (subcode_G92) {
default: break;
#if ENABLED(CNC_COORDINATE_SYSTEMS)
case 1: {
// Zero the G92 values and restore current position
#if !IS_SCARA
LOOP_XYZ(i) if (position_shift[i]) {
position_shift[i] = 0;
update_workspace_offset((AxisEnum)i);
}
#endif // Not SCARA
} return;
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
case 9: {
LOOP_XYZE(i) {
if (parser.seenval(axis_codes[i])) {
current_position[i] = parser.value_axis_units((AxisEnum)i);
if (i == E_AXIS) sync_E = true; else sync_XYZ = true;
}
}
} break;
#endif
case 0: {
LOOP_XYZE(i) {
if (parser.seenval(axis_codes[i])) {
const float l = parser.value_axis_units((AxisEnum)i),
v = i == E_AXIS ? l : LOGICAL_TO_NATIVE(l, i),
d = v - current_position[i];
if (!NEAR_ZERO(d)) {
#if IS_SCARA || !HAS_POSITION_SHIFT
if (i == E_AXIS) sync_E = true; else sync_XYZ = true;
current_position[i] = v; // Without workspaces revert to Marlin 1.0 behavior
#elif HAS_POSITION_SHIFT
if (i == E_AXIS) {
sync_E = true;
current_position.e = v; // When using coordinate spaces, only E is set directly
}
else {
position_shift[i] += d; // Other axes simply offset the coordinate space
update_workspace_offset((AxisEnum)i);
}
#endif
}
}
}
} break;
}
#if ENABLED(CNC_COORDINATE_SYSTEMS)
// Apply workspace offset to the active coordinate system
if (WITHIN(active_coordinate_system, 0, MAX_COORDINATE_SYSTEMS - 1))
coordinate_system[active_coordinate_system] = position_shift;
#endif
if (sync_XYZ) sync_plan_position();
else if (sync_E) sync_plan_position_e();
report_current_position();
}

View File

@@ -0,0 +1,86 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if HAS_M206_COMMAND
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../lcd/ultralcd.h"
#include "../../libs/buzzer.h"
/**
* M206: Set Additional Homing Offset (X Y Z). SCARA aliases T=X, P=Y
*
* *** @thinkyhead: I recommend deprecating M206 for SCARA in favor of M665.
* *** M206 for SCARA will remain enabled in 1.1.x for compatibility.
* *** In the 2.0 release, it will simply be disabled by default.
*/
void GcodeSuite::M206() {
LOOP_XYZ(i)
if (parser.seen(XYZ_CHAR(i)))
set_home_offset((AxisEnum)i, parser.value_linear_units());
#if ENABLED(MORGAN_SCARA)
if (parser.seen('T')) set_home_offset(A_AXIS, parser.value_float()); // Theta
if (parser.seen('P')) set_home_offset(B_AXIS, parser.value_float()); // Psi
#endif
report_current_position();
}
/**
* M428: Set home_offset based on the distance between the
* current_position and the nearest "reference point."
* If an axis is past center its endstop position
* is the reference-point. Otherwise it uses 0. This allows
* the Z offset to be set near the bed when using a max endstop.
*
* M428 can't be used more than 2cm away from 0 or an endstop.
*
* Use M206 to set these values directly.
*/
void GcodeSuite::M428() {
if (axis_unhomed_error()) return;
xyz_float_t diff;
LOOP_XYZ(i) {
diff[i] = base_home_pos((AxisEnum)i) - current_position[i];
if (!WITHIN(diff[i], -20, 20) && home_dir((AxisEnum)i) > 0)
diff[i] = -current_position[i];
if (!WITHIN(diff[i], -20, 20)) {
SERIAL_ERROR_MSG(STR_ERR_M428_TOO_FAR);
LCD_ALERTMESSAGEPGM_P(PSTR("Err: Too far!"));
BUZZ(200, 40);
return;
}
}
LOOP_XYZ(i) set_home_offset((AxisEnum)i, diff[i]);
report_current_position();
LCD_MESSAGEPGM(MSG_HOME_OFFSETS_APPLIED);
BUZZ(100, 659);
BUZZ(100, 698);
}
#endif // HAS_M206_COMMAND

View File

@@ -0,0 +1,31 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../queue.h" // for last_N
/**
* M110: Set Current Line Number
*/
void GcodeSuite::M110() {
if (parser.seenval('N')) queue.last_N = parser.value_long();
}

View File

@@ -0,0 +1,45 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(HOST_KEEPALIVE_FEATURE)
#include "../gcode.h"
/**
* M113: Get or set Host Keepalive interval (0 to disable)
*
* S<seconds> Optional. Set the keepalive interval.
*/
void GcodeSuite::M113() {
if (parser.seenval('S')) {
host_keepalive_interval = parser.value_byte();
NOMORE(host_keepalive_interval, 60);
}
else {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR("M113 S", (unsigned long)host_keepalive_interval);
}
}
#endif // HOST_KEEPALIVE_FEATURE

View File

@@ -0,0 +1,220 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#if ENABLED(M114_DETAIL)
#if HAS_L64XX
#include "../../libs/L64XX/L64XX_Marlin.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../core/debug_out.h"
#endif
void report_xyze(const xyze_pos_t &pos, const uint8_t n=XYZE, const uint8_t precision=3) {
char str[12];
LOOP_L_N(a, n) {
SERIAL_CHAR(' ', axis_codes[a], ':');
SERIAL_ECHO(dtostrf(pos[a], 1, precision, str));
}
SERIAL_EOL();
}
inline void report_xyz(const xyze_pos_t &pos) { report_xyze(pos, 3); }
void report_xyz(const xyz_pos_t &pos, const uint8_t precision=3) {
char str[12];
LOOP_XYZ(a) {
SERIAL_CHAR(' ', XYZ_CHAR(a), ':');
SERIAL_ECHO(dtostrf(pos[a], 1, precision, str));
}
SERIAL_EOL();
}
void report_current_position_detail() {
// Position as sent by G-code
SERIAL_ECHOPGM("\nLogical:");
report_xyz(current_position.asLogical());
// Cartesian position in native machine space
SERIAL_ECHOPGM("Raw: ");
report_xyz(current_position);
xyze_pos_t leveled = current_position;
#if HAS_LEVELING
// Current position with leveling applied
SERIAL_ECHOPGM("Leveled:");
planner.apply_leveling(leveled);
report_xyz(leveled);
// Test planner un-leveling. This should match the Raw result.
SERIAL_ECHOPGM("UnLevel:");
xyze_pos_t unleveled = leveled;
planner.unapply_leveling(unleveled);
report_xyz(unleveled);
#endif
#if IS_KINEMATIC
// Kinematics applied to the leveled position
#if IS_SCARA
SERIAL_ECHOPGM("ScaraK: ");
#else
SERIAL_ECHOPGM("DeltaK: ");
#endif
inverse_kinematics(leveled); // writes delta[]
report_xyz(delta);
#endif
planner.synchronize();
#if HAS_L64XX
char temp_buf[80];
int32_t temp;
//#define ABS_POS_SIGN_MASK 0b1111 1111 1110 0000 0000 0000 0000 0000
#define ABS_POS_SIGN_MASK 0b11111111111000000000000000000000
#define REPORT_ABSOLUTE_POS(Q) do{ \
L64xxManager.say_axis(Q, false); \
temp = L6470_GETPARAM(L6470_ABS_POS,Q); \
if (temp & ABS_POS_SIGN_MASK) temp |= ABS_POS_SIGN_MASK; \
sprintf_P(temp_buf, PSTR(":%8ld "), temp); \
DEBUG_ECHO(temp_buf); \
}while(0)
DEBUG_ECHOPGM("\nL6470:");
#if AXIS_IS_L64XX(X)
REPORT_ABSOLUTE_POS(X);
#endif
#if AXIS_IS_L64XX(X2)
REPORT_ABSOLUTE_POS(X2);
#endif
#if AXIS_IS_L64XX(Y)
REPORT_ABSOLUTE_POS(Y);
#endif
#if AXIS_IS_L64XX(Y2)
REPORT_ABSOLUTE_POS(Y2);
#endif
#if AXIS_IS_L64XX(Z)
REPORT_ABSOLUTE_POS(Z);
#endif
#if AXIS_IS_L64XX(Z2)
REPORT_ABSOLUTE_POS(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
REPORT_ABSOLUTE_POS(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
REPORT_ABSOLUTE_POS(Z4);
#endif
#if AXIS_IS_L64XX(E0)
REPORT_ABSOLUTE_POS(E0);
#endif
#if AXIS_IS_L64XX(E1)
REPORT_ABSOLUTE_POS(E1);
#endif
#if AXIS_IS_L64XX(E2)
REPORT_ABSOLUTE_POS(E2);
#endif
#if AXIS_IS_L64XX(E3)
REPORT_ABSOLUTE_POS(E3);
#endif
#if AXIS_IS_L64XX(E4)
REPORT_ABSOLUTE_POS(E4);
#endif
#if AXIS_IS_L64XX(E5)
REPORT_ABSOLUTE_POS(E5);
#endif
#if AXIS_IS_L64XX(E6)
REPORT_ABSOLUTE_POS(E6);
#endif
#if AXIS_IS_L64XX(E7)
REPORT_ABSOLUTE_POS(E7);
#endif
SERIAL_EOL();
#endif // HAS_L64XX
SERIAL_ECHOPGM("Stepper:");
LOOP_XYZE(i) {
SERIAL_CHAR(' ', axis_codes[i], ':');
SERIAL_ECHO(stepper.position((AxisEnum)i));
}
SERIAL_EOL();
#if IS_SCARA
const xy_float_t deg = {
planner.get_axis_position_degrees(A_AXIS),
planner.get_axis_position_degrees(B_AXIS)
};
SERIAL_ECHOPGM("Degrees:");
report_xyze(deg, 2);
#endif
SERIAL_ECHOPGM("FromStp:");
get_cartesian_from_steppers(); // writes 'cartes' (with forward kinematics)
xyze_pos_t from_steppers = { cartes.x, cartes.y, cartes.z, planner.get_axis_position_mm(E_AXIS) };
report_xyze(from_steppers);
const xyze_float_t diff = from_steppers - leveled;
SERIAL_ECHOPGM("Diff: ");
report_xyze(diff);
}
#endif // M114_DETAIL
/**
* M114: Report the current position to host.
* Since steppers are moving, the count positions are
* projected by using planner calculations.
* D - Report more detail. This syncs the planner. (Requires M114_DETAIL)
* E - Report E stepper position (Requires M114_DETAIL)
* R - Report the realtime position instead of projected.
*/
void GcodeSuite::M114() {
#if ENABLED(M114_DETAIL)
if (parser.seen('D')) {
#if DISABLED(M114_LEGACY)
planner.synchronize();
#endif
report_current_position();
report_current_position_detail();
return;
}
if (parser.seen('E')) {
SERIAL_ECHOLNPAIR("Count E:", stepper.position(E_AXIS));
return;
}
#endif
#if ENABLED(M114_REALTIME)
if (parser.seen('R')) { report_real_position(); return; }
#endif
#if ENABLED(M114_LEGACY)
planner.synchronize();
#endif
report_current_position_projected();
}

Some files were not shown because too many files have changed in this diff Show More