VitorPoncellleo29995
Published © GPL3+

LVDrawer

CNC drawing machine made with DVD step motors.

IntermediateProtip836
LVDrawer

Things used in this project

Story

Read more

Custom parts and enclosures

Structure

Schematics

circuit_s53Q9R3Dw0.jpg

Code

CNC

Arduino
#include <Servo.h>
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define LINE_BUFFER_LENGTH 512

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
float total = 1.0;
String lcdText = "";
String percentText = "";
float cont = 0.0;
float percent = 0.0;



// Servo position for Up and Down 
const int penZUp = 30;
const int penZDown = 120;

// Servo on PWM pin 8
const int penServoPin = 8;

const int stepsPerRevolution = 20; 

Servo penServo;  

// Initialize steppers for X- and Y-axis using this Arduino pins for the L293D H-bridge
Stepper myStepperY(stepsPerRevolution, 9,12,10,13);            
Stepper myStepperX(stepsPerRevolution, 7,4,3,6);  

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;

// Calculate steps per mm. Enter here.
float StepsPerMillimeterX = 8.0;
float StepsPerMillimeterY = 8.0;

// Drawing robot limits, in mm
float Xmin = 0;
float Xmax = 22;
float Ymin = 0;
float Ymax = 22;
float Zmin = 0;
float Zmax = 1;

float Xpos = Xmin;
float Ypos = Ymin;
float Zpos = Zmax; 

// Set to true to get debug output.
boolean verbose = true;

//  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 );
  lcd.begin (16,2);
  lcd.setBacklight(HIGH);
  
  penServo.attach(penServoPin);
  penServo.write(penZUp);
  delay(200);

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

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

  //  Notifications!!!
  Serial.println("Ready to print");
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Ready to print");
}


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
    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;
        } 
        
        lineIsComment = false;
        lineSemiColon = false;
        Serial.println("ok");    
      } 
      else {
        if ( (lineIsComment) || (lineSemiColon) ) {   
          if ( c == ')' )  lineIsComment = false;     
        } 
        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;

  //  G1 for moving
  //  G1 X60 Y30
  //  G1 X30 Y50
  //  M300 S30 (pen down)
  //  M300 S50 (pen up)

  while( currentIndex < charNB ) {
    switch ( line[ currentIndex++ ] ) {              
    case 'G':
      buffer[0] = line[ currentIndex++ ];          
      //      buffer[1] = line[ currentIndex++ ];
      //      buffer[2] = '\0';
      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;
      }
        cont++;
        lcdPrint();
      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 );
      }
      cont++;
      lcdPrint();
    break;
    case 'C':
        Serial.println("flag");
        buffer[0] = line[ currentIndex++ ];        
        buffer[1] = line[ currentIndex++ ];
        buffer[2] = line[ currentIndex++ ];
        buffer[3] = line[ currentIndex++ ];
        buffer[4] = '\0';
        total = atoi(buffer);

      break;
    }
  }



}


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

void lcdPrint(){
  percent = (cont/total)*100;
  if(percent<10){
    percentText = "0"+String((int)percent)+"%";
  }else{
    percentText = String((int)percent)+"%";
  }
  if(!lcdText.equals(percentText)){
    lcdText = percentText;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(lcdText);
  }
  if(lcdText.equals("100%")){
    cont = 0;
    lcdText = "Finish";
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(lcdText);
  }
    Serial.println(percentText);
}

gcode sender

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

Serial port = null;

String portname = 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 the serial port that corresponds to your Arduino board.",
    "Select serial port",
    JOptionPane.QUESTION_MESSAGE,
    null,
    Serial.list(),
    0);
    
  if (result != null) {
    portname = result;
    openSerialPort();
  }
}

void setup()
{
  size(300, 100);
  openSerialPort();
}

void draw()
{
  background(155);  
  fill(0);
  int y = 24, dy = 12;
  text("p: select serial port", 12, y); y += dy;
  text("g: stream a g-code file", 12, y); y += dy;
  text("x: stop streaming g-code", 12, y); y += dy;
  text("c: resume streaming g-code", 12, y); y += dy;
  text("current serial port: " + portname, 12, y); y -= dy;
}

void keyPressed()
{  
  if (!streaming) {
    if (key == 'p') selectSerialPort();
    if (key == 'g') {
    gcode = null; i = 0;
    File file = null; 
    println("Loading file...");
    selectInput("Select a file to process:", "fileSelected", file);
    }
  }
  
  if (key == 'x') streaming = false;
  if (key == 'c') streaming = true; stream();
}

void fileSelected(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    println("User selected " + selection.getAbsolutePath());
    gcode = loadStrings(selection.getAbsolutePath());
    if (gcode == null) return;
    streaming = true;
    sendSize();
  }
}

void sendSize(){
  int total = 0;
  for(String string : gcode){
    if(string.length()>0){
      if(string.charAt(0)=='G'||string.charAt(0)=='M'){
        total++;
      }
    }
  }
  if(total<10){
    port.write("C000"+total+'\n');
  }else if(total<100){
        port.write("C00"+total+'\n');
  }else if(total<1000){
        port.write("C0"+total+'\n');
  }else if (total <10000){
        port.write("C"+total+'\n');
  }else {
    port.write("C9999"+'\n');
  }
  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")) stream();
  if (s.trim().startsWith("error")) stream(); // XXX: really?
}

Credits

VitorPoncell
0 projects • 1 follower
leo29995
0 projects • 0 followers

Comments