aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/gcode/feature
diff options
context:
space:
mode:
authorGeorgiy Bondarenko <69736697+nehilo@users.noreply.github.com>2021-03-04 20:54:23 +0300
committerGeorgiy Bondarenko <69736697+nehilo@users.noreply.github.com>2021-03-04 20:54:23 +0300
commite8701195e66f2d27ffe17fb514eae8173795aaf7 (patch)
tree9f519c4abf6556b9ae7190a6210d87ead1dfadde /Marlin/src/gcode/feature
downloadkp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.tar.xz
kp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.zip
Initial commit
Diffstat (limited to 'Marlin/src/gcode/feature')
-rw-r--r--Marlin/src/gcode/feature/L6470/M122.cpp151
-rw-r--r--Marlin/src/gcode/feature/L6470/M906.cpp370
-rw-r--r--Marlin/src/gcode/feature/L6470/M916-918.cpp651
-rw-r--r--Marlin/src/gcode/feature/advance/M900.cpp147
-rw-r--r--Marlin/src/gcode/feature/baricuda/M126-M129.cpp58
-rw-r--r--Marlin/src/gcode/feature/camera/M240.cpp204
-rw-r--r--Marlin/src/gcode/feature/cancel/M486.cpp57
-rw-r--r--Marlin/src/gcode/feature/caselight/M355.cpp73
-rw-r--r--Marlin/src/gcode/feature/clean/G12.cpp80
-rw-r--r--Marlin/src/gcode/feature/controllerfan/M710.cpp81
-rw-r--r--Marlin/src/gcode/feature/digipot/M907-M910.cpp102
-rw-r--r--Marlin/src/gcode/feature/filwidth/M404-M407.cpp71
-rw-r--r--Marlin/src/gcode/feature/fwretract/G10_G11.cpp51
-rw-r--r--Marlin/src/gcode/feature/fwretract/M207-M209.cpp74
-rw-r--r--Marlin/src/gcode/feature/i2c/M260_M261.cpp76
-rw-r--r--Marlin/src/gcode/feature/leds/M150.cpp84
-rw-r--r--Marlin/src/gcode/feature/leds/M7219.cpp93
-rw-r--r--Marlin/src/gcode/feature/macro/M810-M819.cpp65
-rw-r--r--Marlin/src/gcode/feature/mixing/M163-M165.cpp101
-rw-r--r--Marlin/src/gcode/feature/mixing/M166.cpp103
-rw-r--r--Marlin/src/gcode/feature/network/M552-M554.cpp126
-rw-r--r--Marlin/src/gcode/feature/password/M510-M512.cpp83
-rw-r--r--Marlin/src/gcode/feature/pause/G27.cpp41
-rw-r--r--Marlin/src/gcode/feature/pause/G60.cpp58
-rw-r--r--Marlin/src/gcode/feature/pause/G61.cpp73
-rw-r--r--Marlin/src/gcode/feature/pause/M125.cpp90
-rw-r--r--Marlin/src/gcode/feature/pause/M600.cpp172
-rw-r--r--Marlin/src/gcode/feature/pause/M603.cpp65
-rw-r--r--Marlin/src/gcode/feature/pause/M701_M702.cpp235
-rw-r--r--Marlin/src/gcode/feature/power_monitor/M430.cpp70
-rw-r--r--Marlin/src/gcode/feature/powerloss/M1000.cpp89
-rw-r--r--Marlin/src/gcode/feature/powerloss/M413.cpp62
-rw-r--r--Marlin/src/gcode/feature/prusa_MMU2/M403.cpp49
-rw-r--r--Marlin/src/gcode/feature/runout/M412.cpp64
-rw-r--r--Marlin/src/gcode/feature/trinamic/M122.cpp60
-rw-r--r--Marlin/src/gcode/feature/trinamic/M569.cpp186
-rw-r--r--Marlin/src/gcode/feature/trinamic/M906.cpp173
-rw-r--r--Marlin/src/gcode/feature/trinamic/M911-M914.cpp429
38 files changed, 4817 insertions, 0 deletions
diff --git a/Marlin/src/gcode/feature/L6470/M122.cpp b/Marlin/src/gcode/feature/L6470/M122.cpp
new file mode 100644
index 0000000..d2b7f73
--- /dev/null
+++ b/Marlin/src/gcode/feature/L6470/M122.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/L6470/M906.cpp b/Marlin/src/gcode/feature/L6470/M906.cpp
new file mode 100644
index 0000000..7bd446a
--- /dev/null
+++ b/Marlin/src/gcode/feature/L6470/M906.cpp
@@ -0,0 +1,370 @@
+/**
+ * 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 <https://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
diff --git a/Marlin/src/gcode/feature/L6470/M916-918.cpp b/Marlin/src/gcode/feature/L6470/M916-918.cpp
new file mode 100644
index 0000000..8a1ea48
--- /dev/null
+++ b/Marlin/src/gcode/feature/L6470/M916-918.cpp
@@ -0,0 +1,651 @@
+/**
+ * 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 <https://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 = SEC_TO_MS(M91x_delay_s * 60);
+ 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(); // keep steppers powered
+ watchdog_refresh();
+ 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
diff --git a/Marlin/src/gcode/feature/advance/M900.cpp b/Marlin/src/gcode/feature/advance/M900.cpp
new file mode 100644
index 0000000..5c7155d
--- /dev/null
+++ b/Marlin/src/gcode/feature/advance/M900.cpp
@@ -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 <https://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_DECIMAL(planner.extruder_advance_K[i]);
+ }
+ SERIAL_EOL();
+ #endif
+
+ #endif
+ }
+
+}
+
+#endif // LIN_ADVANCE
diff --git a/Marlin/src/gcode/feature/baricuda/M126-M129.cpp b/Marlin/src/gcode/feature/baricuda/M126-M129.cpp
new file mode 100644
index 0000000..edeba0d
--- /dev/null
+++ b/Marlin/src/gcode/feature/baricuda/M126-M129.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/camera/M240.cpp b/Marlin/src/gcode/feature/camera/M240.cpp
new file mode 100644
index 0000000..fc350d8
--- /dev/null
+++ b/Marlin/src/gcode/feature/camera/M240.cpp
@@ -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 <https://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.
+ * https://captain-slow.dk/2014/03/09/3d-printing-timelapses/
+ * - PHOTOGRAPH_PIN : Pulse a digital pin 16 times.
+ * See https://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 (homing_needed_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
diff --git a/Marlin/src/gcode/feature/cancel/M486.cpp b/Marlin/src/gcode/feature/cancel/M486.cpp
new file mode 100644
index 0000000..1f14ae0
--- /dev/null
+++ b/Marlin/src/gcode/feature/cancel/M486.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/caselight/M355.cpp b/Marlin/src/gcode/feature/caselight/M355.cpp
new file mode 100644
index 0000000..12ae5cf
--- /dev/null
+++ b/Marlin/src/gcode/feature/caselight/M355.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(CASE_LIGHT_ENABLE)
+
+#include "../../../feature/caselight.h"
+#include "../../gcode.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() {
+ bool didset = false;
+ #if CASELIGHT_USES_BRIGHTNESS
+ if (parser.seenval('P')) {
+ didset = true;
+ caselight.brightness = parser.value_byte();
+ }
+ #endif
+ const bool sflag = parser.seenval('S');
+ if (sflag) {
+ didset = true;
+ caselight.on = parser.value_bool();
+ }
+ if (didset) caselight.update(sflag);
+
+ // Always report case light status
+ SERIAL_ECHO_START();
+ SERIAL_ECHOPGM("Case light: ");
+ if (!caselight.on)
+ SERIAL_ECHOLNPGM(STR_OFF);
+ else {
+ #if CASELIGHT_USES_BRIGHTNESS
+ if (PWM_PIN(CASE_LIGHT_PIN)) {
+ SERIAL_ECHOLN(int(caselight.brightness));
+ return;
+ }
+ #endif
+ SERIAL_ECHOLNPGM(STR_ON);
+ }
+}
+
+#endif // CASE_LIGHT_ENABLE
diff --git a/Marlin/src/gcode/feature/clean/G12.cpp b/Marlin/src/gcode/feature/clean/G12.cpp
new file mode 100644
index 0000000..216db5b
--- /dev/null
+++ b/Marlin/src/gcode/feature/clean/G12.cpp
@@ -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 <https://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
+ *
+ * E<bool> : 0=Never or 1=Always apply the "software endstop" limits
+ * P0 S<strokes> : Stroke cleaning with S strokes
+ * P1 Sn T<objects> : Zigzag cleaning with S repeats and T zigzags
+ * P2 Sn R<radius> : Circle cleaning with S repeats and R radius
+ */
+void GcodeSuite::G12() {
+ // Don't allow nozzle cleaning without homing first
+ if (homing_needed_error()) return;
+
+ #ifdef WIPE_SEQUENCE_COMMANDS
+ if (!parser.seen_any()) {
+ gcode.process_subcommands_now_P(PSTR(WIPE_SEQUENCE_COMMANDS));
+ return;
+ }
+ #endif
+
+ 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.linearval('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)
+ | TERN(NOZZLE_CLEAN_NO_Z, 0, (!seenxyz || parser.boolval('Z') ? _BV(Z_AXIS) : 0))
+ ;
+
+ #if HAS_LEVELING
+ // Disable bed leveling if cleaning Z
+ TEMPORARY_BED_LEVELING_STATE(!TEST(cleans, Z_AXIS) && planner.leveling_active);
+ #endif
+
+ SET_SOFT_ENDSTOP_LOOSE(!parser.boolval('E'));
+
+ nozzle.clean(pattern, strokes, radius, objects, cleans);
+
+ SET_SOFT_ENDSTOP_LOOSE(false);
+}
+
+#endif // NOZZLE_CLEAN_FEATURE
diff --git a/Marlin/src/gcode/feature/controllerfan/M710.cpp b/Marlin/src/gcode/feature/controllerfan/M710.cpp
new file mode 100644
index 0000000..cc45073
--- /dev/null
+++ b/Marlin/src/gcode/feature/controllerfan/M710.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/digipot/M907-M910.cpp b/Marlin/src/gcode/feature/digipot/M907-M910.cpp
new file mode 100644
index 0000000..e463666
--- /dev/null
+++ b/Marlin/src/gcode/feature/digipot/M907-M910.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_MOTOR_CURRENT_I2C, HAS_MOTOR_CURRENT_DAC)
+
+#include "../../gcode.h"
+
+#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM
+ #include "../../../module/stepper.h"
+#endif
+
+#if HAS_MOTOR_CURRENT_I2C
+ #include "../../../feature/digipot/digipot.h"
+#endif
+
+#if ENABLED(HAS_MOTOR_CURRENT_DAC)
+ #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_MOTOR_CURRENT_SPI
+
+ LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.set_digipot_current(i, parser.value_int());
+ if (parser.seenval('B')) stepper.set_digipot_current(4, parser.value_int());
+ if (parser.seenval('S')) LOOP_LE_N(i, 4) stepper.set_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.set_digipot_current(0, parser.value_int());
+ #endif
+ #if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
+ if (parser.seenval('Z')) stepper.set_digipot_current(1, parser.value_int());
+ #endif
+ #if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
+ if (parser.seenval('E')) stepper.set_digipot_current(2, parser.value_int());
+ #endif
+
+ #endif
+
+ #if HAS_MOTOR_CURRENT_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(HAS_MOTOR_CURRENT_DAC)
+ if (parser.seenval('S')) {
+ const float dac_percent = parser.value_float();
+ LOOP_LE_N(i, 4) stepper_dac.set_current_percent(i, dac_percent);
+ }
+ LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper_dac.set_current_percent(i, parser.value_float());
+ #endif
+}
+
+#if EITHER(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_DAC)
+
+ /**
+ * M908: Control digital trimpot directly (M908 P<pin> S<current>)
+ */
+ void GcodeSuite::M908() {
+ TERN_(HAS_MOTOR_CURRENT_SPI, stepper.set_digipot_value_spi(parser.intval('P'), parser.intval('S')));
+ TERN_(HAS_MOTOR_CURRENT_DAC, stepper_dac.set_current_value(parser.byteval('P', -1), parser.ushortval('S', 0)));
+ }
+
+ #if ENABLED(HAS_MOTOR_CURRENT_DAC)
+
+ void GcodeSuite::M909() { stepper_dac.print_values(); }
+ void GcodeSuite::M910() { stepper_dac.commit_eeprom(); }
+
+ #endif // HAS_MOTOR_CURRENT_DAC
+
+#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_DAC
+
+#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM || HAS_MOTOR_CURRENT_I2C || HAS_MOTOR_CURRENT_DAC
diff --git a/Marlin/src/gcode/feature/filwidth/M404-M407.cpp b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp
new file mode 100644
index 0000000..a70f7a6
--- /dev/null
+++ b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(FILAMENT_WIDTH_SENSOR)
+
+#include "../../../feature/filwidth.h"
+#include "../../../module/planner.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
diff --git a/Marlin/src/gcode/feature/fwretract/G10_G11.cpp b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp
new file mode 100644
index 0000000..219502f
--- /dev/null
+++ b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp
@@ -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 <https://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 HAS_MULTI_EXTRUDER
+ const bool rs = parser.boolval('S');
+ #endif
+ fwretract.retract(true
+ #if HAS_MULTI_EXTRUDER
+ , rs
+ #endif
+ );
+}
+
+/**
+ * G11 - Recover filament according to settings of M208
+ */
+void GcodeSuite::G11() { fwretract.retract(false); }
+
+#endif // FWRETRACT
diff --git a/Marlin/src/gcode/feature/fwretract/M207-M209.cpp b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp
new file mode 100644
index 0000000..538f16c
--- /dev/null
+++ b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/i2c/M260_M261.cpp b/Marlin/src/gcode/feature/i2c/M260_M261.cpp
new file mode 100644
index 0000000..438d152
--- /dev/null
+++ b/Marlin/src/gcode/feature/i2c/M260_M261.cpp
@@ -0,0 +1,76 @@
+/**
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(EXPERIMENTAL_I2CBUS)
+
+#include "../../gcode.h"
+
+#include "../../../feature/twibus.h"
+
+/**
+ * M260: Send data to a I2C slave device
+ *
+ * This is a PoC, the formatting 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
diff --git a/Marlin/src/gcode/feature/leds/M150.cpp b/Marlin/src/gcode/feature/leds/M150.cpp
new file mode 100644
index 0000000..cf09bf1
--- /dev/null
+++ b/Marlin/src/gcode/feature/leds/M150.cpp
@@ -0,0 +1,84 @@
+/**
+ * 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 <https://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
+ *
+ * With NEOPIXEL_LED:
+ * I<index> Set the NeoPixel index to affect. Default: All
+ *
+ * With NEOPIXEL2_SEPARATE:
+ * S<index> The NeoPixel strip to set. Default is index 0.
+ *
+ * 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
+ * M150 I1 R ; Set NEOPIXEL index 1 to red
+ * M150 S1 I1 R ; Set SEPARATE index 1 to red
+ */
+
+void GcodeSuite::M150() {
+ #if ENABLED(NEOPIXEL_LED)
+ const uint8_t index = parser.intval('I', -1);
+ #if ENABLED(NEOPIXEL2_SEPARATE)
+ const uint8_t unit = parser.intval('S'),
+ brightness = unit ? neo2.brightness() : neo.brightness();
+ *(unit ? &neo2.neoindex : &neo.neoindex) = index;
+ #else
+ const uint8_t brightness = neo.brightness();
+ neo.neoindex = index;
+ #endif
+ #endif
+
+ const LEDColor 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) : brightness
+ );
+
+ #if ENABLED(NEOPIXEL2_SEPARATE)
+ if (unit == 1) { leds2.set_color(color); return; }
+ #endif
+
+ leds.set_color(color);
+}
+
+#endif // HAS_COLOR_LEDS
diff --git a/Marlin/src/gcode/feature/leds/M7219.cpp b/Marlin/src/gcode/feature/leds/M7219.cpp
new file mode 100644
index 0000000..a6ee711
--- /dev/null
+++ b/Marlin/src/gcode/feature/leds/M7219.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/macro/M810-M819.cpp b/Marlin/src/gcode/feature/macro/M810-M819.cpp
new file mode 100644
index 0000000..7b9e1a1
--- /dev/null
+++ b/Marlin/src/gcode/feature/macro/M810-M819.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/mixing/M163-M165.cpp b/Marlin/src/gcode/feature/mixing/M163-M165.cpp
new file mode 100644
index 0000000..a4cb64e
--- /dev/null
+++ b/Marlin/src/gcode/feature/mixing/M163-M165.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/mixing/M166.cpp b/Marlin/src/gcode/feature/mixing/M166.cpp
new file mode 100644
index 0000000..9e071a4
--- /dev/null
+++ b/Marlin/src/gcode/feature/mixing/M166.cpp
@@ -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 <https://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_ECHOPGM(" ; Current Z");
+ #if ENABLED(DELTA)
+ get_cartesian_from_steppers();
+ SERIAL_ECHO(cartes.z);
+ #else
+ SERIAL_ECHO(planner.get_axis_position_mm(Z_AXIS));
+ #endif
+ echo_mix();
+ }
+
+ SERIAL_EOL();
+}
+
+#endif // GRADIENT_MIX
diff --git a/Marlin/src/gcode/feature/network/M552-M554.cpp b/Marlin/src/gcode/feature/network/M552-M554.cpp
new file mode 100644
index 0000000..6ea15fe
--- /dev/null
+++ b/Marlin/src/gcode/feature/network/M552-M554.cpp
@@ -0,0 +1,126 @@
+/**
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if HAS_ETHERNET
+
+#include "../../../feature/ethernet.h"
+#include "../../../core/serial.h"
+#include "../../gcode.h"
+
+void say_ethernet() { SERIAL_ECHOPGM(" Ethernet "); }
+
+void ETH0_report() {
+ say_ethernet();
+ SERIAL_ECHO_TERNARY(ethernet.hardware_enabled, "port ", "en", "dis", "abled.\n");
+ if (ethernet.hardware_enabled) {
+ say_ethernet();
+ SERIAL_ECHO_TERNARY(ethernet.have_telnet_client, "client ", "en", "dis", "abled.\n");
+ }
+ else
+ SERIAL_ECHOLNPGM("Send 'M552 S1' to enable.");
+}
+
+void MAC_report() {
+ uint8_t mac[6];
+ if (ethernet.hardware_enabled) {
+ Ethernet.MACAddress(mac);
+ SERIAL_ECHOPGM(" MAC: ");
+ LOOP_L_N(i, 6) {
+ if (mac[i] < 16) SERIAL_CHAR('0');
+ SERIAL_PRINT(mac[i], HEX);
+ if (i < 5) SERIAL_CHAR(':');
+ }
+ }
+ SERIAL_EOL();
+}
+
+// Display current values when the link is active,
+// otherwise show the stored values
+void ip_report(const uint16_t cmd, PGM_P const post, const IPAddress &ipo) {
+ SERIAL_CHAR('M'); SERIAL_ECHO(cmd); SERIAL_CHAR(' ');
+ LOOP_L_N(i, 4) {
+ SERIAL_ECHO(ipo[i]);
+ if (i < 3) SERIAL_CHAR('.');
+ }
+ SERIAL_ECHOPGM(" ; ");
+ SERIAL_ECHOPGM_P(post);
+ SERIAL_EOL();
+}
+void M552_report() {
+ ip_report(552, PSTR("ip address"), Ethernet.linkStatus() == LinkON ? Ethernet.localIP() : ethernet.ip);
+}
+void M553_report() {
+ ip_report(553, PSTR("subnet mask"), Ethernet.linkStatus() == LinkON ? Ethernet.subnetMask() : ethernet.subnet);
+}
+void M554_report() {
+ ip_report(554, PSTR("gateway"), Ethernet.linkStatus() == LinkON ? Ethernet.gatewayIP() : ethernet.gateway);
+}
+
+/**
+ * M552: Set IP address, enable/disable network interface
+ *
+ * S0 : disable networking
+ * S1 : enable networking
+ * S-1 : reset network interface
+ *
+ * Pnnn : Set IP address, 0.0.0.0 means acquire an IP address using DHCP
+ */
+void GcodeSuite::M552() {
+ const bool seenP = parser.seenval('P');
+ if (seenP) ethernet.ip.fromString(parser.value_string());
+
+ const bool seenS = parser.seenval('S');
+ if (seenS) {
+ switch (parser.value_int()) {
+ case -1:
+ if (ethernet.telnetClient) ethernet.telnetClient.stop();
+ ethernet.init();
+ break;
+ case 0: ethernet.hardware_enabled = false; break;
+ case 1: ethernet.hardware_enabled = true; break;
+ default: break;
+ }
+ }
+ const bool nopar = !seenS && !seenP;
+ if (nopar || seenS) ETH0_report();
+ if (nopar || seenP) M552_report();
+}
+
+/**
+ * M553 Pnnn - Set netmask
+ */
+void GcodeSuite::M553() {
+ if (parser.seenval('P')) ethernet.subnet.fromString(parser.value_string());
+ M553_report();
+}
+
+/**
+ * M554 Pnnn - Set Gateway
+ */
+void GcodeSuite::M554() {
+ if (parser.seenval('P')) ethernet.gateway.fromString(parser.value_string());
+ M554_report();
+}
+
+#endif // HAS_ETHERNET
diff --git a/Marlin/src/gcode/feature/password/M510-M512.cpp b/Marlin/src/gcode/feature/password/M510-M512.cpp
new file mode 100644
index 0000000..eeb9b1d
--- /dev/null
+++ b/Marlin/src/gcode/feature/password/M510-M512.cpp
@@ -0,0 +1,83 @@
+/**
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(PASSWORD_FEATURE)
+
+#include "../../../feature/password/password.h"
+#include "../../../core/serial.h"
+#include "../../gcode.h"
+
+//
+// M510: Lock Printer
+//
+void GcodeSuite::M510() {
+ password.lock_machine();
+}
+
+//
+// M511: Unlock Printer
+//
+#if ENABLED(PASSWORD_UNLOCK_GCODE)
+
+ void GcodeSuite::M511() {
+ if (password.is_locked) {
+ password.value_entry = parser.ulongval('P');
+ password.authentication_check();
+ }
+ }
+
+#endif // PASSWORD_UNLOCK_GCODE
+
+//
+// M512: Set/Change/Remove Password
+//
+#if ENABLED(PASSWORD_CHANGE_GCODE)
+
+ void GcodeSuite::M512() {
+ if (password.is_set && parser.ulongval('P') != password.value) {
+ SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD);
+ return;
+ }
+
+ if (parser.seenval('S')) {
+ password.value_entry = parser.ulongval('S');
+
+ if (password.value_entry < CAT(1e, PASSWORD_LENGTH)) {
+ password.is_set = true;
+ password.value = password.value_entry;
+ SERIAL_ECHOLNPAIR(STR_PASSWORD_SET, password.value); // TODO: Update password.string
+ }
+ else
+ SERIAL_ECHOLNPGM(STR_PASSWORD_TOO_LONG);
+ }
+ else {
+ password.is_set = false;
+ SERIAL_ECHOLNPGM(STR_PASSWORD_REMOVED);
+ }
+ SERIAL_ECHOLNPGM(STR_REMINDER_SAVE_SETTINGS);
+ }
+
+#endif // PASSWORD_CHANGE_GCODE
+
+#endif // PASSWORD_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/G27.cpp b/Marlin/src/gcode/feature/pause/G27.cpp
new file mode 100644
index 0000000..3ce618d
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G27.cpp
@@ -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 <https://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 (homing_needed_error()) return;
+ nozzle.park(parser.ushortval('P'));
+}
+
+#endif // NOZZLE_PARK_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/G60.cpp b/Marlin/src/gcode/feature/pause/G60.cpp
new file mode 100644
index 0000000..6f695b9
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G60.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if SAVED_POSITIONS
+
+#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
diff --git a/Marlin/src/gcode/feature/pause/G61.cpp b/Marlin/src/gcode/feature/pause/G61.cpp
new file mode 100644
index 0000000..5d89af0
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G61.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if SAVED_POSITIONS
+
+#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;
+
+ 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();
+
+ // Apply any given feedrate over 0.0
+ feedRate_t saved_feedrate = feedrate_mm_s;
+ const float fr = parser.linearval('F');
+ if (fr > 0.0) feedrate_mm_s = MMM_TO_MMS(fr);
+
+ // Move to the saved position
+ prepare_line_to_destination();
+
+ feedrate_mm_s = saved_feedrate;
+}
+
+#endif // SAVED_POSITIONS
diff --git a/Marlin/src/gcode/feature/pause/M125.cpp b/Marlin/src/gcode/feature/pause/M125.cpp
new file mode 100644
index 0000000..9391b86
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M125.cpp
@@ -0,0 +1,90 @@
+/**
+ * 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 <https://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 "../../../lcd/marlinui.h"
+#include "../../../module/motion.h"
+#include "../../../module/printcounter.h"
+#include "../../../sd/cardreader.h"
+
+#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<linear> = Override retract Length
+ * X<pos> = Override park position X
+ * Y<pos> = Override park position Y
+ * Z<linear> = Override Z raise
+ *
+ * With an LCD menu:
+ * P<bool> = Always show a prompt and await a response
+ */
+void GcodeSuite::M125() {
+ // Initial retract before move to filament change position
+ const float retract = -ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : (PAUSE_PARK_RETRACT_LENGTH));
+
+ 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
+
+ const bool sd_printing = TERN0(SDSUPPORT, IS_SD_PRINTING());
+
+ ui.pause_show_message(PAUSE_MESSAGE_PARKING, PAUSE_MODE_PAUSE_PRINT);
+
+ // If possible, show an LCD prompt with the 'P' flag
+ const bool show_lcd = TERN0(HAS_LCD_MENU, parser.boolval('P'));
+
+ if (pause_print(retract, park_point, 0, show_lcd)) {
+ TERN_(POWER_LOSS_RECOVERY, if (recovery.enabled) recovery.save(true));
+ if (ENABLED(EXTENSIBLE_UI) || !sd_printing || show_lcd) {
+ wait_for_confirmation(false, 0);
+ resume_print(0, 0, -retract, 0);
+ }
+ }
+}
+
+#endif // PARK_HEAD_ON_PAUSE
diff --git a/Marlin/src/gcode/feature/pause/M600.cpp b/Marlin/src/gcode/feature/pause/M600.cpp
new file mode 100644
index 0000000..1c282f2
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M600.cpp
@@ -0,0 +1,172 @@
+/**
+ * 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 <https://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"
+#include "../../../lcd/marlinui.h"
+
+#if HAS_MULTI_EXTRUDER
+ #include "../../../module/tool_change.h"
+#endif
+
+#if ENABLED(MMU2_MENUS)
+ #include "../../../lcd/menu/menu_mmu2.h"
+#endif
+
+#if ENABLED(MIXING_EXTRUDER)
+ #include "../../../feature/mixing.h"
+#endif
+
+#if HAS_FILAMENT_SENSOR
+ #include "../../../feature/runout.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
+ * R[temp] - Resume temperature (in current units)
+ *
+ * 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 (idex_is_duplicating())
+ DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT2_STATE) ? 1 : 0;
+ #else
+ DXC_ext = active_extruder;
+ #endif
+ }
+ #endif
+
+ // Show initial "wait for start" message
+ #if DISABLED(MMU2_MENUS)
+ ui.pause_show_message(PAUSE_MESSAGE_CHANGING, PAUSE_MODE_PAUSE_PRINT, target_extruder);
+ #endif
+
+ #if ENABLED(HOME_BEFORE_FILAMENT_CHANGE)
+ // If needed, home before parking for filament change
+ if (!all_axes_trusted()) home_all_axes(true);
+ #endif
+
+ #if HAS_MULTI_EXTRUDER
+ // Change toolhead if specified
+ const uint8_t active_extruder_before_filament_change = active_extruder;
+ if (active_extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !idex_is_duplicating()))
+ 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) : (PAUSE_PARK_RETRACT_LENGTH));
+
+ 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', -1
+ #ifdef FILAMENT_CHANGE_ALERT_BEEPS
+ + 1 + FILAMENT_CHANGE_ALERT_BEEPS
+ #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, (parser.seenval('R') ? parser.value_celsius() : 0) DXC_PASS);
+ #endif
+ }
+
+ #if HAS_MULTI_EXTRUDER
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+}
+
+#endif // ADVANCED_PAUSE_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/M603.cpp b/Marlin/src/gcode/feature/pause/M603.cpp
new file mode 100644
index 0000000..9c3b774
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M603.cpp
@@ -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 <https://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 HAS_MULTI_EXTRUDER
+ #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
diff --git a/Marlin/src/gcode/feature/pause/M701_M702.cpp b/Marlin/src/gcode/feature/pause/M701_M702.cpp
new file mode 100644
index 0000000..9a2b774
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M701_M702.cpp
@@ -0,0 +1,235 @@
+/**
+ * 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 <https://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"
+#include "../../../lcd/marlinui.h"
+
+#if HAS_MULTI_EXTRUDER
+ #include "../../../module/tool_change.h"
+#endif
+
+#if HAS_PRUSA_MMU2
+ #include "../../../feature/mmu/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;
+
+ // Don't raise Z if the machine isn't homed
+ if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
+
+ #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
+ ui.pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder);
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // 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 HAS_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 HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+
+ // Show status screen
+ ui.pause_show_message(PAUSE_MESSAGE_STATUS);
+}
+
+/**
+ * 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;
+
+ // Don't raise Z if the machine isn't homed
+ if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
+
+ #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;
+ const bool seenT = parser.seenval('T');
+ if (!seenT) {
+ mixer.T(MIXER_AUTORETRACT_TOOL);
+ mix_multiplier = MIXING_STEPPERS;
+ }
+ #else
+ constexpr bool seenT = true;
+ #endif
+
+ if (seenT) {
+ 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
+ ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder);
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // 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 HAS_PRUSA_MMU2
+ mmu2.unload();
+ #else
+ #if BOTH(HAS_MULTI_EXTRUDER, 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 HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+
+ // Show status screen
+ ui.pause_show_message(PAUSE_MESSAGE_STATUS);
+}
+
+#endif // ADVANCED_PAUSE_FEATURE
diff --git a/Marlin/src/gcode/feature/power_monitor/M430.cpp b/Marlin/src/gcode/feature/power_monitor/M430.cpp
new file mode 100644
index 0000000..9559404
--- /dev/null
+++ b/Marlin/src/gcode/feature/power_monitor/M430.cpp
@@ -0,0 +1,70 @@
+/**
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if HAS_POWER_MONITOR
+
+#include "../../../feature/power_monitor.h"
+#include "../../../MarlinCore.h"
+#include "../../gcode.h"
+
+/**
+ * M430: Enable/disable current LCD display
+ * With no parameters report the system current draw (in Amps)
+ *
+ * I[bool] - Set Display of current on the LCD
+ * V[bool] - Set Display of voltage on the LCD
+ * W[bool] - Set Display of power on the LCD
+ */
+void GcodeSuite::M430() {
+ bool do_report = true;
+ #if HAS_WIRED_LCD
+ #if ENABLED(POWER_MONITOR_CURRENT)
+ if (parser.seen('I')) { power_monitor.set_current_display(parser.value_bool()); do_report = false; }
+ #endif
+ #if HAS_POWER_MONITOR_VREF
+ if (parser.seen('V')) { power_monitor.set_voltage_display(parser.value_bool()); do_report = false; }
+ #endif
+ #if HAS_POWER_MONITOR_WATTS
+ if (parser.seen('W')) { power_monitor.set_power_display(parser.value_bool()); do_report = false; }
+ #endif
+ #endif
+ if (do_report) {
+ SERIAL_ECHOLNPAIR(
+ #if ENABLED(POWER_MONITOR_CURRENT)
+ "Current: ", power_monitor.getAmps(), "A"
+ #if HAS_POWER_MONITOR_VREF
+ " "
+ #endif
+ #endif
+ #if HAS_POWER_MONITOR_VREF
+ "Voltage: ", power_monitor.getVolts(), "V"
+ #endif
+ #if HAS_POWER_MONITOR_WATTS
+ " Power: ", power_monitor.getPower(), "W"
+ #endif
+ );
+ }
+}
+
+#endif // HAS_POWER_MONITOR
diff --git a/Marlin/src/gcode/feature/powerloss/M1000.cpp b/Marlin/src/gcode/feature/powerloss/M1000.cpp
new file mode 100644
index 0000000..14c9253
--- /dev/null
+++ b/Marlin/src/gcode/feature/powerloss/M1000.cpp
@@ -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 <https://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/marlinui.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(" Job 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(DWIN_CREALITY_LCD)
+ recovery.dwin_flag = true;
+ #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
+ TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerStopped());
+ }
+ else
+ recovery.resume();
+ }
+ else
+ plr_error(recovery.info.valid_head ? PSTR("No") : PSTR("Invalid"));
+
+}
+
+#endif // POWER_LOSS_RECOVERY
diff --git a/Marlin/src/gcode/feature/powerloss/M413.cpp b/Marlin/src/gcode/feature/powerloss/M413.cpp
new file mode 100644
index 0000000..64573e5
--- /dev/null
+++ b/Marlin/src/gcode/feature/powerloss/M413.cpp
@@ -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 <https://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/marlinui.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('D')) recovery.debug(PSTR("M413"));
+ #if PIN_EXISTS(POWER_LOSS)
+ if (parser.seen('O')) recovery._outage();
+ #endif
+ 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
diff --git a/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp b/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp
new file mode 100644
index 0000000..31d0763
--- /dev/null
+++ b/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp
@@ -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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if HAS_PRUSA_MMU2
+
+#include "../../gcode.h"
+#include "../../../feature/mmu/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 // HAS_PRUSA_MMU2
diff --git a/Marlin/src/gcode/feature/runout/M412.cpp b/Marlin/src/gcode/feature/runout/M412.cpp
new file mode 100644
index 0000000..130f9c8
--- /dev/null
+++ b/Marlin/src/gcode/feature/runout/M412.cpp
@@ -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 <https://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
+ *
+ * Parameters
+ * R : Reset the runout sensor
+ * S<bool> : Reset and enable/disable the runout sensor
+ * H<bool> : Enable/disable host handling of filament runout
+ * D<linear> : Extra distance to continue after runout is triggered
+ */
+void GcodeSuite::M412() {
+ if (parser.seen("RS"
+ TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, "D")
+ TERN_(HOST_ACTION_COMMANDS, "H")
+ )) {
+ #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();
+ #if HAS_FILAMENT_RUNOUT_DISTANCE
+ 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);
+ #if HAS_FILAMENT_RUNOUT_DISTANCE
+ SERIAL_ECHOLNPAIR("Filament runout distance (mm): ", runout.runout_distance());
+ #endif
+ }
+}
+
+#endif // HAS_FILAMENT_SENSOR
diff --git a/Marlin/src/gcode/feature/trinamic/M122.cpp b/Marlin/src/gcode/feature/trinamic/M122.cpp
new file mode 100644
index 0000000..46e4365
--- /dev/null
+++ b/Marlin/src/gcode/feature/trinamic/M122.cpp
@@ -0,0 +1,60 @@
+/**
+ * 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 <https://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"
+
+/**
+ * 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 (parser.boolval('I')) restore_stepper_drivers();
+
+ #if ENABLED(TMC_DEBUG)
+ #if ENABLED(MONITOR_DRIVER_STATUS)
+ uint16_t interval = MONITOR_DRIVER_STATUS_INTERVAL_MS;
+ if (parser.seen('S') && !parser.value_bool()) interval = 0;
+ if (parser.seenval('P')) NOMORE(interval, parser.value_ushort());
+ tmc_set_report_interval(interval);
+ #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
diff --git a/Marlin/src/gcode/feature/trinamic/M569.cpp b/Marlin/src/gcode/feature/trinamic/M569.cpp
new file mode 100644
index 0000000..6b379f1
--- /dev/null
+++ b/Marlin/src/gcode/feature/trinamic/M569.cpp
@@ -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 <https://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() ? 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
diff --git a/Marlin/src/gcode/feature/trinamic/M906.cpp b/Marlin/src/gcode/feature/trinamic/M906.cpp
new file mode 100644
index 0000000..e834ebd
--- /dev/null
+++ b/Marlin/src/gcode/feature/trinamic/M906.cpp
@@ -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 <https://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
diff --git a/Marlin/src/gcode/feature/trinamic/M911-M914.cpp b/Marlin/src/gcode/feature/trinamic/M911-M914.cpp
new file mode 100644
index 0000000..8c840db
--- /dev/null
+++ b/Marlin/src/gcode/feature/trinamic/M911-M914.cpp
@@ -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 <https://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