Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MarlinSerial emergency-command parser (with M108) #4226

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Marlin/Conditionals.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@
#define HardwareSerial_h // trick to disable the standard HWserial
#endif

#if ENABLED(EMERGENCY_PARSER)
#define EMERGENCY_PARSER_CAPABILITIES " EMERGENCY_CODES:M108,M112,M410"
#else
#define EMERGENCY_PARSER_CAPABILITIES ""
#endif

#include "Arduino.h"

/**
Expand Down
6 changes: 6 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ const unsigned int dropsegments = 5; //everything with less than this number of
#define MAX_CMD_SIZE 96
#define BUFSIZE 4

// Enable an emergency-command parser to intercept certain commands as they
// enter the serial receive buffer, so they cannot be blocked.
// Currently handles M108, M112, M410
// Does not work on boards using AT90USB (USBCON) processors!
//#define EMERGENCY_PARSER

// Bad Serial-connections can miss a received command by sending an 'ok'
// Therefore some clients abort after 30 seconds in a timeout.
// Some other clients start sending commands while receiving a 'wait'.
Expand Down
5 changes: 2 additions & 3 deletions Marlin/Marlin.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,7 @@ void ok_to_send();
void reset_bed_level();
void kill(const char*);

#if DISABLED(DELTA) && DISABLED(SCARA)
void set_current_position_from_planner();
#endif
void quickstop_stepper();

#if ENABLED(FILAMENT_RUNOUT_SENSOR)
void handle_filament_runout();
Expand Down Expand Up @@ -288,6 +286,7 @@ extern float sw_endstop_min[3]; // axis[n].sw_endstop_min
extern float sw_endstop_max[3]; // axis[n].sw_endstop_max
extern bool axis_known_position[3]; // axis[n].is_known
extern bool axis_homed[3]; // axis[n].is_homed
extern volatile bool wait_for_heatup;

// GCode support for external objects
bool code_seen(char);
Expand Down
106 changes: 106 additions & 0 deletions Marlin/MarlinSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "Marlin.h"
#include "MarlinSerial.h"
#include "stepper.h"

#ifndef USBCON
// this next line disables the entire HardwareSerial.cpp,
Expand All @@ -54,6 +55,10 @@ FORCE_INLINE void store_char(unsigned char c) {
rx_buffer.head = i;
}
CRITICAL_SECTION_END;

#if ENABLED(EMERGENCY_PARSER)
emergency_parser(c);
#endif
}


Expand Down Expand Up @@ -310,3 +315,104 @@ MarlinSerial customizedSerial;
#if defined(USBCON) && ENABLED(BLUETOOTH)
HardwareSerial bluetoothSerial;
#endif

#if ENABLED(EMERGENCY_PARSER)

// Currently looking for: M108, M112, M410
// If you alter the parser please don't forget to update the capabilities in Conditionals.h

void emergency_parser(unsigned char c) {

enum e_parser_state {
state_RESET,
state_N,
state_M,
state_M1,
state_M10,
state_M108,
state_M11,
state_M112,
state_M4,
state_M41,
state_M410,
state_IGNORE // to '\n'
};

static e_parser_state state = state_RESET;

switch (state) {
case state_RESET:
switch (c) {
case ' ': break;
case 'N': state = state_N; break;
case 'M': state = state_M; break;
default: state = state_IGNORE;
}
break;
Copy link
Member

@AnHardt AnHardt Jul 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

     case state_RESET:
        switch (c) {
          case 'M': state = state_M;      break;
          case 'G': 
          case 'T': state = state_IGNORE;      break;
          default: state = state_RESET;
        }
        break;

to omit state_N

EDITED 2 times

Copy link
Member Author

@thinkyhead thinkyhead Jul 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only ignore G and T? What about a command (as artificial as this may be) like "Hey there M108!". Although it doesn't start with G or T it would still proceed to state_M108. We know N and are ok beforeM. While everything else can go tostate_IGNORE. This should result in our being instate_IGNORE a lot more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm attempted to say: "Who sends crap - gets crap."

Copy link
Member Author

@thinkyhead thinkyhead Jul 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


case state_N:
switch (c) {
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9': case '-': case ' ': break;
case 'M': state = state_M; break;
default: state = state_IGNORE;
}
break;

case state_M:
switch (c) {
case ' ': break;
case '1': state = state_M1; break;
case '4': state = state_M4; break;
default: state = state_IGNORE;
}
break;

case state_M1:
switch (c) {
case '0': state = state_M10; break;
case '1': state = state_M11; break;
default: state = state_IGNORE;
}
break;

case state_M10:
state = (c == '8') ? state_M108 : state_IGNORE;
break;

case state_M11:
state = (c == '2') ? state_M112 : state_IGNORE;
break;

case state_M4:
state = (c == '1') ? state_M41 : state_IGNORE;
break;

case state_M41:
state = (c == '0') ? state_M410 : state_IGNORE;
break;

case state_IGNORE:
if (c == '\n') state = state_RESET;
break;

default:
if (c == '\n') {
switch (state) {
case state_M108:
wait_for_heatup = false;
break;
case state_M112:
kill(PSTR(MSG_KILLED));
break;
case state_M410:
quickstop_stepper();
break;
}
state = state_RESET;
}
}
}
#endif
9 changes: 9 additions & 0 deletions Marlin/MarlinSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ struct ring_buffer {
extern ring_buffer rx_buffer;
#endif

#if ENABLED(EMERGENCY_PARSER)
#include "language.h"
void emergency_parser(unsigned char c);
#endif

class MarlinSerial { //: public Stream

public:
Expand Down Expand Up @@ -141,6 +146,10 @@ class MarlinSerial { //: public Stream
rx_buffer.head = i;
}
CRITICAL_SECTION_END;

#if ENABLED(EMERGENCY_PARSER)
emergency_parser(c);
#endif
}
}

Expand Down
90 changes: 49 additions & 41 deletions Marlin/Marlin_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
* M105 - Read current temp
* M106 - Fan on
* M107 - Fan off
* M108 - Cancel heatup and wait for the hotend and bed, this G-code is asynchronously handled in the get_serial_commands() parser
* M108 - Stop the waiting for heaters in M109, M190, M303. Does not affect the target temperature.
* M109 - Sxxx Wait for extruder current temp to reach target temp. Waits only when heating
* Rxxx Wait for extruder current temp to reach target temp. Waits when heating and cooling
* IF AUTOTEMP is enabled, S<mintemp> B<maxtemp> F<factor>. Exit autotemp by any M109 without F
Expand Down Expand Up @@ -332,7 +332,7 @@ uint8_t active_extruder = 0;
// Relative Mode. Enable with G91, disable with G90.
static bool relative_mode = false;

bool wait_for_heatup = true;
volatile bool wait_for_heatup = true;

const char errormagic[] PROGMEM = "Error:";
const char echomagic[] PROGMEM = "echo:";
Expand Down Expand Up @@ -1105,9 +1105,12 @@ inline void get_serial_commands() {
}
}

// If command was e-stop process now
if (strcmp(command, "M112") == 0) kill(PSTR(MSG_KILLED));
if (strcmp(command, "M108") == 0) wait_for_heatup = false;
#if DISABLED(EMERGENCY_PARSER)
// If command was e-stop process now
if (strcmp(command, "M108") == 0) wait_for_heatup = false;
if (strcmp(command, "M112") == 0) kill(PSTR(MSG_KILLED));
if (strcmp(command, "M410") == 0) { quickstop_stepper(); }
#endif

#if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
last_command_time = ms;
Expand Down Expand Up @@ -4533,10 +4536,29 @@ inline void gcode_M105() {

#endif // FAN_COUNT > 0

/**
* M108: Cancel heatup and wait for the hotend and bed, this G-code is asynchronously handled in the get_serial_commands() parser
*/
inline void gcode_M108() { wait_for_heatup = false; }
#if DISABLED(EMERGENCY_PARSER)

/**
* M108: Stop the waiting for heaters in M109, M190, M303. Does not affect the target temperature.
*/
inline void gcode_M108() { wait_for_heatup = false; }


/**
* M112: Emergency Stop
*/
inline void gcode_M112() { kill(PSTR(MSG_KILLED)); }


/**
* M410: Quickstop - Abort all planned moves
*
* This will stop the carriages mid-move, so most likely they
* will be out of sync with the stepper position after this.
*/
inline void gcode_M410() { quickstop_stepper(); }

#endif

/**
* M109: Sxxx Wait for extruder(s) to reach temperature. Waits only when heating.
Expand Down Expand Up @@ -4808,11 +4830,6 @@ inline void gcode_M111() {
SERIAL_EOL;
}

/**
* M112: Emergency Stop
*/
inline void gcode_M112() { kill(PSTR(MSG_KILLED)); }

#if ENABLED(HOST_KEEPALIVE_FEATURE)

/**
Expand Down Expand Up @@ -5968,8 +5985,9 @@ inline void gcode_M400() { stepper.synchronize(); }

#endif // FILAMENT_WIDTH_SENSOR

#if DISABLED(DELTA) && DISABLED(SCARA)
void set_current_position_from_planner() {
void quickstop_stepper() {
stepper.quick_stop();
#if DISABLED(DELTA) && DISABLED(SCARA)
stepper.synchronize();
#if ENABLED(AUTO_BED_LEVELING_FEATURE)
vector_3 pos = planner.adjusted_position(); // values directly from steppers...
Expand All @@ -5982,23 +6000,9 @@ inline void gcode_M400() { stepper.synchronize(); }
current_position[Z_AXIS] = stepper.get_axis_position_mm(Z_AXIS);
#endif
sync_plan_position(); // ...re-apply to planner position
}
#endif

/**
* M410: Quickstop - Abort all planned moves
*
* This will stop the carriages mid-move, so most likely they
* will be out of sync with the stepper position after this.
*/
inline void gcode_M410() {
stepper.quick_stop();
#if DISABLED(DELTA) && DISABLED(SCARA)
set_current_position_from_planner();
#endif
}


#if ENABLED(MESH_BED_LEVELING)

/**
Expand Down Expand Up @@ -6953,9 +6957,21 @@ void process_next_command() {
gcode_M111();
break;

case 112: // M112: Emergency Stop
gcode_M112();
break;
#if DISABLED(EMERGENCY_PARSER)

case 108: // M108: Cancel Waiting
gcode_M108();
break;

case 112: // M112: Emergency Stop
gcode_M112();
break;

case 410: // M410 quickstop - Abort all the planned moves.
gcode_M410();
break;

#endif

#if ENABLED(HOST_KEEPALIVE_FEATURE)

Expand All @@ -6974,10 +6990,6 @@ void process_next_command() {
KEEPALIVE_STATE(NOT_BUSY);
return; // "ok" already printed

case 108:
gcode_M108();
break;

case 109: // M109: Wait for temperature
gcode_M109();
break;
Expand Down Expand Up @@ -7261,10 +7273,6 @@ void process_next_command() {
break;
#endif // ENABLED(FILAMENT_WIDTH_SENSOR)

case 410: // M410 quickstop - Abort all the planned moves.
gcode_M410();
break;

#if ENABLED(MESH_BED_LEVELING)
case 420: // M420 Enable/Disable Mesh Bed Leveling
gcode_M420();
Expand Down
7 changes: 7 additions & 0 deletions Marlin/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,13 @@
#endif

/**
* emergency-command parser
*/
#if ENABLED(EMERGENCY_PARSER) && ENABLED(USBCON)
#error "EMERGENCY_PARSER does not work on boards with AT90USB processors (USBCON)."
#endif

/**
* Warnings for old configurations
*/
#if WATCH_TEMP_PERIOD > 500
Expand Down
5 changes: 1 addition & 4 deletions Marlin/endstops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,7 @@ void Endstops::report_state() {
if (stepper.abort_on_endstop_hit) {
card.sdprinting = false;
card.closefile();
stepper.quick_stop();
#if DISABLED(DELTA) && DISABLED(SCARA)
set_current_position_from_planner();
#endif
quickstop_stepper();
thermalManager.disable_all_heaters(); // switch off all heaters.
}
#endif
Expand Down
6 changes: 6 additions & 0 deletions Marlin/example_configurations/Cartesio/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ const unsigned int dropsegments = 5; //everything with less than this number of
#define MAX_CMD_SIZE 96
#define BUFSIZE 4

// Enable an emergency-command parser to intercept certain commands as they
// enter the serial receive buffer, so they cannot be blocked.
// Currently handles M108, M112, M410
// Does not work on boards using AT90USB (USBCON) processors!
//#define EMERGENCY_PARSER

// Bad Serial-connections can miss a received command by sending an 'ok'
// Therefore some clients abort after 30 seconds in a timeout.
// Some other clients start sending commands while receiving a 'wait'.
Expand Down
Loading