I run into a few questions with the general theme of “Why does my printer do ________?”
As long as the printer is healthy, then the printer is simply doing whatever it was instructed to do. So the answer to the above question is “because you told it to do it.” (Or probably because your slicer software told it to do it.)
This article will briefly touch on the workflow (how you get from part design to a printable file) but mostly spend time talking about what’s inside a g-code file and how to read it … and how to edit the instructions … should you have the need.
The 3D design and printing workflow
The general process for 3D printing is:
- Create a part (typically in CAD or Computer Aided Design).
There are loads of 3D CAD applications on the market. Some free. Some very expensive. Some are great for mechanical purposes (designing a mechanical part). Some are great for artistic purposes (creating a sculpture).
The file format of these CAD files is typically unique to the CAD program used to design the part (although there are graphics interchange formats that allow users to share CAD files with users who use a different program.) The file format of one CAD application may not be directly readable by another. More important to the topic of the article… your 3D printer can’t use these files directly.
- Turn the part into a mesh
The printer can’t use the CAD file. It needs to be converted into something the printer can use. But there is a problem… each printer has it’s own nuances. What type of 3D printer is it? How fine is the print-head? What types of materials can it handle? The settings to print a file using a filament such as PLA would not the same as the settings to print the same part using TPU (flexible filaments).
So rather than going straight to a print-ready file… the CAD application creates an intermediate format. This file will be a “mesh” which is a generic representation of the geometry of your part (it represents all the surfaces in your part).
You can think of this as a kind of wireframe of your part… except it’s constructed of lots of small tiles. While the tiles could theoretically be any shape… they are almost always triangles. This structure made out of lots of tiny flat triangles are used to build up the shape of your part surfaces… but are stored in a format that is ready for slicing (but not yet ready for printing).
The most common format are .STL files (Stereo Lithography). But there are other formats such as .OBJ and .3MF and several others. .OBJ and .3MF are newer formats and a bit more sophisticated than .STL … which is a more basic format. .STL is the most common in part because it is the oldest… and also in part because loads of software understands the .STL file format … but programs that support more advanced formats such as .OBJ or .3MF files is not quite as widespread. (Cura understands all of these formats). The differences in formats is beyond the scope of this article.
- Turn the mesh into G-codes
This is the step that ultimately produces a print-ready file.
To do this, you will need a “slicer” program. Cura LulzBot Edition is open source and included with your LulzBot printer. But you could use a number of other slicing programs. The advantage of using the “LulzBot Edition” of Cura is that it comes with built-in printer profiles and materials profiles that ready for your printer.
In other words… if you are new to 3D printing, using this software and using a filament based on the list recommended by Cura will speed you past several steps and get you printing on your printing using materials that have been tested and have recommended print settings so you wont have to work those out yourself. (Note: Cura divides these filaments into categories with beginner, intermediate, and expert levels. Filaments have “personality” and some of them are a bit fussy to print and require a bit more coaxing on your part to make them behave. These are in the “expert” category. So if you are completely new and just want to keep things easy as you get started, best to start with less fussy filaments such as PLA.)
As you gain experience and realize how the slicing software works and how the printer behaves with various settings, you experiment with other filaments – for which you don’t have a profile … or even other slicing software.
If you open a part in Cura, let it “slice” the part, then tell Cura to save the output to a file, it will create a “.gcode” file.
If you make any changes to your settings in Cura (e.g. layer heights, temperatures, print-speeds, etc. etc.) then Cura will need to regenerate a new g-code files.
The rest of this article is all about those .gcode files.
What are G-codes
This is the language used to control 3D printers. But that understates things a bit. It’s also the same language used to control computer numerical control (CNC) machines such as milling machines, lathes, etc. in industrial manufacturing. The language has been around for decades and its use in the world of 3D printing is simply yet another application for the g-code format. It was not specifically created for 3D printing. This is important to know because as you learn more about G-codes, you may start to notice instructions that set the spindle speeds and other settings that don’t make sense for a FDM 3D printer … given that it doesn’t have a spindle. Those codes exist because the same language is used to control machines that have those capabilities.
All of these machines operate in 3D geometric space and locations are expressed in a simple X,Y,Z cartesian coordinate system … just like you would have learned in primary school geometry class. There is no advanced math.
G-codes are human-readable. The file is simple text … not binary. This means you can open a .gcode file with a plain text editor and read it to see what it does. (Make sure that if you edit a .gcode file, you do so with a plain text editor … not a word-processor anything that might try to alter the formatting or introduce special characters. G-code files must be plain ASCII text files or your printer will be confused.)
If you open a G-code file, you’ll notice that there are a lot of lines that begin with the letter “G”. But you’ll also notice lines that begin with the letter “M”. I’ll try to sort those out … but be aware that the rules aren’t strict so there is some occasional blurring of the meanings.
Generally speaking, the “G” codes are go-to instructions (most of them result in the machine performing some movement.) As you might expect, if you tell the machine tool to “go” somewhere, you have to give it a destination … coordinates (in X, Y, Z values). This is not absolutely true… there are a few “G” commands that do not result in the machine moving something.
With that… take a look at this reference document: http://marlinfw.org/meta/gcode/
This is a reference of the G-codes used by Marlin firmware (the firmware running inside your printer). Marlin interprets the codes and controls the motors or electronics accordingly to fulfill the instructions.
For example, if the printer is operating using metric values (and usually it is, although you can tell it to operate in imperial units instead), then a command such as
G0 X10 Y12.5 Z.75
Would send the print head to the X,Y,Z coordinate of X=10mm, Y=12.5mm, and Z-height=0.75mm. Both the G0 and G1 commands order the printer to perform a linear move and both work the same way (I’ll explain why there are two commands to do the same thing a bit later. I will ignore G1 for now and just use G0 for the next few examples.)
Note that decimal values are permitted (and usually would be used but I’m keeping to simple examples.)
Also note that this presumes the print is operating in “absolute” position mode. It can also be instructed to move in “relative” mode. In relative mode, this would move the toolhead 10mm to the right, 25mm back, and 5mm up from the current position (and also negative values can be used).
We could leave out a value (such as the Z value) and this would result in moving the print-head in X & Y … but the Z value would remain the same.
There are optional values that can be included … such as the number of millimeters of filament to extrude while moving from the start point to the end point. (expressed as the E axis value … E being “Extruder”) There is also an F value … the “Feed” rate. This is how fast you want the tool head to move … typically expressed in millimeters per minute (not per second). Notice we didn’t mention an “F” value (feed-rate). I’ll get to this later.
But if we did a move such as:
G0 X5 Y5 F600
This command would perform the move at 600mm per minute (or 10mm per second). For simplicity I’ll ignore feed-rates (because we can) but will re-introduce them again later.
The following bit of code would cause the toolhead to trace out a square (without extruding any filament)
G0 X0 Y0 Z100 ; move to X=0mm, Y=0mm, Z=100mm above the print surface) G0 X10 Y0 ; move to X=10mm, Y=0mm but leave Z alone G0 X10 Y10 ; move to X=10mm, Y=10mm G0 X0 Y10 ; move to X=0mm, Y=10mm G0 X0 Y0 ; move back to X=0mm, Y=0mm
The toolhead always moves in “straight” lines when using either G0 or G1 commands. This means if the next command were to be:
G0 X10 Y10
The toolhead would move diagonally form X0, Y0, to X10, Y10.
To perform these moves, the computer (Arduino) inside the printer does a bit of math to translate the coordinates into step pulses that will be sent to the stepper motors. How many pulses are required to perform a move depends on the printer and defaults were factory calibrated (but you can tune it).
If the next command were to be:
G0 X20 Y50
The computer in your printer will automatically calculate the number of steps to send to each axis (linear interpolation) so that the toolhead will follow a “straight” path at the desired feedrate. The printer does this for you (Cura doesn’t need to do the math … it’s handled in the printer.)
I also introduced some comment text in some of those examples.
In G-code, anytime you see a semi-colon ( ; ) it means everything following the semi-colon is comment text. The printer will ignore that text. It is only in the file in case a human wants to read it.
If the semi-colon occurs at the start of the line, then the whole line is a comment.
If the semi-colon occurs somewhere else in the line, then everything left of the comment will be interpreted as g-code instruction, and everything right of the semi-colon will be ignored and treated as comment text.
Cura uses these comments by default for several reasons. But ONE of those reasons… is to indicate which “Layer” it is printing.
Cura formats layers (which are comments) as
This is a simple comment that means it is starting layer # 25 on your part. This is useful because if you determine that something is happening on a specific layer and you want to understand why, you can quickly find that layer in the g-code file by searching for the comment.
This layer numbering convention is NOT an industry standard. Cura does this, but other slicing programs will have their own way of formatting the layer number comments. It’s not hard to work out how the comment is formatted … because layer #0 will typically be close to the front of the file and most programs at least include the word “layer” in the comment.
What about M codes
If you’ve been snooping in your .gcode files, you’ve probably noticed that while there are many “G” codes (and you can use that reference URL to find out what each of them mean), you will also see lots of “M” codes.
M are traditionally “modal” commands.
Most of the time a “G” code “lives in the moment”. E.g. if I use a g-code to move the print head, once the command has been performed by the printer… it’s done. The printer has no “memory” of the fact that it performed the command. It just is wherever it is now (the toolhead is in a new position and it doesn’t care how it got there.).
“M” commands are “modal” in that they change the “mode” or state of something… and the printer will remain in that state until something tells it to change to some other state.
A classic example is the temperature of the hot-end.
Up to now, I’ve mentioned how to move the tool-head using the G0 command … and I even mentioned that you can tell the printer to extrude filament while moving (so it is “printing”) or to not extrude while moving (so it is “moving”). There is even an optional parameter that sets the rate of that particular move. But nothing about the temperature of the hot-end while extruding… that’s because temperature is a “modal” command.
Once you set the temperature… it will remain at that temperature until you tell it to change to some new value (or shutdown the printer.)
Temperature of the hot-end is set with the “M104” command.
The above command would set the temperature of the hot-end to 170°C.
Interestingly… M104 doesn’t “wait” for the temperature to be reached. It changes the temperature and immediately continues to execute more g-codes. This might be a problem if the hot-end was cold (say 20°C) and you need to print some material at 220°C. You wouldn’t want to do an “M104 S220” and keep going … you would need to wait until the hot-end reaches a temperature where it can melt the filament before it starts printing.
If you want the printer to WAIT (stop executing g-codes until the temperature is reached) then you would use M109 instead of M104.
M109 S220 ; set hot-end to 220°C and wait until temperature is reached before continuing
Why do both G0 and G1 have the same meaning? What’s the difference?
The answer is simple… none. But while this answers one question, it creates others… if there’s no difference than why have two commands?
Recall earlier that I mentioned that “G” commands “live in the moment”. Once the command is executed, there is no “memory” of the command. I also mentioned the “M” commands were “modal” (they change the mode of something … permanently – or at least until a new M command comes along to change the state again).
But I also mentioned that there are a few exceptions that blur the lines between G vs. M commands. It turns out G0 vs. G1 is one of those areas where we blur the lines. This has to do with Feed rates.
I mentioned that you can set an optional F value on the G0 or G1 command. It turns out you can specify a G0 or G1 command… without any coordinate values at all (no X, Y, or Z). This is a G command that does not result in the machine moving anything.
Here’s an example:
Why have a feed rate if the tool head isn’t going to move anywhere? This command sets the Feed rate for all subsequent moves of the G0 command to 2400mm per minute (which works out to 40mm per second). So this becomes the new default feed-rate (at least until a new G0 F___ command is encountered to set some new speed.)
All G0 commands that simply have X, Y, Z values but no “F” value … will use this speed. In other words the Feed rate becomes “modal” even though this was set with a “G” command and not with an “M” command. It’s a little nuance where the lines are blurred but an important one.
Now that you know about this little trick of the feed rate, I can talk about that G1 command.
Suppose you want all the “printing” moves to occur at some speed that you’ve optimized for your filament (suppose that speed is 40mm/sec).
Also suppose that when NOT laying down filament, you want the printer to move at a much faster rate… say 120mm/sec.)
You may have noticed that your 3D printer is rapidly changing from “printing” (laying down filament) to “moving” (not laying down filament). So it would be a bit cumbersome to have to keep setting a “G0 F___” command to change the speed each time you alternate between printing and moving.
THIS is why there’s a G1 command. You can use one of these commands (say G0) for non-printing moves, and use the other command (say G1) for printing moves.
G0 F7200 ; set G0 speed to 120mm/sec for non-printing moves G1 F2400 ; set G1 speed to 40mm/sec for printing moves
This way anytime you want the printer to “move” you order it with G0 and anytime you want it to “print” you order it with G1 (and supply an extrusion amount … otherwise it wont lay down filament as it moves).
Note that the decision in my example to use G0 as a “move” command and G1 as a “print” command is completely arbitrary. I could just as easily produce G-code where G0 is used to “print” and G1 is used to “move”.
This is why there is technically no difference between G0 and G1. It just gives you a way to have two different movement rates and switch between them without having to keep re-setting the F value again and again each time you toggle from printing to moving.
Why do curved sides of my part look like lots of flat/straight segments?
You may wonder if your printer is defective or requires some calibration when you noticed that “round” sections of a part may not be printing very “round” … but look like a polygon with lots of short straight segments.
In the above examples I used the G0 and the common reference command to “go to” a new coordinate (and could have just as well used G1). I also mentioned that these commands cause the toolhead to move in a “straight” path. It will not follow a curve.
It turns out the machine can follow a curve … but you wont see this very often in your .gcode files. This is more to do with how the slicing software works than how the printer works.
How to generate Arcs in G-code
I wont spend as much time here because this isn’t something you’ll likely find in your g-code files (for reasons that are about to be made clear).
The printer does have commands to produce arcs. These are the G2 and G3 commands.
Unlike G0 and G1 (which have no difference), G2 and G3 are the same command but they do have a key difference … which is the direction of the arc.
G2 moves the toolhead from its current position to a new position … around some center point (using I and J instead of X and Y) or using some radius R. But G2 will move to that new position tracing a clockwise arc.
G3 is the same command… but moves counter-clockwise.
If a new X/Y position is not provided … it will trace a complete circle (rather than just an arc segment).
So if the tool-head is currently at X90 Y100 Z50 then
G2 X100 Y100 R15 ; trace a clockwise arc to the new position with radius 15mm
This would cause the toolhead to arc clockwise (so it will start moving back and around) to the destination coordinate (10mm to the right of where it started) but with a 15mm radius.
Meanwhile if we started at the same origin but used G3 instead, then
G3 X100 Y100 R15; trace a counter-clockwise arc to the new position with radius 15mm
This will cause the toolhead to arc counter-clockwise (so it will start moving forward and around … instead of backward and around) but still with a radius of 15mm. In other words it will take a completely different path.
There is still an F value (Feed rate) even though the Marlin reference seems to have omitted this (I assume this is a documentation error).
This is also an interesting “P” value. This is used to draw a helix (e.g. if I wanted to draw 10 circles … while slowly increasing the Z axis)
While the printer supports the notion of arcs, you generally wont find them in most gcode jobs.
Recall waaaay back at the beginning of this article when the “workflow” to produce a printable part started with CAD which then turned the part into a “mesh” of little flat triangles?
The slicer software takes that mesh… and slices it into horizontal “layers”. The slicer software generates the print-path for each layer… and anywhere there is a wall/surface, it just follows the shapes of all those flat little triangles (well… the path of triangles within the “plane” of the current layer it is printing.)
In other words, the slicing software is really generating lots of “straight” moves and really doesn’t have an option to generate arc-shaped moves.
If you scale a part up on the build-plate… these straight segments are likely to be even more noticeable.
When you only feed the slicer software data that has flat segments (all those meshed tiles are flat) then it has no option to produce actual curved moves.
So how do you fix this? You generate a mesh that has a much higher tile count. But this implies you had access to the original file used to produce the mesh (which you may not have if you’re download a part created by someone else). But if you are creating your own meshed files from your own CAD, then this is something you can control in the CAD software.
You can use this information both to understand what your printer is doing … and why.
You can also us this information to understand how to tweak your gcode.
Suppose you want to print a part using a printer that has a single toolhead… but you want to change filament colors are various layer heights.
You could search your G-code to find the layer numbers where you want to change filaments, then inject the “M600” command (M600 is the filament change command). On a LulzBot printer, the firmware typically has some built-in moves that it will automatically perform when it encounters M600.
When I do this on my printer, it
- remembers it’s current position.
- raises the Z height slightly
- moves the X & Y axis away from the part (toward the back of the bed)
- ejects the current filament
- prompts the user to insert new filament
- primes and purges the filament (you extrude a bit until the extruded filament comes out nicely in the new color) You can tell it to purge a little more if the new color isn’t coming out clean
- when you tell the filament change is completed, it will move back to the position it remembered at step 1 … and resumes printing.
You could also tweak g-codes to insert temperature changes throughout the part.
Do edits only in a plain-text editor (do not use a word processor or anything that may insert special characters in the file … your printer will NOT like those.)
Depending on how you print, you can also send g-code commands to your printer on-the-fly… e.g. suppose you realize that your part cooling fan is running a bit fast and you need to slow it down to avoid warping… you could use the fan-speed command (M106). Fan speeds are not based on percentage… but based on the range of 0 (off) to 255 (max speed). e.g. to achieve 20% fan speed you would do:
M106 S51 ; set fan speed to 20%
This is because 255 x .2 = 51
This sort of thing doesn’t require editing the g-code with a text editor because you can use the “console” feature of Cura to send commands directly to the printer … even if it is in the middle of a print (it will slip the command into the buffer that it is streaming to the printer). Note that the command wont get executed instantly… the printer will likely have a few commands queued up in its internal buffer and will execute your command as soon as it completes any commands that were already queued ahead of your command.
A word of caution
You do want to avoid damaging your printer or your parts. For this reason, double check commands before you send them. It would not be advisable to send the tool-head crashing into the bed at high-speed, etc. The printer will simply do whatever you order it to do. It does not check to make sure your commands are ‘safe’ before it performs them.
I’ve tried to avoid errors… I’ve already found and fixed many of them. But I’m just betting I haven’t caught them all. If you catch any typos, errors (grammatical errors or just really poor phrasing), or factually incorrect errors, or even if something just wasn’t very clear – please let me know and I’ll be happy to make corrections as needed.