1//////////////////////////////////////////////////////////////////////
  2// LibFile: screw_drive.scad
  3//   Masks for Phillips, Torx and square (Robertson) driver holes.
  4// Includes:
  5//   include <BOSL2/std.scad>
  6//   include <BOSL2/screw_drive.scad>
  7// FileGroup: Threaded Parts
  8// FileSummary: Masks for Phillips, Torx and square (Robertson) driver holes.
  9//////////////////////////////////////////////////////////////////////
 10
 11
 12include <structs.scad>
 13
 14// Section: Phillips Drive
 15
 16// Module: phillips_mask()
 17// Usage: phillips_mask(size) [ATTACHMENTS];
 18// Description:
 19//   Creates a mask for creating a Phillips drive recess given the Phillips size.  Each mask can
 20//   be lowered to different depths to create different sizes of recess.  
 21// Arguments:
 22//   size = The size of the bit as an integer or string.  "#0", "#1", "#2", "#3", or "#4"
 23//   ---
 24//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 25//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 26//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 27// Example:
 28//   xdistribute(10) {
 29//      phillips_mask(size="#1");
 30//      phillips_mask(size="#2");
 31//      phillips_mask(size=3);
 32//      phillips_mask(size=4);
 33//   }
 34
 35// Specs for phillips recess here:
 36//   https://www.fasteners.eu/tech-info/ISO/4757/
 37
 38function _phillips_shaft(x) = [3,4.5,6,8,10][x];
 39function _ph_bot_angle() = 28.0;
 40function _ph_side_angle() = 26.5;
 41
 42module phillips_mask(size="#2", $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
 43    dummy = assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]));
 44    num = is_num(size) ? size : ord(size[1]) - ord("0");
 45    shaft = _phillips_shaft(num);
 46    b =     [0.61, 0.97, 1.47, 2.41, 3.48][num];
 47    e =     [0.31, 0.435, 0.815, 2.005, 2.415][num];
 48    g =     [0.81, 1.27, 2.29, 3.81, 5.08][num];
 49    alpha = [ 136,  138,  140,  146,  153][num];
 50    beta  = [7.00, 7.00, 5.75, 5.75, 7.00][num];
 51    gamma = 92.0;
 52    h1 = adj_ang_to_opp(g/2, _ph_bot_angle());   // height of the small conical tip
 53    h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle());   // height of larger cone
 54    l = h1+h2;
 55    h3 = adj_ang_to_opp(b/2, _ph_bot_angle());   // height where cutout starts
 56    p0 = [0,0];
 57    p1 = [adj_ang_to_opp(e/2, 90-alpha/2), -e/2];
 58    p2 = p1 + [adj_ang_to_opp((shaft-e)/2, 90-gamma/2),-(shaft-e)/2];
 59    attachable(anchor,spin,orient, d=shaft, l=l) {
 60        down(l/2) {
 61            difference() {
 62                rotate_extrude()
 63                    polygon([[0,0],[g/2,h1],[shaft/2,l],[0,l]]);
 64                zrot(45)
 65                zrot_copies(n=4, r=b/2) {                   
 66                    up(h3) {
 67                        yrot(beta) {
 68                            down(1)
 69                            linear_extrude(height=l+2, convexity=4, center=false) {
 70                                path = [p0, p1, p2, [p2.x,-p2.y], [p1.x,-p1.y]];
 71                                polygon(path);
 72                            }
 73                        }
 74                    }
 75                }
 76            }
 77        }
 78        children();
 79    }
 80}
 81
 82
 83
 84// Function: phillips_depth()
 85// Usage:
 86//   depth = phillips_depth(size, d);
 87// Description:
 88//   Returns the depth of the Phillips recess required to produce the specified diameter, or
 89//   undef if not possible.
 90// Arguments:
 91//   size = size as a number or text string like "#2"
 92//   d = desired diameter
 93function phillips_depth(size, d) =
 94    assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
 95    let(
 96        num = is_num(size) ? size : ord(size[1]) - ord("0"),
 97        shaft = [3,4.5,6,8,10][num],
 98        g =     [0.81, 1.27, 2.29, 3.81, 5.08][num],
 99        h1 = adj_ang_to_opp(g/2, _ph_bot_angle()),   // height of the small conical tip
100        h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle())   // height of larger cone
101    )
102    d>=shaft || d<g ? undef :
103    (d-g) / 2 / tan(_ph_side_angle()) + h1;
104
105
106// Function: phillips_diam()
107// Usage:
108//   diam = phillips_diam(size, depth);
109// Description:
110//   Returns the diameter at the top of the Phillips recess when constructed at the specified depth,
111//   or undef if that depth is not valid.  
112// Arguments:
113//   size = size as number or text string like "#2"
114//   depth = depth of recess to find the diameter of
115function phillips_diam(size, depth) =
116    assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
117    let(
118        num = is_num(size) ? size : ord(size[1]) - ord("0"),
119        shaft = _phillips_shaft(num),
120        g =     [0.81, 1.27, 2.29, 3.81, 5.08][num],
121        h1 = adj_ang_to_opp(g/2, _ph_bot_angle()),   // height of the small conical tip
122        h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle())   // height of larger cone
123    )
124    depth<h1 || depth>= h1+h2 ? undef :
125    2 * tan(_ph_side_angle())*(depth-h1) + g;
126
127
128// Section: Hex drive
129
130// Module hex_drive_mask()
131// Usage:
132//   hex_drive_mask(size, length, [anchor], [spin], [orient], [$slop]) [ATTACHMENTS];
133// Description:
134//   Creates a mask for hex drive.  Note that the hex recess specs requires
135//   a slightly oversized recess.  You can use $slop to increase the size by 
136//   `2 * $slop` if necessary.  
137// 
138module hex_drive_mask(size,length,l,h,height,anchor,spin,orient)
139{
140   length = one_defined([length,height,l,h],"length,height,l,h");
141   realsize = 1.0072*size + 0.0341 + 2 * get_slop();  // Formula emperically determined from ISO standard
142   linear_sweep(height=length,hexagon(id=realsize),anchor=anchor,spin=spin,orient=orient) children();
143}
144function hex_drive_mask(size,length,l,h,height,anchor,spin,orient) = no_function("hex_drive_mask");
145
146
147// Section: Torx Drive
148
149// Module: torx_mask()
150// Usage:
151//   torx_mask(size, l, [center]) [ATTACHMENTS];
152// Description: Creates a torx bit tip.
153// Arguments:
154//   size = Torx size.
155//   l = Length of bit.
156//   center = If true, centers mask vertically.
157//   ---
158//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
159//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
160//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
161// Examples:
162//   torx_mask(size=30, l=10, $fa=1, $fs=1);
163module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
164    od = torx_diam(size);
165    anchor = get_anchor(anchor, center, BOT, BOT);
166    attachable(anchor,spin,orient, d=od, l=l) {
167        linear_extrude(height=l, convexity=4, center=true) {
168            torx_mask2d(size);
169        }
170        children();
171    }
172}
173
174
175
176// Module: torx_mask2d()
177// Usage:
178//   torx_mask2d(size);
179// Description: Creates a torx bit 2D profile.
180// Arguments:
181//   size = Torx size.
182// Example(2D):
183//   torx_mask2d(size=30, $fa=1, $fs=1);
184module torx_mask2d(size) {
185    no_children($children);
186    info = torx_info(size);
187    od = info[0];
188    id = info[1];
189    tip = info[3];
190    rounding = info[4];
191    base = od - 2*tip;
192    $fn = quantup(segs(od/2),12);
193    difference() {
194        union() {
195            circle(d=base);
196            zrot_copies(n=2) {
197                hull() {
198                    zrot_copies(n=3) {
199                        translate([base/2,0,0]) {
200                            circle(r=tip, $fn=$fn/2);
201                        }
202                    }
203                }
204            }
205        }
206        zrot_copies(n=6) {
207            zrot(180/6) {
208                translate([id/2+rounding,0,0]) {
209                    circle(r=rounding);
210                }
211            }
212        }
213    }
214}
215
216
217// Function: torx_info()
218// Usage:
219//   info = torx_info(size);
220// Description:
221//   Get the typical dimensional info for a given Torx size.
222//   Returns a list containing, in order:
223//   - Outer Diameter
224//   - Inner Diameter
225//   - Drive Hole Depth
226//   - External Tip Rounding Radius
227//   - Inner Rounding Radius
228// Arguments:
229//   size = Torx size.
230function torx_info(size) =
231    let( 
232        info_arr = [      // Depth is from metric socket head screws, ISO 14583
233            //T#     OD     ID     H        Re     Ri
234            [  1, [  0.90,  0.65,  0.40,  0.059, 0.201]],  // depth interpolated
235            [  2, [  1.00,  0.73,  0.44,  0.069, 0.224]],  // depth interpolated
236            [  3, [  1.20,  0.87,  0.53,  0.081, 0.266]],  // depth interpolated
237            [  4, [  1.35,  0.98,  0.59,  0.090, 0.308]],  // depth interpolated
238            [  5, [  1.48,  1.08,  0.65,  0.109, 0.330]],  // depth interpolated
239            [  6, [  1.75,  1.27,  0.775, 0.132, 0.383]],
240            [  7, [  2.08,  1.50,  0.886, 0.161, 0.446]],  // depth interpolated
241            [  8, [  2.40,  1.75,  1.0,   0.190, 0.510]],
242            [  9, [  2.58,  1.87,  1.078, 0.207, 0.554]],  // depth interpolated
243            [ 10, [  2.80,  2.05,  1.142, 0.229, 0.598]],
244            [ 15, [  3.35,  2.40,  1.2,   0.267, 0.716]],  // depth interpolated
245            [ 20, [  3.95,  2.85,  1.4,   0.305, 0.859]],  // depth interpolated
246            [ 25, [  4.50,  3.25,  1.61,  0.375, 0.920]],  
247            [ 27, [  5.07,  3.65,  1.84,  0.390, 1.108]],
248            [ 30, [  5.60,  4.05,  2.22,  0.451, 1.194]],
249            [ 40, [  6.75,  4.85,  2.63,  0.546, 1.428]],
250            [ 45, [  7.93,  5.64,  3.115, 0.574, 1.796]],
251            [ 50, [  8.95,  6.45,  3.82,  0.775, 1.816]],
252            [ 55, [ 11.35,  8.05,  5.015, 0.867, 2.667]],
253            [ 60, [ 13.45,  9.60,  5.805, 1.067, 2.883]],
254            [ 70, [ 15.70, 11.20,  6.815, 1.194, 3.477]],
255            [ 80, [ 17.75, 12.80,  7.75,  1.526, 3.627]],
256            [ 90, [ 20.20, 14.40,  8.945, 1.530, 4.468]],
257            [100, [ 22.40, 16.00, 10.79,  1.720, 4.925]],
258        ],
259        found = struct_val(info_arr,size)
260    )
261    assert(found, str("Unsupported Torx size, ",size))
262    found;
263
264
265// Function: torx_diam()
266// Usage:
267//   diam = torx_diam(size);
268// Description: Get the typical outer diameter of Torx profile.
269// Arguments:
270//   size = Torx size.
271function torx_diam(size) = torx_info(size)[0];
272
273
274// Function: torx_depth()
275// Usage:
276//   depth = torx_depth(size);
277// Description: Gets typical drive hole depth.
278// Arguments:
279//   size = Torx size.
280function torx_depth(size) = torx_info(size)[2];
281
282
283
284// Section: Robertson/Square Drives
285
286// Module: robertson_mask()
287// Usage:
288//   robertson_mask(size, [extra], [ang], [$slop=]);
289// Description:
290//   Creates a mask for creating a Robertson/Square drive recess given the drive size as an integer.
291//   The width of the recess will be oversized by `2 * $slop`.  Note that this model is based
292//   on an incomplete spec.   https://www.aspenfasteners.com/content/pdf/square_drive_specification.pdf
293//   We determined the angle by doing print tests on a Prusa MK3S with $slop set to 0.05.
294// Arguments:
295//   size = The size of the square drive, as an integer from 0 to 4.
296//   extra = Extra length of drive mask to create.
297//   ang = taper angle of each face.  Default: 2.5
298//   ---
299//   $slop = enlarge recess by this twice amount.  Default: 0
300// Example:
301//   robertson_mask(size=2);
302// Example:
303//   difference() {
304//       cyl(d1=2, d2=8, h=4, anchor=TOP);
305//       robertson_mask(size=2);
306//   }
307module robertson_mask(size, extra=1, ang=2.5) {
308    dummy=assert(is_int(size) && size>=0 && size<=4);
309    Mmin = [0.0696, 0.0900, 0.1110, 0.1315, 0.1895][size];
310    Mmax = [0.0710, 0.0910, 0.1126, 0.1330, 0.1910][size];
311    M = (Mmin + Mmax) / 2 * INCH;
312    Tmin = [0.063, 0.105, 0.119, 0.155, 0.191][size];
313    Tmax = [0.073, 0.113, 0.140, 0.165, 0.201][size];
314    T = (Tmin + Tmax) / 2 * INCH;
315    Fmin = [0.032, 0.057, 0.065, 0.085, 0.090][size];
316    Fmax = [0.038, 0.065, 0.075, 0.095, 0.100][size];
317    F = (Fmin + Fmax) / 2 * INCH;
318    h = T + extra;
319    Mslop=M+2*get_slop();
320    down(T) {
321        intersection(){
322            Mtop = Mslop + 2*adj_ang_to_opp(F+extra,ang);
323            Mbot = Mslop - 2*adj_ang_to_opp(T-F,ang);
324            prismoid([Mbot,Mbot],[Mtop,Mtop],h=h,anchor=BOT);
325            cyl(d1=0, d2=Mslop/(T-F)*sqrt(2)*h, h=h, anchor=BOT);
326        }
327    }
328}
329
330
331
332// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap