Laser Cutter

After browsing the instructabels.com website I came across this little project [14-nov-2011]

So the idea was spawn to build one myself:

I ordered some parts out of ebay (where else?)

 

This little baby puts out a whopping 250mW @650nM of light (this wavelength resembles red). For those not to familiar with lasers and their power ratings, a ‘normal’ red laser pointer sold in The Netherlands is usually rated at 1mW, sometimes < 5mW.

There is to my knowledge some regulation as to how much a laser pointer may have. This little baby is way more powerful than your conventional laser diode, the thingy on the right (a similar looking thing is located in your laser pen too). Connecting this diode in this form will not work (for long). A lasing diode needs proper cooling, something it has not, when used like this. Therefor I had to buy another part of Ebay.

 

This housing has three functions.

  •  Collimate the beam to a more usefull dot,
  • provide a heat-sink for the diode.
  • Protects the diode from physical strain. There is a tiny little cube (the crystal)  in the diode, if moved. the laser is ruined.

Although it has poor optics it does make the diode burn green/black stuff.

 

 

 

In order to drive this baby one needs a current supply (just connecting batteries will kill it within milliseconds!). This is where our beloved/hated LM317 comes in. It’s is called a current regulator.

As the name may imply it makes sure the laser cannot take more current than a pre-set value. Hence a laser diode == diode so current-limiting is a requirement

 

at the left you see the power regulating board, just the LM317 and some resistors (which one of them is a potentio-meter)

 

 

Another necessity is PROPER eye protection. Where is your eylid’s blinking reflex fast enough to blink when someone annoys you with his laserpointer, with an output of this magnitude, you are to simply too slow, retina damage is imminent and this baby burns holes in it!. Even reflections are a real danger! I am usually not to concerned with safety when doing hobby projects, however this time I am. I can rely recommend buying proper laser-safety-goggles, although they are pricey as E40,- your eyes are worth considerable more.

 

Then for the remainder, there are two drive(s) harvested from old cdrom drives which are places in an X/Y configuration. The upper one drives the laser against one axis and the other one moves the workspace at a 90 degree angle.

The step-motors are of the bi-polar type. therefore two full H-bridge circuits are needed to drive them. Luckly there is an app (uh IC) for that. I think it was the L293D or a pin-compatible one.

The two motors and the laser are connected to a Atmega32 (bit overkill, but had one laying around). So I wrote some software to process commands from the usart and control the IO (input/output), hence the motors and laser on/off

The hardware is controlled by a litte program I wrote using Microsoft’s C#. It reads G-code files (a format common in the milling/CnC industries. I made some pictures using a vector based program (Inkscape). Then parsed these SVG’s with an python program. It’s output is G-code which is fed into my C# application which controls the microprocessor.

The results:

I tried to convert my 2D printing/burning capabilities to 3D by melting. As a ‘fuel’ I tried using chocolate. However I had to conclude that the mechanics weren’t up for the task (an extra axis). to add a thin layer of chocolate powder after every slice.

 

below is the source-code:

The code for the micro-controller (atmel atmega32)

/*
 * CNC_bridge.c
 *
 * Created: 18-11-2011 0:16:27
 *  Author: Vincent
 */ 

#include
#include
#include 

#define START 0xFF

#define F_CPU 8000000
#define BAUDRATE 19200
#define UBRRVAL ((F_CPU/(BAUDRATE*16UL))-1)

void USART_Init();

volatile unsigned char dis;
volatile unsigned char rxSync;

int main(void)
{
	//Zet alle poorten als OUTPUT!
	DDRA=0xFF;
	DDRB=0xFF;
	DDRC=0xFF;
	DDRD=0xFF;

	//set alle state van de poorten op 0x00
	PORTA=0x00;
	PORTB=0x00;
	PORTC=0x00;
	PORTD=0x00;

	//init serial &amp; init variables;
	rxSync = START;
	dis=0x00;
	USART_Init();

	asm("sei");

    while(1)
    {
        USARTWriteChar('p');
		PORTA &amp;= 0xFE;
		_delay_ms(250);
		PORTA != 0x01;
		_delay_ms(250);
    }
}

void USART_Init(void)
{
	/* Set baud rate */
	//UBRRL = 25;   //19200 Baud with 8mHz clock
	//UBRRH = 0;
	/* Enable transmitter */
	//UCSRB = (1 &lt;&lt; RXEN) | (1&lt;&gt;8);	//high byte
	//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
UCSRC=(1&lt;&lt;URSEL)|(0&lt;&lt;UMSEL)|(0&lt;&lt;UPM1)|(0&lt;&lt;UPM0)|
(0&lt;&lt;USBS)|(0&lt;&lt;UCSZ2)|(1&lt;&lt;UCSZ1)|(1&lt;&lt;UCSZ0);
//Enable Transmitter and Receiver
UCSRB=(1&lt;&lt;RXEN)|(1&lt;&lt;TXEN) | (1&lt;&lt;RXCIE);
return;
}
ISR(USART_RXC_vect) //letop voor MEGA 16!!!
{

char r = UDR;
USARTWriteChar(r);

if (r == START &amp;&amp; dis==0)
{

rxSync = 1;
dis=1;
return;
}

else
{
switch (rxSync)
{
case 0x01:
PORTA = r;
break;

case 0x02:
PORTB = r;
break;

case 0x03:
PORTC = r;
break;

case 0x04:
PORTD = r;
dis=0;
USARTWriteChar('A');
break;
default:
break;
}
rxSync++;

if(rxSync == START)
rxSync = rxSync -1;
}
return;

}
void USARTWriteChar(char data)
{
//Wait untill the transmitter is ready

while(!(UCSRA &amp; (1&lt;&lt;UDRE)))
{
//Do nothing
}

//Now write the data to USART buffer

UDR=data;
}

The C# code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO.Ports;

namespace CNCMil
{
    public partial class Form1 : Form
    {
        public int xAxis, yAxis, zAxis = 0;
        int head = 0, newHead = 0;
        public double newX, newY, newZ = 0;
        private string[] theSerialPortNames;
        private byte[] empty = { 0xFF, 0x00, 0x00, 0x00, 0x00 };
        private int _sleep = 40;
        private int _sleepBurn = 5;

        int[] stateCount = { -1, -1, -1, -1 };

        static SerialPort _serialPort;

        public int stepSize = 1; //mm

        private byte StateMachineCounter(int axis, int direction)
        {
            byte ret;
            int max = 3;

            //INVERT DIRECTION bit
            direction = direction * -1;

            if (direction == 0)
                return 0x00;
            if (direction == 1)
            {
                stateCount[axis] = stateCount[axis] +1;
                if (stateCount[axis] &gt; max)
                    stateCount[axis] = 0;
            }

            if (direction == -1)
            {
                stateCount[axis] = stateCount[axis] -1;
                if (stateCount[axis] &lt; 0)
                    stateCount[axis] = max;
            }

            //FULLstEP MODE:
            switch (stateCount[axis])
            {
                  case 0:
                    ret = 0x01;
                    break;
                case 1:
                    ret = 0x04;
                    break;
                case 2:
                    ret = 0x02;
                    break;
                case 3:
                    ret = 0x08;
                    break;
                default:
                    ret = 0x00;
                    break;
            }
                    /*
                case 0:
                    ret = 0x01;
                    break;
                case 1:
                    ret = 0x05;
                    break;
                case 2:
                    ret = 0x04;
                    break;
                case 3:
                    ret = 0x06;
                    break;

                case 5:
                    ret= 0x02;
                    break;
                case 6:
                    ret = 0x0a;
                    break;
                case 7:
                    ret = 0x08;
                    break;

                     *
                     */

            return ret;
        }

        public Form1()
        {
            InitializeComponent();

            //_serialPort.Write("s1234");

         theSerialPortNames = System.IO.Ports.SerialPort.GetPortNames();

            comportList.Items.AddRange(theSerialPortNames);

        }

        private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            String r = _serialPort.ReadExisting();
            if (!string.IsNullOrEmpty(r))
            {
                AddLog(r);
            }

        }

        private delegate void AddLogDelegate(string r);

        private void AddLog(string r)
        {
            if (this.Log.InvokeRequired)
            {
                this.rs232.Invoke(new AddLogDelegate(this.AddLog), r);
            }
            else
            {
                this.rs232.AppendText(r);
            }
        }

        private void SendToUart(int aX, int aY, int aZ, int aH)
        {
            //byte[] send = { 0xFF, aX, AY, aZ };
            //_serialPort.Write(send, 0, send.Length);

            //calculate the values:
            byte ux = StateMachineCounter(0, aX);
            byte uy = StateMachineCounter(1, aY);
            byte uz = StateMachineCounter(2, aZ);

            int total = (uy &lt;&lt; 4) | ux;

            DBG("aX " + aX + "  ay " + aY + " az " + aZ + " laser: " +aH);
            DBG("uX " + ux + "  ay " + uy + " uz " + uz + " laser: " + aH);
            DBG("TOUART: " + total + " laser: "+ aH);
            byte[] send = { 0xFF, (byte)total, (byte)aH, 0x00, 0x00 };
            _serialPort.Write(send, 0, 5);
            System.Threading.Thread.Sleep(int.Parse(sleepBetweenStep.Text));

            //hack voor laser
            if (aH == 1)
            {
                empty[2] = 0x01;
                _serialPort.Write(empty, 0, 5);
                System.Threading.Thread.Sleep(int.Parse(burnSleep.Text));
            }
            else
            {
                empty[2] = 0x00;
                _serialPort.Write(empty, 0, 5);
                System.Threading.Thread.Sleep(5);
            }

        }

        public void SendCommand(double x, double y, double z, int h)
        {
            DBG("CUR COORDS: " + xAxis + " " + yAxis + "  " +zAxis );
            DBG("NEW COORDS: " + x + " " + y + "  "  + z );

            head = h;

            int multiplier = 1;

            //Verschil tussen huidige en nieuw waarden
            int divX = xAxis - (int)(x * multiplier);
            int divY = yAxis - (int)(y * multiplier);
            int divZ = zAxis - (int)(z * multiplier);

            int max = 0;
            if (Math.Abs(divX) &lt; Math.Abs(divY))
                max = Math.Abs(divY);
            else
                max = Math.Abs(divX);
            if (max &lt; Math.Abs(divZ))
                max = Math.Abs(divZ);

            for (int cnt = 0; cnt &lt; max; cnt++)
            {
                int tx = 0, ty = 0, tz = 0;

                if (divX != 0)
                    tx = 1;
                if (divY != 0)
                    ty = 1;
                if (divZ != 0)
                    tz = 1;

                //Pas de huidge x waarde aan met +1 of -1
                if (divX &lt; 0)
                {
                    tx = 1;
                    xAxis++;
                }
                else if (divX &gt; 0)
                {
                    tx = -1;
                    xAxis--;
                }
                if (divY &lt; 0)
                {
                    ty = 1;
                    yAxis++;
                }
                else if (divY &gt; 0)
                {
                    ty = -1;
                    yAxis--;
                }

                if (divZ &lt; 0)
                {
                    ty = 1;
                    zAxis++;
                }
                else if (divZ &gt; 0)
                {
                    ty = -1;
                    zAxis--;
                }

                SendToUart(tx, ty, tz, head);

                divX = incrementer(divX);
                divY = incrementer(divY);
                divZ = incrementer(divZ);

           }

        }

        //ZOrgt ervoor dat inc met +1 of -1 naar nul wordt gebracht.
        public int incrementer(int inc)
        {
            if (inc == 0)
                return 0;
            if ((inc / Math.Abs(inc)) == 1)
                return inc - 1;
            else
                return inc +1;

        }

        public void DBG(String logMessage)
        {
            Log.AppendText(logMessage + Environment.NewLine);
        }

        public void GCodeIntrepret()
        {
            Regex Gcode = new Regex("^[GXYMZ].*", RegexOptions.IgnoreCase);
            // String instructions = this.richTextBox1.Text;

            foreach (String line in richTextBox1.Lines)
            {
                MatchCollection m = Gcode.Matches(line);

                //voor elke match
                foreach (Match n in m)
                {
                    string[] pieces = n.Value.Split(' ');
                    string what = n.Value;
                    //DBG("WHAT: " + what);

                    if (what.StartsWith("G"))
                    {
                        DBG("we hebben G-match");
                        string[] a = what.Split(' ');
                        switch (what.Substring(0, 2))
                        {

                            case "G0":
                                // in dit geval laser aan/uit
                                DBG("G0 found");

                                if (a[1].ToUpper().StartsWith("Z"))
                                {
                                    a[1] = a[1].Replace(".", ",");
                                    Double z = Double.Parse(a[1].Substring(1, a[1].Length - 1));
                                    if (z != 0.0)
                                    {
                                        SendCommand(xAxis, yAxis, zAxis, 0);
                                        DBG("Laser off");
                                    }
                                    else
                                    {
                                        SendCommand(xAxis, yAxis, zAxis, 1);
                                        DBG("Laser on");
                                    }

                                }
                                break;

                            case "G1":
                                DBG("G1 found");
                                if (a[1].ToUpper().StartsWith("Z"))
                                {
                                    a[1] = a[1].Replace(".", ",");
                                    Double z = Double.Parse(a[1].Substring(1, a[1].Length - 1));
                                    if (z &gt; 0.0)
                                    {
                                        SendCommand(xAxis, yAxis, zAxis, 0);
                                        DBG("Laser off");
                                    }
                                    else
                                    {
                                        SendCommand(xAxis, yAxis, zAxis, 1);
                                        DBG("Laser on");
                                    }

                                }
                                break;
                            case "G2":
                                break;
                            case "03":
                                break;

                        }
                    }

                    else if (what.StartsWith("M"))
                    {
                        switch (what)
                        {
                            case "M5":
                                //STOP SPINNER, kill laser 😕
                                SendCommand(xAxis, yAxis, zAxis, 0);
                                DBG("found M5");
                                break;
                            case "M3":
                                //Motor aan, Clockwise
                                SendCommand(xAxis, yAxis, zAxis, 1);
                                DBG("found M3");
                                break;
                            case "M4":
                                //Motor aan, Counter clock wise
                                SendCommand(xAxis, yAxis, zAxis, 1);
                                DBG("found M4");
                                break;
                            case "G03":
                                break;

                        }
                    }

                    else
                    {
                        for (int x = 0; x &lt; pieces.Length; x++)
                        {
                            DBG("piece: " + pieces[x]);
                            //assume normal coordinates
                            Process(pieces[x]);
                        }

                        SendCommand(newX, newY, newZ, head);
                    }
                }
            }

            //Zet laser uit!
            SendCommand(0, 0, 0, 0);
        }

        private void Process(String what)
        {
            what = what.Replace(".", ",");
            if (what.StartsWith("X"))
            {
                Double x = Double.Parse(what.Substring(1, what.Length - 1));
                newX = x;
                DBG("parsed x:" + x);
            }
            if (what.StartsWith("Y"))
            {
                Double y = Double.Parse(what.Substring(1, what.Length - 1));
                newY = y;
                DBG("parsed y:" + y);
            }
            if (what.StartsWith("Z"))
            {
                Double z = Double.Parse(what.Substring(1, what.Length - 1));
                newZ = z;
                DBG("parsed z:" + z);
            }

        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            DBG("Parsing input");
            GCodeIntrepret();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            int _shift = 0;
            _sleep = 35;
            int steps = 100;
            for (int x = 0; x &lt; steps; x++)
            {
               byte xa = StateMachineCounter(1, 1);
               byte ya = StateMachineCounter(2, 1);
               xa = (byte)(xa &lt;
}
}