How to make a DIY CNC Plotter machine using an Arduino Nano

How to make a DIY CNC Plotter machine using an Arduino Nano

Hello and welcome back. In this project, we will learn how to make a DIY CNC plotter machine using an Arduino Nano. For this project, I used two old computer DVD drives that were no longer in use. Therefore, we don’t need to buy expensive stepper motors and other CNC components, which makes this project very low-cost and beginner-friendly.

Using this CNC plotter machine, we can draw simple drawings, logos, text, and other basic designs. This project is a great way to learn about CNC machines, stepper motor control, and Arduino programming. You can convert any image to G-code using the Inkscape software. To make the project easier, I have also designed a custom PCB for this machine. Therefore, we don’t need any additional controller board.

In this project, I will show you the complete building process step by step, including the mechanical assembly, PCB soldering, circuit connections, Arduino programming, and software setup. Also, if you want to know the basic knowledge of these components, please read our previous tutorials.

Ok, let’s do this project step by step. The required components are given below.

Disclosure: These Amazon links are Affiliate links. As an Amazon Associate, I earn from qualifying purchases.

Step 1

First, let’s identify all the components required for this project.

Step 2

Secondly, we will order the PCB for this project.

How to Make a Joystick Controlled Robotic Arm Using an Arduino Nano Board
  • Click the “Instant Quote” button and upload the Gerber file, which you can download from the link below.
  • Gerber file – Download
  • For this project, I ordered five Purple PCBs. Next, select the build time and shipping method. Finally, click “Save to Cart” and complete the payment.

Step 3

Thirdly, let’s unbox the PCB package.

Step 4

“Next, solder all the components onto the PCB.”

Step 5

Now, let’s prepare the DVD drives as shown below. You can design it however you like.

Step 6

Next, cut a piece for mounting the pen. For that, I used a 5mm foam board piece.

Step 7

Now, install the pen on the piece above. For that, we need a small spring. First, insert the spring into the pen clip and mount it on the piece. Then, make a pen mounting bracket using an iron rod or any other material.

Step 8

Afterward, make a push rod for pen up and down movement. For that, I used an iron rod. Then mount the servo motor on the piece and connect the iron rod to the servo horn.

Step 9

Next, install the CNC bed on the lower stepper motor. For that, I used an iron plate. After that, install the pen-mounted part on the upper stepper motor.

Step 10

Now, connect the Arduino Nano board, L293D drivers, and 74HC595 shift register to the PCB.

Step 11

Then, connect the stepper motors and servo motor to the controller board. Connect the lower motor to the M3 and M4 terminals, and the upper motor to the M1 and M2 terminals. After, connect the Arduino Nano board to the computer.

Step12

Now, copy and paste the following program into the Arduino IDE. Also, you need to install the AFMotor library in the Arduino IDE.

#include <Servo.h>
#include <AFMotor.h>

AF_Stepper motory(48, 1);
AF_Stepper motorx(48, 2);
Servo servo;

#define penup 115
#define pendown 160
#define Spin 9

//Structures, global variables
struct point {
  float x;
  float y;
  float z;
};

//Current position of plothead
struct point actuatorPos;

//Drawing settings, should be OK
float StepInc = 1;
int StepDelay = 1;
int LineDelay = 0;

//Steps per width and height of the drawing area
float StepsPerMillimeterX = 250.0;
float StepsPerMillimeterY = 164.0;

//Max and min values of the drawing area(mm)
float Xmin = 0;
float Xmax = 38;
float Ymin = 0;
float Ymax = 38;
float Zmin = 0;
float Zmax = 1;

float Xpos = Xmin;
float Ypos = Ymin;
float Zpos = Zmax;

// Set to true to get debug output.
boolean verbose = false;


void setup() {
  Serial.begin( 9600 );
  servo.attach(Spin);
  servo.write(penup);
  delay(100);
  motorx.setSpeed(600);
  motory.setSpeed(600);
  Serial.println("Your CNC machine is ready...");
}

void loop() {
  delay(100);
  char line[512];
  char c;
  int lineIndex;
  bool lineIsComment, lineSemiColon;

  lineIndex = 0;
  lineSemiColon = false;
  lineIsComment = false;

  while (1) {


    while ( Serial.available() > 0 ) {
      c = Serial.read();
      if (( c == '\n') || (c == '\r') ) {             
        if ( lineIndex > 0 ) {                        
          line[ lineIndex ] = '\0';                   
          if (verbose) {
            Serial.print( "Received : ");
            Serial.println( line );
          }
          processIncomingLine( line, lineIndex );
          lineIndex = 0;
        }
        else {
          // Empty or comment line. Skip block.
        }
        lineIsComment = false;
        lineSemiColon = false;
        Serial.println("ok");
      }
      else {
        if ( (lineIsComment) || (lineSemiColon) ) {   
          if ( c == ')' )  lineIsComment = false;     
        }
        else {
          if ( c <= ' ' ) {                           
          }
          else if ( c == '/' ) {                    
          }
          else if ( c == '(' ) {                    
            lineIsComment = true;
          }
          else if ( c == ';' ) {
            lineSemiColon = true;
          }
          else if ( lineIndex >= 512 - 1 ) {
            Serial.println( "ERROR - lineBuffer overflow" );
            lineIsComment = false;
            lineSemiColon = false;
          }
          else if ( c >= 'a' && c <= 'z' ) {        
            line[ lineIndex++ ] = c - 'a' + 'A';
          }
          else {
            line[ lineIndex++ ] = c;
          }
        }
      }
    }
  }
}

void processIncomingLine( char* line, int charNB ) {
  int currentIndex = 0;
  char buffer[ 64 ];                                 
  struct point newPos;

  newPos.x = 0.0;
  newPos.y = 0.0;

  while ( currentIndex < charNB ) {
    switch ( line[ currentIndex++ ] ) {              
      case 'U':
        penUp();
        break;
      case 'D':
        penDown();
        break;
      case 'G':
        buffer[0] = line[ currentIndex++ ];          
        //      buffer[1] = line[ currentIndex++ ];
        //      buffer[2] = '\0';
        buffer[1] = '\0';

        switch ( atoi( buffer ) ) {                  
          case 0:                                   
          case 1:
            // /!\ Dirty - Suppose that X is before Y
            char* indexX = strchr( line + currentIndex, 'X' ); 
            char* indexY = strchr( line + currentIndex, 'Y' );
            if ( indexY <= 0 ) {
              newPos.x = atof( indexX + 1);
              newPos.y = actuatorPos.y;
            }
            else if ( indexX <= 0 ) {
              newPos.y = atof( indexY + 1);
              newPos.x = actuatorPos.x;
            }
            else {
              newPos.y = atof( indexY + 1);
              indexY = '\0';
              newPos.x = atof( indexX + 1);
            }
            drawLine(newPos.x, newPos.y );
            //        Serial.println("ok");
            actuatorPos.x = newPos.x;
            actuatorPos.y = newPos.y;
            break;
        }
        break;
      case 'M':
        buffer[0] = line[ currentIndex++ ];        
        buffer[1] = line[ currentIndex++ ];
        buffer[2] = line[ currentIndex++ ];
        buffer[3] = '\0';
        switch ( atoi( buffer ) ) {
          case 300:
            {
              char* indexS = strchr( line + currentIndex, 'S' );
              float Spos = atof( indexS + 1);
              //         Serial.println("ok");
              if (Spos == 30) {
                penDown();
              }
              if (Spos == 50) {
                penUp();
              }
              break;
            }
          case 114:                                
            Serial.print( "Absolute position : X = " );
            Serial.print( actuatorPos.x );
            Serial.print( "  -  Y = " );
            Serial.println( actuatorPos.y );
            break;
          default:
            Serial.print( "Command not recognized : M");
            Serial.println( buffer );
        }
    }
  }
}

void drawLine(float x1, float y1) {

  if (verbose) {
    Serial.print("fx1, fy1: ");
    Serial.print(x1);
    Serial.print(",");
    Serial.print(y1);
    Serial.println("");
  }

  y1 = y1 * -1;

  
  if (x1 >= Xmax) {
    x1 = Xmax;
  }
  if (x1 <= Xmin) {
    x1 = Xmin;
  }
  if (y1 >= Ymax) {
    y1 = Ymax;
  }
  if (y1 <= Ymin) {
    y1 = Ymin;
  }



  if (verbose) {
    Serial.print("Xpos, Ypos: ");
    Serial.print(Xpos);
    Serial.print(",");
    Serial.print(Ypos);
    Serial.println("");
  }

  if (verbose) {
    Serial.print("x1, y1: ");
    Serial.print(x1);
    Serial.print(",");
    Serial.print(y1);
    Serial.println("");
  }

  
  x1 = (int)(x1 * StepsPerMillimeterX / 2);
  y1 = (int)(y1 * StepsPerMillimeterY / 2);
  float x0 = Xpos;
  float y0 = Ypos;


  long dx = abs(x1 - x0);
  long dy = abs(y1 - y0);
  int sx = x0 < x1 ? StepInc : -StepInc;
  int sy = y0 < y1 ? StepInc : -StepInc;

  long i;
  long over = 0;

  if (dx > dy) {
    for (i = 0; i < dx; ++i) {
      motorx.onestep(sx, MICROSTEP);
      over += dy;
      if (over >= dx) {
        over -= dx;
        motory.onestep(sy, MICROSTEP);
      }
      delay(StepDelay);
    }
  } else {
    for (i = 0; i < dy; ++i) {
      motory.onestep(sy, MICROSTEP);
      over += dx;
      if (over >= dy) {
        over -= dy;
        motorx.onestep(sx, MICROSTEP);
      }
      delay(StepDelay);
    }
  }

  if (verbose) {
    Serial.print("dx, dy:");
    Serial.print(dx);
    Serial.print(",");
    Serial.print(dy);
    Serial.println("");
  }

  if (verbose) {
    Serial.print("Going to (");
    Serial.print(x0);
    Serial.print(",");
    Serial.print(y0);
    Serial.println(")");
  }

  
  delay(LineDelay);
  Xpos = x1;
  Ypos = y1;
}


void penUp() {
  servo.write(penup);
  delay(50);
  Zpos = Zmax;
}

void penDown() {
  servo.write(pendown);
  delay(50);
  Zpos = Zmin;
}
  • Next, select the board and port. After, click the upload button.

Step 13

Afterward, install the controller board in a safe place and connect an external power supply to it.

Step 14

Now, open the Processing IDE and copy and paste the following program into it. Then, place a paper sheet on the CNC bed.

import javax.swing.JOptionPane;
import processing.serial.*;

Serial port;

String portname = null;
String[] gcode;
int i = 0;

boolean streaming = false;
boolean paused = false;

void setup() {
  size(500, 180);
}

void draw() {
  background(0);
  fill(255);

  text("DIY CNC Controller", 10, 20);
  text("p = select port", 10, 50);
  text("g = load G-code", 10, 70);
  text("s = pause/resume", 10, 90);
  text("x = stop", 10, 110);

  text("Port: " + portname, 10, 140);

  if (gcode != null) {
    text("Progress: " + i + "/" + gcode.length, 10, 160);
  }
}

void keyPressed() {

  if (key == 'p') selectPort();

  if (key == 'g' && !streaming) loadFile();

  if (key == 'x') {
    streaming = false;
    if (port != null) port.write("U\n");
  }

  if (key == 's') {
    paused = !paused;
  }
}

void selectPort() {
  String result = (String) JOptionPane.showInputDialog(
    null,
    "Select COM port",
    "Port",
    JOptionPane.QUESTION_MESSAGE,
    null,
    Serial.list(),
    0
  );

  if (result != null) {
    portname = result;

    if (port != null) port.stop();

    port = new Serial(this, portname, 9600);

    delay(2000); // IMPORTANT for Arduino reset

    port.bufferUntil('\n');
  }
}

void loadFile() {
  selectInput("Select G-code file:", "fileSelected");
}

void fileSelected(File file) {
  if (file == null) return;

  gcode = loadStrings(file.getAbsolutePath());
  i = 0;
  streaming = true;

  stream();
}

void stream() {
  if (!streaming || paused) return;

  if (gcode == null) return;

  while (i < gcode.length && gcode[i].trim().length() == 0) {
    i++;
  }

  if (i >= gcode.length) {
    streaming = false;
    return;
  }

  if (port != null) {
    port.write(gcode[i] + "\n");
  }

  i++;
}

void serialEvent(Serial p) {
  String s = p.readStringUntil('\n');

  if (s == null) return;

  s = trim(s);
  println(s);

  if (s.equals("ok")) {
    if (!paused) stream();
  }
}
  • Now, click the run button. Then you will see the controller window. Now press ‘P’ and select the correct port.

Step 15

Finally, press the G button and select the G-code file. You can also test our example G-codes. If you want to design your own G-code files, please refer to this project.

How to make a DIY CNC Plotter machine using an Arduino Nano

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *