Home / DIY Projects / Make a Mini CNC Plotter with old DVD players Arduino and L293D

Make a Mini CNC Plotter with old DVD players Arduino and L293D

This project propose you to build your own Arduino Mini CNC Plotter. For the X and Y axis, will use the steppers motors of two old CD or DVD players. We will use two L293D motor drivers. One for each X and Y axis. For the Z axis we will use a small servo motor (for example SG90) that moves a pen up and down. The Z axis will be hard to develop (for young Makers), so to simplify the project, i design all mechanical parts that you just need to print with a 3D printer.

You can develop your mini CNC Plotter directly from this post.

Step 1 : BOM

You can buy all components on internet or DIY store.

Step 2 : Prepare translation systems

Dismantle the old DVD players to get the translation system with the stepper motor.

mini traceur cnc plotter projets diy lecteur dvd
Stepper Motor and DVD player translation system ready to use.

Step 3 : Prepare stepper motors (X and Y axis)

It’s better to sold the wires on the flexible connector. You may have problems if you sold directly on the stepper motor. If you can, try to use 4 different colors, one for each Pin. There is no identification on the engine but the connection is quite simple. The first wire corresponds to the first output of the L293D (usually) and so on.

mini traceur cnc plotter soudure stepper motor projetdisy mini traceur cnc plotter soudure stepper motor gaine thermo projetdisy

Step 4 : Print 3D parts

You can download the STL files of this project onThingiverse or Cults3D.

Part 3D design Number
Mounting brackets (X axis) mini traceur cnc plotter arduino equerres montants axe Y 4x
Spacers mini traceur cnc plotter arduino entretoises supports axex x et y 8x
Breadboard spacers mini traceur cnc plotter arduino equerre breadboard 4x
Z axis basis mini traceur cnc plotter arduino support porte crayon 1x
Pencil case (8mm diam.) mini traceur cnc plotter arduino porte crayon diametre 8mm 1x
Pencil anti-tilt ring mini traceur cnc plotter arduino bague anti basculement 1x

I prefer to use PLA filament because it’s an environmental friendly and non toxic material. If you print all parts, you need about 3 hours. I use the French 3D printer Dagoma discovery200.

elements de structure impression3d mini traceur cnc plotter arduino

Step 5 : Prepare the plywood structure

Just cut the 3 parts (see the BOM for dimensions)

Step 6 : Prepare the circuit

The circuit is really easy as you can see bellow. One L293D for each axis and a servo motor for the Z-axis.

mini traceur cnc plotter arduino schema de cablage breadboard

mini traceur cnc plotter arduino projet diy cablage l293d breadboard
2x L293D motor divers

 

mini traceur cnc plotter arduino projet diy zoom cablage arduino
Arduino Uno

Step 7 : Assembling the Mini CNC Plotter

X and Y Axis

Use 4 spacers and 4 screws M4 x 30mm to assembling the X axis on the playwood (200 x 160mm). Do the same for the Y axis. Use the picture bellow to setup the position of elements. Finally attach the Y axis on the vertical uprights. The height is not important, we just need to adjust the position of the pen in its holder.

Drawing surface

Use a glue gun to fix the drawing surface on the axis. To find the good position move the stepper motor to the max. position and fix it.

mini traceur cnc plotter arduino projet diy zoom plateau dessin
Drawing surface (front vue)

Pencil case (Z-axis)

Follow the drawing to assembly the pencil holder. Fix the servo motor with 2x M2 screws or glue.

mini traceur cnc plotter arduino vue ensemble porte crayon
Pencil holder design
mini traceur cnc plotter arduino projet diy zoom gauche porte crayon
Pencil holder (left vue)
mini traceur cnc plotter arduino projet diy zoom droit porte crayon
Pencil holder (right vue) with the servo motor

Breadboard and Arduino

Use spacers to fix the breadboard.

mini traceur cnc plotter arduino projet diy arriere breadboard
Breadboard and Arduino (rear vue)

Step 8 : Setup X and Y axis

Now, we can start programming ! First, we need to test if the X and Y axis are properly connected. After that, we will calibrate the stepper motors.

X axis test

X-Axis use the pins 8,9,10 and 11 by default. Start this program. If all is correct, the stepper motor must do 100 steps. Measure the distance and divide by 100 to obtain the X-Axis calibration value.

/*
  Mini Traceur Arduino (CNC Plotter)
  Test de l'axe X
  Projets DIY 02/2016
*/
#include <Stepper.h>			//Librairie pour piloter les moteurs pas à pas
 
const int pasParRotation = 20; 	//Nombre de pas pour réaliser un tour
 
//Pins sur lesquels est relié le moteur de l'axe X
Stepper myStepperX(pasParRotation, 8,9,10,11); 
 
void setup() {
 myStepperX.setSpeed(100);		//Vitesse de déplacement
 //Indiquer le nombre de pas que le moteur pas à pas doit faire
 //Généralement un moteur de lecteur CD/DVD peut faire 250 pas au maxium
 //Indiquer un chiffre négatif pour inverser le sens de déplacement
 myStepperX.step(100);			
 delay(100);
}
 
void loop() {}

Y axis test

Do the same for the Y axis.

/*
  Mini Traceur Arduino (CNC Plotter)
  Test de l'axe Y
  Projets DIY 02/2016
*/
#include <Stepper.h>			//Librairie pour piloter les moteurs pas à pas
 
const int pasParRotation = 20; 	//Nombre de pas pour réaliser un tour
 
//Pins sur lesquels est relié le moteur de l'axe X
Stepper myStepperX(pasParRotation, 2,3,4,5); 
 
void setup() {
 myStepperX.setSpeed(100);		//Vitesse de déplacement
 //Indiquer le nombre de pas que le moteur pas à pas doit faire
 //Généralement un moteur de lecteur CD/DVD peut faire 250 pas au maxium
 //Indiquer un chiffre négatif pour inverser le sens de déplacement
 myStepperX.step(100);			
 delay(100);
}
 
void loop() {}

In case of problem

The main problems I encountered during the development of my plotter :

  • The engine does not move: first check the wiring of the L293D. The IN and OUT Pins may be reversed.
  • The direction of movement is reversed. Reverse Pins on the L293D.
  • The engine never stops, do not go to the end or “jump”. There is probably an obstacle in its path. Check if one screw is not too long …

Step 9 : Upload the CNC code into the Arduino

Before uploading the CNC program, maybe you need to update the configuration

  • penZUp : angle of the servo when the pen is Up
  • penZDown : angle when the pen is Down (on the paper sheet)
  • Pins : servo, X and Y axis
  • Calibration values of the X and Y-axis

Warning: do not use the servo motor to press the pencil on the paper sheet. The force exerted by the actuator may be too strong and prevent movement of the axis X. The pen is pressed on the sheet by the spring.

/* 
 Mini Traceur CNC Arduino
 Traduction de la configuration du traceur en français et ajout d'une variable "vitesseDeplacement" permettant d'ajuster la vitesse de déplacement des axes X et Y
 Projets DIY - 02/2016
 
 D'après 
 Mini CNC Plotter firmware, based in TinyCNC https://github.com/MakerBlock/TinyCNC-Sketches
 Send GCODE to this Sketch using gctrl.pde https://github.com/damellis/gctrl
 Convert SVG to GCODE with MakerBot Unicorn plugin for Inkscape available here https://github.com/martymcguire/inkscape-unicorn
 
 More information about the Mini CNC Plotter here (german, sorry): http://www.makerblog.at/2015/02/projekt-mini-cnc-plotter-aus-alten-cddvd-laufwerken/
  */

#include <Servo.h>
#include <Stepper.h>

#define LINE_BUFFER_LENGTH 512

/*
   PARAMETRES DE CONFIGURATION
*/

const int penZUp = 130;				// Angle servomoteur, crayon relevé
const int penZDown = 40;            // Angle servomoteur, crayon baissé
const int penServoPin = 6;			// Pin sur lequel est relié le servomoteur
const int stepsPerRevolution = 20; 	// Valeur par défaut
const int vitesseDeplacement = 250;	// Vitesse de déplacement des axes X et Y

Servo penServo;  					// Objet pour actionner le servomoteur

// Initialisation des moteurs pas à pas pilotés à l'aide d'un pont H L293D
Stepper myStepperY(stepsPerRevolution, 2,3,4,5);	// Axe X         
Stepper myStepperX(stepsPerRevolution, 8,9,10,11);  // Axe Y

// Calibration, nombre de pas par millimètre
float StepsPerMillimeterX = 6.0;
float StepsPerMillimeterY = 6.0;

/*
  FIN DE LA CONFIGURATION
*/

/* 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 = 0;
int LineDelay = 50;
int penDelay = 50;

// Drawing robot limits, in mm
// OK to start with. Could go up to 50 mm if calibrated well. 
float Xmin = 0;
float Xmax = 40;
float Ymin = 0;
float Ymax = 40;
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;

//  Needs to interpret 
//  G1 for moving
//  G4 P300 (wait 150ms)
//  M300 S30 (pen down)
//  M300 S50 (pen up)
//  Discard anything with a (
//  Discard any other command!

/**********************
 * void setup() - Initialisations
 ***********************/
void setup() {
  //  Setup
  Serial.begin( 9600 );
  
  penServo.attach(penServoPin);
  penServo.write(penZUp);
  delay(200);

  // Decrease if necessary
  myStepperX.setSpeed(vitesseDeplacement);
  myStepperY.setSpeed(vitesseDeplacement);  

  //  Set & move to initial default position
  // TBD

  //  Notifications!!!
  Serial.println("Mini CNC Plotter alive and kicking!");
  Serial.print("X range is from "); 
  Serial.print(Xmin); 
  Serial.print(" to "); 
  Serial.print(Xmax); 
  Serial.println(" mm."); 
  Serial.print("Y range is from "); 
  Serial.print(Ymin); 
  Serial.print(" to "); 
  Serial.print(Ymax); 
  Serial.println(" mm."); 
}

/**********************
 * void loop() - Main loop
 ***********************/
void loop() 
{
  delay(200);
  char line[ LINE_BUFFER_LENGTH ];
  char c;
  int lineIndex;
  bool lineIsComment, lineSemiColon;

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

  while (1) {

    // Serial reception - Mostly from Grbl, added semicolon support
    while ( Serial.available()>0 ) {
      c = Serial.read();
      if (( c == '\n') || (c == '\r') ) {             // End of line reached
        if ( lineIndex > 0 ) {                        // Line is complete. Then execute!
          line[ lineIndex ] = '\0';                   // Terminate string
          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) ) {   // Throw away all comment characters
          if ( c == ')' )  lineIsComment = false;     // End of comment. Resume line.
        } 
        else {
          if ( c <= ' ' ) {                           // Throw away whitepace and control characters
          } 
          else if ( c == '/' ) {                    // Block delete not supported. Ignore character.
          } 
          else if ( c == '(' ) {                    // Enable comments flag and ignore all characters until ')' or EOL.
            lineIsComment = true;
          } 
          else if ( c == ';' ) {
            lineSemiColon = true;
          } 
          else if ( lineIndex >= LINE_BUFFER_LENGTH-1 ) {
            Serial.println( "ERROR - lineBuffer overflow" );
            lineIsComment = false;
            lineSemiColon = false;
          } 
          else if ( c >= 'a' && c <= 'z' ) {        // Upcase lowercase
            line[ lineIndex++ ] = c-'a'+'A';
          } 
          else {
            line[ lineIndex++ ] = c;
          }
        }
      }
    }
  }
}

void processIncomingLine( char* line, int charNB ) {
  int currentIndex = 0;
  char buffer[ 64 ];                                 // Hope that 64 is enough for 1 parameter
  struct point newPos;

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

  //  Needs to interpret 
  //  G1 for moving
  //  G4 P300 (wait 150ms)
  //  G1 X60 Y30
  //  G1 X30 Y50
  //  M300 S30 (pen down)
  //  M300 S50 (pen up)
  //  Discard anything with a (
  //  Discard any other command!

  while( currentIndex < charNB ) {
    switch ( line[ currentIndex++ ] ) {              // Select command, if any
    case 'U':
      penUp(); 
      break;
    case 'D':
      penDown(); 
      break;
    case 'G':
      buffer[0] = line[ currentIndex++ ];          // /!\ Dirty - Only works with 2 digit commands
      //      buffer[1] = line[ currentIndex++ ];
      //      buffer[2] = '\0';
      buffer[1] = '\0';

      switch ( atoi( buffer ) ){                   // Select G command
      case 0:                                   // G00 & G01 - Movement or fast movement. Same here
      case 1:
        // /!\ Dirty - Suppose that X is before Y
        char* indexX = strchr( line+currentIndex, 'X' );  // Get X/Y position in the string (if any)
        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++ ];        // /!\ Dirty - Only works with 3 digit commands
      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:                                // M114 - Repport position
        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 );
      }
    }
  }
}


/*********************************
 * Draw a line from (x0;y0) to (x1;y1). 
 * Bresenham algo from https://www.marginallyclever.com/blog/2013/08/how-to-build-an-2-axis-arduino-cnc-gcode-interpreter/
 * int (x1;y1) : Starting coordinates
 * int (x2;y2) : Ending coordinates
 **********************************/
void drawLine(float x1, float y1) {

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

  //  Bring instructions within limits
  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("");
  }

  //  Convert coordinates to steps
  x1 = (int)(x1*StepsPerMillimeterX);
  y1 = (int)(y1*StepsPerMillimeterY);
  float x0 = Xpos;
  float y0 = Ypos;

  //  Let's find out the change for the coordinates
  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) {
      myStepperX.step(sx);
      over+=dy;
      if (over>=dx) {
        over-=dx;
        myStepperY.step(sy);
      }
      delay(StepDelay);
    }
  }
  else {
    for (i=0; i<dy; ++i) {
      myStepperY.step(sy);
      over+=dx;
      if (over>=dy) {
        over-=dy;
        myStepperX.step(sx);
      }
      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 before any next lines are submitted
  delay(LineDelay);
  //  Update the positions
  Xpos = x1;
  Ypos = y1;
}

//  Raises pen
void penUp() { 
  penServo.write(penZUp); 
  delay(LineDelay); 
  Zpos=Zmax; 
  if (verbose) { 
    Serial.println("Pen up!"); 
  } 
}
//  Lowers pen
void penDown() { 
  penServo.write(penZDown); 
  delay(LineDelay); 
  Zpos=Zmin; 
  if (verbose) { 
    Serial.println("Pen down."); 
  } 
}

Step 10 : Install Processing or use gctrl.exe

Last step before your first draw. We need to install a program that send gcode file to the Arduino controler.

If you are a Windows user you can directly use the compilated version of gctrl. You can download it here.

For macOS or Linux users, you need to install Processing (here) then run gctrl.pde (download here)

Step 11 : Plot your first draw with gctrl

Before making your first draw :

  • Make sure the plot surface is at the origine.
  • Check that nothing blocks the movement of the axes
  • Place a sheet on the drawing area

gctrl is an efficient but basic program in the sense that it does not check the selected file. The plotter start just after the selection of the gcode file. So, be sure before validate. In case of problem, press “x” !

  • Press “p” to choose the serial port.
  • Press “h” to go at home position
  • Press “g” to chose the gcode file

mini cnc traceur arduino - inkscape gcode gctrl_pde

mini traceur cnc plotter arduino test minion

A short video (in french at the moment…be patient) that explains how to make your Mini CNC plotter.


I hope you find everything you need to make your Mini CNC Plotter. I hope I did not forget anything. Feel free to share your experience or add your tricks. Comments are there for.