// ============================================ // Tapo Ceiling Mount - Exterior Plate // ============================================ // The visible plate below the ceiling. It covers the existing hole, carries // the four M4 screws inside the hole diameter, provides a large central cable // opening, and gives mounting points for the original TP-Link Tapo base. // // Print with the bottom (visible) face on the bed. The bosses and the // centering lip point up. No support material is needed. parameters_only = false; include ; // Solid disc with a chamfered outer edge on the visible face. module plate_disc() { rotate_extrude() polygon([ [0, 0], [plate_r - plate_edge_chamfer, 0], [plate_r, plate_edge_chamfer], [plate_r, plate_thickness], [0, plate_thickness] ]); } // Ring on the plate top that enters the ceiling hole and centers the plate. module centering_lip() { rotate_extrude() translate([0, plate_thickness]) polygon([ [lip_inner_r, 0], [lip_outer_r, 0], [lip_outer_r - centering_lip_lead, centering_lip_height], [lip_inner_r, centering_lip_height] ]); } // Local thickening on the plate top around one screw, for head bearing. module screw_boss_top() { cylinder(d = screw_boss_d, h = plate_thickness + screw_boss_h); } // Central cable opening (the pill), elongated along the cable-routing direction. module central_opening() { translate([0, 0, -1]) linear_extrude(plate_thickness + centering_lip_height + 2) hull() { for (s = [-1, 1]) translate([ s * cable_slot_reach * cos(cable_slot_angle), s * cable_slot_reach * sin(cable_slot_angle) ]) circle(r = central_opening_r); } } // Solid boss under one base mounting hole. It fills the opening locally. module camera_boss() { cylinder(d = camera_boss_d, h = plate_thickness); } // Pilot hole for one base mounting screw, drilled up from the bottom face. module camera_pilot() { translate([0, 0, -0.01]) cylinder(d = camera_mount_hole_diameter, h = camera_screw_depth); } // Ribs that tie each base boss to the solid plate ring across the opening. module camera_ribs() { for (p = camera_mount_xy) { r = norm(p); ang = atan2(p[1], p[0]); ro = max(r + camera_boss_d / 2, central_opening_r + 5); hull() { translate([p[0], p[1], 0]) cylinder(d = camera_rib_w, h = plate_thickness); translate([ro * cos(ang), ro * sin(ang), 0]) cylinder(d = camera_rib_w, h = plate_thickness); } } } // Screw clearance hole plus the head recess for one screw. module plate_screw_hole() { translate([0, 0, -1]) cylinder(d = m4_clearance_d, h = plate_thickness + screw_boss_h + 2); translate([0, 0, -0.01]) cylinder(d = screw_head_recess_d, h = head_recess_depth); if (head_type == "countersunk") translate([0, 0, -0.01]) cylinder(d1 = csk_d, d2 = m4_clearance_d, h = csk_depth); } // The complete exterior plate. // With enable_connector_interface = true the plate carries the bayonet ring // for the modular collar. The collar wraps around the ring and hides it. The // direct camera-base mount is then off. Set enable_connector_interface = false // to keep the direct base mount. module tapo_outer_plate() { union() { difference() { union() { // Disc, lip and screw bosses, central opening removed. difference() { union() { plate_disc(); centering_lip(); for (p = screw_positions) translate([p[0], p[1], 0]) screw_boss_top(); } central_opening(); } // Direct base mount only when no connector is used. if (!enable_connector_interface) { for (p = camera_mount_xy) translate([p[0], p[1], 0]) camera_boss(); camera_ribs(); } } for (p = screw_positions) translate([p[0], p[1], 0]) plate_screw_hole(); if (!enable_connector_interface) for (p = camera_mount_xy) translate([p[0], p[1], 0]) camera_pilot(); } // Bayonet ring for the modular collar. if (enable_connector_interface) plate_bayonet_ring(); } } tapo_outer_plate();