Tinkers Projects

Imagine | Develop | Create

CNC Plasma CutterMain
CNC Plasma Cutter0
CNC Plasma Cutter1
CNC Plasma Cutter2
CNC Plasma Cutter3
CNC Plasma Cutter4
CNC Plasma Cutter5
CNC Plasma Cutter6
CNC Plasma Cutter7
CNC Plasma Cutter8
CNC Plasma Cutter9
I have wanted a plasma cutter for a while and instead of buying one I decided to make one. The 2D plotter machine did not take too long to make and has been sitting around for about a year while I was getting all other parts required for cutting. I did not have trouble getting the plasma cutting head/nozzle but have many problems getting the right hoses. After getting the correct parts can some modification to them the cutter was working. The next problem was the controller for the machine. I wanted to challenge myself and build the controller including the hardware and software. Since the plasma machine gives off lots of EMS, the controller would restart itself whenever the plasma was turned on. I fixed this by putting the controller in a metal box and stopped communication over USB (G-code on SD card) and this fixed the problem can I could test the cuts. The from is made from linear rails and powered by 2 NEMA17 motors in a CoreXY configuration. The controller is PLEX with the Arduino bootloader. You can get the PLEX controller from https://www.tindie.com/products/williambailes/plex-controller/ The controller uses the G-Code Arduino Library to read the G-code from the SD card. The G-Code Arduino Library can be found https://github.com/tinkersprojects/G-Code-Arduino-Library

Code for Project

GNU General Public License v3.0
#include "config.h"
#include <SPI.h>
#include <SD.h>
//http://marlinfw.org/meta/gcode/
#include <Arduino.h>
#include <gcode.h>
#include <AccelStepper.h>
#include <Plex.h>


#define XMultiplier 80
#define YMultiplier 80
#define CircleRatio 1

#define Speed 1000 //200
#define Acceleration 50000000.0

#define XUpperLimitSelect 2
#define XLowerLimitSelect 3
#define YUpperLimitSelect 4
#define YLowerLimitSelect 5
#define scrollButton 6
#define selectButton 7

#define ASelect 1
#define BSelect 7

//#define machineInputPin A7
//#define UserInputPin A4

#define StepPin A2
#define DirPin A5
#define enablePin A4
#define PlasmaPin 3
#define SDchipSelectPin 10


const float pi = 3.14159265359;


double X;
double Y;
int encoder0Pos = 0;
int encoder0PinALast = LOW;

unsigned long currentPosInFile = 0;
bool Running = false;
long inPos = 0;

void homeMachine();
void PlasmaOn();
void PlasmaOff();
void Enabled();
void Disabled();
commandscallback commands[6] = {{"G28",homeMachine},
                                {"M3",PlasmaOn},
                                {"M4",PlasmaOn},
                                {"M5",PlasmaOff},
                                {"M17",Enabled},
                                {"M18",Disabled}};


gcode Commands(6,commands);
AccelStepper stepperA;
AccelStepper stepperB;
Plex PLEX;

File dir;
File Files;

int FileNumber = 0;
bool displayed = false;

void setup()
{
  pinMode(enablePin, OUTPUT);
  pinMode(PlasmaPin, OUTPUT);
  digitalWrite(enablePin,HIGH);
  digitalWrite(PlasmaPin,LOW);

  Commands.begin("OK");
  
  stepperA = AccelStepper(1,StepPin,DirPin);
  stepperA.setMaxSpeed(Speed);
  stepperA.setCurrentPosition(0);
  stepperA.setAcceleration(Acceleration);
  
  stepperB = AccelStepper(1,StepPin,DirPin);
  stepperB.setMaxSpeed(Speed);
  stepperB.setCurrentPosition(0);
  stepperB.setAcceleration(Acceleration);
  
  PLEX.lcd.setCursor(0, 0);
  PLEX.lcd.print("Start");
}

void loop() 
{
  if (!SD.begin(SDchipSelectPin)) 
  {
    PLEX.lcd.setCursor(0,0);
    PLEX.lcd.print("can not read SD!");
    PLEX.lcd.setCursor(0,1);
    PLEX.lcd.print("                ");
    displayed = false;
    FileNumber=0;
  }
  else
  {
    if (PLEX.digitalReadC(scrollButton) == HIGH) 
    {
      Serial.println("scrollButton");
      while(PLEX.digitalReadC(scrollButton) == HIGH);
      FileNumber++;
      displayed = false;
    }

    if(displayed)
    {
      if(FileNumber<0)
        FileNumber=0;
      Files = getFile(FileNumber); 
      if(Files) 
      {
        PrintFileName(Files);
        displayed = true; 
      }
      else if(FileNumber == 0) 
      {
        PLEX.lcd.clear();
        PLEX.lcd.print("can not read SD!");
        displayed = true; 
      }
      else
      {
        FileNumber--;
      }
    }

    if (PLEX.digitalReadC(selectButton) == HIGH) 
    {
      Serial.println("selectButton");
      File dataFile = getFile(FileNumber);
      if(dataFile) 
      {
        Enabled();
        while (dataFile.available()) 
        {
          char SDinput = dataFile.read();
          Serial.print(SDinput);
          if(Commands.available(SDinput))
          {
            if(Commands.GetValue('G') == 0 || Commands.GetValue('G') == 1)
            {      
              Serial.println("gotoLocation");
              gotoLocation();
            }
            else if(Commands.GetValue('G') == 2)      
              arc(1);
            else if(Commands.GetValue('G') == 3)      
              arc(0);
            else if(Commands.GetValue('G') == 4)      
            {
              if(Commands.availableValue('S'))
                delay(Commands.GetValue('S')*1000);
              else if(Commands.availableValue('P'))
                delay(Commands.GetValue('P'));
            }
            Commands.clearBuffer();
          }
        }
        Disabled();
      }
      dataFile.close();
      PLEX.lcd.clear();
      PLEX.lcd.print("    Finished    ");
      displayed = false;
      while(PLEX.digitalReadC(selectButton) == LOW);
      while(PLEX.digitalReadC(selectButton) == HIGH);
    }
  }
}


File getFile(int number) 
{
  int count = 0;
  dir = SD.open("/");
  while (true) 
  {
    File entry =  dir.openNextFile();
    if (!entry.available())
    {
      return entry;
      break;
    }
    if (!entry.isDirectory()) 
    {
      if(number == count)return entry;
      count++;
    }
    entry.close();
  }
  return;
}



void PrintFileName(File entry) 
{
    PLEX.lcd.clear();
    PLEX.lcd.setCursor(0,0);
    PLEX.lcd.print(entry.name());
    PLEX.lcd.setCursor(0,1);
    PLEX.lcd.print(entry.size(), DEC);
}




/****************************** MACHINE CONTROL ******************************/

void PlasmaOn()
{
  digitalWrite(PlasmaPin,HIGH);
  PLEX.lcd.clear();
  PLEX.lcd.print("Plasma on");
}

void PlasmaOff()
{
  digitalWrite(PlasmaPin,LOW);
  PLEX.lcd.clear();
  PLEX.lcd.print("Plasma off");
}

void Enabled()
{
  digitalWrite(enablePin,LOW);
  PLEX.lcd.setCursor(0, 0);
  PLEX.lcd.print("Enabled");
}

void Disabled()
{
  digitalWrite(enablePin,HIGH);
  PLEX.lcd.setCursor(0, 0);
  PLEX.lcd.print("Disabled");
}











/****************************** MOTOR CONTROL ******************************/

void gotoLocation()
{
  if(Commands.availableValue('X'))
    X = Commands.GetValue('X');

  if(Commands.availableValue('Y'))
    Y = Commands.GetValue('Y');

  setMotorLocation(X,Y);
  runStepperMotors();
}

void arc(boolean clockwise)
{
  /*
    http://marlinfw.org/docs/gcode/G002-G003.html

    ****** I,J Form ******
      - I specifies an X offset. J specifies a Y offset.
      - At least one of the I J parameters is required.
      - X and Y can be omitted to do a complete circle.
      - The given X Y is not error-checked. The arc ends based on the angle of the destination.
      - Mixing I or J with R will throw an error.
    ****** R Form ******
      - R specifies the radius. X or Y is required.
      - Omitting both X and Y will throw an error.
      - X or Y must differ from the current XY position.
      - Mixing R with I or J will throw an error.
  */
/* 
  double startX = X;
  double startY = Y;
  double finishedX = X;
  double finishedY = Y;
  double XCenter = X;
  double YCenter = Y;
  double radius = 0;

  //I,J Form
  if((Commands.availableValue('I') || Commands.availableValue('J')) && !Commands.availableValue('R'))
  {
    double J = 0;
    double I = 0;

    if(Commands.availableValue('I')) 
      I = Commands.GetValue('I');

    if(Commands.availableValue('J')) 
      J = Commands.GetValue('J');

    if(Commands.availableValue('X'))
    {
      finishedX = Commands.GetValue('X');
    }
    if(Commands.availableValue('Y'))
    {
      finishedY = Commands.GetValue('Y');
    }

    XCenter = startX + I;
    YCenter = startY + J;
    radius = sqrt(J*J+I*I);
  }
  // R Form
  else if (!Commands.availableValue('I') && !Commands.availableValue('J') && Commands.availableValue('R') && (Commands.availableValue('X') || Commands.availableValue('Y')))
  {
    if(Commands.availableValue('R')) 
      radius = Commands.GetValue('R');

    if(Commands.availableValue('X'))
    {
      finishedX = Commands.GetValue('X');
    }
    if(Commands.availableValue('Y'))
    {
      finishedY = Commands.GetValue('Y');
    }
/*
    double q = sqrt((finishedX-startX)*(finishedX-startX) + (finishedY-startY)*(finishedY-startY));

    double x1 = x3 + sqrt(r^2-(q/2)*(q/2))*(startY-finishedY)/q
    double y1 = y3 + sqrt(r^2-(q/2)*(q/2))*(finishedX-startX)/q 

    double x2 = x3 - sqrt(r^2-(q/2)*(q/2))*(startY-finishedY)/q
    double y2 = y3 - sqrt(r^2-(q/2)*(q/2))*(finishedX-startX)/q

    XCenter = x1;
    YCenter = x2;* /
  }

  float startAngle = atan2((startY-YCenter), (startX-XCenter));
  float finishedAngle = atan2((finishedY-YCenter), (finishedX-XCenter));
  
  float Curcunfrence = atan2((finishedY-YCenter), (finishedX-XCenter));

  double AngleBetween = startAngle - finishedAngle;
  if(startAngle == finishedAngle)
    AngleBetween = 2*pi;
  
 /* if(clockwise)
    AngleBetween = 2*pi - AngleBetween; 
* /

  double NewfinishedAngle = startAngle - finishedAngle;

/*
  if(clockwise)
  {
    if(finishedAngle > startAngle)
      finishedAngle = finishedAngle-2*pi;

    for(double i = startAngle; i<finishedAngle)
    {
      setMotorLocation(X,Y);
      runStepperMotors();
    }
  }
  else
  {
    if(finishedAngle < startAngle)
      finishedAngle = finishedAngle+2*pi;

    for(double i = startAngle; i)
    {
      setMotorLocation(X,Y);
      runStepperMotors();
    }
  }

  setMotorLocation(finishedX,finishedY);
  runStepperMotors();

  Serial.println();
  Serial.println(startX);
  Serial.println(startY);
  Serial.println(finishedX);
  Serial.println(finishedY);
  Serial.println(J);
  Serial.println(I);
  
  Serial.println(YCenter);
  Serial.println(radius);
  Serial.println(startAngle);
  Serial.println(finishedAngle);
  Serial.println(AngleBetween);* /
  */
}

void homeMachine()
{
  /*setMotorLocation(-100000,-100000);
  while((PLEX.digitalReadC(XUpperLimitSelect) == LOW && PLEX.digitalReadC(XLowerLimitSelect) == LOW) || (PLEX.digitalReadC(YUpperLimitSelect) == LOW && PLEX.digitalReadC(YLowerLimitSelect) == LOW))
  {
    PLEX.selectD(BSelect);
    stepperA.run();
    PLEX.selectD(ASelect);
    stepperB.run();
  }
  X = 0;
  Y = 0;
  stepperA.setCurrentPosition(0);
  stepperB.setCurrentPosition(0);*/
}



void setMotorLocation(double NewX,double NewY)
{
  X = NewX;
  Y = NewY;
  double x = NewX*XMultiplier;
  double y = NewY*YMultiplier;
  
  double XCurrent = 0.5*(stepperA.currentPosition() + stepperB.currentPosition());
  double YCurrent = 0.5*(stepperA.currentPosition() - stepperB.currentPosition());
  double R = sqrt((y-YCurrent)*(y-YCurrent)+(x-XCurrent)*(x-XCurrent));
  double t = R/Speed;

  double a = x+y;
  double b = x-y;
  
  double Speeda = abs(a - stepperA.currentPosition())/t;
  double Speedb = abs(b - stepperB.currentPosition())/t;
   
  stepperA.setMaxSpeed(Speeda);
  stepperB.setMaxSpeed(Speedb);
  stepperA.setSpeed(Speeda);
  stepperB.setSpeed(Speedb);
  stepperA.moveTo(a);
  stepperB.moveTo(b);
  
  PLEX.lcd.setCursor(0, 1);
  PLEX.lcd.print(NewX);
  PLEX.lcd.print(",");
  PLEX.lcd.print(NewY);
  PLEX.lcd.print("         ");
}

double currentX()
{
  return (int)(0.5*(stepperA.currentPosition() + stepperB.currentPosition()));
}

double currentY()
{
  return (int)(0.5*(stepperA.currentPosition() - stepperB.currentPosition()));
}

void runStepperMotors()
{
  //if(testLimit(XUpperLimitSelect) == HIGH && currentX()<X){ setMotorLocation(currentX(),Y);}
  //if(testLimit(XLowerLimitSelect) == HIGH && currentX()>X){ setMotorLocation(currentX(),Y);}
  
  //if(testLimit(YUpperLimitSelect) == HIGH && currentY()<Y){ setMotorLocation(X,currentY());}
  //if(testLimit(YLowerLimitSelect) == HIGH && currentY()>Y){ setMotorLocation(X,currentY());}
  while(stepperA.distanceToGo() != 0 || stepperB.distanceToGo() != 0)
  {
    PLEX.selectD(BSelect);
    stepperA.run();
    PLEX.selectD(ASelect);
    stepperB.run();
  }
}
I have wanted a plasma cutter for a while and instead of buying one I decided to make one. The 2D plotter machine did not take too long to make and has been sitting around for about a year while I was getting all other parts required for cutting. I did not have trouble getting the plasma cutting head/nozzle but have many problems getting the right hoses. After getting the correct parts can some modification to them the cutter was working. The next problem was the controller for the machine. I wanted to challenge myself and build the controller including the hardware and software. Since the plasma machine gives off lots of EMS, the controller would restart itself whenever the plasma was turned on. I fixed this by putting the controller in a metal box and stopped communication over USB (G-code on SD card) and this fixed the problem can I could test the cuts. The from is made from linear rails and powered by 2 NEMA17 motors in a CoreXY configuration. The controller is PLEX with the Arduino bootloader. You can get the PLEX controller from https://www.tindie.com/products/williambailes/plex-controller/ The controller uses the G-Code Arduino Library to read the G-code from the SD card. The G-Code Arduino Library can be found https://github.com/tinkersprojects/G-Code-Arduino-Library

Related Projects