산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용

필요한 부품 및 소모품

필요한 소프트웨어

제작 과정

1. DVD 드라이브 분해 및 기계 프레임 구성

DVD 드라이브 2대에서 내부 이동 트레이(스테퍼 모터 포함)를 분리합니다. 하나는 X축, 다른 하나는 Y축으로 사용합니다. 빈 DVD 케이스에 너트/볼트로 각 축을 고정하고 수직으로 교차 배치합니다.

폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용

2. G-code 생성 환경 설정 (Inkscape)

  1. Inkscape 0.48.5 설치 (구버전 다운로드)
  2. G-code 내보내기 애드온 설치: gcodetools (설치 설명 포함)
  3. Inkscape에서 벡터 그래픽 작성/불러오기 → 확장 → Gcodetools → Path to G-code로 변환.

유튜브 튜토리얼 검색 권장: "Inkscape gcode cnc plotter"

3. 펌웨어 업로드 및 실행

  1. 아두이노 IDE에 'Arduino CNC 코드' 업로드.
  2. Processing IDE에 'Processing 코드' 업로드 후 실행 (Play 버튼).
  3. Processing 창에서:
    • p - 시리얼 포트 선택
    • 1/2/3 - 조그 속도 설정 (1/10/100 mil)
    • g - G-code 파일 선택
    • Enter - 플로팅 시작

4. 테스트용 G-code 파일

폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용
폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용
폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용

아두이노 펌웨어 코드

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

#define LINE_BUFFER_LENGTH 512
char STEP = MICROSTEP;

const int penZUp = 95;
const int penZDown = 83;
const int penServoPin = 10;
const int stepsPerRevolution = 48;

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

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

float StepInc = 1;
int StepDelay = 0, LineDelay = 0, penDelay = 50;
float StepsPerMillimeterX = 100.0;
float StepsPerMillimeterY = 100.0;
float Xmin = 0, Xmax = 40, Ymin = 0, Ymax = 40, Zmin = 0, Zmax = 1;
float Xpos = Xmin, Ypos = Ymin, 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: "); Serial.print(Xmin); Serial.print(" to "); Serial.print(Xmax); Serial.println(" mm.");
  Serial.print("Y range: "); 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 = 0; bool lineIsComment = false, lineSemiColon = 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 { 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 = 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 = newPos.y = 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: { // G0/G1 이동
            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);
            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);
        } break;
    }
  }
}

void drawLine(float x1, float y1) {
  if (x1 >= Xmax) x1 = Xmax; if (x1 <= Xmin) x1 = Xmin;
  if (y1 >= Ymax) y1 = Ymax; if (y1 <= Ymin) y1 = Ymin;
  x1 = (int)(x1 * StepsPerMillimeterX);
  y1 = (int)(y1 * StepsPerMillimeterY);
  float x0 = Xpos, y0 = Ypos;
  long dx = abs(x1-x0), dy = abs(y1-y0);
  int sx = x0 dy) {
    for (i=0; i=dx) { over-=dx; myStepperY.onestep(sy,STEP); } delay(StepDelay); }
  } else {
    for (i=0; i=dy) { over-=dy; myStepperX.onestep(sx,STEP); } delay(StepDelay); }
  }
  delay(LineDelay);
  Xpos = x1; Ypos = y1;
}

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."); }

Processing 스트리머 코드

import java.awt.event.KeyEvent;
import javax.swing.JOptionPane;
import processing.serial.*;

Serial port = null;
String portname = null; // null이면 대화형 선택
boolean streaming = false;
float speed = 0.001;
String[] gcode; int i = 0;

void openSerialPort() { if (portname == null) return; if (port != null) port.stop(); port = new Serial(this, portname, 9600); port.bufferUntil('\n'); }
void selectSerialPort() { String result = (String)JOptionPane.showInputDialog(frame, "Select serial port for Arduino.", "Select serial port", JOptionPane.QUESTION_MESSAGE, null, Serial.list(), 0); if (result != null) { portname = result; openSerialPort(); } }

void setup() { size(500, 250); openSerialPort(); }

void draw() {
  background(0); fill(255);
  int y = 24, dy = 12;
  text("INSTRUCTIONS", 12, y); y += dy;
  text("p: select serial port", 12, y); y += dy;
  text("1: 1 mil/jog, 2: 10 mil/jog, 3: 100 mil/jog", 12, y); y += dy;
  text("arrows: jog XY, PgUp/PgDn: jog Z", 12, y); y += dy;
  text("$: grbl settings, h: home, 0: zero", 12, y); y += dy;
  text("g: stream gcode, x: stop streaming", 12, y); y += dy;
  y = height - dy;
  text("jog speed: " + speed + " in/step", 12, y); y -= dy;
  text("port: " + portname, 12, y);
}

void keyPressed() {
  if (key == '1') speed = 0.001;
  if (key == '2') speed = 0.01;
  if (key == '3') speed = 0.1;
  if (!streaming) {
    if (keyCode == LEFT)  port.write("G91\nG20\nG00 X-" + speed + " Y0 Z0\n");
    if (keyCode == RIGHT) port.write("G91\nG20\nG00 X" + speed + " Y0 Z0\n");
    if (keyCode == UP)    port.write("G91\nG20\nG00 X0 Y" + speed + " Z0\n");
    if (keyCode == DOWN)  port.write("G91\nG20\nG00 X0 Y-" + speed + " Z0\n");
    if (keyCode == KeyEvent.VK_PAGE_UP)   port.write("G91\nG20\nG00 X0 Y0 Z" + speed + "\n");
    if (keyCode == KeyEvent.VK_PAGE_DOWN) port.write("G91\nG20\nG00 X0 Y0 Z-" + speed + "\n");
    if (key == 'h') port.write("G90\nG20\nG00 X0 Y0 Z0\n");
    if (key == '0') openSerialPort();
    if (key == 'p') selectSerialPort();
    if (key == '$') port.write("$$\n");
  }
  if (!streaming && key == 'g') { gcode = null; i = 0; println("Loading file..."); selectInput("Select a file to process:", "fileSelected", null); }
  if (key == 'x') streaming = false;
}

void fileSelected(File selection) {
  if (selection == null) { println("Cancelled."); }
  else { println("Selected: " + selection.getAbsolutePath()); gcode = loadStrings(selection.getAbsolutePath()); if (gcode != null) { streaming = true; stream(); } }
}

void stream() {
  if (!streaming) return;
  while (true) { if (i == gcode.length) { streaming = false; return; } if (gcode[i].trim().length() == 0) i++; else break; }
  println(gcode[i]); port.write(gcode[i] + '\n'); i++;
}

void serialEvent(Serial p) {
  String s = p.readStringUntil('\n'); println(s.trim());
  if (s.trim().startsWith("ok") || s.trim().startsWith("error")) stream();
}

회로도

폐 DVD 드라이브로 미니 CNC 2D 플로터 만들기 - L293D 모터 쉴드 활용

팁 및 주의사항

제조공정

  1. 집에서 직접 만드는 DIY 숙제 작성 머신 (펜 플로터)
  2. 아두이노로 모니터 앰비라이트(Ambilight) 시스템 직접 만들기
  3. 아두이노 우노 기반 인간 감지 로봇: 센서 통합 단계별 가이드
  4. 라즈베리파이 모터 쉴드로 만드는 안드로이드 제어 원격 주행 로봇
  5. 아두이노로 만드는 맞춤형 '펀처블' 키보드 버튼: 단계별 가이드
  6. 아두이노로 지구 질량 측정하기: 단계별 DIY 가이드
  7. PHP 기반 웹 인터페이스로 아두이노 우노 제어하기
  8. 아두이노로 음악 톤 만들기: 88키 피아노 음계 구현 가이드
  9. 초음파 센서로 아두이노 자동문 만들기
  10. 아두이노와 프로세싱으로 실시간 디지털 나침반 만들기