Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 0 additions & 74 deletions README

This file was deleted.

144 changes: 144 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<p align="center">
<img width="600" src="logo.png">
<h3 align="center">cut SMT stencils from Gerber files</h3>
</p>
<br><br>

[![GPL](https://img.shields.io/badge/license-GPL-blue)](https://github.com/pmonta/gerber2graphtec/blob/master/LICENSE)
[![Python](https://img.shields.io/badge/language-Python3-orange)](https://www.python.org)
[![GitHub Release](https://img.shields.io/badge/release-v0.2-brightgreen)](https://github.com/pmonta/gerber2graphtec/releases)

## About

SMTCut, previously called **gerber2graphtec**, is a tool for cutting accurate SMT stencils on a **Graphtec** or **Silhouette** vinyl cutter from Gerber files.<br>

Techniques include separately drawn line segments (no complex paths), antibacklash, drag-knife-angle "training", and multiple passes.<br>

This produces stencils usable down to approximately **0.5 mm pitch** (QFP/QFN) and **0201** discrete components, or perhaps even slightly better; but it is slower than normal cutting.


## Getting Started

To be able to run this software, make sure that you have installed the following dependencies:

- [gerbv](http://gerbv.geda-project.org)
- [pstoedit](http://www.calvina.de/pstoedit/)
- [libusb](https://libusb.info)

#### Install on Mac OSX:
Make sure you already have installed [Homebrew](https://brew.sh/) and simply run the following commands:

```
brew install gerbv
```
```
brew install pstoedit
```
```
brew install libusb
```
Also make sure that you've installed the libusb1 python package or install it via
```
pip3 install libusb1
```

## Usage

In general there are two different cases to distinguish depending on your operating system. Please note that this process can take a few minutes before the cutter starts to cut!

### Linux
E.g. a solderpaste gerber file (paste.gbr), and default settings:

```
gerber2graphtec paste.gbr >/dev/usb/lp0
```

Or a more elaborate command line with linear map (to correct spatial miscalibration) and multiple passes with different speeds and forces:

```
gerber2graphtec --offset 3,4 --matrix 1.001,0,-0.0005,0.9985 --speed 2,1 --force 5,25 paste.gbr >/dev/usb/lp0
```

### Mac OSX and Windows
With Mac OS X or Windows, the file2graphtec script can take the place of /dev/usb/lp0. But keep in mind that the software is tested on Mac OS X but not on Windows!

E.g. a solderpaste gerber file (paste.gbr), and default settings:

```
python3 gerber2graphtec paste.gbr > tmpfile
python3 file2graphtec tmpfile
```

Or a more elaborate command line with linear map (to correct spatial miscalibration) and multiple passes with different speeds and forces:

```
python3 gerber2graphtec --offset 3,4 --matrix 1.001,0,-0.0005,0.9985 --speed 2,1 --force 5,25 paste.gbr > tmpfile
python3 file2graphtec tmpfile
```

## Known issues
Due to a bug inside ghostscript, you may need to downgrade to version < 9.26 or you might encounter repeating output --> [Issue17](https://github.com/pmonta/gerber2graphtec/issues/17)

On Mac OSX this can be done by uninstalling ghostscript with:

```
brew uninstall --ignore-dependencies ghostscript
```

and reinstalling the version 9.25

```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/b0b6b9495113b0ac5325e07d5427dc89170f690f/Formula/ghostscript.rb
```

## Calibration
To achieve the best results, one should run different calibrations, which can be found in the test folder after the blade is set. The following example shows how to calibrate the cutter on Mac OSX but the commands are also applicable to other operating systems (make sure you run these files from the main folder).

```
python3 tests/test_forces.py > tmpfile
python3 file2graphtec python3 file2graphtec tmpfile
```

## GUI

An optional GUI has been provided by [jesuscf](https://github.com/jesuscfv). It allows interactive selection of the input Gerber file, parameters, and cutting operations.

To use it, run the following command:

```
python3 g2g_gui.py
```

## Tips & Tricks
You may want to have your CAM tool shrink the paste features by about 2 mils before exporting to gerber. The craft-cutter knife, when cutting thin mylar, seems to spread out the geometry by about this amount. I'd suggest using mylar with thickness between about 3 and 5 mils; the IPC-recommended thicknesses for fine-pitch stencils are approximately in this range. (Typical inexpensive laser-transparency sheets happen to be just right, being somewhere between 3.5 and 4.3 mils.) You may have to experiment with the cutting speeds and forces for best quality with your materials.

Fedora 17 has gerbv-2.6.0 in its supplied package list, so no issue there. For Fedora-like systems, "yum install gerbv pstoedit tkinter".

On some Linux distributions, permissions on /dev/usb/lp0 are restricted by default. To fix this, add yourself to the lp group and then log out and log back in:

```
sudo usermod -a --group lp your_userid
```

## Additional Information

These pages have hints on usage (e.g. on Windows), materials, performance, calibration, etc:

- [pmonta.com](http://pmonta.com/blog/2012/12/25/smt-stencil-cutting/)
- [dangerousprototypes.com](http://dangerousprototypes.com/forum/viewtopic.php?f=68&t=5341)
- [hackaday.com](http://hackaday.com/2012/12/27/diy-smd-stencils-made-with-a-craft-cutter/)

## Credits

Thanks to the authors of robocut and graphtecprint for protocol documentation:

- [robocut](http://gitorious.org/robocut)
- [graphtecprint](http://vidar.botfu.org/graphtecprint)
- [graphtecprint](https://github.com/jnweiger/graphtecprint)

Also thanks to this web page (Cathy Sexton) for inspiration:

- [idleloop.com](http://www.idleloop.com/robotics/cutter/index.php)

Her cutter seems to be quite a bit better than mine was out of the box.
With Silhouette's default software my 0.5 mm pitch pads were distorted to the point of unusability.
2 changes: 1 addition & 1 deletion file2graphtec
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ if len(sys.argv)==2:
elif len(sys.argv)==1:
f = sys.stdin
else:
print 'usage: file2graphtec [filename]'
print('usage: file2graphtec [filename]')
sys.exit(1)

endpoint = 1
Expand Down
36 changes: 17 additions & 19 deletions g2g_gui.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
#!/usr/bin/env python
import Tkinter
import tkMessageBox
import tkFileDialog
import tkinter
import sys
import os
import string

from Tkinter import *
from tkinter import *
from os import path, access, R_OK, W_OK

top = Tkinter.Tk()
top = tkinter.Tk()
top.title("Gerber to Graphtec")

Gerber_name = StringVar()
Expand Down Expand Up @@ -234,55 +232,55 @@ def default_cut_mode_str():

Label(top, text="Gerber File ").grid(row=1, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=Gerber_name).grid(row=1, column=1)
Tkinter.Button(top, width=9, text = "Browse", command = get_input_filename).grid(row=1, column=2)
tkinter.Button(top, width=9, text = "Browse", command = get_input_filename).grid(row=1, column=2)

Label(top, text="Output File ").grid(row=2, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=Output_name).grid(row=2, column=1)
Tkinter.Button(top, width=9, text = "Browse", command = get_output_filename).grid(row=2, column=2)
tkinter.Button(top, width=9, text = "Browse", command = get_output_filename).grid(row=2, column=2)

if os.name=='nt':
Label(top, text="gerbv path ").grid(row=3, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=gerbv_path).grid(row=3, column=1)
Tkinter.Button(top, width=9, text = "Browse", command = get_gerbv_path).grid(row=3, column=2)
tkinter.Button(top, width=9, text = "Browse", command = get_gerbv_path).grid(row=3, column=2)

Label(top, text="pstoedit path ").grid(row=4, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=pstoedit_path).grid(row=4, column=1)
Tkinter.Button(top, width=9, text = "Browse", command = get_pstoedit_path).grid(row=4, column=2)
tkinter.Button(top, width=9, text = "Browse", command = get_pstoedit_path).grid(row=4, column=2)

Label(top, text="Offset ").grid(row=5, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=offset_str).grid(row=5, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_offset_str).grid(row=5, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_offset_str).grid(row=5, column=2)

Label(top, text="Border ").grid(row=6, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=border_str).grid(row=6, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_border_str).grid(row=6, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_border_str).grid(row=6, column=2)

Label(top, text="Matrix ").grid(row=7, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=matrix_str).grid(row=7, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_matrix_str).grid(row=7, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_matrix_str).grid(row=7, column=2)

Label(top, text="Speed ").grid(row=8, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=speed_str).grid(row=8, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_speed_str).grid(row=8, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_speed_str).grid(row=8, column=2)

Label(top, text="Force ").grid(row=9, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=force_str).grid(row=9, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_force_str).grid(row=9, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_force_str).grid(row=9, column=2)

Label(top, text="Cut Mode ").grid(row=10, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=cut_mode_str).grid(row=10, column=1)
Tkinter.Button(top, width=9, text = "Default", command = default_cut_mode_str).grid(row=10, column=2)
tkinter.Button(top, width=9, text = "Default", command = default_cut_mode_str).grid(row=10, column=2)

if os.name=='nt':
Label(top, text="Cutter Shared Name").grid(row=11, column=0, sticky=W)
else:
Label(top, text="Cutter Device Name").grid(row=11, column=0, sticky=W)
Entry(top, bd =1, width=60, textvariable=cutter_shared_name_str).grid(row=11, column=1, sticky=E)

Tkinter.Button(top, width=40, text = "Create Graphtec File", command = main_program).grid(row=12, column=1)
Tkinter.Button(top, width=40, text = "Send Graphtec File to Silhouette Cutter", command = Send_to_Cutter).grid(row=13, column=1)
Tkinter.Button(top, width=40, text = "Save Configuration", command = Save_Configuration).grid(row=14, column=1)
Tkinter.Button(top, width=40, text = "Exit", command = Just_Exit).grid(row=15, column=1)
tkinter.Button(top, width=40, text = "Create Graphtec File", command = main_program).grid(row=12, column=1)
tkinter.Button(top, width=40, text = "Send Graphtec File to Silhouette Cutter", command = Send_to_Cutter).grid(row=13, column=1)
tkinter.Button(top, width=40, text = "Save Configuration", command = Save_Configuration).grid(row=14, column=1)
tkinter.Button(top, width=40, text = "Exit", command = Just_Exit).grid(row=15, column=1)

if path.isfile(CONFPATH) and access(CONFPATH, R_OK):
f = open(CONFPATH,'r')
Expand Down
48 changes: 24 additions & 24 deletions gerber2graphtec
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ media_size = (12,11)
theta = 0

def floats(s):
return map(float,string.split(s,','))
return list(map(float,str.split(s,',')))

argc = 1
while argc<len(sys.argv):
Expand Down Expand Up @@ -61,28 +61,28 @@ while argc<len(sys.argv):
argc = argc + 1

if not input_filename:
print 'usage: gerber2graphtec [options] paste.gbr >/dev/usb/lp0'
print ''
print 'options:'
print ' --offset x,y translate to device coordinates x,y (inches)'
print ' --border bx,by cut a border around the bounding box of the gerber file; 0,0 to disable'
print ' --matrix a,b,c,d transform coordinates by [a b;c d]'
print ' --speed s[,s2[,s3]] use speed s in device units; s2,s3 for multiple passes'
print ' --force f[,f2[,f3]] use force f in device units; f2,f3 for multiple passes'
print ' --cut_mode [0|1] 0 for highest accuracy (fine pitch), 1 for highest speed'
print ' --media_size x,y size of media'
print ' --rotate theta rotate counterclockwise by theta degrees'
print ''
print 'defaults:'
print ' --offset 4.0,0.5 suitable for letter size (portrait) on the Cameo, fed as "media" not "mat"'
print ' --border 1,1 1-inch border in x and y around gerber bounding box'
print ' --matrix 1,0,0,1 identity linear transform for scale and skew calibration'
print ' --speed 2,2 use two passes, speed 2 in each pass'
print ' --force 8,30 use force 8 for first pass, force 30 for second pass'
print ' --cut_mode 0 highest accuracy'
print ' --media_size 12,11 use a media size of 12 inches in x, 11 inches in y'
print ' --rotate 0 no rotation'
print ''
print('usage: gerber2graphtec [options] paste.gbr >/dev/usb/lp0')
print('')
print('options:')
print(' --offset x,y translate to device coordinates x,y (inches)')
print(' --border bx,by cut a border around the bounding box of the gerber file; 0,0 to disable')
print(' --matrix a,b,c,d transform coordinates by [a b;c d]')
print(' --speed s[,s2[,s3]] use speed s in device units; s2,s3 for multiple passes')
print(' --force f[,f2[,f3]] use force f in device units; f2,f3 for multiple passes')
print(' --cut_mode [0|1] 0 for highest accuracy (fine pitch), 1 for highest speed')
print(' --media_size x,y size of media')
print(' --rotate theta rotate counterclockwise by theta degrees')
print('')
print('defaults:')
print(' --offset 4.0,0.5 suitable for letter size (portrait) on the Cameo, fed as "media" not "mat"')
print(' --border 1,1 1-inch border in x and y around gerber bounding box')
print(' --matrix 1,0,0,1 identity linear transform for scale and skew calibration')
print(' --speed 2,2 use two passes, speed 2 in each pass')
print(' --force 8,30 use force 8 for first pass, force 30 for second pass')
print(' --cut_mode 0 highest accuracy')
print(' --media_size 12,11 use a media size of 12 inches in x, 11 inches in y')
print(' --rotate 0 no rotation')
print('')
sys.exit(1)

#
Expand All @@ -92,7 +92,7 @@ if not input_filename:
temp_pdf = "_tmp_gerber.pdf"
temp_pic = "_tmp_gerber.pic"

if string.lower(input_filename[-3:])=='pdf':
if str.lower(input_filename[-3:])=='pdf':
os.system("pstoedit -f pic %s %s 2>/dev/null" % (input_filename,temp_pic))
else:
os.system("gerbv --export=pdf --output=%s --border=20 %s" % (temp_pdf,input_filename))
Expand Down
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions tests/test_array.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env python

import sys
sys.path.insert(0, '')
import random
import graphtec

Expand Down
Loading