omdl  v1.0
OpenSCAD Mechanical Design Library
power_strip.scad
Go to the documentation of this file.
1 //! A power strip maker for electrical receptacles and/or devices.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2025-2026
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28  \details
29 
30  \amu_define group_name (Power Strip)
31  \amu_define group_brief (Electrical receptacle power strip generator.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 //----------------------------------------------------------------------------//
37 
38 /***************************************************************************//**
39  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
40  \amu_define includes_required_add
41  (
42  transforms/base_cs.scad
43  models/3d/misc/omdl_logo.scad
44  models/3d/fastener/screws.scad
45  parts/3d/fastener/clamps.scad
46  parts/3d/fastener/mounts.scad
47  parts/3d/enclosure/project_box_rectangle.scad
48  )
49  \amu_include (include/amu/includes_required.amu)
50 *******************************************************************************/
51 
52 //----------------------------------------------------------------------------//
53 // global configuration variables
54 //----------------------------------------------------------------------------//
55 
56 //! \name Configuration
57 //! @{
58 
59 //! \cond DOXYGEN_SHOULD_SKIP_THIS
60 power_strip_box_doc =
61 [
62  ["wth", "Box wall thickness"],
63  ["roww", "Receptacle row width"],
64  ["dlts", "Device length tab-to-tab space"],
65  ["boxd", "Box internal depth"],
66  ["evrm", "Box & cover rounding mode: {0|1|2}"],
67  ["evr", "Box & cover rounding radius"],
68  ["cdms", "Cover uses device mount screws: {true|false}"],
69  ["lefb", "Box lid edge finish: {0|1|2|3|4}"],
70  ["lefc", "Cover lid edge finish: {0|1|2|3|4}"],
71  ["dlogo", "Detail logo on box rear: {true|false}"],
72  ["drimb", "Detail rim on box rear: {true|false}"],
73  ["drimc", "Detail rim on cover top: {true|false}"],
74  ["fins", "Post fins: [number, angle, width, length]"],
75  ["ribs", "Box ribs configuration: see project_box_rectangle()"],
76  ["wmode", "Box wall mode: see project_box_rectangle()"],
77  ["wiab", "Box wall instance additions: see project_box_rectangle()"],
78  ["pmode", "Box post mode (b7=1 required): see project_box_rectangle()"],
79  ["piab", "Box post instance additions: see project_box_rectangle()"],
80  ["piac", "Cover post instance additions: see project_box_rectangle()"],
81  ["mpc2b", "Mirror cover post additions in box: {true|false}"],
82  ["mphda", "Mirrored post hole diameter adjustment"],
83  ["iscl", "Input space: cord, switch, surge, etc"],
84  ["oscl", "Output space: wire-nuts, led, aux board, etc"],
85  ["lscl", "Left-side extra space"],
86  ["rscl", "Right-side extra space"],
87  ["iwdo", "Internal wall divisions on: {true|false}"],
88  ["iwpd", "Internal wall wire pass diameter"],
89  ["iwps", "Internal wall wire pass side: {+1|-1}"],
90  ["pwco", "Power cord connections offset: [x, z]"],
91  ["pwcd", "Power cord dimensions: {d|[w,h]}"],
92  ["pwcs", "Power cord clamp side: {0|1}"],
93  ["pwct", "Power cord clamp tab width"],
94  ["pwcp", "Power cord clamp pinch bar percentage: [h, w]"],
95  ["pwsd", "Power cord clamp screw diameter"],
96  ["pwsh", "Power cord clamp screw head spec: see screw_bore()"],
97  ["pwsn", "Power cord clamp screw nut spec: see screw_bore()"],
98  ["mtab", "Mount tab: [screw, brace, vrm, vr, wth, size]: see mount_screw_tab()"],
99  ["mtabs", "Mount tab instances: [[edge, zero, move], ...]"],
100  ["mslot", "Mount slot: [screw, cover, size, scale, wth]: see mount_screw_slot()"],
101  ["mslots", "Mount slot instances: [[move, rotate, align], ...]"]
102 ];
103 //! \endcond
104 
105 //! <map> A single gang electrical device box configuration.
106 /***************************************************************************//**
107  \details
108 
109  The default electrical device box configuration map.
110 
111  \amu_define title (Default device box configuration map)
112  \amu_define scope_id (box_default)
113  \amu_define output_scad (false)
114  \amu_define output_console (false)
115  \amu_define notes_table (Map key description is available in source. See the map)
116 
117  \amu_include (include/amu/scope_table.amu)
118 
119  \hideinitializer
120 *******************************************************************************/
122 [
123  ["wth", 2.0], // box wall thickness
124  ["roww", 50.0], // receptacle row width
125  ["dlts", 108.00], // device length tab-to-tab space
126  ["boxd", 25.0], // box internal depth
127 
128  ["evrm", 2], // box & cover rounding mode: {0|1|2}
129  ["evr", 4.0], // box & cover rounding radius
130 
131  ["cdms", false], // cover uses device mount screws: {true|false}
132 
133  ["lefb", 2], // box lid edge finish: {0|1|2|3|4}
134  ["lefc", 2], // cover lid edge finish: {0|1|2|3|4}
135 
136  ["dlogo", true], // detail logo on box rear: {true|false}
137  ["drimb", true], // detail rim on box rear: {true|false}
138  ["drimc", true], // detail rim on cover top: {true|false}
139 
140  ["fins", [3, 270, 3, 3/4]], // post fins: [number, angle, width, length]
141 
142  ["ribs", [0, 1.75] ], // (1) box ribs configuration
143  ["wmode", 426], // (1) box wall mode
144  ["wiab", undef], // (1) box wall instance additions
145  ["pmode", 138], // (1) box post mode (b7=1 required)
146  ["piab", undef], // (1) box post instance additions
147  ["piac", undef], // (1) cover post instance additions
148 
149  ["mpc2b", true], // mirror cover post additions in box: {true|false}
150  ["mphda", 0], // mirrored post hole diameter adjustment
151 
152  ["iscl", 15.0], // input space: cord, switch, surge, etc
153  ["oscl", 0], // output space: wire-nuts, led, aux board, etc
154  ["lscl", 0], // left-side extra space
155  ["rscl", 0], // right-side extra space
156 
157  ["iwdo", true], // internal wall divisions on: {true|false}
158  ["iwpd", 10.0], // internal wall wire pass diameter
159  ["iwps", +1], // internal wall wire pass side: {+1|-1}
160 
161  ["pwco", [0, 0]], // power cord connections offset: [x, z]
162  ["pwcd", 7], // power cord dimensions: {d|[w,h]}
163  ["pwcs", 0], // power cord clamp side: {0|1}
164  ["pwct", 10.5], // power cord clamp tab width
165  ["pwcp", undef], // power cord clamp pinch bar percentage: [h, w]
166 
167  ["pwsd", 3.5], // power cord clamp screw diameter
168  ["pwsh", [6, 3.5]], // (2) power cord clamp screw head spec
169  ["pwsn", [5.75, 2.5]], // (2) power cord clamp screw nut spec
170 
171  ["mtab", [4, 25, 4]], // (3) mount tab: [screw, brace, vrm, vr, wth, size]
172  ["mtabs", undef], // mount tab instances: [[edge, zero, move], ...]
173 
174  ["mslot", [4, [1, 1, 4]]], // (4) mount slot: [screw, cover, size, scale, wth]
175  ["mslots", undef] // mount slot instances: [[move, rotate, align], ...]
176 ];
177  /*
178  (1): see project_box_rectangle() in omdl for descriptions
179  (2): see screw_bore() in omdl for descriptions
180  (3): see mount_screw_tab() in omdl for descriptions
181  (4): see mount_screw_slot() in omdl for descriptions
182  */
183 
184 //! \cond DOXYGEN_SHOULD_SKIP_THIS
185 power_strip_mount_doc =
186 [
187  ["mss", "Device mount screw separation"],
188  ["rmsd", "Mount screw hole diameter"],
189  ["rmsh", "Mount screw head height"],
190  ["rshc", "Mount screw head clearance"],
191  ["rmth", "Mount tab height"]
192 ];
193 //! \endcond
194 
195 //! <map> A single gang electrical device mount configuration.
196 /***************************************************************************//**
197  \details
198 
199  The default electrical device mount configuration map.
200 
201  \amu_define title (Default device mount configuration map)
202  \amu_define scope_id (mount_default)
203  \amu_define output_scad (false)
204  \amu_define output_console (false)
205  \amu_define notes_table (Map key description is available in source. See the map)
206 
207  \amu_include (include/amu/scope_table.amu)
208 
209  \hideinitializer
210 *******************************************************************************/
212 [
213  ["mss", length(3+9/32, "in")], // device mount screw separation
214  ["rmsd", length( 1/8, "in")], // mount screw hole diameter
215  ["rmsh", length( 3/32, "in")], // mount screw head height
216  ["rshc", length( 5/16, "in")], // mount screw head clearance
217  ["rmth", length( 1/16, "in")] // mount tab height
218 ];
219 
220 //! \cond DOXYGEN_SHOULD_SKIP_THIS
221 power_strip_cover_doc =
222 [
223  ["drpo", "Receptacle offset"],
224  ["rpd", "Receptacle diameter"],
225  ["rpfl", "Receptacle flat-to-flat height"],
226  ["rcsd", "Cover center hole screw: [diameter, head-diameter, head-height, tolerance]"]
227 ];
228 //! \endcond
229 
230 //! <map> A single gang duplex receptacle cover configuration.
231 /***************************************************************************//**
232  \details
233 
234  The default electrical device cover configuration map.
235 
236  \amu_define title (Default device cover configuration map)
237  \amu_define scope_id (cover_default)
238  \amu_define output_scad (false)
239  \amu_define output_console (false)
240  \amu_define notes_table (Map key description is available in source. See the map)
241 
242  \amu_include (include/amu/scope_table.amu)
243 
244  \hideinitializer
245 *******************************************************************************/
247 [
248  ["drpo", length(1+1/2, "in")], // receptacle offset
249  ["rpd", length(1+3/8, "in")], // receptacle diameter
250  ["rpfl", length(1+5/32, "in")], // receptacle flat-to-flat height (1/32" added)
251  ["rcsd", // cover center hole screw:
252  [3.51, 7.50, 2.5, 1/2]] // [diameter, head-diameter, head-height, tolerance]
253 ];
254 
255 //! \cond DOXYGEN_SHOULD_SKIP_THIS
259 //! \endcond
260 
261 //! @}
262 
263 //----------------------------------------------------------------------------//
264 // modules
265 //----------------------------------------------------------------------------//
266 
267 //! \name Modules
268 //! @{
269 
270 //! A power strip generator for single gang electrical receptacles.
271 /***************************************************************************//**
272  \param cols <integer> device column count.
273 
274  \param rows <integer> device row count.
275 
276  \param mode <integer> part mode.
277 
278  \param verb <integer> console output verbosity {0|1|2}.
279 
280  \param cm_box <map> box configuration map.
281 
282  \param cm_mount <map> device mount configuration map.
283 
284  \param cm_cover <map> cover configuration map.
285 
286  \details
287 
288  This module constructs a power strip grid of standard [NEMA]
289  electrical power receptacles. The number of columns and rows in the
290  power strip are configurable as well as most aspects of the power
291  strip enclosure box, receptacle device mounts, and device cover.
292  The default configuration of the enclosure box, device mount, and
293  device cover are specified in global variable maps.
294 
295  ### part mode
296 
297  Integer value is binary encoded.
298 
299  b | description
300  ---:|:---------------------------------------
301  0 | generate enclosure box
302  1 | generate clamp top
303  2 | generate enclosure cover
304 
305  The default configuration maps can be completely replaced with a
306  user supplied maps or may be partially updated as shown in the
307  following example.
308 
309  \amu_define scope_id (example)
310  \amu_define title (Custom power strip example)
311  \amu_define image_views (bottom front diag)
312  \amu_define image_size (sxga)
313  \amu_define output_scad (true)
314 
315  \amu_include (include/amu/scope_diagrams_3d.amu)
316 
317  [NEMA]: https://en.wikipedia.org/wiki/NEMA_connector
318 *******************************************************************************/
319 module power_strip_sg
320 (
321  cols = 1,
322  rows = 1,
323 
324  mode = 7,
325  verb = 1,
326 
327  cm_box = power_strip_box_default,
328  cm_mount = power_strip_mount_default,
329  cm_cover = power_strip_cover_default
330 )
331 {
332  //
333  // local modules and functions
334  //
335 
336  // lid edge finish; s:{0|1|2|3|4}
337  function e ( s = 0 )
338  = let
339  (
340  bs = 2/100
341  )
342  (s == 1) ? [wth*1/3, [wth*2/3, [1, 1-bs]]]
343  : (s == 2) ? [wth*1/3, [wth*2/3, [for (s=[0:1/32:1/8]) 1-pow(s,2)]]]
344  : (s == 3) ? [[wth*4/5, [1, 1+bs]], [wth*1/5, 1+bs]]
345  : (s == 4) ? [[wth*4/5, [for (s=[0:1/32:1/8]) 1+pow(s,2)]], [wth*1/5, 1+pow(1/8,2)]]
346  : wth;
347 
348  // enclosure lips; m:{1=box, 2=cover}
349  function l ( m = 0 )
350  = let
351  (
352  rmsh = map_get_value(cm_mount, "rmsh")
353  )
354  [
355  m, // lip mode
356  rmsh*2 // lip height
357  ];
358 
359  // power cord connection z-offset
360  function pczo ( d = 0 )
361  = let
362  (
363  zo = defined_e_or(map_get_value(cm_box, "pwco"), 1, 0)
364  )
365  wth + (is_list(d) ? second(d) : d) * 5/4 + zo;
366 
367  // power connection clamp strap
368  module clamp_strap()
369  {
370  // box power cord connection
371  pwcd = map_get_value(cm_box, "pwcd");
372  pwsd = map_get_value(cm_box, "pwsd");
373  pwsh = map_get_value(cm_box, "pwsh");
374  pwsn = map_get_value(cm_box, "pwsn");
375  pwct = map_get_value(cm_box, "pwct");
376  pwcp = map_get_value(cm_box, "pwcp");
377 
378  pwzo = pczo( pwcd );
379 
380  translate([0, 0, pwct - eps*2])
381  clamp_cg(size=pwcd, clamp=[1, pwzo, [pwsd,undef,pwsh,pwsn], pwct, pwcp], wth=0, mode=2);
382  }
383 
384  // power strip base
385  module enclosure_box()
386  {
387  //
388  // local modules
389  //
390 
391  // convert cover post instance additions for box (x and y dimensions only)
392  // mirror: type, x-align, x-move, and xy-rotate, add diameter adjustment
393  function piac_to_piab(pia)
394  = [
395  for (i=pia)
396  let
397  (
398  mphda = map_get_value(cm_box, "mphda"),
399 
400  t = i[0],
401  a = i[1], ax = a[0], ay = a[1],
402  m = i[2], mx = m[0], my = m[1],
403  r = i[3],
404  h0 = i[4],
405  h1 = i[5],
406  p = i[6],
407  f = i[7]
408  )
409  [
410  t == 0 ? 1 : 0,
411  [is_undef(ax) ? undef : -ax, ay],
412  [is_undef(m) ? undef : -mx, my],
413  is_undef(r) ? undef : -r,
414  [h0[0], h0[1], h0[2], mphda, h0[4], h0[5], h0[6]],
415  h1, p, f
416  ]
417  ];
418 
419  // screw mount tabs
420  module mount_tabs()
421  {
422  mtab = map_get_value(cm_box, "mtab");
423 
424  // configuration and defaults
425  s = defined_e_or(mtab, 0, undef);
426  b = defined_e_or(mtab, 1, undef);
427  vrm = defined_e_or(mtab, 2, undef);
428  vr = defined_e_or(mtab, 3, undef);
429  w = defined_e_or(mtab, 4, wth*2);
430  sz = defined_e_or(mtab, 5, undef);
431 
432  //
433  // instantiate
434  //
435 
436  mtabs = map_get_value(cm_box, "mtabs");
437 
438  if ( is_defined( mtabs ) )
439  for ( i = mtabs )
440  {
441  e = defined_e_or(i, 0, 0); // edge: {0|1|2|3}
442  z = defined_e_or(i, 1, 0); // zero
443  m = defined_e_or(i, 2, 0); // move
444 
445  if ( e == 0 )
446  { // top edge
447  translate([limit(z,-1,+1)*iw/2 + m, il/2 + wth - eps*2, 0])
448  rotate([0, 0, 0])
449  mount_screw_tab(wth=w, screw=s, brace=b, size=sz, vr=vr, vrm=vrm);
450  } else
451  if ( e == 1 )
452  { // right edge
453  translate([iw/2 + wth - eps*2, limit(z,-1,+1)*il/2 + m, 0])
454  rotate([0, 0, -90])
455  mount_screw_tab(wth=w, screw=s, brace=b, size=sz, vr=vr, vrm=vrm);
456  } else
457  if ( e == 2 )
458  { // bottom edge
459  translate([limit(z,-1,+1)*iw/2 + m, -(il/2 + wth - eps*2), 0])
460  rotate([0, 0, 180])
461  mount_screw_tab(wth=w, screw=s, brace=b, size=sz, vr=vr, vrm=vrm);
462  } else
463  if ( e == 3 )
464  { // left edge
465  translate([-(iw/2 + wth - eps*2), limit(z,-1,+1)*il/2 + m, 0])
466  rotate([0, 0, +90])
467  mount_screw_tab(wth=w, screw=s, brace=b, size=sz, vr=vr, vrm=vrm);
468  }
469  }
470  }
471 
472  // screw mount slots
473  module mount_slots(m)
474  {
475  mslot = map_get_value(cm_box, "mslot");
476 
477  // configuration and defaults
478  s = defined_e_or(mslot, 0, undef);
479  c = defined_e_or(mslot, 1, undef);
480  l = defined_e_or(mslot, 2, undef);
481  f = defined_e_or(mslot, 3, undef);
482  w = defined_e_or(mslot, 4, wth);
483 
484  //
485  // instantiate
486  //
487 
488  mslots = map_get_value(cm_box, "mslots");
489 
490  if ( is_defined( mslots ) )
491  for ( i = mslots )
492  {
493  // instance
494  t = defined_e_or(i, 0, origin3d);
495  r = defined_e_or(i, 1, 0);
496  a = defined_e_or(i, 2, 0);
497 
498  translate(t)
499  rotate(r)
500  mount_screw_slot(wth=w, screw=s, cover=c, size=l, align=a, mode=m, f=f);
501  }
502  }
503 
504  // base internal wall wire holes
505  module internal_wire_passage()
506  {
507  dlts = map_get_value(cm_box, "dlts");
508  roww = map_get_value(cm_box, "roww");
509  iscl = map_get_value(cm_box, "iscl");
510  oscl = map_get_value(cm_box, "oscl");
511  lscl = map_get_value(cm_box, "lscl");
512  rscl = map_get_value(cm_box, "rscl");
513  iwpd = map_get_value(cm_box, "iwpd");
514  iwps = map_get_value(cm_box, "iwps");
515 
516  // offset from wall by "wth"
517  zr = let
518  (
519  z = iw/2 - iwpd/2 - wth,
520  s = (iwps>0) ? 1 : -1,
521  o = (iwps>0) ? rscl : -lscl
522  )
523  z * s - o;
524  sr = roww;
525 
526  nc = (oscl>0) ? cols : cols-1;
527  zc = -il/2 - wth + iscl;
528  sc = dlts + wth;
529 
530  // offset from bottom by 'wth"
531  zo = wth*2 + iwpd/2;
532 
533  // for echo row and wall instance
534  for (i=[0:rows-1], j=[0 : nc])
535  translate([zr - i*sr * iwps, zc + j*sc, zo])
536  rotate([90, 0, 0])
537  cylinder(d=iwpd, h = wth+eps*8, center=true);
538  }
539 
540  //
541  // local variables
542  //
543 
544  cdms = map_get_value(cm_box, "cdms");
545 
546  // box power cord connection
547  pwcd = map_get_value(cm_box, "pwcd");
548  pwcs = map_get_value(cm_box, "pwcs");
549  pwsd = map_get_value(cm_box, "pwsd");
550  pwsh = map_get_value(cm_box, "pwsh");
551  pwsn = map_get_value(cm_box, "pwsn");
552  pwct = map_get_value(cm_box, "pwct");
553  pwcp = map_get_value(cm_box, "pwcp");
554 
555  pwzo = pczo( pwcd );
556  pwxo = defined_e_or(map_get_value(cm_box, "pwco"), 0, 0);
557 
558  ih = map_get_value(cm_box, "boxd");
559 
560  // base ribs
561  r = map_get_value(cm_box, "ribs");
562 
563  // base walls
564  w = let
565  (
566  dlts = map_get_value(cm_box, "dlts"),
567  iscl = map_get_value(cm_box, "iscl"),
568  oscl = map_get_value(cm_box, "oscl"),
569  wmode = map_get_value(cm_box, "wmode"),
570  wiab = map_get_value(cm_box, "wiab"),
571  iwdo = map_get_value(cm_box, "iwdo"),
572 
573  nc = (oscl>0) ? cols : cols-1,
574  zc = -il/2 - wth + iscl,
575  sc = dlts + wth
576  )
577  ( !iwdo ) ? undef
578  : [ // wall configuration:mode
579  wmode,
580 
581  // wall instances
582  [
583  for (j=[0 : nc]) [0, zc + j*sc],
584 
585  // add custom instances
586  if ( is_defined(wiab) ) for (i=wiab) i
587  ]
588  ];
589 
590  // base screw posts
591  p = let
592  (
593  dlts = map_get_value(cm_box, "dlts"),
594  roww = map_get_value(cm_box, "roww"),
595  iscl = map_get_value(cm_box, "iscl"),
596  lscl = map_get_value(cm_box, "lscl"),
597  fins = map_get_value(cm_box, "fins"),
598  pmode = map_get_value(cm_box, "pmode"),
599  piab = map_get_value(cm_box, "piab"),
600  piac = map_get_value(cm_box, "piac"),
601  mpc2b = map_get_value(cm_box, "mpc2b"),
602 
603  mss = map_get_value(cm_mount, "mss"),
604  rmsd = map_get_value(cm_mount, "rmsd"),
605  rmsh = map_get_value(cm_mount, "rmsh"),
606  rmth = map_get_value(cm_mount, "rmth"),
607 
608  u = undef,
609 
610  zr = -iw/2 + roww/2 + lscl,
611  sr = roww,
612 
613  zc = iscl + (dlts - mss)/2,
614  sc = dlts + wth,
615  dc = mss,
616 
617  h0 = [rmsd],
618  p1 = [u, u, u, u, -rmth -(cdms?0:rmsh)],
619  f = fins
620  )
621  [ // post configuration:mode
622  pmode,
623 
624  // post instances
625  [
626  for (i=[0:rows-1], j=[0:cols-1])
627  [2, [0, -1], [zr + i*sr, zc + j*sc, 0], 180, h0, u, p1, f ],
628  for (i=[0:rows-1], j=[0:cols-1])
629  [2, [0, -1], [zr + i*sr, zc + j*sc + dc, 0], 000, h0, u, p1, f ],
630 
631  // add custom instances
632  if ( is_defined(piab) ) for (i=piab) i,
633 
634  // mirror custom cover instances
635  if ( is_defined(piac) && mpc2b ) for (i=piac_to_piab(piac)) i
636  ]
637  ];
638 
639  // lid edge finish
640  lf = e( map_get_value(cm_box, "lefb") );
641 
642  //
643  // instances
644  //
645 
646  difference()
647  {
648  union()
649  {
650  // base enclosure
652  (
653  wth = wth,
654  lid = lf,
655  h = ih, size = [iw, il],
656  vrm = evrm, vr = evr,
657 
658  lip = l(1),
659  rib = r,
660 
661  wall = w,
662  post = p,
663 
664  mode = 1,
665 
666  verb = verb
667  );
668 
669  // screw mount slot cover
670  mount_slots(0);
671  }
672  // internal wall wire passage holes
673  internal_wire_passage();
674 
675  // power cord hole (wth*4 to remove obstructing ribs)
676  translate([pwxo, -il/2-wth/2, pwzo]) rotate([90, 0, 0])
677  clamp_cg(size=pwcd, wth=wth*4, mode=0);
678 
679  // screw mount slot (negative)
680  mount_slots(1);
681 
682  // detail: logo
683  if ( map_get_value(cm_box, "dlogo") )
684  {
685  logod = map_get_value(cm_box, "roww")/2;
686 
687  translate([0, logod - il/2, 0])
688  mirror([0, 1, 0]) rotate([0, 0, 180])
689  omdl_logo(d=logod, b=true, t=true, a=1, $fn=36);
690  }
691 
692  // detail: enclosure rim grove trim
693  if ( map_get_value(cm_box, "drimb") )
694  extrude_rotate_trl(r=evr, l=[iw-evr*2, il-evr*2])
695  circle(d=wth/2);
696  }
697 
698  // add power cord clamp bottom
699  translate([pwxo, -il/2-wth/2, pwzo]) rotate([90, 0, 0])
700  clamp_cg(size=pwcd, clamp=[pwcs, pwzo, [pwsd,undef,pwsh,pwsn], pwct, pwcp], cone=pwcs+1, wth=wth, mode=1);
701 
702  // add mount tabs
703  mount_tabs();
704 
705  // report power cord hole size
706  if ( verb > 0 )
707  {
708  echo(str( parent_module(0), "(): power cord hole size = ", pwcd ));
709  }
710  }
711 
712  // power strip cover
713  module enclosure_cover()
714  {
715  //
716  // local modules
717  //
718 
719  module cover_round_cut_duplex()
720  {
721  // for echo row and wall instance
722  for (i=[0:rows-1], j=[0 : cols-1])
723  translate([zr - i*sr, zc + j*sc, zo])
724  union()
725  {
726  if ( cdms )
727  { // device mount screw holes
728  mss = map_get_value(cm_mount, "mss");
729  rmsd = map_get_value(cm_mount, "rmsd");
730 
731  for (i=[-1, 1])
732  translate([0, i*mss/2, 0])
733  screw_bore
734  (
735  d = rmsd,
736  l = zh,
737  f = 1+25/100,
738  a = 0
739  );
740  }
741  else
742  { // cover center screw hole
743  rcsd = map_get_value(cm_cover, "rcsd");
744  rmsh = map_get_value(cm_mount, "rmsh");
745 
746  translate([0, 0, rmsh/2])
747  mirror([0, 0, 1])
748  screw_bore
749  (
750  d = first(rcsd),
751  l = zh + rmsh + eps*4,
752  h = [second(rcsd), 0, third(rcsd)],
753  t = [rcsd[3]],
754  a = 0
755  );
756  }
757 
758  // duplex receptacle thru-holes
759  drpo = map_get_value(cm_cover, "drpo");
760  rpd = map_get_value(cm_cover, "rpd");
761  rpfl = map_get_value(cm_cover, "rpfl");
762 
763  extrude_linear_uss(zh, center=true)
764  for (i=[-1, 1])
765  translate([0, i * drpo/2])
766  difference()
767  {
768  circle(d=rpd);
769 
770  for (j=[-1, 1])
771  translate([0, j * (rpd/2 + rpfl)/2])
772  square([rpd, rpd/2], center=true);
773  }
774  }
775  }
776 
777  // add stabilizer pads when using cover center screw
778  // (mount screws will be hidden under cover)
779  module cover_stabilizers()
780  {
781  if ( !cdms )
782  {
783  // for echo row and wall instance
784  for (i=[0:rows-1], j=[0 : cols-1])
785  translate([zr - i*sr, zc + j*sc, zo])
786  union()
787  {
788  mss = map_get_value(cm_mount, "mss");
789  rmsh = map_get_value(cm_mount, "rmsh");
790  rshc = map_get_value(cm_mount, "rshc");
791 
792  translate([0, 0, zo])
793  extrude_linear_uss(rmsh, center=false)
794  union()
795  {
796  rectangle([4, 1]*rshc, vr=rshc/4, vrm=0, center=true);
797 
798  for (i=[-1, 1])
799  translate([0, i * (mss+rshc)/2])
801  (
802  size = [4,1]*rshc,
803  core = [5/4,1]*rshc,
804  co = [0,1/3]*rshc * -i,
805  vr = rshc/4,
806  vrm = 0,
807  center = true
808  );
809  }
810  }
811  }
812  }
813 
814  //
815  // local variables
816  //
817 
818  // wall height must be >= lip height, with min of wth
819  ih = max([ wth, second(l()) ]);
820 
821  // cover screw posts (add custom instances)
822  p = let
823  (
824  pmode = map_get_value(cm_box, "pmode"),
825  piac = map_get_value(cm_box, "piac")
826  )
827  is_undef(piac) ? undef
828  : [pmode, [for (i=piac) i]];
829 
830  // lid edge finish
831  lf = e( map_get_value(cm_box, "lefc") );
832 
833  dlts = map_get_value(cm_box, "dlts");
834  roww = map_get_value(cm_box, "roww");
835  iscl = map_get_value(cm_box, "iscl");
836  lscl = map_get_value(cm_box, "lscl");
837  cdms = map_get_value(cm_box, "cdms");
838 
839  zr = iw/2 - roww/2 - lscl;
840  sr = roww;
841 
842  zc = -il/2 - wth + iscl + dlts/2;
843  sc = dlts + wth;
844 
845  // calculate the total lid extrusion height
846  zh = extrude_linear_mss_eht(lf) + eps*8;
847  zo = zh/2 - eps*4;
848 
849  //
850  // instances
851  //
852 
853  difference()
854  {
855  // cover enclosure
856  union()
857  {
859  (
860  wth = wth,
861  lid = lf,
862  h = ih, size = [iw, il],
863  vrm = evrm, vr = evr,
864 
865  lip = l(2),
866 
867  post = p,
868 
869  mode = 1,
870 
871  verb = verb
872  );
873 
874  // add cover stabilizers
875  cover_stabilizers();
876  }
877 
878  // duplex receptacle holes
879  cover_round_cut_duplex();
880 
881  // detail: enclosure rim grove trim
882  if ( map_get_value(cm_box, "drimc") )
883  extrude_rotate_trl(r=evr, l=[iw-evr*2, il-evr*2])
884  circle(d=wth/2);
885  }
886  }
887 
888  // check configuration map
889  module check_cm(name, mc, md)
890  {
891  for ( k = map_get_keys(mc) )
892  if( !map_exists(md, k) )
893  {
894  log_warn
895  (
896  strl
897  ([
898  "bad entry in map [", name, "] = [", k, ",", map_get_value(mc, k), "]"
899  ])
900  );
901  }
902  }
903 
904  //
905  // local variables
906  //
907 
908  wth = map_get_value(cm_box, "wth");
909 
910  iw = let
911  (
912  roww = map_get_value(cm_box, "roww"),
913  lscl = map_get_value(cm_box, "lscl"),
914  rscl = map_get_value(cm_box, "rscl")
915  )
916  lscl + roww * rows + rscl;
917 
918  il = let
919  (
920  dlts = map_get_value(cm_box, "dlts"),
921  iscl = map_get_value(cm_box, "iscl"),
922  oscl = map_get_value(cm_box, "oscl")
923  )
924  iscl - wth * 3/2 + cols * (dlts + wth) + oscl;
925 
926  evrm = map_get_value(cm_box, "evrm");
927  evr = map_get_value(cm_box, "evr");
928 
929  //
930  // report errors in configuration maps
931  //
932 
933  if ( verb > 0 )
934  {
935  check_cm("cm_box", cm_box, power_strip_box_default);
936  check_cm("cm_mount", cm_mount, power_strip_mount_default);
937  check_cm("cm_cover", cm_cover, power_strip_cover_default);
938  }
939 
940  //
941  // instances
942  //
943 
944  ps = wth*10;
945 
946  if ( binary_bit_is(mode, 0, 1) )
947  translate([-(iw/2 + ps), 0, 0])
948  enclosure_box();
949 
950  if ( binary_bit_is(mode, 1, 1) )
951  translate([+(iw/2 + ps), -(il/2 + ps), 0])
952  clamp_strap();
953 
954  if ( binary_bit_is(mode, 2, 1) )
955  translate([+(iw/2 + ps), 0, 0])
956  enclosure_cover();
957 }
958 
959 //! @}
960 
961 //! @}
962 //! @}
963 
964 
965 //----------------------------------------------------------------------------//
966 // openscad-amu auxiliary scripts
967 //----------------------------------------------------------------------------//
968 
969 /*
970 BEGIN_SCOPE example;
971  BEGIN_OPENSCAD;
972  include <omdl-base.scad>;
973  include <transforms/base_cs.scad>;
974  include <models/3d/misc/omdl_logo.scad>;
975  include <models/3d/fastener/screws.scad>;
976  include <parts/3d/fastener/clamps.scad>;
977  include <parts/3d/fastener/mounts.scad>;
978  include <parts/3d/enclosure/project_box_rectangle.scad>;
979  include <parts/3d/enclosure/power_strip.scad>;
980 
981  box_conf =
982  [
983  ["iscl", 15.0],
984  ["oscl", 15.0],
985 
986  ["pwcd", [8.6, 3.6]],
987  ["pwsh", [6]],
988  ["pwco", [0, 2]],
989  ["pwcp", 20],
990 
991  ["mtabs", [[0]]]
992  ];
993 
994  custom_box = map_merge(box_conf, power_strip_box_default);
995  map_check(custom_box);
996 
997  power_strip_sg(cm_box=custom_box);
998 
999  // end_include
1000  END_OPENSCAD;
1001 
1002  BEGIN_MFSCRIPT;
1003  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
1004  table_unset_all sizes;
1005 
1006  images name "sizes" types "sxga";
1007  views name "views" views "bottom front diag";
1008 
1009  variables set_opts_combine "sizes views";
1010  variables add_opts "--viewall --autocenter --view=axes";
1011 
1012  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1013  END_MFSCRIPT;
1014 END_SCOPE;
1015 */
1016 
1017 /*
1018 BEGIN_SCOPE box_default;
1019  BEGIN_OPENSCAD;
1020  include <omdl-base.scad>;
1021  include <transforms/base_cs.scad>;
1022  include <models/3d/misc/omdl_logo.scad>;
1023  include <models/3d/fastener/screws.scad>;
1024  include <parts/3d/fastener/clamps.scad>;
1025  include <parts/3d/enclosure/project_box_rectangle.scad>;
1026  include <parts/3d/enclosure/power_strip.scad>;
1027 
1028  map = power_strip_box_default;
1029  doc = power_strip_box_doc;
1030 
1031  table_write
1032  (
1033  r = map_to_table( [map, doc], sort=true ),
1034  c = [["key","key"], ["value","value"], ["description","description"]]
1035  );
1036  END_OPENSCAD;
1037 
1038  BEGIN_MFSCRIPT;
1039  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1040  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1041  END_MFSCRIPT;
1042 END_SCOPE;
1043 
1044 BEGIN_SCOPE mount_default;
1045  BEGIN_OPENSCAD;
1046  include <omdl-base.scad>;
1047  include <transforms/base_cs.scad>;
1048  include <models/3d/misc/omdl_logo.scad>;
1049  include <models/3d/fastener/screws.scad>;
1050  include <parts/3d/fastener/clamps.scad>;
1051  include <parts/3d/enclosure/project_box_rectangle.scad>;
1052  include <parts/3d/enclosure/power_strip.scad>;
1053 
1054  map = power_strip_mount_default;
1055  doc = power_strip_mount_doc;
1056 
1057  table_write
1058  (
1059  r = map_to_table( [map, doc], sort=true ),
1060  c = [["key","key"], ["value","value"], ["description","description"]]
1061  );
1062  END_OPENSCAD;
1063 
1064  BEGIN_MFSCRIPT;
1065  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1066  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1067  END_MFSCRIPT;
1068 END_SCOPE;
1069 
1070 BEGIN_SCOPE cover_default;
1071  BEGIN_OPENSCAD;
1072  include <omdl-base.scad>;
1073  include <transforms/base_cs.scad>;
1074  include <models/3d/misc/omdl_logo.scad>;
1075  include <models/3d/fastener/screws.scad>;
1076  include <parts/3d/fastener/clamps.scad>;
1077  include <parts/3d/enclosure/project_box_rectangle.scad>;
1078  include <parts/3d/enclosure/power_strip.scad>;
1079 
1080  map = power_strip_cover_default;
1081  doc = power_strip_cover_doc;
1082 
1083  table_write
1084  (
1085  r = map_to_table( [map, doc], sort=true ),
1086  c = [["key","key"], ["value","value"], ["description","description"]]
1087  );
1088  END_OPENSCAD;
1089 
1090  BEGIN_MFSCRIPT;
1091  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1092  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1093  END_MFSCRIPT;
1094 END_SCOPE;
1095 */
1096 
1097 //----------------------------------------------------------------------------//
1098 // end of file
1099 //----------------------------------------------------------------------------//
module log_warn(m)
Output warning message to console.
Definition: console.scad:333
origin3d
<point-3d> The origin point coordinate in 3-dimensional Euclidean space.
Definition: constants.scad:425
eps
<decimal> Epsilon, small distance to deal with overlapping shapes.
Definition: constants.scad:195
function binary_bit_is(v, b, t=1)
Test if a binary bit position of an integer value equals a test bit.
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function third(v)
Return the third element of an iterable value.
function second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
function limit(v, l, u)
Limit a list of numbers between an upper and lower bounds.
function strl(v)
Convert a list of values to a concatenated string.
function map_get_value(m, k)
Get the map value associated with a key.
function map_get_keys(m)
Get a list of all map keys.
module map_check(m, verbose=false)
Perform basic format checks on a map and output errors to console.
Definition: map.scad:788
function map_exists(m, k)
Test if a key exists.
function is_defined(v)
Test if a value is defined.
module screw_bore(d=1, l=1, h, n, t, s, f, a)
Flat and beveled-head fastener bore with nut, nut slot, and bore tolerance.
Definition: screws.scad:358
module omdl_logo(r=5, c=false, b=false, t=false, td="ltr", a=0, d)
Standard omdl logo.
Definition: omdl_logo.scad:328
module power_strip_sg(cols=1, rows=1, mode=7, verb=1, cm_box=power_strip_box_default, cm_mount=power_strip_mount_default, cm_cover=power_strip_cover_default)
A power strip generator for single gang electrical receptacles.
power_strip_mount_default
<map> A single gang electrical device mount configuration.
power_strip_box_default
<map> A single gang electrical device box configuration.
power_strip_cover_default
<map> A single gang duplex receptacle cover configuration.
module project_box_rectangle(wth, h, size, vr, vrm, inset, lid, lip, rib, wall, post, hole, shape, align, mode=0, verb=0)
A rectangular box maker for project boxes, enclosures and housings.
module clamp_cg(size=1, clamp, cone, grip, wth=0, gap=10, mode)
A clamp, bushing, and/or grip for wire, hose, and/or pipe wall penetrations.
Definition: clamps.scad:747
module mount_screw_slot(wth, screw, cover, size, align, mode, f)
A screw mount slot with optional cover envelope.
Definition: mounts.scad:630
module mount_screw_tab(wth, screw, brace, size, vr, vrm)
A mount tab with screw hole and support brace.
Definition: mounts.scad:362
module rectangle(size=1, vr, vrm=0, center=false)
A rectangle with corner rounds or chamfers.
Definition: common_2d.scad:471
module rectangle_c(size=1, core, t, co, cr=0, vr, vr1, vr2, vrm=0, vrm1, vrm2, center=false)
A rectangle with a removed rectangular core.
Definition: common_2d.scad:595
module cone(size=1, vr, center=false)
A cone.
Definition: common_3d.scad:461
module extrude_rotate_trl(r, pa=0, ra=360, l, m=255)
Translate, rotate, and revolve a 2d shape about the z-axis with linear elongation.
Definition: extrude.scad:562
module extrude_linear_uss(h, center=false, c=true, ha=0)
Linearly extrude a 2d shape with uniformly-spaced profile scaling.
Definition: extrude.scad:666
function extrude_linear_mss_eht(h, s=false)
Return the total extrusion height for extrude_linear_mss().
function length(v, from=length_unit_default, to=length_unit_base, d=1)
Convert a length value from one unit to another, with optional dimensional scaling.