G-code
Introduction
This page describes the G Codes that the RepRap firmware uses, what they mean, and how they work.
The list of what can be done is extensible. But check here first, and add your extension here first before you implement it.
A typical piece of GCode as sent to a RepRap machine might look like this:
N3 T0 *86 N4 G92 E0 *102 N5 G28 *13 N6 G1 F1500.0 *117 N7 G1 X2.0 Y2.0 F3000.0 *71 N8 G1 X3.0 Y3.0 *39
RepRap G Code Fields
This section explains the letter-preceded fields. The numbers in the fields are represented by nnn. Numbers can be integers, or can contain a decimal point, depending on context. For example clearly an X coordinate can be integer or fractional, whereas selecting extruder number 2.76 makes no sense.
In the section below, individual commands are distinguished.
Letter | Meaning |
---|---|
Gnnn | Standard GCode command, such as move to a point |
Mnnn | RepRap-defined command, such as turn on a cooling fan |
Tnnn | Select tool nnn. In RepRap, tools are extruders |
Snnn | Command parameter, such as the voltage to send to a motor |
Pnnn | Command parameter, such as a time in milliseconds |
Xnnn | An X coordinate, usually to move to |
Ynnn | A Y coordinate, usually to move to |
Znnn | A Z coordinate, usually to move to |
Innn | Parameter - not currently used |
Jnnn | Parameter - not currently used |
Fnnn | Feedrate in mm per minute. |
Rnnn | Parameter - not currently used |
Qnnn | Parameter - not currently used |
Ennn | Length of extrudate in mm. This is exactly like X, Y and Z, but for the length of filament to extrude. |
Nnnn | Line number. Used to request repeat transmission in the case of communications errors. |
*nnn | Checksum. Used to check for communications errors. |
Checksums for a GCode cmd (including its line number) are computed as follows:
for(int i = 0; i < cmd.length(); i++) cs = cs ^ cmd.charAt(i); cs &= 0xff;
and the value is appended to the command after a '*' character.
//the character / means delete block... used for comments and stuff. if (instruction[0] == '/') return;
float fr;
fp.x = 0.0; fp.y = 0.0; fp.z = 0.0;
fp.e = 0.0; fp.f = 0.0;
//get all our parameters! parse_string(&gc, instruction, size);
// Do we have lineNr and checksums in this gcode? if((bool)(gc.seen & GCODE_CHECKSUM) | (bool)(gc.seen & GCODE_N)) { // Check that if recieved a L code, we also got a C code. If not, one of them has been lost, and we have to reset queue if( (bool)(gc.seen & GCODE_CHECKSUM) != (bool)(gc.seen & GCODE_N) ) { if(SendDebug & DEBUG_ERRORS) Serial.println("Serial Error:Recieved a LineNr code without a Checksum code or Checksum without LineNr"); FlushSerialRequestResend(); return; } // Check checksum of this string. Flush buffers and re-request line of error is found if(gc.seen & GCODE_CHECKSUM) // if we recieved a line nr, we know we also recieved a Checksum, so check it { // Calc checksum. byte checksum = 0; byte count=0; while(instruction[count] != '*') checksum = checksum^instruction[count++]; // Check checksum. if(gc.Checksum != (int)checksum) { if(SendDebug & DEBUG_ERRORS) Serial.println("Serial Error: checksum mismatch"); FlushSerialRequestResend(); return; } // Check that this lineNr is LastLineNrRecieved+1. If not, flush if(!( (bool)(gc.seen & GCODE_M) && gc.M == 110)) // unless this is a reset-lineNr command if(gc.N != gc.LastLineNrRecieved+1) { if(SendDebug & DEBUG_ERRORS) Serial.println("Serial Error: LineNr is not the last lineNr+1"); FlushSerialRequestResend(); return; } //If we reach this point, communication is a succes, update our "last good line nr" and continue gc.LastLineNrRecieved = gc.N; } }
/* if no command was seen, but parameters were, then use the last G code as
* the current command
*/
if ((!(gc.seen & (GCODE_G | GCODE_M | GCODE_T))) && ((gc.seen != 0) && (last_gcode_g >= 0)))
{
/* yes - so use the previous command with the new parameters */
gc.G = last_gcode_g;
gc.seen |= GCODE_G;
}
//did we get a gcode?
if (gc.seen & GCODE_G)
{
last_gcode_g = gc.G; /* remember this for future instructions */ fp = where_i_am; if (abs_mode) { if (gc.seen & GCODE_X) fp.x = gc.X; if (gc.seen & GCODE_Y) fp.y = gc.Y; if (gc.seen & GCODE_Z) fp.z = gc.Z; if (gc.seen & GCODE_E) fp.e = gc.E; } else { if (gc.seen & GCODE_X) fp.x += gc.X; if (gc.seen & GCODE_Y) fp.y += gc.Y; if (gc.seen & GCODE_Z) fp.z += gc.Z; if (gc.seen & GCODE_E) fp.e += gc.E; }
// Get feedrate if supplied - feedrates are always absolute??? if ( gc.seen & GCODE_F ) fp.f = gc.F;
// Process the buffered move commands first // If we get one, return immediately
switch (gc.G)
{
//Rapid move case 0:
fr = fp.f; fp.f = FAST_XY_FEEDRATE; qMove(fp); fp.f = fr; return; // Controlled move
case 1:
qMove(fp); return; //go home.
case 28:
where_i_am.f = SLOW_XY_FEEDRATE; specialMoveX(where_i_am.x - 5, FAST_XY_FEEDRATE); specialMoveX(where_i_am.x - 250, FAST_XY_FEEDRATE); where_i_am.x = 0; where_i_am.f = SLOW_XY_FEEDRATE; specialMoveX(where_i_am.x + 1, SLOW_XY_FEEDRATE); specialMoveX(where_i_am.x - 10, SLOW_XY_FEEDRATE); where_i_am.x = 0; specialMoveY(where_i_am.y - 5, FAST_XY_FEEDRATE); specialMoveY(where_i_am.y - 250, FAST_XY_FEEDRATE); where_i_am.y = 0; where_i_am.f = SLOW_XY_FEEDRATE; specialMoveY(where_i_am.y + 1, SLOW_XY_FEEDRATE); specialMoveY(where_i_am.y - 10, SLOW_XY_FEEDRATE); where_i_am.y = 0; where_i_am.f = SLOW_Z_FEEDRATE; specialMoveZ(where_i_am.z - 0.5, FAST_Z_FEEDRATE); specialMoveZ(where_i_am.z - 250, FAST_Z_FEEDRATE); where_i_am.z = 0; where_i_am.f = SLOW_Z_FEEDRATE; specialMoveZ(where_i_am.z + 1, SLOW_Z_FEEDRATE); specialMoveZ(where_i_am.z - 2, SLOW_Z_FEEDRATE); where_i_am.z = 0; where_i_am.f = SLOW_XY_FEEDRATE; // Most sensible feedrate to leave it in
return;
default: break; }
// Non-buffered G commands
// Wait till the buffer q is empty first while(!qEmpty()) delay(WAITING_DELAY); //delay(2*WAITING_DELAY); // For luck
switch (gc.G) {
//Dwell
case 4: delay((int)(gc.P + 0.5)); break;
//Inches for Units case 20:
setUnits(false);
break;
//mm for Units case 21:
setUnits(true);
break;
//Absolute Positioning case 90: abs_mode = true; break;
//Incremental Positioning case 91: abs_mode = false; break;
//Set position as fp case 92:
setPosition(fp);
break;
default: if(SendDebug & DEBUG_ERRORS)
{ Serial.print("huh? G");
Serial.println(gc.G, DEC);
FlushSerialRequestResend(); }
} }
//find us an m code.
if (gc.seen & GCODE_M)
{
// Wait till the q is empty first while(!qEmpty()) delay(WAITING_DELAY); //delay(2*WAITING_DELAY);
switch (gc.M) { //TODO: this is a bug because search_string returns 0. gotta fix that. case 0: break; /* case 0: //todo: stop program break;
case 1: //todo: optional stop break;
case 2: //todo: program end break; */
// Now, with E codes, there is no longer any idea of turning the extruder on or off. // (But see valve on/off below.)
/* //turn extruder on, forward case 101: ex[extruder_in_use]->setDirection(1); ex[extruder_in_use]->setSpeed(extruder_speed); break;
//turn extruder on, reverse case 102: ex[extruder_in_use]->setDirection(0); ex[extruder_in_use]->setSpeed(extruder_speed); break;
//turn extruder off
- /
//custom code for temperature control case 104: if (gc.seen & GCODE_S) { ex[extruder_in_use]->setTemperature((int)gc.S); } break;
//custom code for temperature reading case 105: Serial.print("T:"); Serial.println(ex[extruder_in_use]->getTemperature()); break;
//turn fan on case 106: ex[extruder_in_use]->setCooler(255); break;
//turn fan off case 107: ex[extruder_in_use]->setCooler(0); break;
//set PWM to extruder stepper case 108:
- if MOTHERBOARD > 1
if (gc.seen & GCODE_S)
ex[extruder_in_use]->setPWM((int)(255.0*gc.S + 0.5));
- endif
break;
// Set the temperature and wait for it to get there
case 109: ex[extruder_in_use]->setTemperature((int)gc.S);
ex[extruder_in_use]->waitForTemperature();
break;
// Starting a new print, reset the gc.LastLineNrRecieved counter
case 110: if (gc.seen & GCODE_N) { gc.LastLineNrRecieved = gc.N;
if(SendDebug & DEBUG_INFO) Serial.println("DEBUG:LineNr set");
} break; case 111: SendDebug = gc.S; break; case 112: // STOP!
{ int a=50; while(a--) {blink(); delay(50);} }
cancelAndClearQueue(); break;
case 113: #if MOTHERBOARD > 1 ex[extruder_in_use]->usePotForMotor(); #endif
break;
// The valve (real, or virtual...) is now the way to control any extruder (such as // a pressurised paste extruder) that cannot move using E codes.
// Open the valve case 126: ex[extruder_in_use]->valveSet(true, (int)(gc.P + 0.5)); break; // Close the valve case 127: ex[extruder_in_use]->valveSet(false, (int)(gc.P + 0.5)); break;
default: if(SendDebug & DEBUG_ERRORS)
{ Serial.print("Huh? M");
Serial.println(gc.M, DEC);
FlushSerialRequestResend(); }
}
}
// Tool (i.e. extruder) change?
if (gc.seen & GCODE_T) { while(!qEmpty()) delay(WAITING_DELAY); //delay(2*WAITING_DELAY); newExtruder(gc.T); }