Introduction: Mini Arduino Plotter Without a Servo Motor

In this project I will show you how to turn two old DVD-drives into a mini plotter.this project is simple and easy to do and makes a good practice if you want to make a real CNC machine(this is why i started this project to prepare my self for a bigger CNC),you get to drive stepper motors and convert vector files to gcode..

this plotter is diffrent from the others because i used the cd tray mechnism for the z axis instead of a servo motor to lower the cost of the project hope you like it

let's get started :

Parts and tools:

2 - DVD-drives

3 - L293 H-bridges IC

1- Arduino uno

1 - Breadboard

- Some wires

- hot glue or something similar

- small pieces of plastic

- USB cable (i power the circuit from the pc)

1 - screw driver

i filmed all the steps on my youtube channel so check them out for more information :

Tunisian Maker : https://www.youtube.com/channel/UCsFmFKMpUdyIiE55W3KsTzw

Step 1: Gathering the Parts We Need From the DVD-drives

in this step we will begin by disassembling the DVD-drives to get the stepper motors and the carriages of the laser that reads the disk it's simple grab a screwdriver open the metal case and keep it it'll be the main body of our plotter also we will need the plastic mechanism of the DVD tray for our Z-axis this is how it looks :

this video is for step 2 and 3 also :

Step 2: Wiring the Stepper Motors

This is a tricky part cause the stepper motors are tiny and you need to take your time and be careful not to damage the motor cause i did and this is why:(IMPORTANT)

the stepper motor wires are attached in place with the plastic connector itself so if you remove the old connector the wire get loose and its hard to recover them after so you need to wire the new ones on top of the old one

each stepper motor needs 4 wires cut the four wires to the same length put a tip of solder on top then solder them to the stepper motor.

Step 3: Assembling the Plotter

the base of our plotter is the metal case itself i attached it with screws and bolts but you can use hot glue.

i mounted the X and Y axis perpendicular to each other with hot glue i didn't measure anything but it worked fine

for the Z axis i glued a piece of plastic to its back and glued it to the carriage of the Y axis

Step 4: Wiring the Circuit

get your breadboard put the 3 L293 IC first and wire the wires around them the last step would be wiring the motors

the drawings show it all.now before wiring the motors we need to find out the two pairs of coils i used my multimeter to find out the connected wires and connected each pair on a side of the IC

For the z axis it has a built in dc motor wich connects on a side of the third IC.

connect the arduino:

X axis 2,3,4,5

Y axis 6, 7,8,9

Z axis 10 11 but you can change it in the code if you want

this is the video :

Step 5: Testing the Connections and the Stepper Motors

its a simple arduino sketch using the fuctions of the stepper library like so:

For the X axis

#include <Stepper.h>

const int stepsPerRevolution = 20; //Connection pins: Stepper myStepperX(stepsPerRevolution, 8,9,10,11); void setup() { //Set speed: myStepperX.setSpeed(100); //max 250 steps for dvd/cd stepper motors myStepperX.step(160); delay(100); } void loop() {

}

For the Y axis

#include <Stepper.h>

const int stepsPerRevolution = 20; //Connection pins: Stepper myStepperX(stepsPerRevolution, 6,7,8,9); void setup() { //Set speed: myStepperX.setSpeed(100); //max 250 steps for dvd/cd stepper motors myStepperX.step(160); delay(100); } void loop() {

}

For the Z axis :

void setup() {
// Setup Serial.begin( 9600 ); pinMode(10, OUTPUT); pinMode(11, OUTPUT); delay(200);}

// Raises pen
void penUp() { digitalWrite(I1 , LOW);

digitalWrite(I2 , HIGH);

delay(200);

digitalWrite(I2 , LOW);

digitalWrite(I1 , LOW);

Serial.println("Pen up!"); }

// Lowers pen void penDown() {

digitalWrite(I2 , LOW);

digitalWrite(I1 , HIGH);

delay(200);

digitalWrite(I2 , LOW);

digitalWrite(I1 , LOW);

Serial.println("Pen down."); }

void loop() {

penUp();

penDown();

}

Step 6: Make G-code and Start Plotting

you need inkscape version 0.48.5 or it wont work and the plugin unicorn gcode plugin and the rest of the software with all the links is included are ready download

Comments

author
rcorycollins (author)2017-01-03

Hi, very nice instructable! I had an idea for a variation on this. Has anyone tried using the geared drive open/close mechanism in an xy plotter like this one? Maybe replace the dc motor that has the small pulley for the belt on it with the stepper motor from the laser slide? Could give you a little bit bigger drawing area. Just a thought.... Thanks!

author
Sonal More (author)2016-09-01

Hello sir I have seen the wiring video I noted a voltage regulator Ic and two capacitors please tell us which capacitors and which is the voltage regulator Ic

author
Sivanand (author)2016-07-15

sir, can u upload the code for testing the circuit..im a bigginner with arduino and stepper...

author
TunMaker (author)Sivanand2016-07-17

check step 5 for testing the steppers individualy and the up and down movement

author
Sivanand (author)TunMaker2016-07-17

my z axis is not working thats y i askd u...thanks alot :)

author
Sivanand (author)Sivanand2016-07-17

sir,i tested it with 2cd drive stepper motors,it works...bt i want to make a cnc machine with dimensions 31x50cm and i had 2- mitsumi m35sp-8nb steppers+1 cd drive motor for z axis..what all changes i have to do in the coding?..

http://www.mitsumi.co.jp/latest/Catalog/pdf/motor_m35sp_8_e.pdf

author
TunMaker (author)Sivanand2016-07-18

for x and y i think the circuit can handle it and no code change is needed for z axis you want to replace it with a stepper motor th code you need i already wrote in the comments below =)

author
Sivanand (author)TunMaker2016-07-31

sir,it is not working, the stepper is not rotates smoothly...it turns a around20-30degree and stops then takes a next 20-30degree step then stops in an blink of eye...

can u helpme?

author
TunMaker (author)Sivanand2016-07-18

for x and y i think the circuit can handle it and no code change is needed for z axis you want to replace it with a stepper motor th code you need i already wrote in the comments below =)

author
FarazA19 (author)2016-07-22

I need help.can i used the Dc motor in place of stepper...!

author
TunMaker (author)FarazA192016-07-24

of course not you need 2 stepper motors and 1 dc motor

author
shivanshu.gupta (author)2016-07-02

Hey TunMaker, I was wondering if you could do the same thing but having 3 stepper motors. Instead of having a DC Motor on the Z-Axis, you would have another stepper motor. Do you think it would work? and would the Arduino have enough ports to utilize the 3 Stepper motors?

author

Definitely it works ...i made it using the code Mr.Tunmaker provided

author
TunMaker (author)Ritik KumarS2016-07-05

thank you =D

author
TunMaker (author)shivanshu.gupta2016-07-05

as he told you the code is below in the comments try it and tell me how it works

author
TunMaker (author)shivanshu.gupta2016-07-03

yes the comments just below were about that the code we used for 3 stepper motors did not test it but it should work the others told me it did here try it nad read the comments below you'll know what am talking about

[code]

/*

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 Arduino Plotter at tunisian maker youtube channel

https://www.youtube.com/channel/UCsFmFKMpUdyIiE55W3KsTzw

*/

#include <Stepper.h>

#define LINE_BUFFER_LENGTH 512

// Should be right for DVD steppers, but is not too important here

const int stepsPerRevolution = 20;

// create servo object to control a servo

// Initialize steppers for X- and Y-axis using this Arduino pins for the L293D H-bridge

Stepper myStepperY(stepsPerRevolution, 2,3,4,5);

Stepper myStepperX(stepsPerRevolution, 6,7,8,9);

Stepper myStepperZ(stepsPerRevolution, 10,11,12,13);

/* 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;

// Motor steps to go 1 millimeter.

// Use test sketch to go 100 steps. Measure the length of line.

// Calculate steps per mm. Enter here.

float StepsPerMillimeterX = 6;

float StepsPerMillimeterY = 6;

// 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 = 30;

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 );

// Decrease if necessary

myStepperX.setSpeed(60);

myStepperY.setSpeed(60);

myStepperZ.setSpeed(50);

// 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() {

myStepperZ.step(stepsPerRevolution);

Zpos=Zmax;

if (verbose) {

Serial.println("Pen up!");

}

}

// Lowers pen

void penDown() {

myStepperZ.step(-stepsPerRevolution);

Zpos=Zmin;

if (verbose) {

Serial.println("Pen down.");

}

}

[/code

author
shivanshu.gupta (author)TunMaker2016-07-03

Tyvm I will try this setup out. And report back if it works. :D

author
Ritik KumarS (author)2016-07-01

Sorry for the trouble .... I did solve the problem .. current was less so motors were not getting proper voltage ...just attached a parallel supply to arduino and noweverything working well andgood .Thanks for your support and help ...I hope i can come up again in future for any other coding or hardware problem.

author
TunMaker (author)Ritik KumarS2016-07-02

happy it works mate =) i have a youtube channel https://www.youtube.com/c/tunmaker follow me there and am happy to help anytime =)

author
Ritik KumarS (author)2016-07-01

it would be nice if u can just text me ur mail so that i can send u the video of it for more clarification

author
Ritik KumarS (author)2016-07-01

yeah i did see that and z axis is also working. The problem's like ......

Firstly , when i uploaded the program as given in the tutorial . My robot was making the drawing's 3/4th part correct (i.e; until pen up is reqd.)

But , now when i uploaded this program my robot is not moving just taking steps at its own place. I did change steps per revolution and rpm too but still no success.

author
Ritik KumarS (author)2016-07-01

thanks for the code sir but it's not working well .. movements are not as expected ........... pen is not moving as per gcode ... it starts well , reaches corner and then it just keeps on vibrating at its own place

author
TunMaker (author)Ritik KumarS2016-07-01

did you watch the third video about g-code?.the pen is trying to move from it's limit bacause the letters your trying to write aren't in a 4cm*4cm square or in the settings of the unicorn plugin that exports the gcode your starting from the centre and you should be starting from the front left corner of the plotter but is the z axis working ?

author
Ritik KumarS (author)2016-06-30

Thanks for replying . Here's the code i edited

#include

#define LINE_BUFFER_LENGTH 512

const int stepsPerRevolution = 180;
//Initializing steppers for x,y and z axes

Stepper myStepperX(stepsPerRevolution,2,3,4,5);
Stepper myStepperY(stepsPerRevolution,6,7,8,9);
Stepper myStepperZ(stepsPerRevolution,10,11,12,13);
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;

//Motor steps to go 1 millimeter.
//Use test sketch to go 100 steps.Measure the length of line.
// Calculate steps per mm.
float StepsPerMillimeterX = 6;
float StepsPerMillimeterY = 6;
float StepsPerMillimeterZ = 4;

// Drawing robot limits, in mm
//OK to start with . Could go up to 50mm if calibrated well.
float Xmin = 0;
float Xmax = 40;
float Ymin = 0;
float Ymax = 40;
float Zmin = 0;
float Zmax = 10;

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() {
Serial.begin(9600);
myStepperZ.setSpeed(60);
delay(200);

myStepperX.setSpeed(60);
myStepperY.setSpeed(60);

// 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() {
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 );
}
}
}



}
/************************************************
* 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 int sy = y0
long i;
long over = 0;

if (dx > dy) {
for (i=0; i myStepperX.step(sx);
over+=dy;
if (over>=dx) {
over-=dx;
myStepperY.step(sy);
}
delay(StepDelay);
}
}
else {
for (i=0; 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()
{
myStepper.step(stepsPerRevolution);
delay(200);
Zpos=Zmax;
if(verbose)
{
Serial.printIn("Pen up!");

}
}
//Lowers pen

void penDown()
{
myStepper.step(-stepsPerRevolution);
delay(200);
Zpos=Zmim;
if(verbose)
{
Serial.printIn("Pen down.");
}

}





}

author
TunMaker (author)Ritik KumarS2016-06-30

[code]

/*

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 Arduino Plotter at tunisian maker youtube channel

https://www.youtube.com/channel/UCsFmFKMpUdyIiE55W3KsTzw

*/

#include <Stepper.h>

#define LINE_BUFFER_LENGTH 512

// Should be right for DVD steppers, but is not too important here

const int stepsPerRevolution = 20;

// create servo object to control a servo

// Initialize steppers for X- and Y-axis using this Arduino pins for the L293D H-bridge

Stepper myStepperY(stepsPerRevolution, 2,3,4,5);

Stepper myStepperX(stepsPerRevolution, 6,7,8,9);

Stepper myStepperZ(stepsPerRevolution, 10,11,12,13);

/* 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;

// Motor steps to go 1 millimeter.

// Use test sketch to go 100 steps. Measure the length of line.

// Calculate steps per mm. Enter here.

float StepsPerMillimeterX = 6;

float StepsPerMillimeterY = 6;

// 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 = 30;

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 );

// Decrease if necessary

myStepperX.setSpeed(60);

myStepperY.setSpeed(60);

myStepperZ.setSpeed(50);

// 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() {

myStepperZ.step(stepsPerRevolution);

Zpos=Zmax;

if (verbose) {

Serial.println("Pen up!");

}

}

// Lowers pen

void penDown() {

myStepperZ.step(-stepsPerRevolution);

Zpos=Zmin;

if (verbose) {

Serial.println("Pen down.");

}

}

[/code]

author
TunMaker (author)TunMaker2016-06-30

don't forget to tweak the steps per revoluion and speed of z stepper i can't recommand numbers for floppy drive

author
Ritik KumarS (author)2016-06-29

i've also started making this but using floppy drive motor instead of dc for pen up and down . Struggling while coding with following problems .It would be nice if u help me out to sort these

Arduino: 1.6.9 (Windows 8), Board: "Arduino/Genuino Uno"

C:\Users\MARK-42\Documents\Arduino\testingg1\testingg1.ino: In function 'void processIncomingLine(char*, int)':

testingg1:175: error: 'penUp' was not declared in this scope

penUp();

^

C:\Users\MARK-42\Documents\Arduino\testingg1\testingg1.ino: In function 'void penUP()':

testingg1:360: error: 'myStepper' was not declared in this scope

myStepper.step(stepsPerRevolution);

^

testingg1:365: error: 'class HardwareSerial' has no member named 'printIn'

Serial.printIn("Pen up!");

^

C:\Users\MARK-42\Documents\Arduino\testingg1\testingg1.ino: In function 'void penDown()':

testingg1:373: error: 'myStepper' was not declared in this scope

myStepper.step(-stepsPerRevolution);

^

testingg1:378: error: 'class HardwareSerial' has no member named 'printIn'

Serial.printIn("Pen down.");

^

C:\Users\MARK-42\Documents\Arduino\testingg1\testingg1.ino: At global scope:

testingg1:387: error: expected declaration before '}' token

}

^

exit status 1

'penUp' was not declared in this scope

This report would have more information with

"Show verbose output during compilation"

option enabled in File -> Preferences.

author
TunMaker (author)Ritik KumarS2016-06-29

hi,sorry about the delay of my answer so as i understood you you won't to swap the dc motor with a stepper motor of a floppy drive some changes of the code will be necessary so don't send me the error message send me your code and i'll repair it's easier that way ok ?

author
Syed AbdurR (author)2016-05-17

which pen did you use

author
TunMaker made it! (author)Syed AbdurR2016-05-17

i dont know pen types but using a hair pen will work except it doesn't draw very good because it needs to be flexible so i used a drawing pen like this one.this kind worked for me very good also check how plane is your working surface that may cause a problem

prettybaby-24-colors-fineliner-pens-art-sketch.jpg
author
Syed AbdurR (author)2016-05-17

i made it but first i used sketch pen and pencil then a ball point pen my brother suggested to use a gel pen so going to place a gel pen in the place of sketch pen

author
abbash4 made it! (author)2016-05-13

this is my plotter.how did you find + and - sign with voltmeter . and my motors did not moving

13187907_1020402324717792_1249061296_n.jpg
author
TunMaker (author)abbash42016-05-14

i wasnt looking for positive and negative side i was looking for connected leads of the stepper motors watch the video you ll understand

author
Syed AbdurR (author)2016-05-13

hello! thank you for the advise i did get the z axis. but my x axis joint with motor is stucking in the middle of it.if push a little it moves again please send a reply what to do?

author
TunMaker (author)Syed AbdurR2016-05-13

the board is suuplied from where

author
Syed AbdurR (author)2016-05-12

hey can you give me the measurements between the surface and the tip of the pen please

author
TunMaker (author)Syed AbdurR2016-05-12

the pen is loose before i start the plotter i adjust it so that the pen is barely touching the surface

author
TunMaker (author)TunMaker2016-05-12

when the pen is low of course i tighten that position then i move it up again

author
JokūbasG (author)2016-04-23

hello your project is awesome but can i make it only with 2 l293d motor drives

sorry for my ban english :D

author
TunMaker (author)JokūbasG2016-04-23

Then you'll need to replace the third one with two transistor basicly you need an another H bridge for the dc motor of the z axis

author
hgautam1 (author)2016-03-20

Hey I have modified my previous design with yours DC motor design but struggling with the same glitches :-(
My x axis and y axis are not responding well... Even without a pen... And I followed all your suggestions
Please help me in this regard :-(
If u would ask I will also share the video with you... But please suggest something...

temp_-1961971099.jpg
author
TunMaker (author)hgautam12016-03-20

Have you tried the motors individually just testing. ?

author
hgautam1 (author)TunMaker2016-03-20

Yes it worked at that time... But after that they are just giving the glitches as if they are stucked in between... They are only vibrating at one point.... But it worked with the test codes of individual axis...

author
TunMaker (author)hgautam12016-03-21

i think it's the gcode. it happend to me when i used the wrong version of inkscape and the correct version is 0.48.5 anyway try this gcode file and tell me what happend and start the plotter in the front left.

https://drive.google.com/file/d/0B3LrxyvfzQHHQ0Z0cnlDdWlrYlE/view?usp=sharing

author
hgautam1 (author)TunMaker2016-03-24

Sir this code is also not running.. I think I m getting stucked due to the power supply... As when I puts a very little pressure with my finger tip it moves easily... So would please tell me how did you powered your machine, with how much voltage and amperes...

author
TunMaker (author)hgautam12016-03-27

i used 5v usb from my pc

author
221way (author)2016-03-14

what a good tutor,I'll built it tomorrow

author
TunMaker (author)221way2016-03-14

thanks if you need anything just ask

author
hgautam1 (author)2016-03-08

Hey I am also building the same project but my y or x axis sometimes gets stucked in between the operation... I am not being able to figure out why it's happening so could you help in this regard?? Here it is the result in the image
Please help if you can

temp_342123852.jpgtemp_892876008.jpg
author
TunMaker (author)hgautam12016-03-08

you didnt give much information and my plotter tuto is without a servo but

the problem can be either the g-code your using isn't good you should check it try this visualizer or it can most probably the pen has too much friction with the paper so it gets stuck because either the pen is pushed against the paper too hard or the paper isn't plane enough if this is the situation try to make holder your using more loose so that the pen can move on the paper easily hope i helped watch my pen holder it s pushed against the paper with a loose elastic so i barely touches the paper but writes very good

About This Instructable

16,372views

263favorites

More by TunMaker:Hand Made SpeedBallMini Drilling StationMini arduino Plotter without a servo motor
Add instructable to: