// // 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 . // // //! 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);