Saturday, December 15, 2018

Milling PCBs: Tinkercad Circuits, Eagle, Carbide Copper, and Carvey

This workflow for designing and milling a single-sided PCB on the Carvey CNC is an updated workflow from that which I worked out last year when I first started doing this in my high school classroom/maker space. This workflow is much simpler, and allows my students to make their own simple PCBs in less time and with less frustration, while still giving them an experience of circuit design, PCB layout, and hardware integration to container design.
When following the longer previous workflow some students didn't have enough time to design cases for their projects and they ended up only finishing working PCBs. The overview is to use Tinkercad Circuits for breadboarding > export a board file to Eagle > layout the board in Eagle > export Gerber files > create a g-code file with Carbide Copper 3D > process g-code with a custom Python script > import g-code to Easel to mill the board.

Arduino UNO Prototype

My students made PCBs for a simple circuit with an ATtiny85, an addressable NeoPixel strip, and 3 x AAA battery holder with ON/OFF switch, and are now designing projects in which the neoPixels will be used in side-lit acrylic displays. First they prototyped their circuit and code with an Arduino UNO to get a good sense of what they wanted to do with their code. It was easy for them to choose and modify animations they liked from the Adafruit NeoPixel library using the strandtest example. They connected the NeoPixel strip's VCC wire to 5V, GND to GND, and Data In to pin 2, to make their circuit compatible with the limited pins available on the ATtiny85.

Breadboarding the Circuit

Tinkercad Circuits is great for prototyping and testing a circuit as long as you pay attention to the physical components you plan to include in the resulting board. The main reason for this is that you sometimes can't find the exact component you plan to use so you may have to find an existing one that results in the Eagle footprint you need to end up with. You will see some examples of this in the NeoPixel strip example my students worked through.
To get the correct footprint in the exported Eagle brd file, they had to use a 9V battery for power. The other battery connectors did not work, either exporting without a footprint (for AAA batteries) or exporting with a coin cell battery pin configuration. The closest Tinkercad comes to a NeoPixel strip is a NeoPixel ring, which has the same pin connections anyway. It is great that they have an ATiny to work with. In Tinkercad, export the Eagle .BRD file.

PCB Layout in Eagle

Designing circuits in Eagle the proper way, by building a schematic, is hard for beginners. Since I don't do it often and don't maintain an adequate parts library I often get stuck at finding the correct parts. The Tinkercad export allows students to bypass this step and go directly to layout. 
The first step is to create a new project, then drag the downloaded .BRD file into it.

Double click the board file in the project to open it. Your components will be dumped in the corner so use the move tool to drag them out and separate them, and rotate them to try and get the ratsnest—the thin yellow lines—untangled as much as possible. It's okay if some overlap as there are a few tricks you can use to get around overlaps on a single-sided board design.

The Tinkercad export will default to inches, but it's easier to work in mils—thousandths of an inch—for setting the dimensions of various design elements. Change the default units by clicking View > Grid, and changing the Size setting to 50 mil.

Before drawing the traces move the components up and to the right out of the way of where the Carvey's Smart Clamp will be. To import gcode Easel needs 1 inch, or 25.4mm of clearance on the left and bottom of the circuit design. It should say about (1000 1000) in the corner of the board area when you've found your spot.
To start connecting the ratsnest with routes, select the route tool,
switch to the bottom layer (because you are viewing the ATtiny85 footprint from above),
and set the route size to 40 mils. I have found that a 40 mil setting is needed for milling on the Carvey and any smaller number can lead to traces that are too thin. Notice the different shapes available for routing. Curves can be really fun and creative.
Draw routes by clicking a hole and clicking again each time you want to change direction. To make finer adjustments hold option (Mac) while routing. If you make a routing mistake and need to delete a route use the 'ripup' tool
to remove it. Here is finished routing for my example circuit. Notice even though there is an ATtiny there it's fine for the route to go under it.
One more thing to complete this routing is to widen the pin pads for the ATtiny. The Carvey will pretty much eliminate the copper with such small pads. To do this use the via tool,
and change the sizes to 70 mils width, 30 mils drilling hole, and click on each of the ATtiny pads.
Result, much easier to solder:
As promised, here are couple tips for routing. First, resistors can be like tunnels to get around a crossing trace:
If there is no way across or around, use two vias to make holes with pads on either side of the barrier. Then solder a jumper wire to connect them when the board is milled.
Now to generate CAM files! Click the little factory with a green arrow,
and a popup will show the files that are generated. A zip file will contain the files you will use the the next step, so unzip it and notice that a folder called 'outputs' has been created with your CAM files inside.

Generate G-code

Now it's time to make g-code files from your Gerber CAM files. Go to the Carbide Copper 3D website. Check that the material dimensions match the copper blanks you are using (I got mine here), and go to the next screen. 
Where it says Upload Gerber signal file click browse and find your CAM output folder and select the "copper_bottom.gbr" file to upload. Your traces should appear offset from the bottom corner.
On the next screen upload the "drills.xln" file and holes should appear on the pads in your board design.

On the next screen check the box for Generate Outline Cut and enter the dimensions that will frame your design well. The X offset may not import to Easel for the Carvey if it is less than 25.4 mm.
On the next screen check Generate Area Rubout and lines should appear on the non-copper areas.

Finally, download the '' g-code file.

G-code cleanup

Since this website is designed for CNC machines made by the Carbide 3D company the g-code will need some modifications. The Carvey cannot handle g-code with tool change commands, which pause the job while you change the mill or drill bit, so the tool change commands must be removed and the file split into four files for the isolation, drill, rubout, and cutout processes.
Fortunately I have made a Python program that does this. 
  • Go to and download the file, moving it out of its zip folder to the Downloads folder.
  • Open Terminal on Mac (on PC you will need to figure out how to run a Python program from the command line), navigate to your Downloads folder (cd Downloads), and run the file (python It will find the file downloaded from the Carbide Copper website and split it up. 
  • Next the program asks if you want to change the Z depth. Type yes and enter -0.01. You will do this twice. As it is explained on Github, the Carbide Copper Z depth, or carving depth, is too deep for the Carvey, and should be changed to -0.01. If this depth ends up being too shallow you can open the isolation and rubout files in a text editor and do a search and replace to make it go deeper. Better to cut too little rather than cutting too much.
This will leave you with 4 new files: isolation.gcode, drill.gcode, rubout.gcode, and cutout.gcode.
Time to mill and drill!

Using Easel to Mill the Board

Go to and start a new project. Click File > Import g-code and upload the isolation.gcode file. The lower left corner of your design should be at about 1" X 1".
For the steps to mill the board I am indebted to this tutorial by Larry Ledden, which covers so many important details. Here are some details crucial to our process:
  • Before milling we use double sided tape to tape the copper blank to a 3/4 inch piece of pine, making sure the middle does not bow up. Even a slight bow can cause the traces to be milled too thin. 
  • The first job is the isolation, done with this 0.2mm 30 degree engraving bit.
  • Then import the drill gcode, using a 0.9 or 1.0 mm drill bit from the Inventables PCB bit set. The size of the drill depends on the components that will need to fit in the holes. Usually we can get away with all of them being drilled with 1.0 mm holes. For bigger pins such as those on an Adafruit Trinket, for example, we would need a 1.1 or 1.2 mm bit. Actually one thing this process does not detail is multiple drill files for different hole sizes.
  • Finally switch to a 1/16 inch end mill like this fishtail and run the rubout gcode, and then the cutout gcode.
Enjoy your board!

1 comment :

Rohan said...

The python file must be an old version. I have updated it:

filenames = ['isolation.gcode','drill.gcode','rubout.gcode','cutout.gcode']
tools = ['Contour','Drilling','Rubout','Routing']

# copies contents of Carbide Copper excluding tool change codes, into
# a gcode file
def deleteToolchange():
exclude = "M06"

with open('') as filein, open('gcode.txt', 'w') as fileout:
for line in filein:
if exclude not in line:

# copies initial state gcode into separate file for each toolpath
def copyStates(gcode,splitGcode):
limit = "( )"
with open(gcode) as filein, open(splitGcode,'w') as fileout:
for line in filein:
if limit not in line:

# copies gcode for each toolpath into corresponding toolpath file
# replaces default Zdepth with new ZDepth if user wants
def copyGcode(gcode,splitGcode,tool):
# ZDepth of isolation & rubout toolpaths I found through testing various
# settings will either be set by CC to -0.100 or -0.200mm.
# Both are too deep for me on the Carvey so below I give the option to change
# thorugh user input
zDepthFound = False
oldZDepth1 = "Z-0.100"
oldZDepth2 = "Z-0.200"
newZDepth = "Z" #will be set based on user input
with open(gcode) as filein, open(splitGcode,'a') as fileout:
for line in filein:
if tool in line:
for line in filein:
if '(' in line or 'M30' in line: break
if oldZDepth1 in line:
if not zDepthFound:
print ("Current Z-depth in your " + splitGcode +" file is set to " + oldZDepth1 + ".")
changeZ = input("Would you like to change it? (yes or no) ")
if changeZ == "yes":
newZ = input("Type value including -. ")
newZDepth = newZDepth + newZ
newZDepth = oldZDepth1
zDepthFound = True
line= line.replace(oldZDepth1, newZDepth)
if oldZDepth2 in line:
if not zDepthFound:
print ("Current Z-depth in your " + splitGcode +" file is set to " + oldZDepth2 + ".")
changeZ = raw_input("Would you like to change it? (yes or no) ")
if changeZ == "yes":
newZ = input("Type value including -. ")
newZDepth = newZDepth + newZ
newZDepth = oldZDepth2
zDepthFound = True
line= line.replace(oldZDepth2, newZDepth)

for files in filenames:
for tool,filename in zip(tools,filenames):