OpenFlexure Microscope OpenSCAD docs
libs/cameras/picamera_2.scad
- *
- OpenFlexure Microscope: Raspberry Pi Camera v2 push-fit mount *
- *
- This is part of the OpenFlexure microscope, an open-source *
- microscope and 3-axis translation stage. It gets really good *
- precision over a ~10mm range, by using plastic flexure *
- mechanisms. *
- *
- This file provides two parts for the microscope; the bit that *
- fits onto the camera (picam2_camera_mount) and a cover that *
- protects the PCB (picam_cover). The former is part of the *
- optics module in optics.scad, and the latter is printed *
- directly. *
- *
- The fit is set by one main function, picam2_push_fit(). It's *
- designed to be subtracted from a solid block, with the bottom *
- of the block at z=0. It grips the plastic camera housing with *
- four slightly flexible fingers, which ensures the camera pops *
- in easily but is held relatively firmly. Two screw holes are *
- also provided that should self-tap with M2 or similar screws. *
- I recommend you use these for extra security if the camera is *
- likely to be handled a lot. *
- *
- (c) Richard Bowman, January 2016 *
- Released under the CERN Open Hardware License *
- *
function picamera_2_camera_dict()
[Source]
function picamera_2_camera_dict() = [["mount_height", 4.5], ["sensor_height", 2]];
function picamera_2_bottom_z()
[Source]
function picamera_2_bottom_z() = -key_lookup("mount_height", picamera_2_camera_dict());
function picamera_2_hole_spacing()
[Source]
function picamera_2_hole_spacing() = 21;
module picam2_flex_and_components(camera_width=8.5+1)
[Source]
module picam2_flex_and_components(camera_width=8.5+1){ // A 2D perimeter inside which the flex and components of the camera sit. // NB this should fit both v1 and v2 of the module // camera_width is the width of the camera module's casing, nominally 8 or 8.5mm but // deliberately printed a bit generous to ensure it fits easily without // damaging the flex. //flex (also clears v1 connector) translate([-camera_width/2,camera_width/2-1]){ square([camera_width,13.4-camera_width/2+1]); } //connector translate([-camera_width/2-2.5,6.7]){ square([camera_width+2.5, 5.4]); } }
module picam1_led()
[Source]
module picam1_led(){ // v1 of the camera module has an LED on board that we should make a cut-out for translate([5,10]){ square([3.5,2]); } translate([6,8]){ square([3.5,2]); } }
module picam2_cutout(beam_length=15)
[Source]
module picam2_cutout( beam_length=15){ // This module is designed to be subtracted from the bottom of a shape. // The z=0 plane should be the print bed. // It includes cut-outs for the components on the PCB and also a push-fit hole // for the camera module. This uses flexible "fingers" to grip the camera firmly // but gently. Just push to insert, and wiggle to remove. You may find popping // off the brown ribbon cable and removing the PCB first helps when extracting // the camera module again. mount_height = key_lookup("mount_height", picamera_2_camera_dict()); //width camera box (NOTE: this is deliberately loose fitting) camera_width = 8.5 + 1.0; //height of camera box (including foam support) camera_height=2.9; //size of camera aperture hole_r = 4.3; union(){ sequential_hull(){ //cut-out for camera (/wider at bottom) translate_z(-tiny()){ cube([camera_width+0.5,camera_width+0.5,tiny()],center=true); } translate_z(0.5){ cube([camera_width,camera_width,tiny()],center=true); } translate_z(camera_height/2){ cube([camera_width,camera_width,camera_height],center=true); } cylinder(r=hole_r, h=2*mount_height, center=true); } //clearance for the ribbon cable at top of camera flex_h=2.5; // the height of the flex extra_h = mount_height-flex_h-0.75; // extra height above the flex for the sloping "roof" hull(){ translate_z(-tiny()){ linear_extrude(flex_h){ picam2_flex_and_components(camera_width); } } translate_z(-tiny()){ linear_extrude(flex_h+extra_h){ offset(-extra_h){ picam2_flex_and_components(camera_width); } } } } //clearance for the LED/resistor on v1 of the camera hull(){ translate_z(-tiny()){ linear_extrude(flex_h){ picam1_led(); } } translate_z(-tiny()){ linear_extrude(flex_h+extra_h){ offset(-extra_h){ picam1_led(); } } } } //beam clearance cylinder(r=hole_r, h=beam_length); } }
module picam2_board(h=tiny())
[Source]
module picam2_board(h=tiny()){ // a rounded rectangle with the dimensions of the picamera board v2 // centred on the origin b = 24; w = 25; roc = 2; linear_extrude(h){ hull(){ reflect([1,0]){ reflect([0,1]){ translate([w/2-roc, b/2-roc]){ circle(r=roc,$fn=12); } } } } } }
module picamera_2_camera_mount(screwhole=true, counterbore=false)
[Source]
module picamera_2_camera_mount(screwhole=true, counterbore=false){ // A mount for the pi camera v2 // This should finish at z=0+tiny(), with a surface that can be // hull-ed onto the lens assembly. b = 24; w = 25; difference(){ rotate(45){ translate_y(2.4){ sequential_hull(){ translate_z(picamera_2_bottom_z()){ picam2_board(h=tiny()); } translate_z(-1){ picam2_board(h=tiny()); } cube([w-(-1.5-picamera_2_bottom_z())*2,b,tiny()],center=true); } } } rotate(45){ translate_z(picamera_2_bottom_z()){ picam2_cutout(); } } if(counterbore){ picamera_2_counterbore(); } if(screwhole){ picamera_2_screwholes(); } } }
module picamera_2_screwholes()
[Source]
module picamera_2_screwholes(){ //chamfered screw holes for mounting screw_x = picamera_2_hole_spacing()/2; rotate_z(45){ translate_z(picamera_2_bottom_z()){ reflect_x(){ translate_x(screw_x){ rotate_z(60){ translate_z(-tiny()){ no2_selftap_hole(h=10); } } } } } } }
module picamera_2_counterbore()
[Source]
module picamera_2_counterbore(){ translate_z(picamera_2_bottom_z()-1){ at_picamera_2_hole_pattern(){ cylinder(r=1.25, h=99, $fn=12); } } translate_z(picamera_2_bottom_z()+1){ at_picamera_2_hole_pattern(){ cylinder(r=2.8, h=99, $fn=12); } } }
module picamera_2_bottom_mounting_posts( optics_config, outers=true, cutouts=true, bottom_slice=false )
[Source]
module picamera_2_bottom_mounting_posts(optics_config, outers=true, cutouts=true, bottom_slice=false){ // posts to mount to pi camera from below if (bottom_slice){ //if we want the bottom slice intersect with the bottom of the whole //post found by recalling the function intersection(){ cylinder(h=tiny(), r=99); picamera_2_bottom_mounting_posts(optics_config, outers=outers, cutouts=cutouts); } } else{ h=key_lookup("mounting_post_height", optics_config); r1=3; r2=2; at_picamera_2_hole_pattern(){ difference(){ if(outers){ cylinder(r1=r1, r2=r2, h=h, $fn=12); } if(cutouts){ translate_z(h-6+tiny()){ no2_selftap_hole(h=6); } } } } } }
module at_picamera_2_hole_pattern()
[Source]
module at_picamera_2_hole_pattern(){ // posts to mount to pi camera from below screw_x = picamera_2_hole_spacing()/2; rotate(45){ reflect_x(){ for(y=[0,12.5]){ translate([screw_x, y, 0]){ children(); } } } } }
module picamera_2_cover()
[Source]
module picamera_2_cover(){ // A cover for the camera PCB, slips over the bottom of the camera // mount. This version should be compatible with v1 and v2 of the board h = 3; outer_size = [25, 21, h]; //wall thickness t = 1; //position of the outer edge in y edge_y = 14.4; screw_x = picamera_2_hole_spacing()/2; box_tr = [-outer_size.x/2, edge_y-outer_size.y, 0]; //cutout for connector is loose in x and huge in x/z connector_cutout = [21, 50, 50]; //cutout should start 18mm from the back edge of the housing connector_y = edge_y - 18; difference(){ union(){ //bottom and sides difference(){ translate(box_tr){ cube(outer_size); } // cut out centre to form walls on 3 sides translate(box_tr + [t, -t, 0.75]){ cube(outer_size - [2*t, 0, 0]); } // cut out for connector translate_y(connector_y-connector_cutout.y/2){ cube(connector_cutout, center=true); } } //Bulge for counterbore. (Slightly elongated cylinder) reflect_x(){ translate_x(screw_x){ hull(){ cylinder(r=3.2, h=h, $fn=16); translate_x(0.5){ cylinder(r=3.2, h=h, $fn=16); } } } } } //counterbore the mounting screws reflect_x(){ translate([screw_x, 0, h-2]){ intersection(){ cylinder(r=2.4, h=999, $fn=16, center=true); hole_from_bottom(r=1.3, h=999, base_w=999, layers=2); } } } } }
module generous_camera_bits()
[Source]
module generous_camera_bits(){ //The other stuff on the PCB (mostly the ribbon cable) camera = [8.5,8.5,2.3]; //size of camera box camera_width = camera.x+1; //side length of camera box at bottom (slightly larger) union(){ //ribbon cable at top of camera sequential_hull(){ cube([camera_width-1,tiny(),4],center=true); translate_y(9.4-(4.4/1)/2){ cube([camera_width-1,1,4],center=true); } } //flex connector translate([-1.25,9.4,0]){ cube([camera_width-1+2.5, 4.4+1, 4],center=true); } } }
module picamera_2_gripper()
[Source]
module picamera_2_gripper(){ // this little bit of plastic grips the plastic camera housing // and allows you to safely unscrew the lens // it protects the (surprisingly delicate) flex that connects the camera to the PCB. //size of the picam PCB (+0.5mm so it fits) pcb_dims = [25.4+0.5,24+0.5,2]; //size of the pastic housing camera_housing = [9,9,2.5]; //shift of the camera housing from the centre camera_housing_y_shift = 2.5; //size of the tool outer = pcb_dims+[4,-5,camera_housing.z]; difference(){ translate([0,-1,outer.z/2]){ cube(outer, center=true); } //central hole for the camera housing translate_y(camera_housing_y_shift){ cube(camera_housing + [0,0,999],center=true); } //cut-outs for the other bits (cable etc.) translate([0, camera_housing_y_shift, camera_housing.z]){ rotate_x(180){ generous_camera_bits(); } } //indent for PCB translate_z(outer.z){ cube(pcb_dims + [0,0,pcb_dims.z],center=true); } } }
module picamera_2_lens_removal_tool()
[Source]
module picamera_2_lens_removal_tool(){ //a tool to unscrew the lens from the pi camera inner_r = 4.7/2; union(){ difference(){ cylinder(r=7,h=2); cylinder(r=5,h=999,center=true); } for(a=[0,90,180,270]){ rotate(a){ translate_x(inner_r){ cube([1.5,5,2]); } } } } }