|
// Google Pixel 3a mount for stereo zoom microscope |
|
// Ed Nisley - KE4ZNU - 2019-12 |
|
|
|
Layout = "Show"; // [Show,BuildAll,BuildBumpers,BuildPlate,DrillGuide,Phone,Plate,Bumper] |
|
|
|
/* [Hidden] */ |
|
|
|
ThreadThick = 0.25; |
|
ThreadWidth = 0.40; |
|
|
|
HoleWindage = 0.2; |
|
|
|
Protrusion = 0.1; // make holes end cleanly |
|
|
|
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); |
|
|
|
ID = 0; |
|
OD = 1; |
|
LENGTH = 2; |
|
|
|
inch = 25.4; |
|
|
|
//---------------------- |
|
// Dimensions |
|
|
|
Phone = [74.5,156.0,12.0]; // inside Spigen case |
|
PhoneRadii = [10.0,10.0,3.0]; // corner rounding, likewise |
|
|
|
LensOffset = [-17.0,-18.5,0]; // looking at phone screen, (-) sign = from right/top edge |
|
|
|
PrintReader = [0,Phone.y/2 - 44.0,0]; // fingerprint reader from center |
|
PrintReaderDia = [20.0,30.0,0]; // ... hole for access |
|
|
|
Eyepiece = [11.5,28.0 + 0.50,27.0]; // ID = lens, OD includes clearance |
|
|
|
Insert = [3.0,4.5,4.0]; // M3 threaded brass insert |
|
Screw = [3.0,7.0,3.5]; // OD = washer, LENGTH = washer + head height |
|
|
|
WallThick = 3.0; // minimum wall thickness |
|
|
|
Bumper = [2*Screw[OD],20.0,Phone.z]; // bumper edge piece |
|
BumperOAL = Bumper.y + Bumper.x; // outside length for corner piece |
|
|
|
BumperRadius = 2.0; |
|
|
|
MinMargin = 1.2*Bumper.x; // at least this much extra plate for bumpers |
|
echo(str("MinMargin: ",MinMargin)); |
|
|
|
Plate = [IntegerMultiple(Phone.x + 2*MinMargin,5.0), |
|
IntegerMultiple(Phone.y + 2*MinMargin,5.0), |
|
false ? 3*ThreadThick : max(Insert[LENGTH] + 2*ThreadThick,WallThick)]; |
|
PlateRadius = 5.0; |
|
echo(str("Plate: ",Plate," radius: ",PlateRadius)); |
|
|
|
EmbossDepth = 2*ThreadThick + Protrusion; |
|
DebossHeight = EmbossDepth; |
|
|
|
ScrewOffset = Bumper.x/2; |
|
ScrewAdjust = 1.5*Screw[ID]; |
|
|
|
NumSides = 2*3*4; |
|
|
|
Gap = 2.0; // between build layout parts |
|
|
|
//---------------------- |
|
// Useful routines |
|
|
|
module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes |
|
|
|
Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); |
|
FixDia = Dia / cos(180/Sides); |
|
cylinder(r=(FixDia + HoleWindage)/2, |
|
h=Height, |
|
$fn=Sides); |
|
} |
|
|
|
// Basic shapes |
|
|
|
// Overall phone outline |
|
|
|
module Phone() { |
|
|
|
hull() |
|
for (i=[-1,1], j=[-1,1], k=[-1,1]) |
|
translate([i*(Phone.x/2 - PhoneRadii.x),j*(Phone.y/2 - PhoneRadii.y),k*(Phone.z/2 - PhoneRadii.z)]) |
|
resize(2*PhoneRadii) |
|
sphere(r=1,$fn=NumSides); |
|
} |
|
|
|
module Plate() { |
|
|
|
union() { |
|
difference() { |
|
union() { |
|
hull() |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*(Plate.x/2 - PlateRadius),j*(Plate.y/2 - PlateRadius),0]) |
|
cylinder(r=PlateRadius,h=Plate.z,center=true,$fn=NumSides); |
|
translate([Phone.x/2,Phone.y/2,-Eyepiece[LENGTH]/3 + Plate.z/2] + LensOffset) |
|
cylinder(d=Eyepiece[OD] + 2*WallThick,h=Eyepiece[LENGTH]/3, |
|
center=false,$fn=NumSides); |
|
translate([Phone.x/2,Phone.y/2,-2*Eyepiece[LENGTH]/3 + Plate.z/2 + Protrusion] + LensOffset) |
|
cylinder(d1=Eyepiece[OD] + 10*ThreadThick, |
|
d2=Eyepiece[OD] + 2*WallThick, |
|
h=Eyepiece[LENGTH]/3, |
|
center=false,$fn=NumSides); |
|
} |
|
translate([Phone.x/2,Phone.y/2,-2*Eyepiece[LENGTH] + Plate.z/2 + Protrusion] + LensOffset) |
|
PolyCyl(Eyepiece[OD],2*Eyepiece[LENGTH],NumSides); |
|
translate(PrintReader + [0,0,-Plate.z/2 - Protrusion]) |
|
cylinder(d1=PrintReaderDia[OD],d2=PrintReaderDia[ID],h=Plate.z + 2*Protrusion,$fn=NumSides); |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*(Phone.x/2 + Bumper.x/2),j*(Phone.y/2 - Bumper.y/2),-Plate.z]) |
|
PolyCyl(Insert[OD],2*Plate.z,8); |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*(Phone.x/2 - Bumper.y/2),j*(Phone.y/2 + Bumper.x/2),-Plate.z]) |
|
PolyCyl(Insert[OD],2*Plate.z,8); |
|
|
|
translate([0,-12,Plate.z/2]) // recess for legend |
|
cube([55,40,EmbossDepth],center=true); |
|
} |
|
|
|
translate([0,0,Plate.z/2 - EmbossDepth]) |
|
linear_extrude(height=DebossHeight,convexity=20) |
|
text(text="Pixel 3a",size=6,spacing=1.20, |
|
font="Arial:style:Bold",halign="center",valign="center"); |
|
translate([0,-15,Plate.z/2 - EmbossDepth]) |
|
linear_extrude(height=DebossHeight,convexity=20) |
|
text(text="Ed Nisley",size=6,spacing=1.20, |
|
font="Arial:style:Bold",halign="center",valign="center"); |
|
translate([0,-25,Plate.z/2 - EmbossDepth]) |
|
linear_extrude(height=DebossHeight,convexity=20) |
|
text(text="softsolder.com",size=4,spacing=1.20, |
|
font="Arial:style:Bold",halign="center",valign="center"); |
|
|
|
|
|
} |
|
} |
|
|
|
module BumperPiece() { |
|
difference() { |
|
translate([0,-BumperOAL/2 + Bumper.x,0]) |
|
hull() |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*(Bumper.x/2 - BumperRadius),j*(BumperOAL/2 - BumperRadius),0]) |
|
cylinder(r=BumperRadius,h=Bumper.z,center=true,$fn=NumSides); |
|
translate([0,-Bumper.y/2,-Bumper.z]) |
|
PolyCyl(ScrewAdjust,2*Bumper.z,8); |
|
} |
|
} |
|
|
|
// Side bumpers, XY origin at inner corner |
|
|
|
module BumperCorner() { |
|
|
|
union() { |
|
translate([Bumper.x/2,0,0]) |
|
BumperPiece(); |
|
translate([0,Bumper.x/2,0]) |
|
rotate(-90) |
|
BumperPiece(); |
|
} |
|
} |
|
|
|
|
|
//- Build things |
|
|
|
if (Layout == "Phone") |
|
Phone(); |
|
|
|
if (Layout == "Plate") |
|
Plate(); |
|
|
|
if (Layout == "Bumper") |
|
BumperCorner(); |
|
|
|
if (Layout == "Show") { |
|
color("LightBlue") Plate(); |
|
for (i=[-1,1], j=[-1,1]) { |
|
a = |
|
i > 0 && j > 0 ? 0 : |
|
i < 0 && j > 0 ? 90 : |
|
i > 0 && j < 0 ? -90 : |
|
180 |
|
; |
|
translate([i*Phone.x/2,j*Phone.y/2,Plate.z/2 + Bumper.z/2]) |
|
rotate(a) |
|
color("LightGreen") BumperCorner(); |
|
translate([0,0,Phone.z/2 + Plate.z/2 + Protrusion]) |
|
color("DarkGray",0.5) Phone(); |
|
} |
|
} |
|
|
|
if (Layout == "BuildAll") { |
|
translate([0,0,Plate.z/2]) |
|
rotate([0,180,0]) |
|
Plate(); |
|
for (i=[-1,1], j=[-1,1]) { |
|
a = |
|
i > 0 && j > 0 ? 0 : |
|
i < 0 && j > 0 ? 90 : |
|
i > 0 && j < 0 ? -90 : |
|
180 |
|
; |
|
translate([i*(Plate.x/2 + Gap),j*(Plate.y/2 + Gap),Bumper.z/2]) |
|
rotate(a) |
|
BumperCorner(); |
|
} |
|
} |
|
|
|
|
|
if (Layout == "BuildPlate") { |
|
translate([0,0,Plate.z/2]) |
|
rotate([0,180,0]) |
|
Plate(); |
|
} |
|
|
|
|
|
if (Layout == "BuildBumpers") { |
|
for (i=[-1,1], j=[-1,1]) { |
|
a = |
|
i > 0 && j > 0 ? 180 : |
|
i < 0 && j > 0 ? -90 : |
|
i > 0 && j < 0 ? 90 : |
|
0 |
|
; |
|
translate([i*(Bumper.x + Gap),j*(Bumper.x + Gap),Bumper.z/2]) |
|
rotate(a) |
|
BumperCorner(); |
|
} |
|
} |
|
|
|
|
|
if (Layout == "DrillGuide") { |
|
projection(cut=true) |
|
Plate(); |
|
|
|
} |