Cabin Fever Tchotchke: Engraved Dog Tag

Once again I’m planning to attend the Cabin Fever Expo in York; my shop assistant says this year she won’t barf in the kitchen sink Thursday evening just before bedtime…

If I’m going to haul a Sherline CNC setup that far and spend all day talking machining, I must have some tchotchkes / swag to talk about. We figured a small plastic dog tag with relevant URLs would be appropriate.

Cabin Fever Dog Tag
Cabin Fever Dog Tag

I modeled the tag after my father’s WWII tag, including the mysterious notch. The rounded ends actually have three curves: two small fairing arcs blend the sides into the end cap.

The G-Code routine figures out all the coordinates and suchlike from some basic physical measurements & guesstimates, so tweaking the geometery is pretty straightforward. There was a blizzard going on while I wrote it: a fine day to spend indoors hacking code.

My assistant fired up Inkscape, laid out the text, figured out how to coerce G-Code out of Inkscape using the cnc-club.ru extension, then aligned it properly with the center of the chain hole as the origin on the right side. My routine calls the text G-Code file as a subroutine.

The extension’s header and footer files wrap EMC2’s SUB / ENDSUB syntactic sugar around the main file. The default files include an M2 that kills off the program; took a while to track that one down.

The header file:

O<dogtagtext> SUB

And the matching footer file:

O<dogtagtext> ENDSUB

The Inkscape-to-gcode instructions come out with absolute coordinates relative to the origin you define when you create the layout. The nested loops in my wrapper slap a G55 coordinate offset atop each label in turn, then call the subroutine.

The result is pretty slick:

Screenshot: AXIS Dog Tags
Screenshot: AXIS Dog Tags

I carved out that proof-of-concept label atop double-sided adhesive tape, but peeling off the goo is a real pain; a 2×3 array will be much worse. I’d rather do that than figure out how to clamp the fool things to the sacrificial plate, though.

The engraving is 0.2 mm deep with a Dremel 30 degree tool. My shop assistant describes it as “disturbing” the acrylic, not actually engraving a channel. This isn’t entirely a Bad Thing, as the font isn’t quite a stick font and the outline of each character mushes together. We must fiddle with the font a bit more; she favors a boldified OCR-A look.

Some lessons:

  • The Kate G-Code syntax highlighter isn’t down with EMC2’s dialect
  • Be very sure you touch off the workpiece origin in G54, not G55
  • Xylene doesn’t bother acrylic and works fine on tape adhesive
  • Symlinks aimed across an NFS link work fine in ~/emc2/nc_files/
  • That 2×3 array may be too big for the Sherline’s tooling plate
  • Tool length probing FTW!

The G-Code:

(Cabin Fever 2011 Dogtag)
(Ed Nisley - KE4ZNU - December 2010)
(Origin at center of chain hole near right side)
(Stock held down with double-stick tape)

(--------------------)
(Flow Control)

#<_DoText>      = 1
#<_DoDrill>     = 1
#<_DoMill>      = 1

( Sizes and Shapes)

(-- Tag array layout)

#<_NumTagsX>    = 3                         (number of tags along X axis)
#<_NumTagsY>    = 2                         ( ... Y axis)

#<_TagSpaceX>   = 60                        (center-to-center along X axis)
#<_TagSpaceY>   = 35                        ( ... Y axis)

(-- Tag Dimensions)

#<_TagSizeX>    = 50.8                      (2.0 inches in WWII!)
#<_TagSizeY>    = 28.6                      (1-1/8 inches)
#<_TagSizeZ>    = 2.0

#<_HoleOffsetX> = 4.0                       (hole center to right-side tag edge)

#<_NotchSizeX>      = 3.5                   (locating notch depth from far left edge)
#<_NotchCtrY>       = 5.0                   (locating notch from Y=0)

#<_NotchAngleBot>   = 30                    (lower angle in notch)
#<_NotchAngleTop>   = 45                    (upper angle in notch)

(-- Fairing Curve Dimensions as offsets from end arc center)

#<_EndFairR>    = [0.68 * #<_TagSizeY>]
#<_CornerFairR> = [0.25 * #<_TagSizeY>]

#<_PCRadius>    = [#<_EndFairR> - #<_CornerFairR>]
#<_PCY>         = [[#<_TagSizeY> / 2] - #<_CornerFairR>]
#<_PCTheta>     = ASIN [#<_PCY> / #<_PCRadius>]
#<_PCX>         = [#<_PCRadius> * COS [#<_PCTheta>]]

#<_P1Y>         = [#<_TagSizeY> / 2]                    (top / bottom endpoint)
#<_P1X>         = #<_PCX>

#<_P2X>         = [#<_EndFairR> * COS [#<_PCTheta>]]
#<_P2Y>         = [#<_EndFairR> * SIN [#<_PCTheta>]]

(-- Tooling)

#<_TraverseZ>   = 1.0                       (safe clearance above workpiece)

#<_DrillDia>    = 3.2                       (drill for hole and notch)
#<_DrillNum>    = 1                         ( ... tool number)
#<_DrillRadius> = [#<_DrillDia> / 2]
#<_DrillFeed>   = 200                       (drill feed for holes)
#<_DrillRPM>    = 3000

#<_MillDia>     = 3.2                       (mill for outline)
#<_MillNum>     = 1                         ( ... tool number)
#<_MillRadius> = [#<_MillDia> / 2]
#<_MillFeed>    = 150                       (tool feed for outlines)
#<_MillRPM>     = 5000

#<_TextDia>     = 0.1                       (engraving tool)
#<_TextNum>     = 1
#<_TextFeed>    = 600                       (tool feed for engraving)
#<_TextRPM>     = 10000

(-- Useful calculated values)

#<_TagRightX>   = #<_HoleOffsetX>           (extreme limits of tag in X)
#<_TagLeftX>    = [#<_TagRightX> - #<_TagSizeX>]

#<_EndFairRtX>  = [#<_TagRightX> - #<_EndFairR>]
#<_EndFairLfX>  = [#<_TagLeftX> + #<_EndFairR>]

#<_NotchCtrX>   = [#<_TagLeftX> + #<_NotchSizeX> - #<_DrillRadius>]

(--------------------)
(--------------------)
( Initialize first tool length at probe switch)
(    Assumes G59.3 is still in machine units, returns in G54)
( ** Must set these constants to match G20 / G21 condition!)

#<_Probe_Speed>     = 400            (set for something sensible in mm or inch)
#<_Probe_Retract>   =   1            (ditto)

O<Probe_Tool> SUB

G49                     (clear tool length compensation)
G30                     (move above probe switch)
G59.3                   (coord system 9)

G38.2 Z0 F#<_Probe_Speed>           (trip switch on the way down)

G0 Z[#5063 + #<_Probe_Retract>]     (back off the switch)

G38.2 Z0 F[#<_Probe_Speed> / 10]    (trip switch slowly)

#<_ToolZ> = #5063                    (save new tool length)

G43.1 Z[#<_ToolZ> - #<_ToolRefZ>]    (set new length)

G54                     (coord system 0)
G30                     (return to safe level)

O<Probe_Tool> ENDSUB

(-------------------)
(-- Initialize first tool length at probe switch)

O<Probe_Init> SUB

#<_ToolRefZ> = 0.0      (set up for first call)

O<Probe_Tool> CALL

#<_ToolRefZ> = #5063    (save trip point)

G43.1 Z0                (tool entered at Z=0, so set it there)

O<Probe_Init> ENDSUB

(--------------------)
(Start machining)

G40 G49 G54 G80 G90 G94 G97 G98     (reset many things)

G21                                 (metric!)

(msg,Verify G30.1 position in G54 above tool change switch)
M0
(msg,Verify XYZ=0 touched off at left front tag hole center on surface)
M0

O<Probe_Init> CALL
T0 M6                           (clear the probe tool)

(-- Engrave Text)

O<DoText> IF [#<_DoText>]

(msg,Insert engraving tool)
T#<_TextNum> M6         (load engraving tool)
O<Probe_Tool> CALL

F#<_TextFeed>
S#<_TextRPM>

(debug,Set spindle to #<_TextRPM>)
M0

G0 X0 Y0                (get safely to first tag)
G0 Z#<_TraverseZ>       (to working level)

G10 L20 P2 X0 Y0 Z#<_TraverseZ>         (set G55 origin to 0,0 at this point)
G55                                     (activate G55 coordinates)

O3000 REPEAT [#<_NumTagsX>]

O3100 REPEAT [#<_NumTagsY>]

O<dogtagtext> CALL

G0 X0 Y0
G10 L20 P2 Y[0 - #<_TagSpaceY>]         (set Y orgin relative to next tag in +Y direction)

O3100 ENDREPEAT

G10 L20 P2 X[0 - #<_TagSpaceX>] Y[[#<_NumTagsY> - 1] * #<_TagSpaceY>] (next to +X, Y to front)

O3000 ENDREPEAT

G54                                     (bail out of G55 coordinates)

(-- Drill holes)

O<DoDrill> IF [#<_DoDrill>]

T0 M6
(msg,Insert drill)
T#<_DrillNum> M6
O<Probe_Tool> CALL

F#<_DrillFeed>
S#<_DrillRPM>

#<_DrillZ> = [0 - #<_TagSizeZ> - #<_DrillRadius>]

(debug,Set spindle to #<_DrillRPM>)
M0

G0 X0 Y0                (get safely to first tag)
G0 Z#<_TraverseZ>       (to working level)

#<IndexX> = 0
O1000 DO

#<IndexY> = 0
O1100 DO

#<TagOriginX> = [#<IndexX> * #<_TagSpaceX>]
#<TagOriginY> = [#<IndexY> * #<_TagSpaceY>]

G81 X#<TagOriginX> Y#<TagOriginY> Z#<_DrillZ> R#<_TraverseZ>
G81 X[#<TagOriginX> + #<_NotchCtrX>] Y[#<TagOriginY> + #<_NotchCtrY>] Z#<_DrillZ> R#<_TraverseZ>

#<IndexY> = [#<IndexY> + 1]
O1100 WHILE [#<IndexY> LT #<_NumTagsY>]

#<IndexX> = [#<IndexX> + 1]
O1000 WHILE [#<IndexX> LT #<_NumTagsX>]

G30     (go home)

O<DoDrill> ENDIF

(-- Machine outlines)

O<DoMill> IF [#<_DoMill>]

T0 M6                   (eject drill)
(msg,Insert end mill)
T#<_MillNum> M6         (load mill)
O<Probe_Tool> CALL

F#<_MillFeed>
S#<_MillRPM>

(debug,Set spindle to #<_MillRPM>)
M0

G0 X0 Y0                (get safely to first tag)
G0 Z#<_TraverseZ>       (to working level)

G10 L20 P2 X0 Y0 Z#<_TraverseZ>         (set G55 origin to 0,0 at this point)
G55                                     (activate G55 coordinates)

O2000 REPEAT [#<_NumTagsX>]

O2100 REPEAT [#<_NumTagsY>]

G0 X[#<_NotchCtrX>] Y[#<_NotchCtrY>]     (get to center of notch hole)
G0 Z[0 - #<_TagSizeZ>]                      (down to cutting level)

G91                                         (relative coordinate for notch cutting)
G1 X[0 - #<_NotchSizeX>] Y[0 -  #<_NotchSizeX> * TAN [#<_NotchAngleBot>]]
G1 X[0 + #<_NotchSizeX>] Y[0 +  #<_NotchSizeX> * TAN [#<_NotchAngleBot>]]
G1 X[0 - #<_NotchSizeX>] Y[0 +  #<_NotchSizeX> * TAN [#<_NotchAngleTop>]]
G90                                         (back to abs coords)

G42.1 D#<_MillDia>                          (cutter comp to right)
G1 X[#<_TagLeftX>] Y0                       (comp entry move to tip of left endcap)

G3 X[#<_EndFairLfX> - #<_P2X>] Y[0 - #<_P2Y>] I[#<_EndFairR>] J0    (left endcap front half)

G3 X[#<_EndFairLfX> - #<_P1X>] Y[0 - #<_P1Y>] I[#<_P2X> - #<_PCX>] J[#<_P2Y> - #<_PCY>]

G1 X[#<_EndFairRtX> + #<_P1X>]                                      (front edge)

G3 X[#<_EndFairRtX> + #<_P2X>] Y[0 - #<_P2Y>] I0 J[#<_CornerFairR>]

G3 X[#<_EndFairRtX> + #<_P2X>] Y[#<_P2Y>] I[0 - #<_P2X>] J[#<_P2Y>]    (right endcap)

G3 X[#<_EndFairRtX> + #<_P1X>] Y[#<_P1Y>] I[#<_PCX> - #<_P2X>] J[#<_PCY> - #<_P2Y>]

G1 X[#<_EndFairLfX> - #<_P1X>]                                      (rear edge)

G3 X[#<_EndFairLfX> - #<_P2X>] Y[#<_P2Y>] I0 J[0 - #<_CornerFairR>]

G3 X[#<_EndFairLfX> - #<_P2X>] Y[0 - #<_P2Y>] I[#<_P2X>] J[0 - #<_P2Y>]    (left endcap complete)

G0 Z#<_TraverseZ>

G40

G0 X0 Y0
G10 L20 P2 Y[0 - #<_TagSpaceY>]         (set Y orgin relative to next tag in +Y direction)

O2100 ENDREPEAT

G10 L20 P2 X[0 - #<_TagSpaceX>] Y[[#<_NumTagsY> - 1] * #<_TagSpaceY>] (next to +X, Y to front)

O2000 ENDREPEAT

G54                                     (bail out of G55 coordinates)

G30         (go home)

O<DoMill> ENDIF

M2

The doodles leading to the equations:

Dog Tag Geometry Doodles
Dog Tag Geometry Doodles

We’ll see you there!