diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 374a420539..8c181207a9 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -1518,7 +1518,9 @@ // #define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages #define DEFAULT_KEEPALIVE_INTERVAL 2 // Number of seconds between "busy" messages. Set with M113. +#define KEEPALIVE_INTERVAL_DIVIDER 3 // Divide the KEEPALIVE_INTERVAL for faster reporting #define BUSY_WHILE_HEATING // Some hosts require "busy" messages even during heating +#define FULL_REPORT_TO_HOST_FEATURE // Enable this to send Machine status reports while moving and status reports GRBL style // // G20/G21 Inch mode support diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index f38ba35dd5..c21a2cd319 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -240,6 +240,8 @@ PGMSTR(SP_X_LBL, " X:"); PGMSTR(SP_Y_LBL, " Y:"); PGMSTR(SP_Z_LBL, " Z:"); PGMST MarlinState marlin_state = MF_INITIALIZING; +M_StateEnum M_State_grbl = M_INIT; + // For M109 and M190, this flag may be cleared (by M108) to exit the wait loop bool wait_for_heatup = true; @@ -345,6 +347,16 @@ void quickstop_stepper() { sync_plan_position(); } +void quickpause_stepper() { + planner.quick_pause(); + //planner.synchronize(); +} + +void quickresume_stepper() { + planner.quick_resume(); + //planner.synchronize(); +} + void enable_e_steppers() { #define _ENA_E(N) ENABLE_AXIS_E##N(); REPEAT(E_STEPPERS, _ENA_E) diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h index 908636e967..8dd1f5f75b 100644 --- a/Marlin/src/MarlinCore.h +++ b/Marlin/src/MarlinCore.h @@ -60,6 +60,8 @@ void kill(PGM_P const lcd_error=nullptr, PGM_P const lcd_component=nullptr, cons void minkill(const bool steppers_off=false); void quickstop_stepper(); +void quickpause_stepper(); +void quickresume_stepper(); // Global State of the firmware enum MarlinState : uint8_t { @@ -76,6 +78,27 @@ extern MarlinState marlin_state; inline bool IsRunning() { return marlin_state == MF_RUNNING; } inline bool IsStopped() { return marlin_state != MF_RUNNING; } +// +// Enumerated Status indices +// Coding as GRBL or TinyG for Machine states +// +enum M_StateEnum : uint8_t { + M_INIT = 0, // 0 machine is initializing + M_RESET, // 1 machine is ready for use + M_ALARM, // 2 machine is in alarm state (soft shut down) + M_IDLE, // 3 program stop or no more blocks (M0, M1, M60) + M_END, // 4 program end via M2, M30 + M_RUNNING, // 5 motion is running + M_HOLD, // 6 motion is holding + M_PROBE, // 7 probe cycle active + M_CYCLING, // 8 machine is running (cycling) + M_HOMING, // 9 machine is homing + M_JOGGING, // 10 machine is jogging + M_ERROR // 11 machine is in hard alarm state (shut down) +}; + +extern M_StateEnum M_State_grbl; + bool printingIsActive(); bool printingIsPaused(); void startOrResumeJob(); diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index 0bb0253149..e234d415cc 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -34,30 +34,29 @@ // External references extern bool wait_for_user, wait_for_heatup; void quickstop_stepper(); +void quickpause_stepper(); +void quickresume_stepper(); +void report_current_position_moving(); class EmergencyParser { public: // Currently looking for: M108, M112, M410, M876 + // S000 STATE , P000 for Pause, R000 for resume enum State : char { EP_RESET, EP_N, EP_M, EP_M1, - EP_M10, - EP_M108, - EP_M11, - EP_M112, - EP_M4, - EP_M41, - EP_M410, + EP_M10, EP_M108, + EP_M11, EP_M112, + EP_M4, EP_M41, EP_M410, + EP_S, EP_S0, EP_S00, EP_grblSTATUS, + EP_R, EP_R0, EP_R00, EP_grblRESUME, + EP_P, EP_P0, EP_P00, EP_grblPAUSE, #if ENABLED(HOST_PROMPT_SUPPORT) - EP_M8, - EP_M87, - EP_M876, - EP_M876S, - EP_M876SN, + EP_M8, EP_M87, EP_M876, EP_M876S, EP_M876SN, #endif EP_IGNORE // to '\n' }; @@ -72,7 +71,6 @@ public: EmergencyParser() { enable(); } FORCE_INLINE static void enable() { enabled = true; } - FORCE_INLINE static void disable() { enabled = false; } FORCE_INLINE static void update(State &state, const uint8_t c) { @@ -81,9 +79,12 @@ public: case EP_RESET: switch (c) { case ' ': case '\n': case '\r': break; - case 'N': state = EP_N; break; - case 'M': state = EP_M; break; - default: state = EP_IGNORE; + case 'N': state = EP_N; break; + case 'M': state = EP_M; break; + case 'S': state = EP_S; break; + case 'P': state = EP_P; break; + case 'R': state = EP_R; break; + default: state = EP_IGNORE; } break; @@ -92,10 +93,22 @@ public: case '0' ... '9': case '-': case ' ': break; case 'M': state = EP_M; break; - default: state = EP_IGNORE; + default: state = EP_IGNORE; } break; + case EP_S: state = (c == '0' ? EP_S0 : EP_IGNORE); break; + case EP_S0: state = (c == '0' ? EP_S00 : EP_IGNORE); break; + case EP_S00: state = (c == '0' ? EP_grblSTATUS : EP_IGNORE); break; + + case EP_R: state = (c == '0' ? EP_R0 : EP_IGNORE); break; + case EP_R0: state = (c == '0' ? EP_R00 : EP_IGNORE); break; + case EP_R00: state = (c == '0' ? EP_grblRESUME : EP_IGNORE); break; + + case EP_P: state = (c == '0' ? EP_P0 : EP_IGNORE); break; + case EP_P0: state = (c == '0' ? EP_P00 : EP_IGNORE); break; + case EP_P00: state = (c == '0' ? EP_grblPAUSE : EP_IGNORE); break; + case EP_M: switch (c) { case ' ': break; @@ -116,39 +129,24 @@ public: } break; - case EP_M10: - state = (c == '8') ? EP_M108 : EP_IGNORE; - break; - - case EP_M11: - state = (c == '2') ? EP_M112 : EP_IGNORE; - break; - - case EP_M4: - state = (c == '1') ? EP_M41 : EP_IGNORE; - break; - - case EP_M41: - state = (c == '0') ? EP_M410 : EP_IGNORE; - break; + case EP_M10: state = (c == '8') ? EP_M108 : EP_IGNORE; break; + case EP_M11: state = (c == '2') ? EP_M112 : EP_IGNORE; break; + case EP_M4: state = (c == '1') ? EP_M41 : EP_IGNORE; break; + case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break; #if ENABLED(HOST_PROMPT_SUPPORT) - case EP_M8: - state = (c == '7') ? EP_M87 : EP_IGNORE; - break; - case EP_M87: - state = (c == '6') ? EP_M876 : EP_IGNORE; - break; - - case EP_M876: - switch (c) { - case ' ': break; - case 'S': state = EP_M876S; break; - default: state = EP_IGNORE; break; - } - break; + case EP_M8: state = (c == '7') ? EP_M87 : EP_IGNORE; break; + case EP_M87: state = (c == '6') ? EP_M876 : EP_IGNORE; break; + case EP_M876: + switch (c) { + case ' ': break; + case 'S': state = EP_M876S; break; + default: state = EP_IGNORE; break; + } + break; + case EP_M876S: switch (c) { case ' ': break; @@ -160,6 +158,7 @@ public: break; #endif + case EP_IGNORE: if (ISEOL(c)) state = EP_RESET; break; @@ -173,6 +172,9 @@ public: #if ENABLED(HOST_PROMPT_SUPPORT) case EP_M876SN: host_response_handler(M876_reason); break; #endif + case EP_grblSTATUS: report_current_position_moving(); break; + case EP_grblPAUSE: quickpause_stepper(); break; + case EP_grblRESUME: quickresume_stepper(); break; default: break; } state = EP_RESET; diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index f387dd9bd8..b92ffed535 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -163,6 +163,9 @@ */ G29_TYPE GcodeSuite::G29() { + M_State_grbl = M_PROBE; + report_current_grblstate_moving(); + reset_stepper_timeout(); const bool seenQ = EITHER(DEBUG_LEVELING_FEATURE, PROBE_MANUALLY) && parser.seen('Q'); @@ -885,6 +888,9 @@ G29_TYPE GcodeSuite::G29() { report_current_position(); + M_State_grbl = M_IDLE; + report_current_grblstate_moving(); + G29_RETURN(isnan(measured_z)); } diff --git a/Marlin/src/gcode/bedlevel/mbl/G29.cpp b/Marlin/src/gcode/bedlevel/mbl/G29.cpp index d5cc8a4fcb..e8c7882c93 100644 --- a/Marlin/src/gcode/bedlevel/mbl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/mbl/G29.cpp @@ -60,6 +60,9 @@ inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM(" */ void GcodeSuite::G29() { + M_State_grbl = M_PROBE; + report_current_grblstate_moving(); + static int mbl_probe_index = -1; MeshLevelingState state = (MeshLevelingState)parser.byteval('S', (int8_t)MeshReport); @@ -188,6 +191,9 @@ void GcodeSuite::G29() { } report_current_position(); + + M_State_grbl = M_IDLE; + report_current_grblstate_moving(); } #endif // MESH_BED_LEVELING diff --git a/Marlin/src/gcode/bedlevel/ubl/G29.cpp b/Marlin/src/gcode/bedlevel/ubl/G29.cpp index 2ef3ab4cec..0b133bad88 100644 --- a/Marlin/src/gcode/bedlevel/ubl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/ubl/G29.cpp @@ -31,6 +31,18 @@ #include "../../gcode.h" #include "../../../feature/bedlevel/bedlevel.h" -void GcodeSuite::G29() { ubl.G29(); } +#include "../../../MarlinCore.h" // for M_State_grbl + +void GcodeSuite::G29() { + + M_State_grbl = M_PROBE; + report_current_grblstate_moving(); + + ubl.G29(); + + M_State_grbl = M_IDLE; + report_current_grblstate_moving(); + +} #endif // AUTO_BED_LEVELING_UBL diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index ef39290d2d..016c95e7e3 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -209,6 +209,9 @@ void GcodeSuite::G28() { TERN_(DWIN_CREALITY_LCD, HMI_flag.home_flag = true); + M_State_grbl = M_HOMING; + report_current_grblstate_moving(); + #if ENABLED(DUAL_X_CARRIAGE) bool IDEX_saved_duplication_state = extruder_duplication_enabled; DualXMode IDEX_saved_mode = dual_x_carriage_mode; @@ -463,6 +466,9 @@ void GcodeSuite::G28() { if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS))) SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP); + M_State_grbl = M_IDLE; + report_current_grblstate_moving(); + #if HAS_L64XX // Set L6470 absolute position registers to counts // constexpr *might* move this to PROGMEM. diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp index 77cc45771c..6f132f73a6 100644 --- a/Marlin/src/gcode/calibrate/G33.cpp +++ b/Marlin/src/gcode/calibrate/G33.cpp @@ -385,6 +385,9 @@ static float auto_tune_a() { */ void GcodeSuite::G33() { + M_State_grbl = M_PROBE; + report_current_grblstate_moving(); + const int8_t probe_points = parser.intval('P', DELTA_CALIBRATION_DEFAULT_POINTS); if (!WITHIN(probe_points, 0, 10)) { SERIAL_ECHOLNPGM("?(P)oints implausible (0-10)."); @@ -643,6 +646,9 @@ void GcodeSuite::G33() { while (((zero_std_dev < test_precision && iterations < 31) || iterations <= force_iterations) && zero_std_dev > calibration_precision); ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index)); + + M_State_grbl = M_IDLE; + report_current_grblstate_moving(); } #endif // DELTA_AUTO_CALIBRATION diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 1d6bd94231..f3a06bc982 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -61,7 +61,7 @@ GcodeSuite gcode; #include "../feature/password/password.h" #endif -#include "../MarlinCore.h" // for idle() +#include "../MarlinCore.h" // for idle(), M_State_grbl // Inactivity shutdown millis_t GcodeSuite::previous_move_ms = 0, @@ -82,6 +82,10 @@ uint8_t GcodeSuite::axis_relative = ( #endif #if ENABLED(HOST_KEEPALIVE_FEATURE) + #if !KEEPALIVE_INTERVAL_DIVIDER + #undef KEEPALIVE_INTERVAL_DIVIDER + #define KEEPALIVE_INTERVAL_DIVIDER 1 + #endif GcodeSuite::MarlinBusyState GcodeSuite::busy_state = NOT_BUSY; uint8_t GcodeSuite::host_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL; #endif @@ -256,8 +260,15 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { } #endif - // Handle a known G, M, or T + // Handle a known G, M, or T (S,P & R from emergency parser) switch (parser.command_letter) { + #if ENABLED(FULL_REPORT_TO_HOST_FEATURE) + case 'S': case 'P': case 'R': switch (parser.codenum) { + case 0: if(parser.numchars==3) break; + default: parser.unknown_command_warning(); break; + } + break; + #endif case 'G': switch (parser.codenum) { case 0: case 1: // G0: Fast Move, G1: Linear Move @@ -1028,18 +1039,23 @@ void GcodeSuite::process_subcommands_now(char * gcode) { case IN_HANDLER: case IN_PROCESS: SERIAL_ECHO_MSG(STR_BUSY_PROCESSING); + TERN_(FULL_REPORT_TO_HOST_FEATURE, report_current_position_moving()); break; case PAUSED_FOR_USER: SERIAL_ECHO_MSG(STR_BUSY_PAUSED_FOR_USER); + M_State_grbl = M_HOLD; + report_current_grblstate_moving(); break; case PAUSED_FOR_INPUT: SERIAL_ECHO_MSG(STR_BUSY_PAUSED_FOR_INPUT); + M_State_grbl = M_HOLD; + report_current_grblstate_moving(); break; default: break; } } - next_busy_signal_ms = ms + SEC_TO_MS(host_keepalive_interval); + next_busy_signal_ms = ms + SEC_TO_MS(host_keepalive_interval) / (KEEPALIVE_INTERVAL_DIVIDER); } #endif // HOST_KEEPALIVE_FEATURE diff --git a/Marlin/src/gcode/host/M114.cpp b/Marlin/src/gcode/host/M114.cpp index 85a38f6462..799462136c 100644 --- a/Marlin/src/gcode/host/M114.cpp +++ b/Marlin/src/gcode/host/M114.cpp @@ -181,6 +181,7 @@ const xyze_float_t diff = from_steppers - leveled; SERIAL_ECHOPGM("Diff: "); report_xyze(diff); + report_current_grblstate_moving(); } #endif // M114_DETAIL @@ -216,4 +217,5 @@ void GcodeSuite::M114() { TERN_(M114_LEGACY, planner.synchronize()); report_current_position_projected(); + report_current_grblstate_moving(); } diff --git a/Marlin/src/gcode/motion/G0_G1.cpp b/Marlin/src/gcode/motion/G0_G1.cpp index 9ac49bd93c..a2cae3cce4 100644 --- a/Marlin/src/gcode/motion/G0_G1.cpp +++ b/Marlin/src/gcode/motion/G0_G1.cpp @@ -54,6 +54,8 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) { | (parser.seen('Z') ? _BV(Z_AXIS) : 0) ) #endif ) { + M_State_grbl = M_RUNNING; + report_current_grblstate_moving(); #ifdef G0_FEEDRATE feedRate_t old_feedrate; @@ -117,5 +119,10 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) { SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP); } #endif + + #if ENABLED(FULL_REPORT_TO_HOST_FEATURE) + TERN_(NANODLP_Z_SYNC, M_State_grbl = M_IDLE); + report_current_position_moving(); + #endif } } diff --git a/Marlin/src/gcode/motion/G2_G3.cpp b/Marlin/src/gcode/motion/G2_G3.cpp index c713877a0e..b7cac17d69 100644 --- a/Marlin/src/gcode/motion/G2_G3.cpp +++ b/Marlin/src/gcode/motion/G2_G3.cpp @@ -292,6 +292,8 @@ void plan_arc( */ void GcodeSuite::G2_G3(const bool clockwise) { if (MOTION_CONDITIONS) { + M_State_grbl = M_RUNNING; + report_current_grblstate_moving(); #if ENABLED(SF_ARC_FIX) const bool relative_mode_backup = relative_mode; @@ -351,6 +353,9 @@ void GcodeSuite::G2_G3(const bool clockwise) { } else SERIAL_ERROR_MSG(STR_ERR_ARC_ARGS); + + M_State_grbl = M_IDLE; + TERN_(FULL_REPORT_TO_HOST_FEATURE, report_current_position_moving()); } } diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp index bba64dbbc4..59e33945ed 100644 --- a/Marlin/src/gcode/parser.cpp +++ b/Marlin/src/gcode/parser.cpp @@ -46,6 +46,7 @@ char *GCodeParser::command_ptr, *GCodeParser::value_ptr; char GCodeParser::command_letter; int GCodeParser::codenum; +int GCodeParser::numchars; #if ENABLED(USE_GCODE_SUBCODES) uint8_t GCodeParser::subcode; @@ -147,10 +148,34 @@ void GCodeParser::parse(char *p) { #define SIGNED_CODENUM 1 #endif + // Bail if the letter is not S, P, or R + #if ENABLED(FULL_REPORT_TO_HOST_FEATURE) + switch (letter) { + case 'S': case 'P': case 'R': + // Bail if there's no command code number + if (!NUMERIC(*p)) break; + // Save the command letter at this point + // A '?' signifies an unknown command + command_letter = letter; + // Get the code number - integer digits only + codenum = 0; + numchars=0; + while (NUMERIC(*p)) { + codenum += *p++ - '0'; + codenum *= 10; + numchars++; + } + return; + + default: break; + } + #endif + + // Bail if the letter is not G, M, or T // (or a valid parameter for the current motion mode) - switch (letter) { + switch (letter) { case 'G': case 'M': case 'T': TERN_(MARLIN_DEV_MODE, case 'D':) // Skip spaces to get the numeric part while (*p == ' ') p++; diff --git a/Marlin/src/gcode/parser.h b/Marlin/src/gcode/parser.h index 17fb084388..cb50b165e1 100644 --- a/Marlin/src/gcode/parser.h +++ b/Marlin/src/gcode/parser.h @@ -85,6 +85,7 @@ public: *string_arg, // string of command line command_letter; // G, M, or T static int codenum; // 123 + static int numchars; // 123 #if ENABLED(USE_GCODE_SUBCODES) static uint8_t subcode; // .1 #endif diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 03c8ddc462..5024d12103 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -245,6 +245,48 @@ void report_current_position_projected() { stepper.report_a_position(planner.position); } +/** + * Output the current position (processed) to serial while moving + */ +void report_current_position_moving() { + + get_cartesian_from_steppers(); + const xyz_pos_t lpos = cartes.asLogical(); + SERIAL_ECHOPAIR("X:", lpos.x, " Y:", lpos.y, " Z:", lpos.z, " E:", current_position.e); + + stepper.report_positions(); + #if IS_SCARA + scara_report_positions(); + #endif + report_current_grblstate_moving(); +} + +/** + * Output the current grbl compatible state to serial while moving + */ +void report_current_grblstate_moving() { + #if ENABLED(FULL_REPORT_TO_HOST_FEATURE) + SERIAL_ECHOPAIR("S_XYZ:", M_State_grbl); + SERIAL_EOL(); + #endif +} + +/** + * grbl compatible state to marlin_state + */ +void set_M_state_from_marlin_state() { + switch (marlin_state) { + case MF_INITIALIZING: M_State_grbl = M_INIT; break; + case MF_SD_COMPLETE: M_State_grbl = M_ALARM; break; + case MF_WAITING: M_State_grbl = M_IDLE; break; + case MF_STOPPED: M_State_grbl = M_END; break; + case MF_RUNNING: M_State_grbl = M_RUNNING; break; + case MF_PAUSED: M_State_grbl = M_HOLD; break; + case MF_KILLED: M_State_grbl = M_ERROR; break; + default: M_State_grbl = M_IDLE; + } +} + /** * sync_plan_position * diff --git a/Marlin/src/module/motion.h b/Marlin/src/module/motion.h index abc59f92b8..7bbc01826d 100644 --- a/Marlin/src/module/motion.h +++ b/Marlin/src/module/motion.h @@ -201,6 +201,9 @@ inline float home_bump_mm(const AxisEnum axis) { void report_real_position(); void report_current_position(); void report_current_position_projected(); +void report_current_position_moving(); +void report_current_grblstate_moving(); +void set_M_state_from_marlin_state(); void get_cartesian_from_steppers(); void set_current_from_steppers_for_axis(const AxisEnum axis); diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 74535df1f9..21d8194e3b 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1592,6 +1592,23 @@ void Planner::quick_stop() { stepper.quick_stop(); } +void Planner::quick_pause() { + // Suspend until quick_resume is called + // Don't empty buffers or queues + if (stepper.suspend()) { + M_State_grbl = M_HOLD; + report_current_grblstate_moving(); + } +} + +void Planner::quick_resume() { + // Resume if suspended + set_M_state_from_marlin_state(); + report_current_grblstate_moving(); + stepper.wake_up(); +} + + void Planner::endstop_triggered(const AxisEnum axis) { // Record stepper position and discard the current block stepper.endstop_triggered(axis); diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index c4e11490b1..a72faf6c3b 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -829,6 +829,11 @@ class Planner { // a Full Shutdown is required, or when endstops are hit) static void quick_stop(); + // Force a quick pause of the machine (for example, when a pause + // is required in the middle of move) + static void quick_pause(); + static void quick_resume(); + // Called when an endstop is triggered. Causes the machine to stop inmediately static void endstop_triggered(const AxisEnum axis);