Learn to code: Make G-code with Processing (Part 1)

In the previous tutorial we have seen what G-code is, how it works and how you can write it by yourself on any text editor. In this tutorial we will start to automate the process using Processing. We will write some simple functions that will allow us to easily output G-code files. Notice that this can be obtained in many (and more sophisticated) ways, however here we will try to keep things as simple as possible, in order to give everyone the possibility to understand the concepts and start to make their own experiments. Let’s get started.

 

Processing is a programming language based on Java. It has been created with the goal of making programming accessible to anyone and to rapidly prototype sketches and ideas. If you have no experience with coding, this might be a good chance to start learning this great tool.

 

Setting up the G-code functions

 

Let’s open a new Processing sketch. In this first example we will only need the setup() function.

 

 

As we said last time, the G-code is a simple text file, so we will store the commands in an ArrayList of strings as a global variable:

ArrayList<String> gcode;

Let’s write a function that feeds this ArrayList with our G-code commands:

 

void gCommand(String command) {

 gcode.add(command);

}

Then we need a function to export our G-code:

void gExport() {
  //Create a unique name for the exported file
  String name_save = "gcode_"+day()+""+hour()+""+minute()+"_"+second()+".g";
  
  //Convert from ArrayList to array (required by saveString function)
  String[] arr_gcode = gcode.toArray(new String[gcode.size()]);
  
  // Export GCODE
  saveStrings(name_save, arr_t_gcode);
}

Let’s test in setup() what we have written so far:

void setup() {
  gcode = new ArrayList<String>();
 
  gCommand("G1 X3 Y20");
  gCommand("G1 X20 Y2 Z1");
  gExport(gcode);
}

Run your sketch by pressing CTRL+R, then press CTRL+K to open the sketch folder. You will find the G-code file that you have just generated with inside the two commands that we wrote.

Not bad, right? Let’s write two functions that will manage for us the beginning and the end of the print, as seen in the previous tutorial:

void startPrint(){
  gCommand("G91"); //Relative mode
  gCommand("G1 Z1"); //Up one millimeter
  gCommand("G28 X0 Y0"); //Home X and Y axes
  gCommand("G90"); //Absolute mode
  gCommand("G1 X117.5 Y125 F8000"); //Go to the center (modify according to your printer)
  gCommand("G28 Z0"); //Home Z axis
  gCommand("G1 Z0"); //Go to height 0
  gCommand("T0"); //Select extruder 1
  gCommand("G92 E0"); //Reset extruder position to 0
}
 
void endPrint(){
  gCommand("G91"); //Relative mode
  gCommand("G1 E-4 F3000"); //Retract filament to avoid filament drop on last layer
  gCommand("G1 X0 Y100 Z20"); //Facilitate object removal
  gCommand("G1 E4"); //Restore filament position
  gCommand("M 107"); //Turn fans off
}

Now we need a function able to calculate for us the amount of extrusion between two points.

Let’s first write some global variables:

float path_width = 0.4;
float layer_height = 0.2;

float filament_diameter = 1.75;

Let’s pre-compute some values, also as global variables:

float extruded_path_section = path_width * layer_height;

float filament_section = PI * sq(1.75/2.0f);

And now the extrusion function:

float extrude(PVector p1, PVector p2) {

 float points_distance = dist(p1.x, p1.y, p2.x, p2.y);

 float volume_extruded_path = extruded_path_section * points_distance;

 float length_extruded_path = volume_extruded_path / filament_section;

 return length_extruded_path;

}

And finally, some simple functions for setting the printing speed and enabling and disabling the fans:

void setSpeed(float speed) {

 gCommand("G1 F" + speed);

}



void enableFan() {

 gCommand("M 106");

}



void disableFan() {

 gCommand("M 107");

}

Now we have all what we need: a function for writing G-code, one for exporting it, two functions for setting up and finishing the print and one for calculating the amount of extrusion, plus commands for fans and speed.

A first example

 

Let’s use what we have written so far to print the cube from the first tutorial. This time we will use Absolute mode. Let’s add another two global variables for the center of our printing table (we will also update the startPrint() function accordingly:

float width_table = 235; //mm

float height_table = 250; //mm

float height_printer = 165; //mm



float x_center_table = width_table / 2.0f;

float y_center_table = height_table / 2.0f;



void startPrint() {

//...

 gCommand("G1 " + x_center_talbe + " " + y_center_table + " F8000"); //Go to the center

//...

}

And now, using a two for loops (one for the layers and one for the sides of the squares), we write inside of setup() what we need for our cube. You will now find the complete code for our sketch:

ArrayList<String> gcode;



float width_table = 235; //mm

float height_table = 250; //mm

float height_printer = 165; //mm



float x_center_table = width_table / 2.0f;

float y_center_table = height_table / 2.0f;



float path_width = 0.4;

float layer_height = 0.2;

float filament_diameter = 1.75;



float extruded_path_section = path_width * layer_height;

float filament_section = PI * sq(1.75/2.0f);



void setup() {

 gcode = new ArrayList<String>();



 startPrint();



 float length_side_cube = 10;



 float tot_layers = length_side_cube / layer_height;

 float current_z = 0;



 float extrusion = 0;

 float extrusion_multiplier = 1;



 float angle_increment = TWO_PI / 4.0f;

 PVector previous_point = new PVector();



 for (int layer = 0; layer<tot_layers; layer++) {



   current_z += layer_height;

   gCommand("G1 Z" + current_z);



   if (layer == 0) {

     // Slow speed and thick extrusion at the beginning of the print

     setSpeed(400);

     extrusion_multiplier = 2;

   } else if (layer == 2) {

     extrusion_multiplier = 1;

   } else if (layer == 3) {

     // Increase the speed

     setSpeed(1200);

     enableFan();

   }



   for (float angle = 0; angle<=TWO_PI; angle+=angle_increment) {



     float x = x_center_table + cos(angle) * length_side_cube;

     float y = y_center_table + sin(angle) * length_side_cube;



     PVector next_point = new PVector(x, y);



     if (layer == 0 && angle==0) {

       // Go to starting position

       gCommand("G1 X" + x + " Y" + y);

     } else {

       extrusion += (extrude(previous_point, next_point) * extrusion_multiplier);

       gCommand("G1 X" + x + " Y" + y + " E" + extrusion);

     }

     previous_point = next_point;

   }

 }



 endPrint();



 gExport();



 exit();

}





float extrude(PVector p1, PVector p2) {

 float points_distance = dist(p1.x, p1.y, p2.x, p2.y);

 float volume_extruded_path = extruded_path_section * points_distance;

 float length_extruded_path = volume_extruded_path / filament_section;

 return length_extruded_path;

}



void setSpeed(float speed) {

 gCommand("G1 F" + speed);

}



void enableFan() {

 gCommand("M 106");

}



void disableFan() {

 gCommand("M 107");

}



void startPrint() {

 gCommand("G91"); //Relative mode

 gCommand("G1 Z1"); //Up one millimeter

 gCommand("G28 X0 Y0"); //Home X and Y axes

 gCommand("G90"); //Absolute mode

 gCommand("G1 X" + x_center_table + " Y" + y_center_table + " F8000"); //Go to the center

 gCommand("G28 Z0"); //Home Z axis

 gCommand("G1 Z0"); //Go to height 0

 gCommand("T0"); //Select extruder 1

 gCommand("G92 E0"); //Reset extruder position to 0

}



void endPrint() {

 gCommand("G91"); //Relative mode

 gCommand("G1 E-4 F3000"); //Retract filament to avoid filament drop on last layer

 gCommand("G1 X0 Y100 Z20"); //Facilitate object removal

 gCommand("G1 E4"); //Restore filament position

 gCommand("M 107"); //Turn fans off

}



void gCommand(String command) {

 gcode.add(command);

}



void gExport() {

 //Create a unique name for the exported file

 String name_save = "gcode_"+day()+""+hour()+""+minute()+"_"+second()+".g";

 //Convert from ArrayList to array (required by saveString function)

 String[] arr_gcode = gcode.toArray(new String[gcode.size()]);

 // Export GCODE

 saveStrings(name_save, arr_gcode);

}

 

This example was simple and quite straightforward. Thanks to it you can already start to make some interesting experiments. However, if we decide to expand it, the code could get messy pretty soon. Furthermore, the structure is not very flexible and it would force us to rewrite a lot of code in case that we want to develop different projects.

That’s why, in the following tutorial, we will use the power of Object Oriented Programming (OOP) to create a more solid framework that you will be able to use and reuse in multiple occasions.

In the meantime, how about having a look at the Processing’s page? It’s full of useful tutorials and examples for you to learn.

See you next time!

Leave a Reply