diff --git a/config.h b/config.h
index 55114bf..ccba270 100644
--- a/config.h
+++ b/config.h
@@ -29,7 +29,7 @@
#define config_h
// Default settings. Used when resetting EEPROM. Change to desired name in defaults.h
-#define DEFAULTS_SHERLINE_5400
+#define DEFAULTS_GENERIC
// Serial baud rate
#define BAUD_RATE 115200
diff --git a/coolant_control.c b/coolant_control.c
index a3f2554..9d66c50 100644
--- a/coolant_control.c
+++ b/coolant_control.c
@@ -21,6 +21,7 @@
#include "system.h"
#include "coolant_control.h"
#include "protocol.h"
+#include "gcode.h"
void coolant_init()
@@ -44,6 +45,8 @@ void coolant_stop()
void coolant_run(uint8_t mode)
{
+ if (sys.state != STATE_CHECK_MODE) { return; }
+
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
if (mode == COOLANT_FLOOD_ENABLE) {
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
diff --git a/coolant_control.h b/coolant_control.h
index c19a673..83ce30b 100644
--- a/coolant_control.h
+++ b/coolant_control.h
@@ -22,11 +22,6 @@
#define coolant_control_h
-#define COOLANT_MIST_ENABLE 2
-#define COOLANT_FLOOD_ENABLE 1
-#define COOLANT_DISABLE 0 // Must be zero.
-
-
void coolant_init();
void coolant_stop();
void coolant_run(uint8_t mode);
diff --git a/defaults.h b/defaults.h
index 9699eba..f2726a7 100644
--- a/defaults.h
+++ b/defaults.h
@@ -42,7 +42,6 @@
#define DEFAULT_Y_MAX_TRAVEL 200.0 // mm
#define DEFAULT_Z_MAX_TRAVEL 200.0 // mm
#define DEFAULT_STEP_PULSE_MICROSECONDS 10
- #define DEFAULT_FEEDRATE 250.0 // mm/min
#define DEFAULT_STEPPING_INVERT_MASK 0
#define DEFAULT_DIRECTION_INVERT_MASK ((1<.
*/
-/* This code is inspired by the Arduino GCode Interpreter by Mike Ellery and the NIST RS274/NGC Interpreter
- by Kramer, Proctor and Messina. */
-
#include "system.h"
#include "settings.h"
#include "protocol.h"
@@ -29,34 +26,28 @@
#include "motion_control.h"
#include "spindle_control.h"
#include "coolant_control.h"
+#include "probe.h"
#include "report.h"
+#define MAX_LINE_NUMBER 99999
+
+#define AXIS_COMMAND_NONE 0
+#define AXIS_COMMAND_NON_MODAL 1
+#define AXIS_COMMAND_MOTION_MODE 2
+
// Declare gc extern struct
-parser_state_t gc;
+parser_state_t gc_state;
+parser_block_t gc_block;
-#define FAIL(status) gc.status_code = status;
-
-static uint8_t next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter);
-static void gc_convert_arc_radius_mode(float *target) __attribute__((noinline));
-
-
-static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
-{
- gc.plane_axis_0 = axis_0;
- gc.plane_axis_1 = axis_1;
- gc.plane_axis_2 = axis_2;
-}
+#define FAIL(status) return(status);
void gc_init()
{
- memset(&gc, 0, sizeof(gc));
- gc.feed_rate = settings.default_feed_rate;
- select_plane(X_AXIS, Y_AXIS, Z_AXIS);
- gc.absolute_mode = true;
+ memset(&gc_state, 0, sizeof(gc_state));
// Load default G54 coordinate system.
- if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) {
+ if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) {
report_status_message(STATUS_SETTING_READ_FAIL);
}
}
@@ -68,16 +59,19 @@ void gc_sync_position()
{
uint8_t i;
for (i=0; i 255, variable type must be changed to uint16_t.
- float inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified
- uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53}
- uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal)
-
- float target[N_AXIS];
- clear_vector(target); // XYZ(ABC) axes parameters.
+ while (line[char_counter] != 0) { // Loop until no more g-code words in line.
+
+ // Import the next g-code word, expecting a letter followed by a value. Otherwise, error out.
+ letter = line[char_counter];
+ if((letter < 'A') || (letter > 'Z')) { FAIL(STATUS_EXPECTED_COMMAND_LETTER); } // [Expected word letter]
+ char_counter++;
+ if (!read_float(line, &char_counter, &value)) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // [Expected word value]
- #ifdef USE_LINE_NUMBERS
- int32_t line_number = 0;
- #endif
- gc.arc_radius = 0;
- clear_vector(gc.arc_offset); // IJK Arc offsets are incremental. Value of zero indicates no change.
-
- gc.status_code = STATUS_OK;
-
- /* Pass 1: Commands and set all modes. Check for modal group violations.
- NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */
- uint8_t group_number = MODAL_GROUP_NONE;
- while(next_statement(&letter, &value, line, &char_counter)) {
+ // Convert values to smaller uint8 significand and mantissa values for parsing this word.
+ // NOTE: Mantissa is multiplied by 1000 to catch non-integer command values.
int_value = trunc(value);
+ mantissa = trunc(1000*(value - int_value)); // Compute mantissa for Gxx.x commands
+
+ // Check if the g-code word is supported or errors due to modal group violations or has
+ // been repeated in the g-code block. If ok, update the command or record its value.
switch(letter) {
+
+ /* 'G' and 'M' Command Words: Parse commands and check for modal group violations.
+ NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */
+
case 'G':
- // Set modal group values
+ // Determine 'G' command and its modal group
switch(int_value) {
- case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break;
- case 0: case 1: case 2: case 3: case 38: case 80: group_number = MODAL_GROUP_1; break;
- case 17: case 18: case 19: group_number = MODAL_GROUP_2; break;
- case 90: case 91: group_number = MODAL_GROUP_3; break;
- case 93: case 94: group_number = MODAL_GROUP_5; break;
- case 20: case 21: group_number = MODAL_GROUP_6; break;
- case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break;
- }
- // Set 'G' commands
- switch(int_value) {
- case 0: gc.motion_mode = MOTION_MODE_SEEK; break;
- case 1: gc.motion_mode = MOTION_MODE_LINEAR; break;
- case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break;
- case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break;
- case 4: non_modal_action = NON_MODAL_DWELL; break;
- case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break;
- case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break;
- case 18: select_plane(Z_AXIS, X_AXIS, Y_AXIS); break;
- case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break;
- case 20: gc.inches_mode = true; break;
- case 21: gc.inches_mode = false; break;
- case 28: case 30:
- int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1
+ case 10: case 28: case 30: case 92:
+ // Check for G10/28/30/92 being called with G0/1/2/3/38 on same block.
+ if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1
+ if (axis_explicit) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
+ axis_explicit = AXIS_COMMAND_NON_MODAL;
+ }
+ // No break. Continues to next line.
+ case 4: case 53:
+ word_bit = MODAL_GROUP_G0;
switch(int_value) {
- case 280: non_modal_action = NON_MODAL_GO_HOME_0; break;
- case 281: non_modal_action = NON_MODAL_SET_HOME_0; break;
- case 300: non_modal_action = NON_MODAL_GO_HOME_1; break;
- case 301: non_modal_action = NON_MODAL_SET_HOME_1; break;
- default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
+ case 4: gc_block.non_modal_command = NON_MODAL_DWELL; break; // G4
+ case 10: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_DATA; break; // G10
+ case 28:
+ switch(mantissa) {
+ case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_0; break; // G28
+ case 100: gc_block.non_modal_command = NON_MODAL_SET_HOME_0; break; // G28.1
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G28.x command]
+ }
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ break;
+ case 30:
+ switch(mantissa) {
+ case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_1; break; // G30
+ case 100: gc_block.non_modal_command = NON_MODAL_SET_HOME_1; break; // G30.1
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G30.x command]
+ }
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ break;
+ case 53: gc_block.non_modal_command = NON_MODAL_ABSOLUTE_OVERRIDE; break; // G53
+ case 92:
+ switch(mantissa) {
+ case 0: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_OFFSET; break; // G92
+ case 100: gc_block.non_modal_command = NON_MODAL_RESET_COORDINATE_OFFSET; break; // G92.1
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G92.x command]
+ }
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ break;
}
break;
- case 38:
- int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1
+ case 0: case 1: case 2: case 3: case 38:
+ // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block.
+ if (axis_explicit) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
+ axis_explicit = AXIS_COMMAND_MOTION_MODE;
+ // No break. Continues to next line.
+ case 80:
+ word_bit = MODAL_GROUP_G1;
switch(int_value) {
- case 382: gc.motion_mode = MOTION_MODE_PROBE; break;
- // case 383: gc.motion_mode = MOTION_MODE_PROBE_NO_ERROR; break; // Not supported.
- // case 384: // Not supported.
- // case 385: // Not supported.
- default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
- }
- break;
- case 53: absolute_override = true; break;
- case 54: case 55: case 56: case 57: case 58: case 59:
- gc.coord_select = int_value-54;
+ case 0: gc_block.modal.motion = MOTION_MODE_SEEK; break; // G0
+ case 1: gc_block.modal.motion = MOTION_MODE_LINEAR; break; // G1
+ case 2: gc_block.modal.motion = MOTION_MODE_CW_ARC; break; // G2
+ case 3: gc_block.modal.motion = MOTION_MODE_CCW_ARC; break; // G3
+ case 38:
+ switch(mantissa) {
+ case 200: gc_block.modal.motion = MOTION_MODE_PROBE; break; // G38.2
+ // NOTE: If G38.3+ are enabled, change mantissa variable type to uint16_t.
+ // case 300: gc_block.modal.motion = MOTION_MODE_PROBE_NO_ERROR; break; // G38.3 Not supported.
+ // case 400: // Not supported.
+ // case 500: // Not supported.
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command]
+ }
+ mantissa = 0; // Set to zero to indicate valid non-integer G command.
+ break;
+ case 80: gc_block.modal.motion = MOTION_MODE_NONE; break; // G80
+ }
break;
- case 80: gc.motion_mode = MOTION_MODE_CANCEL; break;
- case 90: gc.absolute_mode = true; break;
- case 91: gc.absolute_mode = false; break;
- case 92:
- int_value = trunc(10*value); // Multiply by 10 to pick up G92.1
+ case 17: case 18: case 19:
+ word_bit = MODAL_GROUP_G2;
switch(int_value) {
- case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break;
- case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break;
- default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
+ case 17: gc_block.modal.plane_select = PLANE_SELECT_XY; break;
+ case 18: gc_block.modal.plane_select = PLANE_SELECT_ZX; break;
+ case 19: gc_block.modal.plane_select = PLANE_SELECT_YZ; break;
}
break;
- case 93: gc.inverse_feed_rate_mode = true; break;
- case 94: gc.inverse_feed_rate_mode = false; break;
- default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
- }
- break;
+ case 90: case 91:
+ word_bit = MODAL_GROUP_G3;
+ if (int_value == 90) { gc_block.modal.distance = DISTANCE_MODE_ABSOLUTE; } // G90
+ else { gc_block.modal.distance = DISTANCE_MODE_INCREMENTAL; } // G91
+ break;
+ case 93: case 94:
+ word_bit = MODAL_GROUP_G5;
+ if (int_value == 93) { gc_block.modal.feed_rate = FEED_RATE_MODE_INVERSE_TIME; } // G93
+ else { gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; } // G94
+ break;
+ case 20: case 21:
+ word_bit = MODAL_GROUP_G6;
+ if (int_value == 20) { gc_block.modal.units = UNITS_MODE_INCHES; } // G20
+ else { gc_block.modal.units = UNITS_MODE_MM; } // G21
+ break;
+ case 54: case 55: case 56: case 57: case 58: case 59:
+ // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.)
+ word_bit = MODAL_GROUP_G12;
+ gc_block.modal.coord_select = int_value-54; // Shift to array indexing.
+ break;
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command]
+ }
+ if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command]
+ // Check for more than one command per modal group violations in the current block
+ // NOTE: Variable 'word_bit' is always assigned, if the command is valid.
+ if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
+ bit_true(command_words,bit(word_bit));
+ break;
+
case 'M':
- // Set modal group values
+
+ // Determine 'M' command and its modal group
+ if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands]
switch(int_value) {
- case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break;
- case 3: case 4: case 5: group_number = MODAL_GROUP_7; break;
- case 7: case 8: case 9: group_number = MODAL_GROUP_8; break;
- }
- // Set 'M' commands
- switch(int_value) {
- case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
- case 1: break; // Optional stop not supported. Ignore.
- case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
- case 3: gc.spindle_direction = SPINDLE_ENABLE_CW; break;
- case 4: gc.spindle_direction = SPINDLE_ENABLE_CCW; break;
- case 5: gc.spindle_direction = SPINDLE_DISABLE; break;
- #ifdef ENABLE_M7
- case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break;
- #endif
- case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break;
- case 9: gc.coolant_mode = COOLANT_DISABLE; break;
- default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
+ case 0: case 1: case 2: case 30:
+ word_bit = MODAL_GROUP_M4;
+ switch(int_value) {
+ case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
+ case 1: break; // Optional stop not supported. Ignore.
+ case 2: case 30: gc_block.modal.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
+ }
+ break;
+ case 3: case 4: case 5:
+ word_bit = MODAL_GROUP_M7;
+ switch(int_value) {
+ case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break;
+ case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break;
+ case 5: gc_block.modal.spindle = SPINDLE_DISABLE; break;
+ }
+ break;
+ #ifdef ENABLE_M7
+ case 7:
+ #endif
+ case 8: case 9:
+ word_bit = MODAL_GROUP_M8;
+ switch(int_value) {
+ #ifdef ENABLE_M7
+ case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break;
+ #endif
+ case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break;
+ case 9: gc_block.modal.coolant = COOLANT_DISABLE; break;
+ }
+ break;
+ default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command]
}
+
+ // Check for more than one command per modal group violations in the current block
+ // NOTE: Variable 'word_bit' is always assigned, if the command is valid.
+ if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
+ bit_true(command_words,bit(word_bit));
break;
- }
- // Check for modal group multiple command violations in the current block
- if (group_number) {
- if ( bit_istrue(modal_group_words,bit(group_number)) ) {
- FAIL(STATUS_MODAL_GROUP_VIOLATION);
- } else {
- bit_true(modal_group_words,bit(group_number));
- }
- group_number = MODAL_GROUP_NONE; // Reset for next command.
- }
- }
-
- // If there were any errors parsing this line, we will return right away with the bad news
- if (gc.status_code) { return(gc.status_code); }
+
+ // NOTE: All remaining letters assign values.
+ default:
- /* Pass 2: Parameters. All units converted according to current block commands. Position
- parameters are converted and flagged to indicate a change. These can have multiple connotations
- for different commands. Each will be converted to their proper value upon execution.
- NOTE: Grbl unconventionally pre-converts these parameter values based on the block G and M
- commands. This is set out of the order of execution defined by NIST only for code efficiency/size
- purposes, but should not affect proper g-code execution. */
- float p = 0;
- uint8_t l = 0;
- char_counter = 0;
- while(next_statement(&letter, &value, line, &char_counter)) {
- switch(letter) {
- case 'G': case 'M': break; // Ignore command statements and line numbers
- case 'F':
- if (value <= 0) { FAIL(STATUS_INVALID_STATEMENT); } // Must be greater than zero
- if (gc.inverse_feed_rate_mode) {
- inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only
- } else {
- gc.feed_rate = to_millimeters(value); // millimeters per minute
+ /* Non-Command Words: This initial parsing phase only checks for repeats of the remaining
+ legal g-code words and stores their value. Error-checking is performed later since some
+ words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */
+ switch(letter){
+ // case 'A': // Not supported
+ // case 'B': // Not supported
+ // case 'C': // Not supported
+ // case 'D': // Not supported
+ case 'F': word_bit = WORD_F; gc_block.values.f = value; break;
+ // case 'H': // Not supported
+ case 'I': word_bit = WORD_I; gc_block.values.ijk[X_AXIS] = value; ijk_words |= (1< MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number]
}
+ // bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking.
- // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets.
- // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this
- // modal group and do not effect these actions.
- switch (non_modal_action) {
- case NON_MODAL_DWELL:
- if (p < 0) { // Time cannot be negative.
- FAIL(STATUS_INVALID_STATEMENT);
+ // Track for unused words at the end of error-checking.
+ // NOTE: Single-meaning value words are removed all at once at the end of error-checking, because
+ // they are always used when present. This was done to save a few bytes of flash. For clarity, the
+ // single-meaning value words may be removed as they are used. Also, axis words are treated in the
+ // same way. If there is an axis explicit/implicit command, XYZ words are always used and are
+ // are removed at the end of error-checking.
+
+ // [1. Comments ]: MSG's NOT SUPPORTED. Comment handling performed by protocol.
+
+ // [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate
+ // is not defined after switching to G94 from G93.
+ if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93
+ // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here.
+ if (axis_explicit == AXIS_COMMAND_MOTION_MODE) {
+ if ((gc_block.modal.motion != MOTION_MODE_NONE) || (gc_block.modal.motion != MOTION_MODE_SEEK)) {
+ if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing]
+ }
+ }
+ // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would
+ // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each
+ // inverse time block, since the commands that use this value already perform undefined checks. This would
+ // also allow other commands, following this switch, to execute and not error out needlessly. This code is
+ // combined with the above feed rate mode and the below set feed rate error-checking.
+
+ // [3. Set feed rate ]: F is negative (done.)
+ // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion.
+ // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word
+ // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error
+ // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires
+ // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined.
+ } else { // = G94
+ // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value.
+ if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94
+ if (bit_istrue(value_words,bit(WORD_F))) {
+ if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
} else {
- // Ignore dwell in check gcode modes
- if (sys.state != STATE_CHECK_MODE) { mc_dwell(p); }
+ gc_block.values.f = gc_state.feed_rate; // Push last state feed rate
+ }
+ } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value.
+ }
+ // bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking.
+
+ // [4. Set spindle speed ]: S is negative (done.)
+ if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; }
+ // bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking.
+
+ // [5. Select tool ]: NOT SUPPORTED. T is negative (done.) Not an integer. Greater than max tool value.
+ // bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking.
+
+ // [6. Change tool ]: N/A
+ // [7. Spindle control ]: N/A
+ // [8. Coolant control ]: N/A
+ // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED.
+
+ // [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below.
+ if (gc_block.non_modal_command == NON_MODAL_DWELL) {
+ if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing]
+ bit_false(value_words,bit(WORD_P));
+ }
+
+ // [11. Set active plane ]: N/A
+ switch (gc_block.modal.plane_select) {
+ case PLANE_SELECT_XY:
+ axis_0 = X_AXIS;
+ axis_1 = Y_AXIS;
+ axis_linear = Z_AXIS;
+ break;
+ case PLANE_SELECT_ZX:
+ axis_0 = Z_AXIS;
+ axis_1 = X_AXIS;
+ axis_linear = Y_AXIS;
+ break;
+ default: // case PLANE_SELECT_YZ:
+ axis_0 = Y_AXIS;
+ axis_1 = Z_AXIS;
+ axis_linear = X_AXIS;
+ }
+
+ // [12. Set length units ]: N/A
+ // Pre-convert XYZ coordinate values to millimeters, if applicable.
+ uint8_t idx;
+ if (gc_block.modal.units == UNITS_MODE_INCHES) {
+ for (idx=0; idx N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
+ if (gc_state.modal.coord_select != gc_block.modal.coord_select) {
+ if (!(settings_read_coord_data(gc_block.modal.coord_select,coordinate_data))) { FAIL(STATUS_SETTING_READ_FAIL); }
+ }
+ }
+
+ // [16. Set path control mode ]: NOT SUPPORTED.
+ // [17. Set distance mode ]: N/A. G90.1 and G91.1 NOT SUPPORTED.
+ // [18. Set retract mode ]: NOT SUPPORTED.
+
+ // [19. Remaining non-modal actions ]: Check go to predefined position, set G10, or set axis offsets.
+ // NOTE: We need to separate the non-modal commands that are axis-explicit (G10/G28/G30/G92), as these
+ // commands all treat axis words differently. G10 as absolute offsets or computes current position as
+ // the axis value, G92 similarly to G10 L20, and G28/30 as an intermediate target position that observes
+ // all the current coordinate system and G92 offsets.
+ switch (gc_block.non_modal_command) {
+ case NON_MODAL_SET_COORDINATE_DATA:
+ // [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.)
+ // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing (done.)
+ // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing (done.)
+ if (bit_isfalse(value_words,((1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
+ if (gc_block.values.l != 20) {
+ if (gc_block.values.l == 2) {
+ if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported]
+ } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L]
+ }
+ bit_false(value_words,(bit(WORD_L)|bit(WORD_P)));
+
+ // Load EEPROM coordinate data and pre-calculate the new coordinate data.
+ if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
+ else { int_value = gc_state.modal.coord_select; } // Index P0 as the active coordinate system
+ if (!settings_read_coord_data(int_value,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
+ for (idx=0; idx N_COORDINATE_SYSTEM)) { // L2 and L20. P1=G54, P2=G55, ...
- FAIL(STATUS_UNSUPPORTED_STATEMENT);
- } else if (!axis_words && l==2) { // No axis words.
- FAIL(STATUS_INVALID_STATEMENT);
- } else {
- if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
- else { int_value = gc.coord_select; } // Index P0 as the active coordinate system
- float coord_data[N_AXIS];
- if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); }
- // Update axes defined only in block. Always in machine coordinates. Can change non-active system.
- for (idx=0; idx C -----------------+--------------- T <- [x,y]
+ | <------ d/2 ---->|
+
+ C - Current position
+ T - Target position
+ O - center of circle that pass through both C and T
+ d - distance from C to T
+ r - designated radius
+ h - distance from center of CT to O
+
+ Expanding the equations:
+
+ d -> sqrt(x^2 + y^2)
+ h -> sqrt(4 * r^2 - x^2 - y^2)/2
+ i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
+ j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
+
+ Which can be written:
+
+ i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
+ j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
+
+ Which we for size and speed reasons optimize to:
+
+ h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
+ i = (x - (y * h_x2_div_d))/2
+ j = (y + (x * h_x2_div_d))/2
+ */
+
+ // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller
+ // than d. If so, the sqrt of a negative number is complex and error out.
+ float h_x2_div_d = 4.0 * gc_block.values.r*gc_block.values.r - x*x - y*y;
+
+ if (h_x2_div_d < 0) { FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); } // [Arc radius error]
+
+ // Finish computing h_x2_div_d.
+ h_x2_div_d = -sqrt(h_x2_div_d)/hypot_f(x,y); // == -(h * 2 / d)
+ // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
+ if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
+
+ /* The counter clockwise circle lies to the left of the target direction. When offset is positive,
+ the left hand circle will be generated - when it is negative the right hand circle is generated.
+
+ T <-- Target position
+
+ ^
+ Clockwise circles with this center | Clockwise circles with this center will have
+ will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
+ \ | /
+ center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
+ |
+ |
+
+ C <-- Current position
+ */
+ // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
+ // even though it is advised against ever generating such circles in a single line of g-code. By
+ // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
+ // travel and thus we get the unadvisably long arcs as prescribed.
+ if (gc_block.values.r < 0) {
+ h_x2_div_d = -h_x2_div_d;
+ gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc
+ }
+ // Complete the operation by calculating the actual center of the arc
+ gc_block.values.ijk[axis_0] = 0.5*(x-(y*h_x2_div_d));
+ gc_block.values.ijk[axis_1] = 0.5*(y+(x*h_x2_div_d));
+
+ } else { // Arc Center Format Offset Mode
+ if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); } // [No offsets in plane]
+ bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K)));
+
+ // Convert IJK values to proper units.
+ if (gc_block.modal.units == UNITS_MODE_INCHES) {
+ for (idx=0; idx 0.002) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error]
+ }
+ break;
+ case MOTION_MODE_PROBE:
+ // [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate
+ // is undefined. Probe is triggered.
+ if (gc_check_same_position(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target]
+ if (probe_get_state()) { FAIL(STATUS_GCODE_PROBE_TRIGGERED); } // [Probe triggered]
+ break;
+ }
+ }
+ }
+
+ // [21. Program flow ]: No error check required.
+
+ // [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc
+ // radius mode, or axis words that aren't used in the block.
+ bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words.
+ if (axis_explicit) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words.
+ if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words]
+
+
+ /* -------------------------------------------------------------------------------------
+ STEP 4: EXECUTE!!
+ Assumes that all error-checking has been completed and no failure modes exist. We just
+ need to update the state and execute the block according to the order-of-execution.
+ */
+
+ // [1. Comments feedback ]: NOT SUPPORTED
+
+ // [2. Set feed rate mode ]:
+ gc_state.modal.feed_rate = gc_block.modal.feed_rate;
+
+ // [3. Set feed rate ]:
+ gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking.
+
+ // [4. Set spindle speed ]:
+ if (gc_state.spindle_speed != gc_block.values.s) {
+ gc_state.spindle_speed = gc_block.values.s;
+
+ // Update running spindle only if not in check mode and not already enabled.
+ if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_state.spindle_speed); }
+ }
+
+ // [5. Select tool ]: NOT SUPPORTED
+
+ // [6. Change tool ]: NOT SUPPORTED
+
+ // [7. Spindle control ]:
+ if (gc_state.modal.spindle != gc_block.modal.spindle) {
+ gc_state.modal.spindle = gc_block.modal.spindle;
+ // Update spindle control and apply spindle speed when enabling it in this block.
+ spindle_run(gc_state.modal.spindle, gc_state.spindle_speed);
+ }
+
+ // [8. Coolant control ]:
+ if (gc_state.modal.coolant != gc_block.modal.coolant) {
+ gc_state.modal.coolant = gc_block.modal.coolant;
+ coolant_run(gc_state.modal.coolant);
+ }
+
+ // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED
+
+ // [10. Dwell ]:
+ if (gc_block.non_modal_command == NON_MODAL_DWELL) { mc_dwell(gc_block.values.p); }
+
+ // [11. Set active plane ]:
+ gc_state.modal.plane_select = gc_block.modal.plane_select;
+
+ // [12. Set length units ]:
+ gc_state.modal.units = gc_block.modal.units;
+
+ // [13. Cutter radius compensation ]: NOT SUPPORTED
+
+ // [14. Cutter length compensation ]: NOT SUPPORTED
+
+ // [15. Coordinate system selection ]:
+ if (gc_state.modal.coord_select != gc_block.modal.coord_select) {
+ gc_state.modal.coord_select = gc_block.modal.coord_select;
+ memcpy(gc_state.coord_system,coordinate_data,sizeof(coordinate_data));
+ }
+
+ // [16. Set path control mode ]: NOT SUPPORTED
+
+ // [17. Set distance mode ]:
+ gc_state.modal.distance = gc_block.modal.distance;
+
+ // [18. Set retract mode ]: NOT SUPPORTED
+
+ // [19. Go to predefined position, Set G10, or Set axis offsets ]:
+ switch(gc_block.non_modal_command) {
+ case NON_MODAL_SET_COORDINATE_DATA:
+
+// TODO: See if I can clean up this int_value.
+ int_value = trunc(gc_block.values.p); // Convert p value to int.
+ if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
+ else { int_value = gc_state.modal.coord_select; } // Index P0 as the active coordinate system
+
+ settings_write_coord_data(int_value,parameter_data);
+ // Update system coordinate system if currently active.
+ if (gc_state.modal.coord_select == int_value) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); }
break;
case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
// Move to intermediate position before going home. Obeys current coordinate system and offsets
// and absolute and incremental modes.
- if (axis_words) {
- // Apply absolute mode coordinate offsets or incremental mode offsets.
- for (idx=0; idx 'Z')) {
- FAIL(STATUS_EXPECTED_COMMAND_LETTER);
- return(0);
- }
- (*char_counter)++;
- if (!read_float(line, char_counter, float_ptr)) {
- FAIL(STATUS_BAD_NUMBER_FORMAT);
- return(0);
- };
- return(1);
-}
-
-
-static void gc_convert_arc_radius_mode(float *target)
-{
-/* We need to calculate the center of the circle that has the designated radius and passes
- through both the current position and the target position. This method calculates the following
- set of equations where [x,y] is the vector from current to target position, d == magnitude of
- that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to
- the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the
- length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point
- [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
-
- d^2 == x^2 + y^2
- h^2 == r^2 - (d/2)^2
- i == x/2 - y/d*h
- j == y/2 + x/d*h
-
- O <- [i,j]
- - |
- r - |
- - |
- - | h
- - |
- [0,0] -> C -----------------+--------------- T <- [x,y]
- | <------ d/2 ---->|
-
- C - Current position
- T - Target position
- O - center of circle that pass through both C and T
- d - distance from C to T
- r - designated radius
- h - distance from center of CT to O
-
- Expanding the equations:
-
- d -> sqrt(x^2 + y^2)
- h -> sqrt(4 * r^2 - x^2 - y^2)/2
- i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
- j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
-
- Which can be written:
-
- i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
- j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
-
- Which we for size and speed reasons optimize to:
-
- h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
- i = (x - (y * h_x2_div_d))/2
- j = (y + (x * h_x2_div_d))/2 */
-
- // Calculate the change in position along each selected axis
- float x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0];
- float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1];
-
- clear_vector(gc.arc_offset);
- // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller
- // than d. If so, the sqrt of a negative number is complex and error out.
- float h_x2_div_d = 4 * gc.arc_radius*gc.arc_radius - x*x - y*y;
- if (h_x2_div_d < 0) { FAIL(STATUS_ARC_RADIUS_ERROR); return; }
- // Finish computing h_x2_div_d.
- h_x2_div_d = -sqrt(h_x2_div_d)/hypot(x,y); // == -(h * 2 / d)
- // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
- if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
-
- /* The counter clockwise circle lies to the left of the target direction. When offset is positive,
- the left hand circle will be generated - when it is negative the right hand circle is generated.
-
-
- T <-- Target position
-
- ^
- Clockwise circles with this center | Clockwise circles with this center will have
- will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
- \ | /
- center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
- |
- |
-
- C <-- Current position */
-
-
- // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
- // even though it is advised against ever generating such circles in a single line of g-code. By
- // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
- // travel and thus we get the unadvisably long arcs as prescribed.
- if (gc.arc_radius < 0) {
- h_x2_div_d = -h_x2_div_d;
- gc.arc_radius = -gc.arc_radius; // Finished with r. Set to positive for mc_arc
- }
- // Complete the operation by calculating the actual center of the arc
- gc.arc_offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d));
- gc.arc_offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d));
-}
+
/*
Not supported:
@@ -648,14 +944,13 @@ static void gc_convert_arc_radius_mode(float *target)
- A,B,C-axes
- Evaluation of expressions
- Variables
- - Probing
- Override control (TBD)
- Tool changes
- Switches
(*) Indicates optional parameter, enabled through config.h and re-compile
group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets)
- group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles)
+ group 1 = {G81 - G89} (Motion modes: Canned cycles)
group 4 = {M1} (Optional stop, ignored)
group 6 = {M6} (Tool change)
group 8 = {*M7} enable mist coolant
diff --git a/gcode.h b/gcode.h
index acd7046..3acba8f 100644
--- a/gcode.h
+++ b/gcode.h
@@ -29,68 +29,151 @@
// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute
// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online,
// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).
-#define MODAL_GROUP_NONE 0
-#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal
-#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G38.2,G80] Motion
-#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection
-#define MODAL_GROUP_3 4 // [G90,G91] Distance mode
-#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping
-#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode
-#define MODAL_GROUP_6 7 // [G20,G21] Units
-#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning
-#define MODAL_GROUP_8 9 // [M7,M8,M9] Coolant control
-#define MODAL_GROUP_12 10 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
+// NOTE: Modal group define values must be sequential and starting from zero.
+#define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal
+#define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G80] Motion
+#define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection
+#define MODAL_GROUP_G3 3 // [G90,G91] Distance mode
+#define MODAL_GROUP_G5 4 // [G93,G94] Feed rate mode
+#define MODAL_GROUP_G6 5 // [G20,G21] Units
+#define MODAL_GROUP_G12 6 // [G54,G55,G56,G57,G58,G59] Coordinate system selection
+
+#define MODAL_GROUP_M4 7 // [M0,M1,M2,M30] Stopping
+#define MODAL_GROUP_M7 8 // [M3,M4,M5] Spindle turning
+#define MODAL_GROUP_M8 9 // [M7,M8,M9] Coolant control
+
+#define OTHER_INPUT_F 10
+#define OTHER_INPUT_S 11
+#define OTHER_INPUT_T 12
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute.
-#define MOTION_MODE_SEEK 0 // G0
-#define MOTION_MODE_LINEAR 1 // G1
-#define MOTION_MODE_CW_ARC 2 // G2
-#define MOTION_MODE_CCW_ARC 3 // G3
-#define MOTION_MODE_PROBE 4 // G38.x
-#define MOTION_MODE_CANCEL 5 // G80
-#define PROGRAM_FLOW_RUNNING 0
-#define PROGRAM_FLOW_PAUSED 1 // M0, M1
-#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
-
-#define NON_MODAL_NONE 0
+// Modal Group 0: Non-modal actions
+#define NON_MODAL_NO_ACTION 0 // (Default: Must be zero)
#define NON_MODAL_DWELL 1 // G4
#define NON_MODAL_SET_COORDINATE_DATA 2 // G10
#define NON_MODAL_GO_HOME_0 3 // G28
#define NON_MODAL_SET_HOME_0 4 // G28.1
#define NON_MODAL_GO_HOME_1 5 // G30
#define NON_MODAL_SET_HOME_1 6 // G30.1
-#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92
-#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1
+#define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53
+#define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92
+#define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1
+
+// Modal Group 1: Motion modes
+#define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero)
+#define MOTION_MODE_LINEAR 1 // G1
+#define MOTION_MODE_CW_ARC 2 // G2
+#define MOTION_MODE_CCW_ARC 3 // G3
+#define MOTION_MODE_PROBE 4 // G38.2
+#define MOTION_MODE_NONE 5 // G80
+
+// Modal Group 2: Plane select
+#define PLANE_SELECT_XY 0 // G17 (Default: Must be zero)
+#define PLANE_SELECT_ZX 1 // G18
+#define PLANE_SELECT_YZ 2 // G19
+
+// Modal Group 3: Distance mode
+#define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero)
+#define DISTANCE_MODE_INCREMENTAL 1 // G91
+
+// Modal Group 4: Program flow
+#define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero)
+#define PROGRAM_FLOW_PAUSED 1 // M0, M1
+#define PROGRAM_FLOW_COMPLETED 2 // M2, M30
+
+// Modal Group 5: Feed rate mode
+#define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero)
+#define FEED_RATE_MODE_INVERSE_TIME 1 // G93
+
+// Modal Group 6: Units mode
+#define UNITS_MODE_MM 0 // G21 (Default: Must be zero)
+#define UNITS_MODE_INCHES 1 // G20
+
+// Modal Group 7: Spindle control
+#define SPINDLE_DISABLE 0 // M5 (Default: Must be zero)
+#define SPINDLE_ENABLE_CW 1 // M3
+#define SPINDLE_ENABLE_CCW 2 // M4
+
+// Modal Group 8: Coolant control
+#define COOLANT_DISABLE 0 // M9 (Default: Must be zero)
+#define COOLANT_MIST_ENABLE 1 // M7
+#define COOLANT_FLOOD_ENABLE 2 // M8
+
+// Modal Group 12: Active work coordinate system
+// N/A: Stores coordinate system value (54-59) to change to.
+
+#define WORD_F 0
+#define WORD_I 1
+#define WORD_J 2
+#define WORD_K 3
+#define WORD_L 4
+#define WORD_N 5
+#define WORD_P 6
+#define WORD_R 7
+#define WORD_S 8
+#define WORD_T 9
+#define WORD_X 10
+#define WORD_Y 11
+#define WORD_Z 12
+
+
+
+
+// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
+typedef struct {
+ uint8_t motion; // {G0,G1,G2,G3,G80}
+ uint8_t feed_rate; // {G93,G94}
+ uint8_t units; // {G20,G21}
+ uint8_t distance; // {G90,G91}
+ uint8_t plane_select; // {G17,G18,G19}
+ uint8_t coord_select; // {G54,G55,G56,G57,G58,G59}
+ uint8_t program_flow; // {M0,M1,M2,M30}
+ uint8_t coolant; // {M7,M8,M9}
+ uint8_t spindle; // {M3,M4,M5}
+} gc_modal_t;
typedef struct {
- uint8_t status_code; // Parser status for current block
- uint8_t motion_mode; // {G0, G1, G2, G3, G80}
- uint8_t inverse_feed_rate_mode; // {G93, G94}
- uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21}
- uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91}
- uint8_t program_flow; // {M0, M1, M2, M30}
- uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable, 2 = Mist Enable {M8, M9}
- uint8_t spindle_direction; // 1 = CW, 2 = CCW, 0 = Stop {M3, M4, M5}
+ float f; // Feed
+ float ijk[3]; // I,J,K Axis arc offsets
+ uint8_t l; // G10 or canned cycles parameters
+ int32_t n; // Line number
+ float p; // G10 or dwell parameters
+ // float q; // G82 peck drilling
+ float r; // Arc radius
+ float s; // Spindle speed
+ // uint8_t t; // Tool selection
+ float xyz[3]; // X,Y,Z Translational axes
+} gc_values_t;
+
+
+typedef struct {
+ gc_modal_t modal;
+
float spindle_speed; // RPM
float feed_rate; // Millimeters/min
- float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
uint8_t tool;
- uint8_t plane_axis_0,
- plane_axis_1,
- plane_axis_2; // The axes of the selected plane
- uint8_t coord_select; // Active work coordinate system number. Default: 0=G54.
+
+ float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
+
float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine
- // position in mm. Loaded from EEPROM when called.
+ // position in mm. Loaded from EEPROM when called.
float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
-
- float arc_radius;
- float arc_offset[N_AXIS];
-
} parser_state_t;
-extern parser_state_t gc;
+extern parser_state_t gc_state;
+
+typedef struct {
+// uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated.
+// uint16_t value_words;
+
+ uint8_t non_modal_command;
+ gc_modal_t modal;
+ gc_values_t values;
+
+} parser_block_t;
+extern parser_block_t gc_block;
// Initialize the parser
void gc_init();
diff --git a/motion_control.c b/motion_control.c
index 6fda0c2..321dfe9 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -97,16 +97,15 @@ void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate)
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal
// distance from segment to the circle when the end points both lie on the circle.
#ifdef USE_LINE_NUMBERS
-void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
- uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise, int32_t line_number)
+void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
+ uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number)
#else
-void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
- uint8_t axis_linear, float feed_rate, uint8_t invert_feed_rate, float radius, uint8_t isclockwise)
+void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate,
+ uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear)
#endif
-{
+{
float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
- float linear_travel = target[axis_linear] - position[axis_linear];
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
float r_axis1 = -offset[axis_1];
float rt_axis0 = target[axis_0] - center_axis0;
@@ -114,7 +113,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
- if (isclockwise) { // Correct atan2 output per direction
+ if (gc_state.modal.motion == MOTION_MODE_CW_ARC) { // Correct atan2 output per direction
if (angular_travel >= 0) { angular_travel -= 2*M_PI; }
} else {
if (angular_travel <= 0) { angular_travel += 2*M_PI; }
@@ -123,10 +122,8 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
// (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit
// is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
- // Computes: mm_per_arc_segment = sqrt(4*arc_tolerance*(2*radius-arc_tolerance)),
- // segments = millimeters_of_travel/mm_per_arc_segment
- float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
- uint16_t segments = floor(0.5*millimeters_of_travel/
+ // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
+ uint16_t segments = floor(fabs(0.5*angular_travel*radius)/
sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
if (segments) {
@@ -136,7 +133,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
if (invert_feed_rate) { feed_rate *= segments; }
float theta_per_segment = angular_travel/segments;
- float linear_per_segment = linear_travel/segments;
+ float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments;
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
and phi is the angle of rotation. Solution approach by Jens Geisler.
@@ -166,17 +163,13 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0);
cos_T *= 0.5;
- float arc_target[N_AXIS];
float sin_Ti;
float cos_Ti;
float r_axisi;
uint16_t i;
uint8_t count = 0;
- // Initialize the linear axis
- arc_target[axis_linear] = position[axis_linear];
-
- for (i = 1; i (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -53,7 +53,7 @@
// Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while float_ptr is
// a pointer to the result variable. Returns true when it succeeds
-int read_float(char *line, uint8_t *char_counter, float *float_ptr);
+uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr);
// Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms().
void delay_ms(uint16_t ms);
@@ -63,4 +63,6 @@ void delay_us(uint32_t us);
uint8_t get_direction_mask(uint8_t i);
+float hypot_f(float x, float y);
+
#endif
diff --git a/print.c b/print.c
index 03e261d..6fe8c2d 100644
--- a/print.c
+++ b/print.c
@@ -76,32 +76,52 @@ void print_uint8_base2(uint8_t n)
serial_write('0' + buf[i - 1]);
}
-void print_uint32_base10(unsigned long n)
+void print_uint8_base10(uint8_t n)
{
- unsigned char buf[10];
- uint8_t i = 0;
-
if (n == 0) {
serial_write('0');
return;
}
+
+ unsigned char buf[3];
+ uint8_t i = 0;
+
+ while (n > 0) {
+ buf[i++] = n % 10 + '0';
+ n /= 10;
+ }
+
+ for (; i > 0; i--)
+ serial_write(buf[i - 1]);
+}
+
+void print_uint32_base10(unsigned long n)
+{
+ if (n == 0) {
+ serial_write('0');
+ return;
+ }
+
+ unsigned char buf[10];
+ uint8_t i = 0;
while (n > 0) {
- buf[i++] = n % 10 + '0';
+ buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
- serial_write(buf[i-1]);
+ serial_write('0' + buf[i-1]);
}
void printInteger(long n)
{
if (n < 0) {
serial_write('-');
- n = -n;
+ print_uint32_base10((-n));
+ } else {
+ print_uint32_base10(n);
}
- print_uint32_base10(n);
}
// Convert float to string by immediately converting to a long integer, which contains
diff --git a/print.h b/print.h
index ecd4238..273a554 100644
--- a/print.h
+++ b/print.h
@@ -35,6 +35,8 @@ void print_uint32_base10(uint32_t n);
void print_uint8_base2(uint8_t n);
+void print_uint8_base10(uint8_t n);
+
void printFloat(float n);
#endif
\ No newline at end of file
diff --git a/probe.c b/probe.c
index 20b43a8..cad0449 100644
--- a/probe.c
+++ b/probe.c
@@ -30,13 +30,19 @@ void probe_init()
}
+// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
+uint8_t probe_get_state()
+{
+ return(!(PROBE_PIN & PROBE_MASK));
+}
+
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
void probe_state_monitor()
{
if (sys.probe_state == PROBE_ACTIVE) {
- if (!(PROBE_PIN & PROBE_MASK)) {
+ if (probe_get_state()) {
sys.probe_state = PROBE_OFF;
memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS);
sys.execute |= EXEC_FEED_HOLD;
diff --git a/probe.h b/probe.h
index e5aef86..294c01b 100644
--- a/probe.h
+++ b/probe.h
@@ -29,6 +29,9 @@
// Probe pin initialization routine.
void probe_init();
+// Returns probe pin state.
+uint8_t probe_get_state();
+
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
void probe_state_monitor();
diff --git a/protocol.c b/protocol.c
index 2bf7bcd..c97829b 100644
--- a/protocol.c
+++ b/protocol.c
@@ -36,37 +36,27 @@ static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
// Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also directs and executes Grbl internal commands,
// such as settings, initiating the homing cycle, and toggling switch states.
-// TODO: Eventually re-organize this function to more cleanly organize order of operations,
-// which will hopefully reduce some of the current spaghetti logic and dynamic memory usage.
static void protocol_execute_line(char *line)
{
protocol_execute_runtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort
- uint8_t status;
if (line[0] == 0) {
// Empty or comment line. Send status message for syncing purposes.
- status = STATUS_OK;
+ report_status_message(STATUS_OK);
} else if (line[0] == '$') {
// Grbl '$' system command
- status = system_execute_line(line);
+ report_status_message(system_execute_line(line));
+ } else if (sys.state == STATE_ALARM) {
+ // Everything else is gcode. Block if in alarm mode.
+ report_status_message(STATUS_ALARM_LOCK);
+
} else {
- // Everything else is gcode. Send to g-code parser! Block if in alarm mode.
- if (sys.state == STATE_ALARM) { status = STATUS_ALARM_LOCK; }
- else { status = gc_execute_line(line); }
-
- // TODO: Separate the parsing from the g-code execution. Need to re-write the parser
- // completely to do this. First parse the line completely, checking for modal group
- // errors and storing all of the g-code words. Then, send the stored g-code words to
- // a separate g-code executor. This will be more in-line with actual g-code protocol.
-
- // TODO: Clean up the multi-tasking workflow with the execution of commands. It's a
- // bit complicated and patch-worked. Could be made simplier to understand.
+ // Parse and execute g-code block!
+ report_status_message(gc_execute_line(line));
}
-
- report_status_message(status);
}
@@ -117,12 +107,27 @@ void protocol_main_loop()
}
} else {
if (c <= ' ') {
- // Throw away whitepace and control characters
+ // Throw away whitepace and control characters
} else if (c == '/') {
- // Block delete not supported. Ignore character.
+ // Block delete NOT SUPPORTED. Ignore character.
+ // NOTE: If supported, would simply need to check the system if block delete is enabled.
} else if (c == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
+ // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now.
+ // In the future, we could simply remove the items within the comments, but retain the
+ // comment control characters, so that the g-code parser can error-check it.
iscomment = true;
+ // } else if (c == ';') {
+ // Comment character to EOL NOT SUPPORTED. LinuxCNC definition. Not NIST.
+
+ // TODO: Install '%' feature
+ // } else if (c == '%') {
+ // Program start-end percent sign NOT SUPPORTED.
+ // NOTE: This maybe installed to tell Grbl when a program is running vs manual input,
+ // where, during a program, the system auto-cycle start will continue to execute
+ // everything until the next '%' sign. This will help fix resuming issues with certain
+ // functions that empty the planner buffer to execute its task on-time.
+
} else if (char_counter >= LINE_BUFFER_SIZE-1) {
// Detect line buffer overflow. Report error and reset line buffer.
report_status_message(STATUS_OVERFLOW);
diff --git a/report.c b/report.c
index e766ee5..bed9661 100644
--- a/report.c
+++ b/report.c
@@ -51,22 +51,16 @@ void report_status_message(uint8_t status_code)
} else {
printPgmString(PSTR("error: "));
switch(status_code) {
- case STATUS_BAD_NUMBER_FORMAT:
- printPgmString(PSTR("Bad number format")); break;
case STATUS_EXPECTED_COMMAND_LETTER:
printPgmString(PSTR("Expected command letter")); break;
- case STATUS_UNSUPPORTED_STATEMENT:
- printPgmString(PSTR("Unsupported statement")); break;
- case STATUS_ARC_RADIUS_ERROR:
- printPgmString(PSTR("Invalid radius")); break;
- case STATUS_MODAL_GROUP_VIOLATION:
- printPgmString(PSTR("Modal group violation")); break;
+ case STATUS_BAD_NUMBER_FORMAT:
+ printPgmString(PSTR("Bad number format")); break;
case STATUS_INVALID_STATEMENT:
printPgmString(PSTR("Invalid statement")); break;
+ case STATUS_NEGATIVE_VALUE:
+ printPgmString(PSTR("Value < 0")); break;
case STATUS_SETTING_DISABLED:
printPgmString(PSTR("Setting disabled")); break;
- case STATUS_SETTING_VALUE_NEG:
- printPgmString(PSTR("Value < 0.0")); break;
case STATUS_SETTING_STEP_PULSE_MIN:
printPgmString(PSTR("Value < 3 usec")); break;
case STATUS_SETTING_READ_FAIL:
@@ -79,6 +73,18 @@ void report_status_message(uint8_t status_code)
printPgmString(PSTR("Homing not enabled")); break;
case STATUS_OVERFLOW:
printPgmString(PSTR("Line overflow")); break;
+
+ // Common g-code parser errors.
+ case STATUS_GCODE_MODAL_GROUP_VIOLATION:
+ printPgmString(PSTR("Modal group violation")); break;
+ case STATUS_GCODE_UNSUPPORTED_COMMAND:
+ printPgmString(PSTR("Unsupported command")); break;
+ case STATUS_GCODE_UNDEFINED_FEED_RATE:
+ printPgmString(PSTR("Undefined feed rate")); break;
+ default:
+ // Remaining g-code parser errors with error codes
+ printPgmString(PSTR("Invalid gcode ID:"));
+ print_uint8_base10(status_code); // Print error code for user reference
}
printPgmString(PSTR("\r\n"));
}
@@ -163,29 +169,28 @@ void report_grbl_settings() {
printPgmString(PSTR(" (z accel, mm/sec^2)\r\n$9=")); printFloat(-settings.max_travel[X_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (x max travel, mm)\r\n$10=")); printFloat(-settings.max_travel[Y_AXIS]); // Grbl internally store this as negative.
printPgmString(PSTR(" (y max travel, mm)\r\n$11=")); printFloat(-settings.max_travel[Z_AXIS]); // Grbl internally store this as negative.
- printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds);
- printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate);
- printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.step_invert_mask);
+ printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); print_uint8_base10(settings.pulse_microseconds);
+ printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); print_uint8_base10(settings.step_invert_mask);
printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
- printPgmString(PSTR(")\r\n$15=")); printInteger(settings.dir_invert_mask);
+ printPgmString(PSTR(")\r\n$14=")); print_uint8_base10(settings.dir_invert_mask);
printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask);
- printPgmString(PSTR(")\r\n$16=")); printInteger(settings.stepper_idle_lock_time);
- printPgmString(PSTR(" (step idle delay, msec)\r\n$17=")); printFloat(settings.junction_deviation);
- printPgmString(PSTR(" (junction deviation, mm)\r\n$18=")); printFloat(settings.arc_tolerance);
- printPgmString(PSTR(" (arc tolerance, mm)\r\n$19=")); printInteger(settings.decimal_places);
- printPgmString(PSTR(" (n-decimals, int)\r\n$20=")); printInteger(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
- printPgmString(PSTR(" (report inches, bool)\r\n$21=")); printInteger(bit_istrue(settings.flags,BITFLAG_AUTO_START));
- printPgmString(PSTR(" (auto start, bool)\r\n$22=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
- printPgmString(PSTR(" (invert step enable, bool)\r\n$23=")); printInteger(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
- printPgmString(PSTR(" (invert limit pins, bool)\r\n$24=")); printInteger(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
- printPgmString(PSTR(" (soft limits, bool)\r\n$25=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
- printPgmString(PSTR(" (hard limits, bool)\r\n$26=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
- printPgmString(PSTR(" (homing cycle, bool)\r\n$27=")); printInteger(settings.homing_dir_mask);
+ printPgmString(PSTR(")\r\n$15=")); print_uint8_base10(settings.stepper_idle_lock_time);
+ printPgmString(PSTR(" (step idle delay, msec)\r\n$16=")); printFloat(settings.junction_deviation);
+ printPgmString(PSTR(" (junction deviation, mm)\r\n$17=")); printFloat(settings.arc_tolerance);
+ printPgmString(PSTR(" (arc tolerance, mm)\r\n$18=")); print_uint8_base10(settings.decimal_places);
+ printPgmString(PSTR(" (n-decimals, int)\r\n$19=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES));
+ printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START));
+ printPgmString(PSTR(" (auto start, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE));
+ printPgmString(PSTR(" (invert step enable, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS));
+ printPgmString(PSTR(" (invert limit pins, bool)\r\n$23=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE));
+ printPgmString(PSTR(" (soft limits, bool)\r\n$24=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
+ printPgmString(PSTR(" (hard limits, bool)\r\n$25=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
+ printPgmString(PSTR(" (homing cycle, bool)\r\n$26=")); print_uint8_base10(settings.homing_dir_mask);
printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
- printPgmString(PSTR(")\r\n$28=")); printFloat(settings.homing_feed_rate);
- printPgmString(PSTR(" (homing feed, mm/min)\r\n$29=")); printFloat(settings.homing_seek_rate);
- printPgmString(PSTR(" (homing seek, mm/min)\r\n$30=")); printInteger(settings.homing_debounce_delay);
- printPgmString(PSTR(" (homing debounce, msec)\r\n$31=")); printFloat(settings.homing_pulloff);
+ printPgmString(PSTR(")\r\n$27=")); printFloat(settings.homing_feed_rate);
+ printPgmString(PSTR(" (homing feed, mm/min)\r\n$28=")); printFloat(settings.homing_seek_rate);
+ printPgmString(PSTR(" (homing seek, mm/min)\r\n$29=")); print_uint8_base10(settings.homing_debounce_delay);
+ printPgmString(PSTR(" (homing debounce, msec)\r\n$30=")); printFloat(settings.homing_pulloff);
printPgmString(PSTR(" (homing pull-off, mm)\r\n"));
}
@@ -222,16 +227,11 @@ void report_ngc_parameters()
}
printPgmString(PSTR("[G"));
switch (coord_select) {
- case 0: printPgmString(PSTR("54:")); break;
- case 1: printPgmString(PSTR("55:")); break;
- case 2: printPgmString(PSTR("56:")); break;
- case 3: printPgmString(PSTR("57:")); break;
- case 4: printPgmString(PSTR("58:")); break;
- case 5: printPgmString(PSTR("59:")); break;
- case 6: printPgmString(PSTR("28:")); break;
- case 7: printPgmString(PSTR("30:")); break;
- // case 8: printPgmString(PSTR("92:")); break; // G92.2, G92.3 not supported. Hence not stored.
- }
+ case 6: printPgmString(PSTR("28")); break;
+ case 7: printPgmString(PSTR("30")); break;
+ default: print_uint8_base10(coord_select+54); break; // G54-G59
+ }
+ printPgmString(PSTR(":"));
for (i=0; i