-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
After asking Github copilot a few questions about the parser, I asked it to document creating a new G-code.
From a quick read, I think this is largely correct.
Perfect! Now I have all the information needed. Let me create a comprehensive documentation page.
How to Add a New G-Code to LinuxCNC
This guide explains how to implement a new G-code command in the LinuxCNC interpreter. It covers the necessary source files, the modal group system, and walks through a concrete example.
Overview
Adding a new G-code to LinuxCNC requires modifications across several source files:
- Header Files - Define constants and function declarations
- Array/Enumeration Files - Register the G-code in modal groups
- Parser Files - Add code to parse the G-code parameters
- Conversion/Execution Files - Implement the actual G-code logic
- Documentation - Document the new G-code for users
Step 1: Define G-Code Constants
File: src/emc/rs274ngc/interp_internal.hh
G-codes are defined as symbolic constants in this header file. The naming convention is G_ followed by the G-code number, where decimal points are represented with underscores.
Examples:
enum GCodes {
G_0 = 0, // Rapid traverse
G_1 = 10, // Linear feed
G_2 = 20, // Clockwise arc
G_3 = 30, // Counterclockwise arc
G_4 = 40, // Dwell
G_28 = 280, // Return to home position
G_38_2 = 382, // Probe (notice: 38.2 becomes 382)
// ... existing codes ...
};Key Points:
- The value is the G-code number multiplied by 10 (to handle decimal places)
- G38.2 is represented as
G_38_2 = 382 - G59.3 would be
G_59_3 = 593
File: src/emc/rs274ngc/rs274ngc_interp.hh
Declare any new conversion functions:
int convert_my_gcode(int g_code, block_pointer block, setup_pointer settings);Step 2: Register in Modal Group System
File: src/emc/rs274ngc/interp_array.cc
G-codes are organized into modal groups. A modal group is a set of G-codes where only one can be active at a time. For example, G0, G1, G2, G3 are all in the same modal group (group 1 - motion modes).
Register your G-code in the gees[] array:
// From interp_array.cc
const int Interp::gees[] = {
/* 0 */ 1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // G0-G9
/* 10 */ 1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // G10-G19
/* 20 */ 1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // G20-G29
/* 30 */ 1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // G30-G39
/* 40 */ 1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // G40-G49
// ...
};The index is the G-code value (remember, multiplied by 10). The value is the modal group number:
-1means undefined/illegal0= Non-modal group1= Motion group2= Plane selection (G17, G18, G19)- etc.
Modal Groups:
Group 0: Non-modal (G4, G10, G28, G30, G52, G53, G92, G92.1, G92.2, G92.3)
Group 1: Motion (G0, G1, G2, G3, G33, G33.1, G38.2-G38.5, canned cycles)
Group 2: Plane (G17, G18, G19)
Group 3: Distance mode (G90, G91)
Group 4: Arc distance mode (G90.1, G91.1)
Group 5: Length units (G20, G21)
Group 6: Cutter compensation (G40, G41, G42)
Group 7: Tool length offset (G43, G49)
Group 8: Coordinate system (G54-G59.3)
Group 9: Feed mode (G93, G94, G95)
Group 10: Retract mode (G98, G99)
...
Step 3: Add Parser Function (if needed)
File: src/emc/rs274ngc/interp_read.cc
If your G-code has parameters (X, Y, Z, I, J, etc.), you may need to add parsing logic. Most parameters are already parsed, but if you need custom parsing:
int Interp::read_my_word(char *line, // G-code line being parsed
int *counter, // Position in line
block_pointer block, // Block being filled
double *parameters) // Parameter array
{
CHKS((line[*counter] != 'm'), NCE_BUG_FUNCTION_SHOULD_NOT_HAVE_BEEN_CALLED);
*counter = (*counter + 1);
// Parse the value (e.g., numeric value)
double value;
CHP(read_real_value(line, counter, &value, parameters));
// Validate
CHKS((value < 0.0), NCE_NEGATIVE_VALUE);
// Store in block
block->my_value = value;
block->my_flag = true;
return INTERP_OK;
}File: src/emc/rs274ngc/interp_read_common.cc
Register the new word parser in read_items() function if it's a new word letter:
int Interp::read_items(block_pointer block, const char *blocktext, double *parameters)
{
// ... existing code ...
else if (blocktext[counter] == 'm') // Your word letter
CHP(read_my_word(blocktext, &counter, block, parameters));
}Step 4: Implement Conversion Function
File: src/emc/rs274ngc/interp_convert.cc
This is where the actual G-code logic is implemented:
int Interp::convert_my_gcode(int g_code,
block_pointer block,
setup_pointer settings)
{
// Validate input
CHKS((settings->cutter_comp_side != CUTTER_COMP::OFF),
_("Cannot use G-code with cutter radius compensation on"));
// Extract parameters from block
double x = block->x_number;
double y = block->y_number;
// Perform calculations or state updates
// ...
// Update machine state
settings->current_x = new_x;
settings->current_y = new_y;
// Queue canonical motion command (if motion-related)
STRAIGHT_FEED(block->line_number, new_x, new_y, new_z,
AA_end, BB_end, CC_end, u_end, v_end, w_end);
// Update motion mode
settings->motion_mode = g_code;
return INTERP_OK;
}File: src/emc/rs274ngc/interp_convert.cc
Register the conversion function in convert_g():
int Interp::convert_g(block_pointer block, setup_pointer settings)
{
int status;
// ... existing code ...
// For non-modal G-codes in group 0:
if ((block->g_modes[GM_MODAL_0] == G_MY_CODE) && ONCE(STEP_MODAL_0)) {
status = convert_my_gcode(block->g_modes[GM_MODAL_0], block, settings);
CHP(status);
}
// For motion codes (group 1):
if ((block->motion_to_be != -1) && ONCE(STEP_MOTION)) {
status = convert_motion(block->motion_to_be, block, settings);
CHP(status);
}
return INTERP_OK;
}Step 5: Integrate with Motion Conversion
File: src/emc/rs274ngc/interp_convert.cc
If your G-code is a motion command (G0, G1, G2, G3, etc.), integrate it into convert_motion():
int Interp::convert_motion(int motion, block_pointer block, setup_pointer settings)
{
// ... existing checks ...
if ((motion == G_0) || (motion == G_1) || (motion == G_MY_MOTION)) {
CHP(convert_straight(motion, block, settings));
}
else if (motion == G_MY_ARC_CODE) {
CHP(convert_arc(motion, block, settings));
}
else if (is_a_cycle(motion)) {
CHP(convert_cycle(motion, block, settings));
}
return INTERP_OK;
}Step 6: Add Canonical Function (if needed)
File: src/emc/task/emccanon.cc or src/emc/rs274ngc/gcodemodule.cc
If your G-code requires a canonical function call to the motion layer:
void MY_GCODE_COMMAND(int line_number, double x, double y, double z)
{
// Create message structure
auto msg = std::make_unique<EMC_TRAJ_MY_GCODE_COMMAND>();
msg->line_number = line_number;
msg->x = x;
msg->y = y;
msg->z = z;
// Send to motion queue
emcmotQueue->write(*msg);
}Step 7: Update Documentation
File: docs/src/gcode/g-code.adoc
Document the new G-code for users:
[[sec:G-MyCode]]
=== G-MyCode
G-MyCode is a new command that does something useful.
Syntax::
G-MyCode X- Y- Z- <P->
Parameters::
X- : X position
Y- : Y position
Z- : Z position
P- : (optional) Some parameter
Example::
G-MyCode X10 Y20 Z5
G-MyCode X10 Y20 P1.5Concrete Example: Adding G43.3 (Dynamic Tool Offset)
Here's a complete example of adding a hypothetical G43.3 (dynamic/temporary tool offset):
Step 1: Header (interp_internal.hh)
enum GCodes {
// ... existing codes ...
G_43_3 = 433, // Dynamic tool offset
};Step 2: Modal Group (interp_array.cc)
// In the gees array, position 433:
/* 430 */ 7, 7, 7, -1, -1, -1, -1, -1, -1, -1,
// ^G43.0 (=430) G43.1 (=431) G43.2 (=432) G43.3 (=433)Modal group 7 is for tool length offset.
Step 3: Declaration (rs274ngc_interp.hh)
int convert_tool_length_offset(int g_code, block_pointer block, setup_pointer settings);Step 4: Implementation (interp_convert.cc)
int Interp::convert_tool_length_offset(int g_code, block_pointer block, setup_pointer settings)
{
EmcPose tool_offset;
ZERO_EMC_POSE(tool_offset);
if (g_code == G_43_3) {
// G43.3: Apply dynamic offsets from block parameters
if (block->x_flag) tool_offset.tran.x = block->x_number;
if (block->y_flag) tool_offset.tran.y = block->y_number;
if (block->z_flag) tool_offset.tran.z = block->z_number;
// ... handle other axes ...
}
// Apply the offset
USE_TOOL_LENGTH_OFFSET(tool_offset);
// Update current position
settings->current_x += (settings->tool_offset.tran.x - tool_offset.tran.x);
settings->current_y += (settings->tool_offset.tran.y - tool_offset.tran.y);
settings->current_z += (settings->tool_offset.tran.z - tool_offset.tran.z);
settings->tool_offset = tool_offset;
return INTERP_OK;
}Step 5: Registration (interp_convert.cc)
In convert_g():
if ((block->g_modes[GM_TOOL_LENGTH_OFFSET] != -1) && ONCE(STEP_TOOL_LENGTH_OFFSET)) {
status = convert_tool_length_offset(
block->g_modes[GM_TOOL_LENGTH_OFFSET],
block,
settings);
CHP(status);
}Step 6: Documentation (g-code.adoc)
[[sec:G43.3]]
=== G43.3
G43.3 applies dynamic (temporary) tool offsets without changing the tool table.
Syntax::
G43.3 X- Y- Z- A- B- C-
Parameters::
X-, Y-, Z- : Linear axis offsets
A-, B-, C- : Rotary axis offsets
Example::
G43.3 X0.1 Y-0.05 Z0.2Testing Your G-Code
After implementation, test your new G-code:
- Unit Tests - Add tests in
unit_tests/interp/ - MDI Testing - Test via the GUI MDI interface
- File Testing - Test in G-code files
- Edge Cases - Test with cutter comp, unit changes, etc.
Key Patterns and Macros
Error Checking Macros
CHKS(condition, error_code, format_args) // Check and return error
CHP(status) // Check previous result and propagate errorBlock Structure Fields
block->x_flag, block->x_number // X axis flag and value
block->y_flag, block->y_number // Y axis flag and value
block->f_flag, block->f_number // Feed rate flag and value
block->g_modes[group_index] // G-code modal valuesSettings Structure Fields
settings->current_x, current_y, current_z // Current position
settings->feed_rate // Current feed rate
settings->plane // Active plane (XY, XZ, YZ)
settings->distance_mode // Absolute or incremental
settings->cutter_comp_side // Cutter comp on/off/left/right
settings->motion_mode // Current motion modeImportant Considerations
- Modal Groups - Only one G-code per modal group can be active at once
- Coordinate Systems - Account for active coordinate system offsets
- Unit Conversions - Handle both metric and imperial units
- Cutter Compensation - Verify compatibility or add checks
- State Management - Update
settingsappropriately - Canon Functions - Queue canonical functions for motion layer
- Parameter Tracking - Store state in
settingsandblockstructures
References
- Enumeration Definitions:
src/emc/rs274ngc/interp_internal.hh - Modal Groups:
src/emc/rs274ngc/interp_array.cc - Parser Functions:
src/emc/rs274ngc/interp_read.cc - Conversion Functions:
src/emc/rs274ngc/interp_convert.cc - Example G-Codes: G2/G3 (arcs), G28/G30 (return to ref), G81-G89 (canned cycles)
This comprehensive guide should help you add new G-codes to LinuxCNC!