|
// Illuminated Tile Grid |
|
// Ed Nisley – KE4ZNU |
|
// 2020-05 |
|
|
|
/* [Configuration] */ |
|
|
|
Layout = "Build"; // [Cell,CellArray,MCU,Base,Show,Build] |
|
|
|
Shape = "Square"; // [Square, Pyramid, Cone] |
|
|
|
Cells = [2,2]; |
|
|
|
CellDepth = 15.0; |
|
|
|
Support = true; |
|
|
|
Inserts = true; |
|
|
|
/* [Hidden] */ |
|
|
|
ThreadThick = 0.25; |
|
ThreadWidth = 0.40; |
|
|
|
HoleWindage = 0.2; |
|
|
|
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); |
|
|
|
Protrusion = 0.1; // make holes end cleanly |
|
|
|
ID = 0; |
|
OD = 1; |
|
LENGTH = 2; |
|
|
|
Tile = [25.0 + 0.1,25.0 + 0.1,4.0]; |
|
|
|
WallThick = 3*ThreadWidth; |
|
|
|
Flange = [4*ThreadWidth,4*ThreadWidth,0]; // ridge supporting tile |
|
|
|
Separator = [3*ThreadWidth,3*ThreadWidth,Tile.z – 1]; // between tiles |
|
|
|
Screw = [3.0,6.0,3.5]; // M3 SHCS, OD=head, LENGTH=head |
|
Insert = [3.0,4.2,8.0]; // threaded brass insert |
|
|
|
PCB = [15.0,8.0,2.5]; |
|
|
|
LED = [5.0 + 2*HoleWindage,5.0 + 2*HoleWindage,1.0]; |
|
|
|
LEDOffset = [0.0,(PCB.y – LED.y)/2 – 0.5,0.0]; // slight offset from +Y PCB edge |
|
|
|
CellOAL = [Tile.x,Tile.y,0] + Separator + [0,0,CellDepth] + [0,0,WallThick] + [0,0,PCB.z]; |
|
|
|
ArrayOAL = [Cells.x*CellOAL.x,Cells.y*CellOAL.y,CellOAL.z]; // just the LED cells |
|
|
|
BlockOAL = ArrayOAL + [2*WallThick,2*WallThick,0]; // LED cells + exterior wall |
|
echo(str("Block OAL: ",BlockOAL)); |
|
|
|
InsertOC = ArrayOAL – [Insert[OD],Insert[OD],0] – [2*WallThick,2*WallThick,0]; |
|
echo(str("Insert OC: ",InsertOC)); |
|
|
|
TapeThick = 1.0; |
|
|
|
Arduino = [44.0,18.0,8.0 + TapeThick]; // Arduino Nano to top of USB Mini-B plug |
|
USBPlug = [15.0,11.0,8.5]; // USB Mini-B plug insulator |
|
USBOffset = [0,0,5.5]; // offset from PCB base |
|
|
|
WiringBay = [BlockOAL.x – 4*WallThick,38.0,3.0]; |
|
PlateOAL = [BlockOAL.x,BlockOAL.y,WallThick + Arduino.z + WiringBay.z]; |
|
echo(str("Base Plate: ",PlateOAL)); |
|
|
|
//———————— |
|
|
|
module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes |
|
Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); |
|
FixDia = Dia / cos(180/Sides); |
|
cylinder(d=(FixDia + HoleWindage),h=Height,$fn=Sides); |
|
} |
|
|
|
|
|
//———————– |
|
// Base and optics in single tile |
|
|
|
module LEDCone() { |
|
|
|
hull() { |
|
translate([0,0,CellDepth + Tile.z/2]) |
|
cube(Tile – [Flange.x,Flange.y,0],center=true); |
|
if (Shape == "Square") { |
|
translate([0,0,LED.z/2]) |
|
cube([Tile.x,Tile.y,LED.z] – [Flange.x,Flange.y,0],center=true); |
|
} |
|
else if (Shape == "Pyramid") { |
|
translate([0,0,LED.z/2]) |
|
cube(LED,center=true); |
|
} |
|
else if (Shape == "Cone") { |
|
translate([0,0,LED.z/2]) |
|
cylinder(d=1.5*LED.x,h=LED.z,center=true); |
|
} |
|
else { |
|
echo(str("Whoopsie! Invalid Shape: ",Shape)); |
|
cube(5); |
|
} |
|
} |
|
} |
|
|
|
// One complete LED cell |
|
|
|
module LEDCell() { |
|
difference() { |
|
|
|
translate([0,0,CellOAL.z/2]) |
|
cube(CellOAL,center=true); |
|
|
|
translate([0,0,CellOAL.z – Separator.z + Tile.z/2]) |
|
cube(Tile,center=true); |
|
|
|
translate([0,0,PCB.z + WallThick]) |
|
LEDCone(); |
|
|
|
cube([LED.x,LED.y,CellOAL.z],center=true); |
|
|
|
translate(-LEDOffset + [0,0,PCB.z/2 – Protrusion/2]) |
|
cube(PCB + [0,0,Protrusion],center=true); |
|
} |
|
|
|
if (Support) |
|
color("Yellow") render() |
|
translate(-LEDOffset) { |
|
// translate([0,0,ThreadThick/2]) |
|
// cube([PCB.x – 2*ThreadWidth,PCB.y – 2*ThreadWidth,ThreadThick],center=true); |
|
intersection() { |
|
translate([0,0,(PCB.z – ThreadThick)/2]) |
|
cube([PCB.x – 2*ThreadWidth,PCB.y – 2*ThreadWidth,PCB.z – ThreadThick],center=true); |
|
union() { for (a=[0:22.5:359]) |
|
rotate(a) |
|
translate([PCB.x/2,0,PCB.z/2]) |
|
cube([PCB.x,2*ThreadWidth,PCB.z],center=true); } |
|
} |
|
} |
|
} |
|
|
|
// The whole array of cells |
|
|
|
module CellArray() { |
|
difference() { |
|
union() { |
|
translate([CellOAL.x/2 – Cells.x*CellOAL.x/2,CellOAL.y/2 – Cells.y*CellOAL.y/2,0]) |
|
for (i=[0:Cells.x – 1], j=[0:Cells.y – 1]) |
|
translate([i*CellOAL.x,j*CellOAL.y,0]) |
|
LEDCell(); |
|
if (Inserts) // bosses |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*InsertOC.x/2,j*InsertOC.y/2,0]) |
|
rotate(180/8) |
|
cylinder(d=Insert[OD] + 3*WallThick,h=Insert[LENGTH],$fn=8); |
|
} |
|
if (Inserts) // holes |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*InsertOC.x/2,j*InsertOC.y/2,-Protrusion]) |
|
rotate(180/8) |
|
PolyCyl(Insert[OD],Insert[LENGTH] + WallThick + Protrusion,8); |
|
|
|
} |
|
|
|
difference() { |
|
translate([0,0,CellOAL.z/2]) |
|
cube(BlockOAL,center=true); |
|
translate([0,0,CellOAL.z]) |
|
cube(ArrayOAL + [0,0,2*CellOAL.z],center=true); |
|
|
|
} |
|
} |
|
|
|
// Arduino bounding box |
|
// Origin at center bottom of PCB |
|
|
|
module Controller() { |
|
union() { |
|
translate([0,0,Arduino.z/2]) |
|
cube(Arduino,center=true); |
|
translate([Arduino.x/2 – Protrusion,-USBPlug.y/2,USBOffset.z + TapeThick – USBPlug.z/2]) |
|
cube(USBPlug + [Protrusion,0,0],center=false); |
|
} |
|
} |
|
|
|
// Baseplate |
|
|
|
module BasePlate() { |
|
|
|
difference() { |
|
translate([0,0,PlateOAL.z/2]) |
|
cube(PlateOAL,center=true); |
|
translate([0,0,WallThick]) |
|
Controller(); |
|
translate([0,0,WallThick + PlateOAL.z/2]) |
|
cube([Arduino.x – 2*2.0,WiringBay.y,PlateOAL.z],center=true); |
|
translate([0,0,PlateOAL.z – WiringBay.z + WiringBay.z/2]) |
|
cube(WiringBay + [0,0,2*Protrusion],center=true); |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*InsertOC.x/2,j*InsertOC.y/2,-Protrusion]) |
|
rotate(180/8) { |
|
PolyCyl(Screw[ID],2*PlateOAL.z,8); |
|
PolyCyl(Screw[OD],Screw[LENGTH] + 4*ThreadThick + Protrusion,8); |
|
} |
|
translate([0,0,ThreadThick-Protrusion]) |
|
cube([17.0,45,2*ThreadThick],center=true); |
|
} |
|
|
|
linear_extrude(height=2*ThreadWidth + Protrusion) { |
|
translate([1,0,-Protrusion]) |
|
rotate(-90) mirror([1,0,0]) |
|
text(text="Ed Nisley",size=6,font="Arial:style:Bold",halign="center"); |
|
translate([-6.5,0,-Protrusion]) |
|
rotate(-90) mirror([1,0,0]) |
|
text(text="softsolder.com",size=4.5,font="Arial:style:Bold",halign="center"); |
|
} |
|
|
|
if (Support) |
|
color("Yellow") |
|
for (i=[-1,1], j=[-1,1]) |
|
translate([i*InsertOC.x/2,j*InsertOC.y/2,0]) |
|
for (a=[0:45:135]) |
|
rotate(a) |
|
translate([0,0,(Screw[LENGTH] – ThreadThick)/2]) |
|
cube([Screw[OD] – 2*ThreadWidth,2*ThreadWidth,Screw[LENGTH] – ThreadThick],center=true); |
|
} |
|
|
|
|
|
//———————– |
|
// Build things |
|
|
|
if (Layout == "Cell") |
|
LEDCell(); |
|
|
|
else if (Layout == "CellArray") |
|
CellArray(); |
|
|
|
else if (Layout == "MCU") |
|
Controller(); |
|
|
|
else if (Layout == "Base") |
|
BasePlate(); |
|
|
|
else if (Layout == "Show") { |
|
translate([0,0,PlateOAL.z + 10]) |
|
CellArray(); |
|
BasePlate(); |
|
} |
|
|
|
else if (Layout == "Build") { |
|
translate([0,0.6*BlockOAL.y,0]) |
|
CellArray(); |
|
translate([0,-0.6*BlockOAL.y,0]) |
|
rotate(90) |
|
BasePlate(); |
|
} |
|
|