There are many projects to recycle old DVD players into a mini CNC Plotter based on Arduino. It is a nice project and ideal to start with its Arduino. The mechanical construction and more particularly the pencil holder can be difficult. I propose in this article to realize a mini plotter by realizing the mechanical elements by 3D printing. We will use two L293D H bridges to drive the engines of the DVD players. We find it for less than €2 in China. You can develop your mini CNC Plotter directly from this post.
Contents
- 1 Step 1 : BOM
- 2 Step 2 : Prepare translation systems
- 3 Step 3 : Prepare stepper motors (X and Y axis)
- 4 Step 4 : Print 3D parts
- 5 Step 5 : Prepare the plywood structure
- 6 Step 6 : Prepare the circuit
- 7 Step 7 : Assembling the Mini CNC Plotter
- 8 Step 8 : Setup X and Y axis
- 9 Step 9 : Upload the CNC code into the Arduino
- 10 Step 10 : Install Processing or use gctrl.exe
- 11 Step 11 : Plot your first draw with gctrl
Step 1 : BOM
You can buy all components on internet or DIY store.
- 1x Arduino Uno
- 1x Breadboard 400 points
- 2x L293D motor drives
- 1x servo motor SG90
- 2 old CD/DVD players
- 2 wires 22AWG (yellow) 15cm (0.6 in)
- 2 wires 22AWG (green) 15cm (0.6 in)
- 2 wires 22AWG (red) 15cm (0.6 in)
- 2 wires 22AWG (white) 15cm (0.6 in)
- Some shrink tubing
- 1x plywood sheet 200 x 160mm (7.87 in x 6.3 in) for X Axis.Thickness : 10mm (0.39in) or 5mm plexiglass (0.2 in)
- 2x plywood sheet 150 x 50mm (5.91 in x 0.2 in) for Y Axis.
- 1x plexiglass plate (0.2 in) 76 x 76 mm (size of a post it, 3 x 3 in)
- 1x 3mm rod (0.12 in) x 80 mm (3.15 in)
- Glue gun
- 2x screw M2 or glue to fix servo
- 8x M4x35mm and nuts
- 1x screw M5x20mm and nut
Step 2 : Prepare translation systems
Dismantle the old DVD players to get the translation system with the stepper motor.
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.
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) | 4x | |
Spacers | 8x | |
Breadboard spacers | 4x | |
Z axis basis | 1x | |
Pencil case (8mm diam.) | 1x | |
Pencil anti-tilt ring | 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.
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.
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.
Pencil case (Z-axis)
Follow the drawing to assembly the pencil holder. Fix the servo motor with 2x M2 screws or glue.
Pencil holder design
Pencil holder (left vue)
Pencil holder (right vue) with the servo motor
Breadboard and Arduino
Use spacers to fix the breadboard.
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 X Axis Projets DIY 02/2016 */ #include <Stepper.h> // Include the stepper Motor librarie const int pasParRotation = 20; // Number of steps by turn. Standard value for CD/DVD // Indicate X axis stepper motor Pins Stepper myStepperX(pasParRotation, 8,9,10,11); void setup() { myStepperX.setSpeed(100); // Stepper motor speed // Indicate the number of steps the drive need to do. // CD/DVD drive can do about 250 steps max. // Negative number to reverse direction of the movement myStepperX.step(100); // Measure the distance the pencil move to calculate X calibration value 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
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.
What i need to do for test X & Y axis?
Hi Itihash. Can you tell at what step your are ?
I’ll make it so i following you but i didn’t understand the step 8.
OK. We want to know how many steps are needed to move the pencil (for example 10mm). So, run X drive with 100 steps, after measure the distance (x) and then divide 100/x mm. Now replace the value of the variable StepsPerMillimeterX (by default, it 6s/mm) . Do the same for the Y axis. After that, the X and Y axis are ready. I hope it’s clear enough now. Have a good day. Christophe
I can do it using gctrl or I need another application?
No. Use the Arduino IDE. Create a new file (for example TestX.ino) and past the code of the step 8 (X axis step). Start the program and do as indicate before. Do the same for the Y axis (or just change the Pins of the Y stepper motor). I’m sorry i forgot to translate in English the comments… but now, i’m sure you read French very well ;-). Bye
Thanks.
Isn’t a little mistake on connection from axis Y , l293d and pin 11 (white path) on breadboard? I guess it should be on oposite pins for l293d ?
Hi Greg. You probably right. Depending your drive, you may need to switch pins to find the correct direction. I hope you’ll succeed with your project. See you soon.
Step 6
I haven’t find similarity between drawing and real photo.
I have this error please help…….
Arduino: 1.8.1 (Windows 7), Board: “Arduino/Genuino Uno”
cnc_plotter:14: error: #include expects “FILENAME” or
#include <Servo.h>
^
cnc_plotter:15: error: #include expects “FILENAME” or
#include <Stepper.h>
^
exit status 1
#include expects “FILENAME” or
This report would have more information with
“Show verbose output during compilation”
option enabled in File -> Preferences.
Hi Mahadi. You need to install the servo library. You just need to install it directly from the library manager (sketch menu). After that, you can download the program. Have a good day.
I add it but the error remain same …why…?
cnc_plotter:18: error: #include expects “FILENAME” or
#include <Servo.h>
^
cnc_plotter:19: error: #include expects “FILENAME” or
#include <Stepper.h>
^
exit status 1
#include expects “FILENAME” or
Sorry Mahadi, it’s totally impossible to help you like that. Could you send me your sketch by using the contact form. Thank’s
I Figure-out the problem and fix it …But now i got a serious problem about G- Code making …Can u Please tell me How can i generate G-Code With out inkscape ?
Actually i Use inkscape 0.48.5 version and also it’s plugin But i don’t find g code export …option.. So..How can i Generate G- Code .?
And How you generate G code For Yourself.
Hello Mahadi. I’m glad if you found the solution ! To prepare your gcode files, just follow this post https://www.diyprojects.io/prepare-gcode-gctrl-files-inkscape-unicorn-makerbot-extension/
See you soon
Can u Please tell me How can i generate G-Code With out inkscape ?
Actually i Use inkscape 0.48.5 version and also it’s plugin But i don’t find g code option.. So..How can i Generate G- Code now..?
I did all steps like you but the stepper motors are not running. The servo motor is running but the steps not. can you help me please
Hi Bujari. Can you tell me how you power the stepper motors? May be that there is not enough power. The problem with recycling stepper motors from older dvd drives is that you do not know pin marking. You have to look for the right wiring. See you soon.
I tried by stepper test, is running properly.
Power I tried 12V and 9V the same.
Hi, please help me.
Have same error as Mahadi….
Arduino: 1.8.3 (Windows 7), Board: “Arduino/Genuino Uno”
cnc_plotter:14: error: #include expects “FILENAME” or etc…
Included servo and stepper libraries as suggested from sketch menu
but still the same error.
I am not an expert please may you give me detailed info on how to solve.
Thanks a lot.
I have problem when I’m trying to execute g code… It’s just showed…. Loading file…. And nothing….. What to do
Solved.
Thanks
Great. Maybe you could explain to everyone how you solved the problem. Thank’s
hi am called sadam i have got a problem i have used the same wiring and code used but my cd rom stepper motor they dont move at all think the problem it ld293 ic but i have just bought them what can i do now because am stack
Hi, very, very good project, I see that many people have been successful.
I have uploaded Test_X_Axis_CNC_Plotter and Y, and my engines have good movement.
I have a problem with gctrl.pde and gctrl.exe, but I do not know why. My engines have no movement with gctrl. Select the serial port (OK), use the arrows and no movement, load a gcode image with (G) and no movement. I do not know where the problem is.
You help me? Thank you
My beta-CNC without Arduino, only router with OpenWRT and L293 driver. Does not interpret GCODE yet:
Hello Eduardo. Great project. I’m sorry but I have no skills on openwrt (at the moment ;-))
Hi I’m trying to build the mini plotter 🙂
I put the code in the arduino IDE and it appeared to me this :
mini_cnc_plotter__franc_s_:407: error: expected ‘}’ at end of input
exit status 1
‘gt’ was not declared in this scope
Should I replace “gt” with something?
Hello Jose. It’s strange, I just compile the program with the Arduino IDE (1.8.2) without any error. Is this the program of step 9?
Yes it is.
I just copied again to arduino IDE and the error is gone 🙂
Thank you for your reply and for your sharing!
Great, it’s cool. Have fun 😀
Somehow instead of ” it appeared ‘>’.
Hello there. Thank you this project. I made it but I have a trouble.
Stepper **myStepperX(stepsPerRevolution, 8,9,10,11)** this step motor not working.
PWM channels 8,9,10,11 dont have voltage.I tried to increase feed voltaje and it didnt work again. I used ardunio mega 2560 and L293d. Another step motor is working and 2,3,4,5 channels have voltage. What is the problem ? help me please.
Great project, but i’m stuck. I’m testing the X-axis and all the motor does is vibrate. i’ve tried to change the connection but all i get is vibrating. Does anyone know what i’m doing wrong?
Hello sylvie and thank you very much. if the engine vibrates and does not move, it is necessary to review the wiring of it. There is a reversal of threads. Unfortunately, each manufacturer can use a different color convention.