Drops all the custom slot/latch-shelf/face-stop/base-notch parameters and modules in favour of the imported rj45Receiver() component. The receiver sits flat behind the front face with its slot opening at cap z=0 (after a 180° flip around Y) and its body extending into the cavity / wall box. Validation now uses rj45_width × rj45_height as the in-plane footprint. Also bumps screw_countersink_diameter from 5.5 mm to 6.6 mm.
272 lines
11 KiB
OpenSCAD
272 lines
11 KiB
OpenSCAD
// ============================================
|
||
// Blind Cap (Tapa Ciega)
|
||
// ============================================
|
||
// A blank cover plate for a flush-mounted electrical wall box.
|
||
// Square plate with rounded corners, hollow back, and two
|
||
// internal screw bosses on the vertical centerline.
|
||
|
||
use <../components/rj45_keystone.scad>;
|
||
|
||
// ============================================
|
||
// CUSTOMIZABLE PARAMETERS
|
||
// ============================================
|
||
|
||
// Outer width and height of the cap (square)
|
||
outer_size = 80; // [50:1:120]
|
||
|
||
// Radius of the rounded outer corners
|
||
corner_radius = 5; // [1:0.5:15]
|
||
|
||
// Total depth of the cap (front face to back rim)
|
||
total_depth = 3; // [5:0.5:20]
|
||
|
||
// Thickness of the front face
|
||
front_thickness = 2; // [1:0.1:5]
|
||
|
||
// Thickness of the side walls (the rim around the cavity)
|
||
wall_thickness = 1; // [1:0.1:5]
|
||
|
||
// Center-to-center distance between the two screw holes
|
||
screw_hole_spacing = 60; // [30:1:100]
|
||
|
||
// Through-hole diameter for the mounting screw shank
|
||
screw_hole_diameter = 3; // [2:0.1:6]
|
||
|
||
// Countersink diameter on the front face for the screw head
|
||
screw_countersink_diameter = 6.6; // [4:0.1:12]
|
||
|
||
// Countersink depth on the front face
|
||
screw_countersink_depth = 1.2; // [0:0.1:3]
|
||
|
||
// Diameter of the internal screw bosses
|
||
boss_diameter = 9; // [5:0.5:15]
|
||
|
||
// Front-face edge rounding (slight chamfer on the visible front edge)
|
||
front_edge_round = 1.5; // [0:0.1:3]
|
||
|
||
// Back-rim edge rounding (slight chamfer on the rear outer edge)
|
||
back_edge_round = 0; // [0:0.1:3]
|
||
|
||
// ============================================
|
||
// KEYSTONE JACK CRADLE
|
||
// ============================================
|
||
// The cradle behind the front-face cutout is the marcuswu RJ45
|
||
// keystone receiver, imported from ../components/rj45_keystone.scad.
|
||
// Its slot, latch shelf, fulcrum/clip bars, and insertion ramp are
|
||
// all defined by that module — we just position it behind the front
|
||
// face and cut a hole in the front face for the keystone face to
|
||
// show through.
|
||
//
|
||
// The receiver's Y axis (insertion direction) is rotated to align
|
||
// with the cap's Z axis (depth). The keystone slides in face-first
|
||
// from the cavity side and clips into the receiver's built-in
|
||
// latch-catch.
|
||
|
||
// Add the keystone receiver behind the front-face cutout
|
||
keystone_enabled = true; // [false, true]
|
||
|
||
// Front-face cutout — sized to the visible portion of the keystone face.
|
||
keystone_face_width = 14.5; // [10:0.1:25]
|
||
keystone_face_length = 16.0; // [10:0.1:25]
|
||
|
||
// Offset of the front-face cutout along the receiver's long axis (Y in
|
||
// cradle local). The receiver's slot is taller than the visible face,
|
||
// so the cutout typically needs to sit a few mm off-centre to align
|
||
// with where the face actually shows.
|
||
keystone_face_offset_y = 1.2; // [-6:0.1:6]
|
||
|
||
// Print-tolerance clearance added on each side of the front-face cutout
|
||
keystone_clearance = 0.2; // [0:0.05:1]
|
||
|
||
// Rotation of the keystone (receiver + front cutout) about the cap's
|
||
// Z axis, in 90° steps. 0° puts the receiver's long axis along Y;
|
||
// 90° puts it along X.
|
||
keystone_rotation = 90; // [0, 90, 180, 270]
|
||
|
||
// Translate the whole keystone (receiver + cutout) away from the cap's
|
||
// centre.
|
||
keystone_position_x = 15; // [-40:0.5:40]
|
||
keystone_position_y = 0; // [-40:0.5:40]
|
||
|
||
// ============================================
|
||
// RENDERING QUALITY
|
||
// ============================================
|
||
|
||
$fn = 64;
|
||
|
||
// ============================================
|
||
// PARAMETER VALIDATION
|
||
// ============================================
|
||
|
||
assert(outer_size > 2 * corner_radius, "Outer size must be larger than twice the corner radius");
|
||
assert(total_depth > front_thickness, "Total depth must be greater than front thickness");
|
||
assert(boss_diameter > screw_hole_diameter, "Boss diameter must exceed screw hole diameter");
|
||
assert(screw_countersink_diameter >= screw_hole_diameter, "Countersink must be at least as wide as the screw hole");
|
||
assert(screw_hole_spacing + boss_diameter < outer_size - 2 * wall_thickness, "Screw bosses must fit inside the cavity");
|
||
assert(front_edge_round < front_thickness, "Front edge round must be smaller than front thickness");
|
||
assert(back_edge_round < total_depth - front_thickness, "Back edge round must be smaller than the cavity depth");
|
||
assert(front_edge_round + back_edge_round < total_depth, "Combined edge rounds must not exceed total depth");
|
||
|
||
// Keystone-specific validation. The receiver sits flat behind the
|
||
// front face, so its X × Y footprint is rj45_width × rj45_height
|
||
// (with rj45_depth becoming the cap-Z protrusion). At 90°/270° the
|
||
// X and Y swap in the cap frame, which changes which screw boss the
|
||
// receiver might hit.
|
||
KS_OUTER_X = rj45_width();
|
||
KS_OUTER_Y = rj45_height();
|
||
KS_ROT_SWAP = (keystone_rotation == 90 || keystone_rotation == 270);
|
||
KS_EFF_X = KS_ROT_SWAP ? KS_OUTER_Y : KS_OUTER_X;
|
||
KS_EFF_Y = KS_ROT_SWAP ? KS_OUTER_X : KS_OUTER_Y;
|
||
|
||
KS_X_MIN = keystone_position_x - KS_EFF_X / 2;
|
||
KS_X_MAX = keystone_position_x + KS_EFF_X / 2;
|
||
KS_Y_MIN = keystone_position_y - KS_EFF_Y / 2;
|
||
KS_Y_MAX = keystone_position_y + KS_EFF_Y / 2;
|
||
|
||
// Helper: do AABBs [a1,a2] and [b1,b2] overlap on one axis?
|
||
function _ks_overlap(a1, a2, b1, b2) = !(a2 < b1 || a1 > b2);
|
||
|
||
// Each screw boss's AABB in cap coords (square inscribing the circle).
|
||
KS_BOSS_R = boss_diameter / 2;
|
||
KS_TOP_Y = screw_hole_spacing / 2;
|
||
KS_BOT_Y = -screw_hole_spacing / 2;
|
||
|
||
KS_HITS_TOP = _ks_overlap(KS_X_MIN, KS_X_MAX, -KS_BOSS_R, KS_BOSS_R)
|
||
&& _ks_overlap(KS_Y_MIN, KS_Y_MAX, KS_TOP_Y - KS_BOSS_R, KS_TOP_Y + KS_BOSS_R);
|
||
KS_HITS_BOT = _ks_overlap(KS_X_MIN, KS_X_MAX, -KS_BOSS_R, KS_BOSS_R)
|
||
&& _ks_overlap(KS_Y_MIN, KS_Y_MAX, KS_BOT_Y - KS_BOSS_R, KS_BOT_Y + KS_BOSS_R);
|
||
|
||
assert(keystone_rotation == 0 || keystone_rotation == 90
|
||
|| keystone_rotation == 180 || keystone_rotation == 270,
|
||
"keystone_rotation must be 0, 90, 180, or 270");
|
||
assert(!keystone_enabled
|
||
|| (KS_X_MIN >= -(outer_size / 2 - wall_thickness)
|
||
&& KS_X_MAX <= (outer_size / 2 - wall_thickness)),
|
||
"Keystone receiver (with rotation+offset) extends beyond the cap's inner cavity along X");
|
||
assert(!keystone_enabled
|
||
|| (KS_Y_MIN >= -(outer_size / 2 - wall_thickness)
|
||
&& KS_Y_MAX <= (outer_size / 2 - wall_thickness)),
|
||
"Keystone receiver (with rotation+offset) extends beyond the cap's inner cavity along Y");
|
||
assert(!keystone_enabled || !KS_HITS_TOP,
|
||
"Keystone receiver (with rotation+offset) would collide with the top screw boss");
|
||
assert(!keystone_enabled || !KS_HITS_BOT,
|
||
"Keystone receiver (with rotation+offset) would collide with the bottom screw boss");
|
||
|
||
// ============================================
|
||
// MAIN ASSEMBLY
|
||
// ============================================
|
||
|
||
difference() {
|
||
union() {
|
||
// Outer shell with the back cavity already removed
|
||
difference() {
|
||
outer_shell();
|
||
|
||
// Hollow out the back cavity
|
||
translate([0, 0, front_thickness])
|
||
rounded_prism(
|
||
size = outer_size - 2 * wall_thickness,
|
||
radius = max(corner_radius - wall_thickness, 0.5),
|
||
height = total_depth - front_thickness + 1
|
||
);
|
||
}
|
||
|
||
// Internal screw bosses (rise from the inside of the front face)
|
||
for (y = [-screw_hole_spacing / 2, screw_hole_spacing / 2])
|
||
translate([0, y, front_thickness])
|
||
cylinder(h = total_depth - front_thickness, d = boss_diameter);
|
||
|
||
// Keystone receiver — imported from components/rj45_keystone.scad.
|
||
// Sits flat behind the front face: receiver Z=0 (its slot
|
||
// opening) is at cap z=0, so the keystone is inserted face-
|
||
// first through the front-face cutout into the receiver. The
|
||
// body extends in cap +Z into the cavity / wall box. Centred
|
||
// in cap X/Y at (keystone_position_x, keystone_position_y) and
|
||
// rotated about cap Z by keystone_rotation.
|
||
if (keystone_enabled)
|
||
translate([keystone_position_x, keystone_position_y, 0])
|
||
rotate([0, 0, keystone_rotation])
|
||
translate([rj45_width() / 2, (-rj45_height() / 2)+2, rj45_depth()])
|
||
rotate([0, 180, 0])
|
||
rj45Receiver();
|
||
}
|
||
|
||
// Through-holes for the mounting screws
|
||
for (y = [-screw_hole_spacing / 2, screw_hole_spacing / 2])
|
||
translate([0, y, -1])
|
||
cylinder(h = total_depth + 2, d = screw_hole_diameter);
|
||
|
||
// Countersinks on the front face for the screw heads
|
||
if (screw_countersink_depth > 0)
|
||
for (y = [-screw_hole_spacing / 2, screw_hole_spacing / 2])
|
||
translate([0, y, -0.01])
|
||
cylinder(
|
||
h = screw_countersink_depth + 0.01,
|
||
d1 = screw_countersink_diameter,
|
||
d2 = screw_hole_diameter
|
||
);
|
||
|
||
// Keystone front-face cutout — sized to the visible portion of the
|
||
// keystone face only. Punches through the cap's front face so the
|
||
// keystone face inside the receiver is visible from outside. The
|
||
// face flange behind the cutout seats against the back of the
|
||
// front-face material. Rotated and translated together with the
|
||
// receiver so they stay aligned.
|
||
if (keystone_enabled) {
|
||
cw = keystone_face_width + 2 * keystone_clearance;
|
||
cl = keystone_face_length + 2 * keystone_clearance;
|
||
translate([keystone_position_x, keystone_position_y, 0])
|
||
rotate([0, 0, keystone_rotation])
|
||
translate([-cw / 2, -cl / 2 + keystone_face_offset_y, -1])
|
||
cube([cw, cl, front_thickness + 2]);
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// COMPONENT MODULES
|
||
// ============================================
|
||
|
||
// Outer shell with softly tapered front and back outer edges.
|
||
// Front face sits at z = 0 (build plate), back rim at z = total_depth.
|
||
module outer_shell() {
|
||
if (front_edge_round > 0 || back_edge_round > 0) {
|
||
hull() {
|
||
// Front face (inset) — only when front edge is rounded
|
||
if (front_edge_round > 0)
|
||
translate([0, 0, 0])
|
||
rounded_prism(
|
||
size = outer_size - 2 * front_edge_round,
|
||
radius = max(corner_radius - front_edge_round, 0.1),
|
||
height = 0.01
|
||
);
|
||
|
||
// Full-size middle section
|
||
translate([0, 0, front_edge_round])
|
||
rounded_prism(
|
||
size = outer_size,
|
||
radius = corner_radius,
|
||
height = total_depth - front_edge_round - back_edge_round
|
||
);
|
||
|
||
// Back rim (inset) — only when back edge is rounded
|
||
if (back_edge_round > 0)
|
||
translate([0, 0, total_depth - 0.01])
|
||
rounded_prism(
|
||
size = outer_size - 2 * back_edge_round,
|
||
radius = max(corner_radius - back_edge_round, 0.1),
|
||
height = 0.01
|
||
);
|
||
}
|
||
} else {
|
||
rounded_prism(outer_size, corner_radius, total_depth);
|
||
}
|
||
}
|
||
|
||
// Square prism with rounded corners, centered on the X/Y origin.
|
||
module rounded_prism(size, radius, height) {
|
||
hull()
|
||
for (x = [-size / 2 + radius, size / 2 - radius])
|
||
for (y = [-size / 2 + radius, size / 2 - radius])
|
||
translate([x, y, 0])
|
||
cylinder(h = height, r = radius);
|
||
}
|