G-code parser G0/G1 bug fix.
- Although stated as invalid in the NIST g-code standard, most g-code parsers, including linuxcnc, allow G0 and G1 to be commanded without axis words present. For example, something like ‘G1 F100.0’ to preset the motion mode and feed rate without a motion commanded. Older CNC controllers actually required this for feed rate settings. This update should now allow this type of behavior.
This commit is contained in:
		
							
								
								
									
										70
									
								
								gcode.c
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								gcode.c
									
									
									
									
									
								
							@@ -92,7 +92,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
     
 | 
			
		||||
  memset(&gc_block, 0, sizeof(gc_block)); // Initialize the parser block struct.
 | 
			
		||||
  memcpy(&gc_block.modal,&gc_state.modal,sizeof(gc_modal_t)); // Copy current modes
 | 
			
		||||
  uint8_t axis_explicit = AXIS_COMMAND_NONE;
 | 
			
		||||
  uint8_t axis_command = AXIS_COMMAND_NONE;
 | 
			
		||||
  uint8_t axis_0, axis_1, axis_linear;
 | 
			
		||||
  float coordinate_data[N_AXIS]; // Multi-use variable to store coordinate data for execution
 | 
			
		||||
  float parameter_data[N_AXIS]; // Multi-use variable to store parameter data for execution
 | 
			
		||||
@@ -149,8 +149,8 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
          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;
 | 
			
		||||
              if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
 | 
			
		||||
              axis_command = AXIS_COMMAND_NON_MODAL;
 | 
			
		||||
            }
 | 
			
		||||
            // No break. Continues to next line.
 | 
			
		||||
          case 4: case 53: 
 | 
			
		||||
@@ -187,8 +187,8 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
            break;
 | 
			
		||||
          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; 
 | 
			
		||||
            if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
 | 
			
		||||
            axis_command = AXIS_COMMAND_MOTION_MODE; 
 | 
			
		||||
            // No break. Continues to next line.
 | 
			
		||||
          case 80: 
 | 
			
		||||
            word_bit = MODAL_GROUP_G1; 
 | 
			
		||||
@@ -363,21 +363,10 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  
 | 
			
		||||
  // [0. Non-specific/common error-checks and miscellaneous setup]: 
 | 
			
		||||
  
 | 
			
		||||
  // Determine how axis words are used and error-check it. Apply implicit conditions and exceptions.
 | 
			
		||||
  if (axis_explicit) { // Either a non-modal or motion mode axis-explicit command has been passed.
 | 
			
		||||
    // Axis-words are required (with exceptions) with axis-explicit commands.
 | 
			
		||||
    // NOTE: G28/30 reserve any axis words as an intermediate motion, but are not required. G2/3 full
 | 
			
		||||
    // circle arcs don't require axis words, but this aren't supported.
 | 
			
		||||
    if (!axis_words) {
 | 
			
		||||
      if ((gc_block.non_modal_command == NON_MODAL_GO_HOME_0) || (gc_block.non_modal_command == NON_MODAL_GO_HOME_1)) {
 | 
			
		||||
        axis_explicit = AXIS_COMMAND_NONE; // No axis words passed for G28/30.
 | 
			
		||||
      } else { 
 | 
			
		||||
        FAIL(STATUS_GCODE_NO_AXIS_WORDS); // [No axis words]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // If no axis-explicit commands in the block, axis words implicitly activate the current motion mode.
 | 
			
		||||
    if (axis_words) { axis_explicit = AXIS_COMMAND_MOTION_MODE; } // Assign implicit motion-mode
 | 
			
		||||
  // Determine implicit axis command conditions. Axis words have been passed, but no explicit axis
 | 
			
		||||
  // command has been sent. If so, set axis command to current motion mode.
 | 
			
		||||
  if (axis_words) {
 | 
			
		||||
    if (!axis_command) { axis_command = AXIS_COMMAND_MOTION_MODE; } // Assign implicit motion-mode
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Check for valid line number N value.
 | 
			
		||||
@@ -391,7 +380,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  // 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 
 | 
			
		||||
  // same way. If there is an explicit/implicit axis 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.
 | 
			
		||||
@@ -400,7 +389,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  //   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 (axis_command == 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]
 | 
			
		||||
      }
 | 
			
		||||
@@ -497,15 +486,16 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  // [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
 | 
			
		||||
  // NOTE: We need to separate the non-modal commands that are axis word-using (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.)
 | 
			
		||||
      // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing.
 | 
			
		||||
      // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing.
 | 
			
		||||
      if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words]
 | 
			
		||||
      if (bit_isfalse(value_words,((1<<WORD_P)|(1<<WORD_L)))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P/L word missing]
 | 
			
		||||
      int_value = trunc(gc_block.values.p); // Convert p value to int.
 | 
			
		||||
      if (int_value > N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
 | 
			
		||||
@@ -533,7 +523,8 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case NON_MODAL_SET_COORDINATE_OFFSET:
 | 
			
		||||
      // [G92 Errors]: No axis words (done.)
 | 
			
		||||
      // [G92 Errors]: No axis words.
 | 
			
		||||
      if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
 | 
			
		||||
    
 | 
			
		||||
      // Update axes defined only in block. Offsets current system to defined value. Does not update when
 | 
			
		||||
      // active coordinate system is selected, but is still active unless G92.1 disables it. 
 | 
			
		||||
@@ -548,7 +539,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
      
 | 
			
		||||
    default:
 | 
			
		||||
    
 | 
			
		||||
      // At this point, the rest of the axis-explicit commands treat the axis values as the traditional
 | 
			
		||||
      // At this point, the rest of the explicit axis commands treat the axis values as the traditional
 | 
			
		||||
      // target position with the coordinate system offsets, G92 offsets, absolute override, and distance
 | 
			
		||||
      // modes applied. This includes the motion mode commands. We can now pre-compute the target position.
 | 
			
		||||
      // NOTE: Tool offsets may be appended to these conversions when/if this feature is added.
 | 
			
		||||
@@ -603,26 +594,31 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  // [20. Motion modes ]: 
 | 
			
		||||
  if (gc_block.modal.motion == MOTION_MODE_NONE) {
 | 
			
		||||
    // [G80 Errors]: Axis word exist and are not used by a non-modal command.
 | 
			
		||||
    if ((axis_words) && (axis_explicit != AXIS_COMMAND_NON_MODAL)) { 
 | 
			
		||||
    if ((axis_words) && (axis_command != AXIS_COMMAND_NON_MODAL)) { 
 | 
			
		||||
      FAIL(STATUS_GCODE_AXIS_WORDS_EXIST); // [No axis words allowed]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  // Check remaining motion modes, if axis word are implicit (exist and not used by G10/28/30/92), or 
 | 
			
		||||
  // was explicitly commanded in the g-code block.
 | 
			
		||||
  } else if ( axis_explicit == AXIS_COMMAND_MOTION_MODE ) {
 | 
			
		||||
  } else if ( axis_command == AXIS_COMMAND_MOTION_MODE ) {
 | 
			
		||||
  
 | 
			
		||||
    // [G0 Errors]: (All done!) No axis words. Axis letter not configured or without real value.
 | 
			
		||||
    if (gc_block.modal.motion == MOTION_MODE_SEEK) {
 | 
			
		||||
      // [G0 Errors]: Axis letter not configured or without real value (done.)
 | 
			
		||||
      // Axis words are optional. If missing, set axis command flag to ignore execution.
 | 
			
		||||
      if (!axis_words) { axis_command = AXIS_COMMAND_NONE; }
 | 
			
		||||
 | 
			
		||||
    // All remaining motion modes (all but G0 and G80), require a valid feed rate value. In units per mm mode,
 | 
			
		||||
    // the value must be positive. In inverse time mode, a positive value must be passed with each block.
 | 
			
		||||
    if (gc_block.modal.motion != MOTION_MODE_SEEK) {
 | 
			
		||||
      
 | 
			
		||||
    } else {      
 | 
			
		||||
      // Check if feed rate is defined for the motion modes that require it.
 | 
			
		||||
      if (gc_block.values.f == 0.0) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [Feed rate undefined]
 | 
			
		||||
     
 | 
			
		||||
      switch (gc_block.modal.motion) {
 | 
			
		||||
        case MOTION_MODE_LINEAR: 
 | 
			
		||||
          // [G1 Errors]: (All done!) No axis words and feed rate undefined. Axis letter not configured or without real value.
 | 
			
		||||
          // [G1 Errors]: Feed rate undefined. Axis letter not configured or without real value.
 | 
			
		||||
          // Axis words are optional. If missing, set axis command flag to ignore execution.
 | 
			
		||||
          if (!axis_words) { axis_command = AXIS_COMMAND_NONE; }
 | 
			
		||||
 | 
			
		||||
          break;
 | 
			
		||||
        case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
 | 
			
		||||
          // [G2/3 Errors All-Modes]: Feed rate undefined.
 | 
			
		||||
@@ -632,6 +628,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
          // [G2/3 Full-Circle-Mode Errors]: NOT SUPPORTED. Axis words exist. No offsets programmed. P must be an integer.        
 | 
			
		||||
          // NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the error-checking.
 | 
			
		||||
        
 | 
			
		||||
          if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
 | 
			
		||||
          if (!(axis_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE); } // [No axis words in plane]
 | 
			
		||||
        
 | 
			
		||||
          // Calculate the change in position along each selected axis
 | 
			
		||||
@@ -760,6 +757,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
        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 (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
 | 
			
		||||
          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;
 | 
			
		||||
@@ -772,7 +770,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  // [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 (axis_command) { 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]
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
@@ -859,7 +857,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
    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_explicit) {
 | 
			
		||||
      if (axis_command) {
 | 
			
		||||
        #ifdef USE_LINE_NUMBERS
 | 
			
		||||
        mc_line(gc_block.values.xyz, -1.0, false, gc_block.values.n);
 | 
			
		||||
        #else
 | 
			
		||||
@@ -893,7 +891,7 @@ uint8_t gc_execute_line(char *line)
 | 
			
		||||
  // Enter motion modes only if there are axis words or a motion mode command word in the block.
 | 
			
		||||
  gc_state.modal.motion = gc_block.modal.motion;
 | 
			
		||||
  if (gc_state.modal.motion != MOTION_MODE_NONE) {
 | 
			
		||||
    if (axis_explicit == AXIS_COMMAND_MOTION_MODE) {
 | 
			
		||||
    if (axis_command == AXIS_COMMAND_MOTION_MODE) {
 | 
			
		||||
      switch (gc_state.modal.motion) {
 | 
			
		||||
        case MOTION_MODE_SEEK:
 | 
			
		||||
          #ifdef USE_LINE_NUMBERS
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user