wordclock/hw/wordclock.scad

278 lines
5.1 KiB
OpenSCAD

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* ITLISASAMPM */
/* ACQUARTERDC */
/* TWENTYFIVEX */
/* FALFSTENFTO */
/* PASTERUNINE */
/* ONESIXTHREE */
/* FOURFIVETWO */
/* EIGHTELEVEN */
/* SEVENTWELVE */
/* TENSEOCLOCK */
a3 = [297, 420];
e = 0.2;
// Overall is [335, 330] on [450, 450] with 58 mm margins.
s = [410, 410, 4];
module rcube(s, r=5) {
hull() {
for (x = [r, s[0]-r]) {
for (y = [r, s[1] - r]) {
translate([x, y, 0])
cylinder(r=r, s[2]);
}
}
}
}
// Size of one LED on a strip.
sled = [100/3, 10+1, 3];
module led_strip(n) {
dc = 28+2.5;
w = 10;
h = 3;
lh = 2;
d = 5;
cube([n*dc, w, h-lh]);
for (x = [0..dc*n]) {
translate([x, 0, 0])
difference() {
cube([d, d, h-lh]);
translate([d/2, d/2, h-1])
cylinder(d=(d-1), 1);
}
}
}
// Width and height in characters
cx = 11;
cy = 10;
// Separation between LEDs
dx = sled[0];
dy = 35;
t = 4;
sleds = [dx*cx, dy*cy, t];
module layer_plate() {
square([s[0], s[1]], center=false);
}
module mholes() {
inset = 7;
m = 4;
d = s[0] - inset*2;
for (x = [inset, s[0]-inset]) {
for (y = [inset:d/3:inset+d]) {
translate([x, y, 0])
circle(d=m, $fn=10);
}
}
}
module back_layer() {
difference() {
layer_plate();
mholes();
}
}
module led_layer() {
module cutout() {
// Done as a snake
// LED strips
for (y = [0:dy:dy*cy-1]) {
translate([sled[1]/2, y, 0])
# square([sled[0]*cx+sled[1], sled[1]]);
}
// Verticals
for (y = [0:dy*2:dy*cy-1]) {
translate([3, y, 0])
square([sled[1]-1, dy+sled[1]]);
}
for (y = [dy:dy*2:dy*(cy-1)-1]) {
translate([sled[0]*cx+sled[1]-3, y+cy-sled[1]+1, 0])
square([sled[1], dy+sled[1]]);
}
}
sc = [dx*cx+sled[1]*2, dy*(cy-1)+sled[1], 4];
difference() {
layer_plate();
translate((s-sc)/2)
cutout();
nodemcu();
mholes();
}
}
module holes_layer() {
// LED hole radius
lhr = 6;
module holes() {
for (y = [0:dy:dy*cy-1]) {
for (x = [0:dx:dx*cx-1]) {
translate([x+lhr, y+lhr, 0])
circle(r=lhr);
}
}
}
sh = [dx*(cx-1)+lhr*2, dy*(cy-1)+lhr*2, t];
difference() {
layer_plate();
translate((s-sh)/2)
holes();
nodemcu();
mholes();
}
}
module diffuse_layer() {
// LED hole radius
lhr = dx-6;
module holes() {
for (y = [0:dy:dy*cy-1]) {
for (x = [0:dx:dx*cx-1]) {
translate([x+lhr/2, y+lhr/2, 0])
square([lhr, lhr]);
}
}
}
sh = [dx*(cx-1)+lhr*2, dy*(cy-1)+lhr*2, t];
difference() {
layer_plate();
translate((s-sh)/2)
holes();
mholes();
}
}
// Diffuser sheet.
sd = [340, 340, 5];
module diffuser_layer() {
difference() {
layer_plate();
translate((s - sd)/2+[0, 0, -e])
cube(sd);
mholes();
}
}
// Front words panel
module words_layer() {
module character(ch) {
if (len(search(str(ch), "APQRDO")) > 0) {
difference() {
text(text=ch, size=dy*.7, font="Bitstream Vera Sans Mono:style=Bold", halign="center");
translate([-.8-.3, -6, 0])
square([3, 30]);
}
} else {
text(text=ch, size=dy*.7, font="Bitstream Vera Sans Mono:style=Bold", halign="center");
}
}
module row(v) {
for (i = [0:len(v)-1]) {
translate([sled[0]*i, 0, 0])
character(v[i]);
}
}
module rows() {
texts = [
"ITSISASAMPM",
"ACQUARTERDC",
"TWENTYFIVEX",
"HALFSTENFTO",
"PASTERUNINE",
"ONESIXTHREE",
"FOURFIVETWO",
"EIGHTELEVEN",
"SEVENTWELVE",
"TENSOKYDUDE",
];
for (i = [0:len(texts)-1]) {
translate([0, dy*i, 0])
row(texts[len(texts)-i-1]);
}
}
difference() {
layer_plate();
translate((s-sleds+[dx, dy, 0])/2 + [0, -11, 0])
rows();
mholes();
}
}
module nodemcu(h=t*2) {
sn = [26, 48+8, h];
susb = [10, 20, h];
translate([s[0]-susb[0]*2, 10, 0])
rotate(90, [0, 0, 1]) {
translate([(sn[0]-susb[0])/2, -susb[1], 0])
square([susb[0], susb[1]], center=false);
square([sn[0], sn[1]], center=false);
}
}
module clock() {
back_layer();
translate([0, 0, t*1])
led_layer();
translate([0, 0, t*2])
holes_layer();
translate([0, 0, t*3])
diffuser_layer();
translate([0, 0, t*4])
words_layer();
}
module sliced(c, o) {
h = 600;
difference() {
translate([0, -c, 0])
children();
translate([-10, o*h, 0])
square(h);
}
}
module slice_bottom() {
sliced(cut, 0) children();
}
module slice_top() {
sliced(cut, -1) children();
}
// cut is how far up to cut the layer into two parts.
cut = 410-(410-297+58);
// The following is a bit messy. For each layer you should do a
// slice_top() layer(); export to svg; then do a slice_bottom()
// layer(); then export to svg.
//slice_top()
slice_bottom()
words_layer();
//diffuse_layer();
//holes_layer();
//led_layer()