mirror of
https://github.com/DJSundog/NopSCADlib.git
synced 2024-11-14 10:53:50 -05:00
393 lines
14 KiB
OpenSCAD
393 lines
14 KiB
OpenSCAD
|
//
|
||
|
// NopSCADlib Copyright Chris Palmer 2020
|
||
|
// nop.head@gmail.com
|
||
|
// hydraraptor.blogspot.com
|
||
|
//
|
||
|
// This file is part of NopSCADlib.
|
||
|
//
|
||
|
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||
|
// GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||
|
// the License, or (at your option) any later version.
|
||
|
//
|
||
|
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||
|
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
// See the GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||
|
// If not, see <https://www.gnu.org/licenses/>.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
//! Housings for PCB cameras.
|
||
|
//
|
||
|
include <../core.scad>
|
||
|
include <../vitamins/cameras.scad>
|
||
|
use <../vitamins/pcb.scad>
|
||
|
use <../vitamins/insert.scad>
|
||
|
|
||
|
wall = 1.75;
|
||
|
min_wall = 2 * extrusion_width;
|
||
|
clearance = 0.2;
|
||
|
|
||
|
connector_size = [23, 6, 2.65]; // Worst case size of flat flex connector
|
||
|
|
||
|
cam_back_clearance = round_to_layer(1.5); // Clearance for components on the back of the pcb
|
||
|
cam_back_overlap = 1; // How much the back overlaps the edge of the pcb
|
||
|
cam_back_wall = min_wall;
|
||
|
|
||
|
function cam_front_clearance(cam) = round_to_layer(camera_connector_size(cam).z + clearance);
|
||
|
|
||
|
function cam_back_size(cam) = let(
|
||
|
pcb = camera_pcb(cam),
|
||
|
pcb_size = pcb_size(pcb),
|
||
|
nut = screw_nut(pcb_screw(pcb)),
|
||
|
holes = [for(h = pcb_holes(pcb)) pcb_coord(pcb, h).x],
|
||
|
pitch = max(holes) - min(holes),
|
||
|
length = pitch + 2 * (nut_radius(nut) + min_wall),
|
||
|
width = pcb_size.y + (length - pcb_size.x) * cos(30)
|
||
|
) [length, width, wall + max(connector_size.z, cam_back_clearance + nut_trap_depth(nut))];
|
||
|
|
||
|
|
||
|
function cam_front_size(cam) = cam_back_size(cam) + [ //! Outside dimensions of the case
|
||
|
2 * (wall + clearance),
|
||
|
2 * (wall + clearance),
|
||
|
pcb_thickness(camera_pcb(cam)) + cam_front_clearance(cam) + wall
|
||
|
];
|
||
|
|
||
|
hinge_screw = M2_cap_screw;
|
||
|
hinge_nut = screw_nut(hinge_screw);
|
||
|
hinge_screw_length = 12;
|
||
|
|
||
|
hinge_r = nut_trap_radius(hinge_nut) + 3 * extrusion_width;
|
||
|
hinge_h = wall + nut_trap_depth(hinge_nut);
|
||
|
hinge_offset = hinge_r + 1;
|
||
|
|
||
|
bracket_screw = M3_dome_screw;
|
||
|
|
||
|
function cam_screw_length(cam) = let(
|
||
|
front = cam_front_size(cam),
|
||
|
screw = pcb_screw(camera_pcb(cam)),
|
||
|
nut = screw_nut(screw)
|
||
|
) screw_longer_than(front.z + washer_thickness(screw_washer(screw)) - nut_trap_depth(nut) + nut_thickness(nut, true));
|
||
|
|
||
|
function hinge_z(cam) = cam_screw_length(cam) - hinge_r;
|
||
|
|
||
|
module cam_holes(cam) {
|
||
|
pcb = camera_pcb(cam);
|
||
|
lens_y = camera_lens_offset(cam).y;
|
||
|
two_holes = !!len([for (h = pcb_holes(pcb)) if(abs(pcb_coord(pcb, h).y - lens_y) < 1) true]);
|
||
|
pcb_screw_positions(pcb) // screw holes
|
||
|
if($i > 1 || !two_holes)
|
||
|
children();
|
||
|
}
|
||
|
|
||
|
module rpi_camera_focus_ring_stl() { //! Focus ring the glue onto RPI lens
|
||
|
stl("rpi_camera_focus_ring");
|
||
|
|
||
|
rad = 15 / 2;
|
||
|
hole_r1 = 2.5 / 2;
|
||
|
hole_r2 = 5 / 2;
|
||
|
thickness = 3;
|
||
|
flutes = 8;
|
||
|
angle = 180 / flutes;
|
||
|
x = rad / (sin(angle / 2) + cos(angle / 2));
|
||
|
r = x * sin(angle / 2);
|
||
|
|
||
|
difference() {
|
||
|
linear_extrude(height = thickness, convexity = 5)
|
||
|
difference() {
|
||
|
union() {
|
||
|
circle(x);
|
||
|
for(i = [0 : flutes - 1])
|
||
|
rotate([0, 0, 2 * angle * i])
|
||
|
translate([x, 0])
|
||
|
circle(r);
|
||
|
}
|
||
|
for(i = [0 : flutes - 1])
|
||
|
rotate([0, 0, 2 * angle * i + angle])
|
||
|
translate([x, 0])
|
||
|
circle(r);
|
||
|
}
|
||
|
hull() {
|
||
|
poly_cylinder(r = hole_r1, h = 0.1, center = true);
|
||
|
|
||
|
translate([0, 0, thickness])
|
||
|
poly_cylinder(r = hole_r2, h = 0.1, center = true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module camera_back(cam) { //! Make the STL for a camera case back
|
||
|
stl(str("camera_back_", cam[0]));
|
||
|
pcb = camera_pcb(cam);
|
||
|
back = cam_back_size(cam);
|
||
|
screw = pcb_screw(pcb);
|
||
|
nut = screw_nut(screw);
|
||
|
|
||
|
translate_z(back.z)
|
||
|
hflip()
|
||
|
difference() {
|
||
|
translate_z(back.z / 2)
|
||
|
cube(back, center = true);
|
||
|
|
||
|
translate([0, -cam_back_overlap])
|
||
|
cube([pcb_length(pcb) - 2 * cam_back_overlap, pcb_width(pcb), 2 * cam_back_clearance], center = true);
|
||
|
|
||
|
translate([0, -pcb_width(pcb) / 2])
|
||
|
cube([connector_size.x + 2 * clearance, 2 * connector_size.y + 1, 2 * round_to_layer(connector_size.z + clearance)], center = true);
|
||
|
|
||
|
translate_z(back.z)
|
||
|
cam_holes(cam)
|
||
|
hflip()
|
||
|
nut_trap(screw, nut, supported = true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module camera_front(cam, hinge = 0) { //! Make the STL for a camera case front
|
||
|
stl(str("camera_front_", cam[0]));
|
||
|
front = cam_front_size(cam);
|
||
|
back = cam_back_size(cam);
|
||
|
pcb = camera_pcb(cam);
|
||
|
pcb_size = pcb_size(pcb);
|
||
|
lens_offset = camera_lens_offset(cam);
|
||
|
screw = pcb_screw(pcb);
|
||
|
|
||
|
shelf = front.z - back.z;
|
||
|
|
||
|
connector_slot = connector_size + 2 * [clearance, 0, layer_height];
|
||
|
rad = wall;
|
||
|
led_hole_r = 1;
|
||
|
led_clearance = [5, 2, 1 * 2];
|
||
|
res_clearance = [3.5, 2, 1 * 2];
|
||
|
|
||
|
conn_pos = camera_connector_pos(cam);
|
||
|
conn = camera_connector_size(cam);
|
||
|
sensor_length = conn_pos.y + conn.y / 2 - lens_offset.y + clearance;
|
||
|
|
||
|
module hinge_pos()
|
||
|
if(!is_undef(hinge))
|
||
|
rotate(hinge * 90)
|
||
|
translate([0, (hinge ? front.x * hinge : front.y) / 2 + hinge_offset, hinge_r])
|
||
|
children();
|
||
|
|
||
|
difference() {
|
||
|
union() {
|
||
|
hull()
|
||
|
for(x = [-1, 1], y = [-1, 1])
|
||
|
translate([x * (front.x / 2 - rad), y * (front.y / 2 - rad)])
|
||
|
hull() { // 3D truncated teardrop gives radiused edges without exceeding 45 degree overhang
|
||
|
translate_z(front.z - 1)
|
||
|
cylinder(r = rad, h = 1);
|
||
|
|
||
|
translate_z(rad)
|
||
|
sphere(rad);
|
||
|
|
||
|
cylinder(r = rad * (sqrt(2) - 1), h = eps);
|
||
|
}
|
||
|
|
||
|
hinge_pos()
|
||
|
hull() {
|
||
|
rotate([-90, 0, -90])
|
||
|
teardrop(r = hinge_r, h = hinge_h, center = false);
|
||
|
|
||
|
translate([0, -10, -hinge_r])
|
||
|
cube([hinge_h, eps, 2 * hinge_r]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hinge_pos()
|
||
|
rotate([90, 0, 90])
|
||
|
teardrop_plus(r = screw_clearance_radius(hinge_screw), h = 100, center = true);
|
||
|
|
||
|
translate_z(front.z / 2 + shelf - layer_height) // recess for the back
|
||
|
cube([back.x + 2 * clearance, back.y + 2 * clearance, front.z], center = true);
|
||
|
|
||
|
translate_z(front.z / 2 + shelf - pcb_size.z) // recess for PCB
|
||
|
cube([pcb_size.x + 2 * clearance, pcb_size.y + 2 * clearance, front.z], center = true);
|
||
|
|
||
|
translate_z(shelf)
|
||
|
hflip() {
|
||
|
pcb_component_position(pcb, "smd_led") // clearance for LED
|
||
|
cube(led_clearance, center = true);
|
||
|
|
||
|
pcb_component_position(pcb, "smd_res") // clearance for resistor
|
||
|
cube(res_clearance, center = true);
|
||
|
}
|
||
|
|
||
|
translate([conn_pos.x, lens_offset.y + sensor_length / 2, shelf - pcb_size.z]) // clearance for sensor connector
|
||
|
cube([conn.x + 2 * clearance, sensor_length, 2 * cam_front_clearance(cam)], center = true);
|
||
|
|
||
|
translate([0, -front.y / 2, shelf + front.z / 2]) // slot for connector
|
||
|
cube([connector_slot.x, connector_slot.y, front.z], center = true);
|
||
|
|
||
|
translate_z(cam_back_clearance + layer_height)
|
||
|
cam_holes(cam)
|
||
|
rotate(90)
|
||
|
poly_cylinder(r = screw_clearance_radius(screw), h = 100, center = true);
|
||
|
|
||
|
translate_z(shelf - pcb_size.z)
|
||
|
hflip()
|
||
|
camera_lens(cam, clearance);
|
||
|
|
||
|
hflip()
|
||
|
pcb_component_position(pcb, "smd_led")
|
||
|
rotate(45)
|
||
|
poly_cylinder(r = led_hole_r, h = 100, center = true); // hole for led
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function bracket_thickness(cam) = max(wall, min(3.5, hinge_z(cam) - hinge_r - 1));
|
||
|
|
||
|
module camera_bracket_screw_positions(cam) { //! Position children at the bracket screw positions
|
||
|
r = washer_radius(screw_washer(bracket_screw)) + 0.5;
|
||
|
wide = bracket_thickness(cam) == wall;
|
||
|
pitch = wide ? cam_front_size(cam).x / 2 - r : hinge_h + 1 + r;
|
||
|
|
||
|
for(side = [-1, 1])
|
||
|
translate([side * pitch, 0])
|
||
|
children();
|
||
|
}
|
||
|
|
||
|
module camera_bracket_position(cam) //! Position children at the bracket position
|
||
|
translate([0, cam_front_size(cam).y / 2 + hinge_offset])
|
||
|
children();
|
||
|
|
||
|
module camera_bracket(cam) { //! Make the STL for the camera bracket
|
||
|
stl(str("camera_bracket_", cam[0]));
|
||
|
|
||
|
t = bracket_thickness(cam);
|
||
|
z = hinge_z(cam);
|
||
|
translate([hinge_h / 2, 0])
|
||
|
difference() {
|
||
|
hull() {
|
||
|
translate_z(eps / 2)
|
||
|
cube([hinge_h, 2 * hinge_r, eps], center = true);
|
||
|
|
||
|
translate_z(z)
|
||
|
rotate([0, 90, 0])
|
||
|
cylinder(r = hinge_r, h = hinge_h, center = true);
|
||
|
}
|
||
|
translate([hinge_h / 2, 0, z])
|
||
|
rotate([90, 0, 90])
|
||
|
nut_trap(hinge_screw, screw_nut(hinge_screw), horizontal = true);
|
||
|
}
|
||
|
|
||
|
linear_extrude(t)
|
||
|
difference() {
|
||
|
hull()
|
||
|
camera_bracket_screw_positions(cam)
|
||
|
circle(washer_radius(screw_washer(bracket_screw)) + 0.5);
|
||
|
|
||
|
camera_bracket_screw_positions(cam)
|
||
|
poly_circle(screw_clearance_radius(bracket_screw));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module camera_assembly(cam, angle = 0) //! Camera case assembly
|
||
|
assembly(str("camera_", cam[0])) {
|
||
|
front = cam_front_size(cam);
|
||
|
screw = pcb_screw(camera_pcb(cam));
|
||
|
nut = screw_nut(screw);
|
||
|
screw_length = cam_screw_length(cam);
|
||
|
hinge_z = hinge_z(cam);
|
||
|
hinge_pos = [0, front.y / 2 + hinge_offset, -hinge_r];
|
||
|
|
||
|
camera_bracket_position(cam) {
|
||
|
nut = screw_nut(hinge_screw);
|
||
|
|
||
|
stl_colour(pp1_colour) render()
|
||
|
camera_bracket(cam);
|
||
|
|
||
|
translate([-hinge_h, 0, hinge_z(cam)])
|
||
|
rotate([-90, 0, 90]) {
|
||
|
vflip()
|
||
|
translate_z(2 * hinge_h - nut_trap_depth(nut))
|
||
|
nut(nut, true);
|
||
|
|
||
|
screw_and_washer(hinge_screw, screw_longer_than(2 * hinge_h));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
translate_z(hinge_z(cam) + hinge_r)
|
||
|
translate(hinge_pos)
|
||
|
rotate([-angle, 0, 0])
|
||
|
translate(-hinge_pos) {
|
||
|
translate_z(cam_back_size(cam).z - front.z)
|
||
|
camera(cam);
|
||
|
|
||
|
stl_colour(pp1_colour) render()
|
||
|
translate_z(-front.z)
|
||
|
camera_back(cam);
|
||
|
|
||
|
cam_holes(cam) {
|
||
|
screw_and_washer(screw, screw_length);
|
||
|
|
||
|
translate_z(-front.z + nut_trap_depth(nut))
|
||
|
vflip()
|
||
|
nut(nut, true);
|
||
|
}
|
||
|
|
||
|
*translate(camera_lens_offset(cam))
|
||
|
translate_z(1.5)
|
||
|
stl_colour(pp1_colour) render()
|
||
|
rpi_camera_focus_ring_stl();
|
||
|
|
||
|
stl_colour(pp2_colour) render()
|
||
|
hflip()
|
||
|
camera_front(cam, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module camera_fastened_assembly(cam, thickness, angle = 0) {
|
||
|
camera_assembly(cam, angle);
|
||
|
|
||
|
camera_bracket_position(cam)
|
||
|
camera_bracket_screw_positions(cam) {
|
||
|
nut = screw_nut(bracket_screw);
|
||
|
washer = screw_washer(bracket_screw);
|
||
|
t = bracket_thickness(cam);
|
||
|
screw_length = screw_longer_than(thickness + t + nut_thickness(nut, true) + 2 * washer_thickness(washer));
|
||
|
vflip()
|
||
|
translate_z(thickness)
|
||
|
screw_and_washer(bracket_screw, screw_length);
|
||
|
|
||
|
translate_z(t)
|
||
|
nut_and_washer(nut, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module camera_back_rpi_camera_stl() camera_back(rpi_camera);
|
||
|
module camera_back_rpi_camera_v1_stl() camera_back(rpi_camera_v1);
|
||
|
module camera_back_rpi_camera_v2_stl() camera_back(rpi_camera_v2);
|
||
|
|
||
|
module camera_front_rpi_camera_stl() camera_front(rpi_camera);
|
||
|
module camera_front_rpi_camera_v1_stl() camera_front(rpi_camera_v1);
|
||
|
module camera_front_rpi_camera_v2_stl() camera_front(rpi_camera_v2);
|
||
|
|
||
|
module camera_bracket_rpi_camera_stl() camera_bracket(rpi_camera);
|
||
|
module camera_bracket_rpi_camera_v1_stl() camera_bracket(rpi_camera_v1);
|
||
|
module camera_bracket_rpi_camera_v2_stl() camera_bracket(rpi_camera_v2);
|
||
|
|
||
|
module camera_rpi_camera_assembly() camera_assembly(rpi_camera);
|
||
|
module camera_rpi_camera_v1_assembly() camera_assembly(rpi_camera_v1);
|
||
|
module camera_rpi_camera_v2_assembly() camera_assembly(rpi_camera_v2);
|
||
|
|
||
|
module camera_housing(cam) {
|
||
|
front = cam_front_size(cam);
|
||
|
|
||
|
camera_front(cam, 0);
|
||
|
|
||
|
translate([front.x, 0])
|
||
|
camera_back(cam);
|
||
|
|
||
|
translate([-front.x / 2 - 2 - hinge_r, 0])
|
||
|
rotate(90)
|
||
|
camera_bracket(cam);
|
||
|
}
|
||
|
|
||
|
cam = rpi_camera_v2;
|
||
|
if($preview)
|
||
|
camera_fastened_assembly(cam, 3);
|
||
|
else
|
||
|
camera_housing(cam);
|