Lasers!

Posted on
  • Hi,

    I've wanted to make a laser cutter for a while - I've got an XY table from an old plotter that'd be perfect. The only thing I'm missing is a high power laser....

    For that, it seems you need:

    • Laser Diode
    • Module (copper tube to hold module, with a threaded end)
    • Glass lens
    • Laser diode driver

    Does anyone know of a high power laser diode that'd be suitable, and anyone (preferably in the UK) that sells all of the required stuff in one sensibly priced kit?

    From what I can tell you generally have to buy each thing from a different eBay seller, which seems crazy...

  • You might ask Hackspace London guys on mailing list, seen someone were playing with lasers there: https://groups.google.com/forum/#!forum/london-hack-space

  • A bit of an update to this... I bought this laser module, which contains a driver IC: http://www.amazon.com/dp/B00HHHVV2K/ref=pe_385040_30332200_TE_item

    It's a bit pricey (£100), but I thought that if I was going to do it then I might as well do it properly!

    I then connected an Espruino up to an old Plotmate A3M plotter that I had hanging around. These are just dumb plotters - internally there's the mechanics, a power supply, drive transistors, and some circuitry that appears to serve no useful purpose apart from to make it difficult to use.

    The connections on the back are easy. There's a 20 pin connector, and all of one side of it is ground. On the other side there's:

    Pin 1,2,3,4 - X stepper
    Pin 5,6,7,8 - Y stepper
    Pin 9 - Pen down (inverted)
    Pin 10 - Axis end stop hit (inverted)
    

    I connected these right down the side of the Espruino board (B13,B14,B15,C4,C5,C6,C7,C8,C9,C10).

    After a huge amount of messing around, I found out the following on how the steppers are driven:

    motorx = [B13,B14,B15,C4] (0,1,2,3)
    Pin 1 (common) -> 4 (coil A), 5 (coil a)
    Pin 2 (common) -> 3 (coil B), 6 (coil b)
    
    
    1324
    AaBb
    0 -> 0110
    1 -> 0101
    2 -> 0100
    3 -> 0101
    
    4 -> 1010
    5 -> 1001
    6 -> 1000
    7 -> 1001
    
    8 -> 0010
    9 -> 0001
    10 -> 0000
    11 -> 0001
    
    12 -> 1010
    13 -> 1001
    14 -> 1000
    15 -> 1001
    
    step order = [14,13,9,3,2,0,8,4]
    
    
    motory = [C5,C6,C7,C8] (4,5,6,7)
    Pin 1 (common) -> 5 (coil A),6 (coil a)
    Pin 2 (common) -> 3 (coil B),4 (coil b)
    
    1324
    AaBb
    
    0 -> 1010
    1 -> 0010
    2 -> 1001
    3 -> 0001
    
    4 -> 0110
    5 -> 0110
    6 -> 0101
    7 -> 0101
    
    8 -> 1000
    9 -> 0000
    10 -> 1001
    11 -> 0001
    
    12 -> 0100
    13 -> 0100
    14 -> 0101
    15 -> 0101
    
    step order = [8,0,1,4,12,14,3,10];
    

    And finally: here's the code so far that's needed to drive the plotter - it'll home the axes, and draw a circle...

    var pins = [B13,B14,B15,C4,C5,C6,C7,C8,C9,C10];
    var ENDSTOP = pins[9]; // negated
    // pins[8] = !pen down
    // pins[9] = !axis hit
    var motorx = pins.slice(0,4);
    var xsteps = [14,13,9,3,2,0,8,4];
    var motory = pins.slice(4,8);
    var ysteps = [8,0,1,4,12,14,3,10];
    var motorxoff = 10;
    var motoryoff = 9;
    
    
    function Stepper(pins, pattern, offpattern) {
      this.pins = pins;
      this.pattern = pattern;
      this.offpattern = offpattern;
      this.pos = 0;
    }
    
    Stepper.prototype.setHome = function() {
      this.pos = 0;
    };
    
    Stepper.prototype.stop = function(turnOff) {
      if (this.interval) {
        clearInterval(this.interval);
        this.interval = undefined;
      }
      if (turnOff && this.offpattern)
        digitalWrite(this.pins, this.offpattern);
    };
    
    Stepper.prototype.moveTo = function(pos, milliseconds, callback, turnOff) {
      pos = 0|pos; // to int
      if (milliseconds===undefined)
        milliseconds = Math.abs(pos-this.pos)*10;
      this.stop(turnOff);
      if (pos != this.pos) {
        var stepper = this;
        this.interval = setInterval(function() {
          // remove interval if needed
          if (stepper.pos == pos) {
            stepper.stop(turnOff);
            if (callback)
              callback();
          } else {
            // move onwards
            stepper.pos += (pos < stepper.pos) ? -1 : 1;
            // now do step
            digitalWrite(stepper.pins, stepper.pattern[ stepper.pos & (stepper.pattern.length-1) ]);
          }
        }, milliseconds / Math.abs(pos-this.pos));
      } else {
        if (callback)
          setTimeout(callback, milliseconds);
      }
    };
    
    var x = new Stepper(motorx, xsteps, motorxoff);
    var y = new Stepper(motory, ysteps, motoryoff);
    
    function home(callback) {
      x.moveTo(50,undefined,function() {
        y.moveTo(50,undefined,function() {
          setWatch(function() {
            y.stop();
            y.setHome();
            y.moveTo(50,undefined,function() {
              setWatch(function() {
                x.stop();
                x.setHome();
                y.moveTo(0,undefined,callback,true);
              }, ENDSTOP, {edge:falling, repeat:false});
              x.moveTo(-10000);
          });
          }, ENDSTOP, {edge:falling, repeat:false});
          y.moveTo(-10000);
        });
      });
    }
    
    function setPenDown(down) {
      digitalWrite(pins[8], !down);
    }
    
    function draw(positions, callback) {
      function moveTo(p, callback) {
        var dx = x.pos - p[0];
        var dy = y.pos - p[1];
        var d = Math.sqrt(dx*dx+dy*dy);
        var time = d*50; // speed
        x.moveTo(p[0],time, undefined, false);
        y.moveTo(p[1],time, callback, false);
      }
      function moveToNext() {
        print(positions.length);
        if (positions.length) {
          moveTo(positions.shift(), moveToNext);
        } else {
          setPenDown(false);
          x.stop(true);
          y.stop(true);
          callback();
        }
      }
      
      if (positions.length) {
        moveTo(positions.shift(), function() {
          setPenDown(true);
          moveToNext();
        });
      } else {
        callback();
      }
    }
    
    function circle() {
      var positions = [];
      for (var i=0;i<20;i++) {
        var t = Math.PI*2*i/20;
        positions.push([(1+Math.cos(t))*200,(1+Math.sin(t))*200]);
      }
      draw(positions, function() {
        print("done");
      }); 
    }
    
    

    The laser is currently attached to a large aluminium heatsink, and then to the pen carriage. Hopefully I'll make laser control automatic soon, but for now it's done manually. It kind of works...


    1 Attachment

    • 20140612_123814_sml.jpg
  • Just to add - my laser protective goggles haven't arrived yet, so I can't actually look at it and see what it's doing when it's running (so I'm pretty sure the laser needs the focus adjusting).

    I'll do a g-code interpreter for it soon... I'm really happy with how easy stuff like the homing and X/Y movement was with callbacks. I remember doing it with PIC+C a while back and it was a nightmare.

  • It's now reading gcode off an SD card and plotting graphics... So about all I have to do now is mount the laser and wire it up.

    The code is now:

    // plotmate
    // connected right down one side
    var pins = [C5,C6,C7,C8,C9,C10,C11,A8,A9,A10];
    var ENDSTOP = pins[9]; // negated
    // pins[8] = !pen down
    // pins[9] = !axis hit
    var motorx = pins.slice(0,4);
    var xsteps = [14,13,9,3,2,0,8,4];
    var motory = pins.slice(4,8);
    var ysteps = [8,0,1,4,12,14,3,10];
    var motorxoff = 10;
    var motoryoff = 9;
    // 3990 steps for width, which = 420mm (A3)
    var stepsPerMM = 3990 / 420;
    
    function Stepper(pins, pattern, offpattern) {
      this.pins = pins;
      this.pattern = pattern;
      this.offpattern = offpattern;
      this.pos = 0;
    }
    
    Stepper.prototype.setHome = function() {
      this.pos = 0;
    };
    
    Stepper.prototype.stop = function(turnOff) {
      if (this.interval) {
        clearInterval(this.interval);
        this.interval = undefined;
      }
      if (turnOff && this.offpattern)
        digitalWrite(this.pins, this.offpattern);
    };
    
    Stepper.prototype.moveTo = function(pos, milliseconds, callback, turnOff) {
      pos = 0|pos; // to int
      if (milliseconds===undefined)
        milliseconds = Math.abs(pos-this.pos)*5;
      this.stop(turnOff);
      if (pos != this.pos) {
        var stepper = this;
        var step = function() {
          // remove interval if needed
          if (stepper.pos == pos) {
            stepper.stop(turnOff);
            if (callback)
              callback();
          } else {
            // move onwards
            stepper.pos += (pos < stepper.pos) ? -1 : 1;
            // now do step
            digitalWrite(stepper.pins, stepper.pattern[ stepper.pos & (stepper.pattern.length-1) ]);
          }
        };
        this.interval = setInterval(step, milliseconds / Math.abs(pos-this.pos));
        step();
      } else {
        if (callback)
          setTimeout(callback, milliseconds);
      }
    };
    
    var x = new Stepper(motorx, xsteps, motorxoff);
    var y = new Stepper(motory, ysteps, motoryoff);
    
    function home(callback) {
      x.moveTo(50,undefined,function() {
        y.moveTo(50,undefined,function() {
          setWatch(function() {
            y.stop();
            y.setHome();
            y.moveTo(50,undefined,function() {
              setWatch(function() {
                x.stop();
                x.setHome();
                y.moveTo(0,undefined,callback,true);
              }, ENDSTOP, {edge:falling, repeat:false});
              x.moveTo(-10000);
          });
          }, ENDSTOP, {edge:falling, repeat:false});
          y.moveTo(-10000);
        });
      });
    }
    
    function setPenDown(down) {
      digitalWrite(pins[8], !down);
    }
    
    function moveTo(p, speed, callback) {
      var dx = x.pos - p[0];
      var dy = y.pos - p[1];
      var d = Math.sqrt(dx*dx+dy*dy);
      var time = d*speed;
      x.moveTo(p[0],time, undefined, false);
      y.moveTo(p[1],time, callback, false);
    }
    
    function plotGCodeLine(line, callback) {
      if (line.length===0) {
        callback();
        return;
      }
      var numbers = "0123456789";
      var codeLen = (numbers.indexOf(line[2])<0) ? 2 : 3;
      var gcode = line.substr(0,codeLen);
      while (line[codeLen]==" ") codeLen++;
      var args = line.substr(codeLen);
      switch(gcode) {
        case "G90": // Simple cycle
        case "G21": // In millimeters
        case "M02": // End of program
          // do nothing...
          callback();break;
        case "M03": // laser on
          setPenDown(true);
          setTimeout(callback, 500);
          break;
        case "M05": // laser off
          setPenDown(false);
          setTimeout(callback, 500);
          break;    
        case "G0":
        case "G1":
        case "G00":
        case "G01":
        case "G02":
        case "G03":
          var d = {};
          // decode arguments
          if (args!==undefined) {
            args.split(" ").map(function(a) {
              d[a[0]] = a.substr(1);
            });
          }
          if (d.X!==undefined && d.Y!==undefined)
            moveTo([d.X*stepsPerMM, d.Y*stepsPerMM], 10, callback);
          else
            callback();
          break;
        
        default:
          console.log("Unknown code "+JSON.stringify(gcode));
          callback();
      }  
    }
    
    function plotGCodeFile(fileName, completeCallback) {
      var f = E.openFile(fileName);
      var buffer = f.read(64);
      if (buffer===undefined) {
        console.log("Couldn't load file");
        completeCallback();
        return;
      }
      
      function newLine() {
        // get new data from file  
        while (buffer.indexOf("\n")<0) {
          var r = f.read(64);
          if (r===undefined) {
            f.close();
            break;
          }
          buffer += r;
        }
        // if we didn't get any data, finish...
        if (buffer==="") {
          // turn stuff off
          setPenDown(false);
          x.stop(true);
          y.stop(true);
          // call completion callback
          completeCallback();
          return;
        }
        // get new line
        var eol = buffer.indexOf("\n");
        if (eol<0) eol = buffer.length;
        var line = buffer.substr(0,eol);
        buffer = buffer.substr(eol+1);
        // do stuff
        plotGCodeLine(line, function() {
          setTimeout(newLine, 5);
        });
      }    
      newLine();
    }
    
    //plotGCodeFile("espruino.nc", function() {print("Done");});
    

    It doesn't do curve interpolation (just simple linear interpolation), but sub-200 lines still seems pretty good for something that can power a laser cutter and/or 3D printer.

  • And... This is the first cut with the exact code above. The laser is still running off a bench power supply, but with one of the relay modules turning it on and off.

    Seems to work ok - just need to focus it now and adjust the feed rates :)


    1 Attachment

    • 20140616_121628.jpg
  • One more try. Slowed it down by 1/3 and now only wait 100ms after turning the laser on so it doesn't make obvious black spots.

    Looking better!


    1 Attachment

    • 20140616_123731-1.jpg
  • Wow, this looks awesome. Will it be able to cut acrylic sheets, or does that require a higher power laser?

    Why the relay, instead of a mosfet, though?

    Looking at how well that worked is making me think about that stack of single-axis tables (with high accuracy scales) that have been sitting in the attic for well over a decade, since we didn't have any way to control them.

  • I'm not really sure what it'll cut yet... It's not up there with 'proper' laser cutters but I'm hopeful that it'll do at least 2mm acrylic. The speed of cut I used was quite slow, but I'm pretty sure that it'll be better when it's properly focussed and when I attach a power supply that'll actually produce enough current!

    I'm just using the relay because I wanted to get something running quick and it had screw terminals. The laser I bought has a fancy constant current driver inside it, so I may not be able to PWM it very successfully anyway. I'll have to give it a try later on.

    What are the tables like? Espruino does seem to do a decent job of stepper motor control...

  • OMG, I remember the Plotmate. Didn't it point at the number on the side to tell you which pen to switch to, or something like that? I think my dad bought one to do PCB masks once he realised the PenMan robotic plotter (srsly... Google it) wasn't up to it.

    Yes, we had an Acorn System 1, too. :)

  • I actually do have a PenMan (well my parents do somewhere)... Now that would be good with a laser :)

    It looks like all the smarts for the Plotmate (or at least the version I have) were in the BBC micro it was connected to, and I haven't actually seen one that was all connected up and working properly - I just got these from a friend whose school was chucking them out. Still, looking at the manual it did have some automatic pen changer, although it looks like it was an optional extra as well :)

  • Wow :)

  • @Gordon this project would be even better if you could etch out PCB boards :) do you think it would be possible?

  • I think with the current lasers it might be tricky - it definitely wouldn't vaporise copper. You may be able to spray-paint it black, burn off areas that you wanted etched, and then etch the PCB.

    Having said that there's no reason to use a laser - it might be possible to put an engraver on a plotter (maybe one a bit less flimsy than this) and use that to mechanically cut the copper (and perhaps also drill holes!).

  • Hey Gordon, just wanted to let you know that you can indeed do it like you said, spray painting the board, burning away the paint, and then etching as normal.

    Ben Heck (Google him, do it, he's amazing) used that method in his PCB etching episode
    , and apparently it's a really reliable way to get crisp etchings.

  • Thanks, I'll have to give it a try. I guess in that case he used a professional laser cutter though? I wonder if my one would actually have enough power.

  • yeah, in the video, it's a professional laser cutter. I work with the little sister of this series (zing 6030). The accuracy of this machines is so awesome. Not comparable with home made stuff, but i'm interested in the result of your home made laser :-)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Lasers!

Posted by Avatar for Gordon @Gordon

Actions