This page describes a script for generating a Gerber-file containing indicators at component-locations. This can save a lot of time when manually populating proto-boards.
All this is probably only useful if you use KiCad, since it relies on the format of KiCad-generated centroid-files.
To manually assemble a non-trivial PCB with many components e.g. in case of a proto, it takes time to have to lookup the component-type/-value of each land to be populated, 'live', in the EDA-tool.
It would be better to have a map of locations of all components of a given type. I didn't know of a way to generate one using the KiCad suite of programs.
Existing Gerber-viewers (e.g. 'gerbview' or 'gerbv' that come with the KiCad resp. gEDA suite) can already display a stack of single-layer images. KiCad (or any other EDA-tool) can already generate its board-images as Gerber-files.
Therefore, I made a script to convert a KiCad centroid-file (X/Y/rotation) to a Gerber-image containing dots at the locations where components should be placed. This layer can then be overlayed onto e.g. the paste-layer image, to get a quick visual overview of where to place components.
#!/usr/pkg/bin/perl
use strict;
use warnings;
# WHAT IS THIS?
#
# This is a tool for manually placing components on a PCB during assembly.
#
# A Gerber-file containing dots at every component-position is constructed, taking as input
# a KiCad position-file as generated by 'pcbnew' as well as a separate file containing
# a list of refdes, one per line.
#
# This generated Gerber-file, when viewed in a Gerber-viewer (e.g. 'gerbv' or 'gerbview')
# as an overlay onto the silk-/paste-/copper-layers, can speed up manually placing many
# components of the same type onto a PCB.
#
# constants: accuracy-specifyers for Gerber encoding.
my $num_dec = 3;
my $num_frac = 3;
my $linenr = 0;
my %refdes; # key exists if refdes occurs in input-file;
# value is x/y coordinate-pair if refdes occurred in pos-file
sub exit_with_help { die "Use: <KiCad_position_filename> <refdes_list_filename> <gerber_output_filename>\n" }
sub open_file
{
my ( $fname, $rw ) = @_;
$rw ||= "<";
open my $fh, $rw, $fname or die "cannot open file '$fname'";
$fh;
}
sub strip_ws { my ( $s ) = @_; $s =~ s/^\s*(.*)[\s\n]*$/$1/; $s; }
sub read_refdeses
{
my $fh = open_file $_[ 0 ];
foreach my $r (<$fh>)
{
$r = strip_ws $r;
$refdes{ $r } = undef;
}
close $fh;
}
sub process_posfile_line
{
my ( $p ) = @_;
# Assume only the 'value' field can contain whitespace. KiCad unfortunately
# uses whitespace as field-separator, and clips fields that are too long.
#
# ref val pkg posx posy rot side
$p =~ /^\s*(\S+)\s+(\S.*\S*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/ or die "syntax error in position-file line $linenr\n";
my ( $refdes, $x, $y ) = ( $1, $4, $5 );
$refdes{ $refdes } = { x => $x, y => $y } if exists $refdes{ $refdes };
}
sub process_posfile
{
my $fh = open_file $_[ 0 ];
foreach my $p (<$fh>)
{
$linenr++;
$p = strip_ws $p;
next if $p =~ /^#/;
next if $p =~ /^\s*$/;
process_posfile_line $p;
}
close $fh;
}
sub write_gerber_header
{
my ( $fh ) = @_;
my $fmt = "$num_dec$num_frac";
print $fh "%FSLAX${fmt}Y${fmt}*%\n"; # specify num format, omit leading zeroes, abs positions
print $fh "%MOMM*%\n"; # units are millimeters
print $fh "%ADD10C,1*%\n"; # 1mm-dia solid circle as position-indicator
print $fh "%LPD*%\n"; # start new dark layer (I think this is redundant)
print $fh "D10*\n"; # select our aperture (I think this is redundant too)
}
sub write_gerber_footer { my $fh = $_[ 0 ]; print $fh "M02*\n" }
sub gerber_numeral
{
my ( $f ) = @_;
my $s = sprintf "%$num_dec.${num_frac}f", $f;
$s =~ s/\.//;
$s;
}
sub write_gerber_pos_indicator
{
my ( $fh, $x, $y ) = @_;
# (D3 = flash selected aperture at given coordinates)
print $fh "X" . gerber_numeral( $x ) . "Y" . gerber_numeral( $y ) . "D3*\n";
}
sub generate_gerber
{
my $fh = open_file $_[ 0 ], ">";
write_gerber_header $fh;
foreach my $r ( keys %refdes )
{
my $c = $refdes{ $r };
if ( $c ) { write_gerber_pos_indicator $fh, $c->{ x }, $c->{ y } }
else { print "warning: refdes '$r' not in pos-file - ignored\n" }
}
write_gerber_footer $fh;
close $fh;
}
################################################################################
my $pos_fname = $ARGV[ 0 ] or exit_with_help;
my $refdes_fname = $ARGV[ 1 ] or exit_with_help;
my $gerber_fname = $ARGV[ 2 ] or exit_with_help;
read_refdeses $refdes_fname;
process_posfile $pos_fname;
generate_gerber $gerber_fname;
And that's all.
NB: personal reminders only beyond this point :-)
sql ~/proj/046/db/046.db 'select childpartid from assemblies where parentpartid = 1267 group by childpartid order by count( childpartid ) desc' | sed 1,2d
(the 'sed 1,2d' are used to remove column-header and blank line, contained in the output at the time of writing)
db=~/proj/046/db/046.db
assy=1267
for p in $( cat partids.txt ); do
echo ---$p---
sql $db "select refdes from assemblies where parentpartid = $assy and childpartid = $p" | sed 1,2d > p$p.txt
kicad-pos2gerb.pl mainboard-sig1.pos p$p.txt tmp.pho
N=$( grep 'D3\*$' tmp.pho | wc -l | tr -d ' ' )
mv tmp.pho p${p}_$N.pho
echo " $N"
done
(the 'N' is the number of components of that part-ID; easy to be able to see as the layer-name while viewing the Gerber. Through-hole components generally don't occur in the centroid-file, so 'N' will be 0 for part-IDs corresponding to through-hole components.)
View generated Gerbers each corresponding to a single component-type, one by one:
for a in $( ls -S p*.pho ); do
./mk_gerbv_proj.pl mainboard-F_Paste.pho $a > tmp.gerbv
gerbv -p tmp.gerbv
done
The 'mk_gerbv_proj.pl' script at the time of writing:
#!/usr/pkg/bin/perl
use strict;
use warnings;
# WHAT IS THIS?
#
# this script creates a gerbv (gEDA Gerber viewer) project consisting of 2 layers; one is
# meant to be a generated layer consisting component-location indicators, and the other is some
# image of the board, e.g. front paste layer.
#
# The component-placement layer (the topmost one in the layer-stack) is shown in a bright colour,
# while the board-layer is shown in a dark colour.
#
# The project contents (just a few lines) are printed on stdout on succes.
my $boardlayer_fname = $ARGV[ 0 ] or die "use board layer as 1st argument\n";
my $poslayer_fname = $ARGV[ 1 ] or die "use position-indicator layer as 2nd argument\n";
print "(gerbv-file-version! \"2.0A\")\n";
print "(define-layer! 1 (cons 'filename \"$boardlayer_fname\")(cons 'visible #t)(cons 'color #(16912 16912 16912)))\n";
print "(define-layer! 0 (cons 'filename \"$poslayer_fname\")(cons 'visible #t)(cons 'color #(65535 0 0)))\n";
print "(set-render-type! 0)\n";
(Using a gerbv project-file instead of just calling it with the 2 Gerber's filenames as arguments, allows one to specify colours for each layer. I have no idea if this is possible without using a project-file.)
Delivered to you by Vim, GNU Make, MultiMarkdown, bozohttpd, NetBSD, and 1 human.