|
@@ -6,7 +6,7 @@ x-date: 2022-11-11
|
6
|
6
|
comments: true
|
7
|
7
|
---
|
8
|
8
|
|
9
|
|
-After dabbling in 3D printing for a couple of years now, I now had to level-up my Makers toolkit.
|
|
9
|
+After dabbling in 3D printing for a couple of years now, I had to level-up my Makers toolkit.
|
10
|
10
|
The next logical step, in my opinion, is laser engraving / laser cutting.
|
11
|
11
|
I have to admit, I was initially a bit scared, and still have some respect for the potential hazards posed by the strong and focussed laser beam.
|
12
|
12
|
So I decided to go slow and start with a low powered diode laser.
|
|
@@ -45,10 +45,12 @@ So when using these parts, the two wheels are not actually moved perpendicular t
|
45
|
45
|
To avoid this problem I made the two holes in the lower bigger as well and used four instead of two eccentric nuts.
|
46
|
46
|
I also slightly increased the distance between the two sets of wheels on the Y-axis, because it was very hard to fit the rail, even with the eccentric nuts as loose as possible.
|
47
|
47
|
|
48
|
|
-I also designed custom endstop mounts using M2.5 heat-melt inserts, as well as a support wheel for the free-hanging side of the cantilever arm.
|
|
48
|
+I also designed custom endstop mounts using M2.5 heat-melt inserts, as well as a support wheel for the free-hanging side of the cantilever arm and a mount for the Ultimaker Controller 2004 LCD.
|
49
|
49
|
|
50
|
50
|
The OpenSCAD and STL files for these parts [can be found on my Printables profile](https://www.printables.com/model/314945-cantilever-laser-engraver-fixes).
|
51
|
51
|
|
|
52
|
+To fit the whole machine comfortably inside my [Ikea Lack tower](ikea-lack.html) I mounted it on a 18mm mutliplex base plate with 540x440mm.
|
|
53
|
+
|
52
|
54
|
### Electronics
|
53
|
55
|
|
54
|
56
|
As mentioned above I used the electronics, namely mainboard, LCD and fans, from my old 3D printer.
|
|
@@ -103,6 +105,11 @@ Apparently I seem to be the first person that tries to run an Ultimaker Controll
|
103
|
105
|
I had to add a couple of `#ifdef Z_AXIS` in `src/lcd/HD44780/marlinui_HD44780.cpp`.
|
104
|
106
|
|
105
|
107
|
You can see all the modifications to the configuration I initially made to get the machine running [in this commit](https://git.xythobuz.de/thomas/marlin/commit/41cd87398d539f41c2ebe54b5f675c6c8b5ce04b).
|
|
108
|
+
|
|
109
|
+I also did some small changes to show the current laser power on the LCD status screen.
|
|
110
|
+These changes can be seen [in this commit](https://git.xythobuz.de/thomas/marlin/commit/58dbdff1d5b6e365bfd5ae4eeb7b42967688c51e).
|
|
111
|
+I also opened a [pull request](https://github.com/MarlinFirmware/Marlin/pull/25003) to hopefully upstream them.
|
|
112
|
+
|
106
|
113
|
My current Marlin configuration for the laser engraver can be found [on my Gitea instance](https://git.xythobuz.de/thomas/marlin/src/branch/laser-engraver).
|
107
|
114
|
|
108
|
115
|
### Host Software
|
|
@@ -262,9 +269,95 @@ TODO
|
262
|
269
|
|
263
|
270
|
I have not yet tested that.
|
264
|
271
|
|
265
|
|
-#### Generating G-Code
|
|
272
|
+#### Working with G-Code
|
|
273
|
+
|
|
274
|
+One nice feature I saw in LaserGRBL, but was not able to use with Marlin, is the ability to draw the outline of the object to be cut, for positioning of the stock material.
|
|
275
|
+So I quickly made up a small Python script that analyzes a G-Code file, taking the coordinates from all G1 cutting moves and generating their bounding box, which is then drawn on the lowest possible power setting multiple times.
|
|
276
|
+I copy this file to the SD card, start it, and just abort it when I'm done with the preparations.
|
|
277
|
+
|
|
278
|
+<pre class="sh_python">
|
|
279
|
+#!/usr/bin/env python
|
|
280
|
+
|
|
281
|
+import sys
|
|
282
|
+
|
|
283
|
+if len(sys.argv) < 3:
|
|
284
|
+ print("Usage:")
|
|
285
|
+ print(" " + sys.argv[0] + " input.nc output.gcode")
|
|
286
|
+ sys.exit(1)
|
|
287
|
+
|
|
288
|
+in_file = sys.argv[1]
|
|
289
|
+out_file = sys.argv[2]
|
|
290
|
+
|
|
291
|
+x_min = None
|
|
292
|
+x_max = None
|
|
293
|
+y_min = None
|
|
294
|
+y_max = None
|
|
295
|
+mode_g1 = False
|
|
296
|
+
|
|
297
|
+pwr = 1
|
|
298
|
+iterations = 100
|
|
299
|
+speed = 3000
|
|
300
|
+
|
|
301
|
+with open(in_file, 'r') as fi, open(out_file, 'w') as fo:
|
|
302
|
+ for line in fi:
|
|
303
|
+ if line.startswith("G1"):
|
|
304
|
+ mode_g1 = True
|
|
305
|
+ elif line.startswith("G0"):
|
|
306
|
+ mode_g1 = False
|
|
307
|
+
|
|
308
|
+ if mode_g1:
|
|
309
|
+ x_pos = line.find("X")
|
|
310
|
+ if x_pos > -1:
|
|
311
|
+ x_str = line[x_pos + 1:]
|
|
312
|
+ x_num = float(x_str.split()[0])
|
|
313
|
+ #print("found x: " + str(x_num))
|
|
314
|
+ if (x_min == None) or (x_num < x_min):
|
|
315
|
+ x_min = x_num
|
|
316
|
+ if (x_max == None) or (x_num > x_max):
|
|
317
|
+ x_max = x_num
|
|
318
|
+
|
|
319
|
+ y_pos = line.find("Y")
|
|
320
|
+ if y_pos > -1:
|
|
321
|
+ y_str = line[y_pos + 1:]
|
|
322
|
+ y_num = float(y_str.split()[0])
|
|
323
|
+ #print("found y: " + str(y_num))
|
|
324
|
+ if (y_min == None) or (y_num < y_min):
|
|
325
|
+ y_min = y_num
|
|
326
|
+ if (y_max == None) or (y_num > y_max):
|
|
327
|
+ y_max = y_num
|
|
328
|
+
|
|
329
|
+ print("x_min: " + str(x_min))
|
|
330
|
+ print("x_max: " + str(x_max))
|
|
331
|
+ print("y_min: " + str(y_min))
|
|
332
|
+ print("y_max: " + str(y_max))
|
|
333
|
+
|
|
334
|
+ pos = [
|
|
335
|
+ ( x_min, y_min ),
|
|
336
|
+ ( x_max, y_min ),
|
|
337
|
+ ( x_max, y_max ),
|
|
338
|
+ ( x_min, y_max ),
|
|
339
|
+ ]
|
|
340
|
+
|
|
341
|
+ def write(s):
|
|
342
|
+ #print(s)
|
|
343
|
+ fo.write(s + "\n")
|
|
344
|
+
|
|
345
|
+ # header
|
|
346
|
+ write("G90")
|
|
347
|
+ write("M3 I")
|
|
348
|
+ write("M3 S0")
|
|
349
|
+
|
|
350
|
+ write("G0 X" + str(x_min) + " Y" + str(y_min) + " S0 F" + str(speed))
|
|
351
|
+
|
|
352
|
+ for i in range(0, iterations):
|
|
353
|
+ for x, y in pos:
|
|
354
|
+ write("G1 X" + str(x) + " Y" + str(y) + " S" + str(pwr) + " F" + str(speed))
|
|
355
|
+
|
|
356
|
+ write("M5")
|
|
357
|
+ write("G0 X" + str(x_min) + " Y" + str(y_min) + " S0 F" + str(speed))
|
|
358
|
+</pre>
|
266
|
359
|
|
267
|
|
-I also did some experimentation with programatically generating G-Code myself, using a simple Python script.
|
|
360
|
+I also did some experimentation with programatically generating G-Code myself, using a simple Python script as well.
|
268
|
361
|
It's drawing a grid for reference on the base plate of the machine, including numerical position indicators.
|
269
|
362
|
|
270
|
363
|
<pre class="sh_python">
|
|
@@ -409,7 +502,7 @@ with open(filename, 'w') as f:
|
409
|
502
|
write("G1 X" + str(w) + " Y" + str(y) + " S" + str(pwr) + " F" + str(speed_g1))
|
410
|
503
|
|
411
|
504
|
for x in range(0, w, d):
|
412
|
|
- n = int(x / 1)
|
|
505
|
+ n = int(x / 10)
|
413
|
506
|
if n >= 10:
|
414
|
507
|
draw_digit(f, int(n / 10), font_w, font_h, font_d + x, font_d)
|
415
|
508
|
draw_digit(f, int(n % 10), font_w, font_h, 2 * font_d + font_w + x, font_d)
|
|
@@ -417,7 +510,7 @@ with open(filename, 'w') as f:
|
417
|
510
|
draw_digit(f, n, font_w, font_h, font_d + x, font_d)
|
418
|
511
|
|
419
|
512
|
for y in range(d, h, d):
|
420
|
|
- n = int(y / 1)
|
|
513
|
+ n = int(y / 10)
|
421
|
514
|
if n >= 10:
|
422
|
515
|
draw_digit(f, int(n / 10), font_w, font_h, font_d, font_d + y)
|
423
|
516
|
draw_digit(f, int(n % 10), font_w, font_h, 2 * font_d + font_w, font_d + y)
|