ARDUINO 2D PRINTER

by Boateng in Circuits > Arduino

2436 Views, 28 Favorites, 0 Comments

ARDUINO 2D PRINTER

Arduino 2d printer
IMG_20231021_154716_114.jpg
IMG_20231021_154731_908.jpg

Hello, I'm Isaac Boateng, an electrical and electronics engineering student at Kwame Nkrumah University of Science and Technology, Ghana. Join me in this step-by-step guide to creating your own Arduino-controlled 2D printer, bringing art to life with technology. 

Supplies

Materials

  • Scrap DVD Drive
  • Arduino uno
  • L293D motor shield
  • Mini servo motor
  • Connecting wires
  • Pen
  • Solar led(green)
  • Wood

Tools

  • Screwdriver
  • Soldering iron
  • Drilling machine(optional)
  • JIG saw(optional)
  • Hot glue gun and sticks
  • Tape Measur

Software

Screenshot (83).png
Screenshot (81).png
Screenshot (87).png
Screenshot (90).png

Before building, use Fusion 360 to design your 2D printer. This step ensures precision, identifies potential challenges, and allows for iterative improvements. I opted for Fusion 360 to visualize the entire assembly, including servo motor placement and frame structure.

Building Your Printer Frame and Installing Components

IMG-20231022-WA0028.jpg
IMG-20231022-WA0033.jpg
IMG-20231022-WA0030.jpg
IMG-20231022-WA0052.jpg
IMG-20231022-WA0037.jpg
IMG-20231022-WA0061.jpg
WhatsApp Image 2023-10-23 at 16.54.28_03e07903.jpg
WhatsApp Image 2023-10-23 at 16.54.29_73000afd.jpg
WhatsApp Image 2023-10-23 at 16.54.27_9ee32535.jpg

Crafting the Frame:

  • Use the Fusion 360 design as a guide to cut and assemble plywood pieces.
  • Pay attention to precision for a seamless fit.

Component Placement:

  • Refer to the Fusion 360 design to position stepper motors, Arduino board, and peripherals.

Secure Attachments:

  • Fasten components to the frame securely to prevent vibrations.
  • Organize and route wires neatly.

Preliminary Test:

  • Conduct an initial test to verify proper connections before finalizing the assembly.

Final Check:

  • Confirm alignment with the Fusion 360 design.
  • Ensure the frame is sturdy without any flexing.


Connecting L293D Shield to DVD Motor and Servo Motor

WhatsApp Image 2024-01-08 at 14.50.11_eb68f6a8.jpg
WhatsApp Image 2024-01-08 at 14.50.06_b69314cd.jpg
WhatsApp Image 2024-01-08 at 14.50.09_eb49c22e.jpg
WhatsApp Image 2024-01-08 at 14.50.07_ff21cc9e.jpg

Now, let's move on to the practical implementation. In this step, you'll connect the L293D shield to the DVD motor. Additionally, you'll integrate the servo motor into the system. This step is crucial for enabling precise control over the movement of your 2D printer.

Connecting the L293D Shield to DVD Motor:

  1. Detach DVD Motor: Remove the DVD motor carefully from its original housing, ensuring you preserve the necessary components.
  2. L293D Shield Connection: Connect the DVD motor to the L293D motor driver shield. The L293D shield acts as an interface between the Arduino and the motor, facilitating controlled movements.
  3. Wiring Configuration: Ensure the wiring follows the specifications of the L293D shield and the DVD motor. This step establishes a reliable connection for seamless integration.

Adding the Servo Motor:

  1. Servo Motor Integration: Integrate the servo motor into the system. The servo motor's precise control is invaluable for certain functionalities, adding versatility to your 2D printer.
  2. Secure Connection: Ensure a secure and stable connection between the servo motor and the control board. This step is vital for accurate positioning during the printing process.

Testing Connections:

  1. Verify Wiring: Double-check all connections to ensure accuracy. Miswiring can lead to erratic behavior, so attention to detail is crucial.
  2. Test Movements: Execute simple movements using the Arduino code to verify that the DVD motor and servo motor respond as expected. This preliminary testing ensures that your hardware setup is on the right track.


Uploading Code to the Arduino Board

IMG_20231021_141735_622.jpg
IMG_20231021_141726_832.jpg
IMG_20231021_141712_872.jpg

Now, let's move on to programming the Arduino board to bring intelligence to our 2D printer.

  1. Accessing the Code:
  • Retrieve the Arduino code designed for the 2D printer. This code dictates how the Arduino controls the stepper motors for precise movements.
  1. Connecting Arduino:
  • Use a USB cable to connect the Arduino board to your computer.
  1. Opening Arduino IDE:
  • Launch the Arduino IDE on your computer.
  1. Loading the Code:
  • Open the code file in the Arduino IDE.
  • Review the code to understand its functionality.
  1. Selecting Board and Port:
  • In the Arduino IDE, choose the correct board type (e.g., Arduino Uno) and port.
  1. Uploading Code:
  • Click the "Upload" button to transfer the code to the Arduino board.
  • Monitor the IDE for any error messages.
  1. Verifying Success:
  • Confirm a successful upload by checking for the "Done uploading" message in the IDE.
//Isaac Boateng
//Arduino 2d printer
#include <Servo.h>//make sure to install the libraries on your IDE


#include <AFMotor.h>


#define LINE_BUFFER_LENGTH 512


char STEP = MICROSTEP ;

const int penZUp = 120;
const int penZDown = 50;


const int penServoPin =10 ;


const int stepsPerRevolution = 48;


Servo penServo;  

AF_Stepper myStepperY(stepsPerRevolution,1);            
AF_Stepper myStepperX(stepsPerRevolution,2);  


struct point {
  float x;
  float y;
  float z;
};

struct point actuatorPos;


float StepInc = 1;
int StepDelay = 1;
int LineDelay =0;
int penDelay = 50;

float StepsPerMillimeterX = 100.0;
float StepsPerMillimeterY = 100.0;
 
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;

boolean verbose = false;

void setup() {
 
  Serial.begin( 9600 );
 
  penServo.attach(penServoPin);
  penServo.write(penZUp);
  delay(100);


  myStepperX.setSpeed(600);


  myStepperY.setSpeed(600);  
 


 


 
  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(100);
  char line[ LINE_BUFFER_LENGTH ];
  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 >= LINE_BUFFER_LENGTH-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] = '\0';


      switch ( atoi( buffer ) ){                  
      case 0:                                  
      case 1:
       
        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 );
   
        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("");
  }  


  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);
  y1 = (int)(y1*StepsPerMillimeterY);
  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) {
      myStepperX.onestep(sx,STEP);
      over+=dy;
      if (over>=dx) {
        over-=dx;
        myStepperY.onestep(sy,STEP);
      }
    delay(StepDelay);
    }
  }
  else {
    for (i=0; i<dy; ++i) {
      myStepperY.onestep(sy,STEP);
      over+=dx;
      if (over>=dy) {
        over-=dy;
        myStepperX.onestep(sx,STEP);
      }
      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;
}


//  Raises pen
void penUp() {
  penServo.write(penZUp);
  delay(penDelay);
  Zpos=Zmax;
  digitalWrite(15, LOW);
    digitalWrite(16, HIGH);
  if (verbose) {
    Serial.println("Pen up!");
   
  }
}


void penDown() {
  penServo.write(penZDown);
  delay(penDelay);
  Zpos=Zmin;
  digitalWrite(15, HIGH);
    digitalWrite(16, LOW);
  if (verbose) {
    Serial.println("Pen down.");
   
   
  }
}


Image to G-code: Inkscape Conversion

Picture4.png
Picture3.png
Picture2.png

Now, let's integrate images into our project using Inkscape and convert them into G-code for the 2D printer, Open Inkscape and import the image you want to draw with your 2D printer.

Image Adjustments:

  • Scale, resize, or make any necessary adjustments to the image within Inkscape.
  • Ensure that the image fits within the printable area defined by your Fusion 360 design.

Tracing Image:

  • Utilize the tracing function in Inkscape to convert the image into a vector format.
  • Adjust settings to achieve the desired level of detail.

Saving as SVG:

  • Save the traced image as an SVG file. This file format is essential for G-code generation.

Generating G-code:

  • Use a G-code generator, such as CNCjs or another appropriate software, to convert the SVG file into G-code.
  • Configure the G-code parameters based on your printer's specifications.

Saving G-code:

  • Save the generated G-code file to a location accessible by your 2D printer.


Printing Your Design With Pronterface

Picture1.png

Pronterface serves as a user-friendly interface to seamlessly upload the G-code representation of your design, configure essential print settings, and then initiate the printing process on your Arduino-controlled 2D printer, transforming your digital creations into tangible and precise artwork.